pub use crate::ia2::ia2_lib::{
Accessible2::IA2Locale,
AccessibleStates::AccessibleStates,
IA2CommonTypes::{IA2CoordinateType, IA2ScrollType},
};
use crate::ia2::{
ia2_lib::{
Accessible2::IAccessible2, Accessible2_2::IAccessible2_2, Accessible2_3::IA2Range,
Accessible2_3::IAccessible2_3,
},
relation::AccessibleRelation,
};
use std::{
ffi::c_void,
fmt::{Debug, Formatter},
};
use win_wrap::{common::HWND, msaa::object::AccessibleObject};
use windows::Win32::System::Variant::VT_BSTR;
use windows::{
core::{Error, IUnknown, Interface, Result, Type, BSTR},
Win32::{
Foundation::S_FALSE,
System::{
Com::{CoTaskMemFree, IServiceProvider},
Variant::VARIANT,
},
UI::Accessibility::IAccessible,
},
};
pub struct Accessible2Object {
_ia2: IAccessible2,
_ia2_2: Option<IAccessible2_2>,
_ia2_3: Option<IAccessible2_3>,
}
impl Accessible2Object {
pub fn from_accessible_object(obj: AccessibleObject) -> Result<Self> {
if let Ok(sp) = obj.get_raw().cast::<IServiceProvider>() {
let ia2 = match unsafe { sp.QueryService::<IAccessible2>(&IAccessible::IID) } {
Err(e) => return Err(e),
Ok(x) => x,
};
let ia2_3 = match unsafe { sp.QueryService::<IAccessible2_3>(&IAccessible::IID) } {
Err(_) => None,
Ok(x) => Some(x),
};
let ia2_2 = match unsafe { sp.QueryService::<IAccessible2_2>(&IAccessible::IID) } {
Err(_) => None,
Ok(x) => Some(x),
};
return Ok(Self {
_ia2: ia2,
_ia2_2: ia2_2,
_ia2_3: ia2_3,
});
}
Err(Error::new(S_FALSE, "Not supported."))
}
pub(crate) fn from_raw(obj: &IUnknown) -> Result<Self> {
let ia2 = match obj.cast::<IAccessible2>() {
Err(e) => return Err(e),
Ok(x) => x,
};
let ia2_3 = match obj.cast::<IAccessible2_3>() {
Err(_) => None,
Ok(x) => Some(x),
};
let ia2_2 = match obj.cast::<IAccessible2_2>() {
Err(_) => None,
Ok(x) => Some(x),
};
Ok(Self {
_ia2: ia2,
_ia2_2: ia2_2,
_ia2_3: ia2_3,
})
}
pub fn selection_ranges(&self) -> Vec<IA2Range> {
if self._ia2_3.is_none() {
return vec![];
}
let ia2 = self._ia2_3.as_ref().unwrap();
unsafe {
let mut ranges = std::mem::zeroed();
let mut num = 0;
if ia2.selectionRanges(&mut ranges, &mut num).is_err() {
return vec![];
}
let mut vec = vec![];
for i in 0..num {
vec.push(*ranges.wrapping_add(i as usize));
}
CoTaskMemFree(Some(ranges as *const c_void));
vec
}
}
pub fn attribute(&self, name: &str) -> IA2ResultType {
if self._ia2_2.is_none() {
return IA2ResultType::None;
}
let ia2 = self._ia2_2.as_ref().unwrap();
unsafe {
let mut out = std::mem::zeroed();
if ia2.attribute(BSTR::from(name), &mut out).is_err() {
return IA2ResultType::None;
}
out.into()
}
}
pub fn accessible_with_caret(&self) -> Option<(IUnknown, i32)> {
if self._ia2_2.is_none() {
return None;
}
let ia2 = self._ia2_2.as_ref().unwrap();
unsafe {
let mut acc = std::mem::zeroed();
let mut caret = 0;
let val = ia2
.accessibleWithCaret(&mut acc, &mut caret)
.and_then(|| Type::from_abi(acc as *mut c_void));
if val.is_err() {
return None;
}
Some((val.unwrap(), caret))
}
}
pub fn relation_targets_of_type(&self, r#type: &str, max_targets: i32) -> Vec<IUnknown> {
if self._ia2_2.is_none() {
return vec![];
}
let ia2 = self._ia2_2.as_ref().unwrap();
unsafe {
let mut targets = std::mem::zeroed();
let mut num = 0;
if ia2
.relationTargetsOfType(BSTR::from(r#type), max_targets, &mut targets, &mut num)
.is_err()
{
return vec![];
}
let mut v = Vec::new();
for i in 0..num {
let it = *targets.wrapping_add(i as usize);
v.push(Type::from_abi(it as *mut c_void).unwrap());
}
CoTaskMemFree(Some(targets as *const c_void));
v
}
}
pub fn n_relations(&self) -> i32 {
unsafe {
let mut num = 0;
if self._ia2.nRelations(&mut num).is_err() {
return 0;
}
num
}
}
pub fn relation(&self, index: i32) -> Result<AccessibleRelation> {
unsafe {
let mut relation = std::mem::zeroed();
if let Ok(relation) = self
._ia2
.relation(index, &mut relation)
.and_then(|| Type::from_abi(relation as *mut c_void))
{
Ok(AccessibleRelation::from_raw(&relation))
} else {
Err(Error::new(S_FALSE, "Not supported."))
}
}
}
pub fn relations(&self, max_relations: i32) -> Vec<AccessibleRelation> {
unsafe {
let mut relations = std::mem::zeroed();
let mut num = 0;
if self
._ia2
.relations(max_relations, &mut relations, &mut num)
.is_err()
{
return vec![];
}
let mut v = vec![];
for i in 0..num {
v.push(AccessibleRelation::from_raw(
&Type::from_abi(relations.wrapping_add(i as usize) as *mut c_void).unwrap(),
));
}
v
}
}
pub fn role(&self) -> i32 {
unsafe {
let mut role = std::mem::zeroed();
self._ia2.role(&mut role).and_then(|| Type::from_abi(role))
}
.unwrap_or(0)
}
pub fn scroll_to(&self, scroll_type: IA2ScrollType) -> bool {
unsafe { self._ia2.scrollTo(scroll_type).is_ok() }
}
pub fn scroll_to_point(&self, coordinate_type: IA2CoordinateType, x: i32, y: i32) -> bool {
unsafe { self._ia2.scrollToPoint(coordinate_type, x, y).is_ok() }
}
pub fn group_position(&self) -> (i32, i32, i32) {
unsafe {
let (mut group_level, mut similar_items_in_group, mut position_in_group) =
std::mem::zeroed();
if self
._ia2
.groupPosition(
&mut group_level,
&mut similar_items_in_group,
&mut position_in_group,
)
.is_err()
{
return (0, 0, 0);
}
(group_level, similar_items_in_group, position_in_group)
}
}
pub fn states(&self) -> Result<AccessibleStates> {
unsafe {
let mut states = std::mem::zeroed();
self._ia2
.states(&mut states)
.and_then(|| Type::from_abi(states))
}
}
pub fn extended_role(&self) -> Result<String> {
unsafe {
let mut role = std::mem::zeroed();
let res = self._ia2.extendedRole(&mut role);
if res.is_err() {
return Err(Error::new(S_FALSE, res.message()));
}
Ok(role.to_string())
}
}
pub fn localized_extended_role(&self) -> Result<String> {
unsafe {
let mut role = std::mem::zeroed();
let res = self._ia2.localizedExtendedRole(&mut role);
if res.is_err() {
return Err(Error::new(S_FALSE, res.message()));
}
Ok(role.to_string())
}
}
pub fn n_extended_states(&self) -> Result<i32> {
unsafe {
let mut states = std::mem::zeroed();
self._ia2
.nExtendedStates(&mut states)
.and_then(|| Type::from_abi(states))
}
}
pub fn extended_states(&self) -> Vec<String> {
unsafe {
let (mut states, mut num) = std::mem::zeroed();
let res = self._ia2.extendedStates(0, &mut states, &mut num);
if res.is_err() {
return vec![];
}
let mut v = vec![];
for i in 0..num {
v.push(&*states.wrapping_add(i as usize));
}
let v = v.iter().map(|i| i.to_string()).collect();
CoTaskMemFree(Some(states as *const c_void));
v
}
}
pub fn localized_extended_states(&self) -> Vec<String> {
unsafe {
let (mut states, mut num) = std::mem::zeroed();
let res = self._ia2.localizedExtendedStates(0, &mut states, &mut num);
if res.is_err() {
return vec![];
}
let mut v = vec![];
for i in 0..num {
v.push(&*states.wrapping_add(i as usize));
}
let v = v.iter().map(|i| i.to_string()).collect();
CoTaskMemFree(Some(states as *const c_void));
v
}
}
pub fn unique_id(&self) -> Result<i32> {
unsafe {
let mut id = std::mem::zeroed();
self._ia2.uniqueID(&mut id).and_then(|| Type::from_abi(id))
}
}
pub fn window_handle(&self) -> Option<HWND> {
unsafe {
let mut h_wnd = std::mem::zeroed();
self._ia2
.windowHandle(&mut h_wnd)
.and_then(|| {
if h_wnd.is_invalid() {
Ok(h_wnd)
} else {
Err(Error::empty())
}
})
.ok()
}
}
pub fn index_in_parent(&self) -> i32 {
unsafe {
let mut index = std::mem::zeroed();
let res = self
._ia2
.indexInParent(&mut index)
.and_then(|| Type::from_abi(index));
if res.is_err() {
-1
} else {
res.unwrap()
}
}
}
pub fn locale(&self) -> Result<IA2Locale> {
unsafe {
let mut locale = std::mem::zeroed();
let res = self._ia2.locale(&mut locale);
if res.is_err() {
return Err(Error::new(S_FALSE, res.message()));
}
Ok(locale)
}
}
pub fn attributes(&self) -> String {
unsafe {
let mut attributes = std::mem::zeroed();
if self._ia2.attributes(&mut attributes).is_err() {
return String::new();
}
attributes.to_string()
}
}
}
impl Debug for Accessible2Object {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Accessible2Object(role:{}, states:{}, attributes:{}, n_relations:{}, window:{:?})",
self.role(),
self.states().unwrap_or(0),
self.attributes(),
self.n_relations(),
self.window_handle().unwrap_or_default().0
)
}
}
pub enum IA2ResultType {
Str(String),
Num(i32),
Bool(bool),
None,
}
impl From<VARIANT> for IA2ResultType {
fn from(value: VARIANT) -> Self {
if let Ok(d) = i32::try_from(&value) {
return Self::Num(d);
}
if value.vt() == VT_BSTR {
return Self::Str(unsafe { value.Anonymous.Anonymous.Anonymous.bstrVal.to_string() });
}
if let Ok(d) = bool::try_from(&value) {
return Self::Bool(d);
}
Self::None
}
}