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.

194 lines
6.6KB

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