From 2bdc93281cd32bc205d26a3c368c199ba45e4c5a Mon Sep 17 00:00:00 2001 From: Bruno Lopes Date: Sat, 28 Mar 2026 20:39:57 -0300 Subject: [PATCH] build, libgimpwidgets, meson: Add ScreenCaptureKit support Inspired by https://github.com/neutralinojs/neutralinojs/pull/1477/changes which was found by Alex. This makes possible to build GIMP on macOS 12+ targets, which is useful for local builds. We, however, will keep targeting macOS 11. --- build/macos/1_build-deps-macports.sh | 3 +- libgimpwidgets/gimppickbutton-quartz.c | 73 ++++++++++++++++++++++++++ libgimpwidgets/meson.build | 2 +- meson.build | 2 + 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/build/macos/1_build-deps-macports.sh b/build/macos/1_build-deps-macports.sh index f06193588b..a9a4353a2c 100644 --- a/build/macos/1_build-deps-macports.sh +++ b/build/macos/1_build-deps-macports.sh @@ -28,8 +28,7 @@ if [ -z "$OPT_PREFIX" ]; then exit 1 fi fi -#FIXME: remove `echo "$CI_JOB_NAME" | grep -q 'gimp'` after ScreenCaptureKit support -if { [ "$OPT_PREFIX" != '/opt/local' ] && [ "$OPT_PREFIX" != '/opt/homebrew' ] } || echo "$CI_JOB_NAME" | grep -q 'gimp'; then +if [ "$OPT_PREFIX" != '/opt/local' ] && [ "$OPT_PREFIX" != '/opt/homebrew' ]; then export MACOSX_DEPLOYMENT_TARGET=$(awk '/LSMinimumSystemVersion/{found=1} found && //{gsub(/.*|<\/string>.*/, ""); print; exit}' build/macos/Info.plist) echo "macosx_deployment_target ${MACOSX_DEPLOYMENT_TARGET}" | tee -a ${OPT_PREFIX}/etc/macports/macports.conf >/dev/null 2>&1 || true sed -i .bak "s/^#build_arch.*/build_arch $(uname -m)/" "${OPT_PREFIX}/etc/macports/macports.conf" >/dev/null 2>&1 || true diff --git a/libgimpwidgets/gimppickbutton-quartz.c b/libgimpwidgets/gimppickbutton-quartz.c index 232ee55592..60f7f5b67c 100644 --- a/libgimpwidgets/gimppickbutton-quartz.c +++ b/libgimpwidgets/gimppickbutton-quartz.c @@ -32,6 +32,9 @@ #include /* For virtual key codes ... */ #include #endif +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 120300 +#import +#endif @interface GimpPickWindowController : NSObject { @@ -177,10 +180,69 @@ rect = [self.window convertRectToScreen:rect]; rect.origin.y = [[[NSScreen screens] objectAtIndex:0] frame].size.height - rect.origin.y; + root_image_ref = nil; + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 120300 + /* ScreenCaptureKit is asyncronous */ + __block CGImageRef captured_image = nil; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent *monitor_content, NSError *monitor_error) { + if (monitor_error == nil && monitor_content != nil) + { + /* find the monitor that contains the rect.origin where the user clicked */ + SCDisplay *target_monitor = monitor_content.displays.firstObject; + for (SCDisplay *monitor in monitor_content.displays) + { + if (CGRectContainsPoint (monitor.frame, rect.origin)) + { + target_monitor = monitor; + break; + } + } + + /* get the whole monitor, without scaling or anti-aliasing effects */ + SCContentFilter *monitor_size = [[SCContentFilter alloc] initWithDisplay:target_monitor excludingWindows:@[]]; + SCStreamConfiguration *monitor_config = [[SCStreamConfiguration alloc] init]; + monitor_config.scalesToFit = NO; + + /* take the actual "screenshot" on the monitor */ + [SCScreenshotManager captureImageWithFilter:monitor_size configuration:monitor_config + completionHandler:^(CGImageRef captured_monitor, NSError *captured_error) { + if (captured_error == nil && captured_monitor != NULL) + { + CGRect captured_crop = CGRectMake (rect.origin.x - target_monitor.frame.origin.x, + rect.origin.y - target_monitor.frame.origin.y, + rect.size.width, rect.size.height); + captured_image = CGImageCreateWithImageInRect (captured_monitor, captured_crop); + } + dispatch_semaphore_signal (semaphore); + }]; + + [monitor_size release]; + [monitor_config release]; + } + else + { + dispatch_semaphore_signal (semaphore); + } + }]; + + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC)); + root_image_ref = captured_image; + +#else root_image_ref = CGWindowListCreateImage (rect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault); +#endif + if (root_image_ref == NULL) + { + g_warning ("Failed to capture screen pixels. Permission denied or DRM protected content."); + return; + } + pixel_data = CGDataProviderCopyData (CGImageGetDataProvider (root_image_ref)); data = CFDataGetBytePtr (pixel_data); @@ -441,6 +503,17 @@ _gimp_pick_button_quartz_pick (GimpPickButton *button) pool = [[NSAutoreleasePool alloc] init]; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 120300 + /* Needed for ScreenCaptureKit which is asyncronous */ + if (!CGPreflightScreenCaptureAccess()) + { + CGRequestScreenCaptureAccess(); + + [pool release]; + return; + } +#endif + controller = [[GimpPickWindowController alloc] initWithButton:button]; [pool release]; diff --git a/libgimpwidgets/meson.build b/libgimpwidgets/meson.build index 7a70085ec7..7893c82d97 100644 --- a/libgimpwidgets/meson.build +++ b/libgimpwidgets/meson.build @@ -207,7 +207,7 @@ libgimpwidgets = library('gimpwidgets-'+ gimp_api_version, libgimpwidgets_sources, include_directories: rootInclude, dependencies: [ - gegl, gexiv2, gtk3, lcms, math, mscms + gegl, gexiv2, gtk3, lcms, math, mscms, screencapturekit ], c_args: [ '-DG_LOG_DOMAIN="LibGimpWidgets"', '-DGIMP_WIDGETS_COMPILATION', ], link_with: [ diff --git a/meson.build b/meson.build index 8d8f6e76d0..0450ba4600 100644 --- a/meson.build +++ b/meson.build @@ -432,6 +432,8 @@ dbghelp = platform_windows ? cc.find_library('dbghelp') : no_dep winsock = platform_windows ? cc.find_library('ws2_32') : no_dep mscms = platform_windows ? cc.find_library('mscms') : no_dep +screencapturekit = platform_osx ? dependency('appleframeworks', modules: 'ScreenCaptureKit', required: false) : no_dep + atk_minver = '2.4.0' atk = dependency('atk', version: '>='+atk_minver) babl_minver = '0.1.118'