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 21KB

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