diff --git a/src/platform/macos/input.cpp b/src/platform/macos/input.cpp index 6be72233a6e..a34a684172f 100644 --- a/src/platform/macos/input.cpp +++ b/src/platform/macos/input.cpp @@ -8,6 +8,8 @@ #include #include +#include "misc.h" + #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" @@ -226,6 +228,11 @@ const KeyCodeMap kKeyCodesMap[] = { }; // clang-format on + /** + * Used to avoid spamming permission requests when the user receives an input event + */ + bool accessibility_permission_requested; + int keysym(int keycode) { KeyCodeMap key_map {}; @@ -242,12 +249,36 @@ const KeyCodeMap kKeyCodesMap[] = { return temp_map->mac_keycode; } + std::string + default_accessibility_log_msg() { + return "Accessibility permission is not enabled," + " please enable sunshine in " + "[System Settings > Privacy & Security > Privacy > Accessibility]" + ", then please restart Sunshine for it to take effect"; + } + + void + print_accessibility_status(const bool is_keyboard_event, const bool release) { + if (!release) return; + + if (!has_accessibility_permission()) { + if (!accessibility_permission_requested) { + accessibility_permission_requested = true; + request_accessibility_permission(); + } + BOOST_LOG(info) << "Received " << (is_keyboard_event ? "keyboard" : "mouse") << " event but " + << default_accessibility_log_msg(); + } + } + void keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) { auto key = keysym(modcode); BOOST_LOG(debug) << "got keycode: 0x"sv << std::hex << modcode << ", translated to: 0x" << std::hex << key << ", release:" << release; + print_accessibility_status(true, release); + if (key < 0) { return; } @@ -437,6 +468,8 @@ const KeyCodeMap kKeyCodesMap[] = { BOOST_LOG(warning) << "Unsupported mouse button for MacOS: "sv << button; return; } + + print_accessibility_status(false, release); macos_input->mouse_down[mac_button] = !release; @@ -538,6 +571,11 @@ const KeyCodeMap kKeyCodesMap[] = { const auto macos_input = static_cast(result.get()); + accessibility_permission_requested = false; + if (request_accessibility_permission()) { + BOOST_LOG(info) << default_accessibility_log_msg() << ", to allow mouse clicks and keyboard inputs."; + } + // Default to main display macos_input->display = CGMainDisplayID(); diff --git a/src/platform/macos/misc.h b/src/platform/macos/misc.h index 47d22ed4eca..e3a0fb9e2d4 100644 --- a/src/platform/macos/misc.h +++ b/src/platform/macos/misc.h @@ -11,6 +11,20 @@ namespace platf { bool is_screen_capture_allowed(); + + /** + * Prompts the user for Accessibility permission + * @return returns true if requested permission, false if already has permission + */ + bool + request_accessibility_permission(); + + /** + * Checks for Accessibility permission + * @return returns true if sunshine has Accessibility permission enabled + */ + bool + has_accessibility_permission(); } namespace dyn { diff --git a/src/platform/macos/misc.mm b/src/platform/macos/misc.mm index 9f11669f0b9..a40ed8eeb96 100644 --- a/src/platform/macos/misc.mm +++ b/src/platform/macos/misc.mm @@ -8,6 +8,8 @@ #define __APPLE_USE_RFC_3542 1 #endif +#include + #include #include #include @@ -538,6 +540,21 @@ return std::make_unique(sockfd, reset_options); } + + bool + request_accessibility_permission() { + NSDictionary* options = @{static_cast(kAXTrustedCheckOptionPrompt): @YES}; + return !AXIsProcessTrustedWithOptions(static_cast(options)); + } + + bool + has_accessibility_permission() { + NSDictionary* options = @{static_cast(kAXTrustedCheckOptionPrompt): @NO}; + // We use kAXTrustedCheckOptionPrompt == NO here, + // instead of using XIsProcessTrusted(), + // because this will update the accessibility list with sunshine current path + return AXIsProcessTrustedWithOptions(static_cast(options)); + } std::string get_host_name() {