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.

176 lines
6.1KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. //==============================================================================
  16. class API_AVAILABLE (macos (10.11)) BluetoothMidiPairingWindowClass : public ObjCClass<NSObject>
  17. {
  18. public:
  19. struct Callbacks
  20. {
  21. std::unique_ptr<ModalComponentManager::Callback> modalExit;
  22. std::function<void()> windowClosed;
  23. };
  24. BluetoothMidiPairingWindowClass() : ObjCClass<NSObject> ("JUCEBluetoothMidiPairingWindowClass_")
  25. {
  26. addIvar<Callbacks*> ("callbacks");
  27. addIvar<CABTLEMIDIWindowController*> ("controller");
  28. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  29. addMethod (@selector (initWithCallbacks:), initWithCallbacks);
  30. addMethod (@selector (show:), show);
  31. addMethod (@selector (receivedWindowWillClose:), receivedWindowWillClose);
  32. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  33. addMethod (@selector (dealloc), dealloc);
  34. registerClass();
  35. }
  36. private:
  37. static CABTLEMIDIWindowController* getController (id self)
  38. {
  39. return getIvar<CABTLEMIDIWindowController*> (self, "controller");
  40. }
  41. static id initWithCallbacks (id self, SEL, Callbacks* cbs)
  42. {
  43. self = sendSuperclassMessage<id> (self, @selector (init));
  44. object_setInstanceVariable (self, "callbacks", cbs);
  45. object_setInstanceVariable (self, "controller", [CABTLEMIDIWindowController new]);
  46. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  47. [[NSNotificationCenter defaultCenter] addObserver: self
  48. selector: @selector (receivedWindowWillClose:)
  49. name: @"NSWindowWillCloseNotification"
  50. object: [getController (self) window]];
  51. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  52. return self;
  53. }
  54. static void dealloc (id self, SEL)
  55. {
  56. [getController (self) release];
  57. sendSuperclassMessage<void> (self, @selector (dealloc));
  58. }
  59. static void show (id self, SEL, Rectangle<int>* bounds)
  60. {
  61. if (bounds != nullptr)
  62. {
  63. auto nsBounds = makeNSRect (*bounds);
  64. auto mainScreenHeight = []
  65. {
  66. if ([[NSScreen screens] count] == 0)
  67. return (CGFloat) 0.0f;
  68. return [[[NSScreen screens] objectAtIndex: 0] frame].size.height;
  69. }();
  70. nsBounds.origin.y = mainScreenHeight - (nsBounds.origin.y + nsBounds.size.height);
  71. [getController (self).window setFrame: nsBounds
  72. display: YES];
  73. }
  74. [getController (self) showWindow: nil];
  75. }
  76. static void receivedWindowWillClose (id self, SEL, NSNotification*)
  77. {
  78. [[NSNotificationCenter defaultCenter] removeObserver: self];
  79. auto* cbs = getIvar<Callbacks*> (self, "callbacks");
  80. if (cbs->modalExit != nullptr)
  81. cbs->modalExit->modalStateFinished (0);
  82. cbs->windowClosed();
  83. }
  84. };
  85. class API_AVAILABLE (macos (10.11)) BluetoothMidiSelectorWindowHelper : public DeletedAtShutdown
  86. {
  87. public:
  88. BluetoothMidiSelectorWindowHelper (ModalComponentManager::Callback* exitCallback,
  89. Rectangle<int>* bounds)
  90. {
  91. std::unique_ptr<ModalComponentManager::Callback> exitCB (exitCallback);
  92. static BluetoothMidiPairingWindowClass cls;
  93. window.reset (cls.createInstance());
  94. auto deletionCB = [safeThis = WeakReference<BluetoothMidiSelectorWindowHelper> { this }]
  95. {
  96. if (safeThis != nullptr)
  97. delete safeThis.get();
  98. };
  99. callbacks.reset (new BluetoothMidiPairingWindowClass::Callbacks { std::move (exitCB),
  100. std::move (deletionCB) });
  101. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  102. [window.get() performSelector: @selector (initWithCallbacks:)
  103. withObject: (id) callbacks.get()];
  104. [window.get() performSelector: @selector (show:)
  105. withObject: (id) bounds];
  106. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  107. }
  108. private:
  109. std::unique_ptr<NSObject, NSObjectDeleter> window;
  110. std::unique_ptr<BluetoothMidiPairingWindowClass::Callbacks> callbacks;
  111. JUCE_DECLARE_WEAK_REFERENCEABLE (BluetoothMidiSelectorWindowHelper)
  112. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BluetoothMidiSelectorWindowHelper)
  113. };
  114. //==============================================================================
  115. bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback* exitCallback,
  116. Rectangle<int>* bounds)
  117. {
  118. if (@available (macOS 10.11, *))
  119. {
  120. new BluetoothMidiSelectorWindowHelper (exitCallback, bounds);
  121. return true;
  122. }
  123. std::unique_ptr<ModalComponentManager::Callback> cb (exitCallback);
  124. // This functionality is unavailable when targetting OSX < 10.11. Instead,
  125. // you should pair Bluetooth MIDI devices using the "Audio MIDI Setup" app
  126. // (located in /Applications/Utilities).
  127. jassertfalse;
  128. return false;
  129. }
  130. bool BluetoothMidiDevicePairingDialogue::isAvailable()
  131. {
  132. if (@available (macOS 10.11, *))
  133. return true;
  134. return false;
  135. }
  136. } // namespace juce