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
23typedef struct _GdkSeatDefaultPrivate GdkSeatDefaultPrivate;
24
25struct _GdkSeatDefaultPrivate
26{
27 GdkDevice *logical_pointer;
28 GdkDevice *logical_keyboard;
29 GList *physical_pointers;
30 GList *physical_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
49G_DEFINE_TYPE_WITH_PRIVATE (GdkSeatDefault, gdk_seat_default, GDK_TYPE_SEAT)
50
51static void
52gdk_seat_default_dispose (GObject *object)
53{
54 GdkSeatDefault *seat = GDK_SEAT_DEFAULT (object);
55 GdkSeatDefaultPrivate *priv = gdk_seat_default_get_instance_private (self: seat);
56 GList *l;
57
58 if (priv->logical_pointer)
59 {
60 gdk_seat_device_removed (GDK_SEAT (seat), device: 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), device: 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), device: l->data);
73 g_object_unref (object: l->data);
74 }
75
76 for (l = priv->physical_keyboards; l; l = l->next)
77 {
78 gdk_seat_device_removed (GDK_SEAT (seat), device: l->data);
79 g_object_unref (object: l->data);
80 }
81
82 g_clear_pointer (&priv->tools, g_ptr_array_unref);
83
84 g_list_free (list: priv->physical_pointers);
85 g_list_free (list: 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
92static GdkSeatCapabilities
93gdk_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
102static GdkGrabStatus
103gdk_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 (device: priv->logical_pointer, surface,
148 owner_events,
149 event_mask: pointer_evmask, cursor,
150 time_: evtime);
151 }
152
153 if (status == GDK_GRAB_SUCCESS &&
154 capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
155 {
156 status = gdk_device_grab (device: priv->logical_keyboard, surface,
157 owner_events,
158 KEYBOARD_EVENTS, cursor,
159 time_: evtime);
160
161 if (status != GDK_GRAB_SUCCESS)
162 {
163 if (capabilities & ~GDK_SEAT_CAPABILITY_KEYBOARD)
164 gdk_device_ungrab (device: priv->logical_pointer, time_: 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
176static void
177gdk_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 (device: priv->logical_pointer, GDK_CURRENT_TIME);
185 gdk_device_ungrab (device: priv->logical_keyboard, GDK_CURRENT_TIME);
186 G_GNUC_END_IGNORE_DEPRECATIONS;
187}
188
189static GdkDevice *
190gdk_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
213static GdkSeatCapabilities
214device_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
240static GList *
241append_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 (device: l->data);
252
253 if ((device_cap & capabilities) != 0)
254 list = g_list_prepend (list, data: l->data);
255 }
256
257 return list;
258}
259
260static GList *
261gdk_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 (list: devices, devices: priv->physical_pointers, capabilities);
271
272 if (capabilities & (GDK_SEAT_CAPABILITY_KEYBOARD | GDK_SEAT_CAPABILITY_TABLET_PAD))
273 devices = append_filtered (list: devices, devices: priv->physical_keyboards, capabilities);
274
275 return devices;
276}
277
278static GList *
279gdk_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 (list: tools, data: tool);
295 }
296
297 return tools;
298}
299
300static void
301gdk_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
318static void
319gdk_seat_default_init (GdkSeatDefault *seat)
320{
321}
322
323GdkSeat *
324gdk_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 (device: pointer);
332
333 seat = g_object_new (GDK_TYPE_SEAT_DEFAULT,
334 first_property_name: "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, device: priv->logical_pointer);
342 gdk_seat_device_added (seat, device: priv->logical_keyboard);
343
344 return seat;
345}
346
347void
348gdk_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 (self: seat);
358 capability = device_get_capability (device);
359
360 if (capability & GDK_SEAT_CAPABILITY_ALL_POINTING)
361 priv->physical_pointers = g_list_prepend (list: 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 (list: 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
376void
377gdk_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 (self: seat);
387
388 if (g_list_find (list: priv->physical_pointers, data: device))
389 {
390 priv->physical_pointers = g_list_remove (list: priv->physical_pointers, data: 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 (object: device);
398 }
399 else if (g_list_find (list: priv->physical_keyboards, data: device))
400 {
401 priv->physical_keyboards = g_list_remove (list: priv->physical_keyboards, data: 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 (object: device);
409 }
410}
411
412void
413gdk_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 (self: seat);
422
423 if (!priv->tools)
424 priv->tools = g_ptr_array_new_with_free_func (element_free_func: (GDestroyNotify) g_object_unref);
425
426 g_ptr_array_add (array: priv->tools, g_object_ref (tool));
427 g_signal_emit_by_name (instance: seat, detailed_signal: "tool-added", tool);
428}
429
430void
431gdk_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 (self: seat);
440
441 if (tool != gdk_seat_get_tool (GDK_SEAT (seat), serial: tool->serial, hw_id: tool->hw_id, type: tool->type))
442 return;
443
444 g_signal_emit_by_name (instance: seat, detailed_signal: "tool-removed", tool);
445 g_ptr_array_remove (array: priv->tools, data: tool);
446}
447

source code of gtk/gdk/gdkseatdefault.c