Search code examples
gocore-graphicsscreenshot

Difference between running simple GOLANG application in terminal or IDE on mac


I want to make a screenshot of my mac desktop using golang language. There is a nice and easy tool for that: https://github.com/kbinani/screenshot I've been using it for quite sometime but recently I've tried to use it again and noticed a weird bahaviour on two of my macbooks (big sur and catalina).

Here is a simple code:

package main

import (
    "fmt"
    "github.com/kbinani/screenshot"
    "image/png"
    "os"
)

func main(){
    bounds := screenshot.GetDisplayBounds(0)

    img, err := screenshot.CaptureRect(bounds)
    if err != nil {
        panic(err)
    }
    fileName := fmt.Sprintf("%d_%dx%d.png", 0, bounds.Dx(), bounds.Dy())
    file, _ := os.Create(fileName)
    defer file.Close()
    png.Encode(file, img)

    fmt.Printf("#%d : %v \"%s\"\n", 0, bounds, fileName)
}

The code above should capture first screen and save the file like "0_2560x1440.png" near binary. When I run binary from the Jetbrains Goland IDE or when I use go run main.go command inside the Jetbrains Goland IDE terminal everything is fine. However if I will run binary or go run main.go from the default terminal (or iTerm) I will receive screenshots without any windows on it, just a plain desktop without any winodws or foldres.

What is the reason for that? What is the difference running binary from IDE terminal or OS terminal?

After some consideration I thought that maybe there is a bug somewhere in the golang screenshot library. I tried to run another code using OSX api:

package main

// To use the two libraries we need to define the respective flags, include the required header files and import "C" immediately after
import (
    // #cgo LDFLAGS: -framework CoreGraphics
    // #cgo LDFLAGS: -framework CoreFoundation
    // #include <CoreGraphics/CoreGraphics.h>
    // #include <CoreFoundation/CoreFoundation.h>
    "C"
    "image"
    "image/png"
    "os"
    "reflect"
    "unsafe"
    // other packages...
)

func main() {
    displayID := C.CGMainDisplayID()
    width := int(C.CGDisplayPixelsWide(displayID))
    height := int(C.CGDisplayPixelsHigh(displayID))
    rawData := C.CGDataProviderCopyData(C.CGImageGetDataProvider(C.CGDisplayCreateImage(displayID)))

    length := int(C.CFDataGetLength(rawData))
    ptr := unsafe.Pointer(C.CFDataGetBytePtr(rawData))

    var slice []byte
    hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
    hdrp.Data = uintptr(ptr)
    hdrp.Len = length
    hdrp.Cap = length

    imageBytes := make([]byte, length)

    for i := 0; i < length; i += 4 {
        imageBytes[i], imageBytes[i+2], imageBytes[i+1], imageBytes[i+3] = slice[i+2], slice[i], slice[i+1], slice[i+3]
    }

    //C.CFRelease(rawData)

    img := &image.RGBA{Pix: imageBytes, Stride: 4 * width, Rect: image.Rect(0, 0, width, height)}
    // There we go, we can now save or process the image further

    file, err := os.Create("file.png")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    if err := png.Encode(file, img); err != nil {
        panic(err)
    }
}

This code gives same result. Run in IDE terminal - normal behaviour. Run somewhere else - screenshot has now content.

Please help me to demystify that.


Solution

  • After some investigation it turned out that the problem is just a OSX permissions. There is a screen recording permission. It was allowed for IDE, but it was not for terminal.

    There was no pop-up window or something like that.