mutter/a11y-manager.patch
Jonas Ådahl ee7d1dc52c Backport Accessibility manager patches
Resolves: RHEL-82072
2025-03-25 12:10:28 +01:00

2158 lines
76 KiB
Diff

From cc92000f1f4ae6c7fcee330ffd38842365bdf811 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 18:36:15 +0100
Subject: [PATCH 01/14] data: Introduce the DBus interface description for the
a11y manager
This object will for now only provide a way for assistive technologies
to receive keyboard events, however it is expected that it will be used for
the new a11y communication protocol in the future.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit 84fc62a2806b893b215fcfb287390ced07a98aa7)
---
data/dbus-interfaces/org.freedesktop.a11y.xml | 92 +++++++++++++++++++
src/meson.build | 5 +
2 files changed, 97 insertions(+)
create mode 100644 data/dbus-interfaces/org.freedesktop.a11y.xml
diff --git a/data/dbus-interfaces/org.freedesktop.a11y.xml b/data/dbus-interfaces/org.freedesktop.a11y.xml
new file mode 100644
index 0000000000..c60480e89c
--- /dev/null
+++ b/data/dbus-interfaces/org.freedesktop.a11y.xml
@@ -0,0 +1,92 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+ <!--
+ org.freedesktop.a11y.KeyboardMonitor:
+ @short_description: interface for monitoring of keyboard input by assistive technologies
+
+ This interface is used by assistive technologies to monitor keyboard
+ input of the compositor. The compositor is expected to listen on
+ the well-known bus name "org.freedesktop.a11y.Manager" at the object
+ path "/org/freedesktop/a11y/Manager".
+ -->
+ <interface name="org.freedesktop.a11y.KeyboardMonitor">
+ <!--
+ GrabKeyboard:
+
+ Starts grabbing all key events. The client receives the events
+ through the KeyEvent signal as always, but the events aren't handled
+ normally by the compositor. This includes changes to the state
+ of toggles like Caps Lock, Num Lock, and Scroll Lock.
+
+ This behavior stays in effect until the same client calls
+ UngrabKeyboard or closes its D-Bus connection.
+ -->
+ <method name="GrabKeyboard" />
+
+ <!--
+ UngrabKeyboard:
+
+ Reverses the effect of calling GrabKeyboard. If GrabKeyboard wasn't
+ previously called, this method does nothing.
+
+ After calling this method, the key grabs specified in the last call
+ to SetKeyGrabs, if any, are still in effect.
+ -->
+ <method name="UngrabKeyboard" />
+
+ <!--
+ SetKeyGrabs:
+ @modifiers: set of custom modifiers to grab
+ @keystrokes: set of keystrokes without custom modifiers to grab
+
+ Sets the current key grabs for the calling client, overriding
+ any previous call to this method. For grabbed key events, the
+ KeyEvent signal is still emitted, but normal key event handling
+ is suppressed, including state changes for toggles like Caps Lock
+ and Num Lock.
+
+ The grabs set by this method stay in effect until the same client
+ calls this method again, or until that client closes its D-Bus
+ connection.
+
+ Each item in @modifiers is an XKB keysym. All keys in this list
+ will be grabbed, and keys pressed while any of these keys are down
+ will also be grabbed.
+
+ Each item in @keystrokes is a struct with the following fields:
+
+ - the XKB keysym of the non-modifier key
+ - the XKB modifier mask of the modifiers, if any, for this keystroke
+
+ If any of the keys in @modifiers is pressed alone, the compositor
+ is required to ignore the key press and release event if a second
+ key press of the same modifier is not received within a reasonable
+ time frame, for example, the key repeat delay.
+ If such event is received, this second event is processed normally.
+ -->
+ <method name="SetKeyGrabs">
+ <arg type="au" name="modifiers" direction="in" />
+ <arg type="a(uu)" name="keystrokes" direction="in" />
+ </method>
+
+ <!--
+ KeyEvent:
+ @released: whether this is a key-up event
+ @state: XKB modifier mask for currently pressed modifiers
+ @keysym: XKB keysym for this key
+ @unichar: Unicode character for this key, or 0 if none
+ @keycode: hardware-dependent keycode for this key
+
+ The compositor emits this signal for each key press or release.
+ -->
+ <signal name="KeyEvent">
+ <arg type="b" name="released" direction="in" />
+ <arg type="u" name="state" direction="in" />
+ <arg type="u" name="keysym" direction="in" />
+ <arg type="u" name="unichar" direction="in" />
+ <arg type="q" name="keycode" direction="in" />
+ </signal>
+ </interface>
+</node>
diff --git a/src/meson.build b/src/meson.build
index e3c92aaf27..6c19300b8f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -947,6 +947,11 @@ if mutter_private_enum_sources.length() > 0
endif
dbus_interfaces = [
+ {
+ 'name': 'meta-dbus-a11y',
+ 'interface': 'org.freedesktop.a11y.xml',
+ 'prefix': 'org.freedesktop.a11y',
+ },
{
'name': 'meta-dbus-display-config',
'interface': 'org.gnome.Mutter.DisplayConfig.xml',
--
2.44.0.501.g19981daefd.dirty
From 7c4e773276b9cd87373e4231db371849157db325 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 18:37:28 +0100
Subject: [PATCH 02/14] clutter: Add ClutterEventFlag to notify a11y modifier
click
Use a Clutter event flag to communicate the the fact that the event
is an a11y modifier first click. The accessibility modifiers will
require special handling in the input and main threads.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit d3a0bbdf524f160b683f5db261382c306e71c58d)
---
clutter/clutter/clutter-enums.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/clutter/clutter/clutter-enums.h b/clutter/clutter/clutter-enums.h
index 9bd508fb67..451e1b8c67 100644
--- a/clutter/clutter/clutter-enums.h
+++ b/clutter/clutter/clutter-enums.h
@@ -519,6 +519,7 @@ typedef enum /*< flags prefix=CLUTTER_EVENT >*/
CLUTTER_EVENT_FLAG_RELATIVE_MOTION = 1 << 3,
CLUTTER_EVENT_FLAG_GRAB_NOTIFY = 1 << 4,
CLUTTER_EVENT_FLAG_POINTER_EMULATED = 1 << 5,
+ CLUTTER_EVENT_FLAG_A11Y_MODIFIER_FIRST_CLICK = 1 << 6,
} ClutterEventFlags;
/**
--
2.44.0.501.g19981daefd.dirty
From 7971ccea8aa76745165cdf44f61a19b5f82a17b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 18:52:00 +0100
Subject: [PATCH 03/14] backends/native: Handle a11y modifier presses in
MetaSeatImpl
The state handling about whether the a11y modifier is a first press (so
could be consumed for other actions), or results in the modifier action
(e.g. caps lock) is performed in the input thread. This information will
be propagated through the CLUTTER_EVENT_FLAG_A11Y_MODIFIER_FIRST_CLICK
flag in the related key events.
Carlos Garnacho: Drop synchronous wait for configuration changes
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit d86796413929bd5d5fb59cb3c2a9ab9d714f70cc)
---
src/backends/native/meta-seat-impl.c | 133 ++++++++++++++++++++++++++-
src/backends/native/meta-seat-impl.h | 4 +
2 files changed, 134 insertions(+), 3 deletions(-)
diff --git a/src/backends/native/meta-seat-impl.c b/src/backends/native/meta-seat-impl.c
index c1e46a0502..0bdf1606fc 100644
--- a/src/backends/native/meta-seat-impl.c
+++ b/src/backends/native/meta-seat-impl.c
@@ -103,6 +103,14 @@ static guint signals[N_SIGNALS] = { 0 };
typedef struct _MetaSeatImplPrivate
{
GHashTable *device_files;
+
+ struct {
+ GHashTable *grabbed_modifiers;
+ GHashTable *pressed_modifiers;
+ uint32_t last_keysym;
+ uint32_t last_keysym_time;
+ gboolean saw_first_release;
+ } a11y;
} MetaSeatImplPrivate;
static void meta_seat_impl_initable_iface_init (GInitableIface *iface);
@@ -389,6 +397,65 @@ emit_signal (MetaSeatImpl *seat_impl,
(GDestroyNotify) signal_data_free);
}
+static gboolean
+is_a11y_modifier_first_click (MetaSeatImpl *seat_impl,
+ uint32_t keysym,
+ uint32_t event_time,
+ gboolean is_press)
+{
+ MetaSeatImplPrivate *priv = meta_seat_impl_get_instance_private (seat_impl);
+ gboolean is_same_keysym = keysym == priv->a11y.last_keysym;
+ gboolean event_soon_enough =
+ event_time - priv->a11y.last_keysym_time < seat_impl->repeat_delay;
+ gboolean is_grabbed_modifier =
+ g_hash_table_contains (priv->a11y.grabbed_modifiers, GUINT_TO_POINTER (keysym));
+
+ priv->a11y.last_keysym = keysym;
+ priv->a11y.last_keysym_time = event_time;
+
+ /* This is not an event for a grabbed modifier */
+ if (!is_grabbed_modifier)
+ return FALSE;
+
+ if (!is_press && g_hash_table_contains (priv->a11y.pressed_modifiers,
+ GUINT_TO_POINTER (keysym)))
+ {
+ g_hash_table_remove (priv->a11y.pressed_modifiers,
+ GUINT_TO_POINTER (keysym));
+ /* This is a release event for a previously pressed modifier */
+ return FALSE;
+ }
+
+ if (is_same_keysym && event_soon_enough)
+ {
+ if (is_press && priv->a11y.saw_first_release)
+ {
+ priv->a11y.saw_first_release = FALSE;
+ g_hash_table_add (priv->a11y.pressed_modifiers,
+ GUINT_TO_POINTER (keysym));
+
+ /* This is the second press event and it is on time, process
+ * it normally
+ */
+ return FALSE;
+ }
+ else
+ {
+ priv->a11y.saw_first_release = TRUE;
+ /* This is the first release event, wait for the second press event */
+ return TRUE;
+ }
+ }
+ else
+ {
+ /* This is either a different modifier, the first press
+ * event, or not on time to progress
+ */
+ priv->a11y.saw_first_release = FALSE;
+ return TRUE;
+ }
+}
+
void
meta_seat_impl_notify_key_in_impl (MetaSeatImpl *seat_impl,
ClutterInputDevice *device,
@@ -401,6 +468,8 @@ meta_seat_impl_notify_key_in_impl (MetaSeatImpl *seat_impl,
ClutterEventFlags flags = CLUTTER_EVENT_NONE;
enum xkb_state_component changed_state;
uint32_t keycode;
+ uint32_t keysym;
+ gboolean should_ignore;
if (state != AUTOREPEAT_VALUE)
{
@@ -421,6 +490,16 @@ meta_seat_impl_notify_key_in_impl (MetaSeatImpl *seat_impl,
flags = CLUTTER_EVENT_FLAG_REPEATED;
}
+ keycode = meta_xkb_evdev_to_keycode (key);
+ keysym = xkb_state_key_get_one_sym (seat_impl->xkb, keycode);
+
+ should_ignore = is_a11y_modifier_first_click (seat_impl,
+ keysym,
+ time_us / 1000,
+ state);
+ if (should_ignore)
+ flags |= CLUTTER_EVENT_FLAG_A11Y_MODIFIER_FIRST_CLICK;
+
event = meta_key_event_new_from_evdev (device,
seat_impl->core_keyboard,
flags,
@@ -428,11 +507,9 @@ meta_seat_impl_notify_key_in_impl (MetaSeatImpl *seat_impl,
seat_impl->button_state,
time_us, key, state);
- keycode = meta_xkb_evdev_to_keycode (key);
-
/* We must be careful and not pass multiple releases to xkb, otherwise it gets
confused and locks the modifiers */
- if (state != AUTOREPEAT_VALUE)
+ if (!should_ignore && state != AUTOREPEAT_VALUE)
{
changed_state = xkb_state_update_key (seat_impl->xkb, keycode,
state ? XKB_KEY_DOWN : XKB_KEY_UP);
@@ -2938,6 +3015,9 @@ input_thread (MetaSeatImpl *seat_impl)
NULL,
(GDestroyNotify) meta_device_file_release);
+ priv->a11y.grabbed_modifiers = g_hash_table_new (NULL, NULL);
+ priv->a11y.pressed_modifiers = g_hash_table_new (NULL, NULL);
+
seat_impl->input_settings = meta_input_settings_native_new_in_impl (seat_impl);
g_signal_connect_object (seat_impl->input_settings, "kbd-a11y-changed",
G_CALLBACK (kbd_a11y_changed_cb), seat_impl, 0);
@@ -3099,6 +3179,9 @@ destroy_in_impl (GTask *task)
g_clear_pointer (&priv->device_files, g_hash_table_destroy);
+ g_clear_pointer (&priv->a11y.grabbed_modifiers, g_hash_table_destroy);
+ g_clear_pointer (&priv->a11y.pressed_modifiers, g_hash_table_destroy);
+
g_main_loop_quit (seat_impl->input_loop);
g_task_return_boolean (task, TRUE);
@@ -3820,6 +3903,50 @@ meta_seat_impl_set_viewports (MetaSeatImpl *seat_impl,
g_cond_clear (&data.cond);
}
+static gboolean
+set_a11y_modifiers (GTask *task)
+{
+ MetaSeatImpl *seat_impl = g_task_get_source_object (task);
+ MetaSeatImplPrivate *priv = meta_seat_impl_get_instance_private (seat_impl);
+ GArray *modifiers = g_task_get_task_data (task);
+ int i;
+
+ g_hash_table_remove_all (priv->a11y.grabbed_modifiers);
+
+ for (i = 0; i < modifiers->len; i++)
+ {
+ uint32_t keysym;
+
+ keysym = g_array_index (modifiers, uint32_t, i);
+ g_hash_table_add (priv->a11y.grabbed_modifiers,
+ GUINT_TO_POINTER (keysym));
+ }
+
+ g_task_return_boolean (task, TRUE);
+
+ return G_SOURCE_REMOVE;
+}
+
+void
+meta_seat_impl_set_a11y_modifiers (MetaSeatImpl *seat_impl,
+ const uint32_t *modifiers,
+ int n_modifiers)
+{
+ g_autoptr (GTask) task = NULL;
+ GArray *modifiers_copy;
+
+ g_return_if_fail (META_IS_SEAT_IMPL (seat_impl));
+
+ modifiers_copy = g_array_new (FALSE, FALSE, sizeof (uint32_t));
+ g_array_append_vals (modifiers_copy, modifiers, n_modifiers);
+
+ task = g_task_new (seat_impl, NULL, NULL, NULL);
+ g_task_set_task_data (task, modifiers_copy,
+ (GDestroyNotify) g_array_unref);
+ meta_seat_impl_run_input_task (seat_impl, task,
+ (GSourceFunc) set_a11y_modifiers);
+}
+
MetaSeatImpl *
meta_seat_impl_new (MetaSeatNative *seat_native,
const char *seat_id,
diff --git a/src/backends/native/meta-seat-impl.h b/src/backends/native/meta-seat-impl.h
index d95fb7176b..7d85fc6fc7 100644
--- a/src/backends/native/meta-seat-impl.h
+++ b/src/backends/native/meta-seat-impl.h
@@ -263,3 +263,7 @@ void meta_seat_impl_add_virtual_input_device (MetaSeatImpl *seat_impl,
void meta_seat_impl_remove_virtual_input_device (MetaSeatImpl *seat_impl,
ClutterInputDevice *device);
+
+void meta_seat_impl_set_a11y_modifiers (MetaSeatImpl *seat_impl,
+ const uint32_t *modifiers,
+ int n_modifiers);
--
2.44.0.501.g19981daefd.dirty
From 4615e7ce13211ee7aea8aa83f265a4318356495e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 18:58:41 +0100
Subject: [PATCH 04/14] backends/native: Plumb a11y modifiers through
MetaSeatNative
These modifiers will be set by the backend from the main thread, and
need to be handled specially for them to be usable as both modifier
buttons, and their own regular action.
Carlos Garnacho: pass modifiers as array+lenght instead of hashtable.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit 8c52e243c03c03c4e1fe0c13c730fd24d84a40a6)
---
src/backends/native/meta-seat-native.c | 8 ++++++++
src/backends/native/meta-seat-native.h | 4 ++++
2 files changed, 12 insertions(+)
diff --git a/src/backends/native/meta-seat-native.c b/src/backends/native/meta-seat-native.c
index d912875684..ed3842bcf1 100644
--- a/src/backends/native/meta-seat-native.c
+++ b/src/backends/native/meta-seat-native.c
@@ -656,6 +656,14 @@ meta_seat_native_set_viewports (MetaSeatNative *seat,
meta_seat_impl_set_viewports (seat->impl, viewports);
}
+void
+meta_seat_native_set_a11y_modifiers (MetaSeatNative *seat,
+ const uint32_t *modifiers,
+ int n_modifiers)
+{
+ meta_seat_impl_set_a11y_modifiers (seat->impl, modifiers, n_modifiers);
+}
+
void
meta_seat_native_run_impl_task (MetaSeatNative *seat,
GSourceFunc dispatch_func,
diff --git a/src/backends/native/meta-seat-native.h b/src/backends/native/meta-seat-native.h
index e82d0043be..cccfa6ce63 100644
--- a/src/backends/native/meta-seat-native.h
+++ b/src/backends/native/meta-seat-native.h
@@ -137,3 +137,7 @@ void meta_seat_native_run_impl_task (MetaSeatNative *seat,
GSourceFunc dispatch_func,
gpointer user_data,
GDestroyNotify destroy_notify);
+
+void meta_seat_native_set_a11y_modifiers (MetaSeatNative *seat,
+ const uint32_t *modifiers,
+ int n_modifiers);
--
2.44.0.501.g19981daefd.dirty
From 893688f075578a5d8f660094105a53d20b1143fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 19:01:26 +0100
Subject: [PATCH 05/14] backends: Add MetaA11yManager
This a11y manager will handle key event emission to screen
readers and other ATs. This initial commit only introduces
the object.
Carlos Garnacho: Make the object take a ::backend property
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit c0c452445236d98fc4297603f269938ba255a206)
---
src/backends/meta-a11y-manager.c | 102 +++++++++++++++++++++++++++++++
src/backends/meta-a11y-manager.h | 29 +++++++++
src/meson.build | 2 +
3 files changed, 133 insertions(+)
create mode 100644 src/backends/meta-a11y-manager.c
create mode 100644 src/backends/meta-a11y-manager.h
diff --git a/src/backends/meta-a11y-manager.c b/src/backends/meta-a11y-manager.c
new file mode 100644
index 0000000000..22de01030d
--- /dev/null
+++ b/src/backends/meta-a11y-manager.c
@@ -0,0 +1,102 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright 2024 GNOME Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "backends/meta-a11y-manager.h"
+#include "meta/meta-backend.h"
+#include "meta/meta-context.h"
+#include "meta/util.h"
+
+#include "meta-dbus-a11y.h"
+
+enum
+{
+ A11Y_MODIFIERS_CHANGED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
+enum
+{
+ PROP_0,
+ PROP_BACKEND,
+ N_PROPS,
+};
+
+static GParamSpec *props[N_PROPS];
+
+typedef struct _MetaA11yManager
+{
+ GObject parent;
+ MetaBackend *backend;
+} MetaA11yManager;
+
+G_DEFINE_TYPE (MetaA11yManager, meta_a11y_manager, G_TYPE_OBJECT)
+
+static void
+meta_a11y_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MetaA11yManager *a11y_manager = META_A11Y_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_BACKEND:
+ a11y_manager->backend = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_a11y_manager_class_init (MetaA11yManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = meta_a11y_manager_set_property;
+
+ props[PROP_BACKEND] =
+ g_param_spec_object ("backend", NULL, NULL,
+ META_TYPE_BACKEND,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+meta_a11y_manager_init (MetaA11yManager *a11y_manager)
+{
+}
+
+MetaA11yManager *
+meta_a11y_manager_new (MetaBackend *backend)
+{
+ return g_object_new (META_TYPE_A11Y_MANAGER,
+ "backend", backend,
+ NULL);
+}
diff --git a/src/backends/meta-a11y-manager.h b/src/backends/meta-a11y-manager.h
new file mode 100644
index 0000000000..df5c9a5d21
--- /dev/null
+++ b/src/backends/meta-a11y-manager.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 GNOME Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+#include "backends/meta-backend-types.h"
+#include "clutter/clutter.h"
+
+#define META_TYPE_A11Y_MANAGER (meta_a11y_manager_get_type ())
+G_DECLARE_FINAL_TYPE (MetaA11yManager, meta_a11y_manager, META, A11Y_MANAGER, GObject)
+
+MetaA11yManager * meta_a11y_manager_new (MetaBackend *backend);
diff --git a/src/meson.build b/src/meson.build
index 6c19300b8f..c57fd935f6 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -193,6 +193,8 @@ mutter_sources = [
'backends/edid.h',
'backends/edid-parse.c',
'backends/gsm-inhibitor-flag.h',
+ 'backends/meta-a11y-manager.c',
+ 'backends/meta-a11y-manager.h',
'backends/meta-backend.c',
'backends/meta-backend-private.h',
'backends/meta-barrier.c',
--
2.44.0.501.g19981daefd.dirty
From 3976dc0c8accf9fcd2968e5980408fca32589a69 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 19:03:10 +0100
Subject: [PATCH 06/14] backends: Manage MetaA11yManager in MetaBackend
The MetaBackend will own the MetaA11yManager, being actually
put to use in backend implementations.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit f4ce1e8a462b96e207ff0ba21040c50e91c62cc2)
---
src/backends/meta-backend-private.h | 3 +++
src/backends/meta-backend.c | 18 ++++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h
index 1d2af368bd..411a0a5d57 100644
--- a/src/backends/meta-backend-private.h
+++ b/src/backends/meta-backend-private.h
@@ -28,6 +28,7 @@
#include "meta/meta-backend.h"
#include "meta/meta-idle-monitor.h"
#include "meta/meta-orientation-manager.h"
+#include "backends/meta-a11y-manager.h"
#include "backends/meta-backend-types.h"
#include "backends/meta-cursor-renderer.h"
#include "backends/meta-egl.h"
@@ -159,6 +160,8 @@ MetaScreenCast * meta_backend_get_screen_cast (MetaBackend *backend);
MetaInputCapture * meta_backend_get_input_capture (MetaBackend *backend);
+MetaA11yManager * meta_backend_get_a11y_manager (MetaBackend *backend);
+
gboolean meta_backend_grab_device (MetaBackend *backend,
int device_id,
uint32_t timestamp);
diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
index cdca232abc..2cdc7edf6c 100644
--- a/src/backends/meta-backend.c
+++ b/src/backends/meta-backend.c
@@ -52,6 +52,7 @@
#include <stdlib.h>
+#include "backends/meta-a11y-manager.h"
#include "backends/meta-barrier-private.h"
#include "backends/meta-color-manager-private.h"
#include "backends/meta-cursor-renderer.h"
@@ -154,6 +155,7 @@ struct _MetaBackendPrivate
MetaRemoteDesktop *remote_desktop;
#endif
MetaInputCapture *input_capture;
+ MetaA11yManager *a11y_manager;
#ifdef HAVE_LIBWACOM
WacomDeviceDatabase *wacom_db;
@@ -226,6 +228,7 @@ meta_backend_dispose (GObject *object)
g_clear_object (&priv->input_capture);
g_clear_object (&priv->dbus_session_watcher);
g_clear_object (&priv->remote_access_controller);
+ g_clear_object (&priv->a11y_manager);
g_clear_object (&priv->dnd);
#ifdef HAVE_LIBWACOM
@@ -607,6 +610,8 @@ meta_backend_real_post_init (MetaBackend *backend)
priv->remote_access_controller,
META_DBUS_SESSION_MANAGER (priv->input_capture));
+ priv->a11y_manager = meta_a11y_manager_new (backend);
+
if (!meta_monitor_manager_is_headless (priv->monitor_manager))
init_pointer_position (backend);
@@ -1548,6 +1553,19 @@ meta_backend_get_remote_access_controller (MetaBackend *backend)
return priv->remote_access_controller;
}
+/**
+ * meta_backend_get_a11y_manager:
+ *
+ * Returns: (transfer none): the #MetaA11yManager
+ */
+MetaA11yManager *
+meta_backend_get_a11y_manager (MetaBackend *backend)
+{
+ MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
+
+ return priv->a11y_manager;
+}
+
/**
* meta_backend_is_rendering_hardware_accelerated:
* @backend: A #MetaBackend
--
2.44.0.501.g19981daefd.dirty
From b6e5ec24b95f0b389b18927af0956f3b59d7b21d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 19:15:54 +0100
Subject: [PATCH 07/14] backends: Implement A11y monitor interface
Implement the org.freedesktop.a11y.KeyboardMonitor interface,
allowing screen readers to interact with Mutter and grab
shortcuts or full keyboard interaction.
Carlos Garnacho: Move setup to ::constructed.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit 0bcda4ecf47f04cb76fb642f05d498d1dd941e15)
---
src/backends/meta-a11y-manager.c | 274 +++++++++++++++++++++++++++++++
1 file changed, 274 insertions(+)
diff --git a/src/backends/meta-a11y-manager.c b/src/backends/meta-a11y-manager.c
index 22de01030d..5f8d5b1ee4 100644
--- a/src/backends/meta-a11y-manager.c
+++ b/src/backends/meta-a11y-manager.c
@@ -44,14 +44,253 @@ enum
static GParamSpec *props[N_PROPS];
+typedef struct _MetaA11yKeystroke
+{
+ uint32_t keysym;
+ ClutterModifierType modifiers;
+} MetaA11yKeystroke;
+
+typedef struct _MetaA11yKeyGrabber
+{
+ MetaA11yManager *manager;
+ GDBusConnection *connection;
+ char *bus_name;
+ guint bus_name_watcher_id;
+ gboolean grab_all;
+ GArray *modifiers;
+ GArray *keystrokes;
+} MetaA11yKeyGrabber;
+
typedef struct _MetaA11yManager
{
GObject parent;
MetaBackend *backend;
+ guint dbus_name_id;
+ MetaDBusKeyboardMonitor *keyboard_monitor_skeleton;
+
+ GList *key_grabbers;
+ GHashTable *grabbed_keypresses;
+ GHashTable *all_grabbed_modifiers;
} MetaA11yManager;
G_DEFINE_TYPE (MetaA11yManager, meta_a11y_manager, G_TYPE_OBJECT)
+static void
+key_grabber_free (MetaA11yKeyGrabber *grabber)
+{
+ if (grabber->bus_name_watcher_id)
+ {
+ g_bus_unwatch_name (grabber->bus_name_watcher_id);
+ grabber->bus_name_watcher_id = 0;
+ }
+
+ g_clear_pointer (&grabber->keystrokes, g_array_unref);
+ g_clear_pointer (&grabber->modifiers, g_array_unref);
+ g_clear_object (&grabber->connection);
+ g_clear_pointer (&grabber->bus_name, g_free);
+
+ g_free (grabber);
+}
+
+static void
+rebuild_all_grabbed_modifiers (MetaA11yManager *a11y_manager,
+ MetaA11yKeyGrabber *ignored_grabber)
+{
+ GList *l;
+ int i;
+
+ g_hash_table_remove_all (a11y_manager->all_grabbed_modifiers);
+
+ for (l = a11y_manager->key_grabbers; l; l = l->next)
+ {
+ MetaA11yKeyGrabber *grabber = l->data;
+
+ if (grabber == ignored_grabber)
+ continue;
+
+ for (i = 0; i < grabber->modifiers->len; i++)
+ {
+ uint32_t modifier_keysym = g_array_index (grabber->modifiers, uint32_t, i);
+ g_hash_table_add (a11y_manager->all_grabbed_modifiers,
+ GUINT_TO_POINTER (modifier_keysym));
+ }
+ }
+}
+
+static void
+key_grabber_bus_name_vanished_callback (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ MetaA11yKeyGrabber *grabber = user_data;
+ MetaA11yManager *a11y_manager = grabber->manager;
+
+ grabber->manager->key_grabbers =
+ g_list_remove (grabber->manager->key_grabbers, grabber);
+
+ if (grabber->modifiers)
+ {
+ rebuild_all_grabbed_modifiers (a11y_manager, grabber);
+ g_signal_emit (grabber->manager, signals[A11Y_MODIFIERS_CHANGED], 0);
+ }
+
+ key_grabber_free (grabber);
+}
+
+static MetaA11yKeyGrabber *
+ensure_key_grabber (MetaA11yManager *a11y_manager,
+ GDBusMethodInvocation *invocation)
+{
+ GDBusConnection *connection =
+ g_dbus_method_invocation_get_connection (invocation);
+ const char *sender = g_dbus_method_invocation_get_sender (invocation);
+ MetaA11yKeyGrabber *grabber;
+ GList *l;
+
+ for (l = a11y_manager->key_grabbers; l; l = l->next)
+ {
+ grabber = l->data;
+
+ if (g_strcmp0 (grabber->bus_name, sender) == 0)
+ return grabber;
+ }
+
+ grabber = g_new0 (MetaA11yKeyGrabber, 1);
+ grabber->manager = a11y_manager;
+ grabber->bus_name = g_strdup (sender);
+ grabber->connection = g_object_ref (connection);
+
+ grabber->bus_name_watcher_id =
+ g_bus_watch_name_on_connection (connection,
+ grabber->bus_name,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ NULL,
+ key_grabber_bus_name_vanished_callback,
+ grabber,
+ NULL);
+
+ a11y_manager->key_grabbers =
+ g_list_prepend (a11y_manager->key_grabbers, grabber);
+
+ return grabber;
+}
+
+static gboolean
+handle_grab_keyboard (MetaDBusKeyboardMonitor *skeleton,
+ GDBusMethodInvocation *invocation,
+ MetaA11yManager *a11y_manager)
+{
+ MetaA11yKeyGrabber *grabber;
+
+ grabber = ensure_key_grabber (a11y_manager, invocation);
+ grabber->grab_all = TRUE;
+ meta_dbus_keyboard_monitor_complete_grab_keyboard (skeleton, invocation);
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+handle_ungrab_keyboard (MetaDBusKeyboardMonitor *skeleton,
+ GDBusMethodInvocation *invocation,
+ MetaA11yManager *a11y_manager)
+{
+ MetaA11yKeyGrabber *grabber;
+
+ grabber = ensure_key_grabber (a11y_manager, invocation);
+ grabber->grab_all = FALSE;
+ meta_dbus_keyboard_monitor_complete_ungrab_keyboard (skeleton, invocation);
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+handle_set_key_grabs (MetaDBusKeyboardMonitor *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *modifiers,
+ GVariant *keystrokes,
+ MetaA11yManager *a11y_manager)
+{
+ MetaA11yKeyGrabber *grabber;
+ GVariantIter iter;
+ uint32_t modifier_keysym;
+ MetaA11yKeystroke keystroke;
+
+ grabber = ensure_key_grabber (a11y_manager, invocation);
+
+ g_clear_pointer (&grabber->modifiers, g_array_unref);
+ g_clear_pointer (&grabber->keystrokes, g_array_unref);
+ grabber->modifiers = g_array_new (FALSE, FALSE, sizeof (uint32_t));
+ grabber->keystrokes = g_array_new (FALSE, FALSE, sizeof (MetaA11yKeystroke));
+
+ g_variant_iter_init (&iter, modifiers);
+ while (g_variant_iter_next (&iter, "u", &modifier_keysym))
+ g_array_append_val (grabber->modifiers, modifier_keysym);
+
+ g_variant_iter_init (&iter, keystrokes);
+ while (g_variant_iter_next (&iter, "(uu)", &keystroke.keysym,
+ &keystroke.modifiers))
+ g_array_append_val (grabber->keystrokes, keystroke);
+
+ rebuild_all_grabbed_modifiers (a11y_manager, NULL);
+ g_signal_emit (a11y_manager, signals[A11Y_MODIFIERS_CHANGED], 0);
+ meta_dbus_keyboard_monitor_complete_set_key_grabs (skeleton, invocation);
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ MetaA11yManager *manager = user_data;
+
+ manager->keyboard_monitor_skeleton = meta_dbus_keyboard_monitor_skeleton_new ();
+
+ g_signal_connect (manager->keyboard_monitor_skeleton, "handle-grab-keyboard",
+ G_CALLBACK (handle_grab_keyboard), manager);
+ g_signal_connect (manager->keyboard_monitor_skeleton, "handle-ungrab-keyboard",
+ G_CALLBACK (handle_ungrab_keyboard), manager);
+ g_signal_connect (manager->keyboard_monitor_skeleton, "handle-set-key-grabs",
+ G_CALLBACK (handle_set_key_grabs), manager);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager->keyboard_monitor_skeleton),
+ connection,
+ "/org/freedesktop/a11y/Manager",
+ NULL);
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ g_debug ("Acquired name %s", name);
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ g_debug ("Lost or failed to acquire name %s", name);
+}
+
+static void
+meta_a11y_manager_finalize (GObject *object)
+{
+ MetaA11yManager *a11y_manager = META_A11Y_MANAGER (object);
+
+ g_list_free_full (a11y_manager->key_grabbers,
+ (GDestroyNotify) key_grabber_free);
+ g_clear_object (&a11y_manager->keyboard_monitor_skeleton);
+ g_clear_pointer (&a11y_manager->grabbed_keypresses, g_hash_table_destroy);
+ g_clear_pointer (&a11y_manager->all_grabbed_modifiers, g_hash_table_destroy);
+ g_bus_unown_name (a11y_manager->dbus_name_id);
+
+ G_OBJECT_CLASS (meta_a11y_manager_parent_class)->finalize (object);
+}
+
static void
meta_a11y_manager_set_property (GObject *object,
guint prop_id,
@@ -71,12 +310,47 @@ meta_a11y_manager_set_property (GObject *object,
}
}
+static void
+meta_a11y_manager_constructed (GObject *object)
+{
+ MetaA11yManager *a11y_manager = META_A11Y_MANAGER (object);
+ MetaContext *context;
+
+ g_assert (a11y_manager->backend);
+ context = meta_backend_get_context (a11y_manager->backend);
+
+ a11y_manager->grabbed_keypresses = g_hash_table_new (NULL, NULL);
+ a11y_manager->all_grabbed_modifiers = g_hash_table_new (NULL, NULL);
+
+ a11y_manager->dbus_name_id =
+ g_bus_own_name (G_BUS_TYPE_SESSION,
+ "org.freedesktop.a11y.Manager",
+ G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
+ (meta_context_is_replacing (context) ?
+ G_BUS_NAME_OWNER_FLAGS_REPLACE :
+ G_BUS_NAME_OWNER_FLAGS_NONE),
+ on_bus_acquired,
+ on_name_acquired,
+ on_name_lost,
+ a11y_manager,
+ NULL);
+}
+
static void
meta_a11y_manager_class_init (MetaA11yManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = meta_a11y_manager_finalize;
object_class->set_property = meta_a11y_manager_set_property;
+ object_class->constructed = meta_a11y_manager_constructed;
+
+ signals[A11Y_MODIFIERS_CHANGED] =
+ g_signal_new ("a11y-modifiers-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
props[PROP_BACKEND] =
g_param_spec_object ("backend", NULL, NULL,
--
2.44.0.501.g19981daefd.dirty
From c60b8ee816991f6ca0ccd6e1c66b19a649a563df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 19:17:08 +0100
Subject: [PATCH 08/14] backends: Add MetaA11yManager method to get configured
a11y modifiers
The ::a11y-modifiers-change method allows tracking for changes in the
configured modifiers, add this method to allow backends to get the
modifiers so that they can be passed along the lower layers.
Carlos Garnacho: Turn into a method instead of a signal argument, turn
into an array+length instead of a hashtable.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit 1498724774976fad1dbd4e5bc210205cef1faf9d)
---
src/backends/meta-a11y-manager.c | 24 ++++++++++++++++++++++++
src/backends/meta-a11y-manager.h | 3 +++
2 files changed, 27 insertions(+)
diff --git a/src/backends/meta-a11y-manager.c b/src/backends/meta-a11y-manager.c
index 5f8d5b1ee4..647962c866 100644
--- a/src/backends/meta-a11y-manager.c
+++ b/src/backends/meta-a11y-manager.c
@@ -374,3 +374,27 @@ meta_a11y_manager_new (MetaBackend *backend)
"backend", backend,
NULL);
}
+
+uint32_t *
+meta_a11y_manager_get_modifier_keysyms (MetaA11yManager *a11y_manager,
+ int *n_modifiers)
+{
+ GArray *modifier_keysyms;
+ GHashTableIter iter;
+ gpointer key;
+
+ modifier_keysyms = g_array_new (FALSE, FALSE, sizeof (uint32_t));
+
+ g_hash_table_iter_init (&iter, a11y_manager->all_grabbed_modifiers);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ {
+ uint32_t keysym = GPOINTER_TO_UINT (key);
+
+ g_array_append_val (modifier_keysyms, keysym);
+ }
+
+ if (n_modifiers)
+ *n_modifiers = modifier_keysyms->len;
+
+ return (uint32_t *) g_array_free (modifier_keysyms, FALSE);
+}
diff --git a/src/backends/meta-a11y-manager.h b/src/backends/meta-a11y-manager.h
index df5c9a5d21..df07c94de2 100644
--- a/src/backends/meta-a11y-manager.h
+++ b/src/backends/meta-a11y-manager.h
@@ -27,3 +27,6 @@
G_DECLARE_FINAL_TYPE (MetaA11yManager, meta_a11y_manager, META, A11Y_MANAGER, GObject)
MetaA11yManager * meta_a11y_manager_new (MetaBackend *backend);
+
+uint32_t * meta_a11y_manager_get_modifier_keysyms (MetaA11yManager *a11y_manager,
+ int *n_modifiers);
--
2.44.0.501.g19981daefd.dirty
From 3ce02680be1fea0c488c9681e87dfafac315fc06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 19:03:50 +0100
Subject: [PATCH 09/14] backends/native: Propagate a11y modifiers from
MetaA11yManager
Listen to changes in MetaA11yManager configured modifiers, and propagate
these along to the MetaSeatNative. This lets the backend keep track of
configuration changes in a11y modifiers.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit 45f6bed7809dadb40df98308b9041fe08b453871)
---
src/backends/native/meta-backend-native.c | 25 +++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index ee02b2783a..8732f3f7d2 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -38,6 +38,7 @@
#include <stdlib.h>
+#include "backends/meta-a11y-manager.h"
#include "backends/meta-color-manager.h"
#include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-idle-manager.h"
@@ -183,6 +184,23 @@ update_viewports (MetaBackend *backend)
g_object_unref (viewports);
}
+static void
+on_a11y_modifiers_changed (MetaA11yManager *a11y_manager,
+ MetaBackend *backend)
+{
+ MetaSeatNative *seat;
+ ClutterBackend *clutter_backend;
+ g_autofree uint32_t *modifiers;
+ int n_modifiers;
+
+ clutter_backend = meta_backend_get_clutter_backend (backend);
+ seat = META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend));
+ modifiers = meta_a11y_manager_get_modifier_keysyms (a11y_manager,
+ &n_modifiers);
+
+ meta_seat_native_set_a11y_modifiers (seat, modifiers, n_modifiers);
+}
+
static void
meta_backend_native_post_init (MetaBackend *backend)
{
@@ -191,6 +209,7 @@ meta_backend_native_post_init (MetaBackend *backend)
meta_backend_native_get_instance_private (backend_native);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
+ MetaA11yManager *a11y_manager = meta_backend_get_a11y_manager (backend);
META_BACKEND_CLASS (meta_backend_native_parent_class)->post_init (backend);
@@ -200,6 +219,12 @@ meta_backend_native_post_init (MetaBackend *backend)
g_signal_connect_swapped (monitor_manager, "monitors-changed-internal",
G_CALLBACK (update_viewports), backend);
update_viewports (backend);
+
+ g_signal_connect_object (a11y_manager,
+ "a11y-modifiers-changed",
+ G_CALLBACK (on_a11y_modifiers_changed),
+ backend,
+ G_CONNECT_DEFAULT);
}
static MetaBackendCapabilities
--
2.44.0.501.g19981daefd.dirty
From b03c4412be7fec86f43c975e418556f17398fd75 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 19:16:41 +0100
Subject: [PATCH 10/14] backends: Add method to pass key events to screen
readers
Accessibility shortcuts and keyboard grabs need to be consumed,
and key events propagated to screen readers. Add a function that
does it all at once.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit 800981c40c4f18f496a79227b3d3d5f0cdc195c9)
---
src/backends/meta-a11y-manager.c | 148 +++++++++++++++++++++++++++++++
src/backends/meta-a11y-manager.h | 3 +
2 files changed, 151 insertions(+)
diff --git a/src/backends/meta-a11y-manager.c b/src/backends/meta-a11y-manager.c
index 647962c866..a6ec4f6f3a 100644
--- a/src/backends/meta-a11y-manager.c
+++ b/src/backends/meta-a11y-manager.c
@@ -27,6 +27,12 @@
#include "meta-dbus-a11y.h"
+#define MOUSE_BUTTONS_MASK (CLUTTER_BUTTON1_MASK | \
+ CLUTTER_BUTTON2_MASK | \
+ CLUTTER_BUTTON3_MASK | \
+ CLUTTER_BUTTON4_MASK | \
+ CLUTTER_BUTTON5_MASK)
+
enum
{
A11Y_MODIFIERS_CHANGED,
@@ -375,6 +381,148 @@ meta_a11y_manager_new (MetaBackend *backend)
NULL);
}
+static gboolean
+should_grab_keypress (MetaA11yManager *a11y_manager,
+ MetaA11yKeyGrabber *grabber,
+ uint32_t keysym,
+ ClutterModifierType modifiers)
+{
+ int i;
+
+ if (grabber->grab_all)
+ return TRUE;
+
+ if (grabber->modifiers)
+ {
+ for (i = 0; i < grabber->modifiers->len; i++)
+ {
+ uint32_t modifier_keysym;
+
+ modifier_keysym = g_array_index (grabber->modifiers, uint32_t, i);
+
+ if (keysym == modifier_keysym ||
+ g_hash_table_contains (a11y_manager->grabbed_keypresses,
+ GUINT_TO_POINTER (modifier_keysym)))
+ return TRUE;
+ }
+ }
+
+ if (grabber->keystrokes)
+ {
+ for (i = 0; i < grabber->keystrokes->len; i++)
+ {
+ MetaA11yKeystroke *keystroke =
+ &(g_array_index (grabber->keystrokes, MetaA11yKeystroke, i));
+
+ if (keysym == keystroke->keysym && modifiers == keystroke->modifiers)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+is_grabbed_modifier_key (MetaA11yManager *a11y_manager,
+ uint32_t keysym)
+{
+ return g_hash_table_contains (a11y_manager->all_grabbed_modifiers,
+ GUINT_TO_POINTER (keysym));
+}
+
+static void
+notify_client (MetaA11yManager *a11y_manager,
+ MetaA11yKeyGrabber *key_grabber,
+ gboolean released,
+ ClutterModifierType state,
+ uint32_t keysym,
+ uint32_t unichar,
+ uint32_t keycode)
+{
+ g_autoptr (GError) error = NULL;
+
+ if (!g_dbus_connection_emit_signal (key_grabber->connection,
+ key_grabber->bus_name,
+ "/org/freedesktop/a11y/Manager",
+ "org.freedesktop.a11y.KeyboardMonitor",
+ "KeyEvent",
+ g_variant_new ("(buuuq)",
+ released,
+ state,
+ keysym,
+ unichar,
+ (uint16_t) keycode),
+ &error))
+ g_warning ("Could not emit a11y KeyEvent: %s", error->message);
+}
+
+gboolean
+meta_a11y_manager_notify_clients (MetaA11yManager *a11y_manager,
+ const ClutterEvent *event)
+{
+ gboolean a11y_grabbed = FALSE;
+ gboolean released = clutter_event_type (event) == CLUTTER_KEY_RELEASE;
+ /* A grabbed modifier is a11y grabbed if it was not double pressed, otherwise we process it normally */
+ gboolean is_ignorable =
+ !!(clutter_event_get_flags (event) &
+ CLUTTER_EVENT_FLAG_A11Y_MODIFIER_FIRST_CLICK);
+ /* The Clutter event modifiers mask includes mouse buttons as well,
+ * but they're not expected by ATs, so we filter them out.
+ */
+ uint32_t keysym = clutter_event_get_key_symbol (event);
+ uint32_t unichar = clutter_event_get_key_unicode (event);
+ uint32_t keycode = clutter_event_get_key_code (event);
+ ClutterModifierType state;
+ GList *l;
+
+ state = clutter_event_get_state (event) & ~MOUSE_BUTTONS_MASK;
+
+ for (l = a11y_manager->key_grabbers; l; l = l->next)
+ {
+ MetaA11yKeyGrabber *grabber = l->data;
+
+ if (should_grab_keypress (a11y_manager, grabber, keysym, state))
+ {
+ notify_client (a11y_manager, grabber, released,
+ state, keysym, unichar, keycode);
+ }
+ }
+
+ if (is_grabbed_modifier_key (a11y_manager, keysym) && !is_ignorable)
+ return FALSE;
+
+ if (released)
+ {
+ if (g_hash_table_contains (a11y_manager->grabbed_keypresses,
+ GUINT_TO_POINTER (keysym)))
+ {
+ g_hash_table_remove (a11y_manager->grabbed_keypresses,
+ GUINT_TO_POINTER (keysym));
+ a11y_grabbed = TRUE;
+ }
+ }
+ else
+ {
+ if (g_hash_table_contains (a11y_manager->grabbed_keypresses,
+ GUINT_TO_POINTER (keysym)))
+ a11y_grabbed = TRUE;
+
+ for (l = a11y_manager->key_grabbers; l; l = l->next)
+ {
+ MetaA11yKeyGrabber *grabber = l->data;
+
+ if (should_grab_keypress (a11y_manager, grabber, keysym, state))
+ {
+ g_hash_table_add (a11y_manager->grabbed_keypresses,
+ GUINT_TO_POINTER (keysym));
+ a11y_grabbed = TRUE;
+ }
+ }
+ }
+
+ return a11y_grabbed;
+}
+
uint32_t *
meta_a11y_manager_get_modifier_keysyms (MetaA11yManager *a11y_manager,
int *n_modifiers)
diff --git a/src/backends/meta-a11y-manager.h b/src/backends/meta-a11y-manager.h
index df07c94de2..754cb65394 100644
--- a/src/backends/meta-a11y-manager.h
+++ b/src/backends/meta-a11y-manager.h
@@ -28,5 +28,8 @@ G_DECLARE_FINAL_TYPE (MetaA11yManager, meta_a11y_manager, META, A11Y_MANAGER, GO
MetaA11yManager * meta_a11y_manager_new (MetaBackend *backend);
+gboolean meta_a11y_manager_notify_clients (MetaA11yManager *a11y_manager,
+ const ClutterEvent *event);
+
uint32_t * meta_a11y_manager_get_modifier_keysyms (MetaA11yManager *a11y_manager,
int *n_modifiers);
--
2.44.0.501.g19981daefd.dirty
From 921a010f93b4b89d50b7ba97666af367bd8719a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Fri, 7 Feb 2025 14:21:17 +0100
Subject: [PATCH 11/14] backends: Add a11y interface methods to subscribe to
key event input
This adds a pair of methods to signal an interest in receiving
all key events without grabbing them, e. g. the previously expected behavior
by screen readers.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit 20c9b8cf0c85dabbe73b5b0ce155729a92f47bb0)
---
data/dbus-interfaces/org.freedesktop.a11y.xml | 31 +++++++++++-
src/backends/meta-a11y-manager.c | 47 ++++++++++++++++++-
2 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/data/dbus-interfaces/org.freedesktop.a11y.xml b/data/dbus-interfaces/org.freedesktop.a11y.xml
index c60480e89c..4f1d210423 100644
--- a/data/dbus-interfaces/org.freedesktop.a11y.xml
+++ b/data/dbus-interfaces/org.freedesktop.a11y.xml
@@ -16,7 +16,7 @@
GrabKeyboard:
Starts grabbing all key events. The client receives the events
- through the KeyEvent signal as always, but the events aren't handled
+ through the KeyEvent signal, and in addition, the events aren't handled
normally by the compositor. This includes changes to the state
of toggles like Caps Lock, Num Lock, and Scroll Lock.
@@ -33,9 +33,36 @@
After calling this method, the key grabs specified in the last call
to SetKeyGrabs, if any, are still in effect.
+ Also, the client will still receive key events through the KeyEvent
+ signal, if it has called WatchKeyboard.
-->
<method name="UngrabKeyboard" />
+ <!--
+ WatchKeyboard:
+
+ Starts watching all key events. The client receives the events
+ through the KeyEvent signal, but the events are still handled
+ normally by the compositor. This includes changes to the state
+ of toggles like Caps Lock, Num Lock, and Scroll Lock.
+
+ This behavior stays in effect until the same client calls
+ UnwatchKeyboard or closes its D-Bus connection.
+ -->
+ <method name="WatchKeyboard" />
+
+ <!--
+ UnwatchKeyboard:
+
+ Reverses the effect of calling WatchKeyboard. If WatchKeyboard wasn't
+ previously called, this method does nothing.
+
+ After calling this method, the key grabs specified in the last call
+ to SetKeyGrabs, if any, are still in effect,
+ but other key events are no longer reported to this client.
+ -->
+ <method name="UnwatchKeyboard" />
+
<!--
SetKeyGrabs:
@modifiers: set of custom modifiers to grab
@@ -43,7 +70,7 @@
Sets the current key grabs for the calling client, overriding
any previous call to this method. For grabbed key events, the
- KeyEvent signal is still emitted, but normal key event handling
+ KeyEvent signal is emitted, and normal key event handling
is suppressed, including state changes for toggles like Caps Lock
and Num Lock.
diff --git a/src/backends/meta-a11y-manager.c b/src/backends/meta-a11y-manager.c
index a6ec4f6f3a..5001d114a2 100644
--- a/src/backends/meta-a11y-manager.c
+++ b/src/backends/meta-a11y-manager.c
@@ -63,6 +63,7 @@ typedef struct _MetaA11yKeyGrabber
char *bus_name;
guint bus_name_watcher_id;
gboolean grab_all;
+ gboolean watch_all;
GArray *modifiers;
GArray *keystrokes;
} MetaA11yKeyGrabber;
@@ -209,6 +210,34 @@ handle_ungrab_keyboard (MetaDBusKeyboardMonitor *skeleton,
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
+static gboolean
+handle_watch_keyboard (MetaDBusKeyboardMonitor *skeleton,
+ GDBusMethodInvocation *invocation,
+ MetaA11yManager *a11y_manager)
+{
+ MetaA11yKeyGrabber *grabber;
+
+ grabber = ensure_key_grabber (a11y_manager, invocation);
+ grabber->watch_all = TRUE;
+ meta_dbus_keyboard_monitor_complete_watch_keyboard (skeleton, invocation);
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
+static gboolean
+handle_unwatch_keyboard (MetaDBusKeyboardMonitor *skeleton,
+ GDBusMethodInvocation *invocation,
+ MetaA11yManager *a11y_manager)
+{
+ MetaA11yKeyGrabber *grabber;
+
+ grabber = ensure_key_grabber (a11y_manager, invocation);
+ grabber->watch_all = FALSE;
+ meta_dbus_keyboard_monitor_complete_unwatch_keyboard (skeleton, invocation);
+
+ return G_DBUS_METHOD_INVOCATION_HANDLED;
+}
+
static gboolean
handle_set_key_grabs (MetaDBusKeyboardMonitor *skeleton,
GDBusMethodInvocation *invocation,
@@ -257,6 +286,10 @@ on_bus_acquired (GDBusConnection *connection,
G_CALLBACK (handle_grab_keyboard), manager);
g_signal_connect (manager->keyboard_monitor_skeleton, "handle-ungrab-keyboard",
G_CALLBACK (handle_ungrab_keyboard), manager);
+ g_signal_connect (manager->keyboard_monitor_skeleton, "handle-watch-keyboard",
+ G_CALLBACK (handle_watch_keyboard), manager);
+ g_signal_connect (manager->keyboard_monitor_skeleton, "handle-unwatch-keyboard",
+ G_CALLBACK (handle_unwatch_keyboard), manager);
g_signal_connect (manager->keyboard_monitor_skeleton, "handle-set-key-grabs",
G_CALLBACK (handle_set_key_grabs), manager);
@@ -422,6 +455,18 @@ should_grab_keypress (MetaA11yManager *a11y_manager,
return FALSE;
}
+static gboolean
+should_watch_keypress (MetaA11yManager *a11y_manager,
+ MetaA11yKeyGrabber *grabber,
+ uint32_t keysym,
+ ClutterModifierType modifiers)
+{
+ if (grabber->watch_all)
+ return TRUE;
+
+ return should_grab_keypress (a11y_manager, grabber, keysym, modifiers);
+}
+
static gboolean
is_grabbed_modifier_key (MetaA11yManager *a11y_manager,
uint32_t keysym)
@@ -481,7 +526,7 @@ meta_a11y_manager_notify_clients (MetaA11yManager *a11y_manager,
{
MetaA11yKeyGrabber *grabber = l->data;
- if (should_grab_keypress (a11y_manager, grabber, keysym, state))
+ if (should_watch_keypress (a11y_manager, grabber, keysym, state))
{
notify_client (a11y_manager, grabber, released,
state, keysym, unichar, keycode);
--
2.44.0.501.g19981daefd.dirty
From 0abd8b0de03c9125a0a7e4871eedd9c9d7376357 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Mon, 10 Feb 2025 16:26:49 +0100
Subject: [PATCH 12/14] backends: Add D-Bus access control helper
Track unique DBus senders and allow only thse which own a (at least so far)
pre-defined set of well known DBus names.
Carlos Garnacho: Renamed to a more generic helper, use g_bus_watch_name().
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit 1c34794b1390ab9d00ab7b7cf47b5017346e36ef)
---
src/backends/meta-a11y-manager.c | 33 ++++
src/backends/meta-dbus-access-checker.c | 212 ++++++++++++++++++++++++
src/backends/meta-dbus-access-checker.h | 39 +++++
src/core/meta-context-private.h | 2 +
src/core/meta-context.c | 8 +
src/meson.build | 2 +
6 files changed, 296 insertions(+)
create mode 100644 src/backends/meta-dbus-access-checker.c
create mode 100644 src/backends/meta-dbus-access-checker.h
diff --git a/src/backends/meta-a11y-manager.c b/src/backends/meta-a11y-manager.c
index 5001d114a2..c51e2f712a 100644
--- a/src/backends/meta-a11y-manager.c
+++ b/src/backends/meta-a11y-manager.c
@@ -21,6 +21,7 @@
#include "config.h"
#include "backends/meta-a11y-manager.h"
+#include "backends/meta-dbus-access-checker.h"
#include "meta/meta-backend.h"
#include "meta/meta-context.h"
#include "meta/util.h"
@@ -78,6 +79,8 @@ typedef struct _MetaA11yManager
GList *key_grabbers;
GHashTable *grabbed_keypresses;
GHashTable *all_grabbed_modifiers;
+
+ MetaDbusAccessChecker *access_checker;
} MetaA11yManager;
G_DEFINE_TYPE (MetaA11yManager, meta_a11y_manager, G_TYPE_OBJECT)
@@ -182,6 +185,28 @@ ensure_key_grabber (MetaA11yManager *a11y_manager,
return grabber;
}
+static gboolean
+check_access (GDBusInterfaceSkeleton *skeleton,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ MetaA11yManager *a11y_manager = META_A11Y_MANAGER (user_data);
+ const char *sender =
+ g_dbus_method_invocation_get_sender (invocation);
+
+ if (!meta_dbus_access_checker_is_sender_allowed (a11y_manager->access_checker,
+ sender))
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "Access denied");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static gboolean
handle_grab_keyboard (MetaDBusKeyboardMonitor *skeleton,
GDBusMethodInvocation *invocation,
@@ -279,9 +304,12 @@ on_bus_acquired (GDBusConnection *connection,
gpointer user_data)
{
MetaA11yManager *manager = user_data;
+ MetaContext *context = meta_backend_get_context (manager->backend);
manager->keyboard_monitor_skeleton = meta_dbus_keyboard_monitor_skeleton_new ();
+ g_signal_connect (manager->keyboard_monitor_skeleton, "g-authorize-method",
+ G_CALLBACK (check_access), manager);
g_signal_connect (manager->keyboard_monitor_skeleton, "handle-grab-keyboard",
G_CALLBACK (handle_grab_keyboard), manager);
g_signal_connect (manager->keyboard_monitor_skeleton, "handle-ungrab-keyboard",
@@ -297,6 +325,10 @@ on_bus_acquired (GDBusConnection *connection,
connection,
"/org/freedesktop/a11y/Manager",
NULL);
+
+ manager->access_checker = meta_dbus_access_checker_new (connection, context);
+ meta_dbus_access_checker_allow_sender (manager->access_checker,
+ "org.gnome.Orca.KeyboardMonitor");
}
static void
@@ -323,6 +355,7 @@ meta_a11y_manager_finalize (GObject *object)
g_list_free_full (a11y_manager->key_grabbers,
(GDestroyNotify) key_grabber_free);
g_clear_object (&a11y_manager->keyboard_monitor_skeleton);
+ g_clear_object (&a11y_manager->access_checker);
g_clear_pointer (&a11y_manager->grabbed_keypresses, g_hash_table_destroy);
g_clear_pointer (&a11y_manager->all_grabbed_modifiers, g_hash_table_destroy);
g_bus_unown_name (a11y_manager->dbus_name_id);
diff --git a/src/backends/meta-dbus-access-checker.c b/src/backends/meta-dbus-access-checker.c
new file mode 100644
index 0000000000..0ace126077
--- /dev/null
+++ b/src/backends/meta-dbus-access-checker.c
@@ -0,0 +1,212 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2024 GNOME Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "meta-dbus-access-checker.h"
+
+#include "core/meta-context-private.h"
+
+enum
+{
+ PROP_0,
+ PROP_CONNECTION,
+ PROP_CONTEXT,
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS];
+
+typedef struct _AllowedSender AllowedSender;
+
+struct _AllowedSender
+{
+ char *name;
+ char *name_owner;
+ guint watch_id;
+};
+
+struct _MetaDbusAccessChecker
+{
+ GObject parent_instance;
+ GDBusConnection *connection;
+ GPtrArray *allowed_senders;
+ MetaContext *context;
+};
+
+G_DEFINE_TYPE (MetaDbusAccessChecker, meta_dbus_access_checker, G_TYPE_OBJECT)
+
+static void
+name_appeared_cb (GDBusConnection *connection,
+ const char *name,
+ const char *name_owner,
+ gpointer user_data)
+{
+ AllowedSender *allowed_sender = user_data;
+
+ allowed_sender->name_owner = g_strdup (name_owner);
+}
+
+static void
+name_vanished_cb (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ AllowedSender *allowed_sender = user_data;
+
+ g_clear_pointer (&allowed_sender->name_owner, g_free);
+}
+
+static AllowedSender *
+allowed_sender_new (MetaDbusAccessChecker *self,
+ const char *name)
+{
+ AllowedSender *allowed_sender;
+
+ allowed_sender = g_new0 (AllowedSender, 1);
+ allowed_sender->name = g_strdup (name);
+ allowed_sender->watch_id =
+ g_bus_watch_name_on_connection (self->connection,
+ name,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ name_appeared_cb,
+ name_vanished_cb,
+ allowed_sender,
+ NULL);
+
+ return allowed_sender;
+}
+
+static void
+allowed_sender_free (AllowedSender *allowed_sender)
+{
+ g_clear_pointer (&allowed_sender->name, g_free);
+ g_clear_pointer (&allowed_sender->name_owner, g_free);
+ g_bus_unwatch_name (allowed_sender->watch_id);
+ g_free (allowed_sender);
+}
+
+static void
+meta_dbus_access_checker_init (MetaDbusAccessChecker *self)
+{
+ self->allowed_senders =
+ g_ptr_array_new_with_free_func ((GDestroyNotify) allowed_sender_free);
+}
+
+static void
+meta_dbus_access_checker_finalize (GObject *object)
+{
+ MetaDbusAccessChecker *self =
+ META_DBUS_ACCESS_CHECKER (object);
+
+ g_clear_pointer (&self->allowed_senders, g_ptr_array_unref);
+ g_clear_object (&self->connection);
+
+ G_OBJECT_CLASS (meta_dbus_access_checker_parent_class)->finalize (object);
+}
+
+static void
+meta_dbus_access_checker_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MetaDbusAccessChecker *self = META_DBUS_ACCESS_CHECKER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ self->connection = g_value_dup_object (value);
+ break;
+ case PROP_CONTEXT:
+ self->context = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_dbus_access_checker_class_init (MetaDbusAccessCheckerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = meta_dbus_access_checker_finalize;
+ object_class->set_property = meta_dbus_access_checker_set_property;
+
+ props[PROP_CONNECTION] =
+ g_param_spec_object ("connection", NULL, NULL,
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ props[PROP_CONTEXT] =
+ g_param_spec_object ("context", NULL, NULL,
+ META_TYPE_CONTEXT,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+MetaDbusAccessChecker *
+meta_dbus_access_checker_new (GDBusConnection *connection,
+ MetaContext *context)
+{
+ return g_object_new (META_TYPE_DBUS_ACCESS_CHECKER,
+ "connection", connection,
+ "context", context,
+ NULL);
+}
+
+void
+meta_dbus_access_checker_allow_sender (MetaDbusAccessChecker *self,
+ const char *name)
+{
+ AllowedSender *allowed_sender;
+
+ allowed_sender = allowed_sender_new (self, name);
+ g_ptr_array_add (self->allowed_senders, allowed_sender);
+}
+
+gboolean
+meta_dbus_access_checker_is_sender_allowed (MetaDbusAccessChecker *self,
+ const char *sender_name)
+{
+ int i;
+
+ if (meta_context_get_unsafe_mode (self->context))
+ return TRUE;
+
+ for (i = 0; i < self->allowed_senders->len; i++)
+ {
+ AllowedSender *allowed_sender;
+
+ allowed_sender = g_ptr_array_index (self->allowed_senders, i);
+
+ if (sender_name &&
+ g_strcmp0 (allowed_sender->name_owner, sender_name) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/src/backends/meta-dbus-access-checker.h b/src/backends/meta-dbus-access-checker.h
new file mode 100644
index 0000000000..acbb942c0c
--- /dev/null
+++ b/src/backends/meta-dbus-access-checker.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024 GNOME Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "meta/meta-context.h"
+
+#define META_TYPE_DBUS_ACCESS_CHECKER (meta_dbus_access_checker_get_type())
+G_DECLARE_FINAL_TYPE (MetaDbusAccessChecker,
+ meta_dbus_access_checker,
+ META, DBUS_ACCESS_CHECKER,
+ GObject)
+
+MetaDbusAccessChecker * meta_dbus_access_checker_new (GDBusConnection *connection,
+ MetaContext *context);
+
+void meta_dbus_access_checker_allow_sender (MetaDbusAccessChecker *self,
+ const char *name);
+
+gboolean meta_dbus_access_checker_is_sender_allowed (MetaDbusAccessChecker *self,
+ const char *sender_name);
diff --git a/src/core/meta-context-private.h b/src/core/meta-context-private.h
index a53f901fb6..c5120f2e37 100644
--- a/src/core/meta-context-private.h
+++ b/src/core/meta-context-private.h
@@ -70,6 +70,8 @@ const char * meta_context_get_gnome_wm_keybindings (MetaContext *context);
void meta_context_set_unsafe_mode (MetaContext *context,
gboolean enable);
+gboolean meta_context_get_unsafe_mode (MetaContext *context);
+
#ifdef HAVE_WAYLAND
META_EXPORT_TEST
MetaServiceChannel * meta_context_get_service_channel (MetaContext *context);
diff --git a/src/core/meta-context.c b/src/core/meta-context.c
index 996dcdd078..b5c51009a6 100644
--- a/src/core/meta-context.c
+++ b/src/core/meta-context.c
@@ -620,6 +620,14 @@ meta_context_destroy (MetaContext *context)
g_object_unref (context);
}
+gboolean
+meta_context_get_unsafe_mode (MetaContext *context)
+{
+ MetaContextPrivate *priv = meta_context_get_instance_private (context);
+
+ return priv->unsafe_mode;
+}
+
void
meta_context_set_unsafe_mode (MetaContext *context,
gboolean enable)
diff --git a/src/meson.build b/src/meson.build
index c57fd935f6..47c1d0575d 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -220,6 +220,8 @@ mutter_sources = [
'backends/meta-cursor-sprite-xcursor.h',
'backends/meta-cursor-tracker.c',
'backends/meta-cursor-tracker-private.h',
+ 'backends/meta-dbus-access-checker.c',
+ 'backends/meta-dbus-access-checker.h',
'backends/meta-dbus-session-manager.c',
'backends/meta-dbus-session-manager.h',
'backends/meta-dbus-session-watcher.c',
--
2.44.0.501.g19981daefd.dirty
From 71a2979a5382b1b5dc986c717a228ce8c97c0f9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Mon, 17 Feb 2025 16:34:21 +0100
Subject: [PATCH 13/14] core: Add a debug control override to disable the a11y
manager ACL
This commit adds another way how to disable the a11y manager ACL in development.
Setting the unsafe mode might not be as straightforward as setting an
environment variable for the session, and you can control it invidually too.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit a817d7c1377e422e43d28fc1d5ab4e2c5e77b9cf)
---
.../org.gnome.Mutter.DebugControl.xml | 2 +-
src/backends/meta-a11y-manager.c | 8 ++++++++
src/core/meta-debug-control-private.h | 2 ++
src/core/meta-debug-control.c | 15 +++++++++++++++
4 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/data/dbus-interfaces/org.gnome.Mutter.DebugControl.xml b/data/dbus-interfaces/org.gnome.Mutter.DebugControl.xml
index 14142a1d91..63dbf645f9 100644
--- a/data/dbus-interfaces/org.gnome.Mutter.DebugControl.xml
+++ b/data/dbus-interfaces/org.gnome.Mutter.DebugControl.xml
@@ -11,7 +11,7 @@
<property name="LuminancePercentage" type="u" access="readwrite" />
<property name="SessionManagementProtocol" type="b" access="readwrite" />
<property name="InhibitHwCursor" type="b" access="readwrite" />
-
+ <property name="A11yManagerWithoutAccessControl" type="b" access="readwrite" />
</interface>
</node>
diff --git a/src/backends/meta-a11y-manager.c b/src/backends/meta-a11y-manager.c
index c51e2f712a..c30f7c3911 100644
--- a/src/backends/meta-a11y-manager.c
+++ b/src/backends/meta-a11y-manager.c
@@ -22,6 +22,7 @@
#include "backends/meta-a11y-manager.h"
#include "backends/meta-dbus-access-checker.h"
+#include "core/meta-debug-control-private.h"
#include "meta/meta-backend.h"
#include "meta/meta-context.h"
#include "meta/util.h"
@@ -191,8 +192,15 @@ check_access (GDBusInterfaceSkeleton *skeleton,
gpointer user_data)
{
MetaA11yManager *a11y_manager = META_A11Y_MANAGER (user_data);
+ MetaContext *context =
+ meta_backend_get_context (a11y_manager->backend);
const char *sender =
g_dbus_method_invocation_get_sender (invocation);
+ MetaDebugControl *debug_control =
+ meta_context_get_debug_control (context);
+
+ if (meta_debug_control_is_a11y_manager_without_access_control (debug_control))
+ return TRUE;
if (!meta_dbus_access_checker_is_sender_allowed (a11y_manager->access_checker,
sender))
diff --git a/src/core/meta-debug-control-private.h b/src/core/meta-debug-control-private.h
index 8aeae09999..1e1cf49278 100644
--- a/src/core/meta-debug-control-private.h
+++ b/src/core/meta-debug-control-private.h
@@ -31,3 +31,5 @@ unsigned int meta_debug_control_get_luminance_percentage (MetaDebugControl *debu
gboolean meta_debug_control_is_session_management_protocol_enabled (MetaDebugControl *debug_control);
gboolean meta_debug_control_is_hw_cursor_inhibited (MetaDebugControl *debug_control);
+
+gboolean meta_debug_control_is_a11y_manager_without_access_control (MetaDebugControl *debug_control);
diff --git a/src/core/meta-debug-control.c b/src/core/meta-debug-control.c
index 8824ebf540..b71cccac97 100644
--- a/src/core/meta-debug-control.c
+++ b/src/core/meta-debug-control.c
@@ -171,6 +171,7 @@ meta_debug_control_init (MetaDebugControl *debug_control)
gboolean enable_hdr, force_linear_blending, color_management_protocol;
gboolean session_management_protocol;
gboolean inhibit_hw_cursor;
+ gboolean a11y_manager_without_access_control;
color_management_protocol =
g_strcmp0 (getenv ("MUTTER_DEBUG_COLOR_MANAGEMENT_PROTOCOL"), "1") == 0;
@@ -196,6 +197,11 @@ meta_debug_control_init (MetaDebugControl *debug_control)
g_strcmp0 (getenv ("MUTTER_DEBUG_INHIBIT_HW_CURSOR"), "1") == 0;
meta_dbus_debug_control_set_inhibit_hw_cursor (dbus_debug_control,
inhibit_hw_cursor);
+
+ a11y_manager_without_access_control =
+ g_strcmp0 (getenv ("MUTTER_DEBUG_A11Y_MANAGER_WITHOUT_ACCESS_CONTROL"), "1") == 0;
+ meta_dbus_debug_control_set_a11y_manager_without_access_control (dbus_debug_control,
+ a11y_manager_without_access_control);
}
gboolean
@@ -279,3 +285,12 @@ meta_debug_control_is_hw_cursor_inhibited (MetaDebugControl *debug_control)
return meta_dbus_debug_control_get_inhibit_hw_cursor (dbus_debug_control);
}
+
+gboolean
+meta_debug_control_is_a11y_manager_without_access_control (MetaDebugControl *debug_control)
+{
+ MetaDBusDebugControl *dbus_debug_control =
+ META_DBUS_DEBUG_CONTROL (debug_control);
+
+ return meta_dbus_debug_control_get_a11y_manager_without_access_control (dbus_debug_control);
+}
--
2.44.0.501.g19981daefd.dirty
From 5c489358384a16f7a2372a1088761d1c54bf8174 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= <ltyrycht@redhat.com>
Date: Wed, 5 Feb 2025 19:04:34 +0100
Subject: [PATCH 14/14] core: Let the MetaA11yManager handle keyboard events
Plumb the MetaA11yManager into key event handling. This manager
is partly in control of keyboard event propagation, depending on
the screen readers connected.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4217>
(cherry picked from commit 5eaed6e3f3a1e14694580dbaa0585c5610c72836)
---
src/core/events.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/core/events.c b/src/core/events.c
index fb4c7574c8..b0b30839ad 100644
--- a/src/core/events.c
+++ b/src/core/events.c
@@ -24,6 +24,7 @@
#include "core/events.h"
+#include "backends/meta-a11y-manager.h"
#include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-dnd-private.h"
#include "backends/meta-idle-manager.h"
@@ -228,6 +229,7 @@ meta_display_handle_event (MetaDisplay *display,
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
+ MetaA11yManager *a11y_manager = meta_backend_get_a11y_manager (backend);
MetaCompositor *compositor = meta_display_get_compositor (display);
ClutterInputDevice *device;
MetaWindow *window = NULL;
@@ -235,6 +237,7 @@ meta_display_handle_event (MetaDisplay *display,
ClutterEventSequence *sequence;
ClutterEventType event_type;
gboolean has_grab;
+ gboolean a11y_grabbed;
MetaTabletActionMapper *mapper;
#ifdef HAVE_WAYLAND
MetaWaylandCompositor *wayland_compositor;
@@ -263,6 +266,13 @@ meta_display_handle_event (MetaDisplay *display,
if (meta_display_process_captured_input (display, event))
return CLUTTER_EVENT_STOP;
+ if (IS_KEY_EVENT (event_type))
+ {
+ a11y_grabbed = meta_a11y_manager_notify_clients (a11y_manager, event);
+ if (a11y_grabbed)
+ return CLUTTER_EVENT_STOP;
+ }
+
device = clutter_event_get_device (event);
clutter_input_pointer_a11y_update (device, event);
--
2.44.0.501.g19981daefd.dirty