1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 2015 Red Hat
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Carlos Garnacho <carlosg@gnome.org>
18  */
19 
20 #include "gdkseatdefaultprivate.h"
21 #include "gdkdevicetoolprivate.h"
22 
23 typedef struct _GdkSeatDefaultPrivate GdkSeatDefaultPrivate;
24 
25 struct _GdkSeatDefaultPrivate
26 {
27   GdkDevice *master_pointer;
28   GdkDevice *master_keyboard;
29   GList *slave_pointers;
30   GList *slave_keyboards;
31   GdkSeatCapabilities capabilities;
32 
33   GPtrArray *tools;
34 };
35 
36 #define KEYBOARD_EVENTS (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |    \
37                          GDK_FOCUS_CHANGE_MASK)
38 #define TOUCH_EVENTS    (GDK_TOUCH_MASK)
39 #define POINTER_EVENTS  (GDK_POINTER_MOTION_MASK |                      \
40                          GDK_BUTTON_PRESS_MASK |                        \
41                          GDK_BUTTON_RELEASE_MASK |                      \
42                          GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK |     \
43                          GDK_ENTER_NOTIFY_MASK |                        \
44                          GDK_LEAVE_NOTIFY_MASK |                        \
45                          GDK_PROXIMITY_IN_MASK |                        \
46                          GDK_PROXIMITY_OUT_MASK |                       \
47                          GDK_TOUCHPAD_GESTURE_MASK)
48 
G_DEFINE_TYPE_WITH_PRIVATE(GdkSeatDefault,gdk_seat_default,GDK_TYPE_SEAT)49 G_DEFINE_TYPE_WITH_PRIVATE (GdkSeatDefault, gdk_seat_default, GDK_TYPE_SEAT)
50 
51 static void
52 gdk_seat_dispose (GObject *object)
53 {
54   GdkSeatDefault *seat = GDK_SEAT_DEFAULT (object);
55   GdkSeatDefaultPrivate *priv = gdk_seat_default_get_instance_private (seat);
56   GList *l;
57 
58   if (priv->master_pointer)
59     {
60       gdk_seat_device_removed (GDK_SEAT (seat), priv->master_pointer);
61       g_clear_object (&priv->master_pointer);
62     }
63 
64   if (priv->master_keyboard)
65     {
66       gdk_seat_device_removed (GDK_SEAT (seat), priv->master_keyboard);
67       g_clear_object (&priv->master_pointer);
68     }
69 
70   for (l = priv->slave_pointers; l; l = l->next)
71     {
72       gdk_seat_device_removed (GDK_SEAT (seat), l->data);
73       g_object_unref (l->data);
74     }
75 
76   for (l = priv->slave_keyboards; l; l = l->next)
77     {
78       gdk_seat_device_removed (GDK_SEAT (seat), l->data);
79       g_object_unref (l->data);
80     }
81 
82   if (priv->tools)
83     {
84       g_ptr_array_unref (priv->tools);
85       priv->tools = NULL;
86     }
87 
88   g_list_free (priv->slave_pointers);
89   g_list_free (priv->slave_keyboards);
90   priv->slave_pointers = NULL;
91   priv->slave_keyboards = NULL;
92 
93   G_OBJECT_CLASS (gdk_seat_default_parent_class)->dispose (object);
94 }
95 
96 static GdkSeatCapabilities
gdk_seat_default_get_capabilities(GdkSeat * seat)97 gdk_seat_default_get_capabilities (GdkSeat *seat)
98 {
99   GdkSeatDefaultPrivate *priv;
100 
101   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
102 
103   return priv->capabilities;
104 }
105 
106 static GdkGrabStatus
gdk_seat_default_grab(GdkSeat * seat,GdkWindow * window,GdkSeatCapabilities capabilities,gboolean owner_events,GdkCursor * cursor,const GdkEvent * event,GdkSeatGrabPrepareFunc prepare_func,gpointer prepare_func_data)107 gdk_seat_default_grab (GdkSeat                *seat,
108                        GdkWindow              *window,
109                        GdkSeatCapabilities     capabilities,
110                        gboolean                owner_events,
111                        GdkCursor              *cursor,
112                        const GdkEvent         *event,
113                        GdkSeatGrabPrepareFunc  prepare_func,
114                        gpointer                prepare_func_data)
115 {
116   GdkSeatDefaultPrivate *priv;
117   guint32 evtime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
118   GdkGrabStatus status = GDK_GRAB_SUCCESS;
119   gboolean was_visible;
120 
121   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
122   was_visible = gdk_window_is_visible (window);
123 
124   if (prepare_func)
125     (prepare_func) (seat, window, prepare_func_data);
126 
127   if (!gdk_window_is_visible (window))
128     {
129       g_critical ("Window %p has not been made visible in GdkSeatGrabPrepareFunc",
130                   window);
131       return GDK_GRAB_NOT_VIEWABLE;
132     }
133 
134   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
135 
136   if (capabilities & GDK_SEAT_CAPABILITY_ALL_POINTING)
137     {
138       /* ALL_POINTING spans 3 capabilities; get the mask for the ones we have */
139       GdkEventMask pointer_evmask = 0;
140 
141       /* We let tablet styli take over the pointer cursor */
142       if (capabilities & (GDK_SEAT_CAPABILITY_POINTER |
143                           GDK_SEAT_CAPABILITY_TABLET_STYLUS))
144         {
145           pointer_evmask |= POINTER_EVENTS;
146         }
147 
148       if (capabilities & GDK_SEAT_CAPABILITY_TOUCH)
149         pointer_evmask |= TOUCH_EVENTS;
150 
151       status = gdk_device_grab (priv->master_pointer, window,
152                                 GDK_OWNERSHIP_NONE, owner_events,
153                                 pointer_evmask, cursor,
154                                 evtime);
155     }
156 
157   if (status == GDK_GRAB_SUCCESS &&
158       capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
159     {
160       status = gdk_device_grab (priv->master_keyboard, window,
161                                 GDK_OWNERSHIP_NONE, owner_events,
162                                 KEYBOARD_EVENTS, cursor,
163                                 evtime);
164 
165       if (status != GDK_GRAB_SUCCESS)
166         {
167           if (capabilities & ~GDK_SEAT_CAPABILITY_KEYBOARD)
168             gdk_device_ungrab (priv->master_pointer, evtime);
169         }
170     }
171 
172   if (status != GDK_GRAB_SUCCESS && !was_visible)
173     gdk_window_hide (window);
174 
175   G_GNUC_END_IGNORE_DEPRECATIONS;
176 
177   return status;
178 }
179 
180 static void
gdk_seat_default_ungrab(GdkSeat * seat)181 gdk_seat_default_ungrab (GdkSeat *seat)
182 {
183   GdkSeatDefaultPrivate *priv;
184 
185   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
186 
187   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
188   gdk_device_ungrab (priv->master_pointer, GDK_CURRENT_TIME);
189   gdk_device_ungrab (priv->master_keyboard, GDK_CURRENT_TIME);
190   G_GNUC_END_IGNORE_DEPRECATIONS;
191 }
192 
193 static GdkDevice *
gdk_seat_default_get_master(GdkSeat * seat,GdkSeatCapabilities capability)194 gdk_seat_default_get_master (GdkSeat             *seat,
195                              GdkSeatCapabilities  capability)
196 {
197   GdkSeatDefaultPrivate *priv;
198 
199   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
200 
201   /* There must be only one flag set */
202   switch (capability)
203     {
204     case GDK_SEAT_CAPABILITY_POINTER:
205     case GDK_SEAT_CAPABILITY_TOUCH:
206       return priv->master_pointer;
207     case GDK_SEAT_CAPABILITY_KEYBOARD:
208       return priv->master_keyboard;
209     default:
210       g_warning ("Unhandled capability %x", capability);
211       break;
212     }
213 
214   return NULL;
215 }
216 
217 static GdkSeatCapabilities
device_get_capability(GdkDevice * device)218 device_get_capability (GdkDevice *device)
219 {
220   GdkInputSource source;
221 
222   source = gdk_device_get_source (device);
223 
224   switch (source)
225     {
226     case GDK_SOURCE_KEYBOARD:
227       return GDK_SEAT_CAPABILITY_KEYBOARD;
228     case GDK_SOURCE_TOUCHSCREEN:
229       return GDK_SEAT_CAPABILITY_TOUCH;
230     case GDK_SOURCE_MOUSE:
231     case GDK_SOURCE_TOUCHPAD:
232     default:
233       return GDK_SEAT_CAPABILITY_POINTER;
234     }
235 
236   return GDK_SEAT_CAPABILITY_NONE;
237 }
238 
239 static GList *
append_filtered(GList * list,GList * devices,GdkSeatCapabilities capabilities)240 append_filtered (GList               *list,
241                  GList               *devices,
242                  GdkSeatCapabilities  capabilities)
243 {
244   GList *l;
245 
246   for (l = devices; l; l = l->next)
247     {
248       GdkSeatCapabilities device_cap;
249 
250       device_cap = device_get_capability (l->data);
251 
252       if ((device_cap & capabilities) != 0)
253         list = g_list_prepend (list, l->data);
254     }
255 
256   return list;
257 }
258 
259 static GList *
gdk_seat_default_get_slaves(GdkSeat * seat,GdkSeatCapabilities capabilities)260 gdk_seat_default_get_slaves (GdkSeat             *seat,
261                              GdkSeatCapabilities  capabilities)
262 {
263   GdkSeatDefaultPrivate *priv;
264   GList *devices = NULL;
265 
266   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
267 
268   if (capabilities & (GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TOUCH))
269     devices = append_filtered (devices, priv->slave_pointers, capabilities);
270 
271   if (capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
272     devices = append_filtered (devices, priv->slave_keyboards, capabilities);
273 
274   return devices;
275 }
276 
277 static GdkDeviceTool *
gdk_seat_default_get_tool(GdkSeat * seat,guint64 serial,guint64 hw_id)278 gdk_seat_default_get_tool (GdkSeat *seat,
279                            guint64  serial,
280                            guint64  hw_id)
281 {
282   GdkSeatDefaultPrivate *priv;
283   GdkDeviceTool *tool;
284   guint i;
285 
286   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
287 
288   if (!priv->tools)
289     return NULL;
290 
291   for (i = 0; i < priv->tools->len; i++)
292     {
293       tool = g_ptr_array_index (priv->tools, i);
294 
295       if (tool->serial == serial && tool->hw_id == hw_id)
296         return tool;
297     }
298 
299   return NULL;
300 }
301 
302 static void
gdk_seat_default_class_init(GdkSeatDefaultClass * klass)303 gdk_seat_default_class_init (GdkSeatDefaultClass *klass)
304 {
305   GObjectClass *object_class = G_OBJECT_CLASS (klass);
306   GdkSeatClass *seat_class = GDK_SEAT_CLASS (klass);
307 
308   object_class->dispose = gdk_seat_dispose;
309 
310   seat_class->get_capabilities = gdk_seat_default_get_capabilities;
311 
312   seat_class->grab = gdk_seat_default_grab;
313   seat_class->ungrab = gdk_seat_default_ungrab;
314 
315   seat_class->get_master = gdk_seat_default_get_master;
316   seat_class->get_slaves = gdk_seat_default_get_slaves;
317 
318   seat_class->get_tool = gdk_seat_default_get_tool;
319 }
320 
321 static void
gdk_seat_default_init(GdkSeatDefault * seat)322 gdk_seat_default_init (GdkSeatDefault *seat)
323 {
324 }
325 
326 GdkSeat *
gdk_seat_default_new_for_master_pair(GdkDevice * pointer,GdkDevice * keyboard)327 gdk_seat_default_new_for_master_pair (GdkDevice *pointer,
328                                       GdkDevice *keyboard)
329 {
330   GdkSeatDefaultPrivate *priv;
331   GdkDisplay *display;
332   GdkSeat *seat;
333 
334   display = gdk_device_get_display (pointer);
335 
336   seat = g_object_new (GDK_TYPE_SEAT_DEFAULT,
337                        "display", display,
338                        NULL);
339 
340   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
341   priv->master_pointer = g_object_ref (pointer);
342   priv->master_keyboard = g_object_ref (keyboard);
343 
344   gdk_seat_device_added (seat, priv->master_pointer);
345   gdk_seat_device_added (seat, priv->master_keyboard);
346 
347   return seat;
348 }
349 
350 void
gdk_seat_default_add_slave(GdkSeatDefault * seat,GdkDevice * device)351 gdk_seat_default_add_slave (GdkSeatDefault *seat,
352                             GdkDevice      *device)
353 {
354   GdkSeatDefaultPrivate *priv;
355   GdkSeatCapabilities capability;
356 
357   g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
358   g_return_if_fail (GDK_IS_DEVICE (device));
359 
360   priv = gdk_seat_default_get_instance_private (seat);
361   capability = device_get_capability (device);
362 
363   if (capability & (GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TOUCH))
364     priv->slave_pointers = g_list_prepend (priv->slave_pointers, g_object_ref (device));
365   else if (capability & GDK_SEAT_CAPABILITY_KEYBOARD)
366     priv->slave_keyboards = g_list_prepend (priv->slave_keyboards, g_object_ref (device));
367   else
368     {
369       g_critical ("Unhandled capability %x for device '%s'",
370                   capability, gdk_device_get_name (device));
371       return;
372     }
373 
374   priv->capabilities |= capability;
375 
376   gdk_seat_device_added (GDK_SEAT (seat), device);
377 }
378 
379 void
gdk_seat_default_remove_slave(GdkSeatDefault * seat,GdkDevice * device)380 gdk_seat_default_remove_slave (GdkSeatDefault *seat,
381                                GdkDevice      *device)
382 {
383   GdkSeatDefaultPrivate *priv;
384   GList *l;
385 
386   g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
387   g_return_if_fail (GDK_IS_DEVICE (device));
388 
389   priv = gdk_seat_default_get_instance_private (seat);
390 
391   if (g_list_find (priv->slave_pointers, device))
392     {
393       priv->slave_pointers = g_list_remove (priv->slave_pointers, device);
394 
395       priv->capabilities &= ~(GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TOUCH);
396       for (l = priv->slave_pointers; l; l = l->next)
397         priv->capabilities |= device_get_capability (GDK_DEVICE (l->data));
398 
399       gdk_seat_device_removed (GDK_SEAT (seat), device);
400       g_object_unref (device);
401     }
402   else if (g_list_find (priv->slave_keyboards, device))
403     {
404       priv->slave_keyboards = g_list_remove (priv->slave_keyboards, device);
405 
406       if (priv->slave_keyboards == NULL)
407         priv->capabilities &= ~GDK_SEAT_CAPABILITY_KEYBOARD;
408 
409       gdk_seat_device_removed (GDK_SEAT (seat), device);
410       g_object_unref (device);
411     }
412 }
413 
414 void
gdk_seat_default_add_tool(GdkSeatDefault * seat,GdkDeviceTool * tool)415 gdk_seat_default_add_tool (GdkSeatDefault *seat,
416                            GdkDeviceTool  *tool)
417 {
418   GdkSeatDefaultPrivate *priv;
419 
420   g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
421   g_return_if_fail (tool != NULL);
422 
423   priv = gdk_seat_default_get_instance_private (seat);
424 
425   if (!priv->tools)
426     priv->tools = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
427 
428   g_ptr_array_add (priv->tools, g_object_ref (tool));
429   g_signal_emit_by_name (seat, "tool-added", tool);
430 }
431 
432 void
gdk_seat_default_remove_tool(GdkSeatDefault * seat,GdkDeviceTool * tool)433 gdk_seat_default_remove_tool (GdkSeatDefault *seat,
434                               GdkDeviceTool  *tool)
435 {
436   GdkSeatDefaultPrivate *priv;
437 
438   g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
439   g_return_if_fail (tool != NULL);
440 
441   priv = gdk_seat_default_get_instance_private (seat);
442 
443   if (tool != gdk_seat_get_tool (GDK_SEAT (seat), tool->serial, tool->hw_id))
444     return;
445 
446   g_signal_emit_by_name (seat, "tool-removed", tool);
447   g_ptr_array_remove (priv->tools, tool);
448 }
449