1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

macOS: Use ScreenCaptureKit for taking window screenshots

- Required when the deployment target is macOS 15+
- As the ScreenCaptureKit framework isn't available on all supported versions
of Xcode it's dynamically loaded
This commit is contained in:
Anthony Nicholls 2024-07-24 14:51:08 +01:00
parent 49ed0f022d
commit 10cf5a98a2
2 changed files with 95 additions and 29 deletions

View file

@ -70,6 +70,10 @@
#import <IOKit/pwr_mgt/IOPMLib.h>
#import <MetalKit/MetalKit.h>
#if defined (MAC_OS_VERSION_14_4) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_14_4
#import <ScreenCaptureKit/ScreenCaptureKit.h>
#endif
#elif JUCE_IOS
#if JUCE_PUSH_NOTIFICATIONS
#import <UserNotifications/UserNotifications.h>

View file

@ -487,7 +487,6 @@ void Displays::findDisplays (const float masterScale)
static void selectImageForDrawing (const Image& image)
{
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithCGContext: juce_getImageContext (image)
flipped: false]];
}
@ -520,37 +519,100 @@ static Image createNSWindowSnapshot (NSWindow* nsWindow)
{
JUCE_AUTORELEASEPOOL
{
// CGWindowListCreateImage is replaced by functions in the ScreenCaptureKit framework, but
// that framework is only available from macOS 12.3 onwards.
// A suitable @available check should be added once the minimum build OS is 12.3 or greater,
// so that ScreenCaptureKit can be weak-linked.
#if defined (MAC_OS_VERSION_14_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_14_0
const auto createImageFromCGImage = [&] (CGImageRef cgImage)
{
jassert (cgImage != nullptr);
const auto width = CGImageGetWidth (cgImage);
const auto height = CGImageGetHeight (cgImage);
const auto cgRect = CGRectMake (0, 0, (CGFloat) width, (CGFloat) height);
const Image image (Image::ARGB, (int) width, (int) height, true);
CGContextDrawImage (juce_getImageContext (image), cgRect, cgImage);
return image;
};
#if defined (MAC_OS_VERSION_14_4) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_14_4
if (dlopen ("/System/Library/Frameworks/ScreenCaptureKit.framework/ScreenCaptureKit", RTLD_LAZY) == nullptr)
{
DBG (dlerror());
jassertfalse;
return {};
}
std::promise<Image> result;
const auto windowId = nsWindow.windowNumber;
const auto windowRect = [nsWindow.screen convertRectToBacking: nsWindow.frame].size;
const auto onSharableContent = [&] (SCShareableContent* content, NSError* contentError)
{
if (contentError != nullptr)
{
jassertfalse;
result.set_value (Image{});
return;
}
const auto window = [&]() -> SCWindow*
{
for (SCWindow* w in content.windows)
if (w.windowID == windowId)
return w;
return nullptr;
}();
if (window == nullptr)
{
jassertfalse;
result.set_value (Image{});
return;
}
Class contentFilterClass = NSClassFromString (@"SCContentFilter");
SCContentFilter* filter = [[[contentFilterClass alloc] initWithDesktopIndependentWindow: window] autorelease];
Class streamConfigurationClass = NSClassFromString (@"SCStreamConfiguration");
SCStreamConfiguration* config = [[[streamConfigurationClass alloc] init] autorelease];
config.colorSpaceName = kCGColorSpaceSRGB;
config.showsCursor = NO;
config.ignoreShadowsSingleWindow = YES;
config.captureResolution = SCCaptureResolutionBest;
config.ignoreGlobalClipSingleWindow = YES;
config.includeChildWindows = NO;
config.width = (size_t) windowRect.width;
config.height = (size_t) windowRect.height;
const auto onScreenshot = [&] (CGImageRef screenshot, NSError* screenshotError)
{
jassert (screenshotError == nullptr);
result.set_value (screenshotError == nullptr ? createImageFromCGImage (screenshot) : Image{});
};
Class screenshotManagerClass = NSClassFromString (@"SCScreenshotManager");
[screenshotManagerClass captureImageWithFilter: filter
configuration: config
completionHandler: onScreenshot];
};
Class shareableContentClass = NSClassFromString (@"SCShareableContent");
[shareableContentClass getCurrentProcessShareableContentWithCompletionHandler: onSharableContent];
return result.get_future().get();
#else
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
#define JUCE_DEPRECATION_IGNORED 1
#endif
CGImageRef screenShot = CGWindowListCreateImage (CGRectNull,
kCGWindowListOptionIncludingWindow,
(CGWindowID) [nsWindow windowNumber],
kCGWindowImageBoundsIgnoreFraming);
#if JUCE_DEPRECATION_IGNORED
return createImageFromCGImage ((CGImageRef) CFAutorelease (CGWindowListCreateImage (CGRectNull,
kCGWindowListOptionIncludingWindow,
(CGWindowID) [nsWindow windowNumber],
kCGWindowImageBoundsIgnoreFraming)));
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#undef JUCE_DEPRECATION_IGNORED
#endif
NSBitmapImageRep* bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage: screenShot];
Image result (Image::ARGB, (int) [bitmapRep size].width, (int) [bitmapRep size].height, true);
selectImageForDrawing (result);
[bitmapRep drawAtPoint: NSMakePoint (0, 0)];
releaseImageAfterDrawing();
[bitmapRep release];
CGImageRelease (screenShot);
return result;
}
}