use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use core_foundation::array::CFArray;
use core_foundation::base::{CFType, CFTypeRef, TCFType};
use super::ffi;
static SECURE_INPUT_ENABLED: AtomicBool = AtomicBool::new(false);
struct PreviousInputSource(Option<*mut ffi::TISInputSource>);
unsafe impl Send for PreviousInputSource {}
unsafe impl Sync for PreviousInputSource {}
static PREVIOUS_INPUT_SOURCE: Mutex<PreviousInputSource> =
Mutex::new(PreviousInputSource(None));
pub fn set_secure_event_input(enabled: bool) {
if enabled == SECURE_INPUT_ENABLED.swap(enabled, Ordering::SeqCst) {
return;
}
if enabled {
unsafe { ffi::EnableSecureEventInput() };
} else {
unsafe { ffi::DisableSecureEventInput() };
}
}
pub fn enter_password_mode() {
let current = unsafe { ffi::TISCopyCurrentKeyboardInputSource() };
if current.is_null() {
return;
}
let mut guard = PREVIOUS_INPUT_SOURCE.lock().unwrap();
if guard.0.is_none() {
guard.0 = Some(current);
} else {
release_input_source(current);
}
drop(guard);
restrict_to_ascii_input_sources();
}
pub fn leave_password_mode() {
unrestrict_input_sources();
let mut guard = PREVIOUS_INPUT_SOURCE.lock().unwrap();
if let Some(previous) = guard.0.take() {
unsafe { ffi::TISSelectInputSource(previous) };
release_input_source(previous);
}
}
fn restrict_to_ascii_input_sources() {
let sources = unsafe { ffi::TISCreateASCIICapableInputSourceList() };
if sources.is_null() {
return;
}
unsafe {
ffi::TSMSetDocumentProperty(
std::ptr::null_mut(),
ffi::kTSMDocumentEnabledInputSourcesPropertyTag,
std::mem::size_of::<*const std::ffi::c_void>() as u32,
std::ptr::addr_of!(sources) as *mut std::ffi::c_void,
)
};
let sources = unsafe { CFArray::<CFType>::wrap_under_create_rule(sources) };
if let Some(source) = sources.iter().next() {
unsafe { ffi::TISSelectInputSource(source.as_concrete_TypeRef().cast_mut().cast()) };
}
}
fn unrestrict_input_sources() {
unsafe {
ffi::TSMRemoveDocumentProperty(
std::ptr::null_mut(),
ffi::kTSMDocumentEnabledInputSourcesPropertyTag,
)
};
}
fn release_input_source(source: *mut ffi::TISInputSource) {
unsafe { core_foundation::base::CFRelease(source as CFTypeRef) };
}