Audio plugin host https://kx.studio/carla
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.

juce_mac_Windowing.mm 22KB

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