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 #include "gdkinternals.h"
23 
24 typedef struct _GdkSeatDefaultPrivate GdkSeatDefaultPrivate;
25 
26 struct _GdkSeatDefaultPrivate
27 {
28   GdkDevice *logical_pointer;
29   GdkDevice *logical_keyboard;
30   GList *physical_pointers;
31   GList *physical_keyboards;
32   GdkSeatCapabilities capabilities;
33 
34   GPtrArray *tools;
35 };
36 
37 #define KEYBOARD_EVENTS (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |    \
38                          GDK_FOCUS_CHANGE_MASK)
39 #define TOUCH_EVENTS    (GDK_TOUCH_MASK)
40 #define POINTER_EVENTS  (GDK_POINTER_MOTION_MASK |                      \
41                          GDK_BUTTON_PRESS_MASK |                        \
42                          GDK_BUTTON_RELEASE_MASK |                      \
43                          GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK |     \
44                          GDK_ENTER_NOTIFY_MASK |                        \
45                          GDK_LEAVE_NOTIFY_MASK |                        \
46                          GDK_PROXIMITY_IN_MASK |                        \
47                          GDK_PROXIMITY_OUT_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_default_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->logical_pointer)
59     {
60       gdk_seat_device_removed (GDK_SEAT (seat), priv->logical_pointer);
61       g_clear_object (&priv->logical_pointer);
62     }
63 
64   if (priv->logical_keyboard)
65     {
66       gdk_seat_device_removed (GDK_SEAT (seat), priv->logical_keyboard);
67       g_clear_object (&priv->logical_pointer);
68     }
69 
70   for (l = priv->physical_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->physical_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   g_clear_pointer (&priv->tools, g_ptr_array_unref);
83 
84   g_list_free (priv->physical_pointers);
85   g_list_free (priv->physical_keyboards);
86   priv->physical_pointers = NULL;
87   priv->physical_keyboards = NULL;
88 
89   G_OBJECT_CLASS (gdk_seat_default_parent_class)->dispose (object);
90 }
91 
92 static GdkSeatCapabilities
gdk_seat_default_get_capabilities(GdkSeat * seat)93 gdk_seat_default_get_capabilities (GdkSeat *seat)
94 {
95   GdkSeatDefaultPrivate *priv;
96 
97   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
98 
99   return priv->capabilities;
100 }
101 
102 static GdkGrabStatus
gdk_seat_default_grab(GdkSeat * seat,GdkSurface * surface,GdkSeatCapabilities capabilities,gboolean owner_events,GdkCursor * cursor,GdkEvent * event,GdkSeatGrabPrepareFunc prepare_func,gpointer prepare_func_data)103 gdk_seat_default_grab (GdkSeat                *seat,
104                        GdkSurface              *surface,
105                        GdkSeatCapabilities     capabilities,
106                        gboolean                owner_events,
107                        GdkCursor              *cursor,
108                        GdkEvent               *event,
109                        GdkSeatGrabPrepareFunc  prepare_func,
110                        gpointer                prepare_func_data)
111 {
112   GdkSeatDefaultPrivate *priv;
113   guint32 evtime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
114   GdkGrabStatus status = GDK_GRAB_SUCCESS;
115   gboolean was_visible;
116 
117   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
118   was_visible = gdk_surface_get_mapped (surface);
119 
120   if (prepare_func)
121     (prepare_func) (seat, surface, prepare_func_data);
122 
123   if (!gdk_surface_get_mapped (surface))
124     {
125       g_critical ("Surface %p has not been mapped in GdkSeatGrabPrepareFunc",
126                   surface);
127       return GDK_GRAB_NOT_VIEWABLE;
128     }
129 
130   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
131 
132   if (capabilities & GDK_SEAT_CAPABILITY_ALL_POINTING)
133     {
134       /* ALL_POINTING spans 3 capabilities; get the mask for the ones we have */
135       GdkEventMask pointer_evmask = 0;
136 
137       /* We let tablet styli take over the pointer cursor */
138       if (capabilities & (GDK_SEAT_CAPABILITY_POINTER |
139                           GDK_SEAT_CAPABILITY_TABLET_STYLUS))
140         {
141           pointer_evmask |= POINTER_EVENTS;
142         }
143 
144       if (capabilities & GDK_SEAT_CAPABILITY_TOUCH)
145         pointer_evmask |= TOUCH_EVENTS;
146 
147       status = gdk_device_grab (priv->logical_pointer, surface,
148                                 owner_events,
149                                 pointer_evmask, cursor,
150                                 evtime);
151     }
152 
153   if (status == GDK_GRAB_SUCCESS &&
154       capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
155     {
156       status = gdk_device_grab (priv->logical_keyboard, surface,
157                                 owner_events,
158                                 KEYBOARD_EVENTS, cursor,
159                                 evtime);
160 
161       if (status != GDK_GRAB_SUCCESS)
162         {
163           if (capabilities & ~GDK_SEAT_CAPABILITY_KEYBOARD)
164             gdk_device_ungrab (priv->logical_pointer, evtime);
165         }
166     }
167 
168   if (status != GDK_GRAB_SUCCESS && !was_visible)
169     gdk_surface_hide (surface);
170 
171   G_GNUC_END_IGNORE_DEPRECATIONS;
172 
173   return status;
174 }
175 
176 static void
gdk_seat_default_ungrab(GdkSeat * seat)177 gdk_seat_default_ungrab (GdkSeat *seat)
178 {
179   GdkSeatDefaultPrivate *priv;
180 
181   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
182 
183   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
184   gdk_device_ungrab (priv->logical_pointer, GDK_CURRENT_TIME);
185   gdk_device_ungrab (priv->logical_keyboard, GDK_CURRENT_TIME);
186   G_GNUC_END_IGNORE_DEPRECATIONS;
187 }
188 
189 static GdkDevice *
gdk_seat_default_get_logical_device(GdkSeat * seat,GdkSeatCapabilities capability)190 gdk_seat_default_get_logical_device (GdkSeat             *seat,
191                                      GdkSeatCapabilities  capability)
192 {
193   GdkSeatDefaultPrivate *priv;
194 
195   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
196 
197   /* There must be only one flag set */
198   switch ((guint) capability)
199     {
200     case GDK_SEAT_CAPABILITY_POINTER:
201     case GDK_SEAT_CAPABILITY_TOUCH:
202       return priv->logical_pointer;
203     case GDK_SEAT_CAPABILITY_KEYBOARD:
204       return priv->logical_keyboard;
205     default:
206       g_warning ("Unhandled capability %x", capability);
207       break;
208     }
209 
210   return NULL;
211 }
212 
213 static GdkSeatCapabilities
device_get_capability(GdkDevice * device)214 device_get_capability (GdkDevice *device)
215 {
216   GdkInputSource source;
217 
218   source = gdk_device_get_source (device);
219 
220   switch (source)
221     {
222     case GDK_SOURCE_KEYBOARD:
223       return GDK_SEAT_CAPABILITY_KEYBOARD;
224     case GDK_SOURCE_TOUCHSCREEN:
225       return GDK_SEAT_CAPABILITY_TOUCH;
226     case GDK_SOURCE_PEN:
227       return GDK_SEAT_CAPABILITY_TABLET_STYLUS;
228     case GDK_SOURCE_TABLET_PAD:
229       return GDK_SEAT_CAPABILITY_TABLET_PAD;
230     case GDK_SOURCE_MOUSE:
231     case GDK_SOURCE_TOUCHPAD:
232     case GDK_SOURCE_TRACKPOINT:
233     default:
234       return GDK_SEAT_CAPABILITY_POINTER;
235     }
236 
237   return GDK_SEAT_CAPABILITY_NONE;
238 }
239 
240 static GList *
append_filtered(GList * list,GList * devices,GdkSeatCapabilities capabilities)241 append_filtered (GList               *list,
242                  GList               *devices,
243                  GdkSeatCapabilities  capabilities)
244 {
245   GList *l;
246 
247   for (l = devices; l; l = l->next)
248     {
249       GdkSeatCapabilities device_cap;
250 
251       device_cap = device_get_capability (l->data);
252 
253       if ((device_cap & capabilities) != 0)
254         list = g_list_prepend (list, l->data);
255     }
256 
257   return list;
258 }
259 
260 static GList *
gdk_seat_default_get_devices(GdkSeat * seat,GdkSeatCapabilities capabilities)261 gdk_seat_default_get_devices (GdkSeat             *seat,
262                               GdkSeatCapabilities  capabilities)
263 {
264   GdkSeatDefaultPrivate *priv;
265   GList *devices = NULL;
266 
267   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
268 
269   if (capabilities & (GDK_SEAT_CAPABILITY_ALL_POINTING))
270     devices = append_filtered (devices, priv->physical_pointers, capabilities);
271 
272   if (capabilities & (GDK_SEAT_CAPABILITY_KEYBOARD | GDK_SEAT_CAPABILITY_TABLET_PAD))
273     devices = append_filtered (devices, priv->physical_keyboards, capabilities);
274 
275   return devices;
276 }
277 
278 static GList *
gdk_seat_default_get_tools(GdkSeat * seat)279 gdk_seat_default_get_tools (GdkSeat *seat)
280 {
281   GdkSeatDefaultPrivate *priv;
282   GdkDeviceTool *tool;
283   GList *tools = NULL;
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       tools = g_list_prepend (tools, tool);
295     }
296 
297   return tools;
298 }
299 
300 static void
gdk_seat_default_class_init(GdkSeatDefaultClass * klass)301 gdk_seat_default_class_init (GdkSeatDefaultClass *klass)
302 {
303   GObjectClass *object_class = G_OBJECT_CLASS (klass);
304   GdkSeatClass *seat_class = GDK_SEAT_CLASS (klass);
305 
306   object_class->dispose = gdk_seat_default_dispose;
307 
308   seat_class->get_capabilities = gdk_seat_default_get_capabilities;
309 
310   seat_class->grab = gdk_seat_default_grab;
311   seat_class->ungrab = gdk_seat_default_ungrab;
312 
313   seat_class->get_logical_device = gdk_seat_default_get_logical_device;
314   seat_class->get_devices = gdk_seat_default_get_devices;
315   seat_class->get_tools = gdk_seat_default_get_tools;
316 }
317 
318 static void
gdk_seat_default_init(GdkSeatDefault * seat)319 gdk_seat_default_init (GdkSeatDefault *seat)
320 {
321 }
322 
323 GdkSeat *
gdk_seat_default_new_for_logical_pair(GdkDevice * pointer,GdkDevice * keyboard)324 gdk_seat_default_new_for_logical_pair (GdkDevice *pointer,
325                                        GdkDevice *keyboard)
326 {
327   GdkSeatDefaultPrivate *priv;
328   GdkDisplay *display;
329   GdkSeat *seat;
330 
331   display = gdk_device_get_display (pointer);
332 
333   seat = g_object_new (GDK_TYPE_SEAT_DEFAULT,
334                        "display", display,
335                        NULL);
336 
337   priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
338   priv->logical_pointer = g_object_ref (pointer);
339   priv->logical_keyboard = g_object_ref (keyboard);
340 
341   gdk_seat_device_added (seat, priv->logical_pointer);
342   gdk_seat_device_added (seat, priv->logical_keyboard);
343 
344   return seat;
345 }
346 
347 void
gdk_seat_default_add_physical_device(GdkSeatDefault * seat,GdkDevice * device)348 gdk_seat_default_add_physical_device (GdkSeatDefault *seat,
349                                       GdkDevice      *device)
350 {
351   GdkSeatDefaultPrivate *priv;
352   GdkSeatCapabilities capability;
353 
354   g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
355   g_return_if_fail (GDK_IS_DEVICE (device));
356 
357   priv = gdk_seat_default_get_instance_private (seat);
358   capability = device_get_capability (device);
359 
360   if (capability & GDK_SEAT_CAPABILITY_ALL_POINTING)
361     priv->physical_pointers = g_list_prepend (priv->physical_pointers, g_object_ref (device));
362   else if (capability & (GDK_SEAT_CAPABILITY_KEYBOARD | GDK_SEAT_CAPABILITY_TABLET_PAD))
363     priv->physical_keyboards = g_list_prepend (priv->physical_keyboards, g_object_ref (device));
364   else
365     {
366       g_critical ("Unhandled capability %x for device '%s'",
367                   capability, gdk_device_get_name (device));
368       return;
369     }
370 
371   priv->capabilities |= capability;
372 
373   gdk_seat_device_added (GDK_SEAT (seat), device);
374 }
375 
376 void
gdk_seat_default_remove_physical_device(GdkSeatDefault * seat,GdkDevice * device)377 gdk_seat_default_remove_physical_device (GdkSeatDefault *seat,
378                                          GdkDevice      *device)
379 {
380   GdkSeatDefaultPrivate *priv;
381   GList *l;
382 
383   g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
384   g_return_if_fail (GDK_IS_DEVICE (device));
385 
386   priv = gdk_seat_default_get_instance_private (seat);
387 
388   if (g_list_find (priv->physical_pointers, device))
389     {
390       priv->physical_pointers = g_list_remove (priv->physical_pointers, device);
391 
392       priv->capabilities &= ~(GDK_SEAT_CAPABILITY_ALL_POINTING);
393       for (l = priv->physical_pointers; l; l = l->next)
394         priv->capabilities |= device_get_capability (GDK_DEVICE (l->data));
395 
396       gdk_seat_device_removed (GDK_SEAT (seat), device);
397       g_object_unref (device);
398     }
399   else if (g_list_find (priv->physical_keyboards, device))
400     {
401       priv->physical_keyboards = g_list_remove (priv->physical_keyboards, device);
402 
403       priv->capabilities &= ~(GDK_SEAT_CAPABILITY_KEYBOARD | GDK_SEAT_CAPABILITY_TABLET_PAD);
404       for (l = priv->physical_keyboards; l; l = l->next)
405         priv->capabilities |= device_get_capability (GDK_DEVICE (l->data));
406 
407       gdk_seat_device_removed (GDK_SEAT (seat), device);
408       g_object_unref (device);
409     }
410 }
411 
412 void
gdk_seat_default_add_tool(GdkSeatDefault * seat,GdkDeviceTool * tool)413 gdk_seat_default_add_tool (GdkSeatDefault *seat,
414                            GdkDeviceTool  *tool)
415 {
416   GdkSeatDefaultPrivate *priv;
417 
418   g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
419   g_return_if_fail (tool != NULL);
420 
421   priv = gdk_seat_default_get_instance_private (seat);
422 
423   if (!priv->tools)
424     priv->tools = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
425 
426   g_ptr_array_add (priv->tools, g_object_ref (tool));
427   g_signal_emit_by_name (seat, "tool-added", tool);
428 }
429 
430 void
gdk_seat_default_remove_tool(GdkSeatDefault * seat,GdkDeviceTool * tool)431 gdk_seat_default_remove_tool (GdkSeatDefault *seat,
432                               GdkDeviceTool  *tool)
433 {
434   GdkSeatDefaultPrivate *priv;
435 
436   g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
437   g_return_if_fail (tool != NULL);
438 
439   priv = gdk_seat_default_get_instance_private (seat);
440 
441   if (tool != gdk_seat_get_tool (GDK_SEAT (seat), tool->serial, tool->hw_id, tool->type))
442     return;
443 
444   g_signal_emit_by_name (seat, "tool-removed", tool);
445   g_ptr_array_remove (priv->tools, tool);
446 }
447