[fix] improve macOS permission request flow

This commit is contained in:
dijunkun
2026-05-28 01:33:50 +08:00
parent 4895ac9c23
commit 5ff6b601c7
3 changed files with 133 additions and 24 deletions
+25
View File
@@ -636,6 +636,12 @@ int Render::LoadSettingsFromCacheFile() {
} }
int Render::ScreenCapturerInit() { int Render::ScreenCapturerInit() {
#ifdef __APPLE__
if (!EnsureMacScreenRecordingPermission()) {
return -1;
}
#endif
if (!screen_capturer_) { if (!screen_capturer_) {
screen_capturer_ = (ScreenCapturer*)screen_capturer_factory_->Create(); screen_capturer_ = (ScreenCapturer*)screen_capturer_factory_->Create();
} }
@@ -705,6 +711,12 @@ int Render::ScreenCapturerInit() {
} }
int Render::StartScreenCapturer() { int Render::StartScreenCapturer() {
#ifdef __APPLE__
if (!EnsureMacScreenRecordingPermission()) {
return -1;
}
#endif
if (!screen_capturer_) { if (!screen_capturer_) {
LOG_INFO("Screen capturer instance missing, recreating before start"); LOG_INFO("Screen capturer instance missing, recreating before start");
if (0 != ScreenCapturerInit()) { if (0 != ScreenCapturerInit()) {
@@ -769,6 +781,12 @@ int Render::StopSpeakerCapturer() {
} }
int Render::StartMouseController() { int Render::StartMouseController() {
#ifdef __APPLE__
if (!EnsureMacAccessibilityPermission()) {
return -1;
}
#endif
if (!device_controller_factory_) { if (!device_controller_factory_) {
LOG_INFO("Device controller factory is nullptr"); LOG_INFO("Device controller factory is nullptr");
return -1; return -1;
@@ -826,6 +844,13 @@ int Render::StopMouseController() {
int Render::StartKeyboardCapturer() { int Render::StartKeyboardCapturer() {
keyboard_capturer_uses_sdl_events_ = false; keyboard_capturer_uses_sdl_events_ = false;
#ifdef __APPLE__
if (!EnsureMacAccessibilityPermission()) {
keyboard_capturer_uses_sdl_events_ = true;
return 0;
}
#endif
#if defined(__linux__) && !defined(__APPLE__) #if defined(__linux__) && !defined(__APPLE__)
if (IsWaylandSession()) { if (IsWaylandSession()) {
keyboard_capturer_uses_sdl_events_ = true; keyboard_capturer_uses_sdl_events_ = true;
+9
View File
@@ -296,6 +296,9 @@ class Render {
void OpenScreenRecordingPreferences(); void OpenScreenRecordingPreferences();
void OpenAccessibilityPreferences(); void OpenAccessibilityPreferences();
bool DrawToggleSwitch(const char* id, bool active, bool enabled); bool DrawToggleSwitch(const char* id, bool active, bool enabled);
void RefreshMacPermissionStatus(bool force);
bool EnsureMacScreenRecordingPermission();
bool EnsureMacAccessibilityPermission();
#endif #endif
public: public:
@@ -699,6 +702,12 @@ class Render {
double new_version_icon_render_start_time_ = 0.0; double new_version_icon_render_start_time_ = 0.0;
#ifdef __APPLE__ #ifdef __APPLE__
bool show_request_permission_window_ = true; bool show_request_permission_window_ = true;
bool mac_permission_status_initialized_ = false;
uint32_t mac_permission_last_check_tick_ = 0;
bool mac_screen_recording_permission_granted_ = false;
bool mac_accessibility_permission_granted_ = false;
bool mac_screen_recording_permission_requested_ = false;
bool mac_accessibility_permission_requested_ = false;
#endif #endif
char client_id_[10] = ""; char client_id_[10] = "";
char client_id_display_[12] = ""; char client_id_display_[12] = "";
+99 -24
View File
@@ -6,11 +6,27 @@
#include <ApplicationServices/ApplicationServices.h> #include <ApplicationServices/ApplicationServices.h>
#include <CoreGraphics/CoreGraphics.h> #include <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include <unistd.h>
#include <cstdlib> #include <cstdlib>
#include <string>
namespace crossdesk { namespace crossdesk {
namespace {
constexpr uint32_t kPermissionRefreshIntervalVisibleMs = 500;
void OpenPrivacyPreferences(const char* pane) {
if (pane == nullptr || pane[0] == '\0') {
return;
}
std::string command =
"open \"x-apple.systempreferences:com.apple.preference.security?";
command += pane;
command += "\"";
system(command.c_str());
}
} // namespace
bool Render::DrawToggleSwitch(const char* id, bool active, bool enabled) { bool Render::DrawToggleSwitch(const char* id, bool active, bool enabled) {
const float TRACK_HEIGHT = ImGui::GetFrameHeight(); const float TRACK_HEIGHT = ImGui::GetFrameHeight();
const float TRACK_WIDTH = TRACK_HEIGHT * 1.8f; const float TRACK_WIDTH = TRACK_HEIGHT * 1.8f;
@@ -35,16 +51,19 @@ bool Render::DrawToggleSwitch(const char* id, bool active, bool enabled) {
bool hovered = ImGui::IsItemHovered(); bool hovered = ImGui::IsItemHovered();
bool clicked = ImGui::IsItemClicked() && enabled; bool clicked = ImGui::IsItemClicked() && enabled;
ImVec4 track_color = active ? (hovered && enabled ? COLOR_ACTIVE_HOVER : COLOR_ACTIVE) ImVec4 track_color =
: (hovered && enabled ? COLOR_INACTIVE_HOVER : COLOR_INACTIVE); active ? (hovered && enabled ? COLOR_ACTIVE_HOVER : COLOR_ACTIVE)
: (hovered && enabled ? COLOR_INACTIVE_HOVER : COLOR_INACTIVE);
if (!enabled) { if (!enabled) {
track_color.w *= DISABLED_ALPHA; track_color.w *= DISABLED_ALPHA;
} }
ImVec2 track_min = ImVec2(track_pos.x, track_pos.y + 0.5f); ImVec2 track_min = ImVec2(track_pos.x, track_pos.y + 0.5f);
ImVec2 track_max = ImVec2(track_pos.x + TRACK_WIDTH, track_pos.y + TRACK_HEIGHT - 0.5f); ImVec2 track_max = ImVec2(track_pos.x + TRACK_WIDTH,
draw_list->AddRectFilled(track_min, track_max, ImGui::GetColorU32(track_color), TRACK_RADIUS); track_pos.y + TRACK_HEIGHT - 0.5f);
draw_list->AddRectFilled(track_min, track_max,
ImGui::GetColorU32(track_color), TRACK_RADIUS);
float knob_position = active ? 1.0f : 0.0f; float knob_position = active ? 1.0f : 0.0f;
float knob_min_x = track_pos.x + KNOB_PADDING; float knob_min_x = track_pos.x + KNOB_PADDING;
@@ -59,7 +78,8 @@ bool Render::DrawToggleSwitch(const char* id, bool active, bool enabled) {
ImVec2 knob_min = ImVec2(knob_x, knob_y); ImVec2 knob_min = ImVec2(knob_x, knob_y);
ImVec2 knob_max = ImVec2(knob_x + KNOB_WIDTH, knob_y + KNOB_HEIGHT); ImVec2 knob_max = ImVec2(knob_x + KNOB_WIDTH, knob_y + KNOB_HEIGHT);
draw_list->AddRectFilled(knob_min, knob_max, ImGui::GetColorU32(knob_color), KNOB_RADIUS); draw_list->AddRectFilled(knob_min, knob_max,
ImGui::GetColorU32(knob_color), KNOB_RADIUS);
return clicked; return clicked;
} }
@@ -81,29 +101,82 @@ bool Render::CheckAccessibilityPermission() {
} }
void Render::OpenAccessibilityPreferences() { void Render::OpenAccessibilityPreferences() {
NSDictionary* options = @{(__bridge id)kAXTrustedCheckOptionPrompt : @YES}; if (!mac_accessibility_permission_requested_) {
AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)options); NSDictionary* options = @{(__bridge id)kAXTrustedCheckOptionPrompt : @YES};
AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)options);
system("open " } else {
"\"x-apple.systempreferences:com.apple.preference.security?Privacy_" OpenPrivacyPreferences("Privacy_Accessibility");
"Accessibility\""); }
} }
void Render::OpenScreenRecordingPreferences() { void Render::OpenScreenRecordingPreferences() {
if (@available(macOS 10.15, *)) { if (@available(macOS 10.15, *)) {
CGRequestScreenCaptureAccess(); if (!mac_screen_recording_permission_requested_) {
CGRequestScreenCaptureAccess();
} else {
OpenPrivacyPreferences("Privacy_ScreenCapture");
}
} else {
OpenPrivacyPreferences("Privacy_ScreenCapture");
}
}
void Render::RefreshMacPermissionStatus(bool force) {
const uint32_t now = static_cast<uint32_t>(SDL_GetTicks());
if (!force && mac_permission_status_initialized_ &&
now - mac_permission_last_check_tick_ <
kPermissionRefreshIntervalVisibleMs) {
return;
} }
system("open " const bool old_screen_recording_granted =
"\"x-apple.systempreferences:com.apple.preference.security?Privacy_" mac_screen_recording_permission_granted_;
"ScreenCapture\""); const bool old_accessibility_granted = mac_accessibility_permission_granted_;
mac_screen_recording_permission_granted_ =
CheckScreenRecordingPermission();
mac_accessibility_permission_granted_ = CheckAccessibilityPermission();
mac_permission_last_check_tick_ = now;
mac_permission_status_initialized_ = true;
if (old_screen_recording_granted !=
mac_screen_recording_permission_granted_ ||
old_accessibility_granted != mac_accessibility_permission_granted_) {
LOG_INFO("macOS permission status: screen_recording={}, accessibility={}",
mac_screen_recording_permission_granted_,
mac_accessibility_permission_granted_);
}
}
bool Render::EnsureMacScreenRecordingPermission() {
RefreshMacPermissionStatus(false);
if (mac_screen_recording_permission_granted_) {
return true;
}
show_request_permission_window_ = true;
return false;
}
bool Render::EnsureMacAccessibilityPermission() {
RefreshMacPermissionStatus(false);
if (mac_accessibility_permission_granted_) {
return true;
}
show_request_permission_window_ = true;
return false;
} }
int Render::RequestPermissionWindow() { int Render::RequestPermissionWindow() {
bool screen_recording_granted = CheckScreenRecordingPermission(); RefreshMacPermissionStatus(false);
bool accessibility_granted = CheckAccessibilityPermission();
show_request_permission_window_ = !screen_recording_granted || !accessibility_granted; const bool screen_recording_granted =
mac_screen_recording_permission_granted_;
const bool accessibility_granted = mac_accessibility_permission_granted_;
show_request_permission_window_ =
!screen_recording_granted || !accessibility_granted;
if (!show_request_permission_window_) { if (!show_request_permission_window_) {
return 0; return 0;
@@ -162,8 +235,10 @@ int Render::RequestPermissionWindow() {
if (accessibility_granted) { if (accessibility_granted) {
DrawToggleSwitch("accessibility_toggle_on", true, false); DrawToggleSwitch("accessibility_toggle_on", true, false);
} else { } else {
if (DrawToggleSwitch("accessibility_toggle", accessibility_granted, !accessibility_granted)) { if (DrawToggleSwitch("accessibility_toggle", false, true)) {
OpenAccessibilityPreferences(); OpenAccessibilityPreferences();
mac_accessibility_permission_requested_ = true;
RefreshMacPermissionStatus(true);
} }
} }
@@ -178,12 +253,12 @@ int Render::RequestPermissionWindow() {
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
ImGui::SetCursorPosX(checkbox_padding); ImGui::SetCursorPosX(checkbox_padding);
if (screen_recording_granted) { if (screen_recording_granted) {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.0f);
DrawToggleSwitch("screen_recording_toggle_on", true, false); DrawToggleSwitch("screen_recording_toggle_on", true, false);
} else { } else {
if (DrawToggleSwitch("screen_recording_toggle", screen_recording_granted, if (DrawToggleSwitch("screen_recording_toggle", false, true)) {
!screen_recording_granted)) {
OpenScreenRecordingPreferences(); OpenScreenRecordingPreferences();
mac_screen_recording_permission_requested_ = true;
RefreshMacPermissionStatus(true);
} }
} }
@@ -202,4 +277,4 @@ int Render::RequestPermissionWindow() {
return 0; return 0;
} }
} // namespace crossdesk } // namespace crossdesk