The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

686 lines
25KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. void LookAndFeel::playAlertSound()
  21. {
  22. NSBeep();
  23. }
  24. //==============================================================================
  25. class OSXMessageBox : private AsyncUpdater
  26. {
  27. public:
  28. OSXMessageBox (const MessageBoxOptions& opts,
  29. std::unique_ptr<ModalComponentManager::Callback>&& c)
  30. : options (opts), callback (std::move (c))
  31. {
  32. }
  33. int getResult() const
  34. {
  35. switch (getRawResult())
  36. {
  37. case NSAlertFirstButtonReturn: return 0;
  38. case NSAlertSecondButtonReturn: return 1;
  39. case NSAlertThirdButtonReturn: return 2;
  40. default: break;
  41. }
  42. jassertfalse;
  43. return 0;
  44. }
  45. using AsyncUpdater::triggerAsyncUpdate;
  46. private:
  47. void handleAsyncUpdate() override
  48. {
  49. auto result = getResult();
  50. if (callback != nullptr)
  51. callback->modalStateFinished (result);
  52. delete this;
  53. }
  54. static void addButton (NSAlert* alert, const String& button)
  55. {
  56. if (! button.isEmpty())
  57. [alert addButtonWithTitle: juceStringToNS (button)];
  58. }
  59. NSInteger getRawResult() const
  60. {
  61. NSAlert* alert = [[[NSAlert alloc] init] autorelease];
  62. [alert setMessageText: juceStringToNS (options.getTitle())];
  63. [alert setInformativeText: juceStringToNS (options.getMessage())];
  64. [alert setAlertStyle: options.getIconType() == MessageBoxIconType::WarningIcon ? NSAlertStyleCritical
  65. : NSAlertStyleInformational];
  66. const auto button1Text = options.getButtonText (0);
  67. addButton (alert, button1Text.isEmpty() ? "OK" : button1Text);
  68. addButton (alert, options.getButtonText (1));
  69. addButton (alert, options.getButtonText (2));
  70. return [alert runModal];
  71. }
  72. MessageBoxOptions options;
  73. std::unique_ptr<ModalComponentManager::Callback> callback;
  74. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXMessageBox)
  75. };
  76. static int showDialog (const MessageBoxOptions& options,
  77. ModalComponentManager::Callback* callbackIn,
  78. AlertWindowMappings::MapFn mapFn)
  79. {
  80. #if JUCE_MODAL_LOOPS_PERMITTED
  81. if (callbackIn == nullptr)
  82. {
  83. jassert (mapFn != nullptr);
  84. OSXMessageBox messageBox (options, nullptr);
  85. return mapFn (messageBox.getResult());
  86. }
  87. #endif
  88. auto messageBox = std::make_unique<OSXMessageBox> (options,
  89. AlertWindowMappings::getWrappedCallback (callbackIn, mapFn));
  90. messageBox->triggerAsyncUpdate();
  91. messageBox.release();
  92. return 0;
  93. }
  94. #if JUCE_MODAL_LOOPS_PERMITTED
  95. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType,
  96. const String& title, const String& message,
  97. Component* /*associatedComponent*/)
  98. {
  99. showDialog (MessageBoxOptions()
  100. .withIconType (iconType)
  101. .withTitle (title)
  102. .withMessage (message)
  103. .withButton (TRANS("OK")),
  104. nullptr, AlertWindowMappings::messageBox);
  105. }
  106. int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options)
  107. {
  108. return showDialog (options, nullptr, AlertWindowMappings::noMapping);
  109. }
  110. #endif
  111. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType,
  112. const String& title, const String& message,
  113. Component* /*associatedComponent*/,
  114. ModalComponentManager::Callback* callback)
  115. {
  116. showDialog (MessageBoxOptions()
  117. .withIconType (iconType)
  118. .withTitle (title)
  119. .withMessage (message)
  120. .withButton (TRANS("OK")),
  121. callback, AlertWindowMappings::messageBox);
  122. }
  123. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType,
  124. const String& title, const String& message,
  125. Component* /*associatedComponent*/,
  126. ModalComponentManager::Callback* callback)
  127. {
  128. return showDialog (MessageBoxOptions()
  129. .withIconType (iconType)
  130. .withTitle (title)
  131. .withMessage (message)
  132. .withButton (TRANS("OK"))
  133. .withButton (TRANS("Cancel")),
  134. callback, AlertWindowMappings::okCancel) != 0;
  135. }
  136. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType,
  137. const String& title, const String& message,
  138. Component* /*associatedComponent*/,
  139. ModalComponentManager::Callback* callback)
  140. {
  141. return showDialog (MessageBoxOptions()
  142. .withIconType (iconType)
  143. .withTitle (title)
  144. .withMessage (message)
  145. .withButton (TRANS("Yes"))
  146. .withButton (TRANS("No"))
  147. .withButton (TRANS("Cancel")),
  148. callback, AlertWindowMappings::yesNoCancel);
  149. }
  150. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType,
  151. const String& title, const String& message,
  152. Component* /*associatedComponent*/,
  153. ModalComponentManager::Callback* callback)
  154. {
  155. return showDialog (MessageBoxOptions()
  156. .withIconType (iconType)
  157. .withTitle (title)
  158. .withMessage (message)
  159. .withButton (TRANS("Yes"))
  160. .withButton (TRANS("No")),
  161. callback, AlertWindowMappings::okCancel);
  162. }
  163. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  164. ModalComponentManager::Callback* callback)
  165. {
  166. showDialog (options, callback, AlertWindowMappings::noMapping);
  167. }
  168. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  169. std::function<void (int)> callback)
  170. {
  171. showAsync (options, ModalCallbackFunction::create (callback));
  172. }
  173. //==============================================================================
  174. static NSRect getDragRect (NSView* view, NSEvent* event)
  175. {
  176. auto eventPos = [event locationInWindow];
  177. return [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f)
  178. fromView: nil];
  179. }
  180. static NSView* getNSViewForDragEvent (Component* sourceComp)
  181. {
  182. if (sourceComp == nullptr)
  183. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
  184. sourceComp = draggingSource->getComponentUnderMouse();
  185. if (sourceComp != nullptr)
  186. return (NSView*) sourceComp->getWindowHandle();
  187. jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
  188. return nil;
  189. }
  190. struct NSDraggingSourceHelper : public ObjCClass<NSObject<NSDraggingSource>>
  191. {
  192. NSDraggingSourceHelper() : ObjCClass<NSObject<NSDraggingSource>> ("JUCENSDraggingSourceHelper_")
  193. {
  194. addIvar<std::function<void()>*> ("callback");
  195. addIvar<String*> ("text");
  196. addIvar<NSDragOperation*> ("operation");
  197. addMethod (@selector (dealloc), dealloc, "v@:");
  198. addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType, "v@:@@@");
  199. addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext, "c@:@@");
  200. addMethod (@selector (draggingSession:endedAtPoint:operation:), draggingSessionEnded, "v@:@@@");
  201. addProtocol (@protocol (NSPasteboardItemDataProvider));
  202. registerClass();
  203. }
  204. static void setText (id self, const String& text)
  205. {
  206. object_setInstanceVariable (self, "text", new String (text));
  207. }
  208. static void setCompletionCallback (id self, std::function<void()> cb)
  209. {
  210. object_setInstanceVariable (self, "callback", new std::function<void()> (cb));
  211. }
  212. static void setDragOperation (id self, NSDragOperation op)
  213. {
  214. object_setInstanceVariable (self, "operation", new NSDragOperation (op));
  215. }
  216. private:
  217. static void dealloc (id self, SEL)
  218. {
  219. delete getIvar<String*> (self, "text");
  220. delete getIvar<std::function<void()>*> (self, "callback");
  221. delete getIvar<NSDragOperation*> (self, "operation");
  222. sendSuperclassMessage<void> (self, @selector (dealloc));
  223. }
  224. static void provideDataForType (id self, SEL, NSPasteboard* sender, NSPasteboardItem*, NSString* type)
  225. {
  226. if ([type compare: NSPasteboardTypeString] == NSOrderedSame)
  227. if (auto* text = getIvar<String*> (self, "text"))
  228. [sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding]
  229. forType: NSPasteboardTypeString];
  230. }
  231. static NSDragOperation sourceOperationMaskForDraggingContext (id self, SEL, NSDraggingSession*, NSDraggingContext)
  232. {
  233. return *getIvar<NSDragOperation*> (self, "operation");
  234. }
  235. static void draggingSessionEnded (id self, SEL, NSDraggingSession*, NSPoint p, NSDragOperation)
  236. {
  237. // Our view doesn't receive a mouse up when the drag ends so we need to generate one here and send it...
  238. if (auto* view = getNSViewForDragEvent (nullptr))
  239. if (auto* cgEvent = CGEventCreateMouseEvent (nullptr, kCGEventLeftMouseUp, CGPointMake (p.x, p.y), kCGMouseButtonLeft))
  240. if (id e = [NSEvent eventWithCGEvent: cgEvent])
  241. [view mouseUp: e];
  242. if (auto* cb = getIvar<std::function<void()>*> (self, "callback"))
  243. cb->operator()();
  244. }
  245. };
  246. static NSDraggingSourceHelper draggingSourceHelper;
  247. bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComponent,
  248. std::function<void()> callback)
  249. {
  250. if (text.isEmpty())
  251. return false;
  252. if (auto* view = getNSViewForDragEvent (sourceComponent))
  253. {
  254. JUCE_AUTORELEASEPOOL
  255. {
  256. if (auto event = [[view window] currentEvent])
  257. {
  258. id helper = [draggingSourceHelper.createInstance() init];
  259. NSDraggingSourceHelper::setText (helper, text);
  260. NSDraggingSourceHelper::setDragOperation (helper, NSDragOperationCopy);
  261. if (callback != nullptr)
  262. NSDraggingSourceHelper::setCompletionCallback (helper, callback);
  263. auto pasteboardItem = [[NSPasteboardItem new] autorelease];
  264. [pasteboardItem setDataProvider: helper
  265. forTypes: [NSArray arrayWithObjects: NSPasteboardTypeString, nil]];
  266. auto dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter: pasteboardItem] autorelease];
  267. NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: nsEmptyString()];
  268. [dragItem setDraggingFrame: getDragRect (view, event) contents: image];
  269. if (auto session = [view beginDraggingSessionWithItems: [NSArray arrayWithObject: dragItem]
  270. event: event
  271. source: helper])
  272. {
  273. session.animatesToStartingPositionsOnCancelOrFail = YES;
  274. session.draggingFormation = NSDraggingFormationNone;
  275. return true;
  276. }
  277. }
  278. }
  279. }
  280. return false;
  281. }
  282. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
  283. Component* sourceComponent, std::function<void()> callback)
  284. {
  285. if (files.isEmpty())
  286. return false;
  287. if (auto* view = getNSViewForDragEvent (sourceComponent))
  288. {
  289. JUCE_AUTORELEASEPOOL
  290. {
  291. if (auto event = [[view window] currentEvent])
  292. {
  293. auto dragItems = [[[NSMutableArray alloc] init] autorelease];
  294. for (auto& filename : files)
  295. {
  296. auto* nsFilename = juceStringToNS (filename);
  297. auto fileURL = [NSURL fileURLWithPath: nsFilename];
  298. auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: fileURL];
  299. auto eventPos = [event locationInWindow];
  300. auto dragRect = [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f)
  301. fromView: nil];
  302. auto dragImage = [[NSWorkspace sharedWorkspace] iconForFile: nsFilename];
  303. [dragItem setDraggingFrame: dragRect
  304. contents: dragImage];
  305. [dragItems addObject: dragItem];
  306. [dragItem release];
  307. }
  308. auto helper = [draggingSourceHelper.createInstance() autorelease];
  309. if (callback != nullptr)
  310. NSDraggingSourceHelper::setCompletionCallback (helper, callback);
  311. NSDraggingSourceHelper::setDragOperation (helper, canMoveFiles ? NSDragOperationMove
  312. : NSDragOperationCopy);
  313. return [view beginDraggingSessionWithItems: dragItems
  314. event: event
  315. source: helper] != nullptr;
  316. }
  317. }
  318. }
  319. return false;
  320. }
  321. //==============================================================================
  322. bool Desktop::canUseSemiTransparentWindows() noexcept
  323. {
  324. return true;
  325. }
  326. Point<float> MouseInputSource::getCurrentRawMousePosition()
  327. {
  328. JUCE_AUTORELEASEPOOL
  329. {
  330. auto p = [NSEvent mouseLocation];
  331. return { (float) p.x, (float) (getMainScreenHeight() - p.y) };
  332. }
  333. }
  334. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  335. {
  336. // this rubbish needs to be done around the warp call, to avoid causing a
  337. // bizarre glitch..
  338. CGAssociateMouseAndMouseCursorPosition (false);
  339. CGWarpMouseCursorPosition (convertToCGPoint (newPosition));
  340. CGAssociateMouseAndMouseCursorPosition (true);
  341. }
  342. double Desktop::getDefaultMasterScale()
  343. {
  344. return 1.0;
  345. }
  346. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  347. {
  348. return upright;
  349. }
  350. //==============================================================================
  351. class ScreenSaverDefeater : public Timer
  352. {
  353. public:
  354. ScreenSaverDefeater()
  355. {
  356. startTimer (5000);
  357. timerCallback();
  358. }
  359. void timerCallback() override
  360. {
  361. if (Process::isForegroundProcess())
  362. {
  363. if (assertion == nullptr)
  364. assertion.reset (new PMAssertion());
  365. }
  366. else
  367. {
  368. assertion.reset();
  369. }
  370. }
  371. struct PMAssertion
  372. {
  373. PMAssertion() : assertionID (kIOPMNullAssertionID)
  374. {
  375. IOReturn res = IOPMAssertionCreateWithName (kIOPMAssertionTypePreventUserIdleDisplaySleep,
  376. kIOPMAssertionLevelOn,
  377. CFSTR ("JUCE Playback"),
  378. &assertionID);
  379. jassert (res == kIOReturnSuccess); ignoreUnused (res);
  380. }
  381. ~PMAssertion()
  382. {
  383. if (assertionID != kIOPMNullAssertionID)
  384. IOPMAssertionRelease (assertionID);
  385. }
  386. IOPMAssertionID assertionID;
  387. };
  388. std::unique_ptr<PMAssertion> assertion;
  389. };
  390. static std::unique_ptr<ScreenSaverDefeater> screenSaverDefeater;
  391. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  392. {
  393. if (isEnabled)
  394. screenSaverDefeater.reset();
  395. else if (screenSaverDefeater == nullptr)
  396. screenSaverDefeater.reset (new ScreenSaverDefeater());
  397. }
  398. bool Desktop::isScreenSaverEnabled()
  399. {
  400. return screenSaverDefeater == nullptr;
  401. }
  402. //==============================================================================
  403. struct DisplaySettingsChangeCallback : private DeletedAtShutdown
  404. {
  405. DisplaySettingsChangeCallback()
  406. {
  407. CGDisplayRegisterReconfigurationCallback (displayReconfigurationCallback, this);
  408. }
  409. ~DisplaySettingsChangeCallback()
  410. {
  411. CGDisplayRemoveReconfigurationCallback (displayReconfigurationCallback, this);
  412. clearSingletonInstance();
  413. }
  414. static void displayReconfigurationCallback (CGDirectDisplayID, CGDisplayChangeSummaryFlags, void* userInfo)
  415. {
  416. if (auto* thisPtr = static_cast<DisplaySettingsChangeCallback*> (userInfo))
  417. if (thisPtr->forceDisplayUpdate != nullptr)
  418. thisPtr->forceDisplayUpdate();
  419. }
  420. std::function<void()> forceDisplayUpdate;
  421. JUCE_DECLARE_SINGLETON (DisplaySettingsChangeCallback, false)
  422. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DisplaySettingsChangeCallback)
  423. };
  424. JUCE_IMPLEMENT_SINGLETON (DisplaySettingsChangeCallback)
  425. static Rectangle<int> convertDisplayRect (NSRect r, CGFloat mainScreenBottom)
  426. {
  427. r.origin.y = mainScreenBottom - (r.origin.y + r.size.height);
  428. return convertToRectInt (r);
  429. }
  430. static Displays::Display getDisplayFromScreen (NSScreen* s, CGFloat& mainScreenBottom, const float masterScale)
  431. {
  432. Displays::Display d;
  433. d.isMain = (mainScreenBottom == 0);
  434. if (d.isMain)
  435. mainScreenBottom = [s frame].size.height;
  436. d.userArea = convertDisplayRect ([s visibleFrame], mainScreenBottom) / masterScale;
  437. d.totalArea = convertDisplayRect ([s frame], mainScreenBottom) / masterScale;
  438. d.scale = masterScale;
  439. if ([s respondsToSelector: @selector (backingScaleFactor)])
  440. d.scale *= s.backingScaleFactor;
  441. NSSize dpi = [[[s deviceDescription] objectForKey: NSDeviceResolution] sizeValue];
  442. d.dpi = (dpi.width + dpi.height) / 2.0;
  443. return d;
  444. }
  445. void Displays::findDisplays (const float masterScale)
  446. {
  447. JUCE_AUTORELEASEPOOL
  448. {
  449. if (DisplaySettingsChangeCallback::getInstanceWithoutCreating() == nullptr)
  450. DisplaySettingsChangeCallback::getInstance()->forceDisplayUpdate = [this] { refresh(); };
  451. CGFloat mainScreenBottom = 0;
  452. for (NSScreen* s in [NSScreen screens])
  453. displays.add (getDisplayFromScreen (s, mainScreenBottom, masterScale));
  454. }
  455. }
  456. //==============================================================================
  457. bool juce_areThereAnyAlwaysOnTopWindows()
  458. {
  459. for (NSWindow* window in [NSApp windows])
  460. if ([window level] > NSNormalWindowLevel)
  461. return true;
  462. return false;
  463. }
  464. //==============================================================================
  465. static void selectImageForDrawing (const Image& image)
  466. {
  467. [NSGraphicsContext saveGraphicsState];
  468. #if (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10)
  469. [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithCGContext: juce_getImageContext (image)
  470. flipped: false]];
  471. #else
  472. [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: juce_getImageContext (image)
  473. flipped: false]];
  474. #endif
  475. }
  476. static void releaseImageAfterDrawing()
  477. {
  478. [[NSGraphicsContext currentContext] flushGraphics];
  479. [NSGraphicsContext restoreGraphicsState];
  480. }
  481. Image juce_createIconForFile (const File& file)
  482. {
  483. JUCE_AUTORELEASEPOOL
  484. {
  485. NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (file.getFullPathName())];
  486. Image result (Image::ARGB, (int) [image size].width, (int) [image size].height, true);
  487. selectImageForDrawing (result);
  488. [image drawAtPoint: NSMakePoint (0, 0)
  489. fromRect: NSMakeRect (0, 0, [image size].width, [image size].height)
  490. operation: NSCompositingOperationSourceOver fraction: 1.0f];
  491. releaseImageAfterDrawing();
  492. return result;
  493. }
  494. }
  495. static Image createNSWindowSnapshot (NSWindow* nsWindow)
  496. {
  497. JUCE_AUTORELEASEPOOL
  498. {
  499. CGImageRef screenShot = CGWindowListCreateImage (CGRectNull,
  500. kCGWindowListOptionIncludingWindow,
  501. (CGWindowID) [nsWindow windowNumber],
  502. kCGWindowImageBoundsIgnoreFraming);
  503. NSBitmapImageRep* bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage: screenShot];
  504. Image result (Image::ARGB, (int) [bitmapRep size].width, (int) [bitmapRep size].height, true);
  505. selectImageForDrawing (result);
  506. [bitmapRep drawAtPoint: NSMakePoint (0, 0)];
  507. releaseImageAfterDrawing();
  508. [bitmapRep release];
  509. CGImageRelease (screenShot);
  510. return result;
  511. }
  512. }
  513. Image createSnapshotOfNativeWindow (void*);
  514. Image createSnapshotOfNativeWindow (void* nativeWindowHandle)
  515. {
  516. if (id windowOrView = (id) nativeWindowHandle)
  517. {
  518. if ([windowOrView isKindOfClass: [NSWindow class]])
  519. return createNSWindowSnapshot ((NSWindow*) windowOrView);
  520. if ([windowOrView isKindOfClass: [NSView class]])
  521. return createNSWindowSnapshot ([(NSView*) windowOrView window]);
  522. }
  523. return {};
  524. }
  525. //==============================================================================
  526. void SystemClipboard::copyTextToClipboard (const String& text)
  527. {
  528. NSPasteboard* pb = [NSPasteboard generalPasteboard];
  529. [pb declareTypes: [NSArray arrayWithObject: NSPasteboardTypeString]
  530. owner: nil];
  531. [pb setString: juceStringToNS (text)
  532. forType: NSPasteboardTypeString];
  533. }
  534. String SystemClipboard::getTextFromClipboard()
  535. {
  536. return nsStringToJuce ([[NSPasteboard generalPasteboard] stringForType: NSPasteboardTypeString]);
  537. }
  538. void Process::setDockIconVisible (bool isVisible)
  539. {
  540. ProcessSerialNumber psn { 0, kCurrentProcess };
  541. OSStatus err = TransformProcessType (&psn, isVisible ? kProcessTransformToForegroundApplication
  542. : kProcessTransformToUIElementApplication);
  543. jassert (err == 0);
  544. ignoreUnused (err);
  545. }
  546. bool Desktop::isOSXDarkModeActive()
  547. {
  548. return [[[NSUserDefaults standardUserDefaults] stringForKey: nsStringLiteral ("AppleInterfaceStyle")]
  549. isEqualToString: nsStringLiteral ("Dark")];
  550. }
  551. } // namespace juce