xref: /qemu/ui/dbus-console.c (revision 513823e7521a09ed7ad1e32e6454bac3b2cbf52d)
1 /*
2  * QEMU DBus display console
3  *
4  * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include "qemu/osdep.h"
25 #include "qemu/error-report.h"
26 #include "qapi/error.h"
27 #include "ui/input.h"
28 #include "ui/kbd-state.h"
29 #include "trace.h"
30 
31 #ifdef G_OS_UNIX
32 #include <gio/gunixfdlist.h>
33 #endif
34 
35 #include "dbus.h"
36 
37 static struct touch_slot touch_slots[INPUT_EVENT_SLOTS_MAX];
38 
39 struct _DBusDisplayConsole {
40     GDBusObjectSkeleton parent_instance;
41     DisplayChangeListener dcl;
42 
43     DBusDisplay *display;
44     GPtrArray *listeners;
45     QemuDBusDisplay1Console *iface;
46 
47     QemuDBusDisplay1Keyboard *iface_kbd;
48     QKbdState *kbd;
49 
50     QemuDBusDisplay1Mouse *iface_mouse;
51     QemuDBusDisplay1MultiTouch *iface_touch;
52     gboolean last_set;
53     guint last_x;
54     guint last_y;
55     Notifier mouse_mode_notifier;
56 };
57 
58 G_DEFINE_TYPE(DBusDisplayConsole,
59               dbus_display_console,
60               G_TYPE_DBUS_OBJECT_SKELETON)
61 
62 static void
63 dbus_display_console_set_size(DBusDisplayConsole *ddc,
64                               uint32_t width, uint32_t height)
65 {
66     g_object_set(ddc->iface,
67                  "width", width,
68                  "height", height,
69                  NULL);
70 }
71 
72 static void
73 dbus_gfx_switch(DisplayChangeListener *dcl,
74                 struct DisplaySurface *new_surface)
75 {
76     DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
77 
78     dbus_display_console_set_size(ddc,
79                                   surface_width(new_surface),
80                                   surface_height(new_surface));
81 }
82 
83 static void
84 dbus_gfx_update(DisplayChangeListener *dcl,
85                 int x, int y, int w, int h)
86 {
87 }
88 
89 static void
90 dbus_gl_scanout_disable(DisplayChangeListener *dcl)
91 {
92 }
93 
94 static void
95 dbus_gl_scanout_texture(DisplayChangeListener *dcl,
96                         uint32_t tex_id,
97                         bool backing_y_0_top,
98                         uint32_t backing_width,
99                         uint32_t backing_height,
100                         uint32_t x, uint32_t y,
101                         uint32_t w, uint32_t h,
102                         void *d3d_tex2d)
103 {
104     DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
105 
106     dbus_display_console_set_size(ddc, w, h);
107 }
108 
109 static void
110 dbus_gl_scanout_dmabuf(DisplayChangeListener *dcl,
111                        QemuDmaBuf *dmabuf)
112 {
113     uint32_t width, height;
114 
115     DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
116 
117     width = qemu_dmabuf_get_width(dmabuf);
118     height = qemu_dmabuf_get_height(dmabuf);
119 
120     dbus_display_console_set_size(ddc, width, height);
121 }
122 
123 static void
124 dbus_gl_scanout_update(DisplayChangeListener *dcl,
125                        uint32_t x, uint32_t y,
126                        uint32_t w, uint32_t h)
127 {
128 }
129 
130 const DisplayChangeListenerOps dbus_console_dcl_ops = {
131     .dpy_name                = "dbus-console",
132     .dpy_gfx_switch          = dbus_gfx_switch,
133     .dpy_gfx_update          = dbus_gfx_update,
134     .dpy_gl_scanout_disable  = dbus_gl_scanout_disable,
135     .dpy_gl_scanout_texture  = dbus_gl_scanout_texture,
136     .dpy_gl_scanout_dmabuf   = dbus_gl_scanout_dmabuf,
137     .dpy_gl_update           = dbus_gl_scanout_update,
138 };
139 
140 static void
141 dbus_display_console_init(DBusDisplayConsole *object)
142 {
143     DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
144 
145     ddc->listeners = g_ptr_array_new_with_free_func(g_object_unref);
146     ddc->dcl.ops = &dbus_console_dcl_ops;
147 }
148 
149 static void
150 dbus_display_console_dispose(GObject *object)
151 {
152     DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
153 
154     unregister_displaychangelistener(&ddc->dcl);
155     g_clear_object(&ddc->iface_touch);
156     g_clear_object(&ddc->iface_mouse);
157     g_clear_object(&ddc->iface_kbd);
158     g_clear_object(&ddc->iface);
159     g_clear_pointer(&ddc->listeners, g_ptr_array_unref);
160     g_clear_pointer(&ddc->kbd, qkbd_state_free);
161 
162     G_OBJECT_CLASS(dbus_display_console_parent_class)->dispose(object);
163 }
164 
165 static void
166 dbus_display_console_class_init(DBusDisplayConsoleClass *klass)
167 {
168     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
169 
170     gobject_class->dispose = dbus_display_console_dispose;
171 }
172 
173 static void
174 listener_vanished_cb(DBusDisplayListener *listener)
175 {
176     DBusDisplayConsole *ddc = dbus_display_listener_get_console(listener);
177     const char *name = dbus_display_listener_get_bus_name(listener);
178 
179     trace_dbus_listener_vanished(name);
180 
181     g_ptr_array_remove_fast(ddc->listeners, listener);
182     qkbd_state_lift_all_keys(ddc->kbd);
183 }
184 
185 static gboolean
186 dbus_console_set_ui_info(DBusDisplayConsole *ddc,
187                          GDBusMethodInvocation *invocation,
188                          guint16 arg_width_mm,
189                          guint16 arg_height_mm,
190                          gint arg_xoff,
191                          gint arg_yoff,
192                          guint arg_width,
193                          guint arg_height)
194 {
195     QemuUIInfo info = {
196         .width_mm = arg_width_mm,
197         .height_mm = arg_height_mm,
198         .xoff = arg_xoff,
199         .yoff = arg_yoff,
200         .width = arg_width,
201         .height = arg_height,
202     };
203 
204     if (!dpy_ui_info_supported(ddc->dcl.con)) {
205         g_dbus_method_invocation_return_error(invocation,
206                                               DBUS_DISPLAY_ERROR,
207                                               DBUS_DISPLAY_ERROR_UNSUPPORTED,
208                                               "SetUIInfo is not supported");
209         return DBUS_METHOD_INVOCATION_HANDLED;
210     }
211 
212     dpy_set_ui_info(ddc->dcl.con, &info, false);
213     qemu_dbus_display1_console_complete_set_uiinfo(ddc->iface, invocation);
214     return DBUS_METHOD_INVOCATION_HANDLED;
215 }
216 
217 #ifdef G_OS_WIN32
218 bool
219 dbus_win32_import_socket(GDBusMethodInvocation *invocation,
220                          GVariant *arg_listener, int *socket)
221 {
222     gsize n;
223     WSAPROTOCOL_INFOW *info = (void *)g_variant_get_fixed_array(arg_listener, &n, 1);
224 
225     if (!info || n != sizeof(*info)) {
226         g_dbus_method_invocation_return_error(
227             invocation,
228             DBUS_DISPLAY_ERROR,
229             DBUS_DISPLAY_ERROR_FAILED,
230             "Failed to get socket infos");
231         return false;
232     }
233 
234     *socket = WSASocketW(FROM_PROTOCOL_INFO,
235                          FROM_PROTOCOL_INFO,
236                          FROM_PROTOCOL_INFO,
237                          info, 0, 0);
238     if (*socket == INVALID_SOCKET) {
239         g_autofree gchar *emsg = g_win32_error_message(WSAGetLastError());
240         g_dbus_method_invocation_return_error(
241             invocation,
242             DBUS_DISPLAY_ERROR,
243             DBUS_DISPLAY_ERROR_FAILED,
244             "Couldn't create socket: %s", emsg);
245         return false;
246     }
247 
248     return true;
249 }
250 #endif
251 
252 static gboolean
253 dbus_console_register_listener(DBusDisplayConsole *ddc,
254                                GDBusMethodInvocation *invocation,
255 #ifdef G_OS_UNIX
256                                GUnixFDList *fd_list,
257 #endif
258                                GVariant *arg_listener)
259 {
260     const char *sender = g_dbus_method_invocation_get_sender(invocation);
261     GDBusConnection *listener_conn;
262     g_autoptr(GError) err = NULL;
263     g_autoptr(GSocket) socket = NULL;
264     g_autoptr(GSocketConnection) socket_conn = NULL;
265     g_autofree char *guid = g_dbus_generate_guid();
266     DBusDisplayListener *listener;
267     int fd;
268 
269 #ifdef G_OS_WIN32
270     if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
271         return DBUS_METHOD_INVOCATION_HANDLED;
272     }
273 #else
274     fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
275     if (err) {
276         g_dbus_method_invocation_return_error(
277             invocation,
278             DBUS_DISPLAY_ERROR,
279             DBUS_DISPLAY_ERROR_FAILED,
280             "Couldn't get peer fd: %s", err->message);
281         return DBUS_METHOD_INVOCATION_HANDLED;
282     }
283 #endif
284 
285     socket = g_socket_new_from_fd(fd, &err);
286     if (err) {
287         g_dbus_method_invocation_return_error(
288             invocation,
289             DBUS_DISPLAY_ERROR,
290             DBUS_DISPLAY_ERROR_FAILED,
291             "Couldn't make a socket: %s", err->message);
292 #ifdef G_OS_WIN32
293         closesocket(fd);
294 #else
295         close(fd);
296 #endif
297         return DBUS_METHOD_INVOCATION_HANDLED;
298     }
299     socket_conn = g_socket_connection_factory_create_connection(socket);
300 
301     qemu_dbus_display1_console_complete_register_listener(
302         ddc->iface, invocation
303 #ifdef G_OS_UNIX
304         , NULL
305 #endif
306     );
307 
308     GDBusConnectionFlags flags =
309         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER;
310 #ifdef WIN32
311     flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
312 #endif
313 
314     listener_conn = g_dbus_connection_new_sync(
315         G_IO_STREAM(socket_conn),
316         guid,
317         flags,
318         NULL, NULL, &err);
319     if (err) {
320         error_report("Failed to setup peer connection: %s", err->message);
321         return DBUS_METHOD_INVOCATION_HANDLED;
322     }
323 
324     listener = dbus_display_listener_new(sender, listener_conn, ddc);
325     if (!listener) {
326         return DBUS_METHOD_INVOCATION_HANDLED;
327     }
328 
329     g_ptr_array_add(ddc->listeners, listener);
330     g_object_connect(listener_conn,
331                      "swapped-signal::closed", listener_vanished_cb, listener,
332                      NULL);
333 
334     trace_dbus_registered_listener(sender);
335     return DBUS_METHOD_INVOCATION_HANDLED;
336 }
337 
338 static gboolean
339 dbus_kbd_press(DBusDisplayConsole *ddc,
340                GDBusMethodInvocation *invocation,
341                guint arg_keycode)
342 {
343     QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode);
344 
345     trace_dbus_kbd_press(arg_keycode);
346 
347     qkbd_state_key_event(ddc->kbd, qcode, true);
348 
349     qemu_dbus_display1_keyboard_complete_press(ddc->iface_kbd, invocation);
350 
351     return DBUS_METHOD_INVOCATION_HANDLED;
352 }
353 
354 static gboolean
355 dbus_kbd_release(DBusDisplayConsole *ddc,
356                  GDBusMethodInvocation *invocation,
357                  guint arg_keycode)
358 {
359     QKeyCode qcode = qemu_input_key_number_to_qcode(arg_keycode);
360 
361     trace_dbus_kbd_release(arg_keycode);
362 
363     qkbd_state_key_event(ddc->kbd, qcode, false);
364 
365     qemu_dbus_display1_keyboard_complete_release(ddc->iface_kbd, invocation);
366 
367     return DBUS_METHOD_INVOCATION_HANDLED;
368 }
369 
370 static void
371 dbus_kbd_qemu_leds_updated(void *data, int ledstate)
372 {
373     DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(data);
374 
375     qemu_dbus_display1_keyboard_set_modifiers(ddc->iface_kbd, ledstate);
376 }
377 
378 static gboolean
379 dbus_mouse_rel_motion(DBusDisplayConsole *ddc,
380                       GDBusMethodInvocation *invocation,
381                       int dx, int dy)
382 {
383     trace_dbus_mouse_rel_motion(dx, dy);
384 
385     if (qemu_input_is_absolute(ddc->dcl.con)) {
386         g_dbus_method_invocation_return_error(
387             invocation, DBUS_DISPLAY_ERROR,
388             DBUS_DISPLAY_ERROR_INVALID,
389             "Mouse is not relative");
390         return DBUS_METHOD_INVOCATION_HANDLED;
391     }
392 
393     qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_X, dx);
394     qemu_input_queue_rel(ddc->dcl.con, INPUT_AXIS_Y, dy);
395     qemu_input_event_sync();
396 
397     qemu_dbus_display1_mouse_complete_rel_motion(ddc->iface_mouse,
398                                                     invocation);
399 
400     return DBUS_METHOD_INVOCATION_HANDLED;
401 }
402 
403 static gboolean
404 dbus_touch_send_event(DBusDisplayConsole *ddc,
405                       GDBusMethodInvocation *invocation,
406                       guint kind, uint64_t num_slot,
407                       double x, double y)
408 {
409     Error *error = NULL;
410     int width, height;
411     trace_dbus_touch_send_event(kind, num_slot, x, y);
412 
413     if (kind != INPUT_MULTI_TOUCH_TYPE_BEGIN &&
414         kind != INPUT_MULTI_TOUCH_TYPE_UPDATE &&
415         kind != INPUT_MULTI_TOUCH_TYPE_CANCEL &&
416         kind != INPUT_MULTI_TOUCH_TYPE_END)
417     {
418         g_dbus_method_invocation_return_error(
419             invocation, DBUS_DISPLAY_ERROR,
420             DBUS_DISPLAY_ERROR_INVALID,
421             "Invalid touch event kind");
422         return DBUS_METHOD_INVOCATION_HANDLED;
423     }
424     width = qemu_console_get_width(ddc->dcl.con, 0);
425     height = qemu_console_get_height(ddc->dcl.con, 0);
426 
427     console_handle_touch_event(ddc->dcl.con, touch_slots,
428                                num_slot, width, height,
429                                x, y, kind, &error);
430     if (error != NULL) {
431         g_dbus_method_invocation_return_error(
432             invocation, DBUS_DISPLAY_ERROR,
433             DBUS_DISPLAY_ERROR_INVALID,
434             error_get_pretty(error), NULL);
435         error_free(error);
436     } else {
437         qemu_dbus_display1_multi_touch_complete_send_event(ddc->iface_touch,
438                                                            invocation);
439     }
440     return DBUS_METHOD_INVOCATION_HANDLED;
441 }
442 
443 static gboolean
444 dbus_mouse_set_pos(DBusDisplayConsole *ddc,
445                    GDBusMethodInvocation *invocation,
446                    guint x, guint y)
447 {
448     int width, height;
449 
450     trace_dbus_mouse_set_pos(x, y);
451 
452     if (!qemu_input_is_absolute(ddc->dcl.con)) {
453         g_dbus_method_invocation_return_error(
454             invocation, DBUS_DISPLAY_ERROR,
455             DBUS_DISPLAY_ERROR_INVALID,
456             "Mouse is not absolute");
457         return DBUS_METHOD_INVOCATION_HANDLED;
458     }
459 
460     width = qemu_console_get_width(ddc->dcl.con, 0);
461     height = qemu_console_get_height(ddc->dcl.con, 0);
462     if (x >= width || y >= height) {
463         g_dbus_method_invocation_return_error(
464             invocation, DBUS_DISPLAY_ERROR,
465             DBUS_DISPLAY_ERROR_INVALID,
466             "Invalid mouse position");
467         return DBUS_METHOD_INVOCATION_HANDLED;
468     }
469     qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_X, x, 0, width);
470     qemu_input_queue_abs(ddc->dcl.con, INPUT_AXIS_Y, y, 0, height);
471     qemu_input_event_sync();
472 
473     qemu_dbus_display1_mouse_complete_set_abs_position(ddc->iface_mouse,
474                                                           invocation);
475 
476     return DBUS_METHOD_INVOCATION_HANDLED;
477 }
478 
479 static gboolean
480 dbus_mouse_press(DBusDisplayConsole *ddc,
481                  GDBusMethodInvocation *invocation,
482                  guint button)
483 {
484     trace_dbus_mouse_press(button);
485 
486     qemu_input_queue_btn(ddc->dcl.con, button, true);
487     qemu_input_event_sync();
488 
489     qemu_dbus_display1_mouse_complete_press(ddc->iface_mouse, invocation);
490 
491     return DBUS_METHOD_INVOCATION_HANDLED;
492 }
493 
494 static gboolean
495 dbus_mouse_release(DBusDisplayConsole *ddc,
496                    GDBusMethodInvocation *invocation,
497                    guint button)
498 {
499     trace_dbus_mouse_release(button);
500 
501     qemu_input_queue_btn(ddc->dcl.con, button, false);
502     qemu_input_event_sync();
503 
504     qemu_dbus_display1_mouse_complete_release(ddc->iface_mouse, invocation);
505 
506     return DBUS_METHOD_INVOCATION_HANDLED;
507 }
508 
509 static void
510 dbus_mouse_update_is_absolute(DBusDisplayConsole *ddc)
511 {
512     g_object_set(ddc->iface_mouse,
513                  "is-absolute", qemu_input_is_absolute(ddc->dcl.con),
514                  NULL);
515 }
516 
517 static void
518 dbus_mouse_mode_change(Notifier *notify, void *data)
519 {
520     DBusDisplayConsole *ddc =
521         container_of(notify, DBusDisplayConsole, mouse_mode_notifier);
522 
523     dbus_mouse_update_is_absolute(ddc);
524 }
525 
526 int dbus_display_console_get_index(DBusDisplayConsole *ddc)
527 {
528     return qemu_console_get_index(ddc->dcl.con);
529 }
530 
531 DBusDisplayConsole *
532 dbus_display_console_new(DBusDisplay *display, QemuConsole *con)
533 {
534     g_autofree char *path = NULL;
535     g_autofree char *label = NULL;
536     char device_addr[256] = "";
537     DBusDisplayConsole *ddc;
538     int idx, i;
539     const char *interfaces[] = {
540         "org.qemu.Display1.Keyboard",
541         "org.qemu.Display1.Mouse",
542         "org.qemu.Display1.MultiTouch",
543         NULL
544     };
545 
546     assert(display);
547     assert(con);
548 
549     label = qemu_console_get_label(con);
550     idx = qemu_console_get_index(con);
551     path = g_strdup_printf(DBUS_DISPLAY1_ROOT "/Console_%d", idx);
552     ddc = g_object_new(DBUS_DISPLAY_TYPE_CONSOLE,
553                         "g-object-path", path,
554                         NULL);
555     ddc->display = display;
556     ddc->dcl.con = con;
557     /* handle errors, and skip non graphics? */
558     qemu_console_fill_device_address(
559         con, device_addr, sizeof(device_addr), NULL);
560 
561     ddc->iface = qemu_dbus_display1_console_skeleton_new();
562     g_object_set(ddc->iface,
563         "label", label,
564         "type", qemu_console_is_graphic(con) ? "Graphic" : "Text",
565         "head", qemu_console_get_head(con),
566         "width", qemu_console_get_width(con, 0),
567         "height", qemu_console_get_height(con, 0),
568         "device-address", device_addr,
569         "interfaces", interfaces,
570         NULL);
571     g_object_connect(ddc->iface,
572         "swapped-signal::handle-register-listener",
573         dbus_console_register_listener, ddc,
574         "swapped-signal::handle-set-uiinfo",
575         dbus_console_set_ui_info, ddc,
576         NULL);
577     g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
578         G_DBUS_INTERFACE_SKELETON(ddc->iface));
579 
580     ddc->kbd = qkbd_state_init(con);
581     ddc->iface_kbd = qemu_dbus_display1_keyboard_skeleton_new();
582     qemu_add_led_event_handler(dbus_kbd_qemu_leds_updated, ddc);
583     g_object_connect(ddc->iface_kbd,
584         "swapped-signal::handle-press", dbus_kbd_press, ddc,
585         "swapped-signal::handle-release", dbus_kbd_release, ddc,
586         NULL);
587     g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
588         G_DBUS_INTERFACE_SKELETON(ddc->iface_kbd));
589 
590     ddc->iface_mouse = qemu_dbus_display1_mouse_skeleton_new();
591     g_object_connect(ddc->iface_mouse,
592         "swapped-signal::handle-set-abs-position", dbus_mouse_set_pos, ddc,
593         "swapped-signal::handle-rel-motion", dbus_mouse_rel_motion, ddc,
594         "swapped-signal::handle-press", dbus_mouse_press, ddc,
595         "swapped-signal::handle-release", dbus_mouse_release, ddc,
596         NULL);
597     g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
598         G_DBUS_INTERFACE_SKELETON(ddc->iface_mouse));
599 
600     ddc->iface_touch = qemu_dbus_display1_multi_touch_skeleton_new();
601     g_object_connect(ddc->iface_touch,
602         "swapped-signal::handle-send-event", dbus_touch_send_event, ddc,
603         NULL);
604     qemu_dbus_display1_multi_touch_set_max_slots(ddc->iface_touch,
605                                                  INPUT_EVENT_SLOTS_MAX);
606     g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(ddc),
607         G_DBUS_INTERFACE_SKELETON(ddc->iface_touch));
608 
609     for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) {
610         struct touch_slot *slot = &touch_slots[i];
611         slot->tracking_id = -1;
612     }
613 
614     register_displaychangelistener(&ddc->dcl);
615     ddc->mouse_mode_notifier.notify = dbus_mouse_mode_change;
616     qemu_add_mouse_mode_change_notifier(&ddc->mouse_mode_notifier);
617     dbus_mouse_update_is_absolute(ddc);
618 
619     return ddc;
620 }
621