2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2019 SUSE LLC.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
23 /* atspi-mutter.c: support for keyboard/mouse handling using the
24 * mutter/gnome-shell remote desktop interfaces
26 * This functionality is analogous to the X11-based code in
27 * device-event-controller-x11.c. Placing the code here, rather than in the
28 * registry daemon, allows the relevant dbus calls to come directly from the
29 * AT-SPI client, rather than at-spi2-registryd being an intermediary,
30 * which may be useful if a distribution wishes to lock down access to the
31 * remote desktop interfaces.
34 #include "atspi-private.h"
39 const char *rd_session_id;
40 const char *rd_session_path;
41 const char *sc_session_id;
42 const char *sc_session_path;
43 const char *sc_stream_path;
44 dbus_uint64_t window_id;
45 gboolean window_id_is_explicit;
48 static ATSPI_MUTTER_DATA data;
50 #define MUTTER_REMOTE_DESKTOP_BUS_NAME "org.gnome.Mutter.RemoteDesktop"
51 #define MUTTER_REMOTE_DESKTOP_OBJECT_PATH "/org/gnome/Mutter/RemoteDesktop"
52 #define MUTTER_REMOTE_DESKTOP_INTERFACE "org.gnome.Mutter.RemoteDesktop"
53 #define MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE "org.gnome.Mutter.RemoteDesktop.Session"
54 #define MUTTER_SCREEN_CAST_BUS_NAME "org.gnome.Mutter.ScreenCast"
55 #define MUTTER_SCREEN_CAST_OBJECT_PATH "/org/gnome/Mutter/ScreenCast"
56 #define MUTTER_SCREEN_CAST_INTERFACE "org.gnome.Mutter.ScreenCast"
57 #define MUTTER_SCREEN_CAST_SESSION_INTERFACE "org.gnome.Mutter.ScreenCast.Session"
59 /* TODO: consider porting this to gdbus */
66 data.bus = dbus_bus_get (DBUS_BUS_SESSION, NULL);
70 ensure_rd_session_path (GError **error)
75 if (data.rd_session_path)
76 return (data.rd_session_path[0] != '\0');
79 dbus_error_init (&d_error);
80 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);
82 data.rd_session_path = g_strdup (session_path);
83 if (!data.rd_session_path[0])
86 dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "Start", &d_error, "");
91 get_window_id (const char *name)
93 DBusMessage *message, *reply;
95 dbus_uint64_t window_id;
96 DBusMessageIter iter, iter_array, iter_dict, iter_sub_array, iter_sub_dict;
97 const char *prop_name;
99 dbus_bool_t cur_focus;
102 dbus_error_init (&d_error);
103 message = dbus_message_new_method_call (MUTTER_REMOTE_DESKTOP_BUS_NAME, "/org/gnome/Shell/Introspect", "org.gnome.Shell.Introspect", "GetWindows");
104 reply = dbus_connection_send_with_reply_and_block (data.bus, message, -1, &d_error);
105 dbus_message_unref (message);
109 if (strcmp (dbus_message_get_signature (reply), "a{ta{sv}}") != 0)
111 dbus_message_unref (reply);
115 dbus_message_iter_init (reply, &iter);
116 dbus_message_iter_recurse (&iter, &iter_array);
117 while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
119 dbus_message_iter_recurse (&iter_array, &iter_dict);
120 dbus_message_iter_get_basic (&iter_dict, &window_id);
121 dbus_message_iter_next (&iter_dict);
122 dbus_message_iter_recurse (&iter_dict, &iter_sub_array);
125 while (dbus_message_iter_get_arg_type (&iter_sub_array) != DBUS_TYPE_INVALID)
127 dbus_message_iter_recurse (&iter_sub_array, &iter_sub_dict);
128 dbus_message_iter_get_basic (&iter_sub_dict, &prop_name);
129 if (!strcmp (prop_name, "wm-class"))
131 DBusMessageIter iter_variant;
132 dbus_message_iter_next (&iter_sub_dict);
133 dbus_message_iter_recurse (&iter_sub_dict, &iter_variant);
134 dbus_message_iter_get_basic (&iter_variant, &cur_name);
136 if (!strcmp (prop_name, "has-focus"))
138 DBusMessageIter iter_variant;
139 dbus_message_iter_next (&iter_sub_dict);
140 dbus_message_iter_recurse (&iter_sub_dict, &iter_variant);
141 dbus_message_iter_get_basic (&iter_variant, &cur_focus);
144 if (cur_name && have_focus)
146 if ((name && !strcmp (name, cur_name)) || cur_focus)
148 dbus_message_unref (reply);
153 dbus_message_iter_next (&iter_sub_array);
155 dbus_message_iter_next (&iter_array);
158 dbus_message_unref (reply);
163 ensure_rd_session_id (GError **error)
165 DBusMessage *message, *reply;
167 const char *interface = "org.gnome.Mutter.RemoteDesktop.Session";
168 const char *prop_name = "SessionId";
169 DBusMessageIter iter, iter_variant;
170 const char *session_id;
172 if (data.rd_session_id)
173 return (data.rd_session_id[0] != '\0');
175 if (!ensure_rd_session_path (error))
178 message = dbus_message_new_method_call (MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, "org.freedesktop.DBus.Properties", "Get");
179 dbus_message_append_args (message, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &prop_name, DBUS_TYPE_INVALID);
181 dbus_error_init (&d_error);
182 reply = dbus_connection_send_with_reply_and_block (data.bus, message, -1, &d_error);
183 dbus_message_unref (message);
186 if (strcmp (dbus_message_get_signature (reply), "v") != 0)
188 dbus_message_unref (reply);
191 dbus_message_iter_init (reply, &iter);
192 dbus_message_iter_recurse (&iter, &iter_variant);
193 dbus_message_iter_get_basic (&iter_variant, &session_id);
194 data.rd_session_id = g_strdup (session_id);
195 dbus_message_unref (reply);
200 ensure_sc_session (GError **error)
202 DBusMessage *message, *reply;
204 DBusMessageIter iter, iter_array, iter_dict_entry, iter_variant;
205 const char *prop_name = "remote-desktop-session-id";
206 const char *sc_session_path;
208 if (!ensure_rd_session_id (error))
211 if (data.sc_session_path)
212 return (data.sc_session_path[0] != '\0');
214 message = dbus_message_new_method_call (MUTTER_SCREEN_CAST_BUS_NAME, MUTTER_SCREEN_CAST_OBJECT_PATH, MUTTER_SCREEN_CAST_INTERFACE, "CreateSession");
215 dbus_message_iter_init_append (message, &iter);
216 dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_array);
217 dbus_message_iter_open_container (&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry);
218 dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop_name);
219 dbus_message_iter_open_container (&iter_dict_entry, DBUS_TYPE_VARIANT, "s", &iter_variant);
220 dbus_message_iter_append_basic (&iter_variant, DBUS_TYPE_STRING, &data.rd_session_id);
221 dbus_message_iter_close_container (&iter_dict_entry, &iter_variant);
222 dbus_message_iter_close_container (&iter_array, &iter_dict_entry);
223 dbus_message_iter_close_container (&iter, &iter_array);
224 dbus_error_init (&d_error);
225 reply = dbus_connection_send_with_reply_and_block (data.bus, message, -1, &d_error);
226 dbus_message_unref (message);
229 if (!dbus_message_get_args (reply, NULL, DBUS_TYPE_OBJECT_PATH, &sc_session_path, DBUS_TYPE_INVALID))
231 dbus_message_unref (reply);
235 data.sc_session_path = g_strdup (sc_session_path);
236 dbus_message_unref (reply);
241 init_mutter (gboolean need_window, GError **error)
243 dbus_uint64_t window_id;
244 const char *prop_name = "window-id";
246 DBusMessageIter iter, iter_array, iter_dict_entry, iter_variant;
247 DBusMessage *message, *reply;
248 const char *sc_stream_path;
250 if (!ensure_rd_session_path (error))
256 window_id = (data.window_id_is_explicit) ? data.window_id
257 : get_window_id (NULL);
261 if (!ensure_sc_session (error))
264 if (window_id == data.window_id)
267 message = dbus_message_new_method_call (MUTTER_SCREEN_CAST_BUS_NAME, data.sc_session_path, MUTTER_SCREEN_CAST_SESSION_INTERFACE, "RecordWindow");
268 dbus_message_iter_init_append (message, &iter);
269 dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_array);
270 dbus_message_iter_open_container (&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry);
271 dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &prop_name);
272 dbus_message_iter_open_container (&iter_dict_entry, DBUS_TYPE_VARIANT, "t", &iter_variant);
273 dbus_message_iter_append_basic (&iter_variant, DBUS_TYPE_UINT64, &window_id);
274 dbus_message_iter_close_container (&iter_dict_entry, &iter_variant);
275 dbus_message_iter_close_container (&iter_array, &iter_dict_entry);
276 dbus_message_iter_close_container (&iter, &iter_array);
277 dbus_error_init (&d_error);
278 reply = dbus_connection_send_with_reply_and_block (data.bus, message, -1, &d_error);
279 dbus_message_unref (message);
282 if (!dbus_message_get_args (reply, NULL, DBUS_TYPE_OBJECT_PATH, &sc_stream_path, DBUS_TYPE_INVALID))
284 dbus_message_unref (reply);
288 data.sc_stream_path = g_strdup (sc_stream_path);
289 dbus_message_unref (reply);
290 data.window_id = window_id;
295 _atspi_mutter_generate_keyboard_event (glong keyval,
296 const gchar *keystring,
297 AtspiKeySynthType synth_type, GError **error)
300 dbus_uint32_t d_keyval = keyval;
302 if (!init_mutter (FALSE, error))
305 dbus_error_init (&d_error);
308 case ATSPI_KEY_PRESS:
309 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);
311 case ATSPI_KEY_RELEASE:
312 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);
314 case ATSPI_KEY_PRESSRELEASE:
315 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);
316 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);
319 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);
320 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);
323 /* TODO: set error */
324 g_warning ("%s: unsupported type", __func__);
327 if (dbus_error_is_set (&d_error))
329 g_warning ("GenerateKeyboardEvent failed: %s", d_error.message);
330 dbus_error_free (&d_error);
338 _atspi_mutter_generate_mouse_event (glong x, glong y, const gchar *name, GError **error)
341 double d_x = x, d_y = y;
344 if (!init_mutter (TRUE, error))
347 dbus_error_init (&d_error);
351 button = name[1] - '1';
352 if (button < 0 || button > 4)
354 if (x != -1 && y != -1)
355 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);
359 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);
362 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);
365 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);
366 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);
369 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);
370 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);
371 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);
372 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);
378 case 'a': /* absolute motion */
379 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);
381 case 'r': /* relative */
382 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);
391 _atspi_mutter_set_reference_window (AtspiAccessible *accessible)
395 AtspiRole role = atspi_accessible_get_role (accessible, NULL);
397 g_return_if_fail (role != ATSPI_ROLE_APPLICATION);
398 name = atspi_accessible_get_name (accessible, NULL);
399 data.window_id = get_window_id (name);
400 data.window_id_is_explicit = TRUE;
405 data.window_id_is_explicit = FALSE;