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.

199 lines
6.7KB

  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. #if defined (MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
  22. //==============================================================================
  23. class BluetoothMidiPairingWindowClass : public ObjCClass<NSObject>
  24. {
  25. public:
  26. struct Callbacks
  27. {
  28. std::unique_ptr<ModalComponentManager::Callback> modalExit;
  29. std::function<void()> windowClosed;
  30. };
  31. BluetoothMidiPairingWindowClass() : ObjCClass<NSObject> ("JUCEBluetoothMidiPairingWindowClass_")
  32. {
  33. addIvar<Callbacks*> ("callbacks");
  34. addIvar<CABTLEMIDIWindowController*> ("controller");
  35. #pragma clang diagnostic push
  36. #pragma clang diagnostic ignored "-Wundeclared-selector"
  37. addMethod (@selector (initWithCallbacks:), initWithCallbacks, "@@:^v");
  38. addMethod (@selector (show:), show, "v@:^v");
  39. addMethod (@selector (receivedWindowWillClose:), receivedWindowWillClose, "v@:^v");
  40. #pragma clang diagnostic pop
  41. addMethod (@selector (dealloc), dealloc, "v@:");
  42. registerClass();
  43. }
  44. private:
  45. static CABTLEMIDIWindowController* getController (id self)
  46. {
  47. return getIvar<CABTLEMIDIWindowController*> (self, "controller");
  48. }
  49. static id initWithCallbacks (id self, SEL, Callbacks* cbs)
  50. {
  51. self = sendSuperclassMessage (self, @selector (init));
  52. object_setInstanceVariable (self, "callbacks", cbs);
  53. object_setInstanceVariable (self, "controller", [CABTLEMIDIWindowController new]);
  54. #pragma clang diagnostic push
  55. #pragma clang diagnostic ignored "-Wundeclared-selector"
  56. [[NSNotificationCenter defaultCenter] addObserver: self
  57. selector: @selector (receivedWindowWillClose:)
  58. name: @"NSWindowWillCloseNotification"
  59. object: [getController (self) window]];
  60. #pragma clang diagnostic pop
  61. return self;
  62. }
  63. static void dealloc (id self, SEL)
  64. {
  65. [getController (self) release];
  66. sendSuperclassMessage (self, @selector (dealloc));
  67. }
  68. static void show (id self, SEL, Rectangle<int>* bounds)
  69. {
  70. if (bounds != nullptr)
  71. {
  72. auto nsBounds = makeNSRect (*bounds);
  73. auto mainScreenHeight = []
  74. {
  75. if ([[NSScreen screens] count] == 0)
  76. return (CGFloat) 0.0f;
  77. return [[[NSScreen screens] objectAtIndex: 0] frame].size.height;
  78. }();
  79. nsBounds.origin.y = mainScreenHeight - (nsBounds.origin.y + nsBounds.size.height);
  80. [getController (self).window setFrame: nsBounds
  81. display: YES];
  82. }
  83. [getController (self) showWindow: nil];
  84. }
  85. static void receivedWindowWillClose (id self, SEL, NSNotification*)
  86. {
  87. [[NSNotificationCenter defaultCenter] removeObserver: self];
  88. auto* cbs = getIvar<Callbacks*> (self, "callbacks");
  89. if (cbs->modalExit != nullptr)
  90. cbs->modalExit->modalStateFinished (0);
  91. cbs->windowClosed();
  92. }
  93. };
  94. class BluetoothMidiSelectorWindowHelper : public DeletedAtShutdown
  95. {
  96. public:
  97. BluetoothMidiSelectorWindowHelper (ModalComponentManager::Callback* exitCallback,
  98. Rectangle<int>* bounds)
  99. {
  100. std::unique_ptr<ModalComponentManager::Callback> exitCB (exitCallback);
  101. static BluetoothMidiPairingWindowClass cls;
  102. window.reset (cls.createInstance());
  103. WeakReference<BluetoothMidiSelectorWindowHelper> safeThis (this);
  104. auto deletionCB = [=]
  105. {
  106. if (auto* t = safeThis.get())
  107. delete t;
  108. };
  109. callbacks.reset (new BluetoothMidiPairingWindowClass::Callbacks { std::move (exitCB),
  110. std::move (deletionCB) });
  111. #pragma clang diagnostic push
  112. #pragma clang diagnostic ignored "-Wundeclared-selector"
  113. [window.get() performSelector: @selector (initWithCallbacks:)
  114. withObject: (id) callbacks.get()];
  115. [window.get() performSelector: @selector (show:)
  116. withObject: (id) bounds];
  117. #pragma clang diagnostic pop
  118. }
  119. private:
  120. std::unique_ptr<NSObject, NSObjectDeleter> window;
  121. std::unique_ptr<BluetoothMidiPairingWindowClass::Callbacks> callbacks;
  122. JUCE_DECLARE_WEAK_REFERENCEABLE (BluetoothMidiSelectorWindowHelper)
  123. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BluetoothMidiSelectorWindowHelper)
  124. };
  125. //==============================================================================
  126. bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback* exitCallback,
  127. Rectangle<int>* bounds)
  128. {
  129. new BluetoothMidiSelectorWindowHelper (exitCallback, bounds);
  130. return true;
  131. }
  132. bool BluetoothMidiDevicePairingDialogue::isAvailable()
  133. {
  134. return true;
  135. }
  136. #else
  137. bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback* exitCallback,
  138. Rectangle<int>*)
  139. {
  140. std::unique_ptr<ModalComponentManager::Callback> cb (exitCallback);
  141. // This functionality is unavailable when targetting OSX < 10.11. Instead,
  142. // you should pair Bluetooth MIDI devices using the "Audio MIDI Setup" app
  143. // (located in /Applications/Utilities).
  144. jassertfalse;
  145. return false;
  146. }
  147. bool BluetoothMidiDevicePairingDialogue::isAvailable()
  148. {
  149. return false;
  150. }
  151. #endif
  152. } // namespace juce