From 7d98413d4621b964105f346d9e5a2361a0ce9bfc Mon Sep 17 00:00:00 2001 From: ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:52:37 +0200 Subject: [PATCH] macOS 15.0 Sequoia: fix capture of window titlebars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous procedure using CGWindowListCreateImageFromArray() is obsoleted in macOS 15.0 . The new procedure requires an additional framework: ScreenCaptureKit; FLTK uses it only for macOS ≥ 15.0 --- CHANGES.txt | 4 +-- CMake/setup.cmake | 18 +++++++--- FL/mac.H | 3 ++ configure.ac | 3 ++ src/Fl_Widget_Surface.cxx | 2 +- src/Fl_cocoa.mm | 74 +++++++++++++++++++++++++++++++++++++-- 6 files changed, 93 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 282830c8d..3189d3056 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -186,8 +186,8 @@ Changes in FLTK 1.4.0 Released: Feb ?? 2024 Other Improvements - (add new items here) - - Added support of macOS 14.0 "Sonoma", 13.0 "Ventura", 12.0 "Monterey", - and 11.0 "Big Sur". + - Added support of macOS 15.0 beta "Sequoia", 14.0 "Sonoma", + 13.0 "Ventura", 12.0 "Monterey", and 11.0 "Big Sur". - Added macOS support for the arm64 architecture since 11.0 (Big Sur). - Added support for macOS 10.15 "Catalina" - Added support for macOS 10.14 "Mojave": all drawing to windows is done diff --git a/CMake/setup.cmake b/CMake/setup.cmake index 5419fb7f2..04cc5d90a 100644 --- a/CMake/setup.cmake +++ b/CMake/setup.cmake @@ -123,23 +123,31 @@ if(APPLE) endif(NOT(${CMAKE_SYSTEM_VERSION} VERSION_LESS 17.0.0)) else() set(FLTK_COCOA_FRAMEWORKS "-framework Cocoa") - set(UTI_CONDITION FALSE) + set(UTI_CONDITION FALSE) # TRUE when framework UniformTypeIdentifiers is used + set(SCK_CONDITION FALSE) # TRUE when framework ScreenCaptureKit is used string(LENGTH "${CMAKE_OSX_DEPLOYMENT_TARGET}" TARGET_LEN) string(LENGTH "${CMAKE_SYSTEM_VERSION}" SDK_LEN) if(TARGET_LEN GREATER 0) if( ${CMAKE_OSX_DEPLOYMENT_TARGET} VERSION_GREATER_EQUAL 11.0) set(UTI_CONDITION TRUE) endif() + if( ${CMAKE_OSX_DEPLOYMENT_TARGET} VERSION_GREATER_EQUAL 15.0) + set(SCK_CONDITION TRUE) + endif() elseif(SDK_LEN GREATER 0) if( ${CMAKE_SYSTEM_VERSION} VERSION_GREATER_EQUAL 20.0 ) set(UTI_CONDITION TRUE) endif() - endif() - if(UTI_CONDITION) # a.k.a. macOS version ≥ 11.0 - if (NOT (CMAKE_OSX_ARCHITECTURES STREQUAL "ppc" OR CMAKE_OSX_ARCHITECTURES STREQUAL "i386")) - list(APPEND FLTK_COCOA_FRAMEWORKS "-framework UniformTypeIdentifiers") + if( ${CMAKE_SYSTEM_VERSION} VERSION_GREATER_EQUAL 24.0 ) + set(SCK_CONDITION TRUE) endif() endif() + if(UTI_CONDITION) # a.k.a. macOS version ≥ 11.0 + list(APPEND FLTK_COCOA_FRAMEWORKS "-framework UniformTypeIdentifiers") + if(SCK_CONDITION) # a.k.a. macOS version ≥ 15.0 + list(APPEND FLTK_COCOA_FRAMEWORKS "-framework ScreenCaptureKit") + endif(SCK_CONDITION) + endif(UTI_CONDITION) endif(FLTK_BACKEND_X11) endif(APPLE) diff --git a/FL/mac.H b/FL/mac.H index 8982057a1..b0a2b84f5 100644 --- a/FL/mac.H +++ b/FL/mac.H @@ -99,6 +99,9 @@ typedef class FLWindow *Window; // pointer to the FLWindow objective-c class #ifndef MAC_OS_VERSION_14_0 #define MAC_OS_VERSION_14_0 140000 #endif +#ifndef MAC_OS_VERSION_15_0 +#define MAC_OS_VERSION_15_0 150000 +#endif #ifndef NSINTEGER_DEFINED // appears with 10.5 in NSObjCRuntime.h diff --git a/configure.ac b/configure.ac index 8b22fc9af..c3089eef5 100644 --- a/configure.ac +++ b/configure.ac @@ -1012,6 +1012,9 @@ AS_CASE([$host_os_gui], [cygwin* | mingw*], [ AS_IF([test $macosversion_maj -ge 11], [ LIBS="$LIBS -framework UniformTypeIdentifiers" ]) + AS_IF([test $macosversion_maj -ge 15], [ + LIBS="$LIBS -framework ScreenCaptureKit" + ]) AS_IF([test x$have_pthread = xyes], [ AC_DEFINE([HAVE_PTHREAD]) diff --git a/src/Fl_Widget_Surface.cxx b/src/Fl_Widget_Surface.cxx index f489897ba..98cc736ad 100644 --- a/src/Fl_Widget_Surface.cxx +++ b/src/Fl_Widget_Surface.cxx @@ -198,7 +198,7 @@ int Fl_Widget_Surface::printable_rect(int *w, int *h) {return 1;} void Fl_Widget_Surface::draw_decorated_window(Fl_Window *win, int win_offset_x, int win_offset_y) { Fl_RGB_Image *top=0, *left=0, *bottom=0, *right=0; - if (win->border() && !win->parent()) { + if (win->shown() && win->border() && !win->parent()) { Fl_Window_Driver::driver(win)->capture_titlebar_and_borders(top, left, bottom, right); } bool need_push = !is_current(); diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 3283143c2..08be3aef7 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -1,7 +1,7 @@ // // macOS-Cocoa specific code for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2023 by Bill Spitzak and others. +// Copyright 1998-2024 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -49,6 +49,9 @@ extern "C" { #include #import +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_15_0 +# import +#endif // #define DEBUG_SELECT // UNCOMMENT FOR SELECT()/THREAD DEBUGGING #ifdef DEBUG_SELECT @@ -4607,20 +4610,85 @@ int Fl_Cocoa_Window_Driver::decorated_h() return h() + bt/s; } +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_15_0 + +// Requires -framework ScreenCaptureKit and used by FLTK for macOS ≥ 15.0 +static CGImageRef capture_titlebar_macOS15(NSWindow *nswin) { + __block CGImageRef capture = NULL; + __block BOOL capture_err = NO; + void (^block_to_stop_main_loop)(void) = ^{ CFRunLoopStop(CFRunLoopGetMain()); }; + CGWindowID target_id = [nswin windowNumber]; + NSRect r = [nswin frame]; + int W = r.size.width, H = r.size.height; + [SCShareableContent getCurrentProcessShareableContentWithCompletionHandler: // macOS 14.4 + ^(SCShareableContent *shareableContent, NSError *error) { + SCWindow *scwin = nil; + if (!error) { + NSEnumerator *enumerator = [[shareableContent windows] objectEnumerator]; + while ((scwin = (SCWindow*)[enumerator nextObject]) != nil) { + if ([scwin windowID] == target_id) { + break; + } + } + } + if (!scwin) { + capture_err = YES; + dispatch_async(dispatch_get_main_queue(), block_to_stop_main_loop); + return; + } + SCContentFilter *filter = [[[SCContentFilter alloc] initWithDesktopIndependentWindow:scwin] autorelease]; + int s = (int)[filter pointPixelScale]; + SCStreamConfiguration *config = [[[SCStreamConfiguration alloc] init] autorelease]; + [config setIgnoreShadowsSingleWindow:YES]; + [config setWidth:W*s]; + [config setHeight:H*s]; + [config setIncludeChildWindows:NO]; // macOS 14.2 + [SCScreenshotManager captureImageWithFilter:filter + configuration:config + completionHandler:^(CGImageRef sampleBuffer, NSError *error) { + if (error) capture_err = YES; + else { + capture = sampleBuffer; + CGImageRetain(capture); + } + dispatch_async(dispatch_get_main_queue(), block_to_stop_main_loop); + } + ]; + } + ]; + // run the main loop until the 1 or 2 blocks above have finished and have stopped the loop + while (!capture_err && !capture) CFRunLoopRun(); + if (capture_err) return NULL; + int bt = [nswin frame].size.height - [[nswin contentView] frame].size.height; + int s = CGImageGetWidth(capture) / W; + CGRect cgr = CGRectMake(0, 0, W * s, bt * s); + CGImageRef title_bar = CGImageCreateWithImageInRect(capture, cgr); + CGImageRelease(capture); + return title_bar; +} +#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_15_0 + + void Fl_Cocoa_Window_Driver::draw_titlebar_to_context(CGContextRef gc, int w, int h) { FLWindow *nswin = fl_xid(pWindow); if ([nswin canBecomeMainWindow]) [nswin makeMainWindow]; [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:nil inMode:NSDefaultRunLoopMode dequeue:NO]; - CGImageRef img; + CGImageRef img = NULL; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - if (fl_mac_os_version >= 100600) { // verified OK from 10.6 +# if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_15_0 + if (fl_mac_os_version >= 150000) img = capture_titlebar_macOS15(nswin); + else +# endif + if (fl_mac_os_version >= 100600) { // verified OK from 10.6 NSInteger win_id = [nswin windowNumber]; CFArrayRef array = CFArrayCreate(NULL, (const void**)&win_id, 1, NULL); CGRect rr = NSRectToCGRect([nswin frame]); rr.origin.y = CGDisplayBounds(CGMainDisplayID()).size.height - (rr.origin.y + rr.size.height); rr.size.height = h; +# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_15_0 img = CGWindowListCreateImageFromArray(rr, array, kCGWindowImageBoundsIgnoreFraming); // 10.5 +# endif CFRelease(array); } else #endif