Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions assets/entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<false/>
<key>com.apple.security.device.input-monitoring</key>
<true/>
<key>com.apple.security.temporary-exception.accessibility</key>
<true/>
</dict>
</plist>
3 changes: 3 additions & 0 deletions scripts/sign-macos.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env -S bash -e

ENTITLEMENTS_PATH="assets/entitlements.plist"

APP_BUNDLE_PATH="${APP_BUNDLE_PATH:?APP_BUNDLE_PATH not set}"

# 1. Create a temporary keychain and import certificate
Expand Down Expand Up @@ -29,6 +31,7 @@ security set-key-partition-list -S apple-tool:,apple:,codesign: \

# 2. Sign app bundle
codesign --deep --force --options runtime --timestamp \
--entitlements $ENTITLEMENTS_PATH \
--sign "$MACOS_CERTIFICATE_NAME" \
"$APP_BUNDLE_PATH"

Expand Down
90 changes: 90 additions & 0 deletions src/platform/macos/accessibility.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/// Taken from: <https://github.com/acsandmann/rift/blob/main/src/sys/accessibility.rs>
use std::ffi::c_void;
use std::thread;
use std::time::{Duration, Instant};

use log::info;
use objc2::rc::autoreleasepool;
use objc2::runtime::AnyObject;
use objc2::{class, msg_send};

#[link(name = "ApplicationServices", kind = "framework")]
unsafe extern "C" {
fn AXIsProcessTrustedWithOptions(options: *const c_void) -> bool;

static kAXTrustedCheckOptionPrompt: *const c_void;
}

#[link(name = "CoreFoundation", kind = "framework")]
unsafe extern "C" {
static kCFBooleanTrue: *const c_void;
static kCFBooleanFalse: *const c_void;
}

const AX_POLL_INTERVAL: Duration = Duration::from_millis(250);
const AX_POLL_TIMEOUT: Duration = Duration::from_secs(30);

#[inline]
fn ax_is_trusted() -> bool {
unsafe {
autoreleasepool(|_| {
let keys: [*mut AnyObject; 1] = [kAXTrustedCheckOptionPrompt as *mut AnyObject];
let vals: [*mut AnyObject; 1] = [kCFBooleanFalse as *mut AnyObject];
let dict: *mut AnyObject = msg_send![
class!(NSDictionary),
dictionaryWithObjects: vals.as_ptr(),
forKeys: keys.as_ptr(),
count: 1usize
];

AXIsProcessTrustedWithOptions(dict.cast())
})
}
}

#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn prompt_ax_trust_dialog() {
autoreleasepool(|_| {
let keys: [*mut AnyObject; 1] = [kAXTrustedCheckOptionPrompt as *mut AnyObject];
let vals: [*mut AnyObject; 1] = [kCFBooleanTrue as *mut AnyObject];

let dict: *mut AnyObject = msg_send![
class!(NSDictionary),
dictionaryWithObjects: vals.as_ptr(),
forKeys: keys.as_ptr(),
count: 1usize
];

let _ = AXIsProcessTrustedWithOptions(dict.cast());
});
}

pub fn ensure_accessibility_permission() {
if ax_is_trusted() {
return;
}

info!("Accessibility permission is not granted; prompting user for permission now.");

unsafe { prompt_ax_trust_dialog() };

let start = Instant::now();
loop {
if ax_is_trusted() {
info!("Accessibility permission granted");
return;
}

if start.elapsed() >= AX_POLL_TIMEOUT {
break;
}

thread::sleep(AX_POLL_INTERVAL);
}

println!(
"Rustcast still does not have accessibility permission. Enable it in System Settings > Privacy & Security > Accessibility, then restart Rustcast."
);

std::process::exit(1);
}
6 changes: 5 additions & 1 deletion src/platform/macos/launching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ use std::sync::{Arc, Mutex};
use block2::RcBlock;
use objc2_app_kit::{NSEvent, NSEventMask, NSEventModifierFlags, NSEventType};

use crate::app::{Message, tile::ExtSender};
use crate::{
app::{Message, tile::ExtSender},
platform::macos::accessibility::ensure_accessibility_permission,
};

pub fn global_handler(sender: ExtSender) {
ensure_accessibility_permission();
local_handler(sender.clone());
let mask = NSEventMask::KeyDown | NSEventMask::FlagsChanged;
let sender = Arc::new(Mutex::new(sender.0.clone()));
Expand Down
Empty file removed src/platform/macos/login.rs
Empty file.
1 change: 1 addition & 0 deletions src/platform/macos/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Macos specific logic, such as window settings, etc.
pub mod accessibility;
pub mod discovery;
pub mod haptics;
pub mod launching;
Expand Down
Loading