1use crate::common::{Result, HWND};
15use std::fmt::{Debug, Display, Formatter};
16pub use windows::Win32::UI::{
17 Accessibility::{
18 ROLE_SYSTEM_ALERT, ROLE_SYSTEM_ANIMATION, ROLE_SYSTEM_APPLICATION, ROLE_SYSTEM_BORDER,
19 ROLE_SYSTEM_BUTTONDROPDOWN, ROLE_SYSTEM_BUTTONDROPDOWNGRID, ROLE_SYSTEM_BUTTONMENU,
20 ROLE_SYSTEM_CARET, ROLE_SYSTEM_CELL, ROLE_SYSTEM_CHARACTER, ROLE_SYSTEM_CHART,
21 ROLE_SYSTEM_CHECKBUTTON, ROLE_SYSTEM_CLIENT, ROLE_SYSTEM_CLOCK, ROLE_SYSTEM_COLUMN,
22 ROLE_SYSTEM_COLUMNHEADER, ROLE_SYSTEM_COMBOBOX, ROLE_SYSTEM_CURSOR, ROLE_SYSTEM_DIAGRAM,
23 ROLE_SYSTEM_DIAL, ROLE_SYSTEM_DIALOG, ROLE_SYSTEM_DOCUMENT, ROLE_SYSTEM_DROPLIST,
24 ROLE_SYSTEM_EQUATION, ROLE_SYSTEM_GRAPHIC, ROLE_SYSTEM_GRIP, ROLE_SYSTEM_GROUPING,
25 ROLE_SYSTEM_HELPBALLOON, ROLE_SYSTEM_HOTKEYFIELD, ROLE_SYSTEM_INDICATOR,
26 ROLE_SYSTEM_IPADDRESS, ROLE_SYSTEM_LINK, ROLE_SYSTEM_LIST, ROLE_SYSTEM_LISTITEM,
27 ROLE_SYSTEM_MENUBAR, ROLE_SYSTEM_MENUITEM, ROLE_SYSTEM_MENUPOPUP, ROLE_SYSTEM_OUTLINE,
28 ROLE_SYSTEM_OUTLINEBUTTON, ROLE_SYSTEM_OUTLINEITEM, ROLE_SYSTEM_PAGETAB,
29 ROLE_SYSTEM_PAGETABLIST, ROLE_SYSTEM_PANE, ROLE_SYSTEM_PROGRESSBAR,
30 ROLE_SYSTEM_PROPERTYPAGE, ROLE_SYSTEM_PUSHBUTTON, ROLE_SYSTEM_RADIOBUTTON, ROLE_SYSTEM_ROW,
31 ROLE_SYSTEM_ROWHEADER, ROLE_SYSTEM_SCROLLBAR, ROLE_SYSTEM_SEPARATOR, ROLE_SYSTEM_SLIDER,
32 ROLE_SYSTEM_SOUND, ROLE_SYSTEM_SPINBUTTON, ROLE_SYSTEM_SPLITBUTTON, ROLE_SYSTEM_STATICTEXT,
33 ROLE_SYSTEM_STATUSBAR, ROLE_SYSTEM_TABLE, ROLE_SYSTEM_TEXT, ROLE_SYSTEM_TITLEBAR,
34 ROLE_SYSTEM_TOOLBAR, ROLE_SYSTEM_TOOLTIP, ROLE_SYSTEM_WHITESPACE, ROLE_SYSTEM_WINDOW,
35 STATE_SYSTEM_HASPOPUP, STATE_SYSTEM_NORMAL,
36 },
37 WindowsAndMessaging::{
38 OBJID_ALERT, OBJID_CARET, OBJID_CLIENT, OBJID_CURSOR, OBJID_HSCROLL, OBJID_MENU,
39 OBJID_NATIVEOM, OBJID_QUERYCLASSNAMEIDX, OBJID_SIZEGRIP, OBJID_SOUND, OBJID_SYSMENU,
40 OBJID_TITLEBAR, OBJID_VSCROLL, OBJID_WINDOW,
41 },
42};
43
44use windows::{
45 core::{Error, Interface, Type, BSTR},
46 Win32::{
47 Foundation::{POINT, S_FALSE},
48 System::{Com::IDispatch, Variant::VARIANT},
49 UI::Accessibility::{
50 AccessibleChildren, AccessibleObjectFromEvent, AccessibleObjectFromPoint,
51 AccessibleObjectFromWindow, GetRoleTextW, GetStateTextW, IAccessible,
52 WindowFromAccessibleObject,
53 },
54 },
55};
56
57#[derive(Clone)]
58pub struct AccessibleObject(IAccessible, i32);
59
60impl AccessibleObject {
61 pub(crate) fn from_raw(acc: IAccessible, child: i32) -> Self {
62 Self(acc, child)
63 }
64 pub fn get_raw(&self) -> &IAccessible {
65 &self.0
66 }
67 pub fn get_child_id(&self) -> i32 {
68 self.1
69 }
70
71 pub fn from_window(h_wnd: HWND) -> Result<Self> {
76 let acc = unsafe {
78 let mut p_acc = std::mem::zeroed();
79 if let Err(e) = AccessibleObjectFromWindow(
80 h_wnd,
81 OBJID_WINDOW.0 as u32,
82 &IAccessible::IID,
83 &mut p_acc,
84 ) {
85 return Err(e);
86 }
87 match IAccessible::from_abi(p_acc) {
88 Err(e) => return Err(e),
89 Ok(r) => r,
90 }
91 };
92 Ok(Self(acc, 0))
93 }
94
95 pub fn from_caret() -> Result<Self> {
99 let acc = unsafe {
101 let mut p_acc = std::mem::zeroed();
102 if let Err(e) = AccessibleObjectFromWindow(
103 Default::default(),
104 OBJID_CARET.0 as u32,
105 &IAccessible::IID,
106 &mut p_acc,
107 ) {
108 return Err(e);
109 }
110 match IAccessible::from_abi(p_acc) {
111 Err(e) => return Err(e),
112 Ok(r) => r,
113 }
114 };
115 Ok(Self(acc, 0))
116 }
117
118 pub fn from_point(x: i32, y: i32) -> Result<(Self, i32)> {
124 let acc = unsafe {
126 let mut p_acc: Option<IAccessible> = None;
127 let point = POINT { x, y };
128 let mut var = Default::default();
129 AccessibleObjectFromPoint(point, &mut p_acc, &mut var)?;
130 match p_acc {
131 None => {
132 return Err(Error::new(
133 S_FALSE,
134 &format!("Can't obtain the accessible object at ({}, {}).", x, y),
135 ));
136 }
137 Some(r) => (r, i32::try_from(&var).unwrap_or(0)),
138 }
139 };
140 Ok((Self(acc.0, acc.1), acc.1))
141 }
142
143 pub fn from_event(h_wnd: HWND, id: i32, child_id: i32) -> Result<(Self, i32)> {
151 let acc = unsafe {
153 let mut p_acc = std::mem::zeroed();
154 let mut var = std::mem::zeroed();
155 if let Err(e) =
156 AccessibleObjectFromEvent(h_wnd, id as u32, child_id as u32, &mut p_acc, &mut var)
157 {
158 return Err(e);
159 }
160 match p_acc {
161 None => {
162 return Err(Error::new(
163 S_FALSE,
164 &format!(
165 "Can't obtain the accessible object, the h_wnd is {:?}.",
166 h_wnd.0
167 ),
168 ));
169 }
170 Some(r) => (r, i32::try_from(&var).unwrap_or(0)),
171 }
172 };
173 Ok((Self(acc.0, acc.1), acc.1))
174 }
175
176 pub fn get_name(&self, child: i32) -> String {
181 unsafe { self.0.get_accName(&VARIANT::from(child)) }
182 .unwrap_or(BSTR::new())
183 .to_string()
184 }
185
186 pub fn get_description(&self, child: i32) -> String {
191 unsafe { self.0.get_accDescription(&VARIANT::from(child)) }
192 .unwrap_or(BSTR::new())
193 .to_string()
194 }
195
196 pub fn get_help(&self, child: i32) -> String {
201 unsafe { self.0.get_accHelp(&VARIANT::from(child)) }
202 .unwrap_or(BSTR::new())
203 .to_string()
204 }
205
206 pub fn get_help_topic(&self, child: i32) -> (String, i32) {
211 unsafe {
212 let mut help_file = BSTR::new();
213 let id_topic = self
214 .0
215 .get_accHelpTopic(&mut help_file, &VARIANT::from(child))
216 .unwrap_or(0);
217 (help_file.to_string(), id_topic)
218 }
219 }
220
221 pub fn get_keyboard_shortcut(&self, child: i32) -> String {
226 unsafe { self.0.get_accKeyboardShortcut(&VARIANT::from(child)) }
227 .unwrap_or(BSTR::new())
228 .to_string()
229 }
230
231 pub fn get_value(&self, child: i32) -> String {
236 unsafe { self.0.get_accValue(&VARIANT::from(child)) }
237 .unwrap_or(BSTR::new())
238 .to_string()
239 }
240
241 pub fn get_default_action(&self, child: i32) -> String {
246 unsafe { self.0.get_accDefaultAction(&VARIANT::from(child)) }
247 .unwrap_or(BSTR::new())
248 .to_string()
249 }
250
251 pub fn get_role(&self, child: i32) -> u32 {
256 unsafe {
257 if let Ok(v) = self.0.get_accRole(&VARIANT::from(child)) {
258 return u32::try_from(&v).unwrap_or(0);
259 } else {
260 0
261 }
262 }
263 }
264
265 pub fn get_role_text(&self, child: i32) -> String {
270 let role = self.get_role(child);
271 let mut text: [u16; 32] = [0; 32];
272 let len = unsafe { GetRoleTextW(role, Some(&mut text)) };
273 String::from_utf16_lossy(&text[..len as usize])
274 }
275
276 pub fn get_state_text(&self, child: i32) -> String {
281 let state = self.get_state(child);
282 let mut text: [u16; 32] = [0; 32];
283 let len = unsafe { GetStateTextW(state, Some(&mut text)) };
284 String::from_utf16_lossy(&text[..len as usize])
285 }
286
287 pub fn get_state(&self, child: i32) -> u32 {
292 unsafe {
293 if let Ok(v) = self.0.get_accState(&VARIANT::from(child)) {
294 return u32::try_from(&v).unwrap_or(0);
295 } else {
296 0
297 }
298 }
299 }
300
301 pub fn do_default_action(&self, child: i32) {
306 unsafe {
307 self.0
308 .accDoDefaultAction(&VARIANT::from(child))
309 .unwrap_or(());
310 }
311 }
312
313 pub fn select(&self, flags: i32, child: i32) {
320 unsafe {
321 self.0.accSelect(flags, &VARIANT::from(child)).unwrap_or(());
322 }
323 }
324
325 pub fn navigate(&self, nav_dir: i32, start: i32) -> Option<Self> {
331 unsafe {
332 let Ok(v) = self.0.accNavigate(nav_dir, &VARIANT::from(start)) else {
333 return None;
334 };
335 let Ok(d) = IDispatch::try_from(&v) else {
336 return match i32::try_from(&v) {
337 Ok(d) => Some(Self::from_raw(self.0.clone(), d)),
338 Err(_) => None,
339 };
340 };
341 Some(Self::from_raw(d.cast().unwrap(), 0))
342 }
343 }
344
345 pub fn hit_test(&self, left: i32, top: i32) -> Option<Self> {
351 unsafe {
352 let Ok(v) = self.0.accHitTest(left, top) else {
353 return None;
354 };
355 if let Ok(d) = IDispatch::try_from(&v) {
356 return Some(Self::from_raw(d.cast().unwrap(), 0));
357 }
358 Some(Self::from_raw(
359 self.0.clone(),
360 i32::try_from(&v).unwrap_or(0),
361 ))
362 }
363 }
364
365 pub fn focus(&self) -> Option<Self> {
369 unsafe {
370 let Ok(v) = self.0.accFocus() else {
371 return None;
372 };
373 if let Ok(d) = IDispatch::try_from(&v) {
374 return Some(Self::from_raw(d.cast().unwrap(), 0));
375 }
376 Some(Self::from_raw(
377 self.0.clone(),
378 i32::try_from(&v).unwrap_or(0),
379 ))
380 }
381 }
382
383 pub fn selection(&self) -> Option<Self> {
387 unsafe {
388 let Ok(v) = self.0.accSelection() else {
389 return None;
390 };
391 if let Ok(d) = IDispatch::try_from(&v) {
392 return Some(Self::from_raw(d.cast().unwrap(), 0));
393 }
394 Some(Self::from_raw(
395 self.0.clone(),
396 i32::try_from(&v).unwrap_or(0),
397 ))
398 }
399 }
400
401 pub fn parent(&self) -> Option<Self> {
405 unsafe {
406 if let Ok(r) = self.0.accParent() {
407 Some(Self::from_raw(r.cast().unwrap(), 0))
408 } else {
409 None
410 }
411 }
412 }
413
414 pub fn child_count(&self) -> u32 {
418 unsafe {
419 if let Ok(r) = self.0.accChildCount() {
420 return r as u32;
421 }
422 }
423 0
424 }
425
426 pub fn get_child(&self, child: i32) -> Result<Self> {
431 unsafe {
432 match self.0.get_accChild(&VARIANT::from(child)) {
433 Err(e) => Err(e),
434 Ok(o) => Ok(Self::from_raw(o.cast().unwrap(), 0)),
435 }
436 }
437 }
438
439 pub fn children(&self, start: u32, count: u32) -> Result<Vec<Self>> {
443 unsafe {
445 let mut arr = vec![];
446 for _ in 0..count {
447 arr.push(VARIANT::default());
448 }
449 let mut cnt = std::mem::zeroed();
450 match AccessibleChildren(&self.0, start as i32, &mut arr, &mut cnt) {
451 Err(e) => Err(e),
452 Ok(_) => {
453 let mut v = vec![];
454 for i in 0..cnt {
455 if let Ok(d) = IDispatch::try_from(&arr[i as usize]) {
456 v.push(Self::from_raw(d.cast().unwrap(), 0));
457 }
458 if let Ok(d) = i32::try_from(&arr[i as usize]) {
459 v.push(Self::from_raw(self.0.clone(), d));
460 }
461 }
462 Ok(v)
463 }
464 }
465 }
466 }
467
468 pub fn location(&self, child: i32) -> (i32, i32, i32, i32) {
473 unsafe {
474 let (mut left, mut top, mut width, mut height) = (0i32, 0i32, 0i32, 0i32);
475 if let Ok(_) = self.0.accLocation(
476 &mut left,
477 &mut top,
478 &mut width,
479 &mut height,
480 &VARIANT::from(child),
481 ) {
482 (left, top, width, height)
483 } else {
484 (0, 0, 0, 0)
485 }
486 }
487 }
488
489 #[allow(unused_variables)]
493 pub fn put_name(&self, child: i32, name: String) {
494 unreachable!()
495 }
496
497 pub fn put_value(&self, child: i32, value: String) {
503 unsafe {
504 self.0
505 .put_accValue(&VARIANT::from(child), &BSTR::from(value.as_str()))
506 .unwrap_or(());
507 }
508 }
509
510 pub fn window(&self) -> HWND {
512 let mut h_wnd = HWND::default();
513 unsafe {
514 WindowFromAccessibleObject(&self.0, Some(&mut h_wnd)).unwrap_or(());
515 }
516 h_wnd
517 }
518}
519
520impl Debug for AccessibleObject {
521 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
522 Display::fmt(self, f)
523 }
524}
525
526impl Display for AccessibleObject {
527 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
528 write!(
529 f,
530 "AccessibleObject(name:{}, description:{}, role:{})",
531 self.get_name(self.1),
532 self.get_description(self.1),
533 self.get_role_text(self.1)
534 )
535 }
536}
537
538unsafe impl Sync for AccessibleObject {}
539
540unsafe impl Send for AccessibleObject {}