From 85fe9feed13243d377dd498654a6698a0769d2ce Mon Sep 17 00:00:00 2001 From: kbinani Date: Thu, 8 Aug 2024 15:42:35 +0900 Subject: [PATCH 1/7] Testing how ScreenCaptureKit works --- screenshot_darwin.go | 59 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/screenshot_darwin.go b/screenshot_darwin.go index e55072e..21081bc 100644 --- a/screenshot_darwin.go +++ b/screenshot_darwin.go @@ -3,24 +3,65 @@ package screenshot /* -#cgo LDFLAGS: -framework CoreGraphics -framework CoreFoundation +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework CoreGraphics -framework CoreFoundation -framework ScreenCaptureKit #include +#import -void* CompatCGDisplayCreateImageForRect(CGDirectDisplayID display, CGRect rect) { +static void* CompatCGDisplayCreateImageForRect(CGDirectDisplayID display, CGRect rect) { return CGDisplayCreateImageForRect(display, rect); } -void CompatCGImageRelease(void* image) { +static void CompatCGImageRelease(void* image) { CGImageRelease(image); } -void* CompatCGImageCreateCopyWithColorSpace(void* image, CGColorSpaceRef space) { +static void* CompatCGImageCreateCopyWithColorSpace(void* image, CGColorSpaceRef space) { return CGImageCreateCopyWithColorSpace((CGImageRef)image, space); } -void CompatCGContextDrawImage(CGContextRef c, CGRect rect, void* image) { +static void CompatCGContextDrawImage(CGContextRef c, CGRect rect, void* image) { CGContextDrawImage(c, rect, (CGImageRef)image); } + +extern void captureResult(CGImageRef img, int session); + +static void startCapture(CGDirectDisplayID id, int session) { + [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error) { + @autoreleasepool { + if (error) { + printf("error1\n"); + captureResult(nil, session); + return; + } + NSArray *displays = [content displays]; + SCDisplay* target = nil; + for (SCDisplay *display in displays) { + if ([display displayID] == id) { + target = display; + break; + } + } + if (!target) { + printf("error2\n"); + captureResult(nil, session); + return; + } + SCContentFilter *filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; + [SCScreenshotManager captureImageWithFilter:filter + configuration:nil + completionHandler:^(CGImageRef img, NSError* error) { + if (error) { + printf("error3\n"); + captureResult(nil, session); + } else { + printf("captured\n"); + captureResult(img, session); + } + }]; + } + }]; +} */ import "C" @@ -28,10 +69,17 @@ import ( "errors" "image" "unsafe" + "fmt" "github.com/kbinani/screenshot/internal/util" ) +//export captureResult +func captureResult(img C.CGImageRef, session C.int) { + //TODO: + fmt.Printf("captureResult: session=%d\n", session) +} + func Capture(x, y, width, height int) (*image.RGBA, error) { if width <= 0 || height <= 0 { return nil, errors.New("width or height should be > 0") @@ -67,6 +115,7 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { defer C.CGColorSpaceRelease(colorSpace) for _, id := range ids { + C.startCapture(id, 0); //TODO: cgBounds := getCoreGraphicsCoordinateOfDisplay(id) cgIntersect := C.CGRectIntersection(cgBounds, cgCaptureBounds) if C.CGRectIsNull(cgIntersect) { From e58ddae0bd228ea9806d7904b03b687179cea43e Mon Sep 17 00:00:00 2001 From: kbinani Date: Thu, 8 Aug 2024 16:11:24 +0900 Subject: [PATCH 2/7] Receive captured CGImageRef through a global channel --- screenshot_darwin.go | 64 +++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/screenshot_darwin.go b/screenshot_darwin.go index 21081bc..bf4f508 100644 --- a/screenshot_darwin.go +++ b/screenshot_darwin.go @@ -24,17 +24,16 @@ static void CompatCGContextDrawImage(CGContextRef c, CGRect rect, void* image) { CGContextDrawImage(c, rect, (CGImageRef)image); } -extern void captureResult(CGImageRef img, int session); +extern void sendCaptureResult(CGImageRef img, int session, int error); static void startCapture(CGDirectDisplayID id, int session) { [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error) { @autoreleasepool { if (error) { - printf("error1\n"); - captureResult(nil, session); + sendCaptureResult(nil, session, 1); return; } - NSArray *displays = [content displays]; + NSArray* displays = [content displays]; SCDisplay* target = nil; for (SCDisplay *display in displays) { if ([display displayID] == id) { @@ -43,20 +42,18 @@ static void startCapture(CGDirectDisplayID id, int session) { } } if (!target) { - printf("error2\n"); - captureResult(nil, session); + sendCaptureResult(nil, session, 2); return; } - SCContentFilter *filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; + SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; + SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init]; [SCScreenshotManager captureImageWithFilter:filter - configuration:nil + configuration:config completionHandler:^(CGImageRef img, NSError* error) { if (error) { - printf("error3\n"); - captureResult(nil, session); + sendCaptureResult(nil, session, 3); } else { - printf("captured\n"); - captureResult(img, session); + sendCaptureResult(img, session, 0); } }]; } @@ -74,10 +71,18 @@ import ( "github.com/kbinani/screenshot/internal/util" ) -//export captureResult -func captureResult(img C.CGImageRef, session C.int) { - //TODO: - fmt.Printf("captureResult: session=%d\n", session) +type captureResult struct { + img unsafe.Pointer + error int +} + + +var channel = make(chan captureResult) + +//export sendCaptureResult +func sendCaptureResult(img C.CGImageRef, session C.int, error C.int) { + result := captureResult{img: unsafe.Pointer(img), error: int(error)} + channel <- result } func Capture(x, y, width, height int) (*image.RGBA, error) { @@ -116,6 +121,8 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { for _, id := range ids { C.startCapture(id, 0); //TODO: + result := <- channel + fmt.Printf("error=%d\n", result.error) cgBounds := getCoreGraphicsCoordinateOfDisplay(id) cgIntersect := C.CGRectIntersection(cgBounds, cgCaptureBounds) if C.CGRectIsNull(cgIntersect) { @@ -125,18 +132,19 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { continue } - // CGDisplayCreateImageForRect potentially fail in case width/height is odd number. - if int(cgIntersect.size.width)%2 != 0 { - cgIntersect.size.width = C.CGFloat(int(cgIntersect.size.width) + 1) - } - if int(cgIntersect.size.height)%2 != 0 { - cgIntersect.size.height = C.CGFloat(int(cgIntersect.size.height) + 1) - } - - diIntersectDisplayLocal := C.CGRectMake(cgIntersect.origin.x-cgBounds.origin.x, - cgBounds.origin.y+cgBounds.size.height-(cgIntersect.origin.y+cgIntersect.size.height), - cgIntersect.size.width, cgIntersect.size.height) - captured := C.CompatCGDisplayCreateImageForRect(id, diIntersectDisplayLocal) + // // CGDisplayCreateImageForRect potentially fail in case width/height is odd number. + // if int(cgIntersect.size.width)%2 != 0 { + // cgIntersect.size.width = C.CGFloat(int(cgIntersect.size.width) + 1) + // } + // if int(cgIntersect.size.height)%2 != 0 { + // cgIntersect.size.height = C.CGFloat(int(cgIntersect.size.height) + 1) + // } + + // diIntersectDisplayLocal := C.CGRectMake(cgIntersect.origin.x-cgBounds.origin.x, + // cgBounds.origin.y+cgBounds.size.height-(cgIntersect.origin.y+cgIntersect.size.height), + // cgIntersect.size.width, cgIntersect.size.height) + // captured := C.CompatCGDisplayCreateImageForRect(id, diIntersectDisplayLocal) + captured := result.img if captured == nil { return nil, errors.New("cannot capture display") } From 1dd994b01860d6bd7df9f3dcbd826048394a9164 Mon Sep 17 00:00:00 2001 From: kbinani Date: Thu, 8 Aug 2024 17:35:41 +0900 Subject: [PATCH 3/7] Receive result through a dedicated channel per capture --- screenshot_darwin.go | 91 +++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 40 deletions(-) diff --git a/screenshot_darwin.go b/screenshot_darwin.go index bf4f508..3b7576f 100644 --- a/screenshot_darwin.go +++ b/screenshot_darwin.go @@ -8,35 +8,27 @@ package screenshot #include #import -static void* CompatCGDisplayCreateImageForRect(CGDirectDisplayID display, CGRect rect) { - return CGDisplayCreateImageForRect(display, rect); -} - static void CompatCGImageRelease(void* image) { CGImageRelease(image); } -static void* CompatCGImageCreateCopyWithColorSpace(void* image, CGColorSpaceRef space) { - return CGImageCreateCopyWithColorSpace((CGImageRef)image, space); -} - static void CompatCGContextDrawImage(CGContextRef c, CGRect rect, void* image) { CGContextDrawImage(c, rect, (CGImageRef)image); } -extern void sendCaptureResult(CGImageRef img, int session, int error); +extern void sendCaptureResult(CGImageRef img, uint64_t session, int error); -static void startCapture(CGDirectDisplayID id, int session) { +static void startCapture(CGDirectDisplayID id, uint64_t session, CGRect diIntersectDisplayLocal, CGColorSpaceRef colorSpace) { [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error) { @autoreleasepool { if (error) { sendCaptureResult(nil, session, 1); return; } - NSArray* displays = [content displays]; + NSArray* displays = content.displays; SCDisplay* target = nil; for (SCDisplay *display in displays) { - if ([display displayID] == id) { + if (display.displayID == id) { target = display; break; } @@ -47,13 +39,17 @@ static void startCapture(CGDirectDisplayID id, int session) { } SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init]; + config.sourceRect = diIntersectDisplayLocal; + config.width = diIntersectDisplayLocal.size.width; + config.height = diIntersectDisplayLocal.size.height; [SCScreenshotManager captureImageWithFilter:filter configuration:config completionHandler:^(CGImageRef img, NSError* error) { if (error) { sendCaptureResult(nil, session, 3); } else { - sendCaptureResult(img, session, 0); + CGImageRef copy = CGImageCreateCopyWithColorSpace(img, colorSpace); + sendCaptureResult(copy, session, 0); } }]; } @@ -66,23 +62,31 @@ import ( "errors" "image" "unsafe" - "fmt" + "sync" + "sync/atomic" "github.com/kbinani/screenshot/internal/util" ) type captureResult struct { - img unsafe.Pointer + img C.CGImageRef error int } -var channel = make(chan captureResult) +var gMut sync.Mutex +var gChannels = make(map[uint64](chan captureResult)) +var gCounter uint64 //export sendCaptureResult -func sendCaptureResult(img C.CGImageRef, session C.int, error C.int) { - result := captureResult{img: unsafe.Pointer(img), error: int(error)} - channel <- result +func sendCaptureResult(img C.CGImageRef, session C.uint64_t, error C.int) { + gMut.Lock() + channel, ok := gChannels[uint64(session)] + gMut.Unlock() + if ok { + result := captureResult{img: img, error: int(error)} + channel <- result + } } func Capture(x, y, width, height int) (*image.RGBA, error) { @@ -120,9 +124,6 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { defer C.CGColorSpaceRelease(colorSpace) for _, id := range ids { - C.startCapture(id, 0); //TODO: - result := <- channel - fmt.Printf("error=%d\n", result.error) cgBounds := getCoreGraphicsCoordinateOfDisplay(id) cgIntersect := C.CGRectIntersection(cgBounds, cgCaptureBounds) if C.CGRectIsNull(cgIntersect) { @@ -132,27 +133,37 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { continue } - // // CGDisplayCreateImageForRect potentially fail in case width/height is odd number. - // if int(cgIntersect.size.width)%2 != 0 { - // cgIntersect.size.width = C.CGFloat(int(cgIntersect.size.width) + 1) - // } - // if int(cgIntersect.size.height)%2 != 0 { - // cgIntersect.size.height = C.CGFloat(int(cgIntersect.size.height) + 1) - // } - - // diIntersectDisplayLocal := C.CGRectMake(cgIntersect.origin.x-cgBounds.origin.x, - // cgBounds.origin.y+cgBounds.size.height-(cgIntersect.origin.y+cgIntersect.size.height), - // cgIntersect.size.width, cgIntersect.size.height) - // captured := C.CompatCGDisplayCreateImageForRect(id, diIntersectDisplayLocal) - captured := result.img - if captured == nil { - return nil, errors.New("cannot capture display") + // CGDisplayCreateImageForRect potentially fail in case width/height is odd number. + if int(cgIntersect.size.width)%2 != 0 { + cgIntersect.size.width = C.CGFloat(int(cgIntersect.size.width) + 1) + } + if int(cgIntersect.size.height)%2 != 0 { + cgIntersect.size.height = C.CGFloat(int(cgIntersect.size.height) + 1) } - defer C.CompatCGImageRelease(captured) - image := C.CompatCGImageCreateCopyWithColorSpace(captured, colorSpace) + diIntersectDisplayLocal := C.CGRectMake(cgIntersect.origin.x-cgBounds.origin.x, + cgBounds.origin.y+cgBounds.size.height-(cgIntersect.origin.y+cgIntersect.size.height), + cgIntersect.size.width, cgIntersect.size.height) + + session := atomic.AddUint64(&gCounter, 1) + ch := make(chan captureResult) + + gMut.Lock() + gChannels[session] = ch + gMut.Unlock() + + C.startCapture(id, C.uint64_t(session), diIntersectDisplayLocal, colorSpace) + result := <- ch + + gMut.Lock() + delete(gChannels, session) + gMut.Unlock() + + close(ch) + + image := unsafe.Pointer(result.img) if image == nil { - return nil, errors.New("failed copying captured image") + return nil, errors.New("cannot capture display") } defer C.CompatCGImageRelease(image) From 29c67e676b6067b35362d526c0832be8ee07ef3d Mon Sep 17 00:00:00 2001 From: kbinani Date: Thu, 8 Aug 2024 17:37:21 +0900 Subject: [PATCH 4/7] Create a channel for each Capture call --- screenshot_darwin.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/screenshot_darwin.go b/screenshot_darwin.go index 3b7576f..a0949c0 100644 --- a/screenshot_darwin.go +++ b/screenshot_darwin.go @@ -123,6 +123,12 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { } defer C.CGColorSpaceRelease(colorSpace) + session := atomic.AddUint64(&gCounter, 1) + ch := make(chan captureResult) + gMut.Lock() + gChannels[session] = ch + gMut.Unlock() + for _, id := range ids { cgBounds := getCoreGraphicsCoordinateOfDisplay(id) cgIntersect := C.CGRectIntersection(cgBounds, cgCaptureBounds) @@ -145,22 +151,10 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { cgBounds.origin.y+cgBounds.size.height-(cgIntersect.origin.y+cgIntersect.size.height), cgIntersect.size.width, cgIntersect.size.height) - session := atomic.AddUint64(&gCounter, 1) - ch := make(chan captureResult) - - gMut.Lock() - gChannels[session] = ch - gMut.Unlock() C.startCapture(id, C.uint64_t(session), diIntersectDisplayLocal, colorSpace) result := <- ch - gMut.Lock() - delete(gChannels, session) - gMut.Unlock() - - close(ch) - image := unsafe.Pointer(result.img) if image == nil { return nil, errors.New("cannot capture display") @@ -172,6 +166,12 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { C.CompatCGContextDrawImage(ctx, cgDrawRect, image) } + gMut.Lock() + delete(gChannels, session) + gMut.Unlock() + + close(ch) + i := 0 for iy := 0; iy < height; iy++ { j := i From 85677d8682df0907b41449a2470ecf564e106ec2 Mon Sep 17 00:00:00 2001 From: kbinani Date: Thu, 8 Aug 2024 17:54:39 +0900 Subject: [PATCH 5/7] Change tab to spaces for objc code --- screenshot_darwin.go | 85 +++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/screenshot_darwin.go b/screenshot_darwin.go index a0949c0..76cd464 100644 --- a/screenshot_darwin.go +++ b/screenshot_darwin.go @@ -5,55 +5,54 @@ package screenshot /* #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework CoreGraphics -framework CoreFoundation -framework ScreenCaptureKit -#include +#import #import -static void CompatCGImageRelease(void* image) { - CGImageRelease(image); +static void compatCGImageRelease(void* image) { + CGImageRelease(image); } -static void CompatCGContextDrawImage(CGContextRef c, CGRect rect, void* image) { - CGContextDrawImage(c, rect, (CGImageRef)image); +static void compatCGContextDrawImage(CGContextRef c, CGRect rect, void* image) { + CGContextDrawImage(c, rect, (CGImageRef)image); } extern void sendCaptureResult(CGImageRef img, uint64_t session, int error); static void startCapture(CGDirectDisplayID id, uint64_t session, CGRect diIntersectDisplayLocal, CGColorSpaceRef colorSpace) { - [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error) { - @autoreleasepool { - if (error) { - sendCaptureResult(nil, session, 1); - return; - } - NSArray* displays = content.displays; - SCDisplay* target = nil; - for (SCDisplay *display in displays) { - if (display.displayID == id) { - target = display; - break; - } - } - if (!target) { - sendCaptureResult(nil, session, 2); - return; - } - SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; - SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init]; - config.sourceRect = diIntersectDisplayLocal; - config.width = diIntersectDisplayLocal.size.width; - config.height = diIntersectDisplayLocal.size.height; - [SCScreenshotManager captureImageWithFilter:filter - configuration:config - completionHandler:^(CGImageRef img, NSError* error) { - if (error) { - sendCaptureResult(nil, session, 3); - } else { - CGImageRef copy = CGImageCreateCopyWithColorSpace(img, colorSpace); - sendCaptureResult(copy, session, 0); - } - }]; - } - }]; + [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error) { + @autoreleasepool { + if (error) { + sendCaptureResult(nil, session, 1); + return; + } + SCDisplay* target = nil; + for (SCDisplay *display in content.displays) { + if (display.displayID == id) { + target = display; + break; + } + } + if (!target) { + sendCaptureResult(nil, session, 2); + return; + } + SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; + SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init]; + config.sourceRect = diIntersectDisplayLocal; + config.width = diIntersectDisplayLocal.size.width; + config.height = diIntersectDisplayLocal.size.height; + [SCScreenshotManager captureImageWithFilter:filter + configuration:config + completionHandler:^(CGImageRef img, NSError* error) { + if (error) { + sendCaptureResult(nil, session, 3); + } else { + CGImageRef copy = CGImageCreateCopyWithColorSpace(img, colorSpace); + sendCaptureResult(copy, session, 0); + } + }]; + } + }]; } */ import "C" @@ -73,7 +72,6 @@ type captureResult struct { error int } - var gMut sync.Mutex var gChannels = make(map[uint64](chan captureResult)) var gCounter uint64 @@ -151,7 +149,6 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { cgBounds.origin.y+cgBounds.size.height-(cgIntersect.origin.y+cgIntersect.size.height), cgIntersect.size.width, cgIntersect.size.height) - C.startCapture(id, C.uint64_t(session), diIntersectDisplayLocal, colorSpace) result := <- ch @@ -159,11 +156,11 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { if image == nil { return nil, errors.New("cannot capture display") } - defer C.CompatCGImageRelease(image) + defer C.compatCGImageRelease(image) cgDrawRect := C.CGRectMake(cgIntersect.origin.x-cgCaptureBounds.origin.x, cgIntersect.origin.y-cgCaptureBounds.origin.y, cgIntersect.size.width, cgIntersect.size.height) - C.CompatCGContextDrawImage(ctx, cgDrawRect, image) + C.compatCGContextDrawImage(ctx, cgDrawRect, image) } gMut.Lock() From f6b7c739355ebb2694e36245c615e4cdc4cf7319 Mon Sep 17 00:00:00 2001 From: kbinani Date: Thu, 8 Aug 2024 23:46:21 +0900 Subject: [PATCH 6/7] Use semaphore to synchronize --- screenshot_darwin.go | 81 +++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 50 deletions(-) diff --git a/screenshot_darwin.go b/screenshot_darwin.go index 76cd464..c5cf2b4 100644 --- a/screenshot_darwin.go +++ b/screenshot_darwin.go @@ -3,10 +3,14 @@ package screenshot /* +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > MAC_OS_VERSION_14_4 #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework CoreGraphics -framework CoreFoundation -framework ScreenCaptureKit -#import -#import +#include +#else +#cgo LDFLAGS: -framework CoreGraphics -framework CoreFoundation +#endif +#include static void compatCGImageRelease(void* image) { CGImageRelease(image); @@ -16,13 +20,14 @@ static void compatCGContextDrawImage(CGContextRef c, CGRect rect, void* image) { CGContextDrawImage(c, rect, (CGImageRef)image); } -extern void sendCaptureResult(CGImageRef img, uint64_t session, int error); - -static void startCapture(CGDirectDisplayID id, uint64_t session, CGRect diIntersectDisplayLocal, CGColorSpaceRef colorSpace) { +static void* capture(CGDirectDisplayID id, CGRect diIntersectDisplayLocal, CGColorSpaceRef colorSpace) { +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > MAC_OS_VERSION_14_4 + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block CGImageRef result = nil; [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error) { @autoreleasepool { if (error) { - sendCaptureResult(nil, session, 1); + dispatch_semaphore_signal(semaphore); return; } SCDisplay* target = nil; @@ -33,7 +38,7 @@ static void startCapture(CGDirectDisplayID id, uint64_t session, CGRect diInters } } if (!target) { - sendCaptureResult(nil, session, 2); + dispatch_semaphore_signal(semaphore); return; } SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; @@ -44,15 +49,27 @@ static void startCapture(CGDirectDisplayID id, uint64_t session, CGRect diInters [SCScreenshotManager captureImageWithFilter:filter configuration:config completionHandler:^(CGImageRef img, NSError* error) { - if (error) { - sendCaptureResult(nil, session, 3); - } else { - CGImageRef copy = CGImageCreateCopyWithColorSpace(img, colorSpace); - sendCaptureResult(copy, session, 0); + if (!error) { + result = CGImageCreateCopyWithColorSpace(img, colorSpace); } + dispatch_semaphore_signal(semaphore); }]; } }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + return result; +#else + CGImageRef img = CGDisplayCreateImageForRect(id, diIntersectDisplayLocal); + if (!img) { + return nil; + } + CGImageRef copy = CGImageCreateCopyWithColorSpace(img, colorSpace); + CGImageRelease(img); + if (!copy) { + return nil; + } + return copy; +#endif } */ import "C" @@ -61,32 +78,10 @@ import ( "errors" "image" "unsafe" - "sync" - "sync/atomic" "github.com/kbinani/screenshot/internal/util" ) -type captureResult struct { - img C.CGImageRef - error int -} - -var gMut sync.Mutex -var gChannels = make(map[uint64](chan captureResult)) -var gCounter uint64 - -//export sendCaptureResult -func sendCaptureResult(img C.CGImageRef, session C.uint64_t, error C.int) { - gMut.Lock() - channel, ok := gChannels[uint64(session)] - gMut.Unlock() - if ok { - result := captureResult{img: img, error: int(error)} - channel <- result - } -} - func Capture(x, y, width, height int) (*image.RGBA, error) { if width <= 0 || height <= 0 { return nil, errors.New("width or height should be > 0") @@ -121,12 +116,6 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { } defer C.CGColorSpaceRelease(colorSpace) - session := atomic.AddUint64(&gCounter, 1) - ch := make(chan captureResult) - gMut.Lock() - gChannels[session] = ch - gMut.Unlock() - for _, id := range ids { cgBounds := getCoreGraphicsCoordinateOfDisplay(id) cgIntersect := C.CGRectIntersection(cgBounds, cgCaptureBounds) @@ -149,10 +138,8 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { cgBounds.origin.y+cgBounds.size.height-(cgIntersect.origin.y+cgIntersect.size.height), cgIntersect.size.width, cgIntersect.size.height) - C.startCapture(id, C.uint64_t(session), diIntersectDisplayLocal, colorSpace) - result := <- ch - - image := unsafe.Pointer(result.img) + result := C.capture(id, diIntersectDisplayLocal, colorSpace) + image := unsafe.Pointer(result) if image == nil { return nil, errors.New("cannot capture display") } @@ -163,12 +150,6 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { C.compatCGContextDrawImage(ctx, cgDrawRect, image) } - gMut.Lock() - delete(gChannels, session) - gMut.Unlock() - - close(ch) - i := 0 for iy := 0; iy < height; iy++ { j := i From 5310ac5ed254ae298c583aa255b0fa6bf91642c8 Mon Sep 17 00:00:00 2001 From: kbinani Date: Fri, 9 Aug 2024 09:10:46 +0900 Subject: [PATCH 7/7] Change return type of capture function --- screenshot_darwin.go | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/screenshot_darwin.go b/screenshot_darwin.go index c5cf2b4..17dd78f 100644 --- a/screenshot_darwin.go +++ b/screenshot_darwin.go @@ -12,15 +12,7 @@ package screenshot #endif #include -static void compatCGImageRelease(void* image) { - CGImageRelease(image); -} - -static void compatCGContextDrawImage(CGContextRef c, CGRect rect, void* image) { - CGContextDrawImage(c, rect, (CGImageRef)image); -} - -static void* capture(CGDirectDisplayID id, CGRect diIntersectDisplayLocal, CGColorSpaceRef colorSpace) { +static CGImageRef capture(CGDirectDisplayID id, CGRect diIntersectDisplayLocal, CGColorSpaceRef colorSpace) { #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > MAC_OS_VERSION_14_4 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block CGImageRef result = nil; @@ -138,16 +130,15 @@ func Capture(x, y, width, height int) (*image.RGBA, error) { cgBounds.origin.y+cgBounds.size.height-(cgIntersect.origin.y+cgIntersect.size.height), cgIntersect.size.width, cgIntersect.size.height) - result := C.capture(id, diIntersectDisplayLocal, colorSpace) - image := unsafe.Pointer(result) - if image == nil { + image := C.capture(id, diIntersectDisplayLocal, colorSpace) + if unsafe.Pointer(image) == nil { return nil, errors.New("cannot capture display") } - defer C.compatCGImageRelease(image) + defer C.CGImageRelease(image) cgDrawRect := C.CGRectMake(cgIntersect.origin.x-cgCaptureBounds.origin.x, cgIntersect.origin.y-cgCaptureBounds.origin.y, cgIntersect.size.width, cgIntersect.size.height) - C.compatCGContextDrawImage(ctx, cgDrawRect, image) + C.CGContextDrawImage(ctx, cgDrawRect, image) } i := 0