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.

793 lines
29KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - 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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-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. return convertResult ([getAlert() runModal]);
  36. }
  37. using AsyncUpdater::triggerAsyncUpdate;
  38. private:
  39. static int convertResult (NSModalResponse response)
  40. {
  41. switch (response)
  42. {
  43. case NSAlertFirstButtonReturn: return 0;
  44. case NSAlertSecondButtonReturn: return 1;
  45. case NSAlertThirdButtonReturn: return 2;
  46. default: break;
  47. }
  48. jassertfalse;
  49. return 0;
  50. }
  51. void handleAsyncUpdate() override
  52. {
  53. if (auto* comp = options.getAssociatedComponent())
  54. {
  55. if (auto* peer = comp->getPeer())
  56. {
  57. if (auto* view = static_cast<NSView*> (peer->getNativeHandle()))
  58. {
  59. if (auto* window = [view window])
  60. {
  61. if (@available (macOS 10.9, *))
  62. {
  63. [getAlert() beginSheetModalForWindow: window completionHandler: ^(NSModalResponse result)
  64. {
  65. handleModalFinished (result);
  66. }];
  67. return;
  68. }
  69. }
  70. }
  71. }
  72. }
  73. handleModalFinished ([getAlert() runModal]);
  74. }
  75. void handleModalFinished (NSModalResponse result)
  76. {
  77. if (callback != nullptr)
  78. callback->modalStateFinished (convertResult (result));
  79. delete this;
  80. }
  81. static void addButton (NSAlert* alert, const String& button)
  82. {
  83. if (! button.isEmpty())
  84. [alert addButtonWithTitle: juceStringToNS (button)];
  85. }
  86. NSAlert* getAlert() const
  87. {
  88. NSAlert* alert = [[[NSAlert alloc] init] autorelease];
  89. [alert setMessageText: juceStringToNS (options.getTitle())];
  90. [alert setInformativeText: juceStringToNS (options.getMessage())];
  91. [alert setAlertStyle: options.getIconType() == MessageBoxIconType::WarningIcon ? NSAlertStyleCritical
  92. : NSAlertStyleInformational];
  93. const auto button1Text = options.getButtonText (0);
  94. addButton (alert, button1Text.isEmpty() ? "OK" : button1Text);
  95. addButton (alert, options.getButtonText (1));
  96. addButton (alert, options.getButtonText (2));
  97. return alert;
  98. }
  99. MessageBoxOptions options;
  100. std::unique_ptr<ModalComponentManager::Callback> callback;
  101. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXMessageBox)
  102. };
  103. static int showDialog (const MessageBoxOptions& options,
  104. ModalComponentManager::Callback* callbackIn,
  105. AlertWindowMappings::MapFn mapFn)
  106. {
  107. #if JUCE_MODAL_LOOPS_PERMITTED
  108. if (callbackIn == nullptr)
  109. {
  110. jassert (mapFn != nullptr);
  111. OSXMessageBox messageBox (options, nullptr);
  112. return mapFn (messageBox.getResult());
  113. }
  114. #endif
  115. auto messageBox = std::make_unique<OSXMessageBox> (options,
  116. AlertWindowMappings::getWrappedCallback (callbackIn, mapFn));
  117. messageBox->triggerAsyncUpdate();
  118. messageBox.release();
  119. return 0;
  120. }
  121. #if JUCE_MODAL_LOOPS_PERMITTED
  122. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (MessageBoxIconType iconType,
  123. const String& title, const String& message,
  124. Component* associatedComponent)
  125. {
  126. showDialog (MessageBoxOptions()
  127. .withIconType (iconType)
  128. .withTitle (title)
  129. .withMessage (message)
  130. .withButton (TRANS("OK"))
  131. .withAssociatedComponent (associatedComponent),
  132. nullptr, AlertWindowMappings::messageBox);
  133. }
  134. int JUCE_CALLTYPE NativeMessageBox::show (const MessageBoxOptions& options)
  135. {
  136. return showDialog (options, nullptr, AlertWindowMappings::noMapping);
  137. }
  138. #endif
  139. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (MessageBoxIconType iconType,
  140. const String& title, const String& message,
  141. Component* associatedComponent,
  142. ModalComponentManager::Callback* callback)
  143. {
  144. showDialog (MessageBoxOptions()
  145. .withIconType (iconType)
  146. .withTitle (title)
  147. .withMessage (message)
  148. .withButton (TRANS("OK"))
  149. .withAssociatedComponent (associatedComponent),
  150. callback, AlertWindowMappings::messageBox);
  151. }
  152. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (MessageBoxIconType iconType,
  153. const String& title, const String& message,
  154. Component* associatedComponent,
  155. ModalComponentManager::Callback* callback)
  156. {
  157. return showDialog (MessageBoxOptions()
  158. .withIconType (iconType)
  159. .withTitle (title)
  160. .withMessage (message)
  161. .withButton (TRANS("OK"))
  162. .withButton (TRANS("Cancel"))
  163. .withAssociatedComponent (associatedComponent),
  164. callback, AlertWindowMappings::okCancel) != 0;
  165. }
  166. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (MessageBoxIconType iconType,
  167. const String& title, const String& message,
  168. Component* associatedComponent,
  169. ModalComponentManager::Callback* callback)
  170. {
  171. return showDialog (MessageBoxOptions()
  172. .withIconType (iconType)
  173. .withTitle (title)
  174. .withMessage (message)
  175. .withButton (TRANS("Yes"))
  176. .withButton (TRANS("No"))
  177. .withButton (TRANS("Cancel"))
  178. .withAssociatedComponent (associatedComponent),
  179. callback, AlertWindowMappings::yesNoCancel);
  180. }
  181. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (MessageBoxIconType iconType,
  182. const String& title, const String& message,
  183. Component* associatedComponent,
  184. ModalComponentManager::Callback* callback)
  185. {
  186. return showDialog (MessageBoxOptions()
  187. .withIconType (iconType)
  188. .withTitle (title)
  189. .withMessage (message)
  190. .withButton (TRANS("Yes"))
  191. .withButton (TRANS("No"))
  192. .withAssociatedComponent (associatedComponent),
  193. callback, AlertWindowMappings::okCancel);
  194. }
  195. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  196. ModalComponentManager::Callback* callback)
  197. {
  198. showDialog (options, callback, AlertWindowMappings::noMapping);
  199. }
  200. void JUCE_CALLTYPE NativeMessageBox::showAsync (const MessageBoxOptions& options,
  201. std::function<void (int)> callback)
  202. {
  203. showAsync (options, ModalCallbackFunction::create (callback));
  204. }
  205. //==============================================================================
  206. static NSRect getDragRect (NSView* view, NSEvent* event)
  207. {
  208. auto eventPos = [event locationInWindow];
  209. return [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f)
  210. fromView: nil];
  211. }
  212. static NSView* getNSViewForDragEvent (Component* sourceComp)
  213. {
  214. if (sourceComp == nullptr)
  215. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource (0))
  216. sourceComp = draggingSource->getComponentUnderMouse();
  217. if (sourceComp != nullptr)
  218. return (NSView*) sourceComp->getWindowHandle();
  219. jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
  220. return nil;
  221. }
  222. struct NSDraggingSourceHelper : public ObjCClass<NSObject<NSDraggingSource>>
  223. {
  224. NSDraggingSourceHelper() : ObjCClass<NSObject<NSDraggingSource>> ("JUCENSDraggingSourceHelper_")
  225. {
  226. addIvar<std::function<void()>*> ("callback");
  227. addIvar<String*> ("text");
  228. addIvar<NSDragOperation*> ("operation");
  229. addMethod (@selector (dealloc), dealloc);
  230. addMethod (@selector (pasteboard:item:provideDataForType:), provideDataForType);
  231. addMethod (@selector (draggingSession:sourceOperationMaskForDraggingContext:), sourceOperationMaskForDraggingContext);
  232. addMethod (@selector (draggingSession:endedAtPoint:operation:), draggingSessionEnded);
  233. addProtocol (@protocol (NSPasteboardItemDataProvider));
  234. registerClass();
  235. }
  236. static void setText (id self, const String& text)
  237. {
  238. object_setInstanceVariable (self, "text", new String (text));
  239. }
  240. static void setCompletionCallback (id self, std::function<void()> cb)
  241. {
  242. object_setInstanceVariable (self, "callback", new std::function<void()> (cb));
  243. }
  244. static void setDragOperation (id self, NSDragOperation op)
  245. {
  246. object_setInstanceVariable (self, "operation", new NSDragOperation (op));
  247. }
  248. private:
  249. static void dealloc (id self, SEL)
  250. {
  251. delete getIvar<String*> (self, "text");
  252. delete getIvar<std::function<void()>*> (self, "callback");
  253. delete getIvar<NSDragOperation*> (self, "operation");
  254. sendSuperclassMessage<void> (self, @selector (dealloc));
  255. }
  256. static void provideDataForType (id self, SEL, NSPasteboard* sender, NSPasteboardItem*, NSString* type)
  257. {
  258. if ([type compare: NSPasteboardTypeString] == NSOrderedSame)
  259. if (auto* text = getIvar<String*> (self, "text"))
  260. [sender setData: [juceStringToNS (*text) dataUsingEncoding: NSUTF8StringEncoding]
  261. forType: NSPasteboardTypeString];
  262. }
  263. static NSDragOperation sourceOperationMaskForDraggingContext (id self, SEL, NSDraggingSession*, NSDraggingContext)
  264. {
  265. return *getIvar<NSDragOperation*> (self, "operation");
  266. }
  267. static void draggingSessionEnded (id self, SEL, NSDraggingSession*, NSPoint p, NSDragOperation)
  268. {
  269. // Our view doesn't receive a mouse up when the drag ends so we need to generate one here and send it...
  270. if (auto* view = getNSViewForDragEvent (nullptr))
  271. if (auto* cgEvent = CGEventCreateMouseEvent (nullptr, kCGEventLeftMouseUp, CGPointMake (p.x, p.y), kCGMouseButtonLeft))
  272. if (id e = [NSEvent eventWithCGEvent: cgEvent])
  273. [view mouseUp: e];
  274. if (auto* cb = getIvar<std::function<void()>*> (self, "callback"))
  275. cb->operator()();
  276. }
  277. };
  278. static NSDraggingSourceHelper draggingSourceHelper;
  279. bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComponent,
  280. std::function<void()> callback)
  281. {
  282. if (text.isEmpty())
  283. return false;
  284. if (auto* view = getNSViewForDragEvent (sourceComponent))
  285. {
  286. JUCE_AUTORELEASEPOOL
  287. {
  288. if (auto event = [[view window] currentEvent])
  289. {
  290. id helper = [draggingSourceHelper.createInstance() init];
  291. NSDraggingSourceHelper::setText (helper, text);
  292. NSDraggingSourceHelper::setDragOperation (helper, NSDragOperationCopy);
  293. if (callback != nullptr)
  294. NSDraggingSourceHelper::setCompletionCallback (helper, callback);
  295. auto pasteboardItem = [[NSPasteboardItem new] autorelease];
  296. [pasteboardItem setDataProvider: helper
  297. forTypes: [NSArray arrayWithObjects: NSPasteboardTypeString, nil]];
  298. auto dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter: pasteboardItem] autorelease];
  299. NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: nsEmptyString()];
  300. [dragItem setDraggingFrame: getDragRect (view, event) contents: image];
  301. if (auto session = [view beginDraggingSessionWithItems: [NSArray arrayWithObject: dragItem]
  302. event: event
  303. source: helper])
  304. {
  305. session.animatesToStartingPositionsOnCancelOrFail = YES;
  306. session.draggingFormation = NSDraggingFormationNone;
  307. return true;
  308. }
  309. }
  310. }
  311. }
  312. return false;
  313. }
  314. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
  315. Component* sourceComponent, std::function<void()> callback)
  316. {
  317. if (files.isEmpty())
  318. return false;
  319. if (auto* view = getNSViewForDragEvent (sourceComponent))
  320. {
  321. JUCE_AUTORELEASEPOOL
  322. {
  323. if (auto event = [[view window] currentEvent])
  324. {
  325. auto dragItems = [[[NSMutableArray alloc] init] autorelease];
  326. for (auto& filename : files)
  327. {
  328. auto* nsFilename = juceStringToNS (filename);
  329. auto fileURL = [NSURL fileURLWithPath: nsFilename];
  330. auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: fileURL];
  331. auto eventPos = [event locationInWindow];
  332. auto dragRect = [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f)
  333. fromView: nil];
  334. auto dragImage = [[NSWorkspace sharedWorkspace] iconForFile: nsFilename];
  335. [dragItem setDraggingFrame: dragRect
  336. contents: dragImage];
  337. [dragItems addObject: dragItem];
  338. [dragItem release];
  339. }
  340. auto helper = [draggingSourceHelper.createInstance() autorelease];
  341. if (callback != nullptr)
  342. NSDraggingSourceHelper::setCompletionCallback (helper, callback);
  343. NSDraggingSourceHelper::setDragOperation (helper, canMoveFiles ? NSDragOperationMove
  344. : NSDragOperationCopy);
  345. return [view beginDraggingSessionWithItems: dragItems
  346. event: event
  347. source: helper] != nullptr;
  348. }
  349. }
  350. }
  351. return false;
  352. }
  353. //==============================================================================
  354. bool Desktop::canUseSemiTransparentWindows() noexcept
  355. {
  356. return true;
  357. }
  358. Point<float> MouseInputSource::getCurrentRawMousePosition()
  359. {
  360. JUCE_AUTORELEASEPOOL
  361. {
  362. auto p = [NSEvent mouseLocation];
  363. return { (float) p.x, (float) (getMainScreenHeight() - p.y) };
  364. }
  365. }
  366. static ComponentPeer* findPeerContainingPoint (Point<float> globalPos)
  367. {
  368. for (int i = 0; i < juce::ComponentPeer::getNumPeers(); ++i)
  369. {
  370. auto* peer = juce::ComponentPeer::getPeer (i);
  371. if (peer->contains (peer->globalToLocal (globalPos).toInt(), false))
  372. return peer;
  373. }
  374. return nullptr;
  375. }
  376. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  377. {
  378. const auto oldPosition = Desktop::getInstance().getMainMouseSource().getRawScreenPosition();
  379. // this rubbish needs to be done around the warp call, to avoid causing a
  380. // bizarre glitch..
  381. CGAssociateMouseAndMouseCursorPosition (false);
  382. CGWarpMouseCursorPosition (convertToCGPoint (newPosition));
  383. CGAssociateMouseAndMouseCursorPosition (true);
  384. // Mouse enter and exit events seem to be always generated as a consequence of programmatically
  385. // moving the mouse. However, when the mouse stays within the same peer no mouse move event is
  386. // generated, and we lose track of the correct Component under the mouse. Hence, we need to
  387. // generate this missing event here.
  388. if (auto* peer = findPeerContainingPoint (newPosition); peer != nullptr
  389. && peer == findPeerContainingPoint (oldPosition))
  390. {
  391. peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse,
  392. peer->globalToLocal (newPosition),
  393. ModifierKeys::currentModifiers,
  394. 0.0f,
  395. 0.0f,
  396. Time::currentTimeMillis());
  397. }
  398. }
  399. double Desktop::getDefaultMasterScale()
  400. {
  401. return 1.0;
  402. }
  403. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  404. {
  405. return upright;
  406. }
  407. bool Desktop::isDarkModeActive() const
  408. {
  409. return [[[NSUserDefaults standardUserDefaults] stringForKey: nsStringLiteral ("AppleInterfaceStyle")]
  410. isEqualToString: nsStringLiteral ("Dark")];
  411. }
  412. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  413. static const auto darkModeSelector = @selector (darkModeChanged:);
  414. static const auto keyboardVisibilitySelector = @selector (keyboardVisiblityChanged:);
  415. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  416. class Desktop::NativeDarkModeChangeDetectorImpl
  417. {
  418. public:
  419. NativeDarkModeChangeDetectorImpl()
  420. {
  421. static DelegateClass delegateClass;
  422. delegate.reset ([delegateClass.createInstance() init]);
  423. observer.emplace (delegate.get(),
  424. darkModeSelector,
  425. @"AppleInterfaceThemeChangedNotification",
  426. nil,
  427. [NSDistributedNotificationCenter class]);
  428. }
  429. private:
  430. struct DelegateClass : public ObjCClass<NSObject>
  431. {
  432. DelegateClass() : ObjCClass<NSObject> ("JUCEDelegate_")
  433. {
  434. addMethod (darkModeSelector, [] (id, SEL, NSNotification*) { Desktop::getInstance().darkModeChanged(); });
  435. registerClass();
  436. }
  437. };
  438. NSUniquePtr<NSObject> delegate;
  439. Optional<ScopedNotificationCenterObserver> observer;
  440. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl)
  441. };
  442. std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl()
  443. {
  444. return std::make_unique<NativeDarkModeChangeDetectorImpl>();
  445. }
  446. //==============================================================================
  447. class ScreenSaverDefeater : public Timer
  448. {
  449. public:
  450. ScreenSaverDefeater()
  451. {
  452. startTimer (5000);
  453. timerCallback();
  454. }
  455. void timerCallback() override
  456. {
  457. if (Process::isForegroundProcess())
  458. {
  459. if (assertion == nullptr)
  460. assertion.reset (new PMAssertion());
  461. }
  462. else
  463. {
  464. assertion.reset();
  465. }
  466. }
  467. struct PMAssertion
  468. {
  469. PMAssertion() : assertionID (kIOPMNullAssertionID)
  470. {
  471. [[maybe_unused]] IOReturn res = IOPMAssertionCreateWithName (kIOPMAssertionTypePreventUserIdleDisplaySleep,
  472. kIOPMAssertionLevelOn,
  473. CFSTR ("JUCE Playback"),
  474. &assertionID);
  475. jassert (res == kIOReturnSuccess);
  476. }
  477. ~PMAssertion()
  478. {
  479. if (assertionID != kIOPMNullAssertionID)
  480. IOPMAssertionRelease (assertionID);
  481. }
  482. IOPMAssertionID assertionID;
  483. };
  484. std::unique_ptr<PMAssertion> assertion;
  485. };
  486. static std::unique_ptr<ScreenSaverDefeater> screenSaverDefeater;
  487. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  488. {
  489. if (isEnabled)
  490. screenSaverDefeater.reset();
  491. else if (screenSaverDefeater == nullptr)
  492. screenSaverDefeater.reset (new ScreenSaverDefeater());
  493. }
  494. bool Desktop::isScreenSaverEnabled()
  495. {
  496. return screenSaverDefeater == nullptr;
  497. }
  498. //==============================================================================
  499. struct DisplaySettingsChangeCallback : private DeletedAtShutdown
  500. {
  501. DisplaySettingsChangeCallback()
  502. {
  503. CGDisplayRegisterReconfigurationCallback (displayReconfigurationCallback, this);
  504. }
  505. ~DisplaySettingsChangeCallback()
  506. {
  507. CGDisplayRemoveReconfigurationCallback (displayReconfigurationCallback, this);
  508. clearSingletonInstance();
  509. }
  510. static void displayReconfigurationCallback (CGDirectDisplayID, CGDisplayChangeSummaryFlags, void* userInfo)
  511. {
  512. if (auto* thisPtr = static_cast<DisplaySettingsChangeCallback*> (userInfo))
  513. if (thisPtr->forceDisplayUpdate != nullptr)
  514. thisPtr->forceDisplayUpdate();
  515. }
  516. std::function<void()> forceDisplayUpdate;
  517. JUCE_DECLARE_SINGLETON (DisplaySettingsChangeCallback, false)
  518. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DisplaySettingsChangeCallback)
  519. };
  520. JUCE_IMPLEMENT_SINGLETON (DisplaySettingsChangeCallback)
  521. static Rectangle<int> convertDisplayRect (NSRect r, CGFloat mainScreenBottom)
  522. {
  523. r.origin.y = mainScreenBottom - (r.origin.y + r.size.height);
  524. return convertToRectInt (r);
  525. }
  526. static Displays::Display getDisplayFromScreen (NSScreen* s, CGFloat& mainScreenBottom, const float masterScale)
  527. {
  528. Displays::Display d;
  529. d.isMain = (mainScreenBottom == 0);
  530. if (d.isMain)
  531. mainScreenBottom = [s frame].size.height;
  532. d.userArea = convertDisplayRect ([s visibleFrame], mainScreenBottom) / masterScale;
  533. d.totalArea = convertDisplayRect ([s frame], mainScreenBottom) / masterScale;
  534. d.scale = masterScale;
  535. if ([s respondsToSelector: @selector (backingScaleFactor)])
  536. d.scale *= s.backingScaleFactor;
  537. NSSize dpi = [[[s deviceDescription] objectForKey: NSDeviceResolution] sizeValue];
  538. d.dpi = (dpi.width + dpi.height) / 2.0;
  539. return d;
  540. }
  541. void Displays::findDisplays (const float masterScale)
  542. {
  543. JUCE_AUTORELEASEPOOL
  544. {
  545. if (DisplaySettingsChangeCallback::getInstanceWithoutCreating() == nullptr)
  546. DisplaySettingsChangeCallback::getInstance()->forceDisplayUpdate = [this] { refresh(); };
  547. CGFloat mainScreenBottom = 0;
  548. for (NSScreen* s in [NSScreen screens])
  549. displays.add (getDisplayFromScreen (s, mainScreenBottom, masterScale));
  550. }
  551. }
  552. //==============================================================================
  553. bool juce_areThereAnyAlwaysOnTopWindows()
  554. {
  555. for (NSWindow* window in [NSApp windows])
  556. if ([window level] > NSNormalWindowLevel)
  557. return true;
  558. return false;
  559. }
  560. //==============================================================================
  561. static void selectImageForDrawing (const Image& image)
  562. {
  563. [NSGraphicsContext saveGraphicsState];
  564. if (@available (macOS 10.10, *))
  565. {
  566. [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithCGContext: juce_getImageContext (image)
  567. flipped: false]];
  568. return;
  569. }
  570. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  571. [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: juce_getImageContext (image)
  572. flipped: false]];
  573. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  574. }
  575. static void releaseImageAfterDrawing()
  576. {
  577. [[NSGraphicsContext currentContext] flushGraphics];
  578. [NSGraphicsContext restoreGraphicsState];
  579. }
  580. Image juce_createIconForFile (const File& file)
  581. {
  582. JUCE_AUTORELEASEPOOL
  583. {
  584. NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (file.getFullPathName())];
  585. Image result (Image::ARGB, (int) [image size].width, (int) [image size].height, true);
  586. selectImageForDrawing (result);
  587. [image drawAtPoint: NSMakePoint (0, 0)
  588. fromRect: NSMakeRect (0, 0, [image size].width, [image size].height)
  589. operation: NSCompositingOperationSourceOver fraction: 1.0f];
  590. releaseImageAfterDrawing();
  591. return result;
  592. }
  593. }
  594. static Image createNSWindowSnapshot (NSWindow* nsWindow)
  595. {
  596. JUCE_AUTORELEASEPOOL
  597. {
  598. CGImageRef screenShot = CGWindowListCreateImage (CGRectNull,
  599. kCGWindowListOptionIncludingWindow,
  600. (CGWindowID) [nsWindow windowNumber],
  601. kCGWindowImageBoundsIgnoreFraming);
  602. NSBitmapImageRep* bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage: screenShot];
  603. Image result (Image::ARGB, (int) [bitmapRep size].width, (int) [bitmapRep size].height, true);
  604. selectImageForDrawing (result);
  605. [bitmapRep drawAtPoint: NSMakePoint (0, 0)];
  606. releaseImageAfterDrawing();
  607. [bitmapRep release];
  608. CGImageRelease (screenShot);
  609. return result;
  610. }
  611. }
  612. Image createSnapshotOfNativeWindow (void* nativeWindowHandle)
  613. {
  614. if (id windowOrView = (id) nativeWindowHandle)
  615. {
  616. if ([windowOrView isKindOfClass: [NSWindow class]])
  617. return createNSWindowSnapshot ((NSWindow*) windowOrView);
  618. if ([windowOrView isKindOfClass: [NSView class]])
  619. return createNSWindowSnapshot ([(NSView*) windowOrView window]);
  620. }
  621. return {};
  622. }
  623. //==============================================================================
  624. void SystemClipboard::copyTextToClipboard (const String& text)
  625. {
  626. NSPasteboard* pb = [NSPasteboard generalPasteboard];
  627. [pb declareTypes: [NSArray arrayWithObject: NSPasteboardTypeString]
  628. owner: nil];
  629. [pb setString: juceStringToNS (text)
  630. forType: NSPasteboardTypeString];
  631. }
  632. String SystemClipboard::getTextFromClipboard()
  633. {
  634. return nsStringToJuce ([[NSPasteboard generalPasteboard] stringForType: NSPasteboardTypeString]);
  635. }
  636. void Process::setDockIconVisible (bool isVisible)
  637. {
  638. ProcessSerialNumber psn { 0, kCurrentProcess };
  639. [[maybe_unused]] OSStatus err = TransformProcessType (&psn, isVisible ? kProcessTransformToForegroundApplication
  640. : kProcessTransformToUIElementApplication);
  641. jassert (err == 0);
  642. }
  643. } // namespace juce