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 "config.h"
21
22#include <glib-object.h>
23#include "gdkdisplay.h"
24#include "gdkdevice.h"
25#include "gdkdevicetoolprivate.h"
26#include "gdkseatprivate.h"
27#include "gdkdeviceprivate.h"
28#include "gdkintl.h"
29
30/**
31 * GdkSeat:
32 *
33 * The `GdkSeat` object represents a collection of input devices
34 * that belong to a user.
35 */
36
37typedef struct _GdkSeatPrivate GdkSeatPrivate;
38
39struct _GdkSeatPrivate
40{
41 GdkDisplay *display;
42};
43
44enum {
45 DEVICE_ADDED,
46 DEVICE_REMOVED,
47 TOOL_ADDED,
48 TOOL_REMOVED,
49 N_SIGNALS
50};
51
52enum {
53 PROP_0,
54 PROP_DISPLAY,
55 N_PROPS
56};
57
58static guint signals[N_SIGNALS] = { 0 };
59static GParamSpec *props[N_PROPS] = { NULL };
60
61G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkSeat, gdk_seat, G_TYPE_OBJECT)
62
63static void
64gdk_seat_set_property (GObject *object,
65 guint prop_id,
66 const GValue *value,
67 GParamSpec *pspec)
68{
69 GdkSeatPrivate *priv = gdk_seat_get_instance_private (GDK_SEAT (object));
70
71 switch (prop_id)
72 {
73 case PROP_DISPLAY:
74 priv->display = g_value_get_object (value);
75 break;
76 default:
77 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
78 break;
79 }
80}
81
82static void
83gdk_seat_get_property (GObject *object,
84 guint prop_id,
85 GValue *value,
86 GParamSpec *pspec)
87{
88 GdkSeatPrivate *priv = gdk_seat_get_instance_private (GDK_SEAT (object));
89
90 switch (prop_id)
91 {
92 case PROP_DISPLAY:
93 g_value_set_object (value, v_object: priv->display);
94 break;
95 default:
96 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
97 break;
98 }
99}
100
101static void
102gdk_seat_class_init (GdkSeatClass *klass)
103{
104 GObjectClass *object_class = G_OBJECT_CLASS (klass);
105
106 object_class->set_property = gdk_seat_set_property;
107 object_class->get_property = gdk_seat_get_property;
108
109 /**
110 * GdkSeat::device-added:
111 * @seat: the object on which the signal is emitted
112 * @device: the newly added `GdkDevice`.
113 *
114 * Emitted when a new input device is related to this seat.
115 */
116 signals [DEVICE_ADDED] =
117 g_signal_new (signal_name: g_intern_static_string (string: "device-added"),
118 G_TYPE_FROM_CLASS (klass),
119 signal_flags: G_SIGNAL_RUN_LAST,
120 G_STRUCT_OFFSET (GdkSeatClass, device_added),
121 NULL, NULL,
122 NULL,
123 G_TYPE_NONE, n_params: 1,
124 GDK_TYPE_DEVICE);
125
126 /**
127 * GdkSeat::device-removed:
128 * @seat: the object on which the signal is emitted
129 * @device: the just removed `GdkDevice`.
130 *
131 * Emitted when an input device is removed (e.g. unplugged).
132 */
133 signals [DEVICE_REMOVED] =
134 g_signal_new (signal_name: g_intern_static_string (string: "device-removed"),
135 G_TYPE_FROM_CLASS (klass),
136 signal_flags: G_SIGNAL_RUN_LAST,
137 G_STRUCT_OFFSET (GdkSeatClass, device_removed),
138 NULL, NULL,
139 NULL,
140 G_TYPE_NONE, n_params: 1,
141 GDK_TYPE_DEVICE);
142
143 /**
144 * GdkSeat::tool-added:
145 * @seat: the object on which the signal is emitted
146 * @tool: the new `GdkDeviceTool` known to the seat
147 *
148 * Emitted whenever a new tool is made known to the seat.
149 *
150 * The tool may later be assigned to a device (i.e. on
151 * proximity with a tablet). The device will emit the
152 * [signal@Gdk.Device::tool-changed] signal accordingly.
153 *
154 * A same tool may be used by several devices.
155 */
156 signals [TOOL_ADDED] =
157 g_signal_new (signal_name: g_intern_static_string (string: "tool-added"),
158 G_TYPE_FROM_CLASS (klass),
159 signal_flags: G_SIGNAL_RUN_LAST,
160 class_offset: 0, NULL, NULL,
161 NULL,
162 G_TYPE_NONE, n_params: 1,
163 GDK_TYPE_DEVICE_TOOL);
164
165 /**
166 * GdkSeat::tool-removed:
167 * @seat: the object on which the signal is emitted
168 * @tool: the just removed `GdkDeviceTool`
169 *
170 * Emitted whenever a tool is no longer known to this @seat.
171 */
172 signals [TOOL_REMOVED] =
173 g_signal_new (signal_name: g_intern_static_string (string: "tool-removed"),
174 G_TYPE_FROM_CLASS (klass),
175 signal_flags: G_SIGNAL_RUN_LAST,
176 class_offset: 0, NULL, NULL,
177 NULL,
178 G_TYPE_NONE, n_params: 1,
179 GDK_TYPE_DEVICE_TOOL);
180
181 /**
182 * GdkSeat:display: (attributes org.gtk.Property.get=gdk_seat_get_display)
183 *
184 * `GdkDisplay` of this seat.
185 */
186 props[PROP_DISPLAY] =
187 g_param_spec_object (name: "display",
188 P_("Display"),
189 P_("Display"),
190 GDK_TYPE_DISPLAY,
191 flags: G_PARAM_READWRITE |
192 G_PARAM_CONSTRUCT_ONLY |
193 G_PARAM_STATIC_STRINGS);
194
195 g_object_class_install_properties (oclass: object_class, n_pspecs: N_PROPS, pspecs: props);
196}
197
198static void
199gdk_seat_init (GdkSeat *seat)
200{
201}
202
203/**
204 * gdk_seat_get_capabilities:
205 * @seat: a `GdkSeat`
206 *
207 * Returns the capabilities this `GdkSeat` currently has.
208 *
209 * Returns: the seat capabilities
210 */
211GdkSeatCapabilities
212gdk_seat_get_capabilities (GdkSeat *seat)
213{
214 GdkSeatClass *seat_class;
215
216 g_return_val_if_fail (GDK_IS_SEAT (seat), GDK_SEAT_CAPABILITY_NONE);
217
218 seat_class = GDK_SEAT_GET_CLASS (seat);
219 return seat_class->get_capabilities (seat);
220}
221
222/*
223 * gdk_seat_grab:
224 * @seat: a `GdkSeat`
225 * @surface: the `GdkSurface` which will own the grab
226 * @capabilities: capabilities that will be grabbed
227 * @owner_events: if %FALSE then all device events are reported with respect to
228 * @surface and are only reported if selected by @event_mask. If %TRUE then
229 * pointer events for this application are reported as normal, but pointer
230 * events outside this application are reported with respect to @surface and
231 * only if selected by @event_mask. In either mode, unreported events are
232 * discarded.
233 * @cursor: (nullable): the cursor to display while the grab is active.
234 * If this is %NULL then the normal cursors are used for @surface and
235 * its descendants, and the cursor for @surface is used elsewhere.
236 * @event: (nullable): the event that is triggering the grab, or %NULL if none
237 * is available.
238 * @prepare_func: (nullable) (scope call): function to prepare the surface
239 * to be grabbed, it can be %NULL if @surface is visible before this call.
240 * @prepare_func_data: (closure): user data to pass to @prepare_func
241 *
242 * Grabs the seat so that all events corresponding to the given @capabilities
243 * are passed to this application.
244 *
245 * The grab remains in place until the seat is ungrabbed with
246 * [method@Gdk.Seat.ungrab], or the surface becomes hidden. This overrides
247 * any previous grab on the seat by this client.
248 *
249 * As a rule of thumb, if a grab is desired over %GDK_SEAT_CAPABILITY_POINTER,
250 * all other "pointing" capabilities (eg. %GDK_SEAT_CAPABILITY_TOUCH) should
251 * be grabbed too, so the user is able to interact with all of those while
252 * the grab holds, you should thus use %GDK_SEAT_CAPABILITY_ALL_POINTING most
253 * commonly.
254 *
255 * Grabs are used for operations which need complete control over the
256 * events corresponding to the given capabilities. For example in GTK this
257 * is used for Drag and Drop operations, popup menus and such.
258 *
259 * Note that if the event mask of a `GdkSurface` has selected both button press
260 * and button release events, or touch begin and touch end, then a press event
261 * will cause an automatic grab until the button is released, equivalent to a
262 * grab on the surface with @owner_events set to %TRUE. This is done because
263 * most applications expect to receive paired press and release events.
264 *
265 * If you set up anything at the time you take the grab that needs to be
266 * cleaned up when the grab ends, you should handle the `GdkEventGrabBroken`
267 * events that are emitted when the grab ends unvoluntarily.
268 *
269 * Returns: %GDK_GRAB_SUCCESS if the grab was successful.
270 */
271GdkGrabStatus
272gdk_seat_grab (GdkSeat *seat,
273 GdkSurface *surface,
274 GdkSeatCapabilities capabilities,
275 gboolean owner_events,
276 GdkCursor *cursor,
277 GdkEvent *event,
278 GdkSeatGrabPrepareFunc prepare_func,
279 gpointer prepare_func_data)
280{
281 GdkSeatClass *seat_class;
282
283 g_return_val_if_fail (GDK_IS_SEAT (seat), GDK_GRAB_FAILED);
284 g_return_val_if_fail (GDK_IS_SURFACE (surface), GDK_GRAB_FAILED);
285 g_return_val_if_fail (gdk_surface_get_display (surface) == gdk_seat_get_display (seat), GDK_GRAB_FAILED);
286
287 capabilities &= GDK_SEAT_CAPABILITY_ALL;
288 g_return_val_if_fail (capabilities != GDK_SEAT_CAPABILITY_NONE, GDK_GRAB_FAILED);
289
290 seat_class = GDK_SEAT_GET_CLASS (seat);
291
292 return seat_class->grab (seat, surface, capabilities, owner_events, cursor,
293 event, prepare_func, prepare_func_data);
294}
295
296/*
297 * gdk_seat_ungrab:
298 * @seat: a `GdkSeat`
299 *
300 * Releases a grab.
301 *
302 * See [method@Gdk.Seat.grab] for more information.
303 */
304void
305gdk_seat_ungrab (GdkSeat *seat)
306{
307 GdkSeatClass *seat_class;
308
309 g_return_if_fail (GDK_IS_SEAT (seat));
310
311 seat_class = GDK_SEAT_GET_CLASS (seat);
312 seat_class->ungrab (seat);
313}
314
315/**
316 * gdk_seat_get_devices:
317 * @seat: a `GdkSeat`
318 * @capabilities: capabilities to get devices for
319 *
320 * Returns the devices that match the given capabilities.
321 *
322 * Returns: (transfer container) (element-type GdkDevice): A list
323 * of `GdkDevices`. The list must be freed with g_list_free(),
324 * the elements are owned by GTK and must not be freed.
325 */
326GList *
327gdk_seat_get_devices (GdkSeat *seat,
328 GdkSeatCapabilities capabilities)
329{
330 GdkSeatClass *seat_class;
331
332 g_return_val_if_fail (GDK_IS_SEAT (seat), NULL);
333
334 seat_class = GDK_SEAT_GET_CLASS (seat);
335 return seat_class->get_devices (seat, capabilities);
336}
337
338/**
339 * gdk_seat_get_pointer:
340 * @seat: a `GdkSeat`
341 *
342 * Returns the device that routes pointer events.
343 *
344 * Returns: (transfer none) (nullable): a `GdkDevice` with pointer
345 * capabilities. This object is owned by GTK and must not be freed.
346 */
347GdkDevice *
348gdk_seat_get_pointer (GdkSeat *seat)
349{
350 GdkSeatClass *seat_class;
351
352 g_return_val_if_fail (GDK_IS_SEAT (seat), NULL);
353
354 seat_class = GDK_SEAT_GET_CLASS (seat);
355 return seat_class->get_logical_device (seat, GDK_SEAT_CAPABILITY_POINTER);
356}
357
358/**
359 * gdk_seat_get_keyboard:
360 * @seat: a `GdkSeat`
361 *
362 * Returns the device that routes keyboard events.
363 *
364 * Returns: (transfer none) (nullable): a `GdkDevice` with keyboard
365 * capabilities. This object is owned by GTK and must not be freed.
366 */
367GdkDevice *
368gdk_seat_get_keyboard (GdkSeat *seat)
369{
370 GdkSeatClass *seat_class;
371
372 g_return_val_if_fail (GDK_IS_SEAT (seat), NULL);
373
374 seat_class = GDK_SEAT_GET_CLASS (seat);
375 return seat_class->get_logical_device (seat, GDK_SEAT_CAPABILITY_KEYBOARD);
376}
377
378void
379gdk_seat_device_added (GdkSeat *seat,
380 GdkDevice *device)
381{
382 gdk_device_set_seat (device, seat);
383 g_signal_emit (instance: seat, signal_id: signals[DEVICE_ADDED], detail: 0, device);
384}
385
386void
387gdk_seat_device_removed (GdkSeat *seat,
388 GdkDevice *device)
389{
390 gdk_device_set_seat (device, NULL);
391 g_signal_emit (instance: seat, signal_id: signals[DEVICE_REMOVED], detail: 0, device);
392}
393
394/**
395 * gdk_seat_get_display: (attributes org.gtk.Method.get_property=display)
396 * @seat: a `GdkSeat`
397 *
398 * Returns the `GdkDisplay` this seat belongs to.
399 *
400 * Returns: (transfer none): a `GdkDisplay`. This object
401 * is owned by GTK and must not be freed.
402 */
403GdkDisplay *
404gdk_seat_get_display (GdkSeat *seat)
405{
406 GdkSeatPrivate *priv = gdk_seat_get_instance_private (self: seat);
407
408 g_return_val_if_fail (GDK_IS_SEAT (seat), NULL);
409
410 return priv->display;
411}
412
413void
414gdk_seat_tool_added (GdkSeat *seat,
415 GdkDeviceTool *tool)
416{
417 g_signal_emit (instance: seat, signal_id: signals[TOOL_ADDED], detail: 0, tool);
418}
419
420void
421gdk_seat_tool_removed (GdkSeat *seat,
422 GdkDeviceTool *tool)
423{
424 g_signal_emit (instance: seat, signal_id: signals[TOOL_REMOVED], detail: 0, tool);
425}
426
427GdkDeviceTool *
428gdk_seat_get_tool (GdkSeat *seat,
429 guint64 serial,
430 guint64 hw_id,
431 GdkDeviceToolType type)
432{
433 GdkDeviceTool *match = NULL;
434 GList *tools, *l;
435
436 tools = gdk_seat_get_tools (seat);
437
438 for (l = tools; l; l = l->next)
439 {
440 GdkDeviceTool *tool = l->data;
441
442 if (tool->serial == serial && tool->hw_id == hw_id && tool->type == type)
443 {
444 match = tool;
445 break;
446 }
447 }
448
449 g_list_free (list: tools);
450
451 return match;
452}
453
454/**
455 * gdk_seat_get_tools:
456 * @seat: a `GdkSeat`
457 *
458 * Returns all `GdkDeviceTools` that are known to the application.
459 *
460 * Returns: (transfer container) (element-type Gdk.DeviceTool):
461 * A list of tools. Free with g_list_free().
462 */
463GList *
464gdk_seat_get_tools (GdkSeat *seat)
465{
466 GdkSeatClass *seat_class;
467
468 g_return_val_if_fail (GDK_IS_SEAT (seat), NULL);
469
470 seat_class = GDK_SEAT_GET_CLASS (seat);
471 return seat_class->get_tools (seat);
472}
473

source code of gtk/gdk/gdkseat.c