From 970af5587f5e247cc4991f0c2679aa0e7dbda874 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Artur=20=C5=9Awigo=C5=84?= Date: Fri, 14 Feb 2020 11:28:41 +0100 Subject: [PATCH 01/16] Fix upstream in .gbs.conf Change-Id: Ia07c670b92bffc0e0d3ba0f7630ce8b88f5db8f1 --- .gbs.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gbs.conf b/.gbs.conf index 00a849f..99e7cc8 100644 --- a/.gbs.conf +++ b/.gbs.conf @@ -1,2 +1,3 @@ [general] -upstream_tag = AT_SPI2_CORE_2_16_0 +upstream_branch = upstream +upstream_tag = upstream/${upstreamversion} -- 2.7.4 From 376e88a463ad9d1f5110808879b68d6bb0a50910 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Artur=20=C5=9Awigo=C5=84?= Date: Fri, 14 Feb 2020 12:12:04 +0100 Subject: [PATCH 02/16] Fix memory management issues atspi/atspi-mutter.c Dynamic memory referenced by 'name' was allocated at atspi-accessible.c:307 by calling function 'atspi_accessible_get_name' at atspi-mutter.c:398 and lost at atspi-mutter.c:401. Pointer '&data.rd_session_path[0]' returned from function 'g_strdup' at atspi-mutter.c:82 may be null, and it is dereferenced at atspi-mutter.c:83. atspi/atspi-event-listener.c Return value of a function '_atspi_dbus_return_accessible_from_iter' is dereferenced at atspi-event-listener.c:1094 without checking, but it is usually checked for this function (5/6). Change-Id: Ie311948f4d8f71025d0b1769a97e2904ee4af387 --- atspi/atspi-event-listener.c | 8 ++++++++ atspi/atspi-mutter.c | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c index 6bab4c8..e7e7116 100644 --- a/atspi/atspi-event-listener.c +++ b/atspi/atspi-event-listener.c @@ -1065,6 +1065,14 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data) if (!strcmp (category, "ScreenReader")) { e.source = accessible; + if (e.source == NULL) + { + g_warning ("Got no valid source accessible for signal %s from interface %s\n", member, category); + g_free (converted_type); + g_free (name); + g_free (detail); + return DBUS_HANDLER_RESULT_HANDLED; + } } else { diff --git a/atspi/atspi-mutter.c b/atspi/atspi-mutter.c index 4445c18..532c1ab 100644 --- a/atspi/atspi-mutter.c +++ b/atspi/atspi-mutter.c @@ -80,7 +80,7 @@ ensure_rd_session_path (GError **error) dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, MUTTER_REMOTE_DESKTOP_OBJECT_PATH, MUTTER_REMOTE_DESKTOP_INTERFACE, "CreateSession", &d_error, "=>o", &session_path); data.rd_session_path = g_strdup (session_path); - if (!data.rd_session_path[0]) + if (!data.rd_session_path || !data.rd_session_path[0]) return FALSE; dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "Start", &d_error, ""); @@ -398,6 +398,7 @@ _atspi_mutter_set_reference_window (AtspiAccessible *accessible) name = atspi_accessible_get_name (accessible, NULL); data.window_id = get_window_id (name); data.window_id_is_explicit = TRUE; + g_free(name); } else -- 2.7.4 From d0eea865e847a4fd2bf3f899699ac6a96da2c88f Mon Sep 17 00:00:00 2001 From: Jongmin Lee Date: Tue, 28 Apr 2020 13:09:55 +0900 Subject: [PATCH 03/16] spec: update license from LGPL-2.0+ to LGPL-2.1+ Change-Id: If2866311d3282b9036c14459934d496a0ba9c7f9 --- packaging/at-spi2-core.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/at-spi2-core.spec b/packaging/at-spi2-core.spec index 6316fb2..bc859b6 100644 --- a/packaging/at-spi2-core.spec +++ b/packaging/at-spi2-core.spec @@ -4,7 +4,7 @@ Name: at-spi2-core Version: 2.34.0 Release: 0 Summary: Assistive Technology Service Provider Interface - D-Bus based implementation -License: LGPL-2.0+ +License: LGPL-2.1+ Group: System/Libraries Url: http://www.gnome.org/ Source: http://ftp.gnome.org/pub/GNOME/sources/at-spi2-core/2.31/%{name}-%{version}.tar.xz -- 2.7.4 From 45d88f8cf5e40b5d18228d43bad85b03e48bb3ce Mon Sep 17 00:00:00 2001 From: Maria Bialota Date: Tue, 26 May 2020 18:11:38 +0200 Subject: [PATCH 04/16] Added printing supported actions to at_spi2_tool Change-Id: I15ba50fb0b06990d314a1f8ad554baf909c2312b --- test/at_spi2_tool.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index cca84f1..f10778c 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -22,8 +22,10 @@ #define FIRST_MATCH 2 #define RELATION_TABLE_COLUMN_COUNT 7 #define ATTRIBUTE_TABLE_COLUMN_COUNT 3 +#define ACTIONS_TABLE_COLUMN_COUNT 2 #define RELATION_TABLE_COLUMN_WIDTH 20 #define ATTRIBUTE_TABLE_BASE_COLUMN_WIDTH 20 +#define ACTIONS_TABLE_BASE_COLUMN_WIDTH 20 #define VERSION "1.1" static unsigned indent_width = 2; @@ -299,7 +301,7 @@ static char *_get_attributes(AtspiAccessible *node, int length_limit, bool *attr return result; } -static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long, bool *app_has_relations) +static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes_are_too_long, bool *app_has_relations, bool *app_has_actions) { if (!node) return NULL; @@ -318,6 +320,15 @@ static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes GArray *relations = atspi_accessible_get_relation_set(node, NULL); bool current_node_has_relations = (relations && relations->len); + bool current_node_has_actions = false; + + AtspiAction* action_if = atspi_accessible_get_action_iface (node); + if (action_if) + { + gint no_actions = atspi_action_get_n_actions (action_if, NULL); + if (no_actions > 0) current_node_has_actions = true; + } + char result[SAFE_BUFFER_SIZE]; int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s(%p)],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]", unique_id, (uintptr_t)eo_ptr, @@ -337,6 +348,9 @@ static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes if (current_node_has_relations) *app_has_relations = true; + if (current_node_has_actions) + *app_has_actions = true; + free(node_name); free(node_role_name); free(unique_id); @@ -383,7 +397,7 @@ static void _test_atspi_parent_child_relation(AtspiAccessible *obj, AtspiAccessi } static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible *object, bool check_integrity, int length_limit, - bool *attributes_are_too_long, bool *app_has_relations) + bool *attributes_are_too_long, bool *app_has_relations, bool *app_has_actions) { char *indent = _multiply_string(' ', indent_number*indent_width); if (indent != NULL) { @@ -391,7 +405,7 @@ static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible * free(indent); } - char *node_info = _get_info(object, length_limit, attributes_are_too_long, app_has_relations); + char *node_info = _get_info(object, length_limit, attributes_are_too_long, app_has_relations, app_has_actions); if (node_info != NULL) { printf("%s\n", node_info); free(node_info); @@ -404,7 +418,7 @@ static int _print_atspi_tree_verify_maybe_r(int indent_number, AtspiAccessible * if (check_integrity) _test_atspi_parent_child_relation(child, object, i); - _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit, attributes_are_too_long, app_has_relations); + _print_atspi_tree_verify_maybe_r(indent_number + 1, child, check_integrity, length_limit, attributes_are_too_long, app_has_relations, app_has_actions); } } return 0; @@ -543,6 +557,61 @@ static void _print_relations_table(AtspiAccessible *node) { _print_relations_for_objects_in_tree(node); } +static void _print_horizontal_line_in_actions_table() { + int size_factor = 1; + for (int i = 0; i < ACTIONS_TABLE_COLUMN_COUNT; i++) { + if (i == ACTIONS_TABLE_COLUMN_COUNT - 1) + size_factor = 4; + for (int j = 0; j < ACTIONS_TABLE_BASE_COLUMN_WIDTH * size_factor; j++) + printf("-"); + printf("+"); + } + printf("\n"); +} + +static void _print_actions_for_object(AtspiAccessible *node) { + char *unique_id = atspi_accessible_get_unique_id(node, NULL); + AtspiAction* action_if = atspi_accessible_get_action_iface (node); + if (action_if) { + gint no_actions = atspi_action_get_n_actions (action_if, NULL); + for (int i=0; i< no_actions; i++) { + gchar * ith_action = atspi_action_get_action_name (action_if, i, NULL); + printf("%*s|", ACTIONS_TABLE_BASE_COLUMN_WIDTH, unique_id ? unique_id : ""); + printf("%*s|\n", ACTIONS_TABLE_BASE_COLUMN_WIDTH * 4, ith_action ? (char *) ith_action : ""); + g_free(ith_action); + } + _print_horizontal_line_in_actions_table(); + } + free(unique_id); +} + +static void _print_actions_for_objects_in_tree(AtspiAccessible *node) { + _iterate_over_tree(_print_actions_for_object, node); +} + +static void _print_header_for_actions_table() { + char *table[] = {"OBJECT", "SUPPORTED ACTION"}; + assert(ARRAY_SIZE(table) == ACTIONS_TABLE_COLUMN_COUNT); + + _print_horizontal_line_in_actions_table(); + + int size_factor = 1; + for (int i = 0; i < ACTIONS_TABLE_COLUMN_COUNT; i++) { + if (i == ACTIONS_TABLE_COLUMN_COUNT - 1) + size_factor = 4; + printf("%*s|", ACTIONS_TABLE_BASE_COLUMN_WIDTH * size_factor , table[i]); + } + + printf("\n"); + _print_horizontal_line_in_actions_table(); +} + +static void _print_actions_table(AtspiAccessible *node) { + printf("\nSUPPPORTED ACTIONS TABLE\n"); + _print_header_for_actions_table(); + _print_actions_for_objects_in_tree(node); +} + static void _print_horizontal_line_in_attributes_table() { int size_factor = 1; for (int i = 0; i < ATTRIBUTE_TABLE_COLUMN_COUNT; i++) { @@ -629,6 +698,7 @@ static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bo char *name = atspi_accessible_get_name(child, NULL); bool attributes_are_too_long = false; bool app_has_relations = false; + bool app_has_actions = false; if (!dump && !check) printf("%s\n", name); @@ -641,7 +711,7 @@ static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bo if (check) _test_atspi_parent_child_relation(child, desktop, i); - _print_atspi_tree_verify_maybe_r(0, child, check, length_limit, &attributes_are_too_long, &app_has_relations); + _print_atspi_tree_verify_maybe_r(0, child, check, length_limit, &attributes_are_too_long, &app_has_relations, &app_has_actions); if (app_has_relations) _print_relations_table(child); @@ -649,6 +719,9 @@ static void _atspi_tree_traverse(const char *app_name, bool dump, bool check, bo if (attributes_are_too_long) _print_attributes_table(child); + if (app_has_actions) + _print_actions_table(child); + if (first_match) { free(name); break; -- 2.7.4 From f8171e414d06924538a11fbddb0ac5d7227f98a6 Mon Sep 17 00:00:00 2001 From: Maria Bialota Date: Tue, 26 May 2020 18:11:38 +0200 Subject: [PATCH 05/16] Added printing toolkit name to at_spi2_tool Change-Id: Ia8ab65788ff932a9371dfdf8a941693b2ac8b552 --- test/at_spi2_tool.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index f10778c..e1e9fc8 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -313,6 +313,8 @@ static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes unsigned long long eo_ptr = 0; sscanf(path, "%llu", &eo_ptr); + char *toolkit = atspi_accessible_get_toolkit_name(node, NULL); + char *attributes = _get_attributes(node, length_limit, attributes_are_too_long); Box_Size *box_size = _get_box_size(node); char *states = _get_states(node, length_limit); @@ -330,9 +332,10 @@ static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes } char result[SAFE_BUFFER_SIZE]; - int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s(%p)],[%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]", + int ret = snprintf(result, SAFE_BUFFER_SIZE, "[[%s(%p)],[%s],[toolkit=%s],[%s],[%s,%s,%s,%s],[%s],[%s],[%s]]", unique_id, (uintptr_t)eo_ptr, node_role_name, + toolkit, attributes, box_size ? box_size->x : "nil", box_size ? box_size->y : "nil", @@ -355,6 +358,7 @@ static char *_get_info(AtspiAccessible *node, int length_limit, bool *attributes free(node_role_name); free(unique_id); free(path); + free(toolkit); free(attributes); if (box_size) { free(box_size->width); -- 2.7.4 From 943b527f267cf624bbd320562e42b3edfef9cb6f Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Tue, 6 Oct 2020 10:30:59 +0200 Subject: [PATCH 06/16] Fix memory leak * Release memory before pointer overwrite. * Free memory before overwrite pointer. * Memory leak with ref_accessible. * Leak in atspi_state_type_get_type. * Remove memory leak in atspi_event_listener_register_from_callback_full. * Reduce memory leaks reported on program exit. * This api is unused by efl but still let's fix it. Change-Id: Ifde65dd44643f2e58442cf70809703f979436e09 --- atspi/atspi-accessible.c | 2 ++ atspi/atspi-event-listener.c | 8 ++++++++ atspi/atspi-misc.c | 40 +++++++++++++++++++++++++++++++++++++--- atspi/atspi-stateset.c | 1 + 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index b5d7616..61a3a88 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -899,6 +899,7 @@ atspi_accessible_get_parent (AtspiAccessible *obj, GError **error) } dbus_message_iter_init (reply, &iter); dbus_message_iter_recurse (&iter, &iter_variant); + g_object_unref(obj->accessible_parent); obj->accessible_parent = _atspi_dbus_return_accessible_from_iter (&iter_variant); dbus_message_unref (reply); _atspi_accessible_add_cache (obj, ATSPI_CACHE_PARENT); @@ -1222,6 +1223,7 @@ atspi_accessible_get_attributes (AtspiAccessible *obj, GError **error) { message = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetAttributes", error, ""); + g_hash_table_unref(obj->attributes); obj->attributes = _atspi_dbus_return_hash_from_message (message); _atspi_accessible_add_cache (obj, ATSPI_CACHE_ATTRIBUTES); } diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c index e7e7116..3d4a8fb 100644 --- a/atspi/atspi-event-listener.c +++ b/atspi/atspi-event-listener.c @@ -420,6 +420,12 @@ listener_entry_free (EventListenerEntry *e) g_free (e->name); if (e->detail) g_free (e->detail); callback_unref (callback); + + for (int i=0; i < e->properties->len; i++) + g_free(g_array_index(e->properties, char*, i)); + + g_array_free(e->properties, TRUE); + g_free (e); } @@ -1064,6 +1070,7 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data) accessible = _atspi_dbus_return_accessible_from_iter (&iter_variant); if (!strcmp (category, "ScreenReader")) { + g_object_unref(e.source); e.source = accessible; if (e.source == NULL) { @@ -1131,6 +1138,7 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data) g_free (name); g_free (detail); g_object_unref (e.source); + g_object_unref (e.sender); g_value_unset (&e.any_data); return DBUS_HANDLER_RESULT_HANDLED; } diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index 84c15df..12135e3 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -132,6 +132,8 @@ _atspi_bus () static AtspiAccessible *desktop; +static void cleanup_deferred_message (void); + static void cleanup () { @@ -152,6 +154,8 @@ cleanup () bus = NULL; } + cleanup_deferred_message(); + if (!desktop) return; @@ -455,8 +459,13 @@ add_accessible_from_iter (DBusMessageIter *iter) dbus_message_iter_get_basic (&iter_struct, &index); if (index >= 0 && accessible->accessible_parent) { - if (index >= accessible->accessible_parent->children->len) + if (index >= accessible->accessible_parent->children->len) { + /* There is no room for this object */ g_ptr_array_set_size (accessible->accessible_parent->children, index + 1); + } else { + /* This place is already taken - let's free this place with dignity */ + g_object_unref (g_ptr_array_index (accessible->accessible_parent->children, index)); + } g_ptr_array_index (accessible->accessible_parent->children, index) = g_object_ref (accessible); } @@ -765,6 +774,23 @@ process_deferred_message (BusDataClosure *closure) static GQueue *deferred_messages = NULL; +static void +destroy_deferred_message_item(gpointer ptr) +{ + /* TODO this is still memory leak on c->data */ + BusDataClosure *c = ptr; + dbus_message_unref (c->message); + dbus_connection_unref (c->bus); + g_free(c); +} + +static void +cleanup_deferred_message(void) +{ + g_queue_free_full(deferred_messages, destroy_deferred_message_item); + deferred_messages = NULL; +} + static gboolean process_deferred_messages (void) { @@ -1279,11 +1305,19 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, } if (!strcmp (type, "(so)")) { + g_object_unref(*(AtspiAccessible**)data); *((AtspiAccessible **)data) = _atspi_dbus_return_accessible_from_iter (&iter_variant); } else { + if (type [0] == 's') + { + g_free(*(char**)data); + *(char**) data = NULL; + } + dbus_message_iter_get_basic (&iter_variant, data); + if (type [0] == 's') *(char **)data = g_strdup (*(char **)data); } @@ -1411,7 +1445,7 @@ _atspi_dbus_set_interfaces (AtspiAccessible *accessible, DBusMessageIter *iter) accessible->interfaces = 0; if (strcmp (iter_sig, "as") != 0) { - g_warning ("_atspi_dbus_set_interfaces: Passed iterator with invalid signature %s", dbus_message_iter_get_signature (iter)); + g_warning ("_atspi_dbus_set_interfaces: Passed iterator with invalid signature %s", iter_sig); dbus_free (iter_sig); return; } @@ -1560,7 +1594,7 @@ get_accessibility_bus_address_dbus (void) address = g_strdup (tmp_address); dbus_message_unref (reply); } - + out: dbus_connection_unref (session_bus); return address; diff --git a/atspi/atspi-stateset.c b/atspi/atspi-stateset.c index c7e1fe2..35489d0 100644 --- a/atspi/atspi-stateset.c +++ b/atspi/atspi-stateset.c @@ -101,6 +101,7 @@ atspi_state_set_set_by_name (AtspiStateSet *set, const gchar *name, gboolean ena if (!value) { g_warning ("AT-SPI: Attempt to set unknown state '%s'", name); + g_type_class_unref (type_class); return; } else -- 2.7.4 From 408eaaf58afa3da94916c88593ce1a0a297988f9 Mon Sep 17 00:00:00 2001 From: Lukasz Oleksak Date: Mon, 12 Oct 2020 13:03:07 +0200 Subject: [PATCH 07/16] fix memory leak in atspi_accessible_get_default_label_info Change-Id: I8e6094e96381fbf276556c04a1f87e0cb656d1b3 --- atspi/atspi-accessible.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index 61a3a88..c2df8ab 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -678,7 +678,8 @@ atspi_accessible_get_default_label_info (AtspiAccessible *obj, GError **error) default_label_info = calloc(1, sizeof(AtspiAccessibleDefaultLabelInfo)); if (!default_label_info) { - return default_label_info; + dbus_message_unref(reply); + return NULL; } dbus_message_iter_init (reply, &iter); @@ -693,6 +694,7 @@ atspi_accessible_get_default_label_info (AtspiAccessible *obj, GError **error) default_label_info->attributes = _atspi_dbus_hash_from_iter (&iter); dbus_message_iter_next (&iter); + dbus_message_unref(reply); return default_label_info; } -- 2.7.4 From 4d09f27a71e675fa756bc68e15a8ff907c069e30 Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Mon, 12 Oct 2020 18:17:29 +0900 Subject: [PATCH 08/16] Fix memory leak, handle hash table size (1) Fix memory leak of atspi_accessible_get_reading_material. (2) Remove defunct accessible from application hash table. In case of chromium-efl, it emits lots of "defunct" events without "RemoveAccessible", and this makes application hash table too big which could cause a Memory Alert on TV product. (3) If AT-server does not emit "RemoveAccessible", then there is not a chance to remove accessible from the application hash table. And then the memory usage of AT-client grows bigger and bigger. So this patch is using newly defined value HASH_TABLE_SIZE_MAX. Change-Id: If60d1fa1714d8bbf73b48cf16b1e1274f5a30cb9 --- atspi/atspi-accessible.c | 2 ++ atspi/atspi-event-listener-private.h | 2 ++ atspi/atspi-event-listener.c | 5 +++++ atspi/atspi-misc.c | 25 +++++++++++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index c2df8ab..138b73e 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -640,6 +640,8 @@ atspi_accessible_get_reading_material (AtspiAccessible *obj, GError **error) parent = _atspi_dbus_return_accessible_from_iter (&iter); reading_material->described_by_accessible = parent; + dbus_message_unref(reply); + return reading_material; } diff --git a/atspi/atspi-event-listener-private.h b/atspi/atspi-event-listener-private.h index 5a1a872..41abdfe 100644 --- a/atspi/atspi-event-listener-private.h +++ b/atspi/atspi-event-listener-private.h @@ -38,6 +38,8 @@ void _atspi_send_event (AtspiEvent *e); DBusHandlerResult _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data); +void remove_accessible_from_app_hash (const char *app_name, AtspiAccessible *a); + void _atspi_reregister_event_listeners (); diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c index 3d4a8fb..859f4a5 100644 --- a/atspi/atspi-event-listener.c +++ b/atspi/atspi-event-listener.c @@ -1122,6 +1122,11 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data) else if (!strncmp (e.type, "object:state-changed", 20)) { cache_process_state_changed (&e); + if (detail && !strncmp(detail, "defunct", 7)) + { + remove_accessible_from_app_hash(sender, e.source); + remove_accessible_from_app_hash(sender, e.sender); + } } else if (!strncmp (e.type, "focus", 5)) { diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index 12135e3..fccd502 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -36,6 +36,9 @@ #include #include +/* This value is not fixed, could be changed. */ +#define HASH_TABLE_SIZE_MAX 1000 + static void handle_get_items (DBusPendingCall *pending, void *user_data); static DBusConnection *bus = NULL; @@ -300,6 +303,13 @@ ref_accessible (const char *app_name, const char *path) a = _atspi_accessible_new (app, path); if (!a) return NULL; + + if (g_hash_table_size (app->hash) > HASH_TABLE_SIZE_MAX) + { + g_warning ("Accessible hash table is bigger than MAX: %d\n", HASH_TABLE_SIZE_MAX); + g_hash_table_remove_all(app->hash); + } + g_hash_table_insert (app->hash, g_strdup (a->parent.path), g_object_ref (a)); return a; } @@ -368,6 +378,21 @@ handle_remove_accessible (DBusConnection *bus, DBusMessage *message, void *user_ return DBUS_HANDLER_RESULT_HANDLED; } +void +remove_accessible_from_app_hash (const char *app_name, AtspiAccessible *a) +{ + AtspiApplication *app; + + if (!a) return; + + app = get_application (app_name); + if (!app) return; + + if (!g_hash_table_lookup (app->hash, a->parent.path)) return; + + g_hash_table_remove (app->hash, a->parent.path); +} + static DBusHandlerResult handle_name_owner_changed (DBusConnection *bus, DBusMessage *message, void *user_data) { -- 2.7.4 From 74a085e37c46b2ae15ae46dd217c23fa2c75c50c Mon Sep 17 00:00:00 2001 From: Lukasz Oleksak Date: Thu, 15 Oct 2020 13:21:59 +0200 Subject: [PATCH 09/16] API for comparing AtspiAccessible objects by unique_id Change-Id: I925a9761013ac6656b1e09ac91cd79397b8b042f --- atspi/atspi-accessible.c | 19 +++++++++++++++---- atspi/atspi-accessible.h | 2 ++ atspi/atspi-event-listener.c | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index 138b73e..394913c 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -213,7 +213,7 @@ atspi_accessible_dispose (GObject *object) if (accessible->children) for (i = accessible->children->len - 1; i >= 0; i--) { AtspiAccessible *child = g_ptr_array_index (accessible->children, i); - if (child && child->accessible_parent == accessible) + if (child && atspi_accessible_is_equal (child->accessible_parent, accessible)) { child->accessible_parent = NULL; g_object_unref (accessible); @@ -1016,7 +1016,7 @@ atspi_accessible_get_index_in_parent (AtspiAccessible *obj, GError **error) goto dbus; for (i = 0; i < obj->accessible_parent->children->len; i++) - if (g_ptr_array_index (obj->accessible_parent->children, i) == obj) + if (atspi_accessible_is_equal (g_ptr_array_index (obj->accessible_parent->children, i), obj)) return i; } @@ -1313,7 +1313,7 @@ atspi_accessible_get_application (AtspiAccessible *obj, GError **error) return root; } } - if (!parent || parent == obj || + if (!parent || atspi_accessible_is_equal (parent, obj) || atspi_accessible_get_role (parent, NULL) == ATSPI_ROLE_DESKTOP_FRAME) { if (parent) @@ -2229,7 +2229,7 @@ atspi_accessible_set_cache_mask (AtspiAccessible *accessible, AtspiCache mask) { g_return_if_fail (accessible != NULL); g_return_if_fail (accessible->parent.app != NULL); - g_return_if_fail (accessible == accessible->parent.app->root); + g_return_if_fail (atspi_accessible_is_equal (accessible, accessible->parent.app->root)); accessible->parent.app->cache = mask; enable_caching = TRUE; } @@ -2407,6 +2407,17 @@ atspi_accessible_get_accessible_id (AtspiAccessible *obj, GError **error) return accessible_id; } +gboolean +atspi_accessible_is_equal (AtspiAccessible *obj, AtspiAccessible *other) +{ + if (obj == other) + return TRUE; + if (!obj || !other) + return FALSE; + return !g_strcmp0(obj->parent.app->bus_name, other->parent.app->bus_name) && + !g_strcmp0(obj->parent.path, other->parent.path); +} + void free_value (gpointer data) { diff --git a/atspi/atspi-accessible.h b/atspi/atspi-accessible.h index a1fe314..de958b8 100644 --- a/atspi/atspi-accessible.h +++ b/atspi/atspi-accessible.h @@ -220,6 +220,8 @@ guint atspi_accessible_get_process_id (AtspiAccessible *accessible, GError **err gchar * atspi_accessible_get_accessible_id (AtspiAccessible *obj, GError **error); +gboolean atspi_accessible_is_equal (AtspiAccessible *obj, AtspiAccessible *other); + /* private */ void _atspi_accessible_add_cache (AtspiAccessible *accessible, AtspiCache flag); AtspiCache _atspi_accessible_get_cache_mask (AtspiAccessible *accessible); diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c index 859f4a5..86ff28c 100644 --- a/atspi/atspi-event-listener.c +++ b/atspi/atspi-event-listener.c @@ -232,7 +232,7 @@ cache_process_children_changed (AtspiEvent *event) else { g_ptr_array_remove (event->source->children, child); - if (child == child->parent.app->root) + if (atspi_accessible_is_equal (child, child->parent.app->root)) g_object_run_dispose (G_OBJECT (child->parent.app)); } } -- 2.7.4 From c286857f85330dc85931e58120c1b1d82d3b0471 Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Mon, 19 Oct 2020 14:21:29 +0900 Subject: [PATCH 10/16] misc: unref bus at the end of cleanup The g_object_run_dispose needs bus as below. g_object_run_dispose > atspi_application_dispose > _atspi_bus So close, unref and set bus to NULL after using bus is done. Change-Id: Idc68f7f0be02119617371efa023e7ef5b4dbb0f5 --- atspi/atspi-misc.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index fccd502..fb17f53 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -150,17 +150,10 @@ cleanup () g_hash_table_destroy (refs); } - if (bus) - { - dbus_connection_close (bus); - dbus_connection_unref (bus); - bus = NULL; - } - cleanup_deferred_message(); if (!desktop) - return; + goto end; /* TODO: Do we need this code, or should we just dispose the desktop? */ for (i = desktop->children->len - 1; i >= 0; i--) @@ -174,6 +167,14 @@ cleanup () g_object_run_dispose (G_OBJECT (desktop->parent.app)); g_object_unref (desktop); desktop = NULL; + +end: + if (bus) + { + dbus_connection_close (bus); + dbus_connection_unref (bus); + bus = NULL; + } } static gboolean atspi_inited = FALSE; -- 2.7.4 From e6c9015e998bbee49260c6bfe228e82d1f39a999 Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Tue, 20 Oct 2020 15:28:42 +0200 Subject: [PATCH 11/16] Fix invalid memory access Change-Id: I36ca4b0ac8772ba9b7e34d60449971e0b681ceda --- atspi/atspi-accessible.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index 394913c..2cc4cb4 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -2410,12 +2410,41 @@ atspi_accessible_get_accessible_id (AtspiAccessible *obj, GError **error) gboolean atspi_accessible_is_equal (AtspiAccessible *obj, AtspiAccessible *other) { + static const char prefix[] = "/org/a11y/atspi/accessible/"; + static const size_t prefix_len = sizeof(prefix)/sizeof(char); + if (obj == other) return TRUE; if (!obj || !other) return FALSE; - return !g_strcmp0(obj->parent.app->bus_name, other->parent.app->bus_name) && - !g_strcmp0(obj->parent.path, other->parent.path); + + /* + * Is it possible to have 2 different objects representing + * the same application? If not we should compare only pointers + * (obj->parent.app and other->parent.app) + */ + + /* One of them is null */ + if ((obj->parent.app != other->parent.app) && (!obj->parent.app || !other->parent.app)) + return FALSE; + + /* Both are not null */ + if ((obj->parent.app != other->parent.app) && g_strcmp0(obj->parent.app->bus_name, other->parent.app->bus_name)) + return FALSE; + + const AtspiObject *o1 = ATSPI_OBJECT (obj); + const AtspiObject *o2 = ATSPI_OBJECT (other); + + const char *path1 = o1 ? o1->path : ""; + const char *path2 = o2 ? o2->path : ""; + + if (!strncmp(path1, prefix, prefix_len)) + path1 += prefix_len; + + if (!strncmp(path2, prefix, prefix_len)) + path2 += prefix_len; + + return !g_strcmp0(path1, path2); } void -- 2.7.4 From 6df6f666f6a0a33182ea69b27715e12e3e5a5ae1 Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Thu, 5 Nov 2020 10:39:36 +0100 Subject: [PATCH 12/16] Prevent passing null to g_hash_table_unref This commit removes annoying message from glib2: "GLib-CRITICAL g_hash_table_unref: assertion 'hash_table != NULL' failed" Change-Id: Idf9eaaebc03f0b0fe8c6275c625d902e3361343a --- atspi/atspi-accessible.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index 2cc4cb4..c743d1e 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -1227,7 +1227,9 @@ atspi_accessible_get_attributes (AtspiAccessible *obj, GError **error) { message = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetAttributes", error, ""); - g_hash_table_unref(obj->attributes); + if (obj->attributes) + g_hash_table_unref(obj->attributes); + obj->attributes = _atspi_dbus_return_hash_from_message (message); _atspi_accessible_add_cache (obj, ATSPI_CACHE_ATTRIBUTES); } -- 2.7.4 From 0ed32e954e09abf691bafdb01048bb06658ed161 Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Thu, 4 Mar 2021 10:43:37 +0100 Subject: [PATCH 13/16] 2.39.90.1 Change-Id: I3d30aea2ebdc1e9ff5d24b564c2977d798f285d6 --- NEWS | 76 ++++ atspi/atspi-accessible.c | 50 ++- atspi/atspi-accessible.h | 1 + atspi/atspi-constants.h | 20 +- atspi/atspi-device-legacy.c | 316 +++++++++++++++++ atspi/atspi-device-legacy.h | 59 ++++ atspi/atspi-device-listener.c | 13 +- atspi/atspi-device-x11.c | 712 ++++++++++++++++++++++++++++++++++++++ atspi/atspi-device-x11.h | 59 ++++ atspi/atspi-device.c | 399 +++++++++++++++++++++ atspi/atspi-device.h | 103 ++++++ atspi/atspi-enum-types.c.template | 2 +- atspi/atspi-event-listener.c | 87 ++--- atspi/atspi-misc-private.h | 2 + atspi/atspi-misc.c | 87 ++++- atspi/atspi-registry.c | 3 +- atspi/atspi-types.h | 9 +- atspi/atspimarshal.list | 1 + atspi/meson.build | 14 +- bus/accessibility.conf.in | 2 +- bus/at-spi-bus-launcher.c | 96 +++-- bus/at-spi-dbus-bus.desktop.in | 2 +- bus/meson.build | 15 +- dbind/dbtest.c | 18 +- meson.build | 6 +- po/gl.po | 89 +---- po/pt.po | 51 +-- po/uk.po | 165 ++++----- registryd/meson.build | 1 - registryd/ucs2keysym.c | 285 ++++++++------- registryd/ucs2keysym.sh | 38 ++ test/memory.c | 8 +- test/meson.build | 17 +- test/test-application.c | 105 ++++++ xml/Accessible.xml | 18 +- xml/Action.xml | 2 +- xml/Application.xml | 2 +- xml/Cache.xml | 12 +- xml/Collection.xml | 18 +- xml/Component.xml | 4 +- xml/DeviceEventController.xml | 24 +- xml/DeviceEventListener.xml | 2 +- xml/Document.xml | 2 +- xml/Event.xml | 112 +++--- xml/Hyperlink.xml | 2 +- xml/Hypertext.xml | 2 +- xml/Image.xml | 2 +- xml/Registry.xml | 4 +- xml/Selection.xml | 2 +- xml/Socket.xml | 22 +- xml/Table.xml | 14 +- xml/TableCell.xml | 8 +- xml/Text.xml | 9 +- 53 files changed, 2573 insertions(+), 599 deletions(-) create mode 100644 atspi/atspi-device-legacy.c create mode 100644 atspi/atspi-device-legacy.h create mode 100644 atspi/atspi-device-x11.c create mode 100644 atspi/atspi-device-x11.h create mode 100644 atspi/atspi-device.c create mode 100644 atspi/atspi-device.h create mode 100755 registryd/ucs2keysym.sh create mode 100644 test/test-application.c diff --git a/NEWS b/NEWS index f18af9a..d964ce2 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,79 @@ +What's new in at-spi2-core 2.39.90.1: + +* Fix a crash introduced in 2.39.90, along with a few warnings (#30). + +What's new in at-spi2-core 2.39.90: + +* Fix build with X11 disabled. + +* Various fixes for the new device API used for key monitoring and grabbing. + +* Fixed several memory leaks. + +What's new in at-spi2-core 2.39.1: + +* Don't use gdbus-broker if not running under systemd (#25). + +* Unref bus at the end of cleanup. + +* Fix XML interfaces (#26). + +* Use unix sockets instead of abstract sockets (#28). + +* Added a device API to replace the old API for capturing key + grabs. This is needed for toolkits that do not report keystrokes + to atk, such as gtk 4. + +What's new in at-spi2-core 2.37.92: + +* Fix a possible memory leak if an event name cannot be parsed. + +* _atspi_dbus_set_interfaces: fix memory leak when called with an invalid + DBus signature. + +* Fix a crash if an event listener is removed during an event callback. + + +What's new in at-spi2-core 2.37.90: + +* Add a mode-changed signal to allow a screen reader to indicate + that its mode has changed. + +* Fix use after free when a device listener is destroyed (#22). + +* Meson: de-duplicate deps of 'Requires' in pkgconfig file (!28). + +* Fix use after free when an event listener is destroyed. + +* Make at-spi-dbus-bus.desktop validate (!30). + +* atspi_accessible_set_cache_mask: relax assert that was generating a + warning and causing the function to fail in some cases. + +* Fixes for synthesizing keys. + +* Fix Qt annotations in DBus xml files. + +* Memory test: replace gedit with a light-weight test application. + +* dbtest: print to stdout instead of stderr. + +* Tests: make test a bit verbose. + +* Constants: fix typos in two _COUNT constants. + +What's new in at-spi2-core 2.35.92: + +* bus-launcher: make session management more robust. + +What's new in at-spi2-core 2.35.1: + +* Fix source reproducibility (!25). + +* Avoid depending on Meson 0.50 (#20). + +* Add ATSPI_ROLE_MARK and ATSPI_ROLE_SUGGESTION (!27). + What's new in at-spi2-core 2.34.0: * Fix a use after free when freeing an event. diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index fd6737d..b078688 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -28,6 +28,7 @@ enum { REGION_CHANGED, + MODE_CHANGED, LAST_SIGNAL }; @@ -48,21 +49,28 @@ screen_reader_signal_watcher (GSignalInvocationHint *signal_hint, const char *name; DBusMessage *signal; DBusMessageIter iter, iter_struct, iter_variant, iter_array; - dbus_int32_t detail1, detail2; + dbus_int32_t detail1 = 0, detail2 = 0; const char *detail = ""; + gchar *dbus_name; object = g_value_get_object (param_values + 0); g_return_val_if_fail (ATSPI_IS_ACCESSIBLE(object), FALSE); g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; - detail1 = g_value_get_int (param_values + 1); - detail2 = g_value_get_int (param_values + 2); + if (signal_hint->detail) + detail = g_quark_to_string (signal_hint->detail); + if (n_param_values > 1) + detail1 = g_value_get_int (param_values + 1); + if (n_param_values > 2 && G_VALUE_HOLDS_INT (param_values + 2)) + detail2 = g_value_get_int (param_values + 2); accessible = ATSPI_ACCESSIBLE (object); + dbus_name = _atspi_strdup_and_adjust_for_dbus (name); signal = dbus_message_new_signal (ATSPI_DBUS_PATH_SCREEN_READER, ATSPI_DBUS_INTERFACE_EVENT_SCREEN_READER, - "RegionChanged"); + dbus_name); + g_free (dbus_name); dbus_message_iter_init_append (signal, &iter); dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &detail); dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &detail1); @@ -180,7 +188,7 @@ atspi_accessible_init (AtspiAccessible *accessible) accessible->priv = atspi_accessible_get_instance_private (accessible); - accessible->children = g_ptr_array_new_with_free_func (atspi_accessible_unref); + accessible->children = g_ptr_array_new_with_free_func ((GDestroyNotify) atspi_accessible_unref); } static void @@ -287,9 +295,36 @@ atspi_accessible_class_init (AtspiAccessibleClass *klass) G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + /** + * AtspiAccessible::mode-changed: + * @atspiaccessible: the object which received the signal + * @arg1: a boolean specifying whether the mode is being toggled on or off. + * @why: an optional string explaining why the mode changed. + * + * The signal "mode-changed" is emitted by a screen reader to indicate + * that its mode has changed. This signal supports the following details: + * focus-tracking + * flat-review + * mouse-review + * say-all + * caret-tracking + */ + atspi_accessible_signals[MODE_CHANGED] = + g_signal_new ("mode_changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (AtspiAccessibleClass, mode_changed), + NULL, NULL, + atspi_marshal_VOID__INT_STRING, + G_TYPE_NONE, + 2, G_TYPE_INT, G_TYPE_STRING); + g_signal_add_emission_hook (atspi_accessible_signals[REGION_CHANGED], 0, screen_reader_signal_watcher, NULL, (GDestroyNotify) NULL); + g_signal_add_emission_hook (atspi_accessible_signals[MODE_CHANGED], 0, + screen_reader_signal_watcher, NULL, + (GDestroyNotify) NULL); } /** @@ -383,6 +418,7 @@ atspi_accessible_get_parent (AtspiAccessible *obj, GError **error) } dbus_message_iter_init (reply, &iter); dbus_message_iter_recurse (&iter, &iter_variant); + g_clear_object (&obj->accessible_parent); obj->accessible_parent = _atspi_dbus_return_accessible_from_iter (&iter_variant); dbus_message_unref (reply); _atspi_accessible_add_cache (obj, ATSPI_CACHE_PARENT); @@ -707,6 +743,8 @@ atspi_accessible_get_attributes (AtspiAccessible *obj, GError **error) { message = _atspi_dbus_call_partial (obj, atspi_interface_accessible, "GetAttributes", error, ""); + g_clear_pointer (&(obj->attributes), g_hash_table_unref); + obj->attributes = _atspi_dbus_return_hash_from_message (message); _atspi_accessible_add_cache (obj, ATSPI_CACHE_ATTRIBUTES); } @@ -1706,7 +1744,7 @@ atspi_accessible_set_cache_mask (AtspiAccessible *accessible, AtspiCache mask) { g_return_if_fail (accessible != NULL); g_return_if_fail (accessible->parent.app != NULL); - g_return_if_fail (accessible == accessible->parent.app->root); + g_return_if_fail (accessible == accessible->parent.app->root || accessible->role == ATSPI_ROLE_APPLICATION); accessible->parent.app->cache = mask; enable_caching = TRUE; } diff --git a/atspi/atspi-accessible.h b/atspi/atspi-accessible.h index 13e4962..35c0d5b 100644 --- a/atspi/atspi-accessible.h +++ b/atspi/atspi-accessible.h @@ -66,6 +66,7 @@ struct _AtspiAccessibleClass AtspiObjectClass parent_class; void (*region_changed) (AtspiAccessible *accessible, gint current_offset, gint last_offset); + void (*mode_changed) (AtspiAccessible *accessible, gboolean enabled); }; GType atspi_accessible_get_type (void); diff --git a/atspi/atspi-constants.h b/atspi/atspi-constants.h index 8657ead..3a8da00 100644 --- a/atspi/atspi-constants.h +++ b/atspi/atspi-constants.h @@ -121,7 +121,7 @@ typedef enum { * * One higher than the highest valid value of #AtspiLocaleType. **/ -#define ATSPI_LOCALE_TYPE _COUNT(5+1) +#define ATSPI_LOCALE_TYPE_COUNT (5+1) /** * AtspiCoordType: @@ -241,7 +241,7 @@ typedef enum { * One higher than the highest valid value of * #AtspiCollection_TreeTraversalType. */ -#define ATSPI_TREETRAVERSALTYPE _COUNT(3+1) +#define ATSPI_TREETRAVERSALTYPE_COUNT (3+1) /** * AtspiComponentLayer: @@ -1240,6 +1240,18 @@ typedef enum { * @ATSPI_ROLE_CONTENT_INSERTION: Content previously inserted or proposed to be * inserted, e.g. in revision history or a content view providing suggestions * from reviewers. @Since: 2.34. + * @ATSPI_ROLE_MARK: A run of content that is marked or highlighted, such as for + * reference purposes, or to call it out as having a special purpose. If the + * marked content has an associated section in the document elaborating on the + * reason for the mark, then %ATSPI_RELATION_DETAILS should be used on the mark + * to point to that associated section. In addition, the reciprocal relation + * %ATSPI_RELATION_DETAILS_FOR should be used on the associated content section + * to point back to the mark. @Since: 2.36. + * @ATSPI_ROLE_SUGGESTION: A container for content that is called out as a proposed + * change from the current version of the document, such as by a reviewer of the + * content. This role should include either %ATSPI_ROLE_CONTENT_DELETION and/or + * %ATSPI_ROLE_CONTENT_INSERTION children, in any order, to indicate what the + * actual change is. @Since: 2.36 * @ATSPI_ROLE_LAST_DEFINED: Not a valid role, used for finding end of * enumeration. * @@ -1375,6 +1387,8 @@ typedef enum { ATSPI_ROLE_FOOTNOTE, ATSPI_ROLE_CONTENT_DELETION, ATSPI_ROLE_CONTENT_INSERTION, + ATSPI_ROLE_MARK, + ATSPI_ROLE_SUGGESTION, ATSPI_ROLE_LAST_DEFINED, } AtspiRole; @@ -1383,7 +1397,7 @@ typedef enum { * * One higher than the highest valid value of #AtspiRole. */ -#define ATSPI_ROLE_COUNT (127+1) +#define ATSPI_ROLE_COUNT (129+1) typedef enum { diff --git a/atspi/atspi-device-legacy.c b/atspi/atspi-device-legacy.c new file mode 100644 index 0000000..6950bc3 --- /dev/null +++ b/atspi/atspi-device-legacy.c @@ -0,0 +1,316 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2020 SUSE LLC. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "atspi-private.h" +#include "atspi-device-legacy.h" + +#ifdef HAVE_X11 +#include +#include +#include +#include +#endif + +typedef struct +{ + guint keycode; + guint modifier; +} AtspiLegacyKeyModifier; + +typedef struct _AtspiDeviceLegacyPrivate AtspiDeviceLegacyPrivate; +struct _AtspiDeviceLegacyPrivate +{ + AtspiDeviceListener *listener; +#ifdef HAVE_X11 + Display *display; + Window window; +#endif + GSList *modifiers; + guint virtual_mods_enabled; + gboolean keyboard_grabbed; +}; + +GObjectClass *device_legacy_parent_class; + +G_DEFINE_TYPE_WITH_CODE (AtspiDeviceLegacy, atspi_device_legacy, + ATSPI_TYPE_DEVICE, + G_ADD_PRIVATE (AtspiDeviceLegacy)) + + +static guint +find_virtual_mapping (AtspiDeviceLegacy *legacy_device, gint keycode) +{ + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + GSList *l; + + for (l = priv->modifiers; l; l = l->next) + { + AtspiLegacyKeyModifier *entry = l->data; + if (entry->keycode == keycode) + return entry->modifier; + } + + return 0; +} + +static void +set_virtual_modifier (AtspiDeviceLegacy *legacy_device, gint keycode, gboolean enabled) +{ + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + guint modifier = find_virtual_mapping (legacy_device, keycode); + + if (enabled) + priv->virtual_mods_enabled |= modifier; + else + priv->virtual_mods_enabled &= ~modifier; +} + + +gboolean +key_cb (const AtspiDeviceEvent *event, void *user_data) +{ + AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (user_data); + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + gboolean ret = priv->keyboard_grabbed; + + set_virtual_modifier (legacy_device, event->hw_code, + event->type == (AtspiEventType)ATSPI_KEY_PRESS); + ret |= atspi_device_notify_key (ATSPI_DEVICE (legacy_device), + event->type == (AtspiEventType)ATSPI_KEY_PRESS, + event->hw_code, event->id, + event->modifiers | priv->virtual_mods_enabled, + event->event_string); + + return ret; +} + +static guint +atspi_device_legacy_get_locked_modifiers (AtspiDevice *device) +{ +#ifdef HAVE_X11 + AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + XkbStateRec state_rec; + + memset (&state_rec, 0, sizeof (state_rec)); + XkbGetState (priv->display, XkbUseCoreKbd, &state_rec); + return state_rec.locked_mods; +#else + return 0; +#endif +} + +static gboolean +check_virtual_modifier (AtspiDeviceLegacy *legacy_device, guint modifier) +{ + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + GSList *l; + + for (l = priv->modifiers; l; l = l->next) + { + AtspiLegacyKeyModifier *entry = l->data; + if (entry->modifier == modifier) + return TRUE; + } + + return FALSE; +} + +static guint +get_unused_virtual_modifier (AtspiDeviceLegacy *legacy_device) +{ + guint ret = 0x1000; + + while (ret < 0x10000) + { + if (!check_virtual_modifier (legacy_device, ret)) + return ret; + ret <<= 1; + } + + return 0; +} + +static guint +atspi_device_legacy_map_modifier (AtspiDevice *device, gint keycode) +{ + AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + guint ret; + AtspiLegacyKeyModifier *entry; +#ifdef HAVE_X11 + XkbDescPtr desc; + + desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd); + + if (keycode < desc->min_key_code || keycode >= desc->max_key_code) + { + XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); + g_warning ("Passed invalid keycode %d", keycode); + return 0; + } + + ret = desc->map->modmap[keycode]; + XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); + if (ret & (ShiftMask | ControlMask)) + return ret; +#endif + + ret = find_virtual_mapping (legacy_device, keycode); + if (ret) + return ret; + + ret = get_unused_virtual_modifier (legacy_device); + + entry = g_new (AtspiLegacyKeyModifier, 1); + entry->keycode = keycode; + entry->modifier = ret; + priv->modifiers = g_slist_append (priv->modifiers, entry); + + return ret; +} + +static void +atspi_device_legacy_unmap_modifier (AtspiDevice *device, gint keycode) +{ + AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + GSList *l; + + for (l = priv->modifiers; l; l = l->next) + { + AtspiLegacyKeyModifier *entry = l->data; + if (entry->keycode == keycode) + { + g_free (entry); + priv->modifiers = g_slist_remove (priv->modifiers, entry); + return; + } + } +} + +static guint +atspi_device_legacy_get_modifier (AtspiDevice *device, gint keycode) +{ + AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); +#ifdef HAVE_X11 + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + XkbDescPtr desc; + guint ret; + + desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd); + + if (keycode < desc->min_key_code || keycode >= desc->max_key_code) + { + XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); + g_warning ("Passed invalid keycode %d", keycode); + return 0; + } + + ret = desc->map->modmap[keycode]; + XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); + if (ret) + return ret; +#endif + + return find_virtual_mapping (legacy_device, keycode); +} + +static gboolean +atspi_device_legacy_grab_keyboard (AtspiDevice *device) +{ + AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + + priv->keyboard_grabbed = TRUE; + return TRUE; +} + +static void +atspi_device_legacy_ungrab_keyboard (AtspiDevice *device) +{ + AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device); + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); + + priv->keyboard_grabbed = FALSE; +} + +static void +atspi_device_legacy_init (AtspiDeviceLegacy *device) +{ + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device); + gint i; + + priv->listener = atspi_device_listener_new (key_cb, device, NULL); + for (i = 0; i < 256; i++) + atspi_register_keystroke_listener (priv->listener, NULL, i, 3, ATSPI_KEYLISTENER_SYNCHRONOUS | ATSPI_KEYLISTENER_CANCONSUME, NULL); + +#ifdef HAVE_X11 + priv->display=XOpenDisplay(""); + if (priv->display) + priv->window = DefaultRootWindow(priv->display); +#endif + +} + +static void +atspi_device_legacy_finalize (GObject *object) +{ + AtspiDeviceLegacy *device = ATSPI_DEVICE_LEGACY (object); + AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device); + + g_clear_object (&priv->listener); + device_legacy_parent_class->finalize (object); +} + + +static void +atspi_device_legacy_class_init (AtspiDeviceLegacyClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + AtspiDeviceClass *device_class = ATSPI_DEVICE_CLASS (klass); + + device_legacy_parent_class = g_type_class_peek_parent (klass); + object_class->finalize = atspi_device_legacy_finalize; + device_class->map_modifier = atspi_device_legacy_map_modifier; + device_class->unmap_modifier = atspi_device_legacy_unmap_modifier; + device_class->get_modifier = atspi_device_legacy_get_modifier; + device_class->get_locked_modifiers = atspi_device_legacy_get_locked_modifiers; + device_class->grab_keyboard = atspi_device_legacy_grab_keyboard; + device_class->ungrab_keyboard = atspi_device_legacy_ungrab_keyboard; +} + +/** + * atspi_device_legacy_new: + * + * Creates a new #AtspiDeviceLegacy. + * + * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceLegacy. + * + **/ +AtspiDeviceLegacy * +atspi_device_legacy_new () +{ + AtspiDeviceLegacy *device = g_object_new (atspi_device_legacy_get_type (), NULL); + + return device; +} diff --git a/atspi/atspi-device-legacy.h b/atspi/atspi-device-legacy.h new file mode 100644 index 0000000..50f777e --- /dev/null +++ b/atspi/atspi-device-legacy.h @@ -0,0 +1,59 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2020 SUSE LLC. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ATSPI_DEVICE_LEGACY_H_ +#define _ATSPI_DEVICE_LEGACY_H_ + +#include "glib-object.h" + +#include "atspi-types.h" +#include "atspi-device.h" + +G_BEGIN_DECLS + +#define ATSPI_TYPE_DEVICE_LEGACY (atspi_device_legacy_get_type ()) +#define ATSPI_DEVICE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_TYPE_DEVICE_LEGACY, AtspiDeviceLegacy)) +#define ATSPI_DEVICE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ATSPI_TYPE_DEVICE_LEGACY, AtspiDeviceLegacyClass)) +#define ATSPI_IS_DEVICE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_TYPE_DEVICE_LEGACY)) +#define ATSPI_IS_DEVICE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ATSPI_TYPE_DEVICE_LEGACY)) +#define ATSPI_DEVICE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_TYPE_DEVICE_LEGACY, AtspiDeviceLegacyClass)) + +typedef struct _AtspiDeviceLegacy AtspiDeviceLegacy; +struct _AtspiDeviceLegacy +{ + AtspiDevice parent; +}; + +typedef struct _AtspiDeviceLegacyClass AtspiDeviceLegacyClass; +struct _AtspiDeviceLegacyClass +{ + AtspiDeviceClass parent_class; +}; + +GType atspi_device_legacy_get_type (void); + +AtspiDeviceLegacy *atspi_device_legacy_new (); + +G_END_DECLS + +#endif /* _ATSPI_DEVICE_LEGACY_H_ */ diff --git a/atspi/atspi-device-listener.c b/atspi/atspi-device-listener.c index 8e04c41..69f77d1 100644 --- a/atspi/atspi-device-listener.c +++ b/atspi/atspi-device-listener.c @@ -32,7 +32,7 @@ typedef struct GDestroyNotify callback_destroyed; } DeviceEventHandler; -GObjectClass *device_parent_class; +GObjectClass *device_listener_parent_class; /* * Misc. helpers. @@ -177,7 +177,9 @@ atspi_device_listener_finalize (GObject *object) { AtspiDeviceListener *listener = (AtspiDeviceListener *) object; GList *l; - + + device_listeners = g_list_remove (device_listeners, listener); + for (l = listener->callbacks; l; l = l->next) { device_event_handler_free (l->data); @@ -185,7 +187,7 @@ atspi_device_listener_finalize (GObject *object) g_list_free (listener->callbacks); - device_parent_class->finalize (object); + device_listener_parent_class->finalize (object); } static void @@ -193,7 +195,7 @@ atspi_device_listener_class_init (AtspiDeviceListenerClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; - device_parent_class = g_type_class_peek_parent (klass); + device_listener_parent_class = g_type_class_peek_parent (klass); object_class->finalize = atspi_device_listener_finalize; klass->device_event = atspi_device_event_dispatch; @@ -396,7 +398,8 @@ done: gchar * _atspi_device_listener_get_path (AtspiDeviceListener *listener) -{ return g_strdup_printf ("/org/a11y/atspi/listeners/%d", listener->id); +{ + return g_strdup_printf ("/org/a11y/atspi/listeners/%d", listener->id); } G_DEFINE_BOXED_TYPE (AtspiDeviceEvent, diff --git a/atspi/atspi-device-x11.c b/atspi/atspi-device-x11.c new file mode 100644 index 0000000..e8f2199 --- /dev/null +++ b/atspi/atspi-device-x11.c @@ -0,0 +1,712 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2020 SUSE LLC. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "atspi-private.h" +#include "atspi-device-x11.h" + +#include +#include +#include +#include + + +#define ATSPI_VIRTUAL_MODIFIER_MASK 0x0000f000 + +typedef struct _AtspiDeviceX11Private AtspiDeviceX11Private; +struct _AtspiDeviceX11Private +{ + Display *display; + Window window; + GSource *source; + int xi_opcode; + int device_id; + int device_id_alt; + GSList *modifiers; + GSList *key_grabs; + guint virtual_mods_enabled; + gboolean keyboard_grabbed; +}; + +GObjectClass *device_x11_parent_class; + +typedef struct _DisplaySource +{ + GSource source; + + Display *display; + GPollFD event_poll_fd; +} DisplaySource; + +typedef struct +{ + guint keycode; + guint modifier; +} AtspiX11KeyModifier; + +typedef struct +{ + AtspiKeyDefinition *kd; + gboolean enabled; +} AtspiX11KeyGrab; + +static gboolean +event_prepare (GSource *source, gint *timeout) +{ + Display *display = ((DisplaySource *)source)->display; + gboolean retval; + + *timeout = -1; + retval = XPending (display); + + return retval; +} + +static gboolean +event_check (GSource *source) +{ + DisplaySource *display_source = (DisplaySource*)source; + gboolean retval; + + if (display_source->event_poll_fd.revents & G_IO_IN) + retval = XPending (display_source->display); + else + retval = FALSE; + + return retval; +} + +static void +xi2keyevent (XIDeviceEvent *xievent, XEvent *xkeyevent) +{ + memset (xkeyevent, 0, sizeof (*xkeyevent)); + + switch (xievent->evtype) + { + case XI_KeyPress: + xkeyevent->type = KeyPress; + break; + case XI_KeyRelease: + xkeyevent->type = KeyRelease; + break; + default: + break; + } + xkeyevent->xkey.serial = xievent->serial; + xkeyevent->xkey.send_event = xievent->send_event; + xkeyevent->xkey.display = xievent->display; + xkeyevent->xkey.window = xievent->event; + xkeyevent->xkey.root = xievent->root; + xkeyevent->xkey.subwindow = xievent->child; + xkeyevent->xkey.time = xievent->time; + xkeyevent->xkey.x = xievent->event_x; + xkeyevent->xkey.y = xievent->event_y; + xkeyevent->xkey.x_root = xievent->root_x; + xkeyevent->xkey.y_root = xievent->root_y; + xkeyevent->xkey.state = xievent->mods.effective; + xkeyevent->xkey.keycode = xievent->detail; + xkeyevent->xkey.same_screen = 1; +} + +G_DEFINE_TYPE_WITH_CODE (AtspiDeviceX11, atspi_device_x11, + ATSPI_TYPE_DEVICE, + G_ADD_PRIVATE (AtspiDeviceX11)) + + +static guint +find_virtual_mapping (AtspiDeviceX11 *x11_device, gint keycode) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + GSList *l; + + for (l = priv->modifiers; l; l = l->next) + { + AtspiX11KeyModifier *entry = l->data; + if (entry->keycode == keycode) + return entry->modifier; + } + + return 0; +} + +static gboolean +grab_should_be_enabled (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + + /* If the whole keyboard is grabbed, then all keys are grabbed elsewhere */ + if (priv->keyboard_grabbed) + return FALSE; + + guint virtual_mods_used = grab->kd->modifiers & ATSPI_VIRTUAL_MODIFIER_MASK; + return ((priv->virtual_mods_enabled & virtual_mods_used) == virtual_mods_used); +} + +static gboolean +grab_has_active_duplicate (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + GSList *l; + + for (l = priv->key_grabs; l; l = l->next) + { + AtspiX11KeyGrab *other = l->data; + if (other != grab && other->enabled && other->kd->keycode == grab->kd->keycode && (other->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK) == (grab->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK)) + return TRUE; + } + return FALSE; +} + +static void +grab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + XIGrabModifiers xi_modifiers; + XIEventMask eventmask; + unsigned char mask[XIMaskLen (XI_LASTEVENT)] = { 0 }; + + xi_modifiers.modifiers = modmask; + xi_modifiers.status = 0; + + eventmask.deviceid = XIAllDevices; + eventmask.mask_len = sizeof(mask); + eventmask.mask = mask; + + XISetMask (mask, XI_KeyPress); + XISetMask (mask, XI_KeyRelease); + + XIGrabKeycode (priv->display, XIAllMasterDevices, keycode, priv->window, XIGrabModeSync, XIGrabModeAsync, False, &eventmask, 1, &xi_modifiers); +} + +static void +enable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + + g_return_if_fail (priv->display != NULL); + + if (!grab_has_active_duplicate (x11_device, grab)) + grab_key (x11_device, grab->kd->keycode, grab->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK); + grab->enabled = TRUE; +} + +static void +ungrab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + XIGrabModifiers xi_modifiers; + + xi_modifiers.modifiers = modmask; + xi_modifiers.status = 0; + + XIUngrabKeycode (priv->display, XIAllMasterDevices, keycode, priv->window, sizeof(xi_modifiers), &xi_modifiers); +} + +static void +disable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + + g_return_if_fail (priv->display != NULL); + + if (!grab->enabled) + return; + + grab->enabled = FALSE; + + if (grab_has_active_duplicate (x11_device, grab)) + return; + + ungrab_key (x11_device, grab->kd->keycode, grab->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK); +} + +static void +refresh_key_grabs (AtspiDeviceX11 *x11_device) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + GSList *l; + + for (l = priv->key_grabs; l; l = l->next) + { + AtspiX11KeyGrab *grab = l->data; + gboolean new_enabled = grab_should_be_enabled (x11_device, grab); + if (new_enabled && !grab->enabled) + enable_key_grab (x11_device, grab); + else if (grab->enabled && !new_enabled) + disable_key_grab (x11_device, grab); + } +} + +static void +set_virtual_modifier (AtspiDeviceX11 *x11_device, gint keycode, gboolean enabled) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + guint modifier = find_virtual_mapping (x11_device, keycode); + + if (!modifier) + return; + + if (enabled) + { + if (priv->virtual_mods_enabled & modifier) + return; + priv->virtual_mods_enabled |= modifier; + } + else + { + if (!(priv->virtual_mods_enabled & modifier)) + return; + priv->virtual_mods_enabled &= ~modifier; + } + + refresh_key_grabs (x11_device); +} + +static gboolean +do_event_dispatch (gpointer user_data) +{ + AtspiDeviceX11 *device = user_data; + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (device); + Display *display = priv->display; + XEvent xevent; + char text[10]; + KeySym keysym; + XComposeStatus status; + + while (XPending (display)) + { + XNextEvent (display, &xevent); + XEvent keyevent; + + switch (xevent.type) + { + case KeyPress: + case KeyRelease: + XLookupString(&xevent.xkey, text, sizeof (text), &keysym, &status); + atspi_device_notify_key (ATSPI_DEVICE (device), (xevent.type == KeyPress), xevent.xkey.keycode, keysym, xevent.xkey.state | priv->virtual_mods_enabled, text); + break; + case GenericEvent: + if (xevent.xcookie.extension == priv->xi_opcode) + { + XGetEventData(priv->display, &xevent.xcookie); + XIRawEvent *xiRawEv = (XIRawEvent *) xevent.xcookie.data; + XIDeviceEvent *xiDevEv = (XIDeviceEvent *) xevent.xcookie.data; + switch (xevent.xcookie.evtype) + { + case XI_KeyPress: + case XI_KeyRelease: + xi2keyevent (xiDevEv, &keyevent); + XLookupString((XKeyEvent *)&keyevent, text, sizeof (text), &keysym, &status); + if (text[0] < ' ') + text[0] = '\0'; + /* The deviceid can change. Would be nice to find a better way of + handling this */ + if (priv->device_id && priv->device_id_alt && xiDevEv->deviceid != priv->device_id && xiDevEv->deviceid != priv->device_id_alt) + priv->device_id = priv->device_id_alt = 0; + else if (priv->device_id && !priv->device_id_alt && xiDevEv->deviceid != priv->device_id) + priv->device_id_alt = xiDevEv->deviceid; + if (!priv->device_id) + priv->device_id = xiDevEv->deviceid; + set_virtual_modifier (device, xiRawEv->detail, xevent.xcookie.evtype == XI_KeyPress); + if (xiDevEv->deviceid == priv->device_id) + atspi_device_notify_key (ATSPI_DEVICE (device), (xevent.xcookie.evtype == XI_KeyPress), xiRawEv->detail, keysym, keyevent.xkey.state, text); + /* otherwise it's probably a duplicate event from a key grab */ + XFreeEventData (priv->display, &xevent.xcookie); + break; + } + } + default: + if (XFilterEvent (&xevent, None)) + continue; + } + } + return TRUE; +} + +static gboolean +event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) +{ + if (callback) + callback (user_data); + return G_SOURCE_CONTINUE; +} + +static GSourceFuncs event_funcs = { + event_prepare, + event_check, + event_dispatch, + NULL +}; + +static GSource * +display_source_new (Display *display) +{ + GSource *source = g_source_new (&event_funcs, sizeof (DisplaySource)); + DisplaySource *display_source = (DisplaySource *) source; + g_source_set_name (source, "[at-spi2-core] display_source_funcs"); + + display_source->display = display; + + return source; +} + +static void +create_event_source (AtspiDeviceX11 *device) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (device); + DisplaySource *display_source; + + int connection_number = ConnectionNumber (priv->display); + + priv->source = display_source_new (priv->display); + display_source = (DisplaySource *)priv->source; + + g_source_set_priority (priv->source, G_PRIORITY_DEFAULT); + + display_source->event_poll_fd.fd = connection_number; + display_source->event_poll_fd.events = G_IO_IN; + + g_source_add_poll (priv->source, &display_source->event_poll_fd); + g_source_set_can_recurse (priv->source, TRUE); + g_source_set_callback (priv->source, do_event_dispatch, device, NULL); + g_source_attach (priv->source, NULL); +} + +static gboolean +check_virtual_modifier (AtspiDeviceX11 *x11_device, guint modifier) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + GSList *l; + + for (l = priv->modifiers; l; l = l->next) + { + AtspiX11KeyModifier *entry = l->data; + if (entry->modifier == modifier) + return TRUE; + } + + return FALSE; +} + +static guint +get_unused_virtual_modifier (AtspiDeviceX11 *x11_device) +{ + guint ret = 0x1000; + + while (ret < 0x10000) + { + if (!check_virtual_modifier (x11_device, ret)) + return ret; + ret <<= 1; + } + + return 0; +} + +static guint +atspi_device_x11_map_modifier (AtspiDevice *device, gint keycode) +{ + AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device); + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + XkbDescPtr desc; + guint ret; + AtspiX11KeyModifier *entry; + + desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd); + + if (keycode < desc->min_key_code || keycode >= desc->max_key_code) + { + XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); + g_warning ("Passed invalid keycode %d", keycode); + return 0; + } + + ret = desc->map->modmap[keycode]; + XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); + if (ret & (ShiftMask | ControlMask)) + return ret; + + ret = find_virtual_mapping (x11_device, keycode); + if (ret) + return ret; + + ret = get_unused_virtual_modifier (x11_device); + + entry = g_new (AtspiX11KeyModifier, 1); + entry->keycode = keycode; + entry->modifier = ret; + priv->modifiers = g_slist_append (priv->modifiers, entry); + + return ret; +} + +static void +atspi_device_x11_unmap_modifier (AtspiDevice *device, gint keycode) +{ + AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device); + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + GSList *l; + + for (l = priv->modifiers; l; l = l->next) + { + AtspiX11KeyModifier *entry = l->data; + if (entry->keycode == keycode) + { + g_free (entry); + priv->modifiers = g_slist_remove (priv->modifiers, entry); + return; + } + } +} + +static guint +atspi_device_x11_get_modifier (AtspiDevice *device, gint keycode) +{ + AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device); + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + XkbDescPtr desc; + guint ret; + + desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd); + + if (keycode < desc->min_key_code || keycode >= desc->max_key_code) + { + XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); + g_warning ("Passed invalid keycode %d", keycode); + return 0; + } + + ret = desc->map->modmap[keycode]; + XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); + if (ret) + return ret; + + return find_virtual_mapping (x11_device, keycode); +} + +static void +atspi_device_x11_init (AtspiDeviceX11 *device) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (device); + int first_event, first_error; + + priv->display=XOpenDisplay(""); + g_return_if_fail (priv->display != NULL); + priv->window = DefaultRootWindow(priv->display); + + if (XQueryExtension(priv->display, "XInputExtension", &priv->xi_opcode, &first_event, &first_error)) + { + int major = 2; + int minor = 1; + if (XIQueryVersion(priv->display, &major, &minor) != BadRequest) + { + XIEventMask eventmask; + unsigned char mask[XIMaskLen (XI_LASTEVENT)] = { 0 }; + + eventmask.deviceid = XIAllDevices; + eventmask.mask_len = sizeof(mask); + eventmask.mask = mask; + + XISetMask (mask, XI_KeyPress); + XISetMask (mask, XI_KeyRelease); + XISetMask (mask, XI_ButtonPress); + XISetMask (mask, XI_ButtonRelease); + XISetMask (mask, XI_Motion); + XISelectEvents (priv->display, priv->window, &eventmask, 1); + create_event_source (device); + } + } +} + +static void +atspi_device_x11_finalize (GObject *object) +{ + AtspiDeviceX11 *device = ATSPI_DEVICE_X11 (object); + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (device); + GSList *l; + + for (l = priv->key_grabs; l; l = l->next) + { + AtspiX11KeyGrab *grab = l->data; + disable_key_grab (device, grab); + g_boxed_free (ATSPI_TYPE_KEY_DEFINITION, grab->kd); + g_free (grab); + } + g_slist_free (priv->key_grabs); + priv->key_grabs = NULL; + + g_slist_free_full (priv->modifiers, g_free); + priv->modifiers = NULL; + + if (priv->source) + { + g_source_destroy ((GSource *) priv->source); + g_source_unref ((GSource *) priv->source); + priv->source = NULL; + } + + device_x11_parent_class->finalize (object); +} + + +static void +atspi_device_x11_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd) +{ + AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device); + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + AtspiX11KeyGrab *grab; + + grab = g_new (AtspiX11KeyGrab, 1); + grab->kd = g_boxed_copy (ATSPI_TYPE_KEY_DEFINITION, kd); + grab->enabled = FALSE; + priv->key_grabs = g_slist_append (priv->key_grabs, grab); + if (grab_should_be_enabled (x11_device, grab)) + enable_key_grab (x11_device, grab); +} + +static void +atspi_device_x11_remove_key_grab (AtspiDevice *device, guint id) +{ + AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device); + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + AtspiKeyDefinition *kd; + GSList *l; + + kd = atspi_device_get_grab_by_id (device, id); + + for (l = priv->key_grabs; l; l = g_slist_next (l)) + { + AtspiX11KeyGrab *other = l->data; + if (other->kd->keycode == kd->keycode && other->kd->modifiers == kd->modifiers) + { + disable_key_grab (x11_device, other); + priv->key_grabs = g_slist_remove (priv->key_grabs, other); + return; + } + } +} + +static guint +atspi_device_x11_get_locked_modifiers (AtspiDevice *device) +{ + AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device); + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + XkbStateRec state_rec; + + memset (&state_rec, 0, sizeof (state_rec)); + XkbGetState (priv->display, XkbUseCoreKbd, &state_rec); + return state_rec.locked_mods; +} + +static void +get_keycode_range (AtspiDeviceX11 *x11_device, int *min, int *max) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + XkbDescPtr desc; + + desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd); + *min = desc->min_key_code; + *max = desc->max_key_code; + XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE); +} + +static gboolean +atspi_device_x11_grab_keyboard (AtspiDevice *device) +{ + AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device); + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + int min, max; + gint i; + + g_return_val_if_fail (priv->display != NULL, FALSE); +#if 0 + /* THis seems like the right thing to do, but it fails for me */ + return (XGrabKeyboard (priv->display, priv->window, TRUE, GrabModeAsync, GrabModeSync, CurrentTime)) == 0; +#else + if (priv->keyboard_grabbed) + return TRUE; + priv->keyboard_grabbed = TRUE; + refresh_key_grabs (x11_device); + + get_keycode_range (x11_device, &min, &max); + for (i = min; i < max; i++) + grab_key (x11_device, i, 0); + return TRUE; +#endif +} + +static void +atspi_device_x11_ungrab_keyboard (AtspiDevice *device) +{ + AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device); + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + int min, max; + gint i; + + g_return_if_fail (priv->display != NULL); +#if 0 + XUngrabKeyboard (priv->display, CurrentTime); +#else + if (!priv->keyboard_grabbed) + return; + priv->keyboard_grabbed = FALSE; + + get_keycode_range (x11_device, &min, &max); + for (i = min; i < max; i++) + ungrab_key (x11_device, i, 0); + + refresh_key_grabs (x11_device); +#endif +} + +static void +atspi_device_x11_class_init (AtspiDeviceX11Class *klass) +{ + AtspiDeviceClass *device_class = ATSPI_DEVICE_CLASS (klass); + GObjectClass *object_class = (GObjectClass *) klass; + + device_x11_parent_class = g_type_class_peek_parent (klass); + device_class->add_key_grab = atspi_device_x11_add_key_grab; + device_class->map_modifier = atspi_device_x11_map_modifier; + device_class->unmap_modifier = atspi_device_x11_unmap_modifier; + device_class->get_modifier = atspi_device_x11_get_modifier; + device_class->remove_key_grab = atspi_device_x11_remove_key_grab; + device_class->get_locked_modifiers = atspi_device_x11_get_locked_modifiers; + device_class->grab_keyboard = atspi_device_x11_grab_keyboard; + device_class->ungrab_keyboard = atspi_device_x11_ungrab_keyboard; + object_class->finalize = atspi_device_x11_finalize; +} + +/** + * atspi_device_x11_new: + * + * Creates a new #AtspiDeviceX11. + * + * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceX11. + * + **/ +AtspiDeviceX11 * +atspi_device_x11_new () +{ + AtspiDeviceX11 *device = g_object_new (atspi_device_x11_get_type (), NULL); + + return device; +} diff --git a/atspi/atspi-device-x11.h b/atspi/atspi-device-x11.h new file mode 100644 index 0000000..3a91edc --- /dev/null +++ b/atspi/atspi-device-x11.h @@ -0,0 +1,59 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2020 SUSE LLC. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ATSPI_DEVICE_X11_H_ +#define _ATSPI_DEVICE_X11_H_ + +#include "glib-object.h" + +#include "atspi-types.h" +#include "atspi-device.h" + +G_BEGIN_DECLS + +#define ATSPI_TYPE_DEVICE_X11 (atspi_device_x11_get_type ()) +#define ATSPI_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_TYPE_DEVICE_X11, AtspiDeviceX11)) +#define ATSPI_DEVICE_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ATSPI_TYPE_DEVICE_X11, AtspiDeviceX11Class)) +#define ATSPI_IS_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_TYPE_DEVICE_X11)) +#define ATSPI_IS_DEVICE_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ATSPI_TYPE_DEVICE_X11)) +#define ATSPI_DEVICE_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_TYPE_DEVICE_X11, AtspiDeviceX11Class)) + +typedef struct _AtspiDeviceX11 AtspiDeviceX11; +struct _AtspiDeviceX11 +{ + AtspiDevice parent; +}; + +typedef struct _AtspiDeviceX11Class AtspiDeviceX11Class; +struct _AtspiDeviceX11Class +{ + AtspiDeviceClass parent_class; +}; + +GType atspi_device_x11_get_type (void); + +AtspiDeviceX11 *atspi_device_x11_new (); + +G_END_DECLS + +#endif /* _ATSPI_DEVICE_X11_H_ */ diff --git a/atspi/atspi-device.c b/atspi/atspi-device.c new file mode 100644 index 0000000..5f62dc3 --- /dev/null +++ b/atspi/atspi-device.c @@ -0,0 +1,399 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2020 SUSE LLC. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "atspi-private.h" +#include "atspi-device.h" +#include "atspi-device-legacy.h" +#include "atspi-device-x11.h" + +typedef struct +{ + guint id; + guint keycode; + guint keysym; + guint modifiers; + AtspiKeyCallback callback; + void *callback_data; + GDestroyNotify callback_destroyed; +} AtspiKeyGrab; + +typedef struct _AtspiDevicePrivate AtspiDevicePrivate; +struct _AtspiDevicePrivate +{ + GSList *key_watchers; + GSList *keygrabs; + guint last_grab_id; +}; + +GObjectClass *device_parent_class; + +static void +atspi_device_init (AtspiDevice *device) +{ +} + +G_DEFINE_TYPE_WITH_CODE (AtspiDevice, atspi_device, + G_TYPE_OBJECT, + G_ADD_PRIVATE (AtspiDevice)) + +static void +atspi_device_finalize (GObject *object) +{ + AtspiDevice *device = (AtspiDevice *) object; + AtspiDevicePrivate *priv = atspi_device_get_instance_private (device); + + g_slist_free_full (priv->keygrabs, g_free); + priv->keygrabs = NULL; + + device_parent_class->finalize (object); +} + +static void +atspi_device_real_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd) +{ +} + +static void +atspi_device_real_remove_key_grab (AtspiDevice *device, guint id) +{ +} + +static void +atspi_device_class_init (AtspiDeviceClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + device_parent_class = g_type_class_peek_parent (klass); + klass->add_key_grab = atspi_device_real_add_key_grab; + klass->remove_key_grab = atspi_device_real_remove_key_grab; + object_class->finalize = atspi_device_finalize; +} + +/** + * atspi_device_new: + * + * Creates a new #AtspiDevice with a specified callback function. + * + * Returns: (transfer full): a pointer to a newly-created #AtspiDevice. + * + **/ +AtspiDevice * +atspi_device_new () +{ +#ifdef HAVE_X11 + if (!g_getenv ("WAYLAND_DISPLAY") && !g_getenv ("ATSPI_USE_LEGACY_DEVICE")) + return ATSPI_DEVICE (atspi_device_x11_new ()); +#endif + + return ATSPI_DEVICE (atspi_device_legacy_new ()); +} + +gboolean +atspi_device_notify_key (AtspiDevice *device, gboolean pressed, int keycode, int keysym, gint state, gchar *text) +{ + AtspiDevicePrivate *priv = atspi_device_get_instance_private (device); + GSList *l; + gboolean ret = FALSE; + + for (l = priv->key_watchers; l; l = l->next) + { + AtspiKeyGrab *grab = l->data; + grab->callback (device, pressed, keycode, keysym, state, text, grab->callback_data); + } + + for (l = priv->keygrabs; l; l = l->next) + { + AtspiKeyGrab *grab = l->data; + //if (keycode == grab->keycode && (grab->modifiers & state) == grab->modifiers) + if (keycode == grab->keycode && grab->modifiers == state) + { + if (grab->callback) + grab->callback (device, pressed, keycode, keysym, state, text, grab->callback_data); + ret = TRUE; + } + } + + return ret; +} + +static gboolean +is_id_used (AtspiDevice *device, guint id) +{ + AtspiDevicePrivate *priv = atspi_device_get_instance_private (device); + GSList *l; + + for (l = priv->key_watchers; l; l = l->next) + { + AtspiKeyGrab *grab = l->data; + if (grab->id == id) + return TRUE; + } + + for (l = priv->keygrabs; l; l = l->next) + { + AtspiKeyGrab *grab = l->data; + if (grab->id == id) + return TRUE; + } + + return FALSE; +} + +static guint +get_grab_id (AtspiDevice *device) +{ + AtspiDevicePrivate *priv = atspi_device_get_instance_private (device); + + while (is_id_used (device, priv->last_grab_id)) priv->last_grab_id++; + return priv->last_grab_id++; +} + +static gboolean +grab_has_duplicate (AtspiDevice *device, AtspiKeyGrab *grab) +{ + AtspiDevicePrivate *priv = atspi_device_get_instance_private (device); + GSList *l; + + for (l = priv->keygrabs; l; l = l->next) + { + AtspiKeyGrab *other_grab = l->data; + if (other_grab->id != grab->id && other_grab->keycode == grab->keycode && other_grab->modifiers == grab->modifiers) + return TRUE; + } + + return FALSE; +} + +/** + *atspi_device_add_key_grab: +* @device: the device. + * @kd: a #AtspiKeyDefinition specifying the key code to grab. + * @callback: (scope notified) (allow-none): the function to call when the + * given key is pressed. + * @user_data: Data to be passed to @callback. + * @callback_destroyed: callback function to be called when @callback is + * destroyed. + * + * Returns: an identifier that can be later used to remove the grab. + * Add a key grab for the given key/modifier combination. + */ +guint +atspi_device_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed) +{ + AtspiDevicePrivate *priv = atspi_device_get_instance_private (device); + AtspiKeyGrab *grab = g_new (AtspiKeyGrab, 1); + grab->keycode = kd->keycode; + grab->keysym = kd->keysym; + grab->modifiers = kd->modifiers; + grab->callback = callback; + grab->callback_data = user_data; + grab->callback_destroyed = callback_destroyed; + grab->id = get_grab_id (device); + priv->keygrabs = g_slist_append (priv->keygrabs, grab); + + if (!grab_has_duplicate (device, grab)) + ATSPI_DEVICE_GET_CLASS (device)->add_key_grab (device, kd); + return grab->id; +} + +/** + * atspi_device_remove_key_grab: + * @device: the device. + * @id: the identifier of the grab to be removed. + * + * Removes the key grab specified by @id. + */ +void +atspi_device_remove_key_grab (AtspiDevice *device, guint id) +{ + AtspiDevicePrivate *priv = atspi_device_get_instance_private (device); + GSList *l; + + for (l = priv->keygrabs; l; l = l->next) + { + AtspiKeyGrab *grab = l->data; + if (grab->id == id) + { + if (!grab_has_duplicate (device, grab)) + ATSPI_DEVICE_GET_CLASS (device)->remove_key_grab (device, id); + priv->keygrabs = g_slist_remove (priv->keygrabs, grab); + if (grab->callback_destroyed) + (*grab->callback_destroyed) (grab->callback); + g_free (grab); + return; + } + } +} + +/** + *atspi_device_add_key_watcher: +* @device: the device. + * @callback: (scope notified): the function to call when the given key is + * pressed. + * @user_data: (closure callback): Data to be passed to @callback. + * @callback_destroyed: (destroy callback): callback function to be called + * when @callback is destroyed. + * + * Add a callback that will receive a notification whenever a key is + * pressed or released. + */ +void +atspi_device_add_key_watcher (AtspiDevice *device, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed) +{ + AtspiDevicePrivate *priv = atspi_device_get_instance_private (device); + AtspiKeyGrab *grab = g_new0 (AtspiKeyGrab, 1); + grab->id = get_grab_id (device); + grab->callback = callback; + grab->callback_data = user_data; + grab->callback_destroyed = callback_destroyed; + priv->key_watchers = g_slist_append (priv->key_watchers, grab); +} + +AtspiKeyDefinition * +atspi_device_get_grab_by_id (AtspiDevice *device, guint id) +{ + AtspiDevicePrivate *priv = atspi_device_get_instance_private (device); + GSList *l; + + for (l = priv->keygrabs; l; l = l->next) + { + AtspiKeyGrab *grab = l->data; + if (grab->id == id) + { + AtspiKeyDefinition *kd = g_new0 (AtspiKeyDefinition, 1); + kd->keycode = grab->keycode;\ + kd->modifiers = grab->modifiers; + return kd; + } + } + return NULL; +} + +/** + * atspi_device_map_modifier: + * @device: the device. + * @keycode: the keycode to map. + * + * Maps the specified key code to a modifier so that it can be used in + * conjunction with other keys to create a key grab. If the given keycode is + * already mapped, then this function will return the modifier that is + * currently mapped to the keycode, without doing anything else. Otherwise, + * it will use the last modifier that AT-SPI used to map a key. If no keys + * have yet been mapped using this device, then it will look for a modifier + * that is not currently being used. If no unused modifier can be found, + * then it will use the first modifier by default. + * + * Returns: the modifier that is now mapped to this keycode. This return + * value can be passed to atspi_device_add_key_grab. + */ +guint +atspi_device_map_modifier (AtspiDevice *device, gint keycode) +{ + if (ATSPI_DEVICE_GET_CLASS (device)->map_modifier) + return ATSPI_DEVICE_GET_CLASS (device)->map_modifier (device, keycode); + + return 0; +} + +/** + * atspi_device_unmap_modifier: + * @device: the device. + * @keycode: the keycode to unmap. + * + * Removes a mapped modifier from the given keycode. + */ +void +atspi_device_unmap_modifier (AtspiDevice *device, gint keycode) +{ + if (ATSPI_DEVICE_GET_CLASS (device)->unmap_modifier) + ATSPI_DEVICE_GET_CLASS (device)->unmap_modifier (device, keycode); +} + +/** + * atspi_device_get_modifier: + * @device: the device. + * @keycode: the keycode to map. + * + * Gets the modifier for a given keycode, if one exists. Does not creatt a new + * mapping. This function should be used when the intention is to query a + * locking modifier such as num lock via atspi_device_get_locked_modifiers, + * rather than to add key grabs. + * + * Returns: the modifier that is mapped to this keycode. + */ +guint +atspi_device_get_modifier (AtspiDevice *device, gint keycode) +{ + if (ATSPI_DEVICE_GET_CLASS (device)->get_modifier) + return ATSPI_DEVICE_GET_CLASS (device)->get_modifier (device, keycode); + + return 0; +} + +/** + * atspi_device_get_locked_modifiers: + * @device: the device. + * + * Returns the locked modifiers (ie, num lock, caps lock) associated with this + * keyboard. + * + * Returns: a guint of modifier flags. + */ +guint +atspi_device_get_locked_modifiers (AtspiDevice *device) +{ + if (ATSPI_DEVICE_GET_CLASS (device)->get_locked_modifiers) + return ATSPI_DEVICE_GET_CLASS (device)->get_locked_modifiers (device); + + return 0; +} + +/** + * atspi_device_grab_keyboard: + * + * Attempts to grab the entire keyboard. This should only be done + * temporarily, as it may conflict with other applications that also want to + * grab the keyboard. + * + * Returns: #TRUE if successful, #FALSE otherwise. + */ +gboolean +atspi_device_grab_keyboard (AtspiDevice *device) +{ + if (ATSPI_DEVICE_GET_CLASS (device)->grab_keyboard) + return ATSPI_DEVICE_GET_CLASS (device)->grab_keyboard (device); + + return FALSE; +} + +/** + * atspi_device_ungrab_keyboard: + * + * Removes a keyboard grab added via a call to atspi_device_add_keyboard. + */ +void +atspi_device_ungrab_keyboard (AtspiDevice *device) +{ + if (ATSPI_DEVICE_GET_CLASS (device)->ungrab_keyboard) + ATSPI_DEVICE_GET_CLASS (device)->ungrab_keyboard (device); +} + diff --git a/atspi/atspi-device.h b/atspi/atspi-device.h new file mode 100644 index 0000000..a246842 --- /dev/null +++ b/atspi/atspi-device.h @@ -0,0 +1,103 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2020 SUSE LLC. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _ATSPI_DEVICE_H_ +#define _ATSPI_DEVICE_H_ + +#include "glib-object.h" + +#include "atspi-types.h" + +G_BEGIN_DECLS + +#define ATSPI_TYPE_DEVICE (atspi_device_get_type ()) +#define ATSPI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_TYPE_DEVICE, AtspiDevice)) +#define ATSPI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ATSPI_TYPE_DEVICE, AtspiDeviceClass)) +#define ATSPI_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_TYPE_DEVICE)) +#define ATSPI_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ATSPI_TYPE_DEVICE)) +#define ATSPI_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_TYPE_DEVICE, AtspiDeviceClass)) + +typedef struct _AtspiDevice AtspiDevice; +struct _AtspiDevice +{ + GObject parent; +}; + +typedef struct _AtspiDeviceClass AtspiDeviceClass; +struct _AtspiDeviceClass +{ + GObjectClass parent_class; + + void (*add_key_grab) (AtspiDevice *device, AtspiKeyDefinition *kd); + void (*remove_key_grab) (AtspiDevice *device, guint id); + guint (*map_modifier) (AtspiDevice *device, gint keycode); + void (*unmap_modifier) (AtspiDevice *device, gint keycode); + guint (*get_modifier) (AtspiDevice *device, gint keycode); + gboolean (*grab_keyboard) (AtspiDevice *device); + void (*ungrab_keyboard) (AtspiDevice *device); + guint (*get_locked_modifiers) (AtspiDevice *device); +}; + +GType atspi_device_get_type (void); + +/** + * AtspiKeyCallback: + * @device: the device. + * @pressed: TRUE if the key is being pressed, FALSE if being released. + * @keycode: the hardware code for the key. + * @keysym: the keysym for the key. + * @modifiers: a bitflag indicating which key modifiers are active. + * @keystring: the text corresponding to the keypress. + * @user_data: (closure): user-supplied data + * + * A callback that will be invoked when a key is pressed. + */ +typedef void (*AtspiKeyCallback) (AtspiDevice *device, gboolean pressed, guint keycode, guint keysym, guint modifiers, const gchar *keystring, void *user_data); + +AtspiDevice *atspi_device_new (); + +gboolean atspi_device_notify_key (AtspiDevice *device, gboolean pressed, int keycode, int keysym, gint state, gchar *text); + +guint atspi_device_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed); + +void atspi_device_remove_key_grab (AtspiDevice *device, guint id); + +void atspi_device_add_key_watcher (AtspiDevice *device, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed); + +AtspiKeyDefinition *atspi_device_get_grab_by_id (AtspiDevice *device, guint id); + +guint atspi_device_map_modifier (AtspiDevice *device, gint keycode); + +void atspi_device_unmap_modifier (AtspiDevice *device, gint keycode); + +guint atspi_device_get_modifier (AtspiDevice *device, gint keycode); + +guint atspi_device_get_locked_modifiers (AtspiDevice *device); + +gboolean atspi_device_grab_keyboard (AtspiDevice *device); + +void atspi_device_ungrab_keyboard (AtspiDevice *device); + +G_END_DECLS + +#endif /* _ATSPI_DEVICE_H_ */ diff --git a/atspi/atspi-enum-types.c.template b/atspi/atspi-enum-types.c.template index 385d0ee..92e4937 100644 --- a/atspi/atspi-enum-types.c.template +++ b/atspi/atspi-enum-types.c.template @@ -5,7 +5,7 @@ /*** BEGIN file-production ***/ /* enumerations from "@basename@" */ -#include "@filename@" +#include "@basename@" /*** END file-production ***/ diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c index 249890b..ca6828f 100644 --- a/atspi/atspi-event-listener.c +++ b/atspi/atspi-event-listener.c @@ -166,6 +166,9 @@ atspi_event_listener_new_simple (AtspiEventListenerSimpleCB callback, } static GList *event_listeners = NULL; +static GList *pending_removals = NULL; +static int in_send = 0; + static gchar * convert_name_from_dbus (const char *name, gboolean path_hack) @@ -332,40 +335,10 @@ demarshal_rect (DBusMessageIter *iter, AtspiRect *rect) return TRUE; } -static gchar * -strdup_and_adjust_for_dbus (const char *s) -{ - gchar *d = g_strdup (s); - gchar *p; - int parts = 0; - - if (!d) - return NULL; - - for (p = d; *p; p++) - { - if (*p == '-') - { - memmove (p, p + 1, g_utf8_strlen (p, -1)); - *p = toupper (*p); - } - else if (*p == ':') - { - parts++; - if (parts == 2) - break; - p [1] = toupper (p [1]); - } - } - - d [0] = toupper (d [0]); - return d; -} - static gboolean convert_event_type_to_dbus (const char *eventType, char **categoryp, char **namep, char **detailp, GPtrArray **matchrule_array) { - gchar *tmp = strdup_and_adjust_for_dbus (eventType); + gchar *tmp = _atspi_strdup_and_adjust_for_dbus (eventType); char *category = NULL, *name = NULL, *detail = NULL; char *saveptr = NULL; @@ -420,6 +393,12 @@ listener_entry_free (EventListenerEntry *e) g_free (e->name); if (e->detail) g_free (e->detail); callback_unref (callback); + + for (int i=0; i < e->properties->len; i++) + g_free (g_array_index (e->properties, char*, i)); + + g_array_free (e->properties, TRUE); + g_free (e); } @@ -627,7 +606,7 @@ copy_event_properties (GArray *src) for (i = 0; i < src->len; i++) { gchar *dup = g_strdup (g_array_index (src, char *, i)); - g_array_append_val (dst, dup); + g_array_append_val (dst, dup); } return dst; } @@ -677,6 +656,7 @@ atspi_event_listener_register_from_callback_full (AtspiEventListenerCB callback, callback_destroyed); if (!convert_event_type_to_dbus (event_type, &e->category, &e->name, &e->detail, &matchrule_array)) { + g_free (e->event_type); g_free (e); return FALSE; } @@ -815,12 +795,12 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback, is_superset (name, e->name) && is_superset (detail, e->detail)) { - gboolean need_replace; DBusMessage *message, *reply; - need_replace = (l == event_listeners); - l = g_list_remove (l, e); - if (need_replace) - event_listeners = l; + l = g_list_next (l); + if (in_send) + pending_removals = g_list_append (pending_removals, e); + else + event_listeners = g_list_remove (event_listeners, e); for (i = 0; i < matchrule_array->len; i++) { char *matchrule = g_ptr_array_index (matchrule_array, i); @@ -837,9 +817,11 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback, if (reply) dbus_message_unref (reply); - listener_entry_free (e); + if (!in_send) + listener_entry_free (e); } - else l = g_list_next (l); + else + l = g_list_next (l); } g_free (category); g_free (name); @@ -882,7 +864,8 @@ atspi_event_copy (AtspiEvent *src) dst->detail2 = src->detail2; g_value_init (&dst->any_data, G_VALUE_TYPE (&src->any_data)); g_value_copy (&src->any_data, &dst->any_data); - dst->sender = g_object_ref (src->sender); + if (src->sender) + dst->sender = g_object_ref (src->sender); return dst; } @@ -892,7 +875,7 @@ atspi_event_free (AtspiEvent *event) g_object_unref (event->source); g_free (event->type); g_value_unset (&event->any_data); - g_object_unref (event->sender); + g_clear_object (&event->sender); g_free (event); } @@ -911,6 +894,13 @@ detail_matches_listener (const char *event_detail, const char *listener_detail) : strcmp (listener_detail, event_detail)); } +static void +resolve_pending_removal (gpointer data) +{ + event_listeners = g_list_remove (event_listeners, data); + listener_entry_free (data); +} + void _atspi_send_event (AtspiEvent *e) { @@ -931,6 +921,7 @@ _atspi_send_event (AtspiEvent *e) g_warning ("AT-SPI: Couldn't parse event: %s\n", e->type); return; } + in_send++; for (l = event_listeners; l; l = g_list_next (l)) { EventListenerEntry *entry = l->data; @@ -947,15 +938,27 @@ _atspi_send_event (AtspiEvent *e) } if (!l2) { + for (l2 = pending_removals; l2; l2 = l2->next) + { + if (l2->data == entry) + break; + } + } + if (!l2) + { entry->callback (atspi_event_copy (e), entry->user_data); called_listeners = g_list_prepend (called_listeners, entry); } } } + in_send--; if (detail) g_free (detail); g_free (name); g_free (category); g_list_free (called_listeners); + + g_list_free_full (pending_removals, resolve_pending_removal); + pending_removals = NULL; } DBusHandlerResult @@ -1057,6 +1060,7 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data) accessible = _atspi_dbus_return_accessible_from_iter (&iter_variant); if (!strcmp (category, "ScreenReader")) { + g_object_unref (e.source); e.source = accessible; } else @@ -1116,6 +1120,7 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data) g_free (name); g_free (detail); g_object_unref (e.source); + g_object_unref (e.sender); g_value_unset (&e.any_data); return DBUS_HANDLER_RESULT_HANDLED; } diff --git a/atspi/atspi-misc-private.h b/atspi/atspi-misc-private.h index f0b5adb..fb78b02 100644 --- a/atspi/atspi-misc-private.h +++ b/atspi/atspi-misc-private.h @@ -168,6 +168,8 @@ gboolean _atspi_set_allow_sync (gboolean val); void _atspi_set_error_no_sync (GError **error); gboolean _atspi_prepare_screen_reader_interface (); + +gchar * _atspi_strdup_and_adjust_for_dbus (const char *s); G_END_DECLS #endif /* _ATSPI_MISC_PRIVATE_H_ */ diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index 9e8595a..7af46e9 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -35,6 +35,7 @@ #include "atspi-gmain.h" #include #include +#include static void handle_get_items (DBusPendingCall *pending, void *user_data); @@ -132,6 +133,8 @@ _atspi_bus () static AtspiAccessible *desktop; +static void cleanup_deferred_message (void); + static void cleanup () { @@ -145,15 +148,8 @@ cleanup () g_hash_table_destroy (refs); } - if (bus) - { - dbus_connection_close (bus); - dbus_connection_unref (bus); - bus = NULL; - } - if (!desktop) - return; + goto end; /* TODO: Do we need this code, or should we just dispose the desktop? */ for (i = desktop->children->len - 1; i >= 0; i--) @@ -167,6 +163,14 @@ cleanup () g_object_run_dispose (G_OBJECT (desktop->parent.app)); g_object_unref (desktop); desktop = NULL; + +end: + if (bus) + { + dbus_connection_close (bus); + dbus_connection_unref (bus); + bus = NULL; + } } static gboolean atspi_inited = FALSE; @@ -454,7 +458,16 @@ add_accessible_from_iter (DBusMessageIter *iter) if (index >= 0 && accessible->accessible_parent) { if (index >= accessible->accessible_parent->children->len) + { + /* There is no room for this object */ g_ptr_array_set_size (accessible->accessible_parent->children, index + 1); + } + else + { + /* This place is already taken - let's free this place with dignity */ + if (g_ptr_array_index (accessible->accessible_parent->children, index)) + g_object_unref (g_ptr_array_index (accessible->accessible_parent->children, index)); + } g_ptr_array_index (accessible->accessible_parent->children, index) = g_object_ref (accessible); } @@ -763,6 +776,23 @@ process_deferred_message (BusDataClosure *closure) static GQueue *deferred_messages = NULL; +static void +destroy_deferred_message_item(gpointer ptr) +{ + /* TODO this is still memory leak on c->data */ + BusDataClosure *c = ptr; + dbus_message_unref (c->message); + dbus_connection_unref (c->bus); + g_free (c); +} + +static void +cleanup_deferred_message(void) +{ + g_queue_free_full (deferred_messages, destroy_deferred_message_item); + deferred_messages = NULL; +} + static gboolean process_deferred_messages (void) { @@ -1244,7 +1274,7 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, process_deferred_messages (); if (!reply) { - // TODO: throw exception + /* TODO: throw exception */ goto done; } @@ -1275,6 +1305,11 @@ _atspi_dbus_get_property (gpointer obj, const char *interface, const char *name, } else { + if (type [0] == 's') + { + *(char**) data = NULL; + } + dbus_message_iter_get_basic (&iter_variant, data); if (type [0] == 's') *(char **)data = g_strdup (*(char **)data); @@ -1403,7 +1438,7 @@ _atspi_dbus_set_interfaces (AtspiAccessible *accessible, DBusMessageIter *iter) accessible->interfaces = 0; if (strcmp (iter_sig, "as") != 0) { - g_warning ("_atspi_dbus_set_interfaces: Passed iterator with invalid signature %s", dbus_message_iter_get_signature (iter)); + g_warning ("_atspi_dbus_set_interfaces: Passed iterator with invalid signature %s", iter_sig); dbus_free (iter_sig); return; } @@ -1552,7 +1587,7 @@ get_accessibility_bus_address_dbus (void) address = g_strdup (tmp_address); dbus_message_unref (reply); } - + out: dbus_connection_unref (session_bus); return address; @@ -1899,3 +1934,33 @@ _atspi_prepare_screen_reader_interface () dbus_connection_add_filter (a11y_bus, screen_reader_filter, NULL, NULL); return TRUE; } + +gchar * +_atspi_strdup_and_adjust_for_dbus (const char *s) +{ + gchar *d = g_strdup (s); + gchar *p; + int parts = 0; + + if (!d) + return NULL; + + for (p = d; *p; p++) + { + if (*p == '-') + { + memmove (p, p + 1, g_utf8_strlen (p, -1)); + *p = toupper (*p); + } + else if (*p == ':') + { + parts++; + if (parts == 2) + break; + p [1] = toupper (p [1]); + } + } + + d [0] = toupper (d [0]); + return d; +} diff --git a/atspi/atspi-registry.c b/atspi/atspi-registry.c index c50ae20..dea5878 100644 --- a/atspi/atspi-registry.c +++ b/atspi/atspi-registry.c @@ -528,7 +528,6 @@ atspi_generate_mouse_event (glong x, glong y, const gchar *name, GError **error) /** * atspi_set_reference_window: - * * @accessible: the #AtspiAccessible corresponding to the window to select. * should be a top-level window with a role of * ATSPI_ROLE_APPLICATION. @@ -557,7 +556,7 @@ atspi_key_definition_copy (AtspiKeyDefinition *src) dst->keysym = src->keysym; if (src->keystring) dst->keystring = g_strdup (src->keystring); - dst->unused = src->unused; + dst->modifiers = src->modifiers; return dst; } diff --git a/atspi/atspi-types.h b/atspi/atspi-types.h index 2631baf..ab43ca8 100644 --- a/atspi/atspi-types.h +++ b/atspi/atspi-types.h @@ -78,9 +78,16 @@ struct _AtspiKeyDefinition gint keycode; gint keysym; gchar *keystring; - gint unused; + guint modifiers; }; +/** + * ATSPI_TYPE_KEY_DEFINITION: + * + * The #GType for a boxed type holding a #AtspiKeyDefinition. + */ +#define ATSPI_TYPE_KEY_DEFINITION (atspi_key_definition_get_type ()) + typedef struct _AtspiEvent AtspiEvent; struct _AtspiEvent { diff --git a/atspi/atspimarshal.list b/atspi/atspimarshal.list index 2d9dd51..163720a 100644 --- a/atspi/atspimarshal.list +++ b/atspi/atspimarshal.list @@ -23,3 +23,4 @@ # BOOL deprecated alias for BOOLEAN VOID:INT,INT +VOID:INT,STRING diff --git a/atspi/meson.build b/atspi/meson.build index 8a2ca27..ace8502 100644 --- a/atspi/meson.build +++ b/atspi/meson.build @@ -4,6 +4,8 @@ atspi_sources = [ 'atspi-application.c', 'atspi-collection.c', 'atspi-component.c', + 'atspi-device.c', + 'atspi-device-legacy.c', 'atspi-device-listener.c', 'atspi-document.c', 'atspi-editabletext.c', @@ -34,6 +36,8 @@ atspi_headers = [ 'atspi-collection.h', 'atspi-component.h', 'atspi-constants.h', + 'atspi-device.h', + 'atspi-device-legacy.h', 'atspi-device-listener.h', 'atspi-document.h', 'atspi-editabletext.h', @@ -56,6 +60,14 @@ atspi_headers = [ 'atspi-value.h', ] +x11_option = get_option('x11') +if x11_option != 'no' + if x11_dep.found() + atspi_sources += ['atspi-device-x11.c'] + atspi_headers += ['atspi-device-x11.h'] + endif +endif + atspi_includedir = join_paths(get_option('prefix'), get_option('includedir'), 'at-spi-2.0', 'atspi') install_headers(atspi_headers, install_dir: atspi_includedir) @@ -127,7 +139,7 @@ pkgconfig.generate( description: 'Accessibility Technology software library', version: meson.project_version(), libraries: atspi, - requires: 'dbus-1 glib-2.0', + requires: ['dbus-1', 'glib-2.0'], subdirs: 'at-spi-2.0', filebase: 'atspi-2', ) diff --git a/bus/accessibility.conf.in b/bus/accessibility.conf.in index 31b6a79..79c5146 100644 --- a/bus/accessibility.conf.in +++ b/bus/accessibility.conf.in @@ -6,7 +6,7 @@ @DATADIR@/dbus-1/accessibility-services EXTERNAL - unix:tmpdir=/tmp + unix:dir=/tmp diff --git a/bus/at-spi-bus-launcher.c b/bus/at-spi-bus-launcher.c index b4f49b8..d7c6690 100644 --- a/bus/at-spi-bus-launcher.c +++ b/bus/at-spi-bus-launcher.c @@ -39,6 +39,9 @@ #include #include #endif +#ifdef DBUS_BROKER +#include +#endif typedef enum { A11Y_BUS_STATE_IDLE = 0, @@ -69,6 +72,7 @@ typedef struct { int pipefd[2]; int listenfd; char *a11y_launch_error_message; + GDBusProxy *sm_proxy; } A11yBusLauncher; static A11yBusLauncher *_global_app = NULL; @@ -140,27 +144,60 @@ client_proxy_ready_cb (GObject *source_object, } static void +client_registered (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + A11yBusLauncher *app = user_data; + GError *error = NULL; + GVariant *variant; + gchar *object_path; + GDBusProxyFlags flags; + + variant = g_dbus_proxy_call_finish (app->sm_proxy, result, &error); + if (!variant) + { + if (error != NULL) + { + g_warning ("Failed to register client: %s", error->message); + g_error_free (error); + } + } + else + { + g_variant_get (variant, "(o)", &object_path); + g_variant_unref (variant); + + flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, flags, NULL, + "org.gnome.SessionManager", object_path, + "org.gnome.SessionManager.ClientPrivate", + NULL, client_proxy_ready_cb, app); + + g_free (object_path); + } + g_clear_object (&app->sm_proxy); +} + +static void register_client (A11yBusLauncher *app) { GDBusProxyFlags flags; - GDBusProxy *sm_proxy; GError *error; const gchar *app_id; const gchar *autostart_id; gchar *client_startup_id; GVariant *parameters; - GVariant *variant; - gchar *object_path; flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS; error = NULL; - sm_proxy = g_dbus_proxy_new_sync (app->session_bus, flags, NULL, - "org.gnome.SessionManager", - "/org/gnome/SessionManager", - "org.gnome.SessionManager", - NULL, &error); + app->sm_proxy = g_dbus_proxy_new_sync (app->session_bus, flags, NULL, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + NULL, &error); if (error != NULL) { @@ -187,31 +224,11 @@ register_client (A11yBusLauncher *app) g_free (client_startup_id); error = NULL; - variant = g_dbus_proxy_call_sync (sm_proxy, - "RegisterClient", parameters, - G_DBUS_CALL_FLAGS_NONE, - -1, NULL, &error); - - g_object_unref (sm_proxy); - - if (error != NULL) - { - g_warning ("Failed to register client: %s", error->message); - g_error_free (error); - - return; - } - - g_variant_get (variant, "(o)", &object_path); - g_variant_unref (variant); - - flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, flags, NULL, - "org.gnome.SessionManager", object_path, - "org.gnome.SessionManager.ClientPrivate", - NULL, client_proxy_ready_cb, app); + g_dbus_proxy_call (app->sm_proxy, + "RegisterClient", parameters, + G_DBUS_CALL_FLAGS_NONE, + G_MAXINT, NULL, client_registered, app); - g_free (object_path); } static void @@ -378,11 +395,26 @@ static gboolean ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path) { char *argv[] = { DBUS_BROKER, config_path, "--scope", "user", NULL }; + char *unit; struct sockaddr_un addr = { .sun_family = AF_UNIX }; socklen_t addr_len = sizeof(addr); GPid pid; GError *error = NULL; + /* This detects whether we are running under systemd. We only try to + * use dbus-broker if we are running under systemd because D-Bus + * service activation won't work otherwise. + */ + if (sd_pid_get_user_unit (getpid (), &unit) >= 0) + { + free (unit); + } + else + { + app->state = A11Y_BUS_STATE_ERROR; + return FALSE; + } + if ((app->listenfd = socket (PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0) g_error ("Failed to create listening socket: %s", strerror (errno)); diff --git a/bus/at-spi-dbus-bus.desktop.in b/bus/at-spi-dbus-bus.desktop.in index b3c90bd..a45d27e 100644 --- a/bus/at-spi-dbus-bus.desktop.in +++ b/bus/at-spi-dbus-bus.desktop.in @@ -4,6 +4,6 @@ Name=AT-SPI D-Bus Bus Exec=@libexecdir@/at-spi-bus-launcher --launch-immediately OnlyShowIn=GNOME;Unity; NoDisplay=true -AutostartCondition=GSETTINGS org.gnome.desktop.interface toolkit-accessibility +AutostartCondition=GSettings org.gnome.desktop.interface toolkit-accessibility X-GNOME-AutoRestart=true X-GNOME-Autostart-Phase=Initialization diff --git a/bus/meson.build b/bus/meson.build index 155dc53..f6c32c9 100644 --- a/bus/meson.build +++ b/bus/meson.build @@ -10,25 +10,21 @@ session_dir = join_paths(atspi_sysconfdir, 'xdg/autostart') configure_file(input: 'accessibility.conf.in', output: 'accessibility.conf', configuration: accessibility_conf, - install: true, install_dir: busconfig_dir) configure_file(input: 'at-spi-dbus-bus.desktop.in', output: 'at-spi-dbus-bus.desktop', configuration: libexec_conf, - install: true, install_dir: session_dir) configure_file(input: 'org.a11y.Bus.service.in', output: 'org.a11y.Bus.service', configuration: libexec_conf, - install: true, install_dir: dbus_services_dir) configure_file(input: 'at-spi-dbus-bus.service.in', output: 'at-spi-dbus-bus.service', configuration: libexec_conf, - install: true, install_dir: systemd_user_dir) launcher_args = [ @@ -52,13 +48,16 @@ else endif endif +needs_systemd = false if get_option('dbus_broker') != 'default' launcher_args += '-DDBUS_BROKER="@0@"'.format(get_option('dbus_broker')) + needs_systemd = true else dbus_broker = find_program('dbus-broker-launch', required: false) if dbus_broker.found() launcher_args += '-DDBUS_BROKER="@0@"'.format(dbus_broker.path()) + needs_systemd = true endif endif @@ -66,9 +65,15 @@ if get_option('default_bus') == 'dbus-broker' launcher_args += '-DWANT_DBUS_BROKER' endif +if needs_systemd + systemd_dep = dependency('libsystemd') +else + systemd_dep = dependency('', required: false) +endif + executable('at-spi-bus-launcher', 'at-spi-bus-launcher.c', include_directories: [ root_inc, include_directories('.') ], - dependencies: [ gio_dep, x11_deps ], + dependencies: [ gio_dep, systemd_dep, x11_deps ], c_args: launcher_args, install: true, install_dir: atspi_libexecdir) diff --git a/dbind/dbtest.c b/dbind/dbtest.c index 4f58540..b338c03 100644 --- a/dbind/dbtest.c +++ b/dbind/dbtest.c @@ -86,7 +86,7 @@ void test_simple () dbind_any_free ("i", &v2); /* nop */ dbus_message_unref (msg); - fprintf (stderr, "simple ok\n"); + printf ("simple ok\n"); } void test_array () @@ -117,7 +117,7 @@ void test_array () dbind_any_free ("ai", &a2); dbus_message_unref (msg); - fprintf (stderr, "array ok\n"); + printf ("array ok\n"); } /* this taught me that the struct type is a mis-nomer, @@ -146,7 +146,7 @@ void test_struct_native () } dbus_message_iter_close_container (&iter, &arr); - fprintf (stderr, "native struct marshalling ok\n"); + printf ("native struct marshalling ok\n"); dbus_message_unref (msg); } @@ -185,7 +185,7 @@ void test_struct_simple () g_assert (!strcmp (g_array_index (a2, FooBaa, 1).baa, "baA")); g_assert (!strcmp (g_array_index (a2, FooBaa, 1).baz, "BaZ")); - fprintf (stderr, "simple struct ok\n"); + printf ("simple struct ok\n"); dbind_any_free ("a(sss)", &a2); dbus_message_unref (msg); @@ -248,7 +248,7 @@ void test_struct_complex () g_assert (c2.pad2 == 1); g_assert (!strcmp (c1.name, "stroustrup")); - fprintf (stderr, "complex struct ok\n"); + printf ("complex struct ok\n"); dbind_any_free (TYPEOF_COMPLEX, &c2); dbus_message_unref (msg); @@ -296,7 +296,7 @@ void test_struct_with_array () g_assert (p[0].pad1 == 2); g_assert (g_array_index (p[1].vals, dbus_uint32_t, 1) == 1000000000); - fprintf (stderr, "struct with array ok\n"); + printf ("struct with array ok\n"); dbind_any_free (TYPEOF_ARRAYSTRUCT, &a2); dbus_message_unref (msg); @@ -342,7 +342,7 @@ void test_twovals () dbind_any_free ("ii", &o); /* nop */ dbus_message_unref (msg); - fprintf (stderr, "two-val ok\n"); + printf ("two-val ok\n"); } void test_marshalling () @@ -355,7 +355,7 @@ void test_marshalling () test_struct_with_array (); test_twovals (); - fprintf (stderr, "Marshalling ok\n"); + printf ("Marshalling ok\n"); } void test_teamspaces (DBusConnection *bus) @@ -403,7 +403,7 @@ void test_helpers () dbind_find_c_alignment ("a(sss)"); dbind_find_c_alignment ("(s(s)yd(d)s)"); dbind_find_c_alignment ("a{ss}"); - fprintf (stderr, "helpers passed\n"); + printf ("helpers passed\n"); } int main (int argc, char **argv) diff --git a/meson.build b/meson.build index 0a2f75e..ca49dfb 100644 --- a/meson.build +++ b/meson.build @@ -1,12 +1,12 @@ project('at-spi2-core', 'c', - version: '2.34.0', + version: '2.39.90.1', license: 'LGPLv2.1+', default_options: [ 'buildtype=debugoptimized', 'warning_level=1', 'c_std=c99', ], - meson_version: '>= 0.50.0') + meson_version: '>= 0.46.0') add_project_arguments([ '-D_POSIX_C_SOURCE=200809L', '-D_DEFAULT_SOURCE' ], language: 'c') @@ -44,7 +44,7 @@ endif # Dependencies libdbus_req_version = '>= 1.5' -glib_req_version = '>= 2.32.0' +glib_req_version = '>= 2.62.0' gobject_req_version = '>= 2.0.0' gio_req_version = '>= 2.28.0' diff --git a/po/gl.po b/po/gl.po index d7a0cf4..932ffea 100644 --- a/po/gl.po +++ b/po/gl.po @@ -6,9 +6,9 @@ msgid "" msgstr "" "Project-Id-Version: at-spi 2-core\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-02-07 00:01+0100\n" -"PO-Revision-Date: 2014-02-07 00:01+0200\n" +"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/at-spi2-core/issues\n" +"POT-Creation-Date: 2018-10-05 04:46+0000\n" +"PO-Revision-Date: 2019-12-25 05:11+0100\n" "Last-Translator: Fran Dieguez \n" "Language-Team: gnome-l10n-gl@gnome.org\n" "Language: gl\n" @@ -16,87 +16,12 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Virtaal 0.7.1\n" +"X-Generator: Poedit 2.2.4\n" -#: ../atspi/atspi-component.c:325 ../atspi/atspi-misc.c:1034 -#: ../atspi/atspi-value.c:111 +#: atspi/atspi-component.c:326 atspi/atspi-misc.c:1073 atspi/atspi-value.c:111 msgid "The application no longer exists" -msgstr "O aplicativo xa non existe" +msgstr "A aplicación xa non existe" -#: ../atspi/atspi-misc.c:1777 +#: atspi/atspi-misc.c:1850 msgid "Attempted synchronous call where prohibited" msgstr "Tentouse unha chamada síncrona onde estaba prohibida" - -#~ msgid "AT-SPI: Unknown signature %s for RemoveAccessible" -#~ msgstr "AT-SPI: Sinatura %s descoñecida para RemoveAccessible" - -#~ msgid "AT-SPI: Error calling getRoot for %s: %s" -#~ msgstr "AT-SPI: Erro ao chamar a getRoot para %s: %s" - -#~ msgid "AT-SPI: Error in GetItems, sender=%s, error=%s" -#~ msgstr "AT-SPI: Erro en GetItems, emisor=%s, erro=%s" - -#~ msgid "" -#~ "AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange " -#~ "signature %s" -#~ msgstr "" -#~ "AT-SPI: Chamouse a _atspi_dbus_return_accessible_from_message cunha " -#~ "sinatura estraña %s" - -#~ msgid "" -#~ "AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange " -#~ "signature %s" -#~ msgstr "" -#~ "AT-SPI: Chamouse a _atspi_dbus_return_hyperlink_from_message cunha " -#~ "sinatura estraña %s" - -#~ msgid "AT-SPI: AddAccessible with unknown signature %s\n" -#~ msgstr "AT-SPI: AddAccessible con sinatura %s descoñecida\n" - -#~ msgid "AT-SPI: Could not get the display\n" -#~ msgstr "AT-SPI: Non é posíbel obter a pantalla\n" - -#~ msgid "AT-SPI: Accessibility bus not found - Using session bus.\n" -#~ msgstr "" -#~ "AT-SPI: Non foi posíbel atopar o bus de accesibilidade - Usando o bus de " -#~ "sesión.\n" - -#~ msgid "AT-SPI: Couldn't connect to bus: %s\n" -#~ msgstr "AT-SPI: Non foi posíbel conectarse ao bus: %s\n" - -#~ msgid "AT-SPI: Couldn't register with bus: %s\n" -#~ msgstr "AT-SPI: Non foi posíbel rexistrarse ao bus: %s\n" - -#~ msgid "" -#~ "AT-SPI: expected a variant when fetching %s from interface %s; got %s\n" -#~ msgstr "" -#~ "AT-SPI: agardábase unha variante ao obter %s desde a interface %s; porén " -#~ "obtívose %s\n" - -#~ msgid "atspi_dbus_get_property: Wrong type: expected %s, got %c\n" -#~ msgstr "" -#~ "atspi_dbus_get_property: Tipo incorrecto: esperábase %s, obtívose %c\n" - -#~ msgid "AT-SPI: Unknown interface %s" -#~ msgstr "AT-SPI: Interface %s descoñecida" - -#~ msgid "AT-SPI: expected 2 values in states array; got %d\n" -#~ msgstr "" -#~ "AT-SPI: agardábanse 2 valores na matriz de estados; porén obtivéronse %d\n" - -#~ msgid "Streamable content not implemented" -#~ msgstr "Contido en fluxo non implementado" - -#~ msgid "" -#~ "called atspi_event_listener_register_from_callback with a NULL event_type" -#~ msgstr "" -#~ "chamouse a atspi_event_listener_register_from_callback con un event_type " -#~ "nulo" - -#~ msgid "Got invalid signature %s for signal %s from interface %s\n" -#~ msgstr "" -#~ "Obtívose unha sinatura non válida %s para o sinal %s desde a interface " -#~ "%s\n" - -#~ msgid "AT-SPI: Got error: %s\n" -#~ msgstr "AT-SPI: Obtívose o erro: %s\n" diff --git a/po/pt.po b/po/pt.po index abe1ff6..9953a00 100644 --- a/po/pt.po +++ b/po/pt.po @@ -7,27 +7,25 @@ msgid "" msgstr "" "Project-Id-Version: 3.12\n" -"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=at-" -"spi&keywords=I18N+L10N&component=general\n" -"POT-Creation-Date: 2015-06-08 02:51+0000\n" -"PO-Revision-Date: 2015-06-24 06:27+0100\n" -"Last-Translator: Pedro Albuquerque \n" +"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/at-spi2-core/issues\n" +"POT-Creation-Date: 2020-05-27 15:50+0000\n" +"PO-Revision-Date: 2020-06-24 11:33+0100\n" +"Last-Translator: Manuela Silva \n" "Language-Team: Português \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Gtranslator 2.91.6\n" +"X-Generator: Poedit 2.3.1\n" -#: ../atspi/atspi-component.c:326 ../atspi/atspi-misc.c:1037 -#: ../atspi/atspi-value.c:111 +#: atspi/atspi-component.c:332 atspi/atspi-misc.c:1075 atspi/atspi-value.c:111 msgid "The application no longer exists" msgstr "A aplicação já não existe" -#: ../atspi/atspi-misc.c:1795 +#: atspi/atspi-misc.c:1853 msgid "Attempted synchronous call where prohibited" -msgstr "Tentada uma chamada síncrona onde é proibida" +msgstr "Tentativa de chamada síncrona onde proibida" #~ msgid "AT-SPI: Unknown signature %s for RemoveAccessible" #~ msgstr "AT-SPI: Assinatura %s desconhecida para RemoveAccessible" @@ -38,19 +36,11 @@ msgstr "Tentada uma chamada síncrona onde é proibida" #~ msgid "AT-SPI: Error in GetItems, sender=%s, error=%s" #~ msgstr "AT-SPI: Erro em GetItems, invocador=%s, erro=%s" -#~ msgid "" -#~ "AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange " -#~ "signature %s" -#~ msgstr "" -#~ "AT-SPI: Chamada de _atspi_dbus_return_accessible_from_message com " -#~ "assinatura incomun %s" +#~ msgid "AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange signature %s" +#~ msgstr "AT-SPI: Chamada de _atspi_dbus_return_accessible_from_message com assinatura incomun %s" -#~ msgid "" -#~ "AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange " -#~ "signature %s" -#~ msgstr "" -#~ "AT-SPI: Chamada de _atspi_dbus_return_hyperlink_from_message com " -#~ "assinatura incomun %s" +#~ msgid "AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange signature %s" +#~ msgstr "AT-SPI: Chamada de _atspi_dbus_return_hyperlink_from_message com assinatura incomun %s" #~ msgid "AT-SPI: AddAccessible with unknown signature %s\n" #~ msgstr "AT-SPI: AddAccessible com assinatura %s desconhecida\n" @@ -59,9 +49,7 @@ msgstr "Tentada uma chamada síncrona onde é proibida" #~ msgstr "AT-SPI: Incapaz de obter o ecrã\n" #~ msgid "AT-SPI: Accessibility bus not found - Using session bus.\n" -#~ msgstr "" -#~ "AT-SPI: Incapaz de encontrar o canal de acessibilidade - A utilizar o " -#~ "canal de sessão.\n" +#~ msgstr "AT-SPI: Incapaz de encontrar o canal de acessibilidade - A utilizar o canal de sessão.\n" #~ msgid "AT-SPI: Couldn't connect to bus: %s\n" #~ msgstr "AT-SPI: Incapaz de se ligar ao canal: %s\n" @@ -69,10 +57,8 @@ msgstr "Tentada uma chamada síncrona onde é proibida" #~ msgid "AT-SPI: Couldn't register with bus: %s\n" #~ msgstr "AT-SPI: Incapaz de se registar no canal: %s\n" -#~ msgid "" -#~ "AT-SPI: expected a variant when fetching %s from interface %s; got %s\n" -#~ msgstr "" -#~ "AT-SPI: esperada uma variante ao obter %s do interface %s; obtida %s\n" +#~ msgid "AT-SPI: expected a variant when fetching %s from interface %s; got %s\n" +#~ msgstr "AT-SPI: esperada uma variante ao obter %s do interface %s; obtida %s\n" #~ msgid "atspi_dbus_get_property: Wrong type: expected %s, got %c\n" #~ msgstr "atspi_dbus_get_property: Tipo incorrecto: esperado %s, obtido %c\n" @@ -86,11 +72,8 @@ msgstr "Tentada uma chamada síncrona onde é proibida" #~ msgid "Streamable content not implemented" #~ msgstr "Conteúdo em fluxo (stream) não está implementado" -#~ msgid "" -#~ "called atspi_event_listener_register_from_callback with a NULL event_type" -#~ msgstr "" -#~ "invocação de atspi_event_listener_register_from_callback com event_type " -#~ "NULL" +#~ msgid "called atspi_event_listener_register_from_callback with a NULL event_type" +#~ msgstr "invocação de atspi_event_listener_register_from_callback com event_type NULL" #~ msgid "Got invalid signature %s for signal %s from interface %s\n" #~ msgstr "Obtida assinatura %s inválida para o sinal %s do interface %s\n" diff --git a/po/uk.po b/po/uk.po index 2da7bed..be0a057 100644 --- a/po/uk.po +++ b/po/uk.po @@ -5,112 +5,89 @@ msgid "" msgstr "" "Project-Id-Version: 1.1\n" -"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=at-" -"spi&component=general\n" -"POT-Creation-Date: 2011-02-04 22:21+0000\n" -"PO-Revision-Date: 2011-02-07 17:41+0300\n" -"Last-Translator: Korostil Daniel \n" +"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/at-spi2-core/issues\n" +"POT-Creation-Date: 2018-06-10 15:50+0000\n" +"PO-Revision-Date: 2019-10-18 18:08+0300\n" +"Last-Translator: vikaig \n" "Language-Team: translation@linux.org.ua\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Virtaal 0.6.1\n" - -#: ../atspi/atspi-misc.c:290 -#, c-format -msgid "AT-SPI: Unknown signature %s for RemoveAccessible" -msgstr "AT-SPI: невідомий підпис %s для RemoveAccessible" - -#: ../atspi/atspi-misc.c:327 -#, c-format -msgid "AT-SPI: Error calling getRoot for %s: %s" -msgstr "AT-SPI: помилка виклику getRoot для %s: %s" - -#: ../atspi/atspi-misc.c:485 -#, c-format -msgid "AT-SPI: Error in GetItems, sender=%s, error=%s" -msgstr "AT-SPI: помилка в GetItems, sender=%s, error=%s" - -#: ../atspi/atspi-misc.c:587 -#, c-format -msgid "" -"AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange " -"signature %s" -msgstr "" -"AT-SPI: викликано _atspi_dbus_return_accessible_from_message з дивним " -"підписом %s" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Poedit 2.2.1\n" -#: ../atspi/atspi-misc.c:616 -#, c-format -msgid "" -"AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange " -"signature %s" -msgstr "" -"AT-SPI: викликано _atspi_dbus_return_hyperlink_from_message з дивним " -"підписом %s" - -#: ../atspi/atspi-misc.c:641 -#, c-format -msgid "AT-SPI: AddAccessible with unknown signature %s\n" -msgstr "AT-SPI: AddAccessible з невідомим підписом %s\n" - -#: ../atspi/atspi-misc.c:826 -msgid "AT-SPI: Could not get the display\n" -msgstr "AT-SPI: неможливо перейти до показу\n" - -#: ../atspi/atspi-misc.c:844 -msgid "AT-SPI: Accessibility bus not found - Using session bus.\n" -msgstr "AT-SPI: не знайдено шини доступності — використовуйте шину сеансу.\n" - -#: ../atspi/atspi-misc.c:848 ../atspi/atspi-misc.c:857 -#, c-format -msgid "AT-SPI: Couldn't connect to bus: %s\n" -msgstr "AT-SPI: неможливо з'єднатись із шиною: %s\n" - -#: ../atspi/atspi-misc.c:864 -#, c-format -msgid "AT-SPI: Couldn't register with bus: %s\n" -msgstr "AT-SPI: неможливо зареєструватись із шиною: %s\n" - -#: ../atspi/atspi-misc.c:1002 ../atspi/atspi-misc.c:1053 -#: ../atspi/atspi-misc.c:1094 +#: atspi/atspi-component.c:326 atspi/atspi-misc.c:1073 atspi/atspi-value.c:111 msgid "The application no longer exists" msgstr "Програми більше не існує" -#: ../atspi/atspi-misc.c:1130 -#, c-format -msgid "AT-SPI: expected a variant when fetching %s from interface %s; got %s\n" -msgstr "AT-SPI: очікуваний варіант, коли звантажуємо %s з інтерфейсу %s; отримаємо %" -"s\n" +#: atspi/atspi-misc.c:1850 +msgid "Attempted synchronous call where prohibited" +msgstr "Спроба синхронного виклику там, де це заборонено" -#: ../atspi/atspi-misc.c:1136 -#, c-format -msgid "atspi_dbus_get_property: Wrong type: expected %s, got %c\n" -msgstr "atspi_dbus_get_property: неправильний тип: очікували %s, отримали %c\n" +#~ msgid "AT-SPI: Unknown signature %s for RemoveAccessible" +#~ msgstr "AT-SPI: невідомий підпис %s для RemoveAccessible" -#: ../atspi/atspi-misc.c:1279 -#, c-format -msgid "AT-SPI: Unknown interface %s" -msgstr "AT-SPI: невідомий інтерфейс %s" +#~ msgid "AT-SPI: Error calling getRoot for %s: %s" +#~ msgstr "AT-SPI: помилка виклику getRoot для %s: %s" -#: ../atspi/atspi-misc.c:1299 -#, c-format -msgid "AT-SPI: expected 2 values in states array; got %d\n" -msgstr "AT-SPI: очікувалось 2 значення в масиві станів; отримали %d\n" +#~ msgid "AT-SPI: Error in GetItems, sender=%s, error=%s" +#~ msgstr "AT-SPI: помилка в GetItems, sender=%s, error=%s" -#: ../atspi/atspi-accessible.c:997 -msgid "Streamable content not implemented" -msgstr "Потоковий вміст не реалізовано" +#~ msgid "" +#~ "AT-SPI: Called _atspi_dbus_return_accessible_from_message with strange " +#~ "signature %s" +#~ msgstr "" +#~ "AT-SPI: викликано _atspi_dbus_return_accessible_from_message з дивним " +#~ "підписом %s" -#: ../atspi/atspi-event-listener.c:510 -msgid "" -"called atspi_event_listener_register_from_callback with a NULL event_type" -msgstr "викликано atspi_event_listener_register_from_callback з NULL event_type" +#~ msgid "" +#~ "AT-SPI: Called _atspi_dbus_return_hyperlink_from_message with strange " +#~ "signature %s" +#~ msgstr "" +#~ "AT-SPI: викликано _atspi_dbus_return_hyperlink_from_message з дивним " +#~ "підписом %s" + +#~ msgid "AT-SPI: AddAccessible with unknown signature %s\n" +#~ msgstr "AT-SPI: AddAccessible з невідомим підписом %s\n" + +#~ msgid "AT-SPI: Could not get the display\n" +#~ msgstr "AT-SPI: неможливо перейти до показу\n" + +#~ msgid "AT-SPI: Accessibility bus not found - Using session bus.\n" +#~ msgstr "" +#~ "AT-SPI: не знайдено шини доступності — використовуйте шину сеансу.\n" + +#~ msgid "AT-SPI: Couldn't connect to bus: %s\n" +#~ msgstr "AT-SPI: неможливо з'єднатись із шиною: %s\n" + +#~ msgid "AT-SPI: Couldn't register with bus: %s\n" +#~ msgstr "AT-SPI: неможливо зареєструватись із шиною: %s\n" + +#~ msgid "" +#~ "AT-SPI: expected a variant when fetching %s from interface %s; got %s\n" +#~ msgstr "" +#~ "AT-SPI: очікуваний варіант, коли звантажуємо %s з інтерфейсу %s; " +#~ "отримаємо %s\n" + +#~ msgid "atspi_dbus_get_property: Wrong type: expected %s, got %c\n" +#~ msgstr "" +#~ "atspi_dbus_get_property: неправильний тип: очікували %s, отримали %c\n" + +#~ msgid "AT-SPI: Unknown interface %s" +#~ msgstr "AT-SPI: невідомий інтерфейс %s" + +#~ msgid "AT-SPI: expected 2 values in states array; got %d\n" +#~ msgstr "AT-SPI: очікувалось 2 значення в масиві станів; отримали %d\n" + +#~ msgid "Streamable content not implemented" +#~ msgstr "Потоковий вміст не реалізовано" + +#~ msgid "" +#~ "called atspi_event_listener_register_from_callback with a NULL event_type" +#~ msgstr "" +#~ "викликано atspi_event_listener_register_from_callback з NULL event_type" -#: ../atspi/atspi-event-listener.c:777 -#, c-format -msgid "Got invalid signature %s for signal %s from interface %s\n" -msgstr "Отримано неправильний підпис %s для сигналу %s з інтерфейсу %s\n" +#~ msgid "Got invalid signature %s for signal %s from interface %s\n" +#~ msgstr "Отримано неправильний підпис %s для сигналу %s з інтерфейсу %s\n" diff --git a/registryd/meson.build b/registryd/meson.build index c2404d2..739509f 100644 --- a/registryd/meson.build +++ b/registryd/meson.build @@ -41,5 +41,4 @@ libexec_conf.set('libexecdir', atspi_libexecdir) configure_file(input: 'org.a11y.atspi.Registry.service.in', output: 'org.a11y.atspi.Registry.service', configuration: libexec_conf, - install: true, install_dir: join_paths(atspi_datadir, 'dbus-1/accessibility-services')) diff --git a/registryd/ucs2keysym.c b/registryd/ucs2keysym.c index b4967be..29a92f3 100644 --- a/registryd/ucs2keysym.c +++ b/registryd/ucs2keysym.c @@ -34,128 +34,132 @@ #include #include "deviceeventcontroller.h" /* for prototype */ +/* DO NOT UPATE BY HAND! + * This table can be regenerated from Xorg's keysymdef.h with the ucs2keysym.sh + * script. */ struct codepair { unsigned short keysym; unsigned short ucs; } keysymtab[] = { + { 0x0abd, 0x002e }, /* decimalpoint . FULL STOP */ { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ + { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ + { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ + { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ + { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ - { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ - { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ - { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ - { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ - { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ - { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ - { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ - { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ - { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ - { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ - { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ - { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ - { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ - { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ - { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ - { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ - { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ - { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ - { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ - { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ - { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ + { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ + { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ + { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ + { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ + { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ - { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ - { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ + { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ + { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ - { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ - { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ - { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ - { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ - { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ - { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ - { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ - { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ - { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ - { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ - { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ - { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ + { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ + { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ + { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ + { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ + { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ + { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ + { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ - { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ - { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ - { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ - { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ - { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ - { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ - { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ - { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ - { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ - { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ - { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ - { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ + { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ + { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ - { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ - { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ - { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ - { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ - { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ - { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ - { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ - { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ - { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ - { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ - { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ - { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ - { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ - { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ - { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ - { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ - { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ - { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ - { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ - { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ - { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ - { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ + { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ + { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ + { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ + { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ + { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ + { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ + { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ + { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ + { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ + { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ + { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ + { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ + { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ + { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ + { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ + { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ + { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ + { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ + { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ + { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ - { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ - { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ - { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ + { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ + { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ + { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ + { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ + { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ + { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ + { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ + { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ + { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ + { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ - { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ - { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ - { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ - { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ - { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ - { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ - { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ - { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ - { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ - { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ - { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ - { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ - { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ - { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ - { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ - { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ - { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ - { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ - { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ + { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ + { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ + { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ + { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ + { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ + { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ + { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ + { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ + { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ + { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ + { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ + { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ + { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ + { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ + { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ + { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ + { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ + { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ + { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ + { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ + { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ + { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ + { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ + { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ + { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ + { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ + { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ + { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ + { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ + { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ + { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ + { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ + { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ + { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ + { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ + { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ + { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ + { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ + { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ + { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ + { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ @@ -196,7 +200,7 @@ struct codepair { { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ - { 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ + { 0x07a5, 0x03aa }, /* Greek_IOTAdieresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ @@ -326,6 +330,8 @@ struct codepair { { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ + { 0x06bd, 0x0490 }, /* Ukrainian_GHE_WITH_UPTURN Ґ CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ + { 0x06ad, 0x0491 }, /* Ukrainian_ghe_with_upturn ґ CYRILLIC SMALL LETTER GHE WITH UPTURN */ { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ @@ -519,7 +525,6 @@ struct codepair { { 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */ { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ - { 0x0aa2, 0x2002 }, /* enspace   EN SPACE */ { 0x0aa1, 0x2003 }, /* emspace   EM SPACE */ { 0x0aa3, 0x2004 }, /* em3space   THREE-PER-EM SPACE */ @@ -544,12 +549,13 @@ struct codepair { { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ { 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */ { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ + { 0x0ad5, 0x2030 }, /* permille ‰ PER MILLE SIGN */ { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ { 0x0afc, 0x2038 }, /* caret ‸ CARET */ { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ - { 0x13a4, 0x20ac }, /* Euro € EURO SIGN */ + { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ @@ -574,15 +580,20 @@ struct codepair { { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ + { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ + { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ + { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ + { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ + { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ { 0x08dd, 0x222a }, /* union ∪ UNION */ { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ @@ -592,44 +603,51 @@ struct codepair { { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ + { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ + { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ - { 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */ - { 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */ - { 0x0bce, 0x22a4 }, /* uptack ⊤ DOWN TACK */ - { 0x0bc2, 0x22a5 }, /* downtack ⊥ UP TACK */ + { 0x0bfc, 0x22a2 }, /* righttack ⊢ RIGHT TACK */ + { 0x0bdc, 0x22a3 }, /* lefttack ⊣ LEFT TACK */ + { 0x0bc2, 0x22a4 }, /* downtack ⊤ DOWN TACK */ + { 0x0bce, 0x22a5 }, /* uptack ⊥ UP TACK */ + { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ - { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */ - { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */ + { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET, not U+27E8 per xterm source */ + { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET, not U+27E9 per xterm source */ { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD */ - { 0x08ab, 0x239b }, /* topleftparens ⎛ ??? */ - { 0x08ac, 0x239d }, /* botleftparens ⎝ ??? */ - { 0x08ad, 0x239e }, /* toprightparens ⎞ ??? */ - { 0x08ae, 0x23a0 }, /* botrightparens ⎠ ??? */ - { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ ??? */ - { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ ??? */ - { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ ??? */ - { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ ??? */ - { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ ??? */ - { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ ??? */ - { 0x08a1, 0x23b7 }, /* leftradical ⎷ ??? */ - { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ - { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ - { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ - { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ + { 0x08ab, 0x239b }, /* topleftparens ⎛ LEFT PARENTHESIS UPPER HOOK */ + { 0x08ac, 0x239d }, /* botleftparens ⎝ LEFT PARENTHESIS LOWER HOOK */ + { 0x08ad, 0x239e }, /* toprightparens ⎞ RIGHT PARENTHESIS UPPER HOOK */ + { 0x08ae, 0x23a0 }, /* botrightparens ⎠ RIGHT PARENTHESIS LOWER HOOK */ + { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ LEFT SQUARE BRACKET UPPER CORNER */ + { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ LEFT SQUARE BRACKET LOWER CORNER */ + { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ RIGHT SQUARE BRACKET UPPER CORNER */ + { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ RIGHT SQUARE BRACKET LOWER CORNER */ + { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ LEFT CURLY BRACKET MIDDLE PIECE */ + { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ RIGHT CURLY BRACKET MIDDLE PIECE */ + { 0x08a1, 0x23b7 }, /* leftradical ⎷ RADICAL SYMBOL BOTTOM */ + { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 */ + { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 */ + { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 */ + { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 */ + { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ + { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ + { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ - { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ + { 0x0aac, 0x2423 }, /* signifblank ␣ OPEN BOX */ { 0x09e8, 0x2424 }, /* nl ␤ SYMBOL FOR NEWLINE */ - { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ { 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ { 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ @@ -638,8 +656,8 @@ struct codepair { { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ - { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ + { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ @@ -657,16 +675,29 @@ struct codepair { { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ + { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ + + { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ + { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ + { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ + { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ + { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ + { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ + { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ + { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ - { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ + { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ + { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ + { 0x0abc, 0x27e8 }, /* leftanglebracket ⟨ MATHEMATICAL LEFT ANGLE BRACKET */ + { 0x0abe, 0x27e9 }, /* rightanglebracket ⟩ MATHEMATICAL RIGHT ANGLE BRACKET */ { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ @@ -824,9 +855,7 @@ long ucs2keysym (long ucs) long keysym2ucs(long keysym) { - int min = 0; - int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; - int mid; + int i; /* first check for Latin-1 characters (1:1 mapping) */ if ((keysym >= 0x0020 && keysym <= 0x007e) || @@ -837,16 +866,10 @@ long keysym2ucs(long keysym) if ((keysym & 0xff000000) == 0x01000000) return keysym & 0x00ffffff; - /* binary search in table */ - while (max >= min) { - mid = (min + max) / 2; - if (keysymtab[mid].keysym < keysym) - min = mid + 1; - else if (keysymtab[mid].keysym > keysym) - max = mid - 1; - else { + for (i = 0; i < sizeof(keysymtab) / sizeof(keysymtab[0]); i++) { + if (keysymtab[i].keysym == keysym) { /* found it */ - return keysymtab[mid].ucs; + return keysymtab[i].ucs; } } diff --git a/registryd/ucs2keysym.sh b/registryd/ucs2keysym.sh new file mode 100755 index 0000000..ff37f53 --- /dev/null +++ b/registryd/ucs2keysym.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +if [ "$#" = 0 ] +then + echo "Usage: $0 /path/to/keysymdef.h" + exit 1 +fi + +# We are only interested in +# - keysyms which have well-defined unicode equivalent +# - and are not just trivial unicode keysyms +# - non-latin1 keysyms +# - not the lamda aliases +# and we tinker with the alias parentheses to make sorting easier + +grep '^#define' "$1" | \ + grep -i "U+" | \ + grep -vi "0x100[0-9a-f][0-9a-f][0-9a-f][0-9a-f]" | \ + grep -vi " 0x00[0-9a-f][0-9a-f] " | \ + grep -vi "_lamda " | \ + sed -e 's/\/\*(/& /' | \ + sed -e 's/)\*\// &/' | \ + sort -k 5 | \ + perl -CS -e ' +my $last = 0; +while (<>) { + chomp; + if ( /^\#define XK_([a-zA-Z_0-9]+)(\s*) 0x([0-9a-f]+)\s*\/\*(\(?) U\+([0-9A-F]{4,6}) (.*) \)?\*\/\s*$/ ) { + my ( $xk, $space, $keysym, $paren, $unicode, $unistr ) = ( $1, $2, $3, $4, $5, $6); + $unicode = hex("0x".$unicode); + + print "\n" if (int($unicode / 256) != int($last / 256)); + $last = $unicode; + + printf " { 0x$keysym, 0x%04x }, /* $space$xk %lc $unistr */\n", $unicode, $unicode; + } +} + ' diff --git a/test/memory.c b/test/memory.c index df5e0a1..2f53dd7 100644 --- a/test/memory.c +++ b/test/memory.c @@ -1,4 +1,5 @@ #include "atspi/atspi.h" +#include #include #include #include @@ -15,19 +16,24 @@ basic (AtspiAccessible *obj) AtspiAccessible *accessible; GError *error = NULL; + printf ("getting name\n"); str = atspi_accessible_get_name (obj, &error); if (str) g_free (str); + printf ("ok, getting parent\n"); accessible = atspi_accessible_get_parent (obj, NULL); if (accessible) g_object_unref (accessible); + printf ("ok, getting children\n"); count = atspi_accessible_get_child_count (obj, &error); for (i = 0; i < count; i++) { accessible = atspi_accessible_get_child_at_index (obj, i, &error); + printf ("ok %d\n", i); if (accessible) g_object_unref (accessible); } + printf ("ok\n"); } static gboolean @@ -78,7 +84,7 @@ main() atspi_event_listener_register (listener, "object:children-changed", NULL); child_pid = fork (); if (!child_pid) - execlp ("gedit", "gedit", NULL); + execlp ("test/test-application", "test/test-application", NULL); atspi_event_main (); return 0; } diff --git a/test/meson.build b/test/meson.build index 1fb35a8..cd3db66 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,4 +1,13 @@ -test('memory', - executable('memory', 'memory.c', - include_directories: root_inc, - dependencies: [ atspi_dep ])) +testapp = executable('test-application', + 'test-application.c', + include_directories: root_inc, + dependencies: [ atspi_dep ], + ) + +memory = executable('memory', + 'memory.c', + include_directories: root_inc, + dependencies: [ atspi_dep ], + ) + +test('memory', memory, depends: testapp) diff --git a/test/test-application.c b/test/test-application.c new file mode 100644 index 0000000..621d1de --- /dev/null +++ b/test/test-application.c @@ -0,0 +1,105 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; https://wiki.gnome.org/Accessibility) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* + * Simple test application for AT-SPI. + * + * The only thing this application does, is registering itself to the AT-SPI + * registry and then waiting to get killed by some external force. + */ + +#include +#include +#include +#include + +static GMainLoop *mainloop; + +int +register_app () +{ + DBusConnection *connection = NULL; + DBusMessage *message; + DBusMessageIter iter; + DBusMessageIter subiter; + DBusError error; + DBusMessage *reply; + const gchar *name; + gchar *path; + + + /* Set up D-Bus connection and register bus name */ + dbus_error_init (&error); + connection = atspi_get_a11y_bus (); + if (!connection) + { + printf("Couldn't get a11y bus!\n"); + return -1; + } + + /* Register this app by sending a signal out to AT-SPI registry daemon */ + message = dbus_message_new_method_call (ATSPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_ROOT, + ATSPI_DBUS_INTERFACE_SOCKET, + "Embed"); + + dbus_message_iter_init_append (message, &iter); + + name = dbus_bus_get_unique_name (connection); + path = g_strdup (ATSPI_DBUS_PATH_NULL); + + dbus_message_iter_open_container (&iter, DBUS_TYPE_STRUCT, NULL, + &subiter); + dbus_message_iter_append_basic (&subiter, DBUS_TYPE_STRING, &name); + dbus_message_iter_append_basic (&subiter, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_close_container (&iter, &subiter); + + g_free (path); + + reply = dbus_connection_send_with_reply_and_block(connection, message, -1, &error); + if (!reply) + { + printf("Did not get a reply from the registry.\n"); + dbus_message_unref (message); + dbus_error_free (&error); + return -1; + } + + dbus_message_unref (message); + dbus_message_unref (reply); + dbus_error_free (&error); + return 0; +} + +int main (int argc, char *argv[]) +{ + int ret = register_app (); + if (ret) { + printf("Failed to send dbus signals. Aborting.\n"); + return ret; + } + + // This keeps the test-application runnig indefinitely, i.e. + // until killed by an external signal. + mainloop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (mainloop); + + return 0; +} diff --git a/xml/Accessible.xml b/xml/Accessible.xml index 7015466..b081bfd 100644 --- a/xml/Accessible.xml +++ b/xml/Accessible.xml @@ -7,7 +7,7 @@ - + @@ -19,12 +19,12 @@ - + - + @@ -33,7 +33,7 @@ - + @@ -50,17 +50,21 @@ - + - + - + + + + + diff --git a/xml/Action.xml b/xml/Action.xml index 0f5aa85..1626700 100644 --- a/xml/Action.xml +++ b/xml/Action.xml @@ -26,7 +26,7 @@ - + diff --git a/xml/Application.xml b/xml/Application.xml index 04a2e70..2191f08 100644 --- a/xml/Application.xml +++ b/xml/Application.xml @@ -7,7 +7,7 @@ - + diff --git a/xml/Cache.xml b/xml/Cache.xml index e693b54..ce06ba4 100644 --- a/xml/Cache.xml +++ b/xml/Cache.xml @@ -3,18 +3,18 @@ - - + + - - + + - - + + diff --git a/xml/Collection.xml b/xml/Collection.xml index 7b39776..a6dff74 100644 --- a/xml/Collection.xml +++ b/xml/Collection.xml @@ -4,44 +4,44 @@ - + - + - + - + - + - + - + - + - + diff --git a/xml/Component.xml b/xml/Component.xml index c1258d2..94056ff 100644 --- a/xml/Component.xml +++ b/xml/Component.xml @@ -14,13 +14,13 @@ - + - + diff --git a/xml/DeviceEventController.xml b/xml/DeviceEventController.xml index 0af9aac..460e79a 100644 --- a/xml/DeviceEventController.xml +++ b/xml/DeviceEventController.xml @@ -4,24 +4,20 @@ - - - + + - - - - - - + + + + - - - + + @@ -52,12 +48,12 @@ - + - + diff --git a/xml/DeviceEventListener.xml b/xml/DeviceEventListener.xml index a6dd3b6..2c72973 100644 --- a/xml/DeviceEventListener.xml +++ b/xml/DeviceEventListener.xml @@ -4,7 +4,7 @@ - + diff --git a/xml/Document.xml b/xml/Document.xml index d12a306..a82eb98 100644 --- a/xml/Document.xml +++ b/xml/Document.xml @@ -17,7 +17,7 @@ - + diff --git a/xml/Event.xml b/xml/Event.xml index fac67de..3614358 100644 --- a/xml/Event.xml +++ b/xml/Event.xml @@ -3,190 +3,190 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/xml/Hyperlink.xml b/xml/Hyperlink.xml index 75f8099..80d5777 100644 --- a/xml/Hyperlink.xml +++ b/xml/Hyperlink.xml @@ -11,7 +11,7 @@ - + diff --git a/xml/Hypertext.xml b/xml/Hypertext.xml index 18a3b34..79b5277 100644 --- a/xml/Hypertext.xml +++ b/xml/Hypertext.xml @@ -9,7 +9,7 @@ - + diff --git a/xml/Image.xml b/xml/Image.xml index 43536ee..1dd72f1 100644 --- a/xml/Image.xml +++ b/xml/Image.xml @@ -9,7 +9,7 @@ - + diff --git a/xml/Registry.xml b/xml/Registry.xml index a3ab93c..7603328 100644 --- a/xml/Registry.xml +++ b/xml/Registry.xml @@ -13,8 +13,8 @@ - - + + diff --git a/xml/Selection.xml b/xml/Selection.xml index 2e19187..7b0ad21 100644 --- a/xml/Selection.xml +++ b/xml/Selection.xml @@ -7,7 +7,7 @@ - + diff --git a/xml/Socket.xml b/xml/Socket.xml index 5f9367e..f9ac76d 100644 --- a/xml/Socket.xml +++ b/xml/Socket.xml @@ -3,25 +3,21 @@ - - - - - - + + + + - - - + + - - - - + + + diff --git a/xml/Table.xml b/xml/Table.xml index 181acaa..3b35989 100644 --- a/xml/Table.xml +++ b/xml/Table.xml @@ -7,11 +7,11 @@ - + - + @@ -22,7 +22,7 @@ - + @@ -66,23 +66,23 @@ - + - + - + - + diff --git a/xml/TableCell.xml b/xml/TableCell.xml index c60a074..dbdbe8c 100644 --- a/xml/TableCell.xml +++ b/xml/TableCell.xml @@ -4,11 +4,15 @@ - + + + - + + + diff --git a/xml/Text.xml b/xml/Text.xml index 313131e..fe9f44b 100644 --- a/xml/Text.xml +++ b/xml/Text.xml @@ -65,12 +65,12 @@ - + - + @@ -136,7 +136,7 @@ - + @@ -145,11 +145,12 @@ - + + -- 2.7.4 From a0687b8680a7bd816094590f9fedf34dff2d3d3d Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Thu, 4 Mar 2021 12:26:54 +0100 Subject: [PATCH 14/16] Version 2.39.90.1 Change-Id: I862adfd66284175a3696f90a7c669845735dd238 --- packaging/at-spi2-core.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/at-spi2-core.spec b/packaging/at-spi2-core.spec index bc859b6..939eb56 100644 --- a/packaging/at-spi2-core.spec +++ b/packaging/at-spi2-core.spec @@ -1,8 +1,8 @@ %bcond_with x Name: at-spi2-core -Version: 2.34.0 -Release: 0 +Version: 2.39.90 +Release: 1 Summary: Assistive Technology Service Provider Interface - D-Bus based implementation License: LGPL-2.1+ Group: System/Libraries -- 2.7.4 From 4043c5aff8bafc955b000aaaaa327a8634ff68bf Mon Sep 17 00:00:00 2001 From: Mike Gorse Date: Mon, 22 Feb 2021 15:53:35 -0600 Subject: [PATCH 15/16] atspi_accessible_get_(name|description): fix memory leak This is likely what the backed-out part of !53 was trying to do. Change-Id: Id6f8bc3eb34caaa8df8a8734a60ce24d7f64aa9f --- atspi/atspi-accessible.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index 958e578..eee40e9 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -340,8 +340,12 @@ gchar * atspi_accessible_get_name (AtspiAccessible *obj, GError **error) { g_return_val_if_fail (obj != NULL, g_strdup ("")); + if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_NAME)) { +if (obj->name) printf("free\n"); + g_free (obj->name); + obj->name = NULL; if (!_atspi_dbus_get_property (obj, atspi_interface_accessible, "Name", error, "s", &obj->name)) return g_strdup (""); @@ -886,6 +890,8 @@ atspi_accessible_get_description (AtspiAccessible *obj, GError **error) if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_DESCRIPTION)) { + g_free (obj->description); + obj->description = NULL; if (!_atspi_dbus_get_property (obj, atspi_interface_accessible, "Description", error, "s", &obj->description)) -- 2.7.4 From 679efa8e2bd9455fb6242c4d2dabfdca2a0612ec Mon Sep 17 00:00:00 2001 From: Mike Gorse Date: Mon, 22 Feb 2021 15:55:11 -0600 Subject: [PATCH 16/16] Remove debug print Change-Id: I1f71997aaf68f2eab803827971ffc4f2d7332657 --- atspi/atspi-accessible.c | 1 - 1 file changed, 1 deletion(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index eee40e9..dddb8fe 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -343,7 +343,6 @@ atspi_accessible_get_name (AtspiAccessible *obj, GError **error) if (!_atspi_accessible_test_cache (obj, ATSPI_CACHE_NAME)) { -if (obj->name) printf("free\n"); g_free (obj->name); obj->name = NULL; if (!_atspi_dbus_get_property (obj, atspi_interface_accessible, "Name", error, -- 2.7.4