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 *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
48G_DEFINE_TYPE_WITH_PRIVATE (GdkSeatDefault, gdk_seat_default, GDK_TYPE_SEAT)
49
50static void
51gdk_seat_dispose (GObject *object)
52{
53 GdkSeatDefault *seat = GDK_SEAT_DEFAULT (object);
54 GdkSeatDefaultPrivate *priv = gdk_seat_default_get_instance_private (seat);
55 GList *l;
56
57 if (priv->master_pointer)
58 {
59 gdk_seat_device_removed (GDK_SEAT (seat), priv->master_pointer);
60 g_clear_object (&priv->master_pointer);
61 }
62
63 if (priv->master_keyboard)
64 {
65 gdk_seat_device_removed (GDK_SEAT (seat), priv->master_keyboard);
66 g_clear_object (&priv->master_pointer);
67 }
68
69 for (l = priv->slave_pointers; l; l = l->next)
70 {
71 gdk_seat_device_removed (GDK_SEAT (seat), l->data);
72 g_object_unref (l->data);
73 }
74
75 for (l = priv->slave_keyboards; l; l = l->next)
76 {
77 gdk_seat_device_removed (GDK_SEAT (seat), l->data);
78 g_object_unref (l->data);
79 }
80
81 if (priv->tools)
82 {
83 g_ptr_array_unref (priv->tools);
84 priv->tools = NULL;
85 }
86
87 g_list_free (priv->slave_pointers);
88 g_list_free (priv->slave_keyboards);
89 priv->slave_pointers = NULL;
90 priv->slave_keyboards = NULL;
91
92 G_OBJECT_CLASS (gdk_seat_default_parent_class)->dispose (object);
93}
94
95static GdkSeatCapabilities
96gdk_seat_default_get_capabilities (GdkSeat *seat)
97{
98 GdkSeatDefaultPrivate *priv;
99
100 priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
101
102 return priv->capabilities;
103}
104
105static GdkGrabStatus
106gdk_seat_default_grab (GdkSeat *seat,
107 GdkWindow *window,
108 GdkSeatCapabilities capabilities,
109 gboolean owner_events,
110 GdkCursor *cursor,
111 const GdkEvent *event,
112 GdkSeatGrabPrepareFunc prepare_func,
113 gpointer prepare_func_data)
114{
115 GdkSeatDefaultPrivate *priv;
116 guint32 evtime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
117 GdkGrabStatus status = GDK_GRAB_SUCCESS;
118
119 priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
120
121 if (prepare_func)
122 (prepare_func) (seat, window, prepare_func_data);
123
124 if (!gdk_window_is_visible (window))
125 {
126 g_critical ("Window %p has not been made visible in GdkSeatGrabPrepareFunc",
127 window);
128 return GDK_GRAB_NOT_VIEWABLE;
129 }
130
131 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
132
133 if (capabilities & GDK_SEAT_CAPABILITY_ALL_POINTING)
134 {
135 status = gdk_device_grab (priv->master_pointer, window,
136 GDK_OWNERSHIP_NONE, owner_events,
137 POINTER_EVENTS, cursor,
138 evtime);
139 }
140
141 if (status == GDK_GRAB_SUCCESS &&
142 capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
143 {
144 status = gdk_device_grab (priv->master_keyboard, window,
145 GDK_OWNERSHIP_NONE, owner_events,
146 KEYBOARD_EVENTS, cursor,
147 evtime);
148
149 if (status != GDK_GRAB_SUCCESS)
150 {
151 if (capabilities & ~GDK_SEAT_CAPABILITY_KEYBOARD)
152 gdk_device_ungrab (priv->master_pointer, evtime);
153 gdk_window_hide (window);
154 }
155 }
156
157 G_GNUC_END_IGNORE_DEPRECATIONS;
158
159 return status;
160}
161
162static void
163gdk_seat_default_ungrab (GdkSeat *seat)
164{
165 GdkSeatDefaultPrivate *priv;
166
167 priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
168
169 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
170 gdk_device_ungrab (priv->master_pointer, GDK_CURRENT_TIME);
171 gdk_device_ungrab (priv->master_keyboard, GDK_CURRENT_TIME);
172 G_GNUC_END_IGNORE_DEPRECATIONS;
173}
174
175static GdkDevice *
176gdk_seat_default_get_master (GdkSeat *seat,
177 GdkSeatCapabilities capability)
178{
179 GdkSeatDefaultPrivate *priv;
180
181 priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
182
183 /* There must be only one flag set */
184 switch (capability)
185 {
186 case GDK_SEAT_CAPABILITY_POINTER:
187 case GDK_SEAT_CAPABILITY_TOUCH:
188 return priv->master_pointer;
189 case GDK_SEAT_CAPABILITY_KEYBOARD:
190 return priv->master_keyboard;
191 default:
192 g_warning ("Unhandled capability %x", capability);
193 break;
194 }
195
196 return NULL;
197}
198
199static GdkSeatCapabilities
200device_get_capability (GdkDevice *device)
201{
202 GdkInputSource source;
203
204 source = gdk_device_get_source (device);
205
206 switch (source)
207 {
208 case GDK_SOURCE_KEYBOARD:
209 return GDK_SEAT_CAPABILITY_KEYBOARD;
210 case GDK_SOURCE_TOUCHSCREEN:
211 return GDK_SEAT_CAPABILITY_TOUCH;
212 case GDK_SOURCE_MOUSE:
213 case GDK_SOURCE_TOUCHPAD:
214 default:
215 return GDK_SEAT_CAPABILITY_POINTER;
216 }
217
218 return GDK_SEAT_CAPABILITY_NONE;
219}
220
221static GList *
222append_filtered (GList *list,
223 GList *devices,
224 GdkSeatCapabilities capabilities)
225{
226 GList *l;
227
228 for (l = devices; l; l = l->next)
229 {
230 GdkSeatCapabilities device_cap;
231
232 device_cap = device_get_capability (l->data);
233
234 if ((device_cap & capabilities) != 0)
235 list = g_list_prepend (list, l->data);
236 }
237
238 return list;
239}
240
241static GList *
242gdk_seat_default_get_slaves (GdkSeat *seat,
243 GdkSeatCapabilities capabilities)
244{
245 GdkSeatDefaultPrivate *priv;
246 GList *devices = NULL;
247
248 priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
249
250 if (capabilities & (GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TOUCH))
251 devices = append_filtered (devices, priv->slave_pointers, capabilities);
252
253 if (capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
254 devices = append_filtered (devices, priv->slave_keyboards, capabilities);
255
256 return devices;
257}
258
259static GdkDeviceTool *
260gdk_seat_default_get_tool (GdkSeat *seat,
261 guint64 serial)
262{
263 GdkSeatDefaultPrivate *priv;
264 GdkDeviceTool *tool;
265 guint i;
266
267 priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
268
269 if (!priv->tools)
270 return NULL;
271
272 for (i = 0; i < priv->tools->len; i++)
273 {
274 tool = g_ptr_array_index (priv->tools, i);
275
276 if (tool->serial == serial)
277 return tool;
278 }
279
280 return NULL;
281}
282
283static void
284gdk_seat_default_class_init (GdkSeatDefaultClass *klass)
285{
286 GObjectClass *object_class = G_OBJECT_CLASS (klass);
287 GdkSeatClass *seat_class = GDK_SEAT_CLASS (klass);
288
289 object_class->dispose = gdk_seat_dispose;
290
291 seat_class->get_capabilities = gdk_seat_default_get_capabilities;
292
293 seat_class->grab = gdk_seat_default_grab;
294 seat_class->ungrab = gdk_seat_default_ungrab;
295
296 seat_class->get_master = gdk_seat_default_get_master;
297 seat_class->get_slaves = gdk_seat_default_get_slaves;
298
299 seat_class->get_tool = gdk_seat_default_get_tool;
300}
301
302static void
303gdk_seat_default_init (GdkSeatDefault *seat)
304{
305}
306
307GdkSeat *
308gdk_seat_default_new_for_master_pair (GdkDevice *pointer,
309 GdkDevice *keyboard)
310{
311 GdkSeatDefaultPrivate *priv;
312 GdkDisplay *display;
313 GdkSeat *seat;
314
315 display = gdk_device_get_display (pointer);
316
317 seat = g_object_new (GDK_TYPE_SEAT_DEFAULT,
318 "display", display,
319 NULL);
320
321 priv = gdk_seat_default_get_instance_private (GDK_SEAT_DEFAULT (seat));
322 priv->master_pointer = g_object_ref (pointer);
323 priv->master_keyboard = g_object_ref (keyboard);
324
325 gdk_seat_device_added (seat, priv->master_pointer);
326 gdk_seat_device_added (seat, priv->master_keyboard);
327
328 return seat;
329}
330
331void
332gdk_seat_default_add_slave (GdkSeatDefault *seat,
333 GdkDevice *device)
334{
335 GdkSeatDefaultPrivate *priv;
336 GdkSeatCapabilities capability;
337
338 g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
339 g_return_if_fail (GDK_IS_DEVICE (device));
340
341 priv = gdk_seat_default_get_instance_private (seat);
342 capability = device_get_capability (device);
343
344 if (capability & (GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TOUCH))
345 priv->slave_pointers = g_list_prepend (priv->slave_pointers, g_object_ref (device));
346 else if (capability & GDK_SEAT_CAPABILITY_KEYBOARD)
347 priv->slave_keyboards = g_list_prepend (priv->slave_keyboards, g_object_ref (device));
348 else
349 {
350 g_critical ("Unhandled capability %x for device '%s'",
351 capability, gdk_device_get_name (device));
352 return;
353 }
354
355 priv->capabilities |= capability;
356
357 gdk_seat_device_added (GDK_SEAT (seat), device);
358}
359
360void
361gdk_seat_default_remove_slave (GdkSeatDefault *seat,
362 GdkDevice *device)
363{
364 GdkSeatDefaultPrivate *priv;
365 GList *l;
366
367 g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
368 g_return_if_fail (GDK_IS_DEVICE (device));
369
370 priv = gdk_seat_default_get_instance_private (seat);
371
372 if (g_list_find (priv->slave_pointers, device))
373 {
374 priv->slave_pointers = g_list_remove (priv->slave_pointers, device);
375
376 priv->capabilities &= ~(GDK_SEAT_CAPABILITY_POINTER | GDK_SEAT_CAPABILITY_TOUCH);
377 for (l = priv->slave_pointers; l; l = l->next)
378 priv->capabilities |= device_get_capability (GDK_DEVICE (l->data));
379
380 gdk_seat_device_removed (GDK_SEAT (seat), device);
381 }
382 else if (g_list_find (priv->slave_keyboards, device))
383 {
384 priv->slave_keyboards = g_list_remove (priv->slave_keyboards, device);
385
386 if (priv->slave_keyboards == NULL)
387 priv->capabilities &= ~GDK_SEAT_CAPABILITY_KEYBOARD;
388
389 gdk_seat_device_removed (GDK_SEAT (seat), device);
390 }
391}
392
393void
394gdk_seat_default_add_tool (GdkSeatDefault *seat,
395 GdkDeviceTool *tool)
396{
397 GdkSeatDefaultPrivate *priv;
398
399 g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
400 g_return_if_fail (tool != NULL);
401
402 priv = gdk_seat_default_get_instance_private (seat);
403
404 if (!priv->tools)
405 priv->tools = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
406
407 g_ptr_array_add (priv->tools, g_object_ref (tool));
408 g_signal_emit_by_name (seat, "tool-added", tool);
409}
410
411void
412gdk_seat_default_remove_tool (GdkSeatDefault *seat,
413 GdkDeviceTool *tool)
414{
415 GdkSeatDefaultPrivate *priv;
416
417 g_return_if_fail (GDK_IS_SEAT_DEFAULT (seat));
418 g_return_if_fail (tool != NULL);
419
420 priv = gdk_seat_default_get_instance_private (seat);
421
422 if (tool != gdk_seat_get_tool (GDK_SEAT (seat),
423 gdk_device_tool_get_serial (tool)))
424 return;
425
426 g_signal_emit_by_name (seat, "tool-removed", tool);
427 g_ptr_array_remove (priv->tools, tool);
428}
429