From 6587d2947cf403d7e74442211780b48ff9edbfb4 Mon Sep 17 00:00:00 2001 From: DataTriny Date: Wed, 5 Apr 2023 18:18:53 +0200 Subject: [PATCH 1/6] Add a cross-platform example using SDL --- bindings/c/examples/sdl/.gitignore | 1 + bindings/c/examples/sdl/CMakeLists.txt | 13 + bindings/c/examples/sdl/README.md | 27 ++ bindings/c/examples/sdl/hello_world.c | 425 +++++++++++++++++++++++++ bindings/c/src/common.rs | 1 - bindings/c/src/unix.rs | 7 +- 6 files changed, 471 insertions(+), 3 deletions(-) create mode 100644 bindings/c/examples/sdl/.gitignore create mode 100644 bindings/c/examples/sdl/CMakeLists.txt create mode 100644 bindings/c/examples/sdl/README.md create mode 100644 bindings/c/examples/sdl/hello_world.c diff --git a/bindings/c/examples/sdl/.gitignore b/bindings/c/examples/sdl/.gitignore new file mode 100644 index 000000000..378eac25d --- /dev/null +++ b/bindings/c/examples/sdl/.gitignore @@ -0,0 +1 @@ +build diff --git a/bindings/c/examples/sdl/CMakeLists.txt b/bindings/c/examples/sdl/CMakeLists.txt new file mode 100644 index 000000000..11a9c6638 --- /dev/null +++ b/bindings/c/examples/sdl/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.20) + +project(sdl_example) + +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIRS}) +find_package(ACCESSKIT REQUIRED) +include_directories(windows_example ${ACCESSKIT_INCLUDE_DIR}) + +add_executable(hello_world hello_world.c) +target_link_libraries(hello_world PUBLIC ${SDL2_LIBRARIES}) +target_link_libraries(hello_world PUBLIC accesskit) +target_compile_definitions(hello_world PRIVATE -DUNICODE -D_UNICODE) diff --git a/bindings/c/examples/sdl/README.md b/bindings/c/examples/sdl/README.md new file mode 100644 index 000000000..c32993cd5 --- /dev/null +++ b/bindings/c/examples/sdl/README.md @@ -0,0 +1,27 @@ +# AccessKit cross-platform SDL example + +This example demonstrates how to make use of the C bindings to create cross-platform applications. + +## Building + +The process will vary based on your operating system. + +### On Windows: + +First download a copy of SDL2 and extract it. + +```bash +cmake -S . -B build -DACCESSKIT_DIR="../.." -DSDL2_ROOT="/cmake" +cmake --build build --config Release +``` + +You will then need to copy `SDL2.dll` into the `build/Release` folder. + +### On Linux + +Make sure to install SDL2 and its development package. + +```bash +cmake -S . -B build -DACCESSKIT_DIR="../.." -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` diff --git a/bindings/c/examples/sdl/hello_world.c b/bindings/c/examples/sdl/hello_world.c new file mode 100644 index 000000000..4df60dc3b --- /dev/null +++ b/bindings/c/examples/sdl/hello_world.c @@ -0,0 +1,425 @@ +#include +#include +#include +#include + +#include "accesskit.h" + +#if (defined(__linux__) || defined(__DragonFly__) || defined(__FreeBSD__) || \ + defined(__NetBSD__) || defined(__OpenBSD__)) +#define UNIX +#endif + +const char WINDOW_TITLE[] = "Hello world"; + +static accesskit_node_id WINDOW_ID; +static accesskit_node_id BUTTON_1_ID; +static accesskit_node_id BUTTON_2_ID; +static accesskit_node_id ANNOUNCEMENT_ID; +static accesskit_node_id INITIAL_FOCUS; + +const accesskit_rect BUTTON_1_RECT = {20.0, 20.0, 100.0, 60.0}; + +const accesskit_rect BUTTON_2_RECT = {20.0, 60.0, 100.0, 100.0}; + +const Sint32 SET_FOCUS_MSG = 0; +const Sint32 DO_DEFAULT_ACTION_MSG = 1; + +const bool node_id_cmp(const accesskit_node_id *id1, + const accesskit_node_id *id2) { + return memcmp(id1, id2, sizeof(accesskit_node_id)) == 0; +} + +accesskit_node_id *node_id_dup(const accesskit_node_id *src) { + accesskit_node_id *result = malloc(sizeof(accesskit_node_id)); + memcpy(result, src, sizeof(accesskit_node_id)); + return result; +} + +accesskit_node *build_button(accesskit_node_id id, const char *name, + accesskit_node_class_set *classes) { + accesskit_rect rect; + if (node_id_cmp(&id, &BUTTON_1_ID)) { + rect = BUTTON_1_RECT; + } else { + rect = BUTTON_2_RECT; + } + + accesskit_node_builder *builder = + accesskit_node_builder_new(ACCESSKIT_ROLE_BUTTON); + accesskit_node_builder_set_bounds(builder, rect); + accesskit_node_builder_set_name(builder, name); + accesskit_node_builder_add_action(builder, ACCESSKIT_ACTION_FOCUS); + accesskit_node_builder_set_default_action_verb( + builder, ACCESSKIT_DEFAULT_ACTION_VERB_CLICK); + return accesskit_node_builder_build(builder, classes); +} + +accesskit_node *build_announcement(const char *text, + accesskit_node_class_set *classes) { + accesskit_node_builder *builder = + accesskit_node_builder_new(ACCESSKIT_ROLE_STATIC_TEXT); + accesskit_node_builder_set_name(builder, text); + accesskit_node_builder_set_live(builder, ACCESSKIT_LIVE_POLITE); + return accesskit_node_builder_build(builder, classes); +} + +struct accesskit_sdl_adapter { +#if defined(__APPLE__) + accesskit_macos_subclassing_adapter adapter; +#elif defined(UNIX) + accesskit_unix_adapter *adapter; +#elif defined(_WIN32) + accesskit_windows_subclassing_adapter *adapter; +#endif +}; + +void accesskit_sdl_adapter_init(struct accesskit_sdl_adapter *adapter, + SDL_Window *window, const char *app_name, + accesskit_tree_update_factory source, + void *source_userdata, + accesskit_action_handler *handler) { +#if defined(__APPLE__) + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + SDL_GetWindowWMInfo(window, &wmInfo); + adapter->adapter = accesskit_macos_subclassing_adapter_new( + (void *)wmInfo.info.cocoa.window->contentView(), source, source_userdata, + handler); +#elif defined(UNIX) + adapter->adapter = accesskit_unix_adapter_new(app_name, "SDL", "2.0", source, + source_userdata, handler); +#elif defined(_WIN32) + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + SDL_GetWindowWMInfo(window, &wmInfo); + adapter->adapter = accesskit_windows_subclassing_adapter_new( + wmInfo.info.win.window, source, source_userdata, handler); +#endif +} + +void accesskit_sdl_adapter_destroy(struct accesskit_sdl_adapter *adapter) { + if (adapter->adapter != NULL) { +#if defined(__APPLE__) + accesskit_macos_subclassing_adapter_free(adapter->adapter); +#elif defined(UNIX) + accesskit_unix_adapter_free(adapter->adapter); +#elif defined(_WIN32) + accesskit_windows_subclassing_adapter_free(adapter->adapter); +#endif + } +} + +void accesskit_sdl_adapter_update(const struct accesskit_sdl_adapter *adapter, + accesskit_tree_update *update) { +#if defined(__APPLE__) + accesskit_macos_queued_events *events = + accesskit_macos_subclassing_adapter_update(adapter->adapter, update); + if (events != NULL) { + accesskit_macos_queued_events_raise(events); + } +#elif defined(UNIX) + accesskit_unix_adapter_update(adapter->adapter, update); +#elif defined(_WIN32) + accesskit_windows_queued_events *events = + accesskit_windows_subclassing_adapter_update(adapter->adapter, update); + if (events != NULL) { + accesskit_windows_queued_events_raise(events); + } +#endif +} + +void accesskit_sdl_adapter_update_if_active( + const struct accesskit_sdl_adapter *adapter, + accesskit_tree_update_factory update_factory, + void *update_factory_userdata) { +#if defined(__APPLE__) + accesskit_macos_queued_events *events = + accesskit_macos_subclassing_adapter_update_if_active( + adapter->adapter, update_factory, update_factory_userdata); + if (events != NULL) { + accesskit_macos_queued_events_raise(events); + } +#elif defined(UNIX) + accesskit_unix_adapter_update(adapter->adapter, + update_factory(update_factory_userdata)); +#elif defined(_WIN32) + accesskit_windows_queued_events *events = + accesskit_windows_subclassing_adapter_update_if_active( + adapter->adapter, update_factory, update_factory_userdata); + if (events != NULL) { + accesskit_windows_queued_events_raise(events); + } +#endif +} + +void accesskit_sdl_adapter_update_root_window_bounds( + const struct accesskit_sdl_adapter *adapter, SDL_Window *window) { +#if defined(UNIX) + int x, y, width, height; + SDL_GetWindowPosition(window, &x, &y); + SDL_GetWindowSize(window, &width, &height); + int top, left, bottom, right; + SDL_GetWindowBordersSize(window, &top, &left, &bottom, &right); + accesskit_rect outer_bounds = {x - left, y - top, x + width + right, + y + height + bottom}; + accesskit_rect inner_bounds = {x, y, x + width, y + height}; + accesskit_unix_adapter_set_root_window_bounds(adapter->adapter, outer_bounds, + inner_bounds); +#endif +} + +struct window_state { + accesskit_node_id focus; + bool is_window_focused; + const char *announcement; + accesskit_node_class_set *node_classes; + SDL_mutex *mutex; +}; + +void window_state_init(struct window_state *state) { + state->focus = INITIAL_FOCUS; + state->is_window_focused = false; + state->announcement = NULL; + state->node_classes = accesskit_node_class_set_new(); + state->mutex = SDL_CreateMutex(); +} + +void window_state_destroy(struct window_state *state) { + accesskit_node_class_set_free(state->node_classes); + SDL_DestroyMutex(state->mutex); +} + +void window_state_lock(struct window_state *state) { + SDL_LockMutex(state->mutex); +} + +void window_state_unlock(struct window_state *state) { + SDL_UnlockMutex(state->mutex); +} + +accesskit_opt_node_id window_state_focus(struct window_state *state) { + accesskit_opt_node_id result; + result.has_value = state->is_window_focused; + if (result.has_value) { + result.value = state->focus; + } + return result; +} + +accesskit_node *window_state_build_root(struct window_state *state) { + accesskit_node_builder *builder = + accesskit_node_builder_new(ACCESSKIT_ROLE_WINDOW); + accesskit_node_builder_push_child(builder, BUTTON_1_ID); + accesskit_node_builder_push_child(builder, BUTTON_2_ID); + if (state->announcement != NULL) { + accesskit_node_builder_push_child(builder, ANNOUNCEMENT_ID); + } + accesskit_node_builder_set_name(builder, WINDOW_TITLE); + return accesskit_node_builder_build(builder, state->node_classes); +} + +accesskit_tree_update *window_state_build_initial_tree( + struct window_state *state) { + accesskit_node *root = window_state_build_root(state); + accesskit_node *button_1 = + build_button(BUTTON_1_ID, "Button 1", state->node_classes); + accesskit_node *button_2 = + build_button(BUTTON_2_ID, "Button 2", state->node_classes); + accesskit_tree_update *result = + accesskit_tree_update_new((state->announcement != NULL) ? 4 : 3); + result->tree.has_value = true; + result->tree.value = accesskit_tree_new(WINDOW_ID); + result->focus = window_state_focus(state); + result->ids[0] = WINDOW_ID; + result->nodes[0] = root; + result->ids[1] = BUTTON_1_ID; + result->nodes[1] = button_1; + result->ids[2] = BUTTON_2_ID; + result->nodes[2] = button_2; + if (state->announcement != NULL) { + result->ids[3] = ANNOUNCEMENT_ID; + result->nodes[3] = + build_announcement(state->announcement, state->node_classes); + } + return result; +} + +accesskit_tree_update *build_tree_update_for_button_press(void *userdata) { + struct window_state *state = userdata; + accesskit_node *announcement = + build_announcement(state->announcement, state->node_classes); + accesskit_node *root = window_state_build_root(state); + accesskit_tree_update *update = accesskit_tree_update_new(2); + update->ids[0] = ANNOUNCEMENT_ID; + update->nodes[0] = announcement; + update->ids[1] = WINDOW_ID; + update->nodes[1] = root; + update->focus = window_state_focus(state); + return update; +} + +void window_state_press_button(struct window_state *state, + const struct accesskit_sdl_adapter *adapter, + accesskit_node_id id) { + const char *text; + if (node_id_cmp(&id, &BUTTON_1_ID)) { + text = "You pressed button 1"; + } else { + text = "You pressed button 2"; + } + state->announcement = text; + accesskit_sdl_adapter_update_if_active( + adapter, build_tree_update_for_button_press, state); +} + +accesskit_tree_update *build_tree_update_for_focus_update(void *userdata) { + struct window_state *state = userdata; + accesskit_opt_node_id focus = window_state_focus(state); + accesskit_tree_update *update = accesskit_tree_update_new(0); + update->focus = focus; + return update; +} + +void window_state_update_focus(struct window_state *state, + const struct accesskit_sdl_adapter *adapter) { + accesskit_sdl_adapter_update_if_active( + adapter, build_tree_update_for_focus_update, state); +} + +struct action_handler_state { + Uint32 event_type; + Uint32 window_id; +}; + +void do_action(const accesskit_action_request *request, void *userdata) { + struct action_handler_state *state = userdata; + SDL_Event event; + SDL_zero(event); + event.type = state->event_type; + event.user.windowID = state->window_id; + event.user.data1 = node_id_dup(&request->target); + if (request->action == ACCESSKIT_ACTION_FOCUS) { + event.user.code = SET_FOCUS_MSG; + SDL_PushEvent(&event); + } else if (request->action == ACCESSKIT_ACTION_DEFAULT) { + event.user.code = DO_DEFAULT_ACTION_MSG; + SDL_PushEvent(&event); + } +} + +accesskit_tree_update *build_initial_tree(void *userdata) { + struct window_state *state = userdata; + window_state_lock(state); + accesskit_tree_update *update = window_state_build_initial_tree(state); + window_state_unlock(state); + return update; +} + +int main(int argc, char *argv[]) { + printf("This example has no visible GUI, and a keyboard interface:\n"); + printf("- [Tab] switches focus between two logical buttons.\n"); + printf( + "- [Space] 'presses' the button, adding static text in a live region " + "announcing that it was pressed.\n"); +#if defined(_WIN32) + printf( + "Enable Narrator with [Win]+[Ctrl]+[Enter] (or [Win]+[Enter] on older " + "versions of Windows).\n"); +#elif defined(UNIX) + printf("Enable Orca with [Super]+[Alt]+[S].\n"); +#endif + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "SDL initialization failed: (%s)\n", SDL_GetError()); + return -1; + } + Uint32 user_event = SDL_RegisterEvents(1); + if (user_event == (Uint32)-1) { + fprintf(stderr, "Couldn't register user event: (%s)\n", SDL_GetError()); + return -1; + } + WINDOW_ID = accesskit_node_id_new(1).value; + BUTTON_1_ID = accesskit_node_id_new(2).value; + BUTTON_2_ID = accesskit_node_id_new(3).value; + ANNOUNCEMENT_ID = accesskit_node_id_new(4).value; + INITIAL_FOCUS = BUTTON_1_ID; + + struct window_state state; + window_state_init(&state); + SDL_Window *window = + SDL_CreateWindow(WINDOW_TITLE, SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_HIDDEN); + Uint32 window_id = SDL_GetWindowID(window); + 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, + accesskit_action_handler_new(do_action, &action_handler)); + SDL_ShowWindow(window); + + SDL_Event event; + while (SDL_WaitEvent(&event)) { + if (event.type == SDL_QUIT) { + break; + } else if (event.type == SDL_WINDOWEVENT && + event.window.windowID == window_id) { + switch (event.window.event) { + case SDL_WINDOWEVENT_FOCUS_GAINED: + window_state_lock(&state); + state.is_window_focused = true; + window_state_update_focus(&state, &adapter); + window_state_unlock(&state); + continue; + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + window_state_lock(&state); + state.is_window_focused = false; + window_state_update_focus(&state, &adapter); + window_state_unlock(&state); + break; + case SDL_WINDOWEVENT_MOVED: + accesskit_sdl_adapter_update_root_window_bounds(&adapter, window); + break; + case SDL_WINDOWEVENT_SHOWN: + accesskit_sdl_adapter_update_root_window_bounds(&adapter, window); + break; + } + } else if (event.type == SDL_KEYDOWN && event.key.windowID == window_id) { + switch (event.key.keysym.sym) { + case SDLK_TAB: + window_state_lock(&state); + if (node_id_cmp(&state.focus, &BUTTON_1_ID)) { + state.focus = BUTTON_2_ID; + } else { + state.focus = BUTTON_1_ID; + } + window_state_update_focus(&state, &adapter); + window_state_unlock(&state); + break; + case SDLK_SPACE: + window_state_lock(&state); + accesskit_node_id id = state.focus; + window_state_press_button(&state, &adapter, id); + window_state_unlock(&state); + break; + } + } else if (event.type == user_event && event.user.windowID == window_id && + (node_id_cmp(event.user.data1, &BUTTON_1_ID) || + node_id_cmp(event.user.data1, &BUTTON_2_ID))) { + window_state_lock(&state); + accesskit_node_id *target = event.user.data1; + if (event.user.code == SET_FOCUS_MSG) { + state.focus = *target; + window_state_update_focus(&state, &adapter); + } else if (event.user.code == DO_DEFAULT_ACTION_MSG) { + window_state_press_button(&state, &adapter, *target); + } + window_state_unlock(&state); + } + } + + accesskit_sdl_adapter_destroy(&adapter); + window_state_destroy(&state); + SDL_Quit(); + return 0; +} diff --git a/bindings/c/src/common.rs b/bindings/c/src/common.rs index c8048eb84..d64ae9abe 100644 --- a/bindings/c/src/common.rs +++ b/bindings/c/src/common.rs @@ -1091,5 +1091,4 @@ impl ActionHandler for FfiActionHandler { } /// This function can't return a null pointer. Ownership of the returned value will be transfered to the caller. -#[cfg(any(target_os = "windows", target_os = "macos", feature = "cbindgen"))] pub type tree_update_factory = Option *mut tree_update>; diff --git a/bindings/c/src/unix.rs b/bindings/c/src/unix.rs index 74df45a48..23372681d 100644 --- a/bindings/c/src/unix.rs +++ b/bindings/c/src/unix.rs @@ -3,7 +3,10 @@ // the LICENSE-APACHE file) or the MIT license (found in // the LICENSE-MIT file), at your option. -use crate::{action_handler, box_from_ptr, ref_from_ptr, tree_update, BoxCastPtr, CastPtr}; +use crate::{ + action_handler, box_from_ptr, ref_from_ptr, tree_update, tree_update_factory, BoxCastPtr, + CastPtr, +}; use accesskit::Rect; use accesskit_unix::Adapter; use std::{ @@ -30,7 +33,7 @@ impl unix_adapter { app_name: *const c_char, toolkit_name: *const c_char, toolkit_version: *const c_char, - initial_state: Option *mut tree_update>, + initial_state: tree_update_factory, initial_state_userdata: *mut c_void, handler: *mut action_handler, ) -> *mut unix_adapter { From dfac4afcc574089aad0b74e56f3cf5a241eaca1f Mon Sep 17 00:00:00 2001 From: DataTriny Date: Wed, 21 Jun 2023 18:58:15 +0200 Subject: [PATCH 2/6] Fix MacOS adapter pointer and creation --- bindings/c/examples/sdl/hello_world.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bindings/c/examples/sdl/hello_world.c b/bindings/c/examples/sdl/hello_world.c index 4df60dc3b..008c17a6b 100644 --- a/bindings/c/examples/sdl/hello_world.c +++ b/bindings/c/examples/sdl/hello_world.c @@ -66,7 +66,7 @@ accesskit_node *build_announcement(const char *text, struct accesskit_sdl_adapter { #if defined(__APPLE__) - accesskit_macos_subclassing_adapter adapter; + accesskit_macos_subclassing_adapter *adapter; #elif defined(UNIX) accesskit_unix_adapter *adapter; #elif defined(_WIN32) @@ -83,9 +83,8 @@ void accesskit_sdl_adapter_init(struct accesskit_sdl_adapter *adapter, SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); SDL_GetWindowWMInfo(window, &wmInfo); - adapter->adapter = accesskit_macos_subclassing_adapter_new( - (void *)wmInfo.info.cocoa.window->contentView(), source, source_userdata, - handler); + 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, handler); From 2b8563a0c9ad1beccc555bd42c0b769f6d61fac6 Mon Sep 17 00:00:00 2001 From: DataTriny Date: Fri, 23 Jun 2023 21:08:09 +0200 Subject: [PATCH 3/6] Improve README --- bindings/c/examples/sdl/README.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/bindings/c/examples/sdl/README.md b/bindings/c/examples/sdl/README.md index c32993cd5..ea272000e 100644 --- a/bindings/c/examples/sdl/README.md +++ b/bindings/c/examples/sdl/README.md @@ -6,18 +6,18 @@ This example demonstrates how to make use of the C bindings to create cross-plat The process will vary based on your operating system. -### On Windows: +### Windows: -First download a copy of SDL2 and extract it. +First download an SDL2 development package from the project's [GitHub release page](https://github.com/libsdl-org/SDL/releases) (SDL2-devel-2.x.y-VC.zip for MSVC) and extract it. ```bash -cmake -S . -B build -DACCESSKIT_DIR="../.." -DSDL2_ROOT="/cmake" +cmake -S . -B build -DACCESSKIT_DIR="../.." -DSDL2_DIR="/cmake" cmake --build build --config Release ``` You will then need to copy `SDL2.dll` into the `build/Release` folder. -### On Linux +### Linux Make sure to install SDL2 and its development package. @@ -25,3 +25,12 @@ Make sure to install SDL2 and its development package. cmake -S . -B build -DACCESSKIT_DIR="../.." -DCMAKE_BUILD_TYPE=Release cmake --build build ``` + +### macOS + +First download an SDL2 package from the project's [GitHub release page](https://github.com/libsdl-org/SDL/releases) (SDL2-2.x.y.dmg) and copy `SDL2.framework` to `/Library/Frameworks`. + +```bash +cmake -S . -B build -DACCESSKIT_DIR="../.." -DCMAKE_BUILD_TYPE=Release +cmake --build build +``` From 8d5c6ac26c83106fec96b3c3523b1b6f617fefd2 Mon Sep 17 00:00:00 2001 From: DataTriny Date: Mon, 31 Jul 2023 20:09:45 +0200 Subject: [PATCH 4/6] Use the new accesskit_tree_update type --- bindings/c/examples/sdl/hello_world.c | 57 ++++++++++++--------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/bindings/c/examples/sdl/hello_world.c b/bindings/c/examples/sdl/hello_world.c index 008c17a6b..f2fab4eca 100644 --- a/bindings/c/examples/sdl/hello_world.c +++ b/bindings/c/examples/sdl/hello_world.c @@ -197,16 +197,14 @@ void window_state_unlock(struct window_state *state) { SDL_UnlockMutex(state->mutex); } -accesskit_opt_node_id window_state_focus(struct window_state *state) { - accesskit_opt_node_id result; - result.has_value = state->is_window_focused; - if (result.has_value) { - result.value = state->focus; +void window_state_set_tree_update_focus(const struct window_state *state, + accesskit_tree_update *update) { + if (state->is_window_focused) { + accesskit_tree_update_set_focus(update, state->focus); } - return result; } -accesskit_node *window_state_build_root(struct window_state *state) { +accesskit_node *window_state_build_root(const struct window_state *state) { accesskit_node_builder *builder = accesskit_node_builder_new(ACCESSKIT_ROLE_WINDOW); accesskit_node_builder_push_child(builder, BUTTON_1_ID); @@ -219,27 +217,23 @@ accesskit_node *window_state_build_root(struct window_state *state) { } accesskit_tree_update *window_state_build_initial_tree( - struct window_state *state) { + const struct window_state *state) { accesskit_node *root = window_state_build_root(state); accesskit_node *button_1 = build_button(BUTTON_1_ID, "Button 1", state->node_classes); accesskit_node *button_2 = build_button(BUTTON_2_ID, "Button 2", state->node_classes); - accesskit_tree_update *result = - accesskit_tree_update_new((state->announcement != NULL) ? 4 : 3); - result->tree.has_value = true; - result->tree.value = accesskit_tree_new(WINDOW_ID); - result->focus = window_state_focus(state); - result->ids[0] = WINDOW_ID; - result->nodes[0] = root; - result->ids[1] = BUTTON_1_ID; - result->nodes[1] = button_1; - result->ids[2] = BUTTON_2_ID; - result->nodes[2] = button_2; + accesskit_tree_update *result = accesskit_tree_update_with_capacity( + (state->announcement != NULL) ? 4 : 3); + accesskit_tree_update_set_tree(result, accesskit_tree_new(WINDOW_ID)); + window_state_set_tree_update_focus(state, result); + 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); if (state->announcement != NULL) { - result->ids[3] = ANNOUNCEMENT_ID; - result->nodes[3] = + accesskit_node *announcement = build_announcement(state->announcement, state->node_classes); + accesskit_tree_update_push_node(result, ANNOUNCEMENT_ID, announcement); } return result; } @@ -249,12 +243,10 @@ accesskit_tree_update *build_tree_update_for_button_press(void *userdata) { accesskit_node *announcement = build_announcement(state->announcement, state->node_classes); accesskit_node *root = window_state_build_root(state); - accesskit_tree_update *update = accesskit_tree_update_new(2); - update->ids[0] = ANNOUNCEMENT_ID; - update->nodes[0] = announcement; - update->ids[1] = WINDOW_ID; - update->nodes[1] = root; - update->focus = window_state_focus(state); + accesskit_tree_update *update = accesskit_tree_update_with_capacity(2); + accesskit_tree_update_push_node(update, ANNOUNCEMENT_ID, announcement); + accesskit_tree_update_push_node(update, WINDOW_ID, root); + accesskit_tree_update_set_focus(update, state->focus); return update; } @@ -274,9 +266,8 @@ void window_state_press_button(struct window_state *state, accesskit_tree_update *build_tree_update_for_focus_update(void *userdata) { struct window_state *state = userdata; - accesskit_opt_node_id focus = window_state_focus(state); - accesskit_tree_update *update = accesskit_tree_update_new(0); - update->focus = focus; + accesskit_tree_update *update = accesskit_tree_update_new(); + accesskit_tree_update_set_focus(update, state->focus); return update; } @@ -376,9 +367,11 @@ int main(int argc, char *argv[]) { window_state_update_focus(&state, &adapter); window_state_unlock(&state); break; + case SDL_WINDOWEVENT_MAXIMIZED: case SDL_WINDOWEVENT_MOVED: - accesskit_sdl_adapter_update_root_window_bounds(&adapter, window); - break; + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_RESTORED: + case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_SHOWN: accesskit_sdl_adapter_update_root_window_bounds(&adapter, window); break; From e62ad1b3af82abc26ea6f1fb40191137167d4f3a Mon Sep 17 00:00:00 2001 From: DataTriny Date: Mon, 31 Jul 2023 21:10:30 +0200 Subject: [PATCH 5/6] Don't use the Unix adapter if the AT-SPI bus is not available --- bindings/c/examples/sdl/hello_world.c | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/bindings/c/examples/sdl/hello_world.c b/bindings/c/examples/sdl/hello_world.c index f2fab4eca..509cb5cdc 100644 --- a/bindings/c/examples/sdl/hello_world.c +++ b/bindings/c/examples/sdl/hello_world.c @@ -118,7 +118,8 @@ void accesskit_sdl_adapter_update(const struct accesskit_sdl_adapter *adapter, accesskit_macos_queued_events_raise(events); } #elif defined(UNIX) - accesskit_unix_adapter_update(adapter->adapter, update); + if (adapter->adapter != NULL) + accesskit_unix_adapter_update(adapter->adapter, update); #elif defined(_WIN32) accesskit_windows_queued_events *events = accesskit_windows_subclassing_adapter_update(adapter->adapter, update); @@ -140,8 +141,10 @@ void accesskit_sdl_adapter_update_if_active( accesskit_macos_queued_events_raise(events); } #elif defined(UNIX) - accesskit_unix_adapter_update(adapter->adapter, - update_factory(update_factory_userdata)); + if (adapter->adapter != NULL) { + accesskit_unix_adapter_update(adapter->adapter, + update_factory(update_factory_userdata)); + } #elif defined(_WIN32) accesskit_windows_queued_events *events = accesskit_windows_subclassing_adapter_update_if_active( @@ -155,16 +158,18 @@ void accesskit_sdl_adapter_update_if_active( void accesskit_sdl_adapter_update_root_window_bounds( const struct accesskit_sdl_adapter *adapter, SDL_Window *window) { #if defined(UNIX) - int x, y, width, height; - SDL_GetWindowPosition(window, &x, &y); - SDL_GetWindowSize(window, &width, &height); - int top, left, bottom, right; - SDL_GetWindowBordersSize(window, &top, &left, &bottom, &right); - accesskit_rect outer_bounds = {x - left, y - top, x + width + right, - y + height + bottom}; - accesskit_rect inner_bounds = {x, y, x + width, y + height}; - accesskit_unix_adapter_set_root_window_bounds(adapter->adapter, outer_bounds, - inner_bounds); + if (adapter->adapter != NULL) { + int x, y, width, height; + SDL_GetWindowPosition(window, &x, &y); + SDL_GetWindowSize(window, &width, &height); + int top, left, bottom, right; + SDL_GetWindowBordersSize(window, &top, &left, &bottom, &right); + accesskit_rect outer_bounds = {x - left, y - top, x + width + right, + y + height + bottom}; + accesskit_rect inner_bounds = {x, y, x + width, y + height}; + accesskit_unix_adapter_set_root_window_bounds(adapter->adapter, + outer_bounds, inner_bounds); + } #endif } From 3b7b67775ded8b9ef5f8fe42d42a874ec7777ae5 Mon Sep 17 00:00:00 2001 From: DataTriny Date: Tue, 8 Aug 2023 18:40:43 +0200 Subject: [PATCH 6/6] Call accesskit_macos_add_focus_forwarder_to_window before creating the macOS adapter --- bindings/c/examples/sdl/hello_world.c | 1 + 1 file changed, 1 insertion(+) diff --git a/bindings/c/examples/sdl/hello_world.c b/bindings/c/examples/sdl/hello_world.c index 509cb5cdc..4d78a7f35 100644 --- a/bindings/c/examples/sdl/hello_world.c +++ b/bindings/c/examples/sdl/hello_world.c @@ -80,6 +80,7 @@ void accesskit_sdl_adapter_init(struct accesskit_sdl_adapter *adapter, void *source_userdata, accesskit_action_handler *handler) { #if defined(__APPLE__) + accesskit_macos_add_focus_forwarder_to_window_class("SDLWindow"); SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); SDL_GetWindowWMInfo(window, &wmInfo);