accessibility_ng/
observer.rs1use std::{ffi::c_void, str::FromStr};
2
3use accessibility_sys_ng::{
4 pid_t, AXObserverAddNotification, AXObserverCallback, AXObserverCallbackWithInfo,
5 AXObserverCreate, AXObserverCreateWithInfoCallback, AXObserverGetRunLoopSource,
6 AXObserverGetTypeID, AXObserverRef, AXObserverRemoveNotification, AXUIElementRef,
7};
8
9use crate::{
10 util::{ax_call, ax_call_void},
11 Error,
12};
13
14use core_foundation::{
15 base::TCFType,
16 declare_TCFType, impl_CFTypeDescription, impl_TCFType,
17 runloop::{kCFRunLoopDefaultMode, CFRunLoop},
18 string::CFString,
19};
20
21use super::AXUIElement;
22
23declare_TCFType!(AXObserver, AXObserverRef);
24impl_TCFType!(AXObserver, AXObserverRef, AXObserverGetTypeID);
25impl_CFTypeDescription!(AXObserver);
26
27unsafe impl Send for AXObserver {}
28
29impl AXObserver {
30 pub fn new(pid: pid_t, callback: AXObserverCallback) -> Result<Self, Error> {
31 unsafe {
32 Ok(TCFType::wrap_under_create_rule(
33 ax_call(|x| AXObserverCreate(pid, callback, x)).map_err(Error::Ax)?,
34 ))
35 }
36 }
37
38 pub fn new_with_info(pid: pid_t, callback: AXObserverCallbackWithInfo) -> Result<Self, Error> {
39 unsafe {
40 Ok(TCFType::wrap_under_create_rule(
41 ax_call(|x| AXObserverCreateWithInfoCallback(pid, callback, x))
42 .map_err(Error::Ax)?,
43 ))
44 }
45 }
46
47 pub fn new_from_bundle(bundle_id: &str, callback: AXObserverCallback) -> Result<Self, Error> {
48 let bundle_ui_element = AXUIElement::application_with_bundle(bundle_id)?;
49 let bundle_pid = bundle_ui_element.pid()?;
50 unsafe {
51 Ok(TCFType::wrap_under_create_rule(
52 ax_call(|x| AXObserverCreate(bundle_pid, callback, x)).map_err(Error::Ax)?,
53 ))
54 }
55 }
56
57 pub fn new_from_bundle_with_info(
58 bundle_id: &str,
59 callback: AXObserverCallbackWithInfo,
60 ) -> Result<Self, Error> {
61 let bundle_ui_element = AXUIElement::application_with_bundle(bundle_id)?;
62 let bundle_pid = bundle_ui_element.pid()?;
63 unsafe {
64 Ok(TCFType::wrap_under_create_rule(
65 ax_call(|x| AXObserverCreateWithInfoCallback(bundle_pid, callback, x))
66 .map_err(Error::Ax)?,
67 ))
68 }
69 }
70
71 pub fn add_notification<T>(
72 &mut self,
73 notification: &str,
74 ui_element: &AXUIElement,
75 mut ctx: T,
76 ) -> Result<(), Error> {
77 unsafe {
78 let notification_cfstr = CFString::from_str(notification).unwrap();
80
81 Ok(ax_call_void(|| {
82 AXObserverAddNotification(
83 self.0,
84 ui_element.as_CFTypeRef() as AXUIElementRef,
85 notification_cfstr.as_concrete_TypeRef(),
86 &mut ctx as *mut _ as *mut c_void,
87 )
88 })
89 .map_err(Error::Ax)?)
90 }
91 }
92
93 pub fn remove_notification(
94 &mut self,
95 notification: &str,
96 ui_element: &AXUIElement,
97 ) -> Result<(), Error> {
98 unsafe {
99 let notification_cfstr = CFString::from_str(notification).unwrap();
101
102 Ok(ax_call_void(|| {
103 AXObserverRemoveNotification(
104 self.0,
105 ui_element.as_CFTypeRef() as AXUIElementRef,
106 notification_cfstr.as_concrete_TypeRef(),
107 )
108 })
109 .map_err(Error::Ax)?)
110 }
111 }
112
113 pub fn start(&self) {
114 let runloop = CFRunLoop::get_current();
115 unsafe {
116 let source = TCFType::wrap_under_create_rule(AXObserverGetRunLoopSource(self.0));
117 runloop.add_source(&source, kCFRunLoopDefaultMode)
118 }
119 }
120
121 pub fn stop(&self) {
122 let runloop = CFRunLoop::get_current();
123 unsafe {
124 let source = TCFType::wrap_under_create_rule(AXObserverGetRunLoopSource(self.0));
125 runloop.remove_source(&source, kCFRunLoopDefaultMode)
126 }
127 }
128}
129
130