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 *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 | |
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 (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 | |
92 | static GdkSeatCapabilities |
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 |
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 (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 | |
176 | static void |
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 (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 | |
189 | static GdkDevice * |
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 |
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 * |
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 (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 | |
260 | static GList * |
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 (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 | |
278 | static GList * |
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 (list: tools, data: tool); |
295 | } |
296 | |
297 | return tools; |
298 | } |
299 | |
300 | static void |
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 |
319 | gdk_seat_default_init (GdkSeatDefault *seat) |
320 | { |
321 | } |
322 | |
323 | GdkSeat * |
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 (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 | |
347 | void |
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 (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 | |
376 | void |
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 (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 | |
412 | void |
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 (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 | |
430 | void |
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 (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 | |