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.

600 lines
20KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. } // namespace juce
  18. @interface NSDraggingSourceHelper : NSObject <NSDraggingSource>
  19. {
  20. }
  21. @end
  22. @implementation NSDraggingSourceHelper
  23. -(NSDragOperation) draggingSession: (NSDraggingSession *)session sourceOperationMaskForDraggingContext: (NSDraggingContext)context
  24. {
  25. juce::ignoreUnused (session, context);
  26. return NSDragOperationCopy;
  27. }
  28. @end
  29. namespace juce {
  30. //==============================================================================
  31. void LookAndFeel::playAlertSound()
  32. {
  33. NSBeep();
  34. }
  35. //==============================================================================
  36. class OSXMessageBox : private AsyncUpdater
  37. {
  38. public:
  39. OSXMessageBox (AlertWindow::AlertIconType type, const String& t, const String& m,
  40. const char* b1, const char* b2, const char* b3,
  41. ModalComponentManager::Callback* c, const bool runAsync)
  42. : iconType (type), title (t), message (m), callback (c),
  43. button1 (b1), button2 (b2), button3 (b3)
  44. {
  45. if (runAsync)
  46. triggerAsyncUpdate();
  47. }
  48. int getResult() const
  49. {
  50. switch (getRawResult())
  51. {
  52. case NSAlertFirstButtonReturn: return 1;
  53. case NSAlertThirdButtonReturn: return 2;
  54. default: return 0;
  55. }
  56. }
  57. static int show (AlertWindow::AlertIconType iconType, const String& title, const String& message,
  58. ModalComponentManager::Callback* callback, const char* b1, const char* b2, const char* b3,
  59. bool runAsync)
  60. {
  61. ScopedPointer<OSXMessageBox> mb (new OSXMessageBox (iconType, title, message, b1, b2, b3,
  62. callback, runAsync));
  63. if (! runAsync)
  64. return mb->getResult();
  65. mb.release();
  66. return 0;
  67. }
  68. private:
  69. AlertWindow::AlertIconType iconType;
  70. String title, message;
  71. ScopedPointer<ModalComponentManager::Callback> callback;
  72. const char* button1;
  73. const char* button2;
  74. const char* button3;
  75. void handleAsyncUpdate() override
  76. {
  77. const int result = getResult();
  78. if (callback != nullptr)
  79. callback->modalStateFinished (result);
  80. delete this;
  81. }
  82. NSInteger getRawResult() const
  83. {
  84. NSAlert* alert = [[[NSAlert alloc] init] autorelease];
  85. [alert setMessageText: juceStringToNS (title)];
  86. [alert setInformativeText: juceStringToNS (message)];
  87. [alert setAlertStyle: iconType == AlertWindow::WarningIcon ? NSAlertStyleCritical
  88. : NSAlertStyleInformational];
  89. addButton (alert, button1);
  90. addButton (alert, button2);
  91. addButton (alert, button3);
  92. return [alert runModal];
  93. }
  94. static void addButton (NSAlert* alert, const char* button)
  95. {
  96. if (button != nullptr)
  97. [alert addButtonWithTitle: juceStringToNS (TRANS (button))];
  98. }
  99. };
  100. #if JUCE_MODAL_LOOPS_PERMITTED
  101. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
  102. const String& title, const String& message,
  103. Component* /*associatedComponent*/)
  104. {
  105. OSXMessageBox::show (iconType, title, message, nullptr, "OK", nullptr, nullptr, false);
  106. }
  107. #endif
  108. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
  109. const String& title, const String& message,
  110. Component* /*associatedComponent*/,
  111. ModalComponentManager::Callback* callback)
  112. {
  113. OSXMessageBox::show (iconType, title, message, callback, "OK", nullptr, nullptr, true);
  114. }
  115. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (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. "OK", "Cancel", nullptr, callback != nullptr) == 1;
  122. }
  123. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
  124. const String& title, const String& message,
  125. Component* /*associatedComponent*/,
  126. ModalComponentManager::Callback* callback)
  127. {
  128. return OSXMessageBox::show (iconType, title, message, callback,
  129. "Yes", "Cancel", "No", callback != nullptr);
  130. }
  131. //==============================================================================
  132. static NSRect getDragRect (NSView* view, NSEvent* event)
  133. {
  134. auto eventPos = [event locationInWindow];
  135. return [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f)
  136. fromView: nil];
  137. }
  138. NSView* getNSViewForDragEvent()
  139. {
  140. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource(0))
  141. if (auto* sourceComp = draggingSource->getComponentUnderMouse())
  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)
  175. {
  176. if (text.isEmpty())
  177. return false;
  178. if (auto* view = getNSViewForDragEvent())
  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. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool /*canMoveFiles*/)
  205. {
  206. if (files.isEmpty())
  207. return false;
  208. if (auto* view = getNSViewForDragEvent())
  209. {
  210. JUCE_AUTORELEASEPOOL
  211. {
  212. if (auto* event = [[view window] currentEvent])
  213. {
  214. auto* dragItems = [[[NSMutableArray alloc] init] autorelease];
  215. for (auto& filename : files)
  216. {
  217. auto* nsFilename = juceStringToNS (filename);
  218. auto* fileURL = [NSURL fileURLWithPath: nsFilename];
  219. auto* dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: fileURL];
  220. auto eventPos = [event locationInWindow];
  221. auto dragRect = [view convertRect: NSMakeRect (eventPos.x - 16.0f, eventPos.y - 16.0f, 32.0f, 32.0f)
  222. fromView: nil];
  223. auto *dragImage = [[NSWorkspace sharedWorkspace] iconForFile: nsFilename];
  224. [dragItem setDraggingFrame: dragRect
  225. contents: dragImage];
  226. [dragItems addObject: dragItem];
  227. [dragItem release];
  228. }
  229. auto* helper = [[NSDraggingSourceHelper alloc] autorelease];
  230. if (! [view beginDraggingSessionWithItems: dragItems
  231. event: event
  232. source: helper])
  233. return false;
  234. return true;
  235. }
  236. }
  237. }
  238. return false;
  239. }
  240. //==============================================================================
  241. bool Desktop::canUseSemiTransparentWindows() noexcept
  242. {
  243. return true;
  244. }
  245. Point<float> MouseInputSource::getCurrentRawMousePosition()
  246. {
  247. JUCE_AUTORELEASEPOOL
  248. {
  249. auto p = [NSEvent mouseLocation];
  250. return { (float) p.x, (float) (getMainScreenHeight() - p.y) };
  251. }
  252. }
  253. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  254. {
  255. // this rubbish needs to be done around the warp call, to avoid causing a
  256. // bizarre glitch..
  257. CGAssociateMouseAndMouseCursorPosition (false);
  258. CGWarpMouseCursorPosition (convertToCGPoint (newPosition));
  259. CGAssociateMouseAndMouseCursorPosition (true);
  260. }
  261. double Desktop::getDefaultMasterScale()
  262. {
  263. return 1.0;
  264. }
  265. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  266. {
  267. return upright;
  268. }
  269. //==============================================================================
  270. #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7)
  271. #define JUCE_USE_IOPM_SCREENSAVER_DEFEAT 1
  272. #endif
  273. #if ! (defined (JUCE_USE_IOPM_SCREENSAVER_DEFEAT) || defined (__POWER__))
  274. extern "C" { extern OSErr UpdateSystemActivity (UInt8); } // Some versions of the SDK omit this function..
  275. #endif
  276. class ScreenSaverDefeater : public Timer
  277. {
  278. public:
  279. #if JUCE_USE_IOPM_SCREENSAVER_DEFEAT
  280. ScreenSaverDefeater()
  281. {
  282. startTimer (5000);
  283. timerCallback();
  284. }
  285. void timerCallback() override
  286. {
  287. if (Process::isForegroundProcess())
  288. {
  289. if (assertion == nullptr)
  290. assertion = new PMAssertion();
  291. }
  292. else
  293. {
  294. assertion = nullptr;
  295. }
  296. }
  297. struct PMAssertion
  298. {
  299. PMAssertion() : assertionID (kIOPMNullAssertionID)
  300. {
  301. IOReturn res = IOPMAssertionCreateWithName (kIOPMAssertionTypePreventUserIdleDisplaySleep,
  302. kIOPMAssertionLevelOn,
  303. CFSTR ("JUCE Playback"),
  304. &assertionID);
  305. jassert (res == kIOReturnSuccess); ignoreUnused (res);
  306. }
  307. ~PMAssertion()
  308. {
  309. if (assertionID != kIOPMNullAssertionID)
  310. IOPMAssertionRelease (assertionID);
  311. }
  312. IOPMAssertionID assertionID;
  313. };
  314. ScopedPointer<PMAssertion> assertion;
  315. #else
  316. ScreenSaverDefeater()
  317. {
  318. startTimer (10000);
  319. timerCallback();
  320. }
  321. void timerCallback() override
  322. {
  323. if (Process::isForegroundProcess())
  324. UpdateSystemActivity (1 /*UsrActivity*/);
  325. }
  326. #endif
  327. };
  328. static ScopedPointer<ScreenSaverDefeater> screenSaverDefeater;
  329. void Desktop::setScreenSaverEnabled (const bool isEnabled)
  330. {
  331. if (isEnabled)
  332. screenSaverDefeater = nullptr;
  333. else if (screenSaverDefeater == nullptr)
  334. screenSaverDefeater = new ScreenSaverDefeater();
  335. }
  336. bool Desktop::isScreenSaverEnabled()
  337. {
  338. return screenSaverDefeater == nullptr;
  339. }
  340. //==============================================================================
  341. class DisplaySettingsChangeCallback : private DeletedAtShutdown
  342. {
  343. public:
  344. DisplaySettingsChangeCallback()
  345. {
  346. CGDisplayRegisterReconfigurationCallback (displayReconfigurationCallBack, 0);
  347. }
  348. ~DisplaySettingsChangeCallback()
  349. {
  350. CGDisplayRemoveReconfigurationCallback (displayReconfigurationCallBack, 0);
  351. clearSingletonInstance();
  352. }
  353. static void displayReconfigurationCallBack (CGDirectDisplayID, CGDisplayChangeSummaryFlags, void*)
  354. {
  355. const_cast<Desktop::Displays&> (Desktop::getInstance().getDisplays()).refresh();
  356. }
  357. juce_DeclareSingleton_SingleThreaded_Minimal (DisplaySettingsChangeCallback)
  358. private:
  359. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DisplaySettingsChangeCallback)
  360. };
  361. juce_ImplementSingleton_SingleThreaded (DisplaySettingsChangeCallback)
  362. static Rectangle<int> convertDisplayRect (NSRect r, CGFloat mainScreenBottom)
  363. {
  364. r.origin.y = mainScreenBottom - (r.origin.y + r.size.height);
  365. return convertToRectInt (r);
  366. }
  367. static Desktop::Displays::Display getDisplayFromScreen (NSScreen* s, CGFloat& mainScreenBottom, const float masterScale)
  368. {
  369. Desktop::Displays::Display d;
  370. d.isMain = (mainScreenBottom == 0);
  371. if (d.isMain)
  372. mainScreenBottom = [s frame].size.height;
  373. d.userArea = convertDisplayRect ([s visibleFrame], mainScreenBottom) / masterScale;
  374. d.totalArea = convertDisplayRect ([s frame], mainScreenBottom) / masterScale;
  375. d.scale = masterScale;
  376. #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
  377. if ([s respondsToSelector: @selector (backingScaleFactor)])
  378. d.scale *= s.backingScaleFactor;
  379. #endif
  380. NSSize dpi = [[[s deviceDescription] objectForKey: NSDeviceResolution] sizeValue];
  381. d.dpi = (dpi.width + dpi.height) / 2.0;
  382. return d;
  383. }
  384. void Desktop::Displays::findDisplays (const float masterScale)
  385. {
  386. JUCE_AUTORELEASEPOOL
  387. {
  388. DisplaySettingsChangeCallback::getInstance();
  389. CGFloat mainScreenBottom = 0;
  390. for (NSScreen* s in [NSScreen screens])
  391. displays.add (getDisplayFromScreen (s, mainScreenBottom, masterScale));
  392. }
  393. }
  394. //==============================================================================
  395. bool juce_areThereAnyAlwaysOnTopWindows()
  396. {
  397. for (NSWindow* window in [NSApp windows])
  398. if ([window level] > NSNormalWindowLevel)
  399. return true;
  400. return false;
  401. }
  402. //==============================================================================
  403. static void selectImageForDrawing (const Image& image)
  404. {
  405. [NSGraphicsContext saveGraphicsState];
  406. [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithGraphicsPort: juce_getImageContext (image)
  407. flipped: false]];
  408. }
  409. static void releaseImageAfterDrawing()
  410. {
  411. [[NSGraphicsContext currentContext] flushGraphics];
  412. [NSGraphicsContext restoreGraphicsState];
  413. }
  414. Image juce_createIconForFile (const File& file)
  415. {
  416. JUCE_AUTORELEASEPOOL
  417. {
  418. NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile: juceStringToNS (file.getFullPathName())];
  419. Image result (Image::ARGB, (int) [image size].width, (int) [image size].height, true);
  420. selectImageForDrawing (result);
  421. [image drawAtPoint: NSMakePoint (0, 0)
  422. fromRect: NSMakeRect (0, 0, [image size].width, [image size].height)
  423. operation: NSCompositingOperationSourceOver fraction: 1.0f];
  424. releaseImageAfterDrawing();
  425. return result;
  426. }
  427. }
  428. static Image createNSWindowSnapshot (NSWindow* nsWindow)
  429. {
  430. JUCE_AUTORELEASEPOOL
  431. {
  432. CGImageRef screenShot = CGWindowListCreateImage (CGRectNull,
  433. kCGWindowListOptionIncludingWindow,
  434. (CGWindowID) [nsWindow windowNumber],
  435. kCGWindowImageBoundsIgnoreFraming);
  436. NSBitmapImageRep* bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage: screenShot];
  437. Image result (Image::ARGB, (int) [bitmapRep size].width, (int) [bitmapRep size].height, true);
  438. selectImageForDrawing (result);
  439. [bitmapRep drawAtPoint: NSMakePoint (0, 0)];
  440. releaseImageAfterDrawing();
  441. [bitmapRep release];
  442. CGImageRelease (screenShot);
  443. return result;
  444. }
  445. }
  446. Image createSnapshotOfNativeWindow (void* nativeWindowHandle)
  447. {
  448. if (id windowOrView = (id) nativeWindowHandle)
  449. {
  450. if ([windowOrView isKindOfClass: [NSWindow class]])
  451. return createNSWindowSnapshot ((NSWindow*) windowOrView);
  452. if ([windowOrView isKindOfClass: [NSView class]])
  453. return createNSWindowSnapshot ([(NSView*) windowOrView window]);
  454. }
  455. return Image();
  456. }
  457. //==============================================================================
  458. void SystemClipboard::copyTextToClipboard (const String& text)
  459. {
  460. NSPasteboard* pb = [NSPasteboard generalPasteboard];
  461. [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType]
  462. owner: nil];
  463. [pb setString: juceStringToNS (text)
  464. forType: NSStringPboardType];
  465. }
  466. String SystemClipboard::getTextFromClipboard()
  467. {
  468. return nsStringToJuce ([[NSPasteboard generalPasteboard] stringForType: NSStringPboardType]);
  469. }
  470. void Process::setDockIconVisible (bool isVisible)
  471. {
  472. ProcessSerialNumber psn { 0, kCurrentProcess };
  473. ProcessApplicationTransformState state = isVisible
  474. ? kProcessTransformToForegroundApplication
  475. : kProcessTransformToUIElementApplication;
  476. OSStatus err = TransformProcessType (&psn, state);
  477. jassert (err == 0);
  478. ignoreUnused (err);
  479. }
  480. bool Desktop::isOSXDarkModeActive()
  481. {
  482. return [[[NSUserDefaults standardUserDefaults] stringForKey: nsStringLiteral ("AppleInterfaceStyle")]
  483. isEqualToString: nsStringLiteral ("Dark")];
  484. }