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.

192 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. auto deletionCB = [safeThis = WeakReference<BluetoothMidiSelectorWindowHelper> { this }]
  101. {
  102. if (safeThis != nullptr)
  103. delete safeThis.get();
  104. };
  105. callbacks.reset (new BluetoothMidiPairingWindowClass::Callbacks { std::move (exitCB),
  106. std::move (deletionCB) });
  107. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  108. [window.get() performSelector: @selector (initWithCallbacks:)
  109. withObject: (id) callbacks.get()];
  110. [window.get() performSelector: @selector (show:)
  111. withObject: (id) bounds];
  112. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  113. }
  114. private:
  115. std::unique_ptr<NSObject, NSObjectDeleter> window;
  116. std::unique_ptr<BluetoothMidiPairingWindowClass::Callbacks> callbacks;
  117. JUCE_DECLARE_WEAK_REFERENCEABLE (BluetoothMidiSelectorWindowHelper)
  118. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BluetoothMidiSelectorWindowHelper)
  119. };
  120. //==============================================================================
  121. bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback* exitCallback,
  122. Rectangle<int>* bounds)
  123. {
  124. new BluetoothMidiSelectorWindowHelper (exitCallback, bounds);
  125. return true;
  126. }
  127. bool BluetoothMidiDevicePairingDialogue::isAvailable()
  128. {
  129. return true;
  130. }
  131. #else
  132. bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback* exitCallback,
  133. Rectangle<int>*)
  134. {
  135. std::unique_ptr<ModalComponentManager::Callback> cb (exitCallback);
  136. // This functionality is unavailable when targetting OSX < 10.11. Instead,
  137. // you should pair Bluetooth MIDI devices using the "Audio MIDI Setup" app
  138. // (located in /Applications/Utilities).
  139. jassertfalse;
  140. return false;
  141. }
  142. bool BluetoothMidiDevicePairingDialogue::isAvailable()
  143. {
  144. return false;
  145. }
  146. #endif
  147. } // namespace juce