From 9a01117b5d428dbe9a09b85ed8d29263ab39daf1 Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Fri, 6 Aug 2021 19:29:28 +0900 Subject: [PATCH 01/16] test: remove warning message This change is removing following message. GLib-CRITICAL: g_atomic_ref_count_dec: assertion 'g_atomic_int_get (arc) > 0' failed It seems that the variant is removed in g_dbus_proxy_call_sync. Change-Id: Ibf1c5990f32a7290aa40fbbdc413c29bb125f11e --- test/at_spi2_tool.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/at_spi2_tool.c b/test/at_spi2_tool.c index e70cd82..0eaa284 100644 --- a/test/at_spi2_tool.c +++ b/test/at_spi2_tool.c @@ -741,7 +741,6 @@ static void _at_spi_client_enable(gboolean enabled) { static GDBusProxy *proxy = NULL; //we keep proxy (dbus connection) until program exits GVariant *result; - GVariant *enabled_variant; GError *error = NULL; GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_NONE; @@ -762,16 +761,13 @@ static void _at_spi_client_enable(gboolean enabled) } } - enabled_variant = g_variant_new_boolean(enabled); result = g_dbus_proxy_call_sync(proxy, "Set", - g_variant_new ("(ssv)", "org.a11y.Status", "IsEnabled", enabled_variant), + g_variant_new ("(ssv)", "org.a11y.Status", "IsEnabled", g_variant_new_boolean(enabled)), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); - if (enabled_variant) - g_variant_unref(enabled_variant); if (result) g_variant_unref(result); -- 2.7.4 From ca3ab5aea45e50202be4547f090d737e72fe6f6b Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Wed, 1 Dec 2021 17:36:19 +0900 Subject: [PATCH 02/16] [Tizen] Provide GetActiveWindow interface If there are two AT clients, AT server does not send 'activate' signal for the second AT client. Because AT server send the 'activate' signal when the 'IsEnabled' status was set to be TRUE. AT client is able to search the 'activate' window using the desktop and its children - applications, but some times it is very slow. Probably.. It is caused by 'throttle' feature of EFL application. We need to keep the 'activate' window information for the second client. The at-spi2-registryd will keep the 'activate' window information. The AT client gets the information using atspi_get_activate_window. Change-Id: Ia5f22465b10a46411768866f245aa8309b2cc013 --- atspi/atspi-registry.c | 47 ++++++++++++++++++ atspi/atspi-registry.h | 4 ++ registryd/registry-main.c | 13 +++++ registryd/registry.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++ registryd/registry.h | 14 ++++++ 5 files changed, 202 insertions(+) diff --git a/atspi/atspi-registry.c b/atspi/atspi-registry.c index 2bb4d08..e9a47e5 100644 --- a/atspi/atspi-registry.c +++ b/atspi/atspi-registry.c @@ -98,6 +98,53 @@ atspi_get_desktop_list () return array; } + +//TIZEN_ONLY(20211206) Provide GetActiveWindow +/** + * atspi_get_active_window: + * + * Gets recently activated window. + * NOTE: the Tizen only interface for multiple AT-clients + * + * Returns: (transfer full): a pointer to active window's + * #AtspiAccessible representation. + **/ +AtspiAccessible* +atspi_get_active_window () +{ + DBusMessage *message, *reply; + DBusMessageIter iter; + AtspiAccessible* active_window; + + message = dbus_message_new_method_call (ATSPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_ROOT, + ATSPI_DBUS_INTERFACE_SOCKET, + "GetActiveWindow"); + if (!message) + return NULL; + + reply = _atspi_dbus_send_with_reply_and_block (message, NULL); + if (!reply) + return NULL; + + if (strcmp (dbus_message_get_signature (reply), "(so)") != 0) + { + dbus_message_unref (reply); + return NULL; + } + + dbus_message_iter_init (reply, &iter); + active_window = _atspi_dbus_return_accessible_from_iter (&iter); + + dbus_message_unref (reply); + + if (!active_window) + return NULL; + + return g_object_ref (active_window); +} +// + static gboolean notify_keystroke_listener (DeviceListenerEntry *e) { diff --git a/atspi/atspi-registry.h b/atspi/atspi-registry.h index 43150c2..b266c7b 100644 --- a/atspi/atspi-registry.h +++ b/atspi/atspi-registry.h @@ -39,6 +39,10 @@ AtspiAccessible* atspi_get_desktop (gint i); GArray *atspi_get_desktop_list (); +//TIZEN_ONLY(20211206) Provide GetActiveWindow +AtspiAccessible* atspi_get_active_window (); +// + gboolean atspi_register_keystroke_listener (AtspiDeviceListener *listener, GArray *key_set, diff --git a/registryd/registry-main.c b/registryd/registry-main.c index be01d2c..c137182 100644 --- a/registryd/registry-main.c +++ b/registryd/registry-main.c @@ -259,6 +259,19 @@ main (int argc, char **argv) g_main_loop_run (mainloop); + //TIZEN_ONLY(20211206) Provide GetActiveWindow + if (registry->active_window_name) + { + g_free (registry->active_window_name); + registry->active_window_name = NULL; + } + if (registry->active_window_path) + { + g_free (registry->active_window_path); + registry->active_window_path = NULL; + } + // + dbus_connection_close (bus); dbus_connection_unref (bus); g_object_unref (dec); diff --git a/registryd/registry.c b/registryd/registry.c index aa867ca..fa2d3a5 100644 --- a/registryd/registry.c +++ b/registryd/registry.c @@ -303,6 +303,63 @@ handle_disconnection (DBusConnection *bus, DBusMessage *message, void *user_data } } +//TIZEN_ONLY(20211206) Provide GetActiveWindow +static void +handle_activate_window (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + SpiRegistry *reg = SPI_REGISTRY (user_data); + + const char *sender = dbus_message_get_sender (message); + const char *path = dbus_message_get_path (message); + + if (!sender || !path) + return; + + char *detail = NULL; + dbus_int32_t detail1; + DBusMessageIter iter; + dbus_message_iter_init (message, &iter); + dbus_message_iter_get_basic (&iter, &detail); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &detail1); + + if (detail1 & ACCESSIBLE_WINDOW_ACTIVATE_INFO_KEYBOARD) + return; + + if (reg->active_window_name) + g_free (reg->active_window_name); + if (reg->active_window_path) + g_free (reg->active_window_path); + + reg->active_window_name = g_strdup(sender); + reg->active_window_path = g_strdup(path); +} + +static void +handle_deactivate_window (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + SpiRegistry *reg = SPI_REGISTRY (user_data); + + if (!reg->active_window_name || !reg->active_window_path) + return; + + const char *sender = dbus_message_get_sender (message); + const char *path = dbus_message_get_path (message); + + if (!sender || !path) + return; + + if (!g_strcmp0(reg->active_window_name, sender) && + !g_strcmp0(reg->active_window_path, path)) + { + g_free (reg->active_window_name); + g_free (reg->active_window_path); + reg->active_window_name = NULL; + reg->active_window_path = NULL; + } +} +// + /* * Converts names of the form "active-descendant-changed" to *" ActiveDescendantChanged" @@ -352,6 +409,15 @@ signal_filter (DBusConnection *bus, DBusMessage *message, void *user_data) if (!g_strcmp0(iface, DBUS_INTERFACE_DBUS) && !g_strcmp0(member, "NameOwnerChanged")) handle_disconnection (bus, message, user_data); + //TIZEN_ONLY(20211206) Provide GetActiveWindow + else if (!g_strcmp0(iface, "org.a11y.atspi.Event.Window")) + { + if (!g_strcmp0(member, "Activate")) + handle_activate_window (bus, message, user_data); + else if (!g_strcmp0(member, "Deactivate")) + handle_deactivate_window (bus, message, user_data); + } + // else res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -831,6 +897,27 @@ impl_GetInterfaces (DBusConnection * bus, return reply; } +//TIZEN_ONLY(20211206) Provide GetActiveWindow +static DBusMessage * +impl_GetActiveWindow (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + SpiRegistry *reg = SPI_REGISTRY (user_data); + DBusMessage *reply; + DBusMessageIter iter; + + reply = dbus_message_new_method_return (message); + dbus_message_iter_init_append (reply, &iter); + + if (!reg->active_window_name || !reg->active_window_path) + append_reference (&iter, SPI_DBUS_NAME_REGISTRY, SPI_DBUS_PATH_NULL); + else + append_reference (&iter, reg->active_window_name, reg->active_window_path); + + return reply; +} +// + static DBusMessage * impl_GetItems (DBusConnection * bus, DBusMessage * message, void *user_data) { @@ -957,6 +1044,24 @@ impl_get_registered_events (DBusConnection *bus, DBusMessage *message, void *use dbus_message_iter_init_append (reply, &iter); dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ss)", &iter_array); + + //TIZEN_ONLY(20211206) Provide GetActiveWindow + //we need this part because EFL sends only registered event + const char *registry_bus_name = SPI_DBUS_NAME_REGISTRY; + const char *window_activate_event = "Window:Activate:"; + const char *window_deactivate_event = "Window:Deactivate:"; + + dbus_message_iter_open_container (&iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, ®istry_bus_name); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &window_activate_event); + dbus_message_iter_close_container (&iter_array, &iter_struct); + + dbus_message_iter_open_container (&iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, ®istry_bus_name); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &window_deactivate_event); + dbus_message_iter_close_container (&iter_array, &iter_struct); + // + for (list = registry->events; list; list = list->next) { gchar *str; @@ -1289,6 +1394,10 @@ handle_method_root (DBusConnection *bus, DBusMessage *message, void *user_data) reply = impl_Embed (bus, message, user_data); else if (!strcmp (member, "Unembed")) reply = impl_Unembed (bus, message, user_data); + //TIZEN_ONLY(20211206) Provide GetActiveWindow + else if (!strcmp (member, "GetActiveWindow")) + reply = impl_GetActiveWindow (bus, message, user_data); + // else result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1446,6 +1555,13 @@ static DBusObjectPathVTable cache_vtable = static gchar *app_sig_match_name_owner = "type='signal', interface='org.freedesktop.DBus', member='NameOwnerChanged'"; +//TIZEN_ONLY(20211206) Provide GetActivateWindow +static gchar *app_sig_match_window_activate = + "type='signal',interface='org.a11y.atspi.Event.Window',member='Activate'"; +static gchar *app_sig_match_window_deactivate = + "type='signal',interface='org.a11y.atspi.Event.Window',member='Deactivate'"; +// + SpiRegistry * spi_registry_new (DBusConnection *bus) { @@ -1456,6 +1572,13 @@ spi_registry_new (DBusConnection *bus) dbus_bus_add_match (bus, app_sig_match_name_owner, NULL); dbus_connection_add_filter (bus, signal_filter, reg, NULL); + //TIZEN_ONLY(20211206) Provide GetActiveWindow + reg->active_window_name = NULL; + reg->active_window_path = NULL; + dbus_bus_add_match (reg->bus, app_sig_match_window_activate, NULL); + dbus_bus_add_match (reg->bus, app_sig_match_window_deactivate, NULL); + // + dbus_connection_register_object_path (bus, SPI_DBUS_PATH_ROOT, &root_vtable, reg); dbus_connection_register_object_path (bus, SPI_DBUS_PATH_CACHE, &cache_vtable, reg); @@ -1468,5 +1591,6 @@ spi_registry_new (DBusConnection *bus) return reg; } +// /*END------------------------------------------------------------------------*/ diff --git a/registryd/registry.h b/registryd/registry.h index 19b1a8f..ee82ef5 100644 --- a/registryd/registry.h +++ b/registryd/registry.h @@ -50,8 +50,22 @@ struct _SpiRegistry { DBusConnection *bus; GList *events; + + //TIZEN_ONLY(20211206) Provide GetActiveWindow + gchar *active_window_name; + gchar *active_window_path; + // }; +//TIZEN_ONLY(20211206) Provide GetActiveWindow +typedef enum { + ACCESSIBLE_WINDOW_ACTIVATE_INFO_DEFAULT_LABEL_ENABLED = 0, + ACCESSIBLE_WINDOW_ACTIVATE_INFO_DEFAULT_LABEL_ENABLED_WITHOUT_WINDOW = 1 << 0, + ACCESSIBLE_WINDOW_ACTIVATE_INFO_DEFAULT_LABEL_DISABLED = 1 << 1, + ACCESSIBLE_WINDOW_ACTIVATE_INFO_KEYBOARD = 1 << 2, +} WindowActivateInfoType; +// + struct _SpiRegistryClass { GObjectClass parent_class; }; -- 2.7.4 From dcf4342815f8921cdf37db7d7e6c4b5fd05b74ea Mon Sep 17 00:00:00 2001 From: =?utf8?q?Artur=20=C5=9Awigo=C5=84?= Date: Tue, 1 Feb 2022 11:01:46 +0100 Subject: [PATCH 03/16] 2.42.0 Change-Id: Ie3cf2615d77cd923b22e9f6c9a7233fe8682f95a --- NEWS | 29 ++++++++++++++++++ README | 24 ++++++--------- atspi/atspi-accessible.c | 5 +++ atspi/atspi-device-legacy.c | 19 ++++++++++-- atspi/atspi-device-listener.h | 2 +- atspi/atspi-device-x11.c | 57 ++++++++++++++++++++++++++++++++--- atspi/atspi-device.c | 16 ++++++++-- atspi/atspi-event-listener.c | 5 ++- bus/00-at-spi | 10 ++++++ bus/accessibility.conf.in | 2 +- bus/meson.build | 14 +++++++++ meson.build | 2 +- po/LINGUAS | 1 + po/ab.po | 28 +++++++++++++++++ po/be.po | 32 +++++++++++--------- po/gl.po | 8 ++--- registryd/de-marshaller.c | 6 +++- registryd/deviceeventcontroller-x11.c | 2 +- registryd/deviceeventcontroller.c | 2 +- xml/Collection.xml | 6 ++-- xml/Component.xml | 2 ++ xml/Document.xml | 2 +- xml/Text.xml | 2 ++ 23 files changed, 223 insertions(+), 53 deletions(-) create mode 100755 bus/00-at-spi create mode 100644 po/ab.po diff --git a/NEWS b/NEWS index d964ce2..6738a3d 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,32 @@ +What's new in at-spi2-core 2.42.0: + +* Set X root property when Xwayland starts on demand. + +* Several dbus introspection fixes. + +What's new in at-spi2-core 2.40.3: + +* Use abstract sockets if libdbus is older than 1.12.0 (#37). + +What's new in at-spi2-core 2.40.2: + +* README: Remove outdated links. + +* Key grab fixes for the new API. + +* registryd: Add a missing call to va_end. + +What's new in at-spi2-core 2.40.1: + +* Fix double free when removing event listeners (#35). + +* Fix numlock detection. + +What's new in at-spi2-core 2.39.91: +* Fix a couple of memory leaks. + +* Remove const from AtspiDeviceListenerCB prototype (#31). + 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). diff --git a/README b/README index e41a5c6..b377fca 100644 --- a/README +++ b/README @@ -5,24 +5,22 @@ This version of at-spi is a major break from version 1.x. It has been completely rewritten to use D-Bus rather than ORBIT / CORBA for its transport protocol. -A page including instructions for testing, project status and -TODO items is kept up to date at: +An outdated page including instructions for testing, project status and +TODO items is at: - http://www.linuxfoundation.org/en/AT-SPI_on_D-Bus + https://wiki.linuxfoundation.org/accessibility/atk/at-spi/at-spi_on_d-bus The mailing list used for general questions is: - accessibility-atspi@lists.linux-foundation.org + https://lists.linuxfoundation.org/mailman/listinfo/accessibility-atspi -For bug reports, feature requests, patches or enhancements please use -the AT-SPI project on bugzilla.gnome.org. Use the at-spi2-core component for -bugs specific to this module. +For bug reports, feature requests, patches or enhancements please use: - http://bugzilla.gnome.org + https://gitlab.gnome.org/GNOME/at-spi2-core/ A git repository with the latest development code is available at: - git://git.gnome.org/at-spi2-core + https://gitlab.gnome.org/GNOME/at-spi2-core/ More information ---------------- @@ -32,15 +30,13 @@ the results of which are available on the GNOME wiki. Keep in mind that the D-Bus AT-SPI design documents on this page have not been kept up to date. - http://live.gnome.org/GAP/AtSpiDbusInvestigation/ + https://wiki.gnome.org/Accessibility/Documentation/GNOME2/ATSPI2-Investigation Other sources of relevant information about AT-SPI and Accessibility include: - http://live.gnome.org/Accessibility - http://www.sun.com/software/star/gnome/accessibility/architecture.xml - http://accessibility.kde.org/developer/atk.php - http://www.gnome.org/~billh/at-spi-idl/html/ + https://wiki.gnome.org/Accessibility + https://community.kde.org/Accessibility Contents of this package diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index b078688..26cf0ba 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -340,8 +340,11 @@ 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)) { + g_free (obj->name); + obj->name = NULL; if (!_atspi_dbus_get_property (obj, atspi_interface_accessible, "Name", error, "s", &obj->name)) return g_strdup (""); @@ -366,6 +369,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)) diff --git a/atspi/atspi-device-legacy.c b/atspi/atspi-device-legacy.c index 6950bc3..eb67768 100644 --- a/atspi/atspi-device-legacy.c +++ b/atspi/atspi-device-legacy.c @@ -47,6 +47,7 @@ struct _AtspiDeviceLegacyPrivate GSList *modifiers; guint virtual_mods_enabled; gboolean keyboard_grabbed; + unsigned int numlock_physical_mask; }; GObjectClass *device_legacy_parent_class; @@ -86,20 +87,27 @@ set_virtual_modifier (AtspiDeviceLegacy *legacy_device, gint keycode, gboolean e gboolean -key_cb (const AtspiDeviceEvent *event, void *user_data) +key_cb (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; - + guint modifiers; + set_virtual_modifier (legacy_device, event->hw_code, event->type == (AtspiEventType)ATSPI_KEY_PRESS); + + modifiers = event->modifiers | priv->virtual_mods_enabled; + if (modifiers & (1 << ATSPI_MODIFIER_NUMLOCK)) + modifiers &= ~priv->numlock_physical_mask; + 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, + modifiers, event->event_string); + g_boxed_free (ATSPI_TYPE_DEVICE_EVENT, event); return ret; } @@ -125,6 +133,9 @@ check_virtual_modifier (AtspiDeviceLegacy *legacy_device, guint modifier) AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device); GSList *l; + if (modifier == (1 << ATSPI_MODIFIER_NUMLOCK)) + return TRUE; + for (l = priv->modifiers; l; l = l->next) { AtspiLegacyKeyModifier *entry = l->data; @@ -268,6 +279,8 @@ atspi_device_legacy_init (AtspiDeviceLegacy *device) priv->display=XOpenDisplay(""); if (priv->display) priv->window = DefaultRootWindow(priv->display); + priv->numlock_physical_mask = XkbKeysymToModifiers (priv->display, + XK_Num_Lock); #endif } diff --git a/atspi/atspi-device-listener.h b/atspi/atspi-device-listener.h index d91a203..718a415 100644 --- a/atspi/atspi-device-listener.h +++ b/atspi/atspi-device-listener.h @@ -45,7 +45,7 @@ GType atspi_device_event_get_type (void); * Returns: #TRUE if the client wishes to consume/preempt the event, preventing it from being * relayed to the currently focussed application, #FALSE if the event delivery should proceed as normal. **/ -typedef gboolean (*AtspiDeviceListenerCB) (const AtspiDeviceEvent *stroke, +typedef gboolean (*AtspiDeviceListenerCB) (AtspiDeviceEvent *stroke, void *user_data); /** diff --git a/atspi/atspi-device-x11.c b/atspi/atspi-device-x11.c index e8f2199..4f88e60 100644 --- a/atspi/atspi-device-x11.c +++ b/atspi/atspi-device-x11.c @@ -44,6 +44,7 @@ struct _AtspiDeviceX11Private GSList *key_grabs; guint virtual_mods_enabled; gboolean keyboard_grabbed; + unsigned int numlock_physical_mask; }; GObjectClass *device_x11_parent_class; @@ -176,7 +177,7 @@ grab_has_active_duplicate (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab) } static void -grab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask) +grab_key_aux (AtspiDeviceX11 *x11_device, int keycode, int modmask) { AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); XIGrabModifiers xi_modifiers; @@ -197,6 +198,22 @@ grab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask) } static void +grab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + + grab_key_aux (x11_device, keycode, modmask); + if (!(modmask & LockMask)) + grab_key_aux (x11_device, keycode, modmask | LockMask); + if (!(modmask & priv->numlock_physical_mask)) + { + grab_key_aux (x11_device, keycode, modmask | priv->numlock_physical_mask); + if (!(modmask & LockMask)) + grab_key_aux (x11_device, keycode, modmask | LockMask | priv->numlock_physical_mask); + } +} + +static void enable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab) { AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); @@ -209,7 +226,7 @@ enable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab) } static void -ungrab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask) +ungrab_key_aux (AtspiDeviceX11 *x11_device, int keycode, int modmask) { AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); XIGrabModifiers xi_modifiers; @@ -221,6 +238,22 @@ ungrab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask) } static void +ungrab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask) +{ + AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); + + ungrab_key_aux (x11_device, keycode, modmask); + if (!(modmask & LockMask)) + ungrab_key_aux (x11_device, keycode, modmask | LockMask); + if (!(modmask & priv->numlock_physical_mask)) + { + ungrab_key_aux (x11_device, keycode, modmask | priv->numlock_physical_mask); + if (!(modmask & LockMask)) + ungrab_key_aux (x11_device, keycode, modmask | LockMask | priv->numlock_physical_mask); + } +} + +static void disable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab) { AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); @@ -290,6 +323,7 @@ do_event_dispatch (gpointer user_data) char text[10]; KeySym keysym; XComposeStatus status; + guint modifiers; while (XPending (display)) { @@ -301,7 +335,13 @@ do_event_dispatch (gpointer user_data) 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); + modifiers = xevent.xkey.state | priv->virtual_mods_enabled; + if (modifiers & priv->numlock_physical_mask) + { + modifiers |= (1 << ATSPI_MODIFIER_NUMLOCK); + modifiers &= ~priv->numlock_physical_mask; + } + atspi_device_notify_key (ATSPI_DEVICE (device), (xevent.type == KeyPress), xevent.xkey.keycode, keysym, modifiers, text); break; case GenericEvent: if (xevent.xcookie.extension == priv->xi_opcode) @@ -326,8 +366,11 @@ do_event_dispatch (gpointer user_data) if (!priv->device_id) priv->device_id = xiDevEv->deviceid; set_virtual_modifier (device, xiRawEv->detail, xevent.xcookie.evtype == XI_KeyPress); + modifiers = keyevent.xkey.state | priv->virtual_mods_enabled; + if (modifiers & priv->numlock_physical_mask) + modifiers |= (1 << ATSPI_MODIFIER_NUMLOCK); 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); + atspi_device_notify_key (ATSPI_DEVICE (device), (xevent.xcookie.evtype == XI_KeyPress), xiRawEv->detail, keysym, modifiers, text); /* otherwise it's probably a duplicate event from a key grab */ XFreeEventData (priv->display, &xevent.xcookie); break; @@ -396,6 +439,9 @@ check_virtual_modifier (AtspiDeviceX11 *x11_device, guint modifier) AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device); GSList *l; + if (modifier == (1 << ATSPI_MODIFIER_NUMLOCK)) + return TRUE; + for (l = priv->modifiers; l; l = l->next) { AtspiX11KeyModifier *entry = l->data; @@ -534,6 +580,9 @@ atspi_device_x11_init (AtspiDeviceX11 *device) create_event_source (device); } } + + priv->numlock_physical_mask = XkbKeysymToModifiers (priv->display, + XK_Num_Lock); } static void diff --git a/atspi/atspi-device.c b/atspi/atspi-device.c index 5f62dc3..e4a564c 100644 --- a/atspi/atspi-device.c +++ b/atspi/atspi-device.c @@ -107,6 +107,19 @@ atspi_device_new () return ATSPI_DEVICE (atspi_device_legacy_new ()); } +static gboolean +key_matches_modifiers (guint key_mods, guint grab_mods) +{ + /* The presence or lack thereof of locking modifiers should make no + difference when testing, but other modifiers should match. If the + keypress has modifiers that (a) the grab does not check for and (b) are + not lock modifiers, then we reject the match. Alt + left arrow should not + match a grab on left arrow, for instance, but whether numlock is on or + off would be irrelevant. */ + key_mods &= ~((1 << ATSPI_MODIFIER_SHIFTLOCK) | (1 << ATSPI_MODIFIER_NUMLOCK)); + return (key_mods == grab_mods); +} + gboolean atspi_device_notify_key (AtspiDevice *device, gboolean pressed, int keycode, int keysym, gint state, gchar *text) { @@ -123,8 +136,7 @@ atspi_device_notify_key (AtspiDevice *device, gboolean pressed, int keycode, int 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 (keycode == grab->keycode && key_matches_modifiers (state, grab->modifiers)) { if (grab->callback) grab->callback (device, pressed, keycode, keysym, state, text, grab->callback_data); diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c index ca6828f..5455b58 100644 --- a/atspi/atspi-event-listener.c +++ b/atspi/atspi-event-listener.c @@ -798,7 +798,10 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback, DBusMessage *message, *reply; l = g_list_next (l); if (in_send) - pending_removals = g_list_append (pending_removals, e); + { + pending_removals = g_list_remove (pending_removals, e); + 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++) diff --git a/bus/00-at-spi b/bus/00-at-spi new file mode 100755 index 0000000..dc3bb60 --- /dev/null +++ b/bus/00-at-spi @@ -0,0 +1,10 @@ +#!/bin/sh + +# Copy a11y bus address from the DBus session bus to the X11 root property. +# This is useful when Xwayland is started on demand and if the user has an +# application with X11 access that does not have access to the session bus. + +ADDR="$( busctl call --user org.a11y.Bus /org/a11y/bus org.a11y.Bus GetAddress )" || exit 0 +ADDR="$( echo $ADDR | sed 's/s "\(.*\)"/\1/' )" || exit 0 + +exec xprop -root -format AT_SPI_BUS 8s -set AT_SPI_BUS "$ADDR" diff --git a/bus/accessibility.conf.in b/bus/accessibility.conf.in index 79c5146..33d6e1c 100644 --- a/bus/accessibility.conf.in +++ b/bus/accessibility.conf.in @@ -6,7 +6,7 @@ @DATADIR@/dbus-1/accessibility-services EXTERNAL - unix:dir=/tmp + @SOCKET_ADDRESS@ diff --git a/bus/meson.build b/bus/meson.build index f6c32c9..21408f2 100644 --- a/bus/meson.build +++ b/bus/meson.build @@ -4,8 +4,15 @@ libexec_conf.set('libexecdir', atspi_libexecdir) accessibility_conf = configuration_data() accessibility_conf.set('DATADIR', atspi_datadir) +if libdbus_dep.version().version_compare('>= 1.12.0') + accessibility_conf.set('SOCKET_ADDRESS', 'unix:dir=/tmp') +else + accessibility_conf.set('SOCKET_ADDRESS', 'unix:tmpdir=/tmp') +endif + busconfig_dir = join_paths(atspi_datadir, 'defaults/at-spi2') session_dir = join_paths(atspi_sysconfdir, 'xdg/autostart') +xwayland_session_dir = join_paths(atspi_sysconfdir, 'xdg/Xwayland-session.d') configure_file(input: 'accessibility.conf.in', output: 'accessibility.conf', @@ -27,6 +34,13 @@ configure_file(input: 'at-spi-dbus-bus.service.in', configuration: libexec_conf, install_dir: systemd_user_dir) +if x11_dep.found() + # Note: It is safe to always install it. However, we only need this on + # systemd enabled machines where Xwayland may be started on-demand. + install_data('00-at-spi', + install_dir: xwayland_session_dir) +endif + launcher_args = [ '-DSYSCONFDIR="@0@"'.format(atspi_sysconfdir), '-DDATADIR="@0@"'.format(atspi_datadir), diff --git a/meson.build b/meson.build index ca49dfb..b5104c8 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('at-spi2-core', 'c', - version: '2.39.90.1', + version: '2.42.0', license: 'LGPLv2.1+', default_options: [ 'buildtype=debugoptimized', diff --git a/po/LINGUAS b/po/LINGUAS index 4e2efbd..328b6c4 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1,3 +1,4 @@ +ab an as ast diff --git a/po/ab.po b/po/ab.po new file mode 100644 index 0000000..6f34b54 --- /dev/null +++ b/po/ab.po @@ -0,0 +1,28 @@ +# Abkhazian translation for at-spi2-core. +# Copyright (C) 2011 at-spi2-core's COPYRIGHT HOLDER +# This file is distributed under the same license as the at-spi2-core package. +# Nart Tlisha , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: at-spi2-core master\n" +"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=at-" +"spi&keywords=I18N+L10N&component=general\n" +"POT-Creation-Date: 2014-02-03 23:50+0000\n" +"PO-Revision-Date: 2021-08-16 13:50+0000\n" +"Last-Translator: Nart Tlisha \n" +"Language-Team: Abkhazian\n" +"Language: ab\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.4\n" + +#: ../atspi/atspi-component.c:325 ../atspi/atspi-misc.c:1034 +#: ../atspi/atspi-value.c:111 +msgid "The application no longer exists" +msgstr "Аԥшьы уаҳа ыҟаӡам" + +#: ../atspi/atspi-misc.c:1777 +msgid "Attempted synchronous call where prohibited" +msgstr "Синхронтәу асра аҟаҵара азин ыҟаӡам" diff --git a/po/be.po b/po/be.po index f7bc9c1..f0991f3 100644 --- a/po/be.po +++ b/po/be.po @@ -1,29 +1,31 @@ # Yuras Shumovich , 2017. msgid "" msgstr "" -"Project-Id-Version: at-spi2-core.master\n" -"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=at-" -"spi&keywords=I18N+L10N&component=at-spi2-core\n" -"POT-Creation-Date: 2017-03-13 21:58+0000\n" -"PO-Revision-Date: 2017-03-26 18:30+0300\n" +"Project-Id-Version: ab10e6d45488175674299c2d7fc854f6\n" +"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/at-spi2-core/issues\n" +"POT-Creation-Date: 2020-12-28 16:53+0000\n" +"PO-Revision-Date: 2021-06-15 12:46\n" "Last-Translator: Yuras Shumovich \n" -"Language-Team: Belarusian \n" +"Language-Team: Belarusian\n" "Language: be\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\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" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || n%10>=5 && n%10<=9 || n%100>=11 && n%100<=14 ? 2 : 3);\n" "X-Generator: Poedit 1.8.11\n" +"X-Crowdin-Project: ab10e6d45488175674299c2d7fc854f6\n" +"X-Crowdin-Project-ID: 104\n" +"X-Crowdin-Language: be\n" +"X-Crowdin-File: /Localizations/Gnome/Gnome development/in/at-spi2-core.master.be.po\n" +"X-Crowdin-File-ID: 2608\n" -#: ../atspi/atspi-component.c:326 ../atspi/atspi-misc.c:1073 -#: ../atspi/atspi-value.c:111 +#: atspi/atspi-component.c:332 atspi/atspi-misc.c:1077 atspi/atspi-value.c:111 msgid "The application no longer exists" msgstr "Праграма больш не існуе" -#: ../atspi/atspi-misc.c:1832 +#: atspi/atspi-misc.c:1855 msgid "Attempted synchronous call where prohibited" -msgstr "Спроба зрабіць сінхронны выклік, там дзе гэта забаронена" +msgstr "Спроба сінхроннага выкліку ў забароненым месцы" #~ msgid "AT-SPI: Unknown signature %s for RemoveAccessible" #~ msgstr "AT-SPI: Невядомы подпіс %s для RemoveAccessible" @@ -57,8 +59,7 @@ 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" +#~ msgstr "atspi_dbus_get_property: Няправільны тып: чакаўся %s, атрыманы %c\n" #~ msgid "AT-SPI: Unknown interface %s" #~ msgstr "AT-SPI: Невядомы інтэрфейс %s" @@ -77,3 +78,4 @@ msgstr "Спроба зрабіць сінхронны выклік, там дз #~ msgid "Got invalid signature %s for signal %s from interface %s\n" #~ msgstr "Атрыманы хібны подпіс %s для сігналу %s інтэрфейсу %s\n" + diff --git a/po/gl.po b/po/gl.po index 932ffea..ed8f1fd 100644 --- a/po/gl.po +++ b/po/gl.po @@ -7,10 +7,10 @@ msgid "" msgstr "" "Project-Id-Version: at-spi 2-core\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" +"POT-Creation-Date: 2020-12-28 16:53+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-Team: Proxecto Trasno \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,10 +18,10 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.2.4\n" -#: atspi/atspi-component.c:326 atspi/atspi-misc.c:1073 atspi/atspi-value.c:111 +#: atspi/atspi-component.c:332 atspi/atspi-misc.c:1077 atspi/atspi-value.c:111 msgid "The application no longer exists" msgstr "A aplicación xa non existe" -#: atspi/atspi-misc.c:1850 +#: atspi/atspi-misc.c:1855 msgid "Attempted synchronous call where prohibited" msgstr "Tentouse unha chamada síncrona onde estaba prohibida" diff --git a/registryd/de-marshaller.c b/registryd/de-marshaller.c index 2f212b0..f109484 100644 --- a/registryd/de-marshaller.c +++ b/registryd/de-marshaller.c @@ -70,7 +70,11 @@ dbus_bool_t spi_dbus_message_iter_append_struct(DBusMessageIter *iter, ...) ptr = va_arg(args, void *); dbus_message_iter_append_basic(&iter_struct, type, ptr); } - if (!dbus_message_iter_close_container(iter, &iter_struct)) return FALSE; + if (!dbus_message_iter_close_container(iter, &iter_struct)) + { + va_end(args); + return FALSE; + } va_end(args); return TRUE; } diff --git a/registryd/deviceeventcontroller-x11.c b/registryd/deviceeventcontroller-x11.c index df16bd8..55239f4 100644 --- a/registryd/deviceeventcontroller-x11.c +++ b/registryd/deviceeventcontroller-x11.c @@ -82,7 +82,7 @@ static unsigned int mouse_button_mask = Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask; static unsigned int key_modifier_mask = Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask | ShiftMask | LockMask | ControlMask | SPI_KEYMASK_NUMLOCK; -static unsigned int _numlock_physical_mask = Mod2Mask; /* a guess, will be reset */ +extern unsigned int _numlock_physical_mask; static XModifierKeymap* xmkeymap = NULL; diff --git a/registryd/deviceeventcontroller.c b/registryd/deviceeventcontroller.c index acd089b..942f383 100644 --- a/registryd/deviceeventcontroller.c +++ b/registryd/deviceeventcontroller.c @@ -73,7 +73,7 @@ static unsigned int key_modifier_mask = SPI_KEYMASK_MOD1 | SPI_KEYMASK_MOD2 | SPI_KEYMASK_MOD3 | SPI_KEYMASK_MOD4 | SPI_KEYMASK_MOD5 | SPI_KEYMASK_SHIFT | SPI_KEYMASK_SHIFTLOCK | SPI_KEYMASK_CONTROL | SPI_KEYMASK_NUMLOCK; -static unsigned int _numlock_physical_mask = SPI_KEYMASK_MOD2; /* a guess, will be reset */ +unsigned int _numlock_physical_mask = SPI_KEYMASK_MOD2; /* a guess, will be reset */ static gboolean have_mouse_listener = FALSE; static gboolean have_mouse_event_listener = FALSE; diff --git a/xml/Collection.xml b/xml/Collection.xml index a6dff74..913ebcb 100644 --- a/xml/Collection.xml +++ b/xml/Collection.xml @@ -3,7 +3,7 @@ - + @@ -15,7 +15,7 @@ - + @@ -29,7 +29,7 @@ - + diff --git a/xml/Component.xml b/xml/Component.xml index 94056ff..afefab8 100644 --- a/xml/Component.xml +++ b/xml/Component.xml @@ -74,12 +74,14 @@ + + diff --git a/xml/Document.xml b/xml/Document.xml index a82eb98..03c9693 100644 --- a/xml/Document.xml +++ b/xml/Document.xml @@ -16,7 +16,7 @@ - + diff --git a/xml/Text.xml b/xml/Text.xml index fe9f44b..da772ae 100644 --- a/xml/Text.xml +++ b/xml/Text.xml @@ -157,6 +157,7 @@ + @@ -165,6 +166,7 @@ + -- 2.7.4 From 6c0b52b2f7f16ecb6bfb60d1e95c68203c260971 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Artur=20=C5=9Awigo=C5=84?= Date: Mon, 21 Feb 2022 10:34:35 +0100 Subject: [PATCH 04/16] Fix meson 0.60.3 errors Change-Id: I74be6c48e1dd6a5120a506109a7b6849793d20ae --- bus/meson.build | 7 ------- packaging/at-spi2-core.spec | 18 +++++++++--------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/bus/meson.build b/bus/meson.build index 9981e3a..eae2e1d 100644 --- a/bus/meson.build +++ b/bus/meson.build @@ -34,13 +34,6 @@ configure_file(input: 'at-spi-dbus-bus.service.in', configuration: libexec_conf, install_dir: systemd_user_dir) -if x11_dep.found() - # Note: It is safe to always install it. However, we only need this on - # systemd enabled machines where Xwayland may be started on-demand. - install_data('00-at-spi', - install_dir: xwayland_session_dir) -endif - launcher_args = [ '-DSYSCONFDIR="@0@"'.format(atspi_sysconfdir), '-DDATADIR="@0@"'.format(atspi_datadir), diff --git a/packaging/at-spi2-core.spec b/packaging/at-spi2-core.spec index 09c3abb..3742942 100644 --- a/packaging/at-spi2-core.spec +++ b/packaging/at-spi2-core.spec @@ -73,21 +73,21 @@ to develop applications that require these. cp %{SOURCE1001} . %build -meson --prefix /usr --libdir %{_libdir} build -Dwith-dbus-daemondir=%{_bindir} -Ddbus_daemon=/usr/bin/dbus-daemon \ -%if !%{with x} - -Denable-x11=no \ -%else - -Denable-x11=yes \ -%endif - -Denable-static=no +meson setup \ + --prefix=/usr \ + --libdir=%{_libdir} \ + --default-library=shared \ + -Ddbus_daemon=/usr/bin/dbus-daemon \ + -Dx11=no \ + build -ninja -C build all +meson compile -C build %install find %{buildroot} -name '*.la' -or -name '*.a' | xargs rm -f export DESTDIR=%{buildroot} -ninja -C build install +meson install -C build %find_lang %{name} -- 2.7.4 From dd2dcd4f313f7d60020d4c04aa23789f7af9a7da Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Wed, 20 Apr 2022 17:22:12 +0900 Subject: [PATCH 05/16] [Tizen] Provide GetForegroundWindows interface This interface will work for selecting a window on x,y position. It will be needed in multi-window environment. Change-Id: Idc86f360de0d43c867dddec5f958a26aa2693c08 --- atspi/atspi-registry.c | 61 +++++++++++- atspi/atspi-registry.h | 1 + registryd/registry-main.c | 11 +-- registryd/registry.c | 244 +++++++++++++++++++++++++++++++++++++++------- registryd/registry.h | 6 +- 5 files changed, 276 insertions(+), 47 deletions(-) diff --git a/atspi/atspi-registry.c b/atspi/atspi-registry.c index e9a47e5..c5153ae 100644 --- a/atspi/atspi-registry.c +++ b/atspi/atspi-registry.c @@ -117,8 +117,8 @@ atspi_get_active_window () AtspiAccessible* active_window; message = dbus_message_new_method_call (ATSPI_DBUS_NAME_REGISTRY, - ATSPI_DBUS_PATH_ROOT, - ATSPI_DBUS_INTERFACE_SOCKET, + ATSPI_DBUS_PATH_REGISTRY, + ATSPI_DBUS_INTERFACE_REGISTRY, "GetActiveWindow"); if (!message) return NULL; @@ -143,6 +143,63 @@ atspi_get_active_window () return g_object_ref (active_window); } + +/** + * atspi_get_foreground_windows: + * + * Gets the array of foreground windows On return, @array will point + * to a newly-created, NULL terminated array of foreground window + * pointers. + * It is the responsibility of the caller to free this array when + * it is no longer needed. + * + * Returns: (element-type AtspiAccessible*) (transfer full): a #GArray of + * foreground windows. + **/ +GArray * +atspi_get_foreground_windows () +{ + DBusMessage *message, *reply; + DBusMessageIter iter, iter_array, iter_dict; + GArray *ret; + + message = dbus_message_new_method_call (ATSPI_DBUS_NAME_REGISTRY, + ATSPI_DBUS_PATH_REGISTRY, + ATSPI_DBUS_INTERFACE_REGISTRY, + "GetForegroundWindows"); + if (!message) + return NULL; + + reply = _atspi_dbus_send_with_reply_and_block (message, NULL); + if (!reply) + return NULL; + + if (strcmp (dbus_message_get_signature (reply), "a(so)") != 0) + { + dbus_message_unref (reply); + return NULL; + } + + dbus_message_iter_init (reply, &iter); + ret = g_array_new (TRUE, TRUE, sizeof (AtspiAccessible *)); + + dbus_message_iter_recurse (&iter, &iter_array); + while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) + { + const char *name, *path; + AtspiAccessible *accessible; + dbus_message_iter_recurse (&iter_array, &iter_dict); + dbus_message_iter_get_basic (&iter_dict, &name); + dbus_message_iter_next (&iter_dict); + dbus_message_iter_get_basic (&iter_dict, &path); + accessible = ref_accessible(name, path); + ret = g_array_append_val (ret, accessible); + dbus_message_iter_next (&iter_array); + } + dbus_message_unref (reply); + + return ret; +} // static gboolean diff --git a/atspi/atspi-registry.h b/atspi/atspi-registry.h index b266c7b..877a685 100644 --- a/atspi/atspi-registry.h +++ b/atspi/atspi-registry.h @@ -41,6 +41,7 @@ GArray *atspi_get_desktop_list (); //TIZEN_ONLY(20211206) Provide GetActiveWindow AtspiAccessible* atspi_get_active_window (); +GArray* atspi_get_foreground_windows (); // gboolean diff --git a/registryd/registry-main.c b/registryd/registry-main.c index c137182..f364ac1 100644 --- a/registryd/registry-main.c +++ b/registryd/registry-main.c @@ -260,16 +260,7 @@ main (int argc, char **argv) g_main_loop_run (mainloop); //TIZEN_ONLY(20211206) Provide GetActiveWindow - if (registry->active_window_name) - { - g_free (registry->active_window_name); - registry->active_window_name = NULL; - } - if (registry->active_window_path) - { - g_free (registry->active_window_path); - registry->active_window_path = NULL; - } + spi_registry_free_active_window (registry); // dbus_connection_close (bus); diff --git a/registryd/registry.c b/registryd/registry.c index fa2d3a5..9757067 100644 --- a/registryd/registry.c +++ b/registryd/registry.c @@ -29,6 +29,26 @@ #include "paths.h" #include "registry.h" #include "introspection.h" +//TIZEN_ONLY(20220420) Provide GetForegroundWindows +#include "atspi/atspi.h" + +static gboolean +_is_same (const char *s1, const char *s2) +{ + size_t l1, l2; + + if (!s1 || !s2) + return (s1 == s2); + + l1 = strlen (s1); + l2 = strlen (s2); + + if (l1 != l2) + return FALSE; + + return !strncmp (s1, s2, l1); +} +// typedef struct event_data event_data; struct event_data @@ -92,6 +112,7 @@ static void spi_registry_init (SpiRegistry *registry) { registry->apps = g_ptr_array_new_with_free_func ((GDestroyNotify) spi_reference_free); + registry->wins = g_ptr_array_new_with_free_func ((GDestroyNotify) spi_reference_free); } /*---------------------------------------------------------------------------*/ @@ -299,22 +320,25 @@ handle_disconnection (DBusConnection *bus, DBusMessage *message, void *user_data } remove_events (reg, old, ""); + //TIZEN_ONLY(20220420) Provide GetForegroundWindows + for (i = 0; i < reg->wins->len; i++) + { + SpiReference *ref = g_ptr_array_index (reg->wins, i); + if (_is_same (old, ref->name)) + { + g_ptr_array_remove_index (reg->wins, i); + i--; + } + } + // } } } //TIZEN_ONLY(20211206) Provide GetActiveWindow static void -handle_activate_window (DBusConnection *bus, DBusMessage *message, void *user_data) +_set_active_window (SpiRegistry *reg, DBusMessage *message, const char* sender, const char *path) { - SpiRegistry *reg = SPI_REGISTRY (user_data); - - const char *sender = dbus_message_get_sender (message); - const char *path = dbus_message_get_path (message); - - if (!sender || !path) - return; - char *detail = NULL; dbus_int32_t detail1; DBusMessageIter iter; @@ -326,13 +350,58 @@ handle_activate_window (DBusConnection *bus, DBusMessage *message, void *user_da if (detail1 & ACCESSIBLE_WINDOW_ACTIVATE_INFO_KEYBOARD) return; - if (reg->active_window_name) - g_free (reg->active_window_name); - if (reg->active_window_path) - g_free (reg->active_window_path); + if (reg->active_win) spi_reference_free (reg->active_win); + reg->active_win = spi_reference_new (sender, path); +} + +static void +_unset_active_window (SpiRegistry *reg, const char* sender, const char *path) +{ + SpiReference *ref; + + if (!reg->active_win) + return; + + ref = spi_reference_new (sender, path); + + if (compare_reference (ref, reg->active_win)) + { + spi_reference_free (reg->active_win); + reg->active_win = NULL; + } +} + +static void +_add_window_info (SpiRegistry *reg, const char* sender, const char *path) +{ + guint index; + if (find_index_of_reference (reg->wins, sender, path, &index)) + return; + + g_ptr_array_add (reg->wins, spi_reference_new (sender, path)); +} - reg->active_window_name = g_strdup(sender); - reg->active_window_path = g_strdup(path); +static void +_remove_window_info (SpiRegistry *reg, const char* sender, const char *path) +{ + guint index; + if (find_index_of_reference (reg->wins, sender, path, &index)) + g_ptr_array_remove_index (reg->wins, index); +} + +static void +handle_activate_window (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + SpiRegistry *reg = SPI_REGISTRY (user_data); + + const char *sender = dbus_message_get_sender (message); + const char *path = dbus_message_get_path (message); + + if (!sender || !path) + return; + + _set_active_window (reg, message, sender, path); + _add_window_info (reg, sender, path); } static void @@ -340,23 +409,79 @@ handle_deactivate_window (DBusConnection *bus, DBusMessage *message, void *user_ { SpiRegistry *reg = SPI_REGISTRY (user_data); - if (!reg->active_window_name || !reg->active_window_path) + const char *sender = dbus_message_get_sender (message); + const char *path = dbus_message_get_path (message); + + if (!sender || !path) return; + _unset_active_window (reg, sender, path); + /* deactivate window can be showing, so no need _remove_window_info */ +} + +static void +handle_destroy_window (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + SpiRegistry *reg = SPI_REGISTRY (user_data); + const char *sender = dbus_message_get_sender (message); const char *path = dbus_message_get_path (message); if (!sender || !path) return; - if (!g_strcmp0(reg->active_window_name, sender) && - !g_strcmp0(reg->active_window_path, path)) + _unset_active_window (reg, sender, path); + _remove_window_info (reg, sender, path); +} + +static void +handle_state_changed (DBusConnection *bus, DBusMessage *message, void *user_data) +{ + SpiRegistry *reg = SPI_REGISTRY (user_data); + + const char *sender = dbus_message_get_sender (message); + const char *path = dbus_message_get_path (message); + const char *signature = dbus_message_get_signature (message); + + if (!sender || !path || !signature) + return; + + if (!_is_same (signature, "siiv(so)") && + !_is_same (signature, "siiva{sv}")) + return; + + DBusMessageIter iter; + dbus_int32_t detail1, detail2; + char *detail = NULL; + + dbus_message_iter_init (message, &iter); + dbus_message_iter_get_basic (&iter, &detail); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &detail1); + dbus_message_iter_next (&iter); + dbus_message_iter_get_basic (&iter, &detail2); + dbus_message_iter_next (&iter); + + /* TODO: detail == showing */ + if (!detail || !_is_same (detail, "visible")) + return; + + AtspiAccessible *accessible; + AtspiRole role; + + accessible = ref_accessible (sender, path); + role = atspi_accessible_get_role (accessible, NULL); + + if (role == ATSPI_ROLE_WINDOW || + role == ATSPI_ROLE_INPUT_METHOD_WINDOW) { - g_free (reg->active_window_name); - g_free (reg->active_window_path); - reg->active_window_name = NULL; - reg->active_window_path = NULL; + if (detail1) + _add_window_info (reg, sender, path); + else + _remove_window_info (reg, sender, path); } + + g_object_unref (accessible); } // @@ -406,16 +531,28 @@ signal_filter (DBusConnection *bus, DBusMessage *message, void *user_data) if (type != DBUS_MESSAGE_TYPE_SIGNAL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + //TIZEN_ONLY(20220420) Provide GetForegroundWindows + if (!iface || !member) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + // + if (!g_strcmp0(iface, DBUS_INTERFACE_DBUS) && !g_strcmp0(member, "NameOwnerChanged")) handle_disconnection (bus, message, user_data); //TIZEN_ONLY(20211206) Provide GetActiveWindow - else if (!g_strcmp0(iface, "org.a11y.atspi.Event.Window")) + else if (_is_same (iface, "org.a11y.atspi.Event.Window")) { - if (!g_strcmp0(member, "Activate")) + if (_is_same (member, "Activate")) handle_activate_window (bus, message, user_data); - else if (!g_strcmp0(member, "Deactivate")) + else if (_is_same (member, "Deactivate")) handle_deactivate_window (bus, message, user_data); + else if (_is_same (member, "Destroy")) + handle_destroy_window (bus, message, user_data); + } + else if (_is_same (iface, "org.a11y.atspi.Event.Object")) + { + if (_is_same (member, "StateChanged")) + handle_state_changed (bus, message, user_data); } // else @@ -909,13 +1046,40 @@ impl_GetActiveWindow (DBusConnection * bus, reply = dbus_message_new_method_return (message); dbus_message_iter_init_append (reply, &iter); - if (!reg->active_window_name || !reg->active_window_path) + SpiReference *active_window = reg->active_win; + if (!active_window) append_reference (&iter, SPI_DBUS_NAME_REGISTRY, SPI_DBUS_PATH_NULL); else - append_reference (&iter, reg->active_window_name, reg->active_window_path); + append_reference (&iter, active_window->name, active_window->path); return reply; } + +static DBusMessage * +impl_GetForegroundWindows (DBusConnection * bus, + DBusMessage * message, void *user_data) +{ + SpiRegistry *reg = SPI_REGISTRY (user_data); + DBusMessage *reply; + DBusMessageIter iter, iter_array, iter_struct; + guint i = 0; + + reply = dbus_message_new_method_return (message); + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(so)", &iter_array); + + SpiReference *win; + for (i = 0; i < reg->wins->len; i++) + { + win = g_ptr_array_index (reg->wins, i); + dbus_message_iter_open_container (&iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &win->name); + dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_OBJECT_PATH, &win->path); + dbus_message_iter_close_container (&iter_array, &iter_struct); + } + dbus_message_iter_close_container (&iter, &iter_array); + return reply; +} // static DBusMessage * @@ -1394,10 +1558,6 @@ handle_method_root (DBusConnection *bus, DBusMessage *message, void *user_data) reply = impl_Embed (bus, message, user_data); else if (!strcmp (member, "Unembed")) reply = impl_Unembed (bus, message, user_data); - //TIZEN_ONLY(20211206) Provide GetActiveWindow - else if (!strcmp (member, "GetActiveWindow")) - reply = impl_GetActiveWindow (bus, message, user_data); - // else result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1496,6 +1656,12 @@ handle_method_registry (DBusConnection *bus, DBusMessage *message, void *user_da reply = impl_deregister_event (bus, message, user_data); else if (!strcmp(member, "GetRegisteredEvents")) reply = impl_get_registered_events (bus, message, user_data); + //TIZEN_ONLY(20211206) Provide GetActiveWindow + else if (_is_same(member, "GetActiveWindow")) + reply = impl_GetActiveWindow (bus, message, user_data); + else if (_is_same(member, "GetForegroundWindows")) + reply = impl_GetForegroundWindows(bus, message, user_data); + // else result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1560,6 +1726,16 @@ static gchar *app_sig_match_window_activate = "type='signal',interface='org.a11y.atspi.Event.Window',member='Activate'"; static gchar *app_sig_match_window_deactivate = "type='signal',interface='org.a11y.atspi.Event.Window',member='Deactivate'"; +static gchar *app_sig_match_window_destroy = + "type='signal',interface='org.a11y.atspi.Event.Window',member='Destroy'"; +static gchar *app_sig_match_state_changed = + "type='signal',interface='org.a11y.atspi.Event.Object',member='StateChanged'"; + +void spi_registry_free_active_window (SpiRegistry *reg) +{ + if (reg->active_win) + spi_reference_free (reg->active_win); +} // SpiRegistry * @@ -1573,10 +1749,12 @@ spi_registry_new (DBusConnection *bus) dbus_connection_add_filter (bus, signal_filter, reg, NULL); //TIZEN_ONLY(20211206) Provide GetActiveWindow - reg->active_window_name = NULL; - reg->active_window_path = NULL; + reg->active_win = NULL; + dbus_bus_add_match (reg->bus, app_sig_match_window_activate, NULL); dbus_bus_add_match (reg->bus, app_sig_match_window_deactivate, NULL); + dbus_bus_add_match (reg->bus, app_sig_match_window_destroy, NULL); + dbus_bus_add_match (reg->bus, app_sig_match_state_changed, NULL); // dbus_connection_register_object_path (bus, SPI_DBUS_PATH_ROOT, &root_vtable, reg); diff --git a/registryd/registry.h b/registryd/registry.h index ee82ef5..5b2f4ec 100644 --- a/registryd/registry.h +++ b/registryd/registry.h @@ -52,8 +52,8 @@ struct _SpiRegistry { GList *events; //TIZEN_ONLY(20211206) Provide GetActiveWindow - gchar *active_window_name; - gchar *active_window_path; + GPtrArray *wins; + gpointer active_win; // }; @@ -64,6 +64,8 @@ typedef enum { ACCESSIBLE_WINDOW_ACTIVATE_INFO_DEFAULT_LABEL_DISABLED = 1 << 1, ACCESSIBLE_WINDOW_ACTIVATE_INFO_KEYBOARD = 1 << 2, } WindowActivateInfoType; + +void spi_registry_free_active_window (SpiRegistry *reg); // struct _SpiRegistryClass { -- 2.7.4 From 646910d52fccb983857e13e539ebe1950677cb4c Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Fri, 20 May 2022 16:05:10 +0900 Subject: [PATCH 06/16] Fix memory leak This patch is removing memory leak detected by analysis tool. Change-Id: I473e2a6d20b92a9e28300ed758e9bd3508b78fcb --- registryd/registry.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/registryd/registry.c b/registryd/registry.c index 9757067..0e1703e 100644 --- a/registryd/registry.c +++ b/registryd/registry.c @@ -369,6 +369,8 @@ _unset_active_window (SpiRegistry *reg, const char* sender, const char *path) spi_reference_free (reg->active_win); reg->active_win = NULL; } + + spi_reference_free (ref); } static void -- 2.7.4 From d88142a637d082fcb1b386a2379c378442fbc3d3 Mon Sep 17 00:00:00 2001 From: Shinwoo Kim Date: Wed, 25 May 2022 13:47:14 +0900 Subject: [PATCH 07/16] registryd: handle 'showing' window For multi-window case and multi-AT-client case, registryd keep the information about 'showing' window. This patch works well with following related patch: https://review.tizen.org/gerrit/#/c/platform/core/accessibility/screen-reader/+/275382/ https://review.tizen.org/gerrit/#/c/platform/core/uifw/dali-adaptor/+/275412/ Change-Id: I39a7094c6745d5b83b480a589322b0bc35b9191e --- registryd/registry.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/registryd/registry.c b/registryd/registry.c index 0e1703e..089590a 100644 --- a/registryd/registry.c +++ b/registryd/registry.c @@ -464,8 +464,7 @@ handle_state_changed (DBusConnection *bus, DBusMessage *message, void *user_data dbus_message_iter_get_basic (&iter, &detail2); dbus_message_iter_next (&iter); - /* TODO: detail == showing */ - if (!detail || !_is_same (detail, "visible")) + if (!detail || (!_is_same (detail, "visible") && !_is_same (detail, "showing"))) return; AtspiAccessible *accessible; -- 2.7.4 From 902dc3558d17fce8d60117ca66c905fe8993f5c8 Mon Sep 17 00:00:00 2001 From: Lukasz Oleksak Date: Fri, 17 Jun 2022 11:11:42 +0200 Subject: [PATCH 08/16] Increasing cache maximum size to handle complex UIs Change-Id: I5a9072894df80214532c9521d0f30aa454d0a239 --- atspi/atspi-misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index be8030e..ae2de51 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -38,7 +38,7 @@ #include /* This value is not fixed, could be changed. */ -#define HASH_TABLE_SIZE_MAX 1000 +#define HASH_TABLE_SIZE_MAX 3000 static void handle_get_items (DBusPendingCall *pending, void *user_data); -- 2.7.4 From 23abf1e6f26e855879c534ebd07b370182704dee Mon Sep 17 00:00:00 2001 From: Lukasz Oleksak Date: Thu, 7 Jul 2022 21:00:49 +0200 Subject: [PATCH 09/16] Do not return intermediate results Intermediate result is a non-navigable Proxy object that is used only to continue searching of UI element in embeded GUIs. It should never be returned as a final result of search. Change-Id: I1c8962154e8955526074f37f32039100b68ea363 --- atspi/atspi-accessible.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index dddb8fe..d461055 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -493,10 +493,12 @@ atspi_accessible_get_navigable_at_point (AtspiAccessible *root, } break; } - - if (return_value) - g_object_unref(return_value); - return_value = root = tmp; + root = tmp; + if (!recurse) { + if (return_value) + g_object_unref(return_value); + return_value = tmp; + } } while(recurse); return return_value; } -- 2.7.4 From c572f70de5d589fdf871114164fbdbc6aa168b97 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Artur=20=C5=9Awigo=C5=84?= Date: Thu, 23 Feb 2023 10:16:54 +0100 Subject: [PATCH 10/16] 2.44.1 Change-Id: Id40e27e0a19f1d06e4bd8ce9fa68e433bfb53a11 --- .gitlab-ci.yml | 225 ++++++++++++++++++++++++++++++++++ .gitlab-ci/README.md | 76 ++++++++++++ .gitlab-ci/gen-coverage.sh | 30 +++++ .gitlab-ci/lcovrc | 13 ++ .gitlab-ci/opensuse.Dockerfile | 34 +++++ .gitlab-ci/run-docker.sh | 132 ++++++++++++++++++++ .gitlab-ci/run-style-check.sh | 33 +++++ .gitlab-ci/run-tests.sh | 17 +++ .gitlab-ci/search-common-ancestor.sh | 36 ++++++ NEWS | 29 +++++ README | 112 ----------------- README.md | 71 +++++++++++ at-spi2-core.doap | 7 ++ atspi/atspi-device-legacy.c | 2 +- atspi/atspi-device-listener.c | 2 +- atspi/atspi-device-x11.c | 2 +- atspi/atspi-event-listener.c | 9 ++ atspi/atspi-misc.c | 74 +++++------ atspi/atspi-registry.c | 22 ++-- bus/README | 10 -- bus/README.md | 98 +++++++++++++++ bus/at-spi-bus-launcher.c | 198 ++++++++++++++++++++++-------- dbind/dbind-any.c | 22 ++-- dbind/dbtest.c | 7 +- devel-docs/gitlab-ci.md | 188 ++++++++++++++++++++++++++++ meson.build | 7 +- po/LINGUAS | 1 + po/is.po | 26 ++++ registryd/deviceeventcontroller-x11.c | 96 +++++++-------- registryd/deviceeventcontroller-x11.h | 28 +++++ registryd/deviceeventcontroller.c | 65 +++++++--- registryd/deviceeventcontroller.h | 1 - registryd/registry.c | 10 +- test/memory.c | 15 ++- xml/Cache.xml | 4 +- 35 files changed, 1378 insertions(+), 324 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 .gitlab-ci/README.md create mode 100644 .gitlab-ci/gen-coverage.sh create mode 100644 .gitlab-ci/lcovrc create mode 100644 .gitlab-ci/opensuse.Dockerfile create mode 100755 .gitlab-ci/run-docker.sh create mode 100755 .gitlab-ci/run-style-check.sh create mode 100755 .gitlab-ci/run-tests.sh create mode 100755 .gitlab-ci/search-common-ancestor.sh delete mode 100644 README create mode 100644 README.md delete mode 100644 bus/README create mode 100644 bus/README.md create mode 100644 devel-docs/gitlab-ci.md create mode 100644 po/is.po create mode 100644 registryd/deviceeventcontroller-x11.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..963a79e --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,225 @@ +# Continuous Integration configuration for at-spi2-core +# +# For documentation on how this works, see devel-docs/gitlab-ci.md +# +# Full documentation for Gitlab CI: https://docs.gitlab.com/ee/ci/ +# +# Introduction to Gitlab CI: https://docs.gitlab.com/ee/ci/quick_start/index.html + +variables: + OPENSUSE_TUMBLEWEED_IMAGE: "registry.gitlab.gnome.org/gnome/at-spi2-core/opensuse:latest" + +# Stages in the CI pipeline in which jobs will be run +stages: +# - style-check + - build + - analysis + - docs + - deploy + +# Base definition for jobs. +# +# We have the package dependencies to install on top of a stock opensuse/tumbleweed image, +# and the rules for when to run each job (on merge requests and on personal branches). +.only-default: + only: + - merge_requests + - branches + except: + - tags + +# C coding style checker. +# +# Disabled for now, since we need to decide to reindent all the code first. +# +# style-check-diff: +# extends: .only-default +# image: fedora:latest +# stage: style-check +# script: +# - dnf install -y clang-tools-extra curl diffutils git +# - sh -x ./.gitlab-ci/run-style-check.sh + + +# Template for the default build recipe. +# +# Depends on these variables: +# @MESON_EXTRA_FLAGS: extra arguments for the meson setup invocation +.build-default: + image: $OPENSUSE_TUMBLEWEED_IMAGE + extends: .only-default + script: + - meson setup ${MESON_EXTRA_FLAGS} --prefix /usr _build . + - meson compile -C _build + - meson install -C _build + - mkdir /tmp/test+dir+with+funny+chars + - export XDG_RUNTIME_DIR=/tmp/test+dir+with+funny+chars # See https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues/48 + - dbus-run-session -- .gitlab-ci/run-tests.sh + artifacts: + reports: + junit: "_build/meson-logs/testlog.junit.xml" + when: always + name: "at-spi2-core-${CI_COMMIT_REF_NAME}" + paths: + - "${CI_PROJECT_DIR}/_build/meson-logs" + - "${CI_PROJECT_DIR}/_build/atspi/Atspi-2.0.gir" + +# Inherit to build the API reference via gi-docgen +# @PROJECT_DEPS: the dependencies of the project (on Fedora) +# @MESON_EXTRA_FLAGS: extra arguments for the meson setup invocation +# @DOCS_FLAGS: doc-related arguments for the meson setup invocation +# @DOCS_PATH: the docs output directory under the build directory +# .gidocgen-build: +# image: fedora:latest +# before_script: +# - export PATH="$HOME/.local/bin:$PATH" +# - dnf install -y python3 python3-pip python3-wheel gobject-introspection-devel graphviz ninja-build redhat-rpm-config +# - dnf install -y ${PROJECT_DEPS} +# - pip3 install --user meson==${MESON_VERSION} gi-docgen jinja2 Markdown markupsafe pygments toml typogrify +# script: +# - meson setup ${MESON_EXTRA_FLAGS} ${DOCS_FLAGS} _docs . +# - meson compile -C _docs +# - | +# pushd "_docs/${DOCS_PATH}" > /dev/null +# tar cf ${CI_PROJECT_NAME}-docs.tar . +# popd > /dev/null +# - mv _docs/${DOCS_PATH}/${CI_PROJECT_NAME}-docs.tar . +# artifacts: +# when: always +# name: 'Documentation' +# expose_as: 'Download the API reference' +# paths: +# - ${CI_PROJECT_NAME}-docs.tar + +# Build and run the test suite. +# +# Look at .build-default for where the artifacts are stored (build/test logs, built binaries). +opensuse-x86_64: + extends: .build-default + stage: build + needs: [] + variables: + MESON_EXTRA_FLAGS: "--buildtype=debug" # -Dwerror=true + +# Run static analysis on the code. +# +# The logs are part of the compilation stderr. +static-scan: + image: $OPENSUSE_TUMBLEWEED_IMAGE + stage: analysis + needs: [] + variables: + MESON_EXTRA_FLAGS: "--buildtype=debug -Dintrospection=no -Ddocs=false" + script: + - meson setup ${MESON_EXTRA_FLAGS} --prefix /usr _scan_build . + - ninja -C _scan_build scan-build + artifacts: + name: "at-spi2-core-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}" + when: always + paths: + - "_scan_build/meson-logs/scanbuild" + +# Build and run with address sanitizer (asan). +asan-build: + image: $OPENSUSE_TUMBLEWEED_IMAGE + stage: analysis + needs: [] + variables: + MESON_EXTRA_FLAGS: "--buildtype=debug -Db_sanitize=address -Db_lundef=false -Dintrospection=no -Ddocs=false" + script: + - CC=clang meson setup ${MESON_EXTRA_FLAGS} --prefix /usr _build . + - meson compile -C _build + - meson install -C _build + - dbus-run-session -- .gitlab-ci/run-tests.sh + artifacts: + name: "at-spi2-core-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}" + when: always + paths: + - "_asan_build/meson-logs" + allow_failure: true + +# Run the test suite and extract code coverage information. +# +# See the _coverage/ artifact for the HTML report. +coverage: + image: $OPENSUSE_TUMBLEWEED_IMAGE + stage: analysis + needs: [] + variables: + MESON_EXTRA_FLAGS: "--buildtype=debug -Ddocs=false -Dintrospection=no" + CFLAGS: "-coverage -ftest-coverage -fprofile-arcs" + script: + - meson setup ${MESON_EXTRA_FLAGS} --prefix /usr _build . + - meson compile -C _build + - meson install -C _build + - mkdir -p _coverage + - lcov --config-file .gitlab-ci/lcovrc --directory _build --capture --initial --output-file "_coverage/${CI_JOB_NAME}-baseline.lcov" + - dbus-run-session -- .gitlab-ci/run-tests.sh + - lcov --config-file .gitlab-ci/lcovrc --directory _build --capture --output-file "_coverage/${CI_JOB_NAME}.lcov" + - bash -x .gitlab-ci/gen-coverage.sh + - mkdir -p public/ + - cp -r _coverage public/coverage + coverage: '/^\s+lines\.+:\s+([\d.]+\%)\s+/' + artifacts: + name: "at-spi2-core-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}" + expire_in: 2 days + when: always + paths: + - "_build/meson-logs" + - public + +# Build the reference documentation. +# +# reference: +# stage: docs +# needs: [] +# extends: .gidocgen-build +# variables: +# PROJECT_DEPS: +# gdk-pixbuf2-devel +# geocode-glib-devel +# gettext +# git +# gobject-introspection-devel +# itstool +# libsoup-devel +# libxml2-devel +# ninja-build +# pylint +# python3 +# python3-gobject +# python3-pip +# python3-wheel +# redhat-rpm-config +# vala +# MESON_VERSION: "0.55.3" +# DOCS_FLAGS: -Dgtk_doc=true +# DOCS_PATH: doc/libgweather-4.0 +# +# +# Publish the generated HTML reference documentation. +# +# pages: +# stage: deploy +# needs: ['reference'] +# script: +# - mkdir public && cd public +# - tar xf ../${CI_PROJECT_NAME}-docs.tar +# artifacts: +# paths: +# - public +# only: +# - master +# - main + +# Publish the test coverage report +pages: + stage: deploy + needs: [ coverage ] + script: + - echo # dummy - contents were generated in another job + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH diff --git a/.gitlab-ci/README.md b/.gitlab-ci/README.md new file mode 100644 index 0000000..9adfaac --- /dev/null +++ b/.gitlab-ci/README.md @@ -0,0 +1,76 @@ +# Continuous Integration scripts for at-spi2-core + +Please see the general [documentation for at-spi2-core's Gitlab CI][ci-docs]. + +This directory contains scripts which get called during a run of a CI +pipeline, and utilities to maintain the CI infrastructure. + +## Scripts used during a run of a CI pipeline: + +* `run-tests.sh` - Runs the test suite and prints other diagnostics. + +* `gen-coverage.sh` - After the test suite is run, merges the various + code coverage reports from `lcov`, and generates an HTML report. + +* `lcovrc` - Configuration file for `lcov`, used by `gen-coverage.sh`. + Among other things, this tells `lcov` to exclude branch coverage for + the unreachable branches of `g_return_if_fail()` and friends. + +* `run-style-check.sh` - Runs `clang-format-diff` to test for source + files with inconsistent formatting, and uploads the resulting report + to gitlab so it can be viewed as part of a merge request's analysis. + +* `search-common-ancestor.sh` - Utility used from + `run-style-check.sh`; finds a git branch point from the current + commit. + +## Utilities to maintain the CI infrastructure: + +To make pipelines fast, and avoid a lot of repeated downloads, +at-spi2-core uses pre-built container images for CI pipelines, instead +of using a stock image like opensuse/tumbleweed and then installing +all the dependencies on top of it every time. + +The prebuilt images are stored here: +https://gitlab.gnome.org/GNOME/at-spi2-core/container_registry + +Instead of maintaining those images by hand with `docker` or `podman` +commands, here is a little script (stolen from [glib][glib-ci]) to +maintain them, which you can start exploring with `./run-docker.sh help`. + +This script knows how to build and upload images from Dockerfiles +called `foo.Dockerfile`. The image configurations we have: + +* `opensuse.Dockerfile` - starts with an opensuse/tumbleweed image and + installs the package dependencies for building at-spi2-core. + +If you are one of at-spi2-core's maintainers, you'll want to update +the CI images periodically. First, install `podman` and +`podman-docker`. Then, run this: + +```sh +# "opensuse" in these commands indicates to use the opensuse.Dockerfile configuration + +./run-docker.sh build --base=opensuse # builds the image, takes a while + +./run-docker.sh run --base=opensuse # launch the container; poke around; see that it works + +./run-docker.sh push --base=opensuse # push the image to registry.gitlab.gnome.org +``` + +The `build` subcommand creates an image named +`registry.gitlab.gnome.org/gnome/at-spi2-core/opensuse/tumbleweed:latest` +**that is only stored in your localhost**. + +The `run` subcommand launches a container with that image and gives +you a shell prompt. This is equivalent to `podman run`. + +The `push` subcommand takes that built image and uploads it to +`registry.gitlab.gnome.org`. It will then be visible from +https://gitlab.gnome.org/GNOME/at-spi2-core/container_registry - the +CI configuration in [`.gitlab-ci.yml`](../.gitlab-ci.yml) uses this +image for the pipeline. + +[ci-docs]: ../devel-docs/gitlab-ci.md +[container-registry-docs]: https://gitlab.gnome.org/help/user/packages/container_registry/index +[glib-ci]: https://gitlab.gnome.org/GNOME/glib/-/tree/main/.gitlab-ci diff --git a/.gitlab-ci/gen-coverage.sh b/.gitlab-ci/gen-coverage.sh new file mode 100644 index 0000000..3949256 --- /dev/null +++ b/.gitlab-ci/gen-coverage.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -e + +for path in _coverage/*.lcov; do + lcov --config-file .gitlab-ci/lcovrc -r "${path}" '*/_build/*' -o "$(pwd)/${path}" + lcov --config-file .gitlab-ci/lcovrc -e "${path}" "$(pwd)/*" -o "$(pwd)/${path}" +done + +genhtml \ + --ignore-errors=source \ + --config-file .gitlab-ci/lcovrc \ + _coverage/*.lcov \ + -o _coverage/coverage + +cd _coverage +rm -f ./*.lcov + +cat >index.html < +at-spi2-core Coverage + + + + +EOL diff --git a/.gitlab-ci/lcovrc b/.gitlab-ci/lcovrc new file mode 100644 index 0000000..ac5997b --- /dev/null +++ b/.gitlab-ci/lcovrc @@ -0,0 +1,13 @@ +# lcov and genhtml configuration +# See http://ltp.sourceforge.net/coverage/lcov/lcovrc.5.php + +# Always enable branch coverage +lcov_branch_coverage = 1 + +# Exclude precondition assertions, as we can never reasonably get full branch +# coverage of them, as they should never normally fail. +# See https://github.com/linux-test-project/lcov/issues/44 +lcov_excl_br_line = LCOV_EXCL_BR_LINE|g_return_if_fail|g_return_val_if_fail|g_assert|g_assert_ + +# Similarly for unreachable assertions. +lcov_excl_line = LCOV_EXCL_LINE|g_return_if_reached|g_return_val_if_reached|g_assert_not_reached diff --git a/.gitlab-ci/opensuse.Dockerfile b/.gitlab-ci/opensuse.Dockerfile new file mode 100644 index 0000000..0bcdfe4 --- /dev/null +++ b/.gitlab-ci/opensuse.Dockerfile @@ -0,0 +1,34 @@ +# Dockerfile to build container images for Gitlab Continuous Integration +# +# This starts with an openSUSE Tumbleweed image, and installs the dependencies +# for building and testing at-spi2-core. +# +# See README.md for documentation. + +FROM opensuse/tumbleweed:latest + +RUN zypper refresh \ + && zypper install -y \ + clang \ + clang-tools \ + findutils \ + gcc \ + dbus-1 \ + dbus-1-devel \ + gettext \ + git \ + glib2-devel \ + gobject-introspection-devel \ + gsettings-desktop-schemas \ + itstool \ + libasan6 \ + libxml2-devel \ + libxkbcommon-devel \ + libXi-devel \ + libXtst-devel \ + lcov \ + meson \ + ninja \ + python38 \ + python38-gobject \ + && zypper clean --all diff --git a/.gitlab-ci/run-docker.sh b/.gitlab-ci/run-docker.sh new file mode 100755 index 0000000..d6be467 --- /dev/null +++ b/.gitlab-ci/run-docker.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +read_arg() { + # $1 = arg name + # $2 = arg value + # $3 = arg parameter + local rematch='^[^=]*=(.*)$' + if [[ $2 =~ $rematch ]]; then + read -r "$1" <<< "${BASH_REMATCH[1]}" + else + read -r "$1" <<< "$3" + # There is no way to shift our callers args, so + # return 1 to indicate they should do it instead. + return 1 + fi +} + +SUDO_CMD="sudo" +if docker -v |& grep -q podman; then + # Using podman + SUDO_CMD="" + # Docker is actually implemented by podman, and its OCI output + # is incompatible with some of the dockerd instances on GitLab + # CI runners. + export BUILDAH_FORMAT=docker +fi + +set -e + +base="" +base_version="" +build=0 +run=0 +push=0 +list=0 +print_help=0 +no_login=0 + +while (($# > 0)); do + case "${1%%=*}" in + build) build=1;; + run) run=1;; + push) push=1;; + list) list=1;; + help) print_help=1;; + --base|-b) read_arg base "$@" || shift;; + --base-version) read_arg base_version "$@" || shift;; + --no-login) no_login=1;; + *) echo -e "\\e[1;31mERROR\\e[0m: Unknown option '$1'"; exit 1;; + esac + shift +done + +if [ $print_help == 1 ]; then + echo "$0 - Build and run Docker images" + echo "" + echo "Usage: $0 [options] [basename]" + echo "" + echo "Available commands" + echo "" + echo " build --base= - Build Docker image .Dockerfile" + echo " run --base= - Run Docker image " + echo " push --base= - Push Docker image to the registry" + echo " list - List available images" + echo " help - This help message" + echo "" + exit 0 +fi + +cd "$(dirname "$0")" + +if [ $list == 1 ]; then + echo "Available Docker images:" + for f in *.Dockerfile; do + filename=$( basename -- "$f" ) + basename="${filename%.*}" + + echo -e " \\e[1;39m$basename\\e[0m" + done + exit 0 +fi + +# All commands after this require --base to be set +if [ -z "${base}" ]; then + echo "Usage: $0 " + echo "Or use \"$0 help\" for a list of commands" + exit 1 +fi + +if [ ! -f "$base.Dockerfile" ]; then + echo -e "\\e[1;31mERROR\\e[0m: Dockerfile for '$base' not found" + exit 1 +fi + +if [ -z "${base_version}" ]; then + base_version="latest" +else + base_version="v$base_version" +fi + +TAG="registry.gitlab.gnome.org/gnome/at-spi2-core/${base}:${base_version}" + +if [ $build == 1 ]; then + echo -e "\\e[1;32mBUILDING\\e[0m: ${base} as ${TAG}" + $SUDO_CMD docker build \ + --tag "${TAG}" \ + --file "${base}.Dockerfile" . + exit $? +fi + +if [ $push == 1 ]; then + echo -e "\\e[1;32mPUSHING\\e[0m: ${base} as ${TAG}" + + if [ $no_login == 0 ]; then + $SUDO_CMD docker login registry.gitlab.gnome.org + fi + + $SUDO_CMD docker push $TAG + exit $? +fi + +if [ $run == 1 ]; then + echo -e "\\e[1;32mRUNNING\\e[0m: ${base} as ${TAG}" + $SUDO_CMD docker run \ + --rm \ + --volume "$(pwd)/..:/home/user/app" \ + --workdir "/home/user/app" \ + --tty \ + --interactive "${TAG}" \ + bash + exit $? +fi diff --git a/.gitlab-ci/run-style-check.sh b/.gitlab-ci/run-style-check.sh new file mode 100755 index 0000000..9d741bc --- /dev/null +++ b/.gitlab-ci/run-style-check.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +ancestor_horizon=28 # days (4 weeks) + +# Wrap everything in a subshell so we can propagate the exit status. +( + +source .gitlab-ci/search-common-ancestor.sh + +git diff -U0 --no-color "${newest_common_ancestor_sha}" atspi/*.c bus/*.c dbind/*.c registryd/*.c test/*.c | clang-format-diff -p1 > format-diff.log + +) +exit_status=$? + +[ ${exit_status} == 0 ] || exit ${exit_status} + +format_diff="$( format.log + cat format-diff.log >> format.log + echo '```' >> format.log + [ -n "$CI_MERGE_REQUEST_IID" ] && curl \ + --request POST \ + --header "Private-Token: $STYLE_CHECK_TOKEN" \ + --data-urlencode "$(/dev/null 2>&1 ; then + git remote add upstream https://gitlab.gnome.org/GNOME/${CI_PROJECT_NAME}.git +fi +git fetch --shallow-since="$(date --date="${ancestor_horizon} days ago" +%Y-%m-%d)" upstream + +# Work out the newest common ancestor between the detached HEAD that this CI job +# has checked out, and the upstream target branch (which will typically be +# `upstream/main` or `upstream/gnome-40`). +# `${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}` or `${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}` +# are only defined if we’re running in a merge request pipeline, +# fall back to `${CI_DEFAULT_BRANCH}` or `${CI_COMMIT_BRANCH}` respectively +# otherwise. + +source_branch="${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME:-${CI_COMMIT_BRANCH}}" +git fetch --shallow-since="$(date --date="${ancestor_horizon} days ago" +%Y-%m-%d)" origin "${source_branch}" + +newest_common_ancestor_sha=$(diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "upstream/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-${CI_DEFAULT_BRANCH}}") <(git rev-list --first-parent "origin/${source_branch}") | head -1) +if [ -z "${newest_common_ancestor_sha}" ]; then + echo "Couldn’t find common ancestor with upstream main branch. This typically" + echo "happens if you branched from main a long time ago. Please update" + echo "your clone, rebase, and re-push your branch." + exit 1 +fi diff --git a/NEWS b/NEWS index 6738a3d..b4c1230 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,32 @@ +What's new in at-spi2-core 2.44.1: + +* Fix use after free when removing a hung process. + +* Fix the build with X11 disabled. + +* Fix crash when NULL is passed to some listener-related functions. + +* impl_deregister_keystroke_listener: fix memory leak on iteration error. + +What's new in at-spi2-core 2.44.0: + +* Unlink the socket before binding when using dbus-broker. Fixes regression + introduced in 2.43.92 where restarting the bus launcher would fail. + +What's new in at-spi2-core 2.43.92: + +* The AT-SPI bus now uses the user's XDG_RUNTIME_DIR for its socket. +Fixes accessibility for Snap-confined applications. + +* Caps lock is now unlocked for key synthesis. Fixes cutting and +pasting from brltty when caps lock is on. + +* Several fixes to the dbus specification. + +* Fix the build when x11 is disabled. + +* Fix several compiler warnings. + What's new in at-spi2-core 2.42.0: * Set X root property when Xwayland starts on demand. diff --git a/README b/README deleted file mode 100644 index b377fca..0000000 --- a/README +++ /dev/null @@ -1,112 +0,0 @@ -D-Bus AT-SPI ------------- - -This version of at-spi is a major break from version 1.x. -It has been completely rewritten to use D-Bus rather than -ORBIT / CORBA for its transport protocol. - -An outdated page including instructions for testing, project status and -TODO items is at: - - https://wiki.linuxfoundation.org/accessibility/atk/at-spi/at-spi_on_d-bus - -The mailing list used for general questions is: - - https://lists.linuxfoundation.org/mailman/listinfo/accessibility-atspi - -For bug reports, feature requests, patches or enhancements please use: - - https://gitlab.gnome.org/GNOME/at-spi2-core/ - -A git repository with the latest development code is available at: - - https://gitlab.gnome.org/GNOME/at-spi2-core/ - -More information ----------------- - -The project was started with a D-Bus performance review -the results of which are available on the GNOME wiki. Keep in -mind that the D-Bus AT-SPI design documents on this page -have not been kept up to date. - - https://wiki.gnome.org/Accessibility/Documentation/GNOME2/ATSPI2-Investigation - -Other sources of relevant information about AT-SPI and Accessibility -include: - - https://wiki.gnome.org/Accessibility - https://community.kde.org/Accessibility - - -Contents of this package ------------------------- - -This package includes the protocol definitions for the new D-Bus -at-spi. - -Also included is the daemon necessary for forwarding device events -and registering accessible applicaitions. - - -Directory structure -------------------- - -The directories within this package are arranged as follows: - - xml - - This directory contains XML documents describing - the D-Bus protocol in the format used for D-Bus introspection. - - idl - - The D-Bus specification in an idl-like format. This is likely not - parseable by any existing tools, is not entirely up-to-date, and may - by removed in a future release. - - registryd - - The registry daemon code. The registry daemon - keeps a register of accessible applications and presents - this to clients (ATs). - It is also responsible for delivering device events. - - dbind - - Library to ease making D-Bus method calls, contains - marshalling code to convert function arguments - and a provided D-Bus signature into a D-Bus message. - - Used by libatspi. - - atspi - - C library for use by ATs. Wraps the various D-Bus calls, provides - an interface for listening to events, and caches some information about - accessible objects. Also contains some functions used by at-spi2-atk. - - bus - - A server that sits on the session bus and provides an interface - allowing applications to find the accessibility bus daemon, launching - it as needed. The accessibility bus is separate from the session bus - because it may in fact span user sessions if a user, for instance, - runs an application that escalates to run as root. The accessibility - bus is thus tied to the X session rather than the D-Bus session. - - doc - - Contains infrastructure for creating libatspi documentation. - - test - - Contains files that may be useful for testing AT-SPI. - - m4 - - Some macros used for building the module. - - po - - Infrastructure used for translation. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6085fa8 --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# Assistive Technology Service Provider Interface (AT-SPI) + +This repository contains the [DBus][DBus] interface definitions for AT-SPI, the Assistive +Technology Service Provider Interface — the core of an accessibility stack for free +software systems. It also contains the basic daemons of the accessibility stack. + +The version control repository and bug tracker are at https://gitlab.gnome.org/GNOME/at-spi2-core/ + +The code in this repository is not intended for application programmers. To write +accessible applications, look into [ATK][ATK] or your programming language's bindings for +the `xml` DBus interfaces mentioned below. + +While this module started within the [GNOME][GNOME] project's umbrella, it is not used +only in GNOME. Other sources of relevant information about AT-SPI and Accessibility +include: + +* [GNOME Accessibility wiki][gnome-a11y-wiki] +* [KDE Accessibility wiki][kde-a11y-wiki] +* [Accessibility documentation for GNOME users][docs-users] + + +## Summary of this repository's contents + +* `xml` - DBus interfaces for accessibility, described in DBus's XML introspection format. + Ideally, your programming language's implementation of DBus makes use of these files to + generate callable bindings. + +* `bus` - Launcher for the session's accessibility bus; see its [README.md](bus/README.md) + for details. + +* `registryd` - Daemon that keeps track of accessible applications in the user's session, + and lets them talk to each other and to assistive technologies (ATs) like screen + readers. + +* `atspi` - Hand-written binding for the `xml` DBus interface above, for use from C with + [GObject][GObject]. This is not normally what you would use; use a language-specific + binding instead. This module is for use mainly by [`at-spi2-atk`][at-spi2-atk]. + +* `dbind` - DBus utilities for use by `atspi` above. `atspi` was written before the more + modern C bindings like [GDBusConnection][GDBus] were available, so there is a lot of + hand-written IPC here. + +* Documentation for the Gitlab [Continuous Integration pipeline](devel-docs/gitlab-ci.md). + +## Historical note + +Versions 1.x of AT-SPI were based on [CORBA][CORBA] for inter-process communication (IPC), +using GNOME's ORBit implementation thereof. During the GNOME 2 and 3 release series, +CORBA was phased out in favor of [DBus][DBus], a more modern IPC mechanism. + +The original CORBA interfaces for AT-SPI were based on Java's implementation of +accessibility. Later, these CORBA interfaces were translated to DBus. This is why the +interfaces sometimes have a 1990s feeling to them. + +The project was started with a D-Bus performance review, the results of which are available +on the GNOME wiki. Keep in mind that the D-Bus AT-SPI design documents on this page have +not been kept up to date. + + https://wiki.gnome.org/Accessibility/Documentation/GNOME2/ATSPI2-Investigation + + +[CORBA]: https://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture +[DBus]: https://www.freedesktop.org/wiki/Software/dbus/ +[GObject]: https://docs.gtk.org/gobject/ +[at-spi2-atk]: https://gitlab.gnome.org/GNOME/at-spi2-atk +[GDBus]: https://docs.gtk.org/gio/class.DBusConnection.html +[ATK]: https://gitlab.gnome.org/GNOME/atk/ +[GNOME]: https://www.gnome.org +[docs-users]: https://help.gnome.org/users/gnome-help/stable/a11y.html +[gnome-a11y-wiki]: https://wiki.gnome.org/Accessibility +[kde-a11y-wiki]: https://community.kde.org/Accessibility diff --git a/at-spi2-core.doap b/at-spi2-core.doap index 85d64df..7c1cde3 100644 --- a/at-spi2-core.doap +++ b/at-spi2-core.doap @@ -29,4 +29,11 @@ wrapper around the DBus interfaces. mgorse + + + Federico Mena Quintero + + federico + + diff --git a/atspi/atspi-device-legacy.c b/atspi/atspi-device-legacy.c index eb67768..bfb63d4 100644 --- a/atspi/atspi-device-legacy.c +++ b/atspi/atspi-device-legacy.c @@ -212,8 +212,8 @@ atspi_device_legacy_unmap_modifier (AtspiDevice *device, gint keycode) AtspiLegacyKeyModifier *entry = l->data; if (entry->keycode == keycode) { - g_free (entry); priv->modifiers = g_slist_remove (priv->modifiers, entry); + g_free (entry); return; } } diff --git a/atspi/atspi-device-listener.c b/atspi/atspi-device-listener.c index 69f77d1..9776ebd 100644 --- a/atspi/atspi-device-listener.c +++ b/atspi/atspi-device-listener.c @@ -53,7 +53,7 @@ device_event_handler_new (AtspiDeviceListenerCB callback, } static gboolean -device_remove_datum (const AtspiDeviceEvent *event, void *user_data) +device_remove_datum (AtspiDeviceEvent *event, void *user_data) { AtspiDeviceListenerSimpleCB cb = user_data; return cb (event); diff --git a/atspi/atspi-device-x11.c b/atspi/atspi-device-x11.c index 4f88e60..eafdba6 100644 --- a/atspi/atspi-device-x11.c +++ b/atspi/atspi-device-x11.c @@ -516,8 +516,8 @@ atspi_device_x11_unmap_modifier (AtspiDevice *device, gint keycode) AtspiX11KeyModifier *entry = l->data; if (entry->keycode == keycode) { - g_free (entry); priv->modifiers = g_slist_remove (priv->modifiers, entry); + g_free (entry); return; } } diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c index 5455b58..5cdc806 100644 --- a/atspi/atspi-event-listener.c +++ b/atspi/atspi-event-listener.c @@ -1000,6 +1000,13 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data) } category++; } + else + { + // TODO: Error + // Note that the single caller of this function, process_deferred_message(), ignores the return value. + // We should probably free the message if we aren't going to process it after all. + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } dbus_message_iter_get_basic (&iter, &detail); dbus_message_iter_next (&iter); dbus_message_iter_get_basic (&iter, &detail1); @@ -1087,6 +1094,8 @@ _atspi_dbus_handle_event (DBusConnection *bus, DBusMessage *message, void *data) break; } + g_assert (e.source != NULL); + dbus_message_iter_next (&iter); if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY) { diff --git a/atspi/atspi-misc.c b/atspi/atspi-misc.c index 7af46e9..d896afb 100644 --- a/atspi/atspi-misc.c +++ b/atspi/atspi-misc.c @@ -877,43 +877,6 @@ atspi_dbus_filter (DBusConnection *bus, DBusMessage *message, void *data) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -/* - * Returns a 'canonicalized' value for DISPLAY, - * with the screen number stripped off if present. - * - * TODO: Avoid having duplicate functions for this here and in at-spi2-atk - */ -static gchar * -spi_display_name (void) -{ - char *canonical_display_name = NULL; - const gchar *display_env = g_getenv ("AT_SPI_DISPLAY"); - - if (!display_env) - { - display_env = g_getenv ("DISPLAY"); - if (!display_env || !display_env[0]) - return NULL; - else - { - gchar *display_p, *screen_p; - canonical_display_name = g_strdup (display_env); - display_p = g_utf8_strrchr (canonical_display_name, -1, ':'); - screen_p = g_utf8_strrchr (canonical_display_name, -1, '.'); - if (screen_p && display_p && (screen_p > display_p)) - { - *screen_p = '\0'; - } - } - } - else - { - canonical_display_name = g_strdup (display_env); - } - - return canonical_display_name; -} - /** * atspi_init: * @@ -1499,6 +1462,43 @@ _atspi_error_quark (void) * Gets the IOR from the XDisplay. */ #ifdef HAVE_X11 +/* + * Returns a 'canonicalized' value for DISPLAY, + * with the screen number stripped off if present. + * + * TODO: Avoid having duplicate functions for this here and in at-spi2-atk + */ +static gchar * +spi_display_name (void) +{ + char *canonical_display_name = NULL; + const gchar *display_env = g_getenv ("AT_SPI_DISPLAY"); + + if (!display_env) + { + display_env = g_getenv ("DISPLAY"); + if (!display_env || !display_env[0]) + return NULL; + else + { + gchar *display_p, *screen_p; + canonical_display_name = g_strdup (display_env); + display_p = g_utf8_strrchr (canonical_display_name, -1, ':'); + screen_p = g_utf8_strrchr (canonical_display_name, -1, '.'); + if (screen_p && display_p && (screen_p > display_p)) + { + *screen_p = '\0'; + } + } + } + else + { + canonical_display_name = g_strdup (display_env); + } + + return canonical_display_name; +} + static char * get_accessibility_bus_address_x11 (void) { diff --git a/atspi/atspi-registry.c b/atspi/atspi-registry.c index dea5878..382308f 100644 --- a/atspi/atspi-registry.c +++ b/atspi/atspi-registry.c @@ -270,19 +270,22 @@ atspi_deregister_keystroke_listener (AtspiDeviceListener *listener, GError **error) { GArray *d_key_set; - gchar *path = _atspi_device_listener_get_path (listener); + gchar *path; gint i; dbus_uint32_t d_modmask = modmask; dbus_uint32_t d_event_types = event_types; DBusError d_error; GList *l; - dbus_error_init (&d_error); if (!listener) { return FALSE; } + dbus_error_init (&d_error); + + path = _atspi_device_listener_get_path (listener); + /* copy the keyval filter values from the C api into the DBind KeySet */ if (key_set) { @@ -363,15 +366,18 @@ atspi_register_device_event_listener (AtspiDeviceListener *listener, { gboolean retval = FALSE; dbus_uint32_t d_event_types = event_types; - gchar *path = _atspi_device_listener_get_path (listener); + gchar *path; DBusError d_error; - dbus_error_init (&d_error); if (!listener) { return retval; } + dbus_error_init (&d_error); + + path = _atspi_device_listener_get_path (listener); + dbind_method_call_reentrant (_atspi_bus(), atspi_bus_registry, atspi_path_dec, atspi_interface_dec, "RegisterDeviceEventListener", &d_error, "ou=>b", path, d_event_types, &retval); if (dbus_error_is_set (&d_error)) { @@ -400,16 +406,18 @@ atspi_deregister_device_event_listener (AtspiDeviceListener *listener, void *filter, GError **error) { dbus_uint32_t event_types = 0; - gchar *path = _atspi_device_listener_get_path (listener); + gchar *path; DBusError d_error; - dbus_error_init (&d_error); - if (!listener) { return FALSE; } + dbus_error_init (&d_error); + + path = _atspi_device_listener_get_path (listener); + event_types |= (1 << ATSPI_BUTTON_PRESSED_EVENT); event_types |= (1 << ATSPI_BUTTON_RELEASED_EVENT); diff --git a/bus/README b/bus/README deleted file mode 100644 index 40b9ad6..0000000 --- a/bus/README +++ /dev/null @@ -1,10 +0,0 @@ -The a11y bus is accessed via two mechanisms: - -1) The DBus session bus, service "org.a11y.Bus", method "GetAddress") -2) The X11 root window property AT_SPI_BUS - -If the "toolkit-accessibility" variable is set, the bus is launched -immediately (and will be accessible immediately via the X11 property). -Otherwise, it will be spawned dynamically. - - diff --git a/bus/README.md b/bus/README.md new file mode 100644 index 0000000..eb14e9f --- /dev/null +++ b/bus/README.md @@ -0,0 +1,98 @@ +# Launcher for the accessibility bus + +The communications mechanism for accessibility does not run through the user's session +DBus; it runs in a separate bus just for accessibility purposes. The accessibility +interfaces for DBus are very chatty; using a separate bus prevents the main session bus +from getting too much traffic. + +Throughout this document we will distinguish between the **session bus** and the +**accessibility bus**. + +## Who launches the accessibility bus? + +This source directory `bus` contains a little daemon, `at-spi-bus-launcher`, which +launches the **accessibility bus** and manages its lifetime according to the user's +session. + +The **accessibility bus** is just a separate instance of `dbus-daemon` or equivalent, like +`dbus-broker`. That bus allows communication using the accessibility interfaces defined +in the `xml` directory in this repository. It also has the accessibility registry — +`registryd` in this repository, which claims the name `org.a11y.atspi.Registry` in that +bus. + +## When does the accessibility bus get launched? + +When a normal application starts up, it will want to find the **accesibility bus**, and +then contact the accessibility registry in that bus (`registryd` in this repository) to +inform the world that they are up and running. Finding the accessibility bus can then be +done on demand for normal applications, via the `GetAddress` method described below. + +However, a screen reader is special: it needs to start up automatically during login, and +immediatelly tell the accessibility registry (... via the **accessibility bus**) that it +is running. If you need a screen reader to use your computer, you cannot easily launch it +by hand if there is no screen reader already running! + +That is, if a screen reader is turned on — and we assume it will start up turned on for +future sessions — we need to launch the **accessibility bus** unconditionally, not on +demand, at session startup. This is why `at-spi-dbus-bus.desktop`, described below, is an +[XDG autostart][xdg-autostart] file which runs `at-spi-bus-launcher --launch-immediately`, +but only if a certain GSettings key is turned on. + +In summary, `at-spi-bus-launcher` will launch the **accessibility bus** under two situations: + +* On demand via the `GetAddress` method; see below. + +* Shortly after `at-spi-bus-launcher` starts up, if the gsettings key + `org.gnome.desktop.interface toolkit-accessibility` is set to true, due to the + `at-spi-dbus-bus.desktop` XDG autostart file. + +* The gsettings key `org.gnome.desktop.a11y.applications screen-reader-enabled` is set to true. + +## Contents of this `bus` directory + +This `bus` source directory has a configuration file for the `dbus-daemon` which will run +as the **accessibility bus**, and a helper daemon called `at-spi-bus-launcher`, which actually +starts that bus and makes its address visible to the user's session. The files are as follows: + +* `accessibility.conf.in` - template for the configuration for the accessibility bus, + which gets installed in `$(datadir)/defaults/at-spi2/accessibility.conf`. + +* `at-spi-bus-launcher.c` - See [`at-spi-bus-launcher`](#at-spi-bus-launcher) below. + +* `at-spi-dbus-bus.service.in` - template for a systemd user service to start `at-spi-bus-launcher`. + +* `org.a11y.Bus.service.in` - template for a DBus user service to start `at-spi-bus-launcher`. + +* `at-spi-dbus-bus.desktop.in` - template for a XDG autostart file to start + `at-spi-bus-launcher` at session startup, only if the `org.gnome.desktop.interface + toolkit-accessibility` GSettings key is turned on. + +* `00-at-spi` - script to set the `AT_SPI_BUS` property on the X root window, for + Wayland-based systems where XWayland is started on demand. That X window property is an + alternative way of finding the address of the **accessibility bus**. + +## at-spi-bus-launcher + +This is a tiny daemon which registers a service in the normal **session bus**, and which +can then be used to query the address of the actual **accessibility bus**. The daemon +claims ownership of the `org.a11y.Bus` name in the **session bus**, and exports a +single object, `/org/a11y/bus`, with two interfaces: + +* `org.a11y.Bus` - has a single `GetAddress` method, which returns the address of the + actual **accessibility bus**. Accessibility clients must use this address when creating + their initial DBus connection. + +* `org.a11y.Status` - has properties to query whether the **accessibility bus** is enabled + and whether a screen reader is running. + +`at-spi-bus-launcher` starts the separate `dbus-daemon` (or `dbus-broker` equivalent) for +the **accessibility bus** on demand. The following actions can cause it to launch: + +* Calling the `GetAddress` method. The daemon gets launched and queried for its address; + the method returns that. This is the normal way to start the accessibility bus. + +* If `at-spi-bus-launcher` was run with the `--launch-immediately` option, the + accessibility bus launches as soon as `at-spi-bus-launcher` is able to claim ownership + of the `org.a11y.Bus` name in the session bus. This is intended for session startup. + +[xdg-autostart]: https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html diff --git a/bus/at-spi-bus-launcher.c b/bus/at-spi-bus-launcher.c index d7c6690..0633c30 100644 --- a/bus/at-spi-bus-launcher.c +++ b/bus/at-spi-bus-launcher.c @@ -42,6 +42,7 @@ #ifdef DBUS_BROKER #include #endif +#include typedef enum { A11Y_BUS_STATE_IDLE = 0, @@ -64,7 +65,8 @@ typedef struct { A11yBusState state; /* -1 == error, 0 == pending, > 0 == running */ - int a11y_bus_pid; + GPid a11y_bus_pid; + char *socket_name; char *a11y_bus_address; #ifdef HAVE_X11 gboolean x11_prop_set; @@ -84,10 +86,10 @@ static const gchar introspection_xml[] = " " " " " " - "" - "" - "" - "" + " " + " " + " " + " " ""; static GDBusNodeInfo *introspection_data = NULL; @@ -248,19 +250,44 @@ name_appeared_handler (GDBusConnection *connection, * Read all data from a file descriptor to a C string buffer. */ static gboolean -unix_read_all_fd_to_string (int fd, - char *buf, - ssize_t max_bytes) +unix_read_all_fd_to_string (int fd, + char *buf, + ssize_t max_bytes, + char **error_msg) { - ssize_t bytes_read; + g_assert (max_bytes > 1); + *error_msg = NULL; + + max_bytes -= 1; /* allow space for nul terminator */ - while (max_bytes > 1 && (bytes_read = read (fd, buf, MIN (4096, max_bytes - 1)))) + while (max_bytes > 1) { - if (bytes_read < 0) - return FALSE; - buf += bytes_read; - max_bytes -= bytes_read; + ssize_t bytes_read; + + again: + bytes_read = read (fd, buf, max_bytes); + + if (bytes_read == 0) + { + break; + } + else if (bytes_read > 0) + { + buf += bytes_read; + max_bytes -= bytes_read; + } + else if (errno == EINTR) + { + goto again; + } + else + { + int err_save = errno; + *error_msg = g_strdup_printf ("Failed to read data from accessibility bus: %s", g_strerror (err_save)); + return FALSE; + } } + *buf = '\0'; return TRUE; } @@ -284,53 +311,69 @@ on_bus_exited (GPid pid, app->a11y_launch_error_message = g_strdup_printf ("Bus stopped by signal %d", WSTOPSIG (status)); } g_main_loop_quit (app->loop); -} +} #ifdef DBUS_DAEMON -static void -setup_bus_child_daemon (gpointer data) +static gboolean +ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path) { - A11yBusLauncher *app = data; - (void) app; + char *address_param; - close (app->pipefd[0]); - dup2 (app->pipefd[1], 3); - close (app->pipefd[1]); + if (app->socket_name) + { + gchar *escaped_address = g_dbus_address_escape_value (app->socket_name); + address_param = g_strconcat ("--address=unix:path=", escaped_address, NULL); + g_free (escaped_address); + } + else + { + address_param = NULL; + } - /* On Linux, tell the bus process to exit if this process goes away */ -#ifdef __linux__ - prctl (PR_SET_PDEATHSIG, 15); -#endif -} + if (pipe (app->pipefd) < 0) + g_error ("Failed to create pipe: %s", strerror (errno)); -static gboolean -ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path) -{ - char *argv[] = { DBUS_DAEMON, config_path, "--nofork", "--print-address", "3", NULL }; + char *print_address_fd_param = g_strdup_printf ("%d", app->pipefd[1]); + + char *argv[] = { DBUS_DAEMON, config_path, "--nofork", "--print-address", print_address_fd_param, address_param, NULL }; + gint source_fds[1] = { app->pipefd[1] }; + gint target_fds[1] = { app->pipefd[1] }; + G_STATIC_ASSERT (G_N_ELEMENTS (source_fds) == G_N_ELEMENTS (target_fds)); GPid pid; char addr_buf[2048]; GError *error = NULL; - - if (pipe (app->pipefd) < 0) - g_error ("Failed to create pipe: %s", strerror (errno)); + char *error_from_read; g_clear_pointer (&app->a11y_launch_error_message, g_free); - if (!g_spawn_async (NULL, - argv, - NULL, - G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, - setup_bus_child_daemon, - app, - &pid, - &error)) + if (!g_spawn_async_with_pipes_and_fds (NULL, + (const gchar * const *) argv, + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, + NULL, /* child_setup */ + app, + -1, /* stdin_fd */ + -1, /* stdout_fd */ + -1, /* stdout_fd */ + source_fds, + target_fds, + G_N_ELEMENTS (source_fds), /* n_fds in source_fds and target_fds */ + &pid, + NULL, /* stdin_pipe_out */ + NULL, /* stdout_pipe_out */ + NULL, /* stderr_pipe_out */ + &error)) { app->a11y_bus_pid = -1; app->a11y_launch_error_message = g_strdup (error->message); g_clear_error (&error); + g_free (address_param); + g_free (print_address_fd_param); goto error; } + g_free (address_param); + g_free (print_address_fd_param); close (app->pipefd[1]); app->pipefd[1] = -1; @@ -339,10 +382,12 @@ ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path) app->state = A11Y_BUS_STATE_READING_ADDRESS; app->a11y_bus_pid = pid; g_debug ("Launched a11y bus, child is %ld", (long) pid); - if (!unix_read_all_fd_to_string (app->pipefd[0], addr_buf, sizeof (addr_buf))) + error_from_read = NULL; + if (!unix_read_all_fd_to_string (app->pipefd[0], addr_buf, sizeof (addr_buf), &error_from_read)) { - app->a11y_launch_error_message = g_strdup_printf ("Failed to read address: %s", strerror (errno)); + app->a11y_launch_error_message = error_from_read; kill (app->a11y_bus_pid, SIGTERM); + g_spawn_close_pid (app->a11y_bus_pid); app->a11y_bus_pid = -1; goto error; } @@ -386,9 +431,6 @@ setup_bus_child_broker (gpointer data) pid_str = g_strdup_printf("%u", getpid()); g_setenv("LISTEN_PID", pid_str, TRUE); g_free(pid_str); - - /* Tell the bus process to exit if this process goes away */ - prctl (PR_SET_PDEATHSIG, SIGTERM); } static gboolean @@ -396,11 +438,17 @@ 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 }; + struct sockaddr_un addr = { .sun_family = AF_UNIX, "" }; socklen_t addr_len = sizeof(addr); GPid pid; GError *error = NULL; + if (app->socket_name) + { + strcpy (addr.sun_path, app->socket_name); + unlink (app->socket_name); + } + /* 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. @@ -418,10 +466,11 @@ ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path) if ((app->listenfd = socket (PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0) g_error ("Failed to create listening socket: %s", strerror (errno)); - if (bind (app->listenfd, (struct sockaddr *)&addr, sizeof(sa_family_t)) < 0) + if (bind (app->listenfd, (struct sockaddr *)&addr, addr_len) < 0) g_error ("Failed to bind listening socket: %s", strerror (errno)); - if (getsockname (app->listenfd, (struct sockaddr *)&addr, &addr_len) < 0) + if (!app->socket_name && + getsockname (app->listenfd, (struct sockaddr *)&addr, &addr_len) < 0) g_error ("Failed to get socket name for listening socket: %s", strerror(errno)); if (listen (app->listenfd, 1024) < 0) @@ -452,7 +501,10 @@ ensure_a11y_bus_broker (A11yBusLauncher *app, char *config_path) g_debug ("Launched a11y bus, child is %ld", (long) pid); app->state = A11Y_BUS_STATE_RUNNING; - app->a11y_bus_address = g_strconcat("unix:abstract=", addr.sun_path + 1, NULL); + if (app->socket_name) + app->a11y_bus_address = g_strconcat("unix:path=", addr.sun_path, NULL); + else + app->a11y_bus_address = g_strconcat("unix:abstract=", addr.sun_path + 1, NULL); g_debug ("a11y bus address: %s", app->a11y_bus_address); return TRUE; @@ -476,6 +528,7 @@ ensure_a11y_bus (A11yBusLauncher *app) { char *config_path = NULL; gboolean success = FALSE; + const gchar *xdg_runtime_dir; if (app->a11y_bus_pid != 0) return FALSE; @@ -485,6 +538,36 @@ ensure_a11y_bus (A11yBusLauncher *app) else config_path = "--config-file="DATADIR"/defaults/at-spi2/accessibility.conf"; + xdg_runtime_dir = g_get_user_runtime_dir (); + if (xdg_runtime_dir) + { + const gchar *display = g_getenv ("DISPLAY"); + gchar *at_spi_dir = g_strconcat (xdg_runtime_dir, "/at-spi", NULL); + gchar *p; + mkdir (xdg_runtime_dir, 0700); + if (!g_path_is_absolute (at_spi_dir)) + { + gchar *new_dir = g_canonicalize_filename (at_spi_dir, NULL); + g_free (at_spi_dir); + at_spi_dir = new_dir; + } + if (mkdir (at_spi_dir, 0700) == 0 || errno == EEXIST) + { + app->socket_name = g_strconcat (at_spi_dir, "/bus", display, NULL); + g_free (at_spi_dir); + p = strchr (app->socket_name, ':'); + if (p) + *p = '_'; + if (strlen (app->socket_name) >= 100) + { + g_free (app->socket_name); + app->socket_name = NULL; + } + } + else + g_free (at_spi_dir); + } + #ifdef WANT_DBUS_BROKER success = ensure_a11y_bus_broker (app, config_path); if (!success) @@ -816,6 +899,11 @@ get_schema (const gchar *name) { #if GLIB_CHECK_VERSION (2, 32, 0) GSettingsSchemaSource *source = g_settings_schema_source_get_default (); + if (!source) + { + g_error ("Cannot get the default GSettingsSchemaSource - is the gsettings-desktop-schemas package installed?"); + } + GSettingsSchema *schema = g_settings_schema_source_lookup (source, name, FALSE); if (schema == NULL) @@ -856,7 +944,7 @@ main (int argc, gboolean screen_reader_set = FALSE; gint i; - _global_app = g_slice_new0 (A11yBusLauncher); + _global_app = g_new0 (A11yBusLauncher, 1); _global_app->loop = g_main_loop_new (NULL, FALSE); for (i = 1; i < argc; i++) @@ -917,7 +1005,11 @@ main (int argc, g_main_loop_run (_global_app->loop); if (_global_app->a11y_bus_pid > 0) - kill (_global_app->a11y_bus_pid, SIGTERM); + { + kill (_global_app->a11y_bus_pid, SIGTERM); + g_spawn_close_pid (_global_app->a11y_bus_pid); + _global_app->a11y_bus_pid = -1; + } /* Clear the X property if our bus is gone; in the case where e.g. * GDM is launching a login on an X server it was using before, diff --git a/dbind/dbind-any.c b/dbind/dbind-any.c index efdba73..512c1b3 100644 --- a/dbind/dbind-any.c +++ b/dbind/dbind-any.c @@ -696,14 +696,8 @@ dbind_any_demarshal_va (DBusMessageIter *iter, { const char *p = *arg_types; - /* Pass in args */ + /* Just consume the in args without doing anything to them */ for (;*p != '\0' && *p != '=';) { - int intarg; - void *ptrarg; - double doublearg; - dbus_int64_t int64arg; - void *arg = NULL; - switch (*p) { case DBUS_TYPE_BYTE: case DBUS_TYPE_BOOLEAN: @@ -711,14 +705,14 @@ dbind_any_demarshal_va (DBusMessageIter *iter, case DBUS_TYPE_UINT16: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: - intarg = va_arg (args, int); + va_arg (args, int); break; case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: - int64arg = va_arg (args, dbus_int64_t); + va_arg (args, dbus_int64_t); break; case DBUS_TYPE_DOUBLE: - doublearg = va_arg (args, double); + va_arg (args, double); break; /* ptr types */ case DBUS_TYPE_STRING: @@ -726,18 +720,18 @@ dbind_any_demarshal_va (DBusMessageIter *iter, case DBUS_TYPE_SIGNATURE: case DBUS_TYPE_ARRAY: case DBUS_TYPE_DICT_ENTRY: - ptrarg = va_arg (args, void *); + va_arg (args, void *); break; case DBUS_STRUCT_BEGIN_CHAR: - ptrarg = va_arg (args, void *); + va_arg (args, void *); break; case DBUS_DICT_ENTRY_BEGIN_CHAR: - ptrarg = va_arg (args, void *); + va_arg (args, void *); break; case DBUS_TYPE_VARIANT: fprintf (stderr, "No variant support yet - very toolkit specific\n"); - ptrarg = va_arg (args, void *); + va_arg (args, void *); break; default: fprintf (stderr, "Unknown / invalid arg type %c\n", *p); diff --git a/dbind/dbtest.c b/dbind/dbtest.c index b338c03..511839c 100644 --- a/dbind/dbtest.c +++ b/dbind/dbtest.c @@ -40,7 +40,10 @@ void demarshal (DBusMessage *msg, const char *type, void *ptr) DBusMessageIter iter; if (!dbus_message_iter_init (msg, &iter)) + { fprintf (stderr, "no data in msg\n"); + g_assert_not_reached (); + } else dbind_any_demarshal (&iter, &type, &ptr); } @@ -293,8 +296,8 @@ void test_struct_with_array () demarshal (msg, TYPEOF_ARRAYSTRUCT, &a2); q = &g_array_index (a2, ArrayStruct, 0); - g_assert (p[0].pad1 == 2); - g_assert (g_array_index (p[1].vals, dbus_uint32_t, 1) == 1000000000); + g_assert (q[0].pad1 == 2); + g_assert (g_array_index (q[1].vals, dbus_uint32_t, 1) == 1000000000); printf ("struct with array ok\n"); diff --git a/devel-docs/gitlab-ci.md b/devel-docs/gitlab-ci.md new file mode 100644 index 0000000..d7d675e --- /dev/null +++ b/devel-docs/gitlab-ci.md @@ -0,0 +1,188 @@ +# Gitlab Continuous Integration (CI) for at-spi2-core + +Summary: make the robots set up an environment for running the test +suite, run it, and report back to us. + +If you have questions about the CI, mail federico@gnome.org, or [file +an issue](https://gitlab.gnome.org/GNOME/at-spi2-core/-/issues) and +mention `@federico` in it. + +Table of contents: + +[[_TOC_]] + +# Quick overview + +By having a [`.gitlab-ci.yml`](../.gitlab-ci.yml) file in the toplevel +directory of a project, Gitlab knows that it must run a continuous +integration pipeline when certain events occur, for example, when +someone creates a merge request, or pushes to a branch. + +What's a pipeline? It is an automated version of the following. +Running the test suite for at-spi2-core involves some repetitive +steps: + +* Create a pristine and minimal environment for testing, without all the random + gunk from one's development system. Gitlab CI uses Linux containers, + with pre-built operating system images in the [Open Container + Initiative][OCI] format — this is what Docker, Podman, etc. all use. + +* Install the build-time dependencies (gcc, meson, libfoo-devel, + etc.), the test-time dependencies (dbus-daemon, etc.) in that + pristine environment, as well as special tools (lcov, libasan, + clang-tools). + +* Run the build and install it, and run the test suite. + +* Run variations of the build and test suite with other tools — for + example, using static analysis during compilation, or with address + sanitizer (asan), or with a code coverage tool. Gitlab can collect + the analysis results of each of these tools and present them as part + of the merge request that is being evaluated. It also lets + developers obtain those useful results without dealing with a lot of + fiddly tools on their own computers. + +Additionally, on each pipeline run we'd like to do extra repetitive +work like building the reference documentation, and publishing it on a +web page. + +The `.gitlab-ci.yml` file defines the CI pipeline, the jobs it will +run (build/test, coverage, asan, static-scan, etc.), and the locations +where each job's artifacts will be stored. + +What's an artifact or a job? Read on! + +# A little glossary + +**Pipeline** - A collection of **jobs**, which can be run in parallel +or sequentially. For example, a pair of "build" and "test" jobs would +need to run sequentially, but maybe a "render documentation" job can +run in parallel with them. Similarly, "build" jobs for various +distributions or configurations could be run in parallel. + +**Job** - Think of it as running a shell script within a container. +It can have input from other previous jobs: if you have separate +"build" and "test" jobs, then the "build" job will want to keep around +its compiled artifacts so that the "test" job can use them. It can +provide output artifacts that can be stored for human perusal, or for +use by other jobs. + +**Artifact** - Something produced from a job. If your job compiles +binaries, those binaries could be artifacts if you decide to keep them +around for use later. A documentation job can produce HTML artifacts +from the rendered documentation. A code coverage job will produce a +coverage report artifact. + +**Runner** - An operating system setup for running jobs. +Gitlab.gnome.org provides runners for Linux, BSD, Windows, and MacOS. +For example. the Linux runners let you use any OCI image, so you can +test on openSUSE, Fedora, a custom distro, etc. You don't normally +need to be concerned with runners; Gitlab assigns the shared runners +automatically to your pipeline. + +**Container** - You can think of it as a chroot with extra isolation, +or a lightweight virtual machine. Basically, the Linux kernel can +isolate groups of processes in control groups (cgroups). Each cgroup +can have a different view of the file system, as if you had a +different chroot for each cgroup. Cgroups can be isolated to be in +their own PID namespace, so running "ps" in the container will not +show all the processes in the system, but only those inside the +container's cgroup. File system overlays allow you to have read-only +images for the operating system (the OCI images we talked about above) +plus a read-write overlay that is kept around only during the lifetime +of a container, or persistently if one wants. For Gitlab CI one does +not need to deal with containers directly, but keep in mind that your +jobs will run inside a container, which is more limited than e.g. a +shell session on a graphical, development machine. + +# The CI pipeline for at-spi2-core + +The `.gitlab-ci.yml` file is a more-or-less declarative description +the CI pipeline, with some `script` sections which are imperative +commands to actually *do stuff*. + +Jobs are run in `stages`, and the names of the stages are declared +near the beginning of the YAML file. The stage names are arbitrary; +the ones here follow some informal GNOME conventions. + +Jobs are declared at the toplevel of the YAML file, and they are +distinguished from other declarations by having a container `image` +declared for them, as well as a `script` to execute. + +Many jobs need exactly the same kind of setup (same container images, +mostly same package dependencies), so they use `extends:` to use a +declared template with all that stuff instead of redeclaring it each +time. In our configuration, the `.only-default` template has the +`PROJECT_DEPS`, with the dependencies that most jobs need. The +`.build-setup` template is for the analysis jobs, and it lets them +declare `EXTRA_DEPS` as an environment variable with the names of +extra dependencies: for example, the coverage job puts `lcov` in +`EXTRA_DEPS`. The commands in `before_script` blocks use these +environment variables to install the package dependencies, for example +`zypper install -y ${PROJECT_DEPS}` for an openSUSE job. + +The `build` stage has these jobs: + +* `opensuse-x86_64` - Extends the `.build-default` rule, + builds/installs the code, and runs the tests. Generally this is the + job that one cares about during regular development. + +The `analysis` stage has these jobs: + +* `static-scan` - Runs static analysis during compilation, which + performs interprocedural analysis to detect things like double + `free()` or uninitialized struct fields across functions. + +* `asan-build` - Builds and runs with Address Sanitizer (libasan). + +* `coverage` - Instruments the build to get code coverage information, + and runs the test suite to see how much of the code it manages to + exercise. This is to see which code paths may be untested + automatically, and to decide which ones would require manual + testing, or refactoring to allow automated testing. + +As of 2021/Dec/15 there are some commented-out jobs to generate +documentation and publish it; this needs to be made to work. + +# General advice and future work + +A failed run of a CI pipeline should trouble you; it either means that +some test broke, or that something is not completely deterministic. +Fix it at once. + +Try not to accept merge requests that fail the CI, as this will make +`git bisect` hard in the future. There are tools like Marge-bot to +enforce this; ask federico@gnome.org about it. Read ["The Not Rocket +Science Rule Of Software +Engineering"](https://graydon.livejournal.com/186550.html), which can +be summarized as "automatically maintain a repository of code that +always passes all the tests" for inspiration. Marge-bot is an +implementation of that, and can be used with Gitlab. + +If your software can be configured to build with substantial changes, +the CI pipeline should have jobs that test each of those +configurations. For example, at-spi-bus-launcher operates differently +depending on whether dbus-daemon or dbus-broker are being used. As of +2021/Dec/15 the CI only tests dbus-daemon; there should be a test for +dbus-broker, too. + +Although the YAML syntax for `.gitlab-ci.yml` is a bit magic, the +scripts and configuration are quite amenable to refactoring. Do it +often! + +Minimizing the amount of time that CI takes to run is a good goal. It +reduces energy consumption in the build farm, and allows you to have a +faster feedback loop. Instead of installing package dependencies on +each job, we can move to prebuilt container images. + +# References + +Full documentation for Gitlab CI: https://docs.gitlab.com/ee/ci/ + +Introduction to Gitlab CI: https://docs.gitlab.com/ee/ci/quick_start/index.html + +at-spi2-core's CI pipeline is mostly [cut-and-pasted from +libgweather](https://gitlab.gnome.org/GNOME/libgweather/-/blob/main/.gitlab-ci.yml). +Thanks to Emmanuele Bassi for his advice on how to use it. + +[OCI]: https://opencontainers.org/ diff --git a/meson.build b/meson.build index b5104c8..c566b25 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('at-spi2-core', 'c', - version: '2.42.0', + version: '2.44.1', license: 'LGPLv2.1+', default_options: [ 'buildtype=debugoptimized', @@ -47,6 +47,7 @@ libdbus_req_version = '>= 1.5' glib_req_version = '>= 2.62.0' gobject_req_version = '>= 2.0.0' gio_req_version = '>= 2.28.0' +gir_req_version = '>= 0.6.7' libdbus_dep = dependency('dbus-1', version: libdbus_req_version) glib_dep = dependency('glib-2.0', version: glib_req_version) @@ -62,6 +63,8 @@ endif x11_deps = [] x11_option = get_option('x11') +# ensure x11_dep is defined for use elsewhere, such as bus/meson.build +x11_dep = dependency('', required: false) if x11_option != 'no' x11_dep = dependency('x11', required: false) @@ -104,7 +107,7 @@ have_gir = false introspection_option = get_option('introspection') if introspection_option != 'no' - gir_dep = dependency('gobject-introspection-1.0', version: '>= 0.6.7', required: false) + gir_dep = dependency('gobject-introspection-1.0', version: gir_req_version, required: false) if gir_dep.found() have_gir = true diff --git a/po/LINGUAS b/po/LINGUAS index 328b6c4..d590968 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -31,6 +31,7 @@ hi hr hu id +is it ja kk diff --git a/po/is.po b/po/is.po new file mode 100644 index 0000000..61246a7 --- /dev/null +++ b/po/is.po @@ -0,0 +1,26 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Sveinn í Felli , 2021. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/at-spi2-core/issues\n" +"POT-Creation-Date: 2021-12-08 22:05+0000\n" +"PO-Revision-Date: 2021-12-15 23:05+0000\n" +"Last-Translator: Sveinn í Felli \n" +"Language-Team: Icelandic \n" +"Language: is\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: Lokalize 19.12.3\n" + +#: atspi/atspi-component.c:332 atspi/atspi-misc.c:1105 atspi/atspi-value.c:111 +msgid "The application no longer exists" +msgstr "Forritið er ekki lengur til" + +#: atspi/atspi-misc.c:1888 +msgid "Attempted synchronous call where prohibited" +msgstr "Reyndi samhæft kall þegar slíkt er bannað" diff --git a/registryd/deviceeventcontroller-x11.c b/registryd/deviceeventcontroller-x11.c index 55239f4..6556048 100644 --- a/registryd/deviceeventcontroller-x11.c +++ b/registryd/deviceeventcontroller-x11.c @@ -34,11 +34,6 @@ #include #include -#include -#include -#include -#include - #define XK_MISCELLANY #define XK_LATIN1 #include @@ -54,6 +49,7 @@ #include "display.h" #include "event-source.h" +#include "deviceeventcontroller-x11.h" #include "deviceeventcontroller.h" #include "reentrant-list.h" @@ -89,31 +85,26 @@ static XModifierKeymap* xmkeymap = NULL; static int (*x_default_error_handler) (Display *display, XErrorEvent *error_event); -typedef struct { - Display *xevie_display; - unsigned int last_press_keycode; - unsigned int last_release_keycode; - struct timeval last_press_time; - struct timeval last_release_time; - int have_xkb; - int xkb_major_extension_opcode; - int xkb_base_event_code; - int xkb_base_error_code; - unsigned int xkb_latch_mask; - unsigned int pending_xkb_mod_relatch_mask; - XkbDescPtr xkb_desc; - KeyCode reserved_keycode; - KeySym reserved_keysym; - guint reserved_reset_timeout; -} DEControllerPrivateData; - static void spi_controller_register_with_devices (SpiDEController *controller); static gboolean spi_device_event_controller_forward_key_event (SpiDEController *controller, const XEvent *event); - static SpiDEController *saved_controller; +/* Normally this function would be provided by the macro call in deviceeventcontroller.c: + * G_DEFINE_TYPE_WITH_CODE (..., G_ADD_PRIVATE (SpiDEController)) + * + * However, that machinery creates a static function for + * _get_instance_private, so it is only visible in that file. Here + * we'll re-define it by hand, using the same name as that generated + * function in case we can later merge the implementations together. + */ +static SpiDEControllerPrivate * +spi_device_event_controller_get_instance_private (SpiDEController *controller) +{ + return g_type_instance_get_private ((GTypeInstance *) controller, SPI_DEVICE_EVENT_CONTROLLER_TYPE); +} + static unsigned int keysym_mod_mask (KeySym keysym, KeyCode keycode) { @@ -174,7 +165,7 @@ keysym_mod_mask (KeySym keysym, KeyCode keycode) } static gboolean -replace_map_keysym (DEControllerPrivateData *priv, KeyCode keycode, KeySym keysym) +replace_map_keysym (SpiDEControllerPrivate *priv, KeyCode keycode, KeySym keysym) { #ifdef HAVE_XKB Display *dpy = spi_get_display (); @@ -214,7 +205,7 @@ replace_map_keysym (DEControllerPrivateData *priv, KeyCode keycode, KeySym keysy static gboolean spi_dec_reset_reserved (gpointer data) { - DEControllerPrivateData *priv = data; + SpiDEControllerPrivate *priv = data; replace_map_keysym (priv, priv->reserved_keycode, priv->reserved_keysym); priv->reserved_reset_timeout = 0; return FALSE; @@ -233,7 +224,7 @@ spi_dec_x11_get_keycode (SpiDEController *controller, keycode = XKeysymToKeycode (spi_get_display (), (KeySym) keysym); if (!keycode && fix) { - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); /* if there's no keycode available, fix it */ if (replace_map_keysym (priv, priv->reserved_keycode, keysym)) { @@ -258,7 +249,7 @@ spi_dec_x11_get_keycode (SpiDEController *controller, static void spi_dec_set_unlatch_pending (SpiDEController *controller, unsigned mask) { - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); #ifdef SPI_XKB_DEBUG if (priv->xkb_latch_mask) fprintf (stderr, "unlatch pending! %x\n", priv->xkb_latch_mask); @@ -392,11 +383,15 @@ spi_dec_x11_mouse_check (SpiDEController *controller, Window root_return, child_return; Display *display = spi_get_display (); - if (display != NULL) - XQueryPointer(display, DefaultRootWindow (display), - &root_return, &child_return, - x, y, - &win_x_return, &win_y_return, &mask_return); + if (display == NULL) + { + return 0; + } + + XQueryPointer(display, DefaultRootWindow (display), + &root_return, &child_return, + x, y, + &win_x_return, &win_y_return, &mask_return); /* * Since many clients grab the pointer, and X goes an automatic * pointer grab on mouse-down, we often must detect mouse button events @@ -471,7 +466,7 @@ spi_dec_init_mouse_listener (SpiDEController *dec) static void spi_dec_clear_unlatch_pending (SpiDEController *controller) { - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); priv->xkb_latch_mask = 0; } @@ -563,11 +558,9 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller, static void global_filter_fn (XEvent *xevent, void *data) { - SpiDEController *controller; - DEControllerPrivateData *priv; + SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER (data); + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); Display *display = spi_get_display (); - controller = SPI_DEVICE_EVENT_CONTROLLER (data); - priv = controller->priv; if (xevent->type == MappingNotify) xmkeymap = NULL; @@ -705,10 +698,9 @@ _spi_controller_device_error_handler (Display *display, XErrorEvent *error) static void spi_controller_register_with_devices (SpiDEController *controller) { - DEControllerPrivateData *priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); int event_base, error_base, major_version, minor_version; - priv = controller->priv; if (XTestQueryExtension (spi_get_display(), &event_base, &error_base, &major_version, &minor_version)) { XTestGrabControl (spi_get_display (), True); @@ -973,7 +965,7 @@ static unsigned int xkb_get_slowkeys_delay (SpiDEController *controller) { unsigned int retval = 0; - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); #ifdef HAVE_XKB #ifdef XKB_HAS_GET_SLOW_KEYS_DELAY retval = XkbGetSlowKeysDelay (spi_get_display (), @@ -1001,7 +993,7 @@ static unsigned int xkb_get_bouncekeys_delay (SpiDEController *controller) { unsigned int retval = 0; - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); #ifdef HAVE_XKB #ifdef XKB_HAS_GET_BOUNCE_KEYS_DELAY retval = XkbGetBounceKeysDelay (spi_get_display (), @@ -1027,7 +1019,7 @@ xkb_get_bouncekeys_delay (SpiDEController *controller) static gboolean spi_dec_x11_synth_keycode_press (SpiDEController *controller, - unsigned int keycode) + unsigned int keycode) { unsigned int time = CurrentTime; unsigned int bounce_delay; @@ -1035,7 +1027,7 @@ spi_dec_x11_synth_keycode_press (SpiDEController *controller, unsigned int elapsed_msec; struct timeval tv; #endif - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); spi_x_error_trap (); if (keycode == priv->last_release_keycode) @@ -1081,7 +1073,7 @@ spi_dec_x11_synth_keycode_release (SpiDEController *controller, unsigned int elapsed_msec; struct timeval tv; #endif - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); spi_x_error_trap (); if (keycode == priv->last_press_keycode) @@ -1119,7 +1111,7 @@ spi_dec_x11_synth_keycode_release (SpiDEController *controller, static gboolean spi_dec_x11_lock_modifiers (SpiDEController *controller, unsigned modifiers) { - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); if (priv->have_xkb) { return XkbLockModifiers (spi_get_display (), XkbUseCoreKbd, @@ -1138,7 +1130,7 @@ spi_dec_x11_lock_modifiers (SpiDEController *controller, unsigned modifiers) static gboolean spi_dec_x11_unlock_modifiers (SpiDEController *controller, unsigned modifiers) { - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); if (priv->have_xkb) { return XkbLockModifiers (spi_get_display (), XkbUseCoreKbd, @@ -1238,7 +1230,7 @@ spi_dec_x11_synth_keystring (SpiDEController *controller, guint synth_type, gint static void spi_dec_x11_init (SpiDEController *controller) { - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); spi_events_init (spi_get_display ()); @@ -1254,7 +1246,7 @@ spi_dec_x11_init (SpiDEController *controller) static void spi_dec_x11_finalize (SpiDEController *controller) { - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); /* disconnect any special listeners, get rid of outstanding keygrabs */ XUngrabKey (spi_get_display (), AnyKey, AnyModifier, DefaultRootWindow (spi_get_display ())); @@ -1268,7 +1260,7 @@ static gboolean spi_device_event_controller_forward_key_event (SpiDEController *controller, const XEvent *event) { - DEControllerPrivateData *priv = controller->priv; + SpiDEControllerPrivate *priv = spi_device_event_controller_get_instance_private (controller); Accessibility_DeviceEvent key_event; gboolean ret; @@ -1411,8 +1403,6 @@ spi_dec_x11_generate_mouse_event (SpiDEController *controller, void spi_dec_setup_x11 (SpiDEControllerClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - klass->plat.get_keycode = spi_dec_x11_get_keycode; klass->plat.mouse_check = spi_dec_x11_mouse_check; klass->plat.synth_keycode_press = spi_dec_x11_synth_keycode_press; @@ -1427,6 +1417,4 @@ spi_dec_setup_x11 (SpiDEControllerClass *klass) klass->plat.init = spi_dec_x11_init; klass->plat.finalize = spi_dec_x11_finalize; - - g_type_class_add_private (object_class, sizeof (DEControllerPrivateData)); } diff --git a/registryd/deviceeventcontroller-x11.h b/registryd/deviceeventcontroller-x11.h new file mode 100644 index 0000000..62e2984 --- /dev/null +++ b/registryd/deviceeventcontroller-x11.h @@ -0,0 +1,28 @@ +#ifndef _DEVICEEVENTCONTROLLER_X11_H_ +#define _DEVICEEVENTCONTROLLER_X11_H_ + +#include +#include +#include +#include +#include + +typedef struct { + Display *xevie_display; + unsigned int last_press_keycode; + unsigned int last_release_keycode; + struct timeval last_press_time; + struct timeval last_release_time; + int have_xkb; + int xkb_major_extension_opcode; + int xkb_base_event_code; + int xkb_base_error_code; + unsigned int xkb_latch_mask; + unsigned int pending_xkb_mod_relatch_mask; + XkbDescPtr xkb_desc; + KeyCode reserved_keycode; + KeySym reserved_keysym; + guint reserved_reset_timeout; +} SpiDEControllerPrivate; + +#endif /* _DEVICEEVENTCONTROLLER_X11_H_ */ diff --git a/registryd/deviceeventcontroller.c b/registryd/deviceeventcontroller.c index 942f383..0fe6555 100644 --- a/registryd/deviceeventcontroller.c +++ b/registryd/deviceeventcontroller.c @@ -43,16 +43,16 @@ #include "de-marshaller.h" #include "keymasks.h" +#include "deviceeventcontroller.h" +#include "reentrant-list.h" +#include "introspection.h" + #ifdef HAVE_X11 +#include "deviceeventcontroller-x11.h" #include "display.h" #include "event-source.h" #endif -#include "deviceeventcontroller.h" -#include "reentrant-list.h" - -#include "introspection.h" - #define CHECK_RELEASE_DELAY 20 #define BIT(c, x) (c[x/8]&(1<<(x%8))) static SpiDEController *saved_controller; @@ -60,6 +60,16 @@ static SpiDEController *saved_controller; /* Our parent Gtk object type */ #define PARENT_TYPE G_TYPE_OBJECT +#ifndef HAVE_X11 +/* If we are using X11, SpiDEControllerPrivate is defined in deviceeventcontroller-x11.h. + * Otherwise, there is no private data and so we use a dummy struct. + * This is so that G_ADD_PRIVATE() will have a type to work with. + */ +typedef struct { + int _dummy; +} SpiDEControllerPrivate; +#endif + /* A pointer to our parent object class */ static int spi_error_code = 0; struct _SpiPoint { @@ -97,7 +107,8 @@ static gboolean eventtype_seq_contains_event (dbus_uint32_t types, static gboolean spi_dec_poll_mouse_moving (gpointer data); static gboolean spi_dec_poll_mouse_idle (gpointer data); -G_DEFINE_TYPE(SpiDEController, spi_device_event_controller, G_TYPE_OBJECT) +G_DEFINE_TYPE_WITH_CODE(SpiDEController, spi_device_event_controller, G_TYPE_OBJECT, + G_ADD_PRIVATE (SpiDEController)) static gint spi_dec_plat_get_keycode (SpiDEController *controller, @@ -111,7 +122,13 @@ spi_dec_plat_get_keycode (SpiDEController *controller, if (klass->plat.get_keycode) return klass->plat.get_keycode (controller, keysym, key_str, fix, modmask); else - return keysym; + { + if (modmask) + { + *modmask = 0; + } + return keysym; + } } static guint @@ -123,7 +140,13 @@ spi_dec_plat_mouse_check (SpiDEController *controller, if (klass->plat.mouse_check) return klass->plat.mouse_check (controller, x, y, moved); else - return 0; + { + if (moved) + { + *moved = FALSE; + } + return 0; + } } static gboolean @@ -873,8 +896,9 @@ reset_hung_process (DBusPendingCall *pending, void *data) { if (!strcmp (l->data, dest)) { - g_free (l->data); - hung_processes = g_slist_remove (hung_processes, l->data); + gpointer l_data = l->data; + hung_processes = g_slist_remove (hung_processes, l_data); + g_free (l_data); break; } } @@ -898,8 +922,9 @@ reset_hung_process_from_ping (DBusPendingCall *pending, void *data) { if (!strcmp (l->data, data)) { - g_free (l->data); - hung_processes = g_slist_remove (hung_processes, l->data); + gpointer l_data = l->data; + hung_processes = g_slist_remove (hung_processes, l_data); + g_free (l_data); break; } } @@ -1365,6 +1390,7 @@ impl_register_keystroke_listener (DBusConnection *bus, Accessibility_KeyDefinition *kd = (Accessibility_KeyDefinition *)g_malloc(sizeof(Accessibility_KeyDefinition)); if (!spi_dbus_message_iter_get_struct(&iter_array, DBUS_TYPE_INT32, &kd->keycode, DBUS_TYPE_INT32, &kd->keysym, DBUS_TYPE_STRING, &keystring, DBUS_TYPE_INVALID)) { + g_free(kd); break; } kd->keystring = g_strdup (keystring); @@ -1575,6 +1601,7 @@ impl_deregister_keystroke_listener (DBusConnection *bus, if (!spi_dbus_message_iter_get_struct(&iter_array, DBUS_TYPE_INT32, &kd->keycode, DBUS_TYPE_INT32, &kd->keysym, DBUS_TYPE_STRING, &keystring, DBUS_TYPE_INVALID)) { + g_free(kd); break; } kd->keystring = g_strdup (keystring); @@ -1675,6 +1702,7 @@ impl_get_device_event_listeners (DBusConnection *bus, static unsigned get_modifier_state (SpiDEController *controller) { + spi_dec_poll_mouse_moved (controller); return mouse_mask_state; } @@ -1696,12 +1724,17 @@ spi_dec_synth_keysym (SpiDEController *controller, long keysym) if (synth_mods != modifiers) { lock_mods = synth_mods & ~modifiers; spi_dec_plat_lock_modifiers (controller, lock_mods); + if (modifiers & SPI_KEYMASK_SHIFTLOCK) + spi_dec_plat_unlock_modifiers (controller, SPI_KEYMASK_SHIFTLOCK); } spi_dec_plat_synth_keycode_press (controller, key_synth_code); spi_dec_plat_synth_keycode_release (controller, key_synth_code); - if (synth_mods != modifiers) + if (synth_mods != modifiers) { spi_dec_plat_unlock_modifiers (controller, lock_mods); + if (modifiers & SPI_KEYMASK_SHIFTLOCK) + spi_dec_plat_lock_modifiers (controller, SPI_KEYMASK_SHIFTLOCK); + } return TRUE; } @@ -1858,9 +1891,7 @@ spi_device_event_controller_class_init (SpiDEControllerClass *klass) #ifdef HAVE_X11 if (g_getenv ("DISPLAY") != NULL && g_getenv ("WAYLAND_DISPLAY") == NULL) spi_dec_setup_x11 (klass); - else #endif - g_type_class_add_private (object_class, sizeof (long)); /* dummy */ } static void @@ -1869,10 +1900,6 @@ spi_device_event_controller_init (SpiDEController *device_event_controller) SpiDEControllerClass *klass; klass = SPI_DEVICE_EVENT_CONTROLLER_GET_CLASS (device_event_controller); - /* TODO: shouldn't be gpointer below */ - device_event_controller->priv = G_TYPE_INSTANCE_GET_PRIVATE (device_event_controller, - SPI_DEVICE_EVENT_CONTROLLER_TYPE, - gpointer); device_event_controller->message_queue = g_queue_new (); saved_controller = device_event_controller; diff --git a/registryd/deviceeventcontroller.h b/registryd/deviceeventcontroller.h index 94c01cf..46ea169 100644 --- a/registryd/deviceeventcontroller.h +++ b/registryd/deviceeventcontroller.h @@ -52,7 +52,6 @@ struct _SpiDEController { GList *keygrabs_list; GQueue *message_queue; guint message_queue_idle; - gpointer priv; }; typedef enum { diff --git a/registryd/registry.c b/registryd/registry.c index 8d35f8e..e22fbcb 100644 --- a/registryd/registry.c +++ b/registryd/registry.c @@ -248,8 +248,8 @@ remove_events (SpiRegistry *registry, const char *bus_name, const char *event) g_strfreev (evdata->data); g_free (evdata->bus_name); g_slist_free_full (evdata->properties, g_free); - g_free (evdata); registry->events = g_list_remove (registry->events, evdata); + g_free (evdata); } else { @@ -1056,7 +1056,7 @@ emit_event (DBusConnection *bus, const char *path) { DBusMessage *sig; - DBusMessageIter iter, iter_variant; + DBusMessageIter iter, iter_variant, iter_array; sig = dbus_message_new_signal(SPI_DBUS_PATH_ROOT, klass, major); @@ -1071,9 +1071,9 @@ emit_event (DBusConnection *bus, append_reference (&iter_variant, name, path); dbus_message_iter_close_container (&iter, &iter_variant); - append_reference (&iter, - dbus_bus_get_unique_name (bus), - SPI_DBUS_PATH_ROOT); + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}", + &iter_array); + dbus_message_iter_close_container (&iter, &iter_array); dbus_connection_send(bus, sig, NULL); dbus_message_unref(sig); diff --git a/test/memory.c b/test/memory.c index 2f53dd7..54f0652 100644 --- a/test/memory.c +++ b/test/memory.c @@ -47,7 +47,7 @@ end (void *data) static gboolean kill_child (void *data) { - kill (child_pid, SIGTERM); + g_assert_no_errno (kill (child_pid, SIGTERM)); return FALSE; } @@ -56,6 +56,7 @@ on_event (AtspiEvent *event, void *data) { if (atspi_accessible_get_role (event->source, NULL) == ATSPI_ROLE_DESKTOP_FRAME) { + printf ("memory: event: %s\n", event->type); if (strstr (event->type, "add")) { AtspiAccessible *desktop = atspi_get_desktop (0); @@ -83,8 +84,16 @@ main() listener = atspi_event_listener_new (on_event, NULL, NULL); atspi_event_listener_register (listener, "object:children-changed", NULL); child_pid = fork (); - if (!child_pid) - execlp ("test/test-application", "test/test-application", NULL); + if (child_pid == 0) + { + g_assert_no_errno (execlp ("test/test-application", "test/test-application", NULL)); + } + else if (child_pid == -1) + { + const char *error = g_strerror (errno); + g_error ("could not fork test-application child: %s", error); + } + printf ("memory: child pid: %d\n", (int) child_pid); atspi_event_main (); return 0; } diff --git a/xml/Cache.xml b/xml/Cache.xml index ce06ba4..8954616 100644 --- a/xml/Cache.xml +++ b/xml/Cache.xml @@ -3,12 +3,12 @@ - + - + -- 2.7.4 From 34cee6b9389a671e8e08fc0ee8f875ecb632375b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Artur=20=C5=9Awigo=C5=84?= Date: Fri, 24 Feb 2023 09:49:14 +0100 Subject: [PATCH 11/16] Fix merging errors Change-Id: Ib0534823627f61af248909a8bb47ace6d4ee38a3 --- atspi/atspi-registry.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/atspi/atspi-registry.c b/atspi/atspi-registry.c index 465083d..f906126 100644 --- a/atspi/atspi-registry.c +++ b/atspi/atspi-registry.c @@ -385,7 +385,6 @@ atspi_deregister_keystroke_listener (AtspiDeviceListener *listener, { return FALSE; } - path = _atspi_device_listener_get_path (listener); dbus_error_init (&d_error); @@ -478,7 +477,6 @@ atspi_register_device_event_listener (AtspiDeviceListener *listener, { return retval; } - path = _atspi_device_listener_get_path (listener); dbus_error_init (&d_error); @@ -519,7 +517,6 @@ atspi_deregister_device_event_listener (AtspiDeviceListener *listener, { return FALSE; } - path = _atspi_device_listener_get_path (listener); dbus_error_init (&d_error); -- 2.7.4 From 98cabb9ce5fc1a7e9ed3808b60f8b2c73d2d91ba Mon Sep 17 00:00:00 2001 From: =?utf8?q?Artur=20=C5=9Awigo=C5=84?= Date: Fri, 24 Feb 2023 09:49:55 +0100 Subject: [PATCH 12/16] Null-check dbus_message_get_path() result Change-Id: I0abcb6981030269af20b796e4cf68bec055328c7 --- atspi/atspi-device-listener.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atspi/atspi-device-listener.c b/atspi/atspi-device-listener.c index 9776ebd..f09ff41 100644 --- a/atspi/atspi-device-listener.c +++ b/atspi/atspi-device-listener.c @@ -357,7 +357,7 @@ _atspi_dbus_handle_DeviceEvent (DBusConnection *bus, DBusMessage *message, void goto done; } - if (sscanf (path, "/org/a11y/atspi/listeners/%d", &id) != 1) + if (!path || sscanf (path, "/org/a11y/atspi/listeners/%d", &id) != 1) { g_warning ("AT-SPI: Bad listener path: %s\n", path); goto done; -- 2.7.4 From 8065e1146c0775090aca0ff7e6f24b4eec7b589c Mon Sep 17 00:00:00 2001 From: Woochan Lee Date: Fri, 17 Feb 2023 09:42:49 +0900 Subject: [PATCH 13/16] [Tizen] Provide SetListenPostRender interface Asks the UI Toolkit listen for post render callback to get post render event in client. Change-Id: Ie2b95ff4f02888b1266fb621c3990565f3f69bf8 --- atspi/atspi-accessible.c | 15 +++++++++++++++ atspi/atspi-accessible.h | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index d461055..faba394 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -1465,6 +1465,21 @@ atspi_accessible_get_id (AtspiAccessible *obj, GError **error) return ret; } +//TIZEN_ONLY(20230307) Provide post render callback to client +/** + * atspi_accessible_set_listen_post_render: + * @obj: a pointer to the #AtspiAccessible object on which to operate. + * @enabled: a boolean true listen post render callback, false otherwise. + * + * Asks the UI Toolkit listen for the post render callback. + **/ +void +atspi_accessible_set_listen_post_render(AtspiAccessible *obj, gboolean enabled, GError **error) +{ + g_return_val_if_fail (obj != NULL, FALSE); + + _atspi_dbus_call (obj, atspi_interface_accessible, "SetListenPostRender", error, "b", enabled, NULL); +} /* Interface query methods */ diff --git a/atspi/atspi-accessible.h b/atspi/atspi-accessible.h index 5211972..364e75e 100644 --- a/atspi/atspi-accessible.h +++ b/atspi/atspi-accessible.h @@ -159,6 +159,10 @@ gint atspi_accessible_get_id (AtspiAccessible *obj, GError **error); AtspiAccessible * atspi_accessible_get_application (AtspiAccessible *obj, GError **error); +//TIZEN_ONLY(20230307) Provide post render callback to client +void atspi_accessible_set_listen_post_render(AtspiAccessible *obj, gboolean enabled, GError **error); +// + #ifndef ATSPI_DISABLE_DEPRECATED AtspiAction * atspi_accessible_get_action (AtspiAccessible *obj); -- 2.7.4 From 99201d6e99f7a16d3299350117fe21ed407584c4 Mon Sep 17 00:00:00 2001 From: Woochan Lee Date: Thu, 9 Mar 2023 19:12:37 +0900 Subject: [PATCH 14/16] [Tizen] Fix build warning. Change-Id: Ia163d957c06b715295c22423975fc2a9f0f95649 --- atspi/atspi-accessible.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atspi/atspi-accessible.c b/atspi/atspi-accessible.c index faba394..e5a2c7c 100644 --- a/atspi/atspi-accessible.c +++ b/atspi/atspi-accessible.c @@ -1476,7 +1476,7 @@ atspi_accessible_get_id (AtspiAccessible *obj, GError **error) void atspi_accessible_set_listen_post_render(AtspiAccessible *obj, gboolean enabled, GError **error) { - g_return_val_if_fail (obj != NULL, FALSE); + g_return_if_fail (obj != NULL); _atspi_dbus_call (obj, atspi_interface_accessible, "SetListenPostRender", error, "b", enabled, NULL); } -- 2.7.4 From 34a26a69425377e18b52474d46db5a20bf1d3197 Mon Sep 17 00:00:00 2001 From: Maria Bialota Date: Tue, 14 Mar 2023 15:04:05 +0100 Subject: [PATCH 15/16] at-spi-bus-launcher: fixed memory leak Change-Id: Ieb4985780a21f4608f11d1e8e51aef1c401b1637 --- bus/at-spi-bus-launcher.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bus/at-spi-bus-launcher.c b/bus/at-spi-bus-launcher.c index 36c8f73..7877fd4 100644 --- a/bus/at-spi-bus-launcher.c +++ b/bus/at-spi-bus-launcher.c @@ -383,7 +383,10 @@ ensure_a11y_bus_daemon (A11yBusLauncher *app, char *config_path) } if (app->a11y_bus_pid != 0) - return FALSE; + { + g_free(address_param); + return FALSE; + } if (pipe (app->pipefd) < 0) { -- 2.7.4 From 32c7ce95032c5e6f3e26ba9713702627d67671a5 Mon Sep 17 00:00:00 2001 From: Maria Bialota Date: Tue, 14 Mar 2023 16:20:38 +0100 Subject: [PATCH 16/16] at-spi-bus-launcher: Added checking return value when creating directory Change-Id: I44aa5995488a466a5610887bfa2067ed1b739538 --- bus/at-spi-bus-launcher.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bus/at-spi-bus-launcher.c b/bus/at-spi-bus-launcher.c index 7877fd4..2950a45 100644 --- a/bus/at-spi-bus-launcher.c +++ b/bus/at-spi-bus-launcher.c @@ -610,12 +610,11 @@ ensure_a11y_bus (A11yBusLauncher *app) config_path = "--config-file="DATADIR"/defaults/at-spi2/accessibility.conf"; xdg_runtime_dir = g_get_user_runtime_dir (); - if (xdg_runtime_dir) + if (xdg_runtime_dir && (mkdir (xdg_runtime_dir, 0700) == 0 || errno == EEXIST)) { const gchar *display = g_getenv ("DISPLAY"); gchar *at_spi_dir = g_strconcat (xdg_runtime_dir, "/at-spi", NULL); gchar *p; - mkdir (xdg_runtime_dir, 0700); if (!g_path_is_absolute (at_spi_dir)) { gchar *new_dir = g_canonicalize_filename (at_spi_dir, NULL); -- 2.7.4