use accessibility_sys::*;
use core_foundation::base::{CFType, TCFType};
use core_foundation::string::CFString;
use objc2::{class, msg_send, sel, sel_impl};
use std::ffi;
use std::ptr;
#[link(name = "AppKit", kind = "framework")]
extern "C" {}
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Testing macOS Accessibility Attributes");
println!("======================================");
println!();
if !unsafe { AXIsProcessTrusted() } {
eprintln!("Error: Accessibility permissions required. Please enable accessibility access in System Settings > Privacy & Security > Accessibility.");
eprintln!(
"Note: You may need to grant permission to your terminal app (Terminal, iTerm, etc.)"
);
return Ok(());
}
println!("Accessibility permissions verified");
println!();
let focused_window = get_focused_window()?;
println!("Successfully obtained focused window element");
println!();
test_accessibility_attributes(focused_window);
Ok(())
}
fn get_focused_window() -> Result<AXUIElementRef, Box<dyn std::error::Error>> {
use objc2::{class, msg_send, sel, sel_impl};
unsafe {
let workspace_class = class!(NSWorkspace);
let workspace: *mut objc2::runtime::Object = msg_send![workspace_class, sharedWorkspace];
let frontmost_app: *mut objc2::runtime::Object = msg_send![workspace, frontmostApplication];
if frontmost_app.is_null() {
return Err("No frontmost application found".into());
}
let pid: i32 = msg_send![frontmost_app, processIdentifier];
println!("Frontmost app PID: {}", pid);
let app_element = accessibility_sys::AXUIElementCreateApplication(pid);
let mut focused_window: *mut ffi::c_void = ptr::null_mut();
let focused_window_attr = CFString::from_static_string(kAXFocusedWindowAttribute);
let result = AXUIElementCopyAttributeValue(
app_element,
focused_window_attr.as_concrete_TypeRef(),
std::ptr::from_mut::<*mut ffi::c_void>(&mut focused_window)
.cast::<*const ffi::c_void>(),
);
if result != 0 || focused_window.is_null() {
return Err("Failed to get focused window".into());
}
Ok(focused_window as AXUIElementRef)
}
}
fn test_accessibility_attributes(window: AXUIElementRef) {
println!("Testing accessibility attributes for focused window:");
println!("---------------------------------------------------");
let attributes = vec![
("AXRole", kAXRoleAttribute),
("AXSubrole", kAXSubroleAttribute),
("AXRoleDescription", kAXRoleDescriptionAttribute),
("AXHelp", kAXHelpAttribute),
("AXTitle", kAXTitleAttribute),
("AXValue", kAXValueAttribute),
("AXValueDescription", kAXValueDescriptionAttribute),
("AXMinValue", kAXMinValueAttribute),
("AXMaxValue", kAXMaxValueAttribute),
("AXValueIncrement", kAXValueIncrementAttribute),
("AXAllowedValues", kAXAllowedValuesAttribute),
("AXPlaceholderValue", kAXPlaceholderValueAttribute),
("AXEnabled", kAXEnabledAttribute),
("AXElementBusy", kAXElementBusyAttribute),
("AXFocused", kAXFocusedAttribute),
("AXParent", kAXParentAttribute),
("AXChildren", kAXChildrenAttribute),
("AXSelectedChildren", kAXSelectedChildrenAttribute),
("AXVisibleChildren", kAXVisibleChildrenAttribute),
("AXWindow", kAXWindowAttribute),
("AXTopLevelUIElement", kAXTopLevelUIElementAttribute),
("AXPosition", kAXPositionAttribute),
("AXSize", kAXSizeAttribute),
("AXOrientation", kAXOrientationAttribute),
("AXDescription", kAXDescriptionAttribute),
("AXSelectedText", kAXSelectedTextAttribute),
("AXSelectedTextRange", kAXSelectedTextRangeAttribute),
("AXSelectedTextRanges", kAXSelectedTextRangesAttribute),
("AXVisibleCharacterRange", kAXVisibleCharacterRangeAttribute),
("AXNumberOfCharacters", kAXNumberOfCharactersAttribute),
("AXSharedTextUIElements", kAXSharedTextUIElementsAttribute),
("AXSharedCharacterRange", kAXSharedCharacterRangeAttribute),
("AXSharedFocusElements", kAXSharedFocusElementsAttribute),
(
"AXInsertionPointLineNumber",
kAXInsertionPointLineNumberAttribute,
),
("AXMain", kAXMainAttribute),
("AXMinimized", kAXMinimizedAttribute),
("AXCloseButton", kAXCloseButtonAttribute),
("AXZoomButton", kAXZoomButtonAttribute),
("AXMinimizeButton", kAXMinimizeButtonAttribute),
("AXToolbarButton", kAXToolbarButtonAttribute),
("AXFullScreenButton", kAXFullScreenButtonAttribute),
("AXProxy", kAXProxyAttribute),
("AXGrowArea", kAXGrowAreaAttribute),
("AXModal", kAXModalAttribute),
("AXDefaultButton", kAXDefaultButtonAttribute),
("AXCancelButton", kAXCancelButtonAttribute),
("AXMenuItemCmdChar", kAXMenuItemCmdCharAttribute),
("AXMenuItemCmdVirtualKey", kAXMenuItemCmdVirtualKeyAttribute),
("AXMenuItemCmdGlyph", kAXMenuItemCmdGlyphAttribute),
("AXMenuItemCmdModifiers", kAXMenuItemCmdModifiersAttribute),
("AXMenuItemMarkChar", kAXMenuItemMarkCharAttribute),
(
"AXMenuItemPrimaryUIElement",
kAXMenuItemPrimaryUIElementAttribute,
),
("AXMenuBar", kAXMenuBarAttribute),
("AXWindows", kAXWindowsAttribute),
("AXFrontmost", kAXFrontmostAttribute),
("AXHidden", kAXHiddenAttribute),
("AXMainWindow", kAXMainWindowAttribute),
("AXFocusedWindow", kAXFocusedWindowAttribute),
("AXFocusedUIElement", kAXFocusedUIElementAttribute),
("AXExtrasMenuBar", kAXExtrasMenuBarAttribute),
("AXHeader", kAXHeaderAttribute),
("AXEdited", kAXEditedAttribute),
("AXValueWraps", kAXValueWrapsAttribute),
("AXTabs", kAXTabsAttribute),
("AXTitleUIElement", kAXTitleUIElementAttribute),
("AXHorizontalScrollBar", kAXHorizontalScrollBarAttribute),
("AXVerticalScrollBar", kAXVerticalScrollBarAttribute),
("AXOverflowButton", kAXOverflowButtonAttribute),
("AXFilename", kAXFilenameAttribute),
("AXExpanded", kAXExpandedAttribute),
("AXSelected", kAXSelectedAttribute),
("AXSplitters", kAXSplittersAttribute),
("AXNextContents", kAXNextContentsAttribute),
("AXDocument", kAXDocumentAttribute),
("AXDecrementButton", kAXDecrementButtonAttribute),
("AXIncrementButton", kAXIncrementButtonAttribute),
("AXPreviousContents", kAXPreviousContentsAttribute),
("AXContents", kAXContentsAttribute),
("AXIncrementor", kAXIncrementorAttribute),
("AXHourField", kAXHourFieldAttribute),
("AXMinuteField", kAXMinuteFieldAttribute),
("AXSecondField", kAXSecondFieldAttribute),
("AXAMPMField", kAXAMPMFieldAttribute),
("AXDayField", kAXDayFieldAttribute),
("AXMonthField", kAXMonthFieldAttribute),
("AXYearField", kAXYearFieldAttribute),
("AXColumnTitles", kAXColumnTitlesAttribute),
("AXURL", kAXURLAttribute),
("AXLabelUIElements", kAXLabelUIElementsAttribute),
("AXLabelValue", kAXLabelValueAttribute),
("AXShownMenuUIElement", kAXShownMenuUIElementAttribute),
(
"AXServesAsTitleForUIElements",
kAXServesAsTitleForUIElementsAttribute,
),
("AXLinkedUIElements", kAXLinkedUIElementsAttribute),
("AXRows", kAXRowsAttribute),
("AXVisibleRows", kAXVisibleRowsAttribute),
("AXSelectedRows", kAXSelectedRowsAttribute),
("AXColumns", kAXColumnsAttribute),
("AXVisibleColumns", kAXVisibleColumnsAttribute),
("AXSelectedColumns", kAXSelectedColumnsAttribute),
("AXSortDirection", kAXSortDirectionAttribute),
("AXIndex", kAXIndexAttribute),
("AXDisclosing", kAXDisclosingAttribute),
("AXDisclosedRows", kAXDisclosedRowsAttribute),
("AXDisclosedByRow", kAXDisclosedByRowAttribute),
("AXDisclosureLevel", kAXDisclosureLevelAttribute),
("AXMatteHole", kAXMatteHoleAttribute),
("AXMatteContentUIElement", kAXMatteContentUIElementAttribute),
("AXMarkerUIElements", kAXMarkerUIElementsAttribute),
("AXUnits", kAXUnitsAttribute),
("AXUnitDescription", kAXUnitDescriptionAttribute),
("AXMarkerType", kAXMarkerTypeAttribute),
("AXMarkerTypeDescription", kAXMarkerTypeDescriptionAttribute),
("AXIsApplicationRunning", kAXIsApplicationRunningAttribute),
("AXSearchButton", kAXSearchButtonAttribute),
("AXClearButton", kAXClearButtonAttribute),
("AXFocusedApplication", kAXFocusedApplicationAttribute),
("AXRowCount", kAXRowCountAttribute),
("AXColumnCount", kAXColumnCountAttribute),
("AXOrderedByRow", kAXOrderedByRowAttribute),
("AXWarningValue", kAXWarningValueAttribute),
("AXCriticalValue", kAXCriticalValueAttribute),
("AXSelectedCells", kAXSelectedCellsAttribute),
("AXVisibleCells", kAXVisibleCellsAttribute),
("AXRowHeaderUIElements", kAXRowHeaderUIElementsAttribute),
(
"AXColumnHeaderUIElements",
kAXColumnHeaderUIElementsAttribute,
),
("AXRowIndexRange", kAXRowIndexRangeAttribute),
("AXColumnIndexRange", kAXColumnIndexRangeAttribute),
("AXHorizontalUnits", kAXHorizontalUnitsAttribute),
("AXVerticalUnits", kAXVerticalUnitsAttribute),
(
"AXHorizontalUnitDescription",
kAXHorizontalUnitDescriptionAttribute,
),
(
"AXVerticalUnitDescription",
kAXVerticalUnitDescriptionAttribute,
),
("AXHandles", kAXHandlesAttribute),
("AXText", kAXTextAttribute),
("AXVisibleText", kAXVisibleTextAttribute),
("AXIsEditable", kAXIsEditableAttribute),
("AXIdentifier", kAXIdentifierAttribute),
("AXAlternateUIVisible", kAXAlternateUIVisibleAttribute),
];
let mut available_count = 0;
let total_count = attributes.len();
for (name, attr) in attributes {
match get_attribute_value(window, attr) {
Ok(Some(value)) => {
println!("✓ {}: {}", name, value);
available_count += 1;
}
Ok(None) => {
println!("- {}: <no value>", name);
}
Err(_) => {
println!("✗ {}: <not available>", name);
}
}
}
println!();
println!("Summary:");
println!(" Total attributes tested: {}", total_count);
println!(" Attributes with values: {}", available_count);
println!(
" Attributes not available: {}",
total_count - available_count
);
}
fn get_attribute_value(
window: AXUIElementRef,
attribute: &'static str,
) -> Result<Option<String>, ()> {
unsafe {
let mut value_ptr: *mut ffi::c_void = ptr::null_mut();
let attr_cfstring = CFString::from_static_string(attribute);
let result = AXUIElementCopyAttributeValue(
window,
attr_cfstring.as_concrete_TypeRef(),
std::ptr::from_mut::<*mut ffi::c_void>(&mut value_ptr).cast::<*const ffi::c_void>(),
);
if result != 0 || value_ptr.is_null() {
return Err(());
}
if !value_ptr.is_null() {
let cf_value = CFType::wrap_under_create_rule(value_ptr);
Ok(Some(format!("{:#?}", cf_value)))
} else {
Ok(None)
}
}
}