mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-21 03:40:40 +08:00
[fix] improve macOS permission request flow
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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] = "";
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user