diff options
author | Mike Gorse <mgorse@alum.wpi.edu> | 2019-08-05 14:31:58 -0500 |
---|---|---|
committer | Mike Gorse <mgorse@alum.wpi.edu> | 2019-08-05 14:31:58 -0500 |
commit | 673340d60a0bf7b97863985f02854b943ee009bb (patch) | |
tree | f949e12e42296b07db963bea4458e8a0ce16f5c6 /atspi/atspi-mutter.c | |
parent | 3ab9289a679e7dba0312e3379e187a2487e0a703 (diff) | |
download | at-spi2-core-673340d60a0bf7b97863985f02854b943ee009bb.tar.gz |
Support mutter remote desktop interface for key/mouse events
Mutter now has a remote desktop interface with methods to synthesize keyboard
and mouse events. This functionality has not been available through AT-SPI on
wayland since it uses X-specific functions.
Also add atspi_set_reference_window.
Probably a work in progress; doesn't appear to be behaving as intended.
Also requires that introspection be enabled on gnome-shell. Ie,
gsettings set org.gnome.shell introspect true
https://bugzilla.gnome.org/show_bug.cgi?id=709999
https://gitlab.gnome.org/GNOME/at-spi2-core/issues/14
Diffstat (limited to 'atspi/atspi-mutter.c')
-rw-r--r-- | atspi/atspi-mutter.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/atspi/atspi-mutter.c b/atspi/atspi-mutter.c new file mode 100644 index 00000000..80106807 --- /dev/null +++ b/atspi/atspi-mutter.c @@ -0,0 +1,407 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2019 SUSE LLC. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* atspi-mutter.c: support for keyboard/mouse handling using the + * mutter/gnome-shell remote desktop interfaces + * + * This functionality is analogous to the X11-based code in + * device-event-controller-x11.c. Placing the code here, rather than in the + * registry daemon, allows the relevant dbus calls to come directly from the + * AT-SPI client, rather than at-spi2-registryd being an intermediary, + * which may be useful if a distribution wishes to lock down access to the + * remote desktop interfaces. + */ + +#include "atspi-private.h" + +typedef struct +{ + DBusConnection *bus; + const char *rd_session_id; + const char *rd_session_path; + const char *sc_session_id; + const char *sc_session_path; + const char *sc_stream_path; + dbus_uint64_t window_id; + gboolean window_id_is_explicit; +} ATSPI_MUTTER_DATA; + +static ATSPI_MUTTER_DATA data; + +#define MUTTER_REMOTE_DESKTOP_BUS_NAME "org.gnome.Mutter.RemoteDesktop" +#define MUTTER_REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/Mutter/RemoteDesktop" +#define MUTTER_REMOTE_DESKTOP_INTERFACE "org.gnome.Mutter.RemoteDesktop" +#define MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE "org.gnome.Mutter.RemoteDesktop.Session" +#define MUTTER_SCREEN_CAST_BUS_NAME "org.gnome.Mutter.ScreenCast" +#define MUTTER_SCREEN_CAST_OBJECT_PATH "/org/gnome/Mutter/ScreenCast" +#define MUTTER_SCREEN_CAST_INTERFACE "org.gnome.Mutter.ScreenCast" +#define MUTTER_SCREEN_CAST_SESSION_INTERFACE "org.gnome.Mutter.ScreenCast.Session" + +/* TODO: consider porting this to gdbus */ + +static void +ensure_bus () +{ + if (data.bus) + return; + data.bus = dbus_bus_get (DBUS_BUS_SESSION, NULL); +} + +static gboolean +ensure_rd_session_path (GError **error) +{ + char *session_path; + DBusError d_error; + + if (data.rd_session_path) + return (data.rd_session_path[0] != '\0'); + ensure_bus (); + + dbus_error_init (&d_error); + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, MUTTER_REMOTE_DESKTOP_OBJECT_PATH, MUTTER_REMOTE_DESKTOP_INTERFACE, "CreateSession", &d_error, "=>o", &session_path); + + data.rd_session_path = g_strdup (session_path); + if (!data.rd_session_path[0]) + return FALSE; + + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "Start", &d_error, ""); + return TRUE; +} + +static dbus_uint64_t +get_window_id (const char *name) +{ + DBusMessage *message, *reply; + DBusError d_error; + dbus_uint64_t window_id; + DBusMessageIter iter, iter_array, iter_dict, iter_sub_array, iter_sub_dict; + const char *prop_name; + const char *cur_name; + dbus_bool_t cur_focus; + gboolean have_focus; + + dbus_error_init (&d_error); + message = dbus_message_new_method_call (MUTTER_REMOTE_DESKTOP_BUS_NAME, "/org/gnome/Shell/Introspect", "org.gnome.Shell.Introspect", "GetWindows"); + reply = dbus_connection_send_with_reply_and_block (data.bus, message, -1, &d_error); + dbus_message_unref (message); + + if (!reply) + return FALSE; + if (strcmp (dbus_message_get_signature (reply), "a{ta{sv}}") != 0) + { + dbus_message_unref (reply); + return FALSE; + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_array); + while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) + { + dbus_message_iter_recurse (&iter_array, &iter_dict); + dbus_message_iter_get_basic (&iter_dict, &window_id); + dbus_message_iter_next (&iter_dict); + dbus_message_iter_recurse (&iter_dict, &iter_sub_array); + cur_name = NULL; + have_focus = FALSE; + while (dbus_message_iter_get_arg_type (&iter_sub_array) != DBUS_TYPE_INVALID) + { + dbus_message_iter_recurse (&iter_sub_array, &iter_sub_dict); + dbus_message_iter_get_basic (&iter_sub_dict, &prop_name); + if (!strcmp (prop_name, "wm-class")) + { + DBusMessageIter iter_variant; + dbus_message_iter_next (&iter_sub_dict); + dbus_message_iter_recurse (&iter_sub_dict, &iter_variant); + dbus_message_iter_get_basic (&iter_variant, &cur_name); + } + if (!strcmp (prop_name, "has-focus")) + { + DBusMessageIter iter_variant; + dbus_message_iter_next (&iter_sub_dict); + dbus_message_iter_recurse (&iter_sub_dict, &iter_variant); + dbus_message_iter_get_basic (&iter_variant, &cur_focus); + have_focus = TRUE; + } + if (cur_name && have_focus) + { + if ((name && !strcmp (name, cur_name)) || cur_focus) + { + dbus_message_unref (reply); + return window_id; + } + break; + } + dbus_message_iter_next (&iter_sub_array); + } + dbus_message_iter_next (&iter_array); + } + + dbus_message_unref (reply); + return 0; +} + +static gboolean +ensure_rd_session_id (GError **error) +{ + DBusMessage *message, *reply; + DBusError d_error; + const char *interface = "org.gnome.Mutter.RemoteDesktop.Session"; + const char *prop_name = "SessionId"; + DBusMessageIter iter, iter_variant; + const char *session_id; + + if (data.rd_session_id) + return (data.rd_session_id[0] != '\0'); + + if (!ensure_rd_session_path (error)) + return FALSE; + + message = dbus_message_new_method_call (MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, "org.freedesktop.DBus.Properties", "Get"); + dbus_message_append_args (message, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &prop_name, DBUS_TYPE_INVALID); + + dbus_error_init (&d_error); + reply = dbus_connection_send_with_reply_and_block (data.bus, message, -1, &d_error); + dbus_message_unref (message); + if (!reply) + return FALSE; + if (strcmp (dbus_message_get_signature (reply), "v") != 0) + { + dbus_message_unref (reply); + return FALSE; + } + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &iter_variant); + dbus_message_iter_get_basic (&iter_variant, &session_id); + data.rd_session_id = g_strdup (session_id); + dbus_message_unref (reply); + return TRUE; +} + +static gboolean +ensure_sc_session (GError **error) +{ + DBusMessage *message, *reply; + DBusError d_error; + DBusMessageIter iter, iter_array, iter_dict_entry, iter_variant; + const char *prop_name = "remote-desktop-session-id"; + const char *sc_session_path; + + if (!ensure_rd_session_id (error)) + return FALSE; + + if (data.sc_session_path) + return (data.sc_session_path[0] != '\0'); + + message = dbus_message_new_method_call (MUTTER_SCREEN_CAST_BUS_NAME, MUTTER_SCREEN_CAST_OBJECT_PATH, MUTTER_SCREEN_CAST_INTERFACE, "CreateSession"); + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_array); + dbus_message_iter_open_container (&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry); + dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop_name); + dbus_message_iter_open_container (&iter_dict_entry, DBUS_TYPE_VARIANT, "s", &iter_variant); + dbus_message_iter_append_basic (&iter_variant, DBUS_TYPE_STRING, &data.rd_session_id); + dbus_message_iter_close_container (&iter_dict_entry, &iter_variant); + dbus_message_iter_close_container (&iter_array, &iter_dict_entry); + dbus_message_iter_close_container (&iter, &iter_array); + dbus_error_init (&d_error); + reply = dbus_connection_send_with_reply_and_block (data.bus, message, -1, &d_error); + dbus_message_unref (message); + if (!reply) + return FALSE; + if (!dbus_message_get_args (reply, NULL, DBUS_TYPE_OBJECT_PATH, &sc_session_path, DBUS_TYPE_INVALID)) + { + dbus_message_unref (reply); + return FALSE; + } + + data.sc_session_path = g_strdup (sc_session_path); + dbus_message_unref (reply); + return TRUE; +} + +static gboolean +init_mutter (gboolean need_window, GError **error) +{ + dbus_uint64_t window_id; + const char *prop_name = "window-id"; + DBusError d_error; + DBusMessageIter iter, iter_array, iter_dict_entry, iter_variant; + DBusMessage *message, *reply; + const char *sc_stream_path; + + if (!ensure_rd_session_path (error)) + return FALSE; + + if (!need_window) + return TRUE; + + window_id = (data.window_id_is_explicit) ? data.window_id + : get_window_id (NULL); + if (!window_id) + return FALSE; + + if (!ensure_sc_session (error)) + return FALSE; + + if (window_id == data.window_id) + return TRUE; + + message = dbus_message_new_method_call (MUTTER_SCREEN_CAST_BUS_NAME, data.sc_session_path, MUTTER_SCREEN_CAST_SESSION_INTERFACE, "RecordWindow"); + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_array); + dbus_message_iter_open_container (&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry); + dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop_name); + dbus_message_iter_open_container (&iter_dict_entry, DBUS_TYPE_VARIANT, "t", &iter_variant); + dbus_message_iter_append_basic (&iter_variant, DBUS_TYPE_UINT64, &window_id); + dbus_message_iter_close_container (&iter_dict_entry, &iter_variant); + dbus_message_iter_close_container (&iter_array, &iter_dict_entry); + dbus_message_iter_close_container (&iter, &iter_array); + dbus_error_init (&d_error); + reply = dbus_connection_send_with_reply_and_block (data.bus, message, -1, &d_error); + dbus_message_unref (message); + if (!reply) + return FALSE; + if (!dbus_message_get_args (reply, NULL, DBUS_TYPE_OBJECT_PATH, &sc_stream_path, DBUS_TYPE_INVALID)) + { + dbus_message_unref (reply); + return FALSE; + } + + data.sc_stream_path = g_strdup (sc_stream_path); + dbus_message_unref (reply); + data.window_id = window_id; + return TRUE; +} + +gboolean +_atspi_mutter_generate_keyboard_event (glong keyval, + const gchar *keystring, + AtspiKeySynthType synth_type, GError **error) +{ + DBusError d_error; + dbus_uint32_t d_keyval = keyval; + + if (!init_mutter (FALSE, error)) + return FALSE; + + dbus_error_init (&d_error); + switch (synth_type) + { + case ATSPI_KEY_PRESS: + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyKeyboardKeycode", &d_error, "ub", d_keyval, TRUE); + break; + case ATSPI_KEY_RELEASE: + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyKeyboardKeycode", &d_error, "ub", d_keyval, FALSE); + break; + case ATSPI_KEY_PRESSRELEASE: + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyKeyboardKeycode", &d_error, "ub", d_keyval, TRUE); + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyKeyboardKeycode", &d_error, "ub", d_keyval, FALSE); + break; + case ATSPI_KEY_SYM: + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyKeyboardKeysyme", &d_error, "ub", d_keyval, TRUE); + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyKeyboardKeysyme", &d_error, "ub", d_keyval, FALSE); + break; + default: + /* TODO: set error */ + g_warning ("%s: unsupported type", __func__); + return FALSE; + } + if (dbus_error_is_set (&d_error)) + { + g_warning ("GenerateKeyboardEvent failed: %s", d_error.message); + dbus_error_free (&d_error); + return FALSE; + } + + return TRUE; +} + +gboolean +_atspi_mutter_generate_mouse_event (glong x, glong y, const gchar *name, GError **error) +{ + gint button = 0; + double d_x = x, d_y = y; + DBusError d_error; + + if (!init_mutter (TRUE, error)) + return FALSE; + + dbus_error_init (&d_error); + switch (name[0]) + { + case 'b': + button = name[1] - '1'; + if (button < 0 || button > 4) + return FALSE; + if (x != -1 && y != -1) + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerMotionAbsolute", &d_error, "sdd", data.sc_stream_path, d_x, d_y); + switch (name[2]) + { + case 'p': + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerButton", &d_error, "ib", button, TRUE); + break; + case 'r': + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerButton", &d_error, "ib", button, FALSE); + break; + case 'c': + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerButton", &d_error, "ib", button, TRUE); + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerButton", &d_error, "ib", button, FALSE); + break; + case 'd': + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerButton", &d_error, "ib", button, TRUE); + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerButton", &d_error, "ib", button, FALSE); + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerButton", &d_error, "ib", button, TRUE); + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerButton", &d_error, "ib", button, FALSE); + break; + default: + return FALSE; + } + break; + case 'a': /* absolute motion */ + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerMotionAbsolute", &d_error, "sdd", data.sc_stream_path, d_x, d_y); + break; + case 'r': /* relative */ + dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "NotifyPointerMotionRelative", &d_error, "dd", d_x, d_y); + break; + default: + return FALSE; + } + return TRUE; +} + +void +_atspi_mutter_set_reference_window (AtspiAccessible *accessible) +{ + if (accessible) + { + AtspiRole role = atspi_accessible_get_role (accessible, NULL); + gchar *name; + g_return_if_fail (role != ATSPI_ROLE_APPLICATION); + name = atspi_accessible_get_name (accessible, NULL); + data.window_id = get_window_id (name); + data.window_id_is_explicit = TRUE; + } + + else + { + data.window_id_is_explicit = FALSE; + } +} |