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