Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/juce_gui_basics/juce_gui_basics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,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>
Expand Down
111 changes: 87 additions & 24 deletions modules/juce_gui_basics/native/juce_Windowing_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -520,37 +520,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
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
#define JUCE_DEPRECATION_IGNORED 1
#endif
const auto createImageFromCGImage = [&] (CGImageRef cgImage)
{
jassert (cgImage != nullptr);

CGImageRef screenShot = CGWindowListCreateImage (CGRectNull,
kCGWindowListOptionIncludingWindow,
(CGWindowID) [nsWindow windowNumber],
kCGWindowImageBoundsIgnoreFraming);
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);

#if JUCE_DEPRECATION_IGNORED
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#undef JUCE_DEPRECATION_IGNORED
#endif
CGContextDrawImage (juce_getImageContext (image), cgRect, cgImage);

NSBitmapImageRep* bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage: screenShot];
return image;
};

Image result (Image::ARGB, (int) [bitmapRep size].width, (int) [bitmapRep size].height, true);
#if defined (MAC_OS_VERSION_14_4) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_14_4

selectImageForDrawing (result);
[bitmapRep drawAtPoint: NSMakePoint (0, 0)];
releaseImageAfterDrawing();
if (dlopen ("/System/Library/Frameworks/ScreenCaptureKit.framework/ScreenCaptureKit", RTLD_LAZY) == nullptr)
{
DBG (dlerror());
jassertfalse;
return {};
}

[bitmapRep release];
CGImageRelease (screenShot);
std::promise<Image> result;

return 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")
return createImageFromCGImage ((CGImageRef) CFAutorelease (CGWindowListCreateImage (CGRectNull,
kCGWindowListOptionIncludingWindow,
(CGWindowID) [nsWindow windowNumber],
kCGWindowImageBoundsIgnoreFraming)));
JUCE_END_IGNORE_WARNINGS_GCC_LIKE

#endif
}
}

Expand Down