#![allow(unsafe_code)]
use std::ffi::{c_void, CStr};
use crate::error::ComputerError;
use crate::tier::SKYLIGHT_FRONT_SYMBOLS;
#[repr(C)]
#[allow(dead_code)]
struct ProcessSerialNumber {
high_long_of_psn: u32,
low_long_of_psn: u32,
}
#[link(name = "ApplicationServices", kind = "framework")]
extern "C" {
fn GetProcessForPID(pid: libc::pid_t, psn: *mut ProcessSerialNumber) -> i32;
}
const SKYLIGHT: &CStr = c"/System/Library/PrivateFrameworks/SkyLight.framework/SkyLight";
type SlpsFn = unsafe extern "C" fn(*const ProcessSerialNumber, u32, u32) -> i32;
type SlsFn = unsafe extern "C" fn(i32, *const ProcessSerialNumber, u32) -> i32;
type CpsFn = unsafe extern "C" fn(*const ProcessSerialNumber) -> i32;
type ConnFn = unsafe extern "C" fn() -> i32;
pub(crate) fn claim(pid: i32, window_id: u32) -> Result<(), ComputerError> {
let handle = unsafe { libc::dlopen(SKYLIGHT.as_ptr(), libc::RTLD_LAZY) };
if handle.is_null() {
return Err(ComputerError::Unsupported(
"SkyLight.framework did not load".into(),
));
}
let outcome = claim_loaded(handle, pid, window_id);
unsafe { libc::dlclose(handle) };
outcome
}
fn claim_loaded(handle: *mut c_void, pid: i32, window_id: u32) -> Result<(), ComputerError> {
let slps = symbol(handle, c"SLPSSetFrontProcessWithOptions");
let sls = symbol(handle, c"SLSSetFrontProcessWithOptions");
let cps = symbol(handle, c"CPSSetFrontProcess");
let connection = symbol(handle, c"SLSMainConnectionID");
if slps.is_null() && sls.is_null() && cps.is_null() {
return Err(ComputerError::Unsupported(format!(
"SkyLight symbol {} was not found",
SKYLIGHT_FRONT_SYMBOLS.join(", ")
)));
}
if slps.is_null() && cps.is_null() && !sls.is_null() && connection.is_null() {
return Err(ComputerError::Unsupported(
"SkyLight symbol SLSMainConnectionID was not found".into(),
));
}
let mut psn: ProcessSerialNumber = unsafe { std::mem::zeroed() };
let lookup = unsafe { GetProcessForPID(pid, &mut psn) };
if lookup != 0 {
return Err(ComputerError::ExecutionFailed(format!(
"GetProcessForPID failed: {lookup}"
)));
}
let status = if !slps.is_null() {
let function: SlpsFn = unsafe { std::mem::transmute(slps) };
unsafe { function(&psn, window_id, 0) }
} else if !sls.is_null() && !connection.is_null() {
let connection: ConnFn = unsafe { std::mem::transmute(connection) };
let function: SlsFn = unsafe { std::mem::transmute(sls) };
let connection_id = unsafe { connection() };
unsafe { function(connection_id, &psn, 0) }
} else if !cps.is_null() {
let function: CpsFn = unsafe { std::mem::transmute(cps) };
unsafe { function(&psn) }
} else {
return Err(ComputerError::Unsupported(format!(
"SkyLight symbol {} was not found",
SKYLIGHT_FRONT_SYMBOLS.join(", ")
)));
};
if status != 0 {
return Err(ComputerError::ExecutionFailed(format!(
"synthetic focus failed: {status}"
)));
}
Ok(())
}
fn symbol(handle: *mut c_void, name: &CStr) -> *mut c_void {
unsafe { libc::dlsym(handle, name.as_ptr()) }
}