diff --git a/AUTHORS b/AUTHORS index c20fd9f3a..432f1b7bb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -6,3 +6,4 @@ Matt Campbell Arnold Loubriat Google LLC +Leonard de Ruijter diff --git a/bindings/c/examples/sdl/hello_world.c b/bindings/c/examples/sdl/hello_world.c index 8406cc3c0..0e626143f 100644 --- a/bindings/c/examples/sdl/hello_world.c +++ b/bindings/c/examples/sdl/hello_world.c @@ -16,7 +16,7 @@ const accesskit_node_id WINDOW_ID = 0; const accesskit_node_id BUTTON_1_ID = 1; const accesskit_node_id BUTTON_2_ID = 2; const accesskit_node_id ANNOUNCEMENT_ID = 3; -const accesskit_node_id INITIAL_FOCUS = BUTTON_1_ID; +#define INITIAL_FOCUS BUTTON_1_ID const accesskit_rect BUTTON_1_RECT = {20.0, 20.0, 100.0, 60.0}; @@ -64,7 +64,7 @@ struct accesskit_sdl_adapter { }; void accesskit_sdl_adapter_init(struct accesskit_sdl_adapter *adapter, - SDL_Window *window, const char *app_name, + SDL_Window *window, accesskit_tree_update_factory source, void *source_userdata, accesskit_action_handler *handler) { @@ -76,8 +76,8 @@ void accesskit_sdl_adapter_init(struct accesskit_sdl_adapter *adapter, adapter->adapter = accesskit_macos_subclassing_adapter_for_window( (void *)wmInfo.info.cocoa.window, source, source_userdata, handler); #elif defined(UNIX) - adapter->adapter = accesskit_unix_adapter_new( - app_name, "SDL", "2.0", source, source_userdata, false, handler); + adapter->adapter = + accesskit_unix_adapter_new(source, source_userdata, false, handler); #elif defined(_WIN32) SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); @@ -229,7 +229,9 @@ accesskit_tree_update *window_state_build_initial_tree( build_button(BUTTON_2_ID, "Button 2", state->node_classes); accesskit_tree_update *result = accesskit_tree_update_with_capacity_and_focus( (state->announcement != NULL) ? 4 : 3, state->focus); - accesskit_tree_update_set_tree(result, accesskit_tree_new(WINDOW_ID)); + accesskit_tree *tree = accesskit_tree_new(WINDOW_ID); + accesskit_tree_set_app_name(tree, "Hello World"); + accesskit_tree_update_set_tree(result, tree); accesskit_tree_update_push_node(result, WINDOW_ID, root); accesskit_tree_update_push_node(result, BUTTON_1_ID, button_1); accesskit_tree_update_push_node(result, BUTTON_2_ID, button_2); @@ -343,7 +345,7 @@ int main(int argc, char *argv[]) { struct action_handler_state action_handler = {user_event, window_id}; struct accesskit_sdl_adapter adapter; accesskit_sdl_adapter_init( - &adapter, window, "hello_world", build_initial_tree, &state, + &adapter, window, build_initial_tree, &state, accesskit_action_handler_new(do_action, &action_handler)); SDL_ShowWindow(window); diff --git a/bindings/c/examples/windows/hello_world.c b/bindings/c/examples/windows/hello_world.c index d76a2ff09..da301da44 100644 --- a/bindings/c/examples/windows/hello_world.c +++ b/bindings/c/examples/windows/hello_world.c @@ -11,7 +11,7 @@ const accesskit_node_id WINDOW_ID = 0; const accesskit_node_id BUTTON_1_ID = 1; const accesskit_node_id BUTTON_2_ID = 2; const accesskit_node_id ANNOUNCEMENT_ID = 3; -const accesskit_node_id INITIAL_FOCUS = BUTTON_1_ID; +#define INITIAL_FOCUS BUTTON_1_ID const accesskit_rect BUTTON_1_RECT = {20.0, 20.0, 100.0, 60.0}; @@ -88,7 +88,9 @@ accesskit_tree_update *window_state_build_initial_tree( build_button(BUTTON_2_ID, "Button 2", state->node_classes); accesskit_tree_update *result = accesskit_tree_update_with_capacity_and_focus( (state->announcement != NULL) ? 4 : 3, state->focus); - accesskit_tree_update_set_tree(result, accesskit_tree_new(WINDOW_ID)); + accesskit_tree *tree = accesskit_tree_new(WINDOW_ID); + accesskit_tree_set_app_name(tree, "Hello World"); + accesskit_tree_update_set_tree(result, tree); accesskit_tree_update_push_node(result, WINDOW_ID, root); accesskit_tree_update_push_node(result, BUTTON_1_ID, button_1); accesskit_tree_update_push_node(result, BUTTON_2_ID, button_2); diff --git a/bindings/c/src/common.rs b/bindings/c/src/common.rs index c62f4a68c..32a6a4364 100644 --- a/bindings/c/src/common.rs +++ b/bindings/c/src/common.rs @@ -851,24 +851,105 @@ impl node_builder { } } -#[repr(C)] pub struct tree { - pub root: node_id, + _private: [u8; 0], } +impl CastPtr for tree { + type RustType = Tree; +} + +impl BoxCastPtr for tree {} + impl tree { #[no_mangle] - pub extern "C" fn accesskit_tree_new(root: node_id) -> tree { - tree { root } + pub extern "C" fn accesskit_tree_new(root: node_id) -> *mut tree { + let tree = Tree::new(root.into()); + BoxCastPtr::to_mut_ptr(tree) } -} -impl From for Tree { - fn from(tree: tree) -> Self { - Self { - root: tree.root.into(), + #[no_mangle] + pub extern "C" fn accesskit_tree_free(tree: *mut tree) { + drop(box_from_ptr(tree)); + } + + /// Caller must call `accesskit_string_free` with the return value. + #[no_mangle] + pub extern "C" fn accesskit_tree_get_app_name(tree: *const tree) -> *mut c_char { + let tree = ref_from_ptr(tree); + match tree.app_name.as_ref() { + Some(value) => CString::new(value.clone()).unwrap().into_raw(), + None => ptr::null_mut(), } } + + #[no_mangle] + pub extern "C" fn accesskit_tree_set_app_name(tree: *mut tree, app_name: *const c_char) { + let tree = mut_from_ptr(tree); + tree.app_name = Some(String::from( + unsafe { CStr::from_ptr(app_name) }.to_string_lossy(), + )); + } + + #[no_mangle] + pub extern "C" fn accesskit_tree_clear_app_name(tree: *mut tree) { + let tree = mut_from_ptr(tree); + tree.app_name = None; + } + + /// Caller must call `accesskit_string_free` with the return value. + #[no_mangle] + pub extern "C" fn accesskit_tree_get_toolkit_name(tree: *const tree) -> *mut c_char { + let tree = ref_from_ptr(tree); + match tree.toolkit_name.as_ref() { + Some(value) => CString::new(value.clone()).unwrap().into_raw(), + None => ptr::null_mut(), + } + } + + #[no_mangle] + pub extern "C" fn accesskit_tree_set_toolkit_name( + tree: *mut tree, + toolkit_name: *const c_char, + ) { + let tree = mut_from_ptr(tree); + tree.toolkit_name = Some(String::from( + unsafe { CStr::from_ptr(toolkit_name) }.to_string_lossy(), + )); + } + + #[no_mangle] + pub extern "C" fn accesskit_tree_clear_toolkit_name(tree: *mut tree) { + let tree = mut_from_ptr(tree); + tree.toolkit_name = None; + } + + /// Caller must call `accesskit_string_free` with the return value. + #[no_mangle] + pub extern "C" fn accesskit_tree_get_toolkit_version(tree: *const tree) -> *mut c_char { + let tree = ref_from_ptr(tree); + match tree.toolkit_version.as_ref() { + Some(value) => CString::new(value.clone()).unwrap().into_raw(), + None => ptr::null_mut(), + } + } + + #[no_mangle] + pub extern "C" fn accesskit_tree_set_toolkit_version( + tree: *mut tree, + toolkit_version: *const c_char, + ) { + let tree = mut_from_ptr(tree); + tree.toolkit_version = Some(String::from( + unsafe { CStr::from_ptr(toolkit_version) }.to_string_lossy(), + )); + } + + #[no_mangle] + pub extern "C" fn accesskit_tree_clear_toolkit_version(tree: *mut tree) { + let tree = mut_from_ptr(tree); + tree.toolkit_version = None; + } } pub struct tree_update { @@ -924,9 +1005,9 @@ impl tree_update { } #[no_mangle] - pub extern "C" fn accesskit_tree_update_set_tree(update: *mut tree_update, tree: tree) { + pub extern "C" fn accesskit_tree_update_set_tree(update: *mut tree_update, tree: *mut tree) { let update = mut_from_ptr(update); - update.tree = Some(tree.into()); + update.tree = Some(*box_from_ptr(tree)); } #[no_mangle] diff --git a/bindings/c/src/unix.rs b/bindings/c/src/unix.rs index 48a27639b..63b676d8d 100644 --- a/bindings/c/src/unix.rs +++ b/bindings/c/src/unix.rs @@ -9,11 +9,7 @@ use crate::{ }; use accesskit::Rect; use accesskit_unix::Adapter; -use std::{ - ffi::CStr, - os::raw::{c_char, c_void}, - ptr, -}; +use std::{os::raw::c_void, ptr}; pub struct unix_adapter { _private: [u8; 0], @@ -26,27 +22,17 @@ impl CastPtr for unix_adapter { impl BoxCastPtr for unix_adapter {} impl unix_adapter { - /// Caller is responsible for freeing `app_name`, `toolkit_name` and `toolkit_version`. /// This function will take ownership of the pointer returned by `initial_state`, which can't be null. #[no_mangle] pub extern "C" fn accesskit_unix_adapter_new( - app_name: *const c_char, - toolkit_name: *const c_char, - toolkit_version: *const c_char, initial_state: tree_update_factory, initial_state_userdata: *mut c_void, is_window_focused: bool, handler: *mut action_handler, ) -> *mut unix_adapter { - let app_name = unsafe { CStr::from_ptr(app_name).to_string_lossy().into() }; - let toolkit_name = unsafe { CStr::from_ptr(toolkit_name).to_string_lossy().into() }; - let toolkit_version = unsafe { CStr::from_ptr(toolkit_version).to_string_lossy().into() }; let initial_state = initial_state.unwrap(); let handler = box_from_ptr(handler); let adapter = Adapter::new( - app_name, - toolkit_name, - toolkit_version, move || *box_from_ptr(initial_state(initial_state_userdata)), is_window_focused, handler, diff --git a/common/src/lib.rs b/common/src/lib.rs index b92a2b65c..2cd49a477 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -2236,13 +2236,25 @@ impl JsonSchema for Node { #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct Tree { + /// The identifier of the tree's root node. pub root: NodeId, + /// The name of the application this tree belongs to. + pub app_name: Option, + /// The name of the UI toolkit in use. + pub toolkit_name: Option, + /// The version of the UI toolkit. + pub toolkit_version: Option, } impl Tree { #[inline] pub fn new(root: NodeId) -> Tree { - Tree { root } + Tree { + root, + app_name: None, + toolkit_name: None, + toolkit_version: None, + } } } diff --git a/consumer/src/tree.rs b/consumer/src/tree.rs index 19c7845c4..2b5c86ded 100644 --- a/consumer/src/tree.rs +++ b/consumer/src/tree.rs @@ -258,6 +258,18 @@ impl State { pub fn focus(&self) -> Option> { self.focus_id().map(|id| self.node_by_id(id).unwrap()) } + + pub fn app_name(&self) -> Option { + self.data.app_name.clone() + } + + pub fn toolkit_name(&self) -> Option { + self.data.toolkit_name.clone() + } + + pub fn toolkit_version(&self) -> Option { + self.data.toolkit_version.clone() + } } pub trait ChangeHandler { diff --git a/platforms/unix/src/adapter.rs b/platforms/unix/src/adapter.rs index 1ae274f0d..57f7938cb 100644 --- a/platforms/unix/src/adapter.rs +++ b/platforms/unix/src/adapter.rs @@ -209,9 +209,6 @@ pub struct Adapter { impl Adapter { /// Create a new Unix adapter. pub fn new( - app_name: String, - toolkit_name: String, - toolkit_version: String, initial_state: impl 'static + FnOnce() -> TreeUpdate, is_window_focused: bool, action_handler: Box, @@ -230,7 +227,11 @@ impl Adapter { let tree = Tree::new(initial_state(), is_window_focused); let id = NEXT_ADAPTER_ID.fetch_add(1, Ordering::SeqCst); let root_id = tree.state().root_id(); - let app_context = AppContext::get_or_init(app_name, toolkit_name, toolkit_version); + let app_context = AppContext::get_or_init( + tree.state().app_name().unwrap_or_default(), + tree.state().toolkit_name().unwrap_or_default(), + tree.state().toolkit_version().unwrap_or_default(), + ); let context = Context::new(tree, action_handler, &app_context); let adapter_index = app_context.write().unwrap().push_adapter(id, &context); block_on(async { diff --git a/platforms/windows/examples/hello_world.rs b/platforms/windows/examples/hello_world.rs index 2c7c5d6c7..98a1e4d9b 100644 --- a/platforms/windows/examples/hello_world.rs +++ b/platforms/windows/examples/hello_world.rs @@ -105,13 +105,16 @@ impl InnerWindowState { let root = self.build_root(); let button_1 = build_button(BUTTON_1_ID, "Button 1", &mut self.node_classes); let button_2 = build_button(BUTTON_2_ID, "Button 2", &mut self.node_classes); + let mut tree = Tree::new(WINDOW_ID); + tree.app_name = Some(env!("CARGO_BIN_NAME").to_string()); + let mut result = TreeUpdate { nodes: vec![ (WINDOW_ID, root), (BUTTON_1_ID, button_1), (BUTTON_2_ID, button_2), ], - tree: Some(Tree::new(WINDOW_ID)), + tree: Some(tree), focus: self.focus, }; if let Some(announcement) = &self.announcement { diff --git a/platforms/windows/src/node.rs b/platforms/windows/src/node.rs index 1d6295e9b..b1c3cd2e6 100644 --- a/platforms/windows/src/node.rs +++ b/platforms/windows/src/node.rs @@ -524,6 +524,19 @@ impl PlatformNode { }) } + fn resolve_with_tree_state_and_context(&self, f: F) -> Result + where + for<'a> F: FnOnce(Node<'a>, &TreeState, &Context) -> Result, + { + self.with_tree_state_and_context(|state, context| { + if let Some(node) = state.node_by_id(self.node_id) { + f(node, state, context) + } else { + Err(element_not_available()) + } + }) + } + fn resolve(&self, f: F) -> Result where for<'a> F: FnOnce(Node<'a>) -> Result, @@ -597,16 +610,25 @@ impl IRawElementProviderSimple_Impl for PlatformNode { } fn GetPropertyValue(&self, property_id: UIA_PROPERTY_ID) -> Result { - self.resolve_with_context(|node, context| { + self.resolve_with_tree_state_and_context(|node, state, context| { let wrapper = NodeWrapper::Node(&node); let mut result = wrapper.get_property_value(property_id); - if result.is_empty() && node.is_root() { - match property_id { - UIA_NamePropertyId => { - result = window_title(context.hwnd).into(); + if result.is_empty() { + if node.is_root() { + match property_id { + UIA_NamePropertyId => { + result = window_title(context.hwnd).into(); + } + UIA_NativeWindowHandlePropertyId => { + result = (context.hwnd.0 as i32).into(); + } + _ => (), } - UIA_NativeWindowHandlePropertyId => { - result = (context.hwnd.0 as i32).into(); + } + match property_id { + UIA_FrameworkIdPropertyId => result = state.toolkit_name().into(), + UIA_ProviderDescriptionPropertyId => { + result = app_and_toolkit_description(state).into() } _ => (), } diff --git a/platforms/windows/src/util.rs b/platforms/windows/src/util.rs index 7668b9c81..652264e34 100644 --- a/platforms/windows/src/util.rs +++ b/platforms/windows/src/util.rs @@ -4,6 +4,7 @@ // the LICENSE-MIT file), at your option. use accesskit::Point; +use accesskit_consumer::TreeState; use std::{ mem::ManuallyDrop, sync::{Arc, Weak}, @@ -242,6 +243,27 @@ pub(crate) fn window_title(hwnd: HWND) -> Option { Some(BSTR::from_wide(&buffer).unwrap()) } +pub(crate) fn app_and_toolkit_description(state: &TreeState) -> Option { + let app_name = state.app_name(); + let toolkit_name = state.toolkit_name(); + let toolkit_version = state.toolkit_version(); + match (&app_name, &toolkit_name, &toolkit_version) { + (Some(app_name), Some(toolkit_name), Some(toolkit_version)) => Some(format!( + "{} <{} {}>", + app_name, toolkit_name, toolkit_version + )), + (Some(app_name), Some(toolkit_name), None) => { + Some(format!("{} <{}>", app_name, toolkit_name)) + } + (None, Some(toolkit_name), Some(toolkit_version)) => { + Some(format!("{} {}", toolkit_name, toolkit_version)) + } + _ if toolkit_name.is_some() => toolkit_name, + _ if app_name.is_some() => app_name, + _ => None, + } +} + pub(crate) fn upgrade(weak: &Weak) -> Result> { if let Some(strong) = weak.upgrade() { Ok(strong) diff --git a/platforms/winit/examples/simple.rs b/platforms/winit/examples/simple.rs index 146cf8cbc..2b27cc1d6 100644 --- a/platforms/winit/examples/simple.rs +++ b/platforms/winit/examples/simple.rs @@ -83,13 +83,15 @@ impl State { let root = self.build_root(); let button_1 = build_button(BUTTON_1_ID, "Button 1", &mut self.node_classes); let button_2 = build_button(BUTTON_2_ID, "Button 2", &mut self.node_classes); + let mut tree = Tree::new(WINDOW_ID); + tree.app_name = Some(env!("CARGO_BIN_NAME").to_string()); let mut result = TreeUpdate { nodes: vec![ (WINDOW_ID, root), (BUTTON_1_ID, button_1), (BUTTON_2_ID, button_2), ], - tree: Some(Tree::new(WINDOW_ID)), + tree: Some(tree), focus: self.focus, }; if let Some(announcement) = &self.announcement { diff --git a/platforms/winit/src/platform_impl/unix.rs b/platforms/winit/src/platform_impl/unix.rs index 85dfd6e92..2c40684cf 100644 --- a/platforms/winit/src/platform_impl/unix.rs +++ b/platforms/winit/src/platform_impl/unix.rs @@ -18,14 +18,7 @@ impl Adapter { source: impl 'static + FnOnce() -> TreeUpdate, action_handler: ActionHandlerBox, ) -> Self { - let adapter = UnixAdapter::new( - String::new(), - String::new(), - String::new(), - source, - false, - action_handler, - ); + let adapter = UnixAdapter::new(source, false, action_handler); Self { adapter } }