diff --git a/3rdparty/libossia b/3rdparty/libossia
index dbbf02defb..e8d84aaa46 160000
--- a/3rdparty/libossia
+++ b/3rdparty/libossia
@@ -1 +1 @@
-Subproject commit dbbf02defbcf580abef3ffeb43597ffd65acae43
+Subproject commit e8d84aaa46e859599cc8cb15cd3c25a5004c0657
diff --git a/src/app/Info.plist.in b/src/app/Info.plist.in
index d1f0344f9c..7c6a82a106 100644
--- a/src/app/Info.plist.in
+++ b/src/app/Info.plist.in
@@ -43,6 +43,9 @@
NSHumanReadableCopyright
${MACOSX_BUNDLE_COPYRIGHT}
+ NSAppleEventsUsageDescription
+ ossia score needs assistive access for MIDI keyboard input and minimap movement
+
NSMicrophoneUsageDescription
ossia score needs microphone permission for audio input.
diff --git a/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIDevice.cpp b/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIDevice.cpp
index 07a194f134..cc4e9fd40f 100644
--- a/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIDevice.cpp
+++ b/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIDevice.cpp
@@ -8,6 +8,8 @@
#include
+#include
+#include
#include
#include
@@ -29,48 +31,11 @@
#include
// clang-format on
-#include
+#include
+#include
namespace Protocols
{
-class MidiKeyboardEventFilter : public QObject
-{
-public:
- libremidi::kbd_input_configuration::scancode_callback press, release;
- MidiKeyboardEventFilter(
- libremidi::kbd_input_configuration::scancode_callback press,
- libremidi::kbd_input_configuration::scancode_callback release)
- : press{std::move(press)}
- , release{std::move(release)}
- , target{score::GUIAppContext().mainWindow}
- {
- // X11 quirk
- if(qApp->platformName() == "xcb")
- scanCodeOffset = -8;
- }
-
- bool eventFilter(QObject* object, QEvent* event)
- {
- if(object == target)
- {
- if(event->type() == QEvent::KeyPress)
- {
- QKeyEvent* keyEvent = static_cast(event);
- if(!keyEvent->isAutoRepeat())
- press(keyEvent->nativeScanCode() + scanCodeOffset);
- }
- else if(event->type() == QEvent::KeyRelease)
- {
- QKeyEvent* keyEvent = static_cast(event);
- if(!keyEvent->isAutoRepeat())
- release(keyEvent->nativeScanCode() + scanCodeOffset);
- }
- }
- return false;
- }
- QObject* target{};
- int scanCodeOffset{0};
-};
MIDIDevice::MIDIDevice(
const Device::DeviceSettings& settings, const ossia::net::network_context_ptr& ctx)
@@ -119,7 +84,7 @@ bool MIDIDevice::reconnect()
case libremidi::API::ALSA_SEQ: {
conf.timestamps = libremidi::timestamp_mode::AudioFrame;
- auto ptr = std::any_cast(&api_conf);
+ auto ptr = std::get_if(&api_conf);
SCORE_ASSERT(ptr);
ptr->client_name = "ossia score";
break;
@@ -133,11 +98,13 @@ bool MIDIDevice::reconnect()
break;
}
case libremidi::API::KEYBOARD: {
- auto ptr = std::any_cast(&api_conf);
+ auto ptr = std::get_if(&api_conf);
SCORE_ASSERT(ptr);
ptr->set_input_scancode_callbacks = [this](auto keypress, auto keyrelease) {
m_kbdfilter = new MidiKeyboardEventFilter{keypress, keyrelease};
+#if !defined(__APPLE__)
qApp->installEventFilter(m_kbdfilter);
+#endif
};
break;
}
diff --git a/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIKeyboardEventFilter.linux.hpp b/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIKeyboardEventFilter.linux.hpp
new file mode 100644
index 0000000000..0b5343ef26
--- /dev/null
+++ b/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIKeyboardEventFilter.linux.hpp
@@ -0,0 +1,62 @@
+#pragma once
+#if !defined(__APPLE__)
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+namespace Protocols
+{
+class MidiKeyboardEventFilter : public QObject
+{
+public:
+ libremidi::kbd_input_configuration::scancode_callback press, release;
+ MidiKeyboardEventFilter(
+ libremidi::kbd_input_configuration::scancode_callback press,
+ libremidi::kbd_input_configuration::scancode_callback release)
+ : press{std::move(press)}
+ , release{std::move(release)}
+ , target{score::GUIAppContext().mainWindow}
+ {
+ }
+
+ bool eventFilter(QObject* object, QEvent* event)
+ {
+ if(object == target)
+ {
+ if(event->type() == QEvent::KeyPress)
+ {
+ QKeyEvent* keyEvent = static_cast(event);
+ if(!keyEvent->isAutoRepeat())
+ press(key(*keyEvent));
+ }
+ else if(event->type() == QEvent::KeyRelease)
+ {
+ QKeyEvent* keyEvent = static_cast(event);
+ if(!keyEvent->isAutoRepeat())
+ release(key(*keyEvent));
+ }
+ }
+ return false;
+ }
+
+ inline uint32_t key(QKeyEvent& e)
+ {
+#if defined(__linux__)
+ // X11 quirk
+ static const int scanCodeOffset = (qApp->platformName() == "xcb") ? -8 : 0;
+ return e.nativeScanCode() - scanCodeOffset;
+#elif defined(__APPLE__)
+ return e.nativeVirtualKey();
+#else
+ return e.nativeScanCode();
+#endif
+ }
+ QObject* target{};
+};
+}
+#endif
diff --git a/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIKeyboardEventFilter.macos.hpp b/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIKeyboardEventFilter.macos.hpp
new file mode 100644
index 0000000000..95cdcc6667
--- /dev/null
+++ b/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIKeyboardEventFilter.macos.hpp
@@ -0,0 +1,53 @@
+#pragma once
+#if defined(__APPLE__)
+#include
+#include
+
+#include
+#include
+
+namespace Protocols
+{
+class MidiKeyboardEventFilter : public QObject
+{
+public:
+ libremidi::kbd_input_configuration::scancode_callback press, release;
+ MidiKeyboardEventFilter(
+ libremidi::kbd_input_configuration::scancode_callback press,
+ libremidi::kbd_input_configuration::scancode_callback release)
+ : press{std::move(press)}
+ , release{std::move(release)}
+ {
+ CGEventMask mask = CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp)
+ | CGEventMaskBit(kCGEventScrollWheel)
+ | CGEventMaskBit(kCGEventLeftMouseDown)
+ | CGEventMaskBit(kCGEventRightMouseDown);
+ auto eventTap = CGEventTapCreate(
+ kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, mask,
+ [](CGEventTapProxy, CGEventType type, CGEventRef event, void* ctx) {
+ auto& self = *decltype(this)(ctx);
+ switch(type)
+ {
+ case kCGEventKeyDown:
+ self.press(CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode));
+ break;
+ case kCGEventKeyUp:
+ self.release(CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode));
+ break;
+ }
+ return event;
+ }, this);
+ if(eventTap == nullptr)
+ return;
+
+ CFRunLoopAddSource(
+ CFRunLoopGetCurrent(),
+ CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0),
+ kCFRunLoopCommonModes);
+ // Enable the event tap.
+ CGEventTapEnable(eventTap, true);
+ }
+};
+}
+
+#endif
diff --git a/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIProtocolFactory.cpp b/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIProtocolFactory.cpp
index a78b4feb92..7449a0d601 100644
--- a/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIProtocolFactory.cpp
+++ b/src/plugins/score-plugin-protocols/Protocols/MIDI/MIDIProtocolFactory.cpp
@@ -21,8 +21,10 @@
#include
#include
-#include
+// clang-format off
#include
+#include
+// clang-format on
namespace Device
{
class DeviceInterface;
@@ -71,7 +73,7 @@ template
class MidiEnumerator : public Device::DeviceEnumerator
{
libremidi::API m_api = getCurrentAPI();
- std::any m_observer_conf = [this] {
+ libremidi::observer_api_configuration m_observer_conf = [this] {
auto api_conf = libremidi::observer_configuration_for(m_api);
libremidi::midi_any::for_observer_configuration([&](auto& conf) {
diff --git a/src/plugins/score-plugin-protocols/Protocols/MIDIUtils.hpp b/src/plugins/score-plugin-protocols/Protocols/MIDIUtils.hpp
index b2ba43f9e8..fb4a18c88a 100644
--- a/src/plugins/score-plugin-protocols/Protocols/MIDIUtils.hpp
+++ b/src/plugins/score-plugin-protocols/Protocols/MIDIUtils.hpp
@@ -4,8 +4,7 @@
#include
-#include
-#include
+#include
namespace Protocols
{
inline libremidi::API getCurrentAPI()