Fix memory management issues
[platform/upstream/at-spi2-core.git] / atspi / atspi-mutter.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2019 SUSE LLC.
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 /* atspi-mutter.c: support for keyboard/mouse handling using the
24  * mutter/gnome-shell remote desktop interfaces
25  *
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.
32  */
33
34 #include "atspi-private.h"
35
36 typedef struct
37 {
38   DBusConnection *bus;
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;
46 } ATSPI_MUTTER_DATA;
47
48 static ATSPI_MUTTER_DATA data;
49
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"
58
59 /* TODO: consider porting this to gdbus */
60
61 static void
62 ensure_bus ()
63 {
64   if (data.bus)
65     return;
66   data.bus = dbus_bus_get (DBUS_BUS_SESSION, NULL);
67 }
68
69 static gboolean
70 ensure_rd_session_path (GError **error)
71 {
72   char *session_path;
73   DBusError d_error;
74
75   if (data.rd_session_path)
76     return (data.rd_session_path[0] != '\0');
77   ensure_bus ();
78
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);
81
82   data.rd_session_path = g_strdup (session_path);
83   if (!data.rd_session_path || !data.rd_session_path[0])
84     return FALSE;
85
86   dbind_method_call_reentrant (data.bus, MUTTER_REMOTE_DESKTOP_BUS_NAME, data.rd_session_path, MUTTER_REMOTE_DESKTOP_SESSION_INTERFACE, "Start", &d_error, "");
87   return TRUE;
88 }
89
90 static dbus_uint64_t
91 get_window_id (const char *name)
92 {
93   DBusMessage *message, *reply;
94   DBusError d_error;
95   dbus_uint64_t window_id;
96   DBusMessageIter iter, iter_array, iter_dict, iter_sub_array, iter_sub_dict;
97   const char *prop_name;
98   const char *cur_name;
99   dbus_bool_t cur_focus;
100   gboolean have_focus;
101
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);
106
107   if (!reply)
108     return FALSE;
109   if (strcmp (dbus_message_get_signature (reply), "a{ta{sv}}") != 0)
110   {
111     dbus_message_unref (reply);
112     return FALSE;
113   }
114
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)
118   {
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);
123     cur_name = NULL;
124     have_focus = FALSE;
125     while (dbus_message_iter_get_arg_type (&iter_sub_array) != DBUS_TYPE_INVALID)
126     {
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"))
130       {
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);
135       }
136       if (!strcmp (prop_name, "has-focus"))
137       {
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);
142         have_focus = TRUE;
143       }
144       if (cur_name && have_focus)
145       {
146         if ((name && !strcmp (name, cur_name)) || cur_focus)
147         {
148           dbus_message_unref (reply);
149           return window_id;
150         }
151         break;
152       }
153       dbus_message_iter_next (&iter_sub_array);
154     }
155     dbus_message_iter_next (&iter_array);
156   }
157
158   dbus_message_unref (reply);
159   return 0;
160 }
161
162 static gboolean
163 ensure_rd_session_id (GError **error)
164 {
165   DBusMessage *message, *reply;
166   DBusError d_error;
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;
171
172   if (data.rd_session_id)
173     return (data.rd_session_id[0] != '\0');
174
175   if (!ensure_rd_session_path (error))
176     return FALSE;
177
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);
180
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);
184   if (!reply)
185     return FALSE;
186   if (strcmp (dbus_message_get_signature (reply), "v") != 0)
187   {
188     dbus_message_unref (reply);
189     return FALSE;
190   }
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);
196   return TRUE;
197 }
198
199 static gboolean
200 ensure_sc_session (GError **error)
201 {
202   DBusMessage *message, *reply;
203   DBusError d_error;
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;
207
208   if (!ensure_rd_session_id (error))
209     return FALSE;
210
211   if (data.sc_session_path)
212     return (data.sc_session_path[0] != '\0');
213
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);
227   if (!reply)
228     return FALSE;
229   if (!dbus_message_get_args (reply, NULL, DBUS_TYPE_OBJECT_PATH, &sc_session_path, DBUS_TYPE_INVALID))
230   {
231     dbus_message_unref (reply);
232     return FALSE;
233   }
234
235   data.sc_session_path = g_strdup (sc_session_path);
236   dbus_message_unref (reply);
237   return TRUE;
238 }
239
240 static gboolean
241 init_mutter (gboolean need_window, GError **error)
242 {
243   dbus_uint64_t window_id;
244   const char *prop_name = "window-id";
245   DBusError d_error;
246   DBusMessageIter iter, iter_array, iter_dict_entry, iter_variant;
247   DBusMessage *message, *reply;
248   const char *sc_stream_path;
249
250   if (!ensure_rd_session_path (error))
251     return FALSE;
252
253   if (!need_window)
254     return TRUE;
255
256   window_id = (data.window_id_is_explicit) ? data.window_id
257               : get_window_id (NULL);
258   if (!window_id)
259     return FALSE;
260
261   if (!ensure_sc_session (error))
262     return FALSE;
263
264   if (window_id == data.window_id)
265     return TRUE;
266
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);
280   if (!reply)
281     return FALSE;
282   if (!dbus_message_get_args (reply, NULL, DBUS_TYPE_OBJECT_PATH, &sc_stream_path, DBUS_TYPE_INVALID))
283   {
284     dbus_message_unref (reply);
285     return FALSE;
286   }
287
288   data.sc_stream_path = g_strdup (sc_stream_path);
289   dbus_message_unref (reply);
290   data.window_id = window_id;
291   return TRUE;
292 }
293
294 gboolean
295 _atspi_mutter_generate_keyboard_event (glong keyval,
296                                        const gchar *keystring,
297                                        AtspiKeySynthType synth_type, GError **error)
298 {
299   DBusError d_error;
300   dbus_uint32_t d_keyval = keyval;
301
302   if (!init_mutter (FALSE, error))
303     return FALSE;
304
305   dbus_error_init (&d_error);
306   switch (synth_type)
307   {
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);
310     break;
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);
313     break;
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);
317     break;
318   case ATSPI_KEY_SYM:
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);
321     break;
322   default:
323     /* TODO: set error */
324     g_warning ("%s: unsupported type", __func__);
325     return FALSE;
326   }
327   if (dbus_error_is_set (&d_error))
328     {
329       g_warning ("GenerateKeyboardEvent failed: %s", d_error.message);
330       dbus_error_free (&d_error);
331          return FALSE;
332     }
333
334   return TRUE;
335 }
336
337 gboolean
338 _atspi_mutter_generate_mouse_event (glong x, glong y, const gchar *name, GError **error)
339 {
340   gint button = 0;
341   double d_x = x, d_y = y;
342   DBusError d_error;
343
344   if (!init_mutter (TRUE, error))
345     return FALSE;
346
347   dbus_error_init (&d_error);
348   switch (name[0])
349   {
350   case 'b':
351     button = name[1] - '1';
352     if (button < 0 || button > 4)
353       return FALSE;
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);
356     switch (name[2])
357     {
358     case 'p':
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);
360       break;
361     case 'r':
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);
363       break;
364     case 'c':
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);
367       break;
368     case 'd':
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);
373       break;
374     default:
375       return FALSE;
376     }
377     break;
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);
380     break;
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);
383     break;
384   default:
385     return FALSE;
386   }
387   return TRUE;
388 }
389
390 void
391 _atspi_mutter_set_reference_window (AtspiAccessible *accessible)
392 {
393   if (accessible)
394   {
395     AtspiRole role = atspi_accessible_get_role (accessible, NULL);
396     gchar *name;
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;
401     g_free(name);
402   }
403
404   else
405   {
406     data.window_id_is_explicit = FALSE;
407   }
408 }