1/* GDK - The GIMP Drawing Kit
2 * gdkdisplay.c
3 *
4 * Copyright 2001 Sun Microsystems Inc.
5 *
6 * Erwann Chenede <erwann.chenede@sun.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "config.h"
23
24#include "gdkdisplay.h"
25#include "gdkdisplayprivate.h"
26
27#include "gdkintl.h"
28#include "gdk-private.h"
29
30#include "gdkapplaunchcontext.h"
31#include "gdkclipboardprivate.h"
32#include "gdkdeviceprivate.h"
33#include "gdkdisplaymanagerprivate.h"
34#include "gdkframeclockidleprivate.h"
35#include "gdkeventsprivate.h"
36#include "gdkglcontextprivate.h"
37#include "gdkmonitorprivate.h"
38
39#ifdef HAVE_EGL
40#include <epoxy/egl.h>
41#endif
42#include <math.h>
43#include <stdlib.h>
44
45/**
46 * GdkDisplay:
47 *
48 * `GdkDisplay` objects are the GDK representation of a workstation.
49 *
50 * Their purpose are two-fold:
51 *
52 * - To manage and provide information about input devices (pointers, keyboards, etc)
53 * - To manage and provide information about output devices (monitors, projectors, etc)
54 *
55 * Most of the input device handling has been factored out into separate
56 * [class@Gdk.Seat] objects. Every display has a one or more seats, which
57 * can be accessed with [method@Gdk.Display.get_default_seat] and
58 * [method@Gdk.Display.list_seats].
59 *
60 * Output devices are represented by [class@Gdk.Monitor] objects, which can
61 * be accessed with [method@Gdk.Display.get_monitor_at_surface] and similar APIs.
62 */
63
64enum
65{
66 PROP_0,
67 PROP_COMPOSITED,
68 PROP_RGBA,
69 PROP_INPUT_SHAPES,
70 LAST_PROP
71};
72
73static GParamSpec *props[LAST_PROP] = { NULL, };
74
75enum {
76 OPENED,
77 CLOSED,
78 SEAT_ADDED,
79 SEAT_REMOVED,
80 SETTING_CHANGED,
81 LAST_SIGNAL
82};
83
84typedef struct _GdkDisplayPrivate GdkDisplayPrivate;
85
86struct _GdkDisplayPrivate {
87 /* The base context that all other contexts inherit from.
88 * This context is never exposed to public API and is
89 * allowed to have a %NULL surface.
90 */
91 GdkGLContext *gl_context;
92 GError *gl_error;
93
94#ifdef HAVE_EGL
95 EGLDisplay egl_display;
96 EGLConfig egl_config;
97 EGLConfig egl_config_high_depth;
98#endif
99
100 guint rgba : 1;
101 guint composited : 1;
102 guint input_shapes : 1;
103
104 GdkDebugFlags debug_flags;
105};
106
107static void gdk_display_dispose (GObject *object);
108static void gdk_display_finalize (GObject *object);
109
110
111static GdkAppLaunchContext *gdk_display_real_get_app_launch_context (GdkDisplay *display);
112
113static guint signals[LAST_SIGNAL] = { 0 };
114
115G_DEFINE_TYPE_WITH_PRIVATE (GdkDisplay, gdk_display, G_TYPE_OBJECT)
116
117static void
118gdk_display_get_property (GObject *object,
119 guint prop_id,
120 GValue *value,
121 GParamSpec *pspec)
122{
123 GdkDisplay *display = GDK_DISPLAY (object);
124
125 switch (prop_id)
126 {
127 case PROP_COMPOSITED:
128 g_value_set_boolean (value, v_boolean: gdk_display_is_composited (display));
129 break;
130
131 case PROP_RGBA:
132 g_value_set_boolean (value, v_boolean: gdk_display_is_rgba (display));
133 break;
134
135 case PROP_INPUT_SHAPES:
136 g_value_set_boolean (value, v_boolean: gdk_display_supports_input_shapes (display));
137 break;
138
139 default:
140 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
141 }
142}
143
144static void
145gdk_display_real_make_default (GdkDisplay *display)
146{
147}
148
149static GdkGLContext *
150gdk_display_default_init_gl (GdkDisplay *display,
151 GError **error)
152{
153 g_set_error_literal (err: error, GDK_GL_ERROR, code: GDK_GL_ERROR_NOT_AVAILABLE,
154 _("The current backend does not support OpenGL"));
155
156 return NULL;
157}
158
159static guint
160gdk_display_default_rate_egl_config (GdkDisplay *display,
161 gpointer egl_display,
162 gpointer config)
163{
164 guint distance = 0;
165#ifdef HAVE_EGL
166 int tmp;
167
168 if (!eglGetConfigAttrib (egl_display, config, EGL_SAMPLE_BUFFERS, &tmp) || tmp != 0)
169 distance += 0x20000;
170
171 if (!eglGetConfigAttrib (egl_display, config, EGL_DEPTH_SIZE, &tmp) || tmp != 0 ||
172 !eglGetConfigAttrib (egl_display, config, EGL_STENCIL_SIZE, &tmp) || tmp != 0)
173 distance += 0x10000;
174#endif
175
176 return distance;
177}
178
179static GdkSeat *
180gdk_display_real_get_default_seat (GdkDisplay *display)
181{
182 if (!display->seats)
183 return NULL;
184
185 return display->seats->data;
186}
187
188static void
189gdk_display_real_opened (GdkDisplay *display)
190{
191 _gdk_display_manager_add_display (manager: gdk_display_manager_get (), display);
192}
193
194static void
195gdk_display_class_init (GdkDisplayClass *class)
196{
197 GObjectClass *object_class = G_OBJECT_CLASS (class);
198
199 object_class->finalize = gdk_display_finalize;
200 object_class->dispose = gdk_display_dispose;
201 object_class->get_property = gdk_display_get_property;
202
203 class->make_default = gdk_display_real_make_default;
204 class->get_app_launch_context = gdk_display_real_get_app_launch_context;
205 class->init_gl = gdk_display_default_init_gl;
206 class->rate_egl_config = gdk_display_default_rate_egl_config;
207 class->get_default_seat = gdk_display_real_get_default_seat;
208 class->opened = gdk_display_real_opened;
209
210 /**
211 * GdkDisplay:composited: (attributes org.gtk.Property.get=gdk_display_is_composited)
212 *
213 * %TRUE if the display properly composites the alpha channel.
214 */
215 props[PROP_COMPOSITED] =
216 g_param_spec_boolean (name: "composited",
217 P_("Composited"),
218 P_("Composited"),
219 TRUE,
220 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
221
222 /**
223 * GdkDisplay:rgba: (attributes org.gtk.Property.get=gdk_display_is_rgba)
224 *
225 * %TRUE if the display supports an alpha channel.
226 */
227 props[PROP_RGBA] =
228 g_param_spec_boolean (name: "rgba",
229 P_("RGBA"),
230 P_("RGBA"),
231 TRUE,
232 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
233
234 /**
235 * GdkDisplay:input-shapes: (attributes org.gtk.Property.get=gdk_display_supports_input_shapes)
236 *
237 * %TRUE if the display supports input shapes.
238 */
239 props[PROP_INPUT_SHAPES] =
240 g_param_spec_boolean (name: "input-shapes",
241 P_("Input shapes"),
242 P_("Input shapes"),
243 TRUE,
244 flags: G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
245
246 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: props);
247
248 /**
249 * GdkDisplay::opened:
250 * @display: the object on which the signal is emitted
251 *
252 * Emitted when the connection to the windowing system for @display is opened.
253 */
254 signals[OPENED] =
255 g_signal_new (signal_name: g_intern_static_string (string: "opened"),
256 G_OBJECT_CLASS_TYPE (object_class),
257 signal_flags: G_SIGNAL_RUN_LAST,
258 G_STRUCT_OFFSET (GdkDisplayClass, opened),
259 NULL, NULL,
260 NULL,
261 G_TYPE_NONE, n_params: 0);
262
263 /**
264 * GdkDisplay::closed:
265 * @display: the object on which the signal is emitted
266 * @is_error: %TRUE if the display was closed due to an error
267 *
268 * Emitted when the connection to the windowing system for @display is closed.
269 */
270 signals[CLOSED] =
271 g_signal_new (signal_name: g_intern_static_string (string: "closed"),
272 G_OBJECT_CLASS_TYPE (object_class),
273 signal_flags: G_SIGNAL_RUN_LAST,
274 G_STRUCT_OFFSET (GdkDisplayClass, closed),
275 NULL, NULL,
276 NULL,
277 G_TYPE_NONE,
278 n_params: 1,
279 G_TYPE_BOOLEAN);
280
281 /**
282 * GdkDisplay::seat-added:
283 * @display: the object on which the signal is emitted
284 * @seat: the seat that was just added
285 *
286 * Emitted whenever a new seat is made known to the windowing system.
287 */
288 signals[SEAT_ADDED] =
289 g_signal_new (signal_name: g_intern_static_string (string: "seat-added"),
290 G_OBJECT_CLASS_TYPE (object_class),
291 signal_flags: G_SIGNAL_RUN_LAST,
292 class_offset: 0, NULL, NULL,
293 NULL,
294 G_TYPE_NONE, n_params: 1, GDK_TYPE_SEAT);
295
296 /**
297 * GdkDisplay::seat-removed:
298 * @display: the object on which the signal is emitted
299 * @seat: the seat that was just removed
300 *
301 * Emitted whenever a seat is removed by the windowing system.
302 */
303 signals[SEAT_REMOVED] =
304 g_signal_new (signal_name: g_intern_static_string (string: "seat-removed"),
305 G_OBJECT_CLASS_TYPE (object_class),
306 signal_flags: G_SIGNAL_RUN_LAST,
307 class_offset: 0, NULL, NULL,
308 NULL,
309 G_TYPE_NONE, n_params: 1, GDK_TYPE_SEAT);
310
311 /**
312 * GdkDisplay::setting-changed:
313 * @display: the object on which the signal is emitted
314 * @setting: the name of the setting that changed
315 *
316 * Emitted whenever a setting changes its value.
317 */
318 signals[SETTING_CHANGED] =
319 g_signal_new (signal_name: g_intern_static_string (string: "setting-changed"),
320 G_OBJECT_CLASS_TYPE (object_class),
321 signal_flags: G_SIGNAL_RUN_LAST,
322 class_offset: 0, NULL, NULL,
323 NULL,
324 G_TYPE_NONE, n_params: 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
325}
326
327static void
328free_pointer_info (GdkPointerSurfaceInfo *info)
329{
330 g_clear_object (&info->surface_under_pointer);
331 g_slice_free (GdkPointerSurfaceInfo, info);
332}
333
334static void
335free_device_grab (GdkDeviceGrabInfo *info)
336{
337 g_object_unref (object: info->surface);
338 g_free (mem: info);
339}
340
341static gboolean
342free_device_grabs_foreach (gpointer key,
343 gpointer value,
344 gpointer user_data)
345{
346 GList *list = value;
347
348 g_list_free_full (list, free_func: (GDestroyNotify) free_device_grab);
349
350 return TRUE;
351}
352
353static void
354gdk_display_init (GdkDisplay *display)
355{
356 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
357
358 display->double_click_time = 250;
359 display->double_click_distance = 5;
360
361 display->device_grabs = g_hash_table_new (NULL, NULL);
362
363 display->pointers_info = g_hash_table_new_full (NULL, NULL, NULL,
364 value_destroy_func: (GDestroyNotify) free_pointer_info);
365
366 g_queue_init (queue: &display->queued_events);
367
368 priv->debug_flags = _gdk_debug_flags;
369
370 priv->composited = TRUE;
371 priv->rgba = TRUE;
372 priv->input_shapes = TRUE;
373}
374
375static void
376gdk_display_dispose (GObject *object)
377{
378 GdkDisplay *display = GDK_DISPLAY (object);
379 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
380
381 _gdk_display_manager_remove_display (manager: gdk_display_manager_get (), display);
382
383 g_queue_clear (queue: &display->queued_events);
384
385 g_clear_object (&priv->gl_context);
386#ifdef HAVE_EGL
387 g_clear_pointer (&priv->egl_display, eglTerminate);
388#endif
389 g_clear_error (err: &priv->gl_error);
390
391 G_OBJECT_CLASS (gdk_display_parent_class)->dispose (object);
392}
393
394static void
395gdk_display_finalize (GObject *object)
396{
397 GdkDisplay *display = GDK_DISPLAY (object);
398
399 g_hash_table_foreach_remove (hash_table: display->device_grabs,
400 func: free_device_grabs_foreach,
401 NULL);
402 g_hash_table_destroy (hash_table: display->device_grabs);
403
404 g_hash_table_destroy (hash_table: display->pointers_info);
405
406 g_list_free_full (list: display->seats, free_func: g_object_unref);
407
408 G_OBJECT_CLASS (gdk_display_parent_class)->finalize (object);
409}
410
411/**
412 * gdk_display_close:
413 * @display: a `GdkDisplay`
414 *
415 * Closes the connection to the windowing system for the given display.
416 *
417 * This cleans up associated resources.
418 */
419void
420gdk_display_close (GdkDisplay *display)
421{
422 g_return_if_fail (GDK_IS_DISPLAY (display));
423
424 if (!display->closed)
425 {
426 display->closed = TRUE;
427
428 g_signal_emit (instance: display, signal_id: signals[CLOSED], detail: 0, FALSE);
429 g_object_run_dispose (G_OBJECT (display));
430
431 g_object_unref (object: display);
432 }
433}
434
435/**
436 * gdk_display_is_closed:
437 * @display: a `GdkDisplay`
438 *
439 * Finds out if the display has been closed.
440 *
441 * Returns: %TRUE if the display is closed.
442 */
443gboolean
444gdk_display_is_closed (GdkDisplay *display)
445{
446 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
447
448 return display->closed;
449}
450
451/*<private>
452 * gdk_display_get_event:
453 * @display: a `GdkDisplay`
454 *
455 * Gets the next `GdkEvent` to be processed for @display,
456 * fetching events from the windowing system if necessary.
457 *
458 * Returns: (nullable) (transfer full): the next `GdkEvent`
459 * to be processed
460 */
461GdkEvent *
462gdk_display_get_event (GdkDisplay *display)
463{
464 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
465
466 if (display->event_pause_count == 0)
467 GDK_DISPLAY_GET_CLASS (display)->queue_events (display);
468
469 return _gdk_event_unqueue (display);
470}
471
472/**
473 * gdk_display_put_event:
474 * @display: a `GdkDisplay`
475 * @event: (transfer none): a `GdkEvent`
476 *
477 * Appends the given event onto the front of the event
478 * queue for @display.
479 *
480 * This function is only useful in very special situations
481 * and should not be used by applications.
482 **/
483void
484gdk_display_put_event (GdkDisplay *display,
485 GdkEvent *event)
486{
487 g_return_if_fail (GDK_IS_DISPLAY (display));
488 g_return_if_fail (event != NULL);
489
490 _gdk_event_queue_append (display, event: gdk_event_ref (event: (GdkEvent *)event));
491}
492
493static void
494generate_grab_broken_event (GdkDisplay *display,
495 GdkSurface *surface,
496 GdkDevice *device,
497 gboolean implicit,
498 GdkSurface *grab_surface)
499{
500 g_return_if_fail (surface != NULL);
501
502 if (!GDK_SURFACE_DESTROYED (surface))
503 {
504 GdkEvent *event;
505
506 event = gdk_grab_broken_event_new (surface,
507 device,
508 grab_surface,
509 implicit);
510
511 _gdk_event_queue_append (display, event);
512 }
513}
514
515GdkDeviceGrabInfo *
516_gdk_display_get_last_device_grab (GdkDisplay *display,
517 GdkDevice *device)
518{
519 GList *l;
520
521 l = g_hash_table_lookup (hash_table: display->device_grabs, key: device);
522
523 if (l)
524 {
525 l = g_list_last (list: l);
526 return l->data;
527 }
528
529 return NULL;
530}
531
532GdkDeviceGrabInfo *
533_gdk_display_add_device_grab (GdkDisplay *display,
534 GdkDevice *device,
535 GdkSurface *surface,
536 gboolean owner_events,
537 GdkEventMask event_mask,
538 unsigned long serial_start,
539 guint32 time,
540 gboolean implicit)
541{
542 GdkDeviceGrabInfo *info, *other_info;
543 GList *grabs, *l;
544
545 info = g_new0 (GdkDeviceGrabInfo, 1);
546
547 info->surface = g_object_ref (surface);
548 info->serial_start = serial_start;
549 info->serial_end = G_MAXULONG;
550 info->owner_events = owner_events;
551 info->event_mask = event_mask;
552 info->time = time;
553 info->implicit = implicit;
554
555 grabs = g_hash_table_lookup (hash_table: display->device_grabs, key: device);
556
557 /* Find the first grab that has a larger start time (if any) and insert
558 * before that. I.E we insert after already existing grabs with same
559 * start time */
560 for (l = grabs; l != NULL; l = l->next)
561 {
562 other_info = l->data;
563
564 if (info->serial_start < other_info->serial_start)
565 break;
566 }
567
568 grabs = g_list_insert_before (list: grabs, sibling: l, data: info);
569
570 /* Make sure the new grab end before next grab */
571 if (l)
572 {
573 other_info = l->data;
574 info->serial_end = other_info->serial_start;
575 }
576
577 /* Find any previous grab and update its end time */
578 l = g_list_find (list: grabs, data: info);
579 l = l->prev;
580 if (l)
581 {
582 other_info = l->data;
583 other_info->serial_end = serial_start;
584 }
585
586 g_hash_table_insert (hash_table: display->device_grabs, key: device, value: grabs);
587
588 return info;
589}
590
591static GdkSurface *
592get_current_toplevel (GdkDisplay *display,
593 GdkDevice *device,
594 int *x_out,
595 int *y_out,
596 GdkModifierType *state_out)
597{
598 GdkSurface *pointer_surface;
599 double x, y;
600 GdkModifierType state;
601
602 pointer_surface = _gdk_device_surface_at_position (device, win_x: &x, win_y: &y, mask: &state);
603
604 if (pointer_surface != NULL &&
605 GDK_SURFACE_DESTROYED (pointer_surface))
606 pointer_surface = NULL;
607
608 *x_out = round (x: x);
609 *y_out = round (x: y);
610 *state_out = state;
611
612 return pointer_surface;
613}
614
615static void
616switch_to_pointer_grab (GdkDisplay *display,
617 GdkDevice *device,
618 GdkDeviceGrabInfo *grab,
619 GdkDeviceGrabInfo *last_grab,
620 guint32 time,
621 gulong serial)
622{
623 GdkSurface *new_toplevel;
624 GdkPointerSurfaceInfo *info;
625 GList *old_grabs;
626 GdkModifierType state;
627 int x = 0, y = 0;
628
629 /* Temporarily unset pointer to make sure we send the crossing events below */
630 old_grabs = g_hash_table_lookup (hash_table: display->device_grabs, key: device);
631 g_hash_table_steal (hash_table: display->device_grabs, key: device);
632 info = _gdk_display_get_pointer_info (display, device);
633
634 if (grab)
635 {
636 /* New grab is in effect */
637 if (!grab->implicit)
638 {
639 /* !owner_event Grabbing a surface that we're not inside, current status is
640 now NULL (i.e. outside grabbed surface) */
641 if (!grab->owner_events && info->surface_under_pointer != grab->surface)
642 _gdk_display_set_surface_under_pointer (display, device, NULL);
643 }
644
645 grab->activated = TRUE;
646 }
647
648 if (last_grab)
649 {
650 new_toplevel = NULL;
651
652 if (grab == NULL /* ungrab */ ||
653 (!last_grab->owner_events && grab->owner_events) /* switched to owner_events */ )
654 {
655 new_toplevel = get_current_toplevel (display, device, x_out: &x, y_out: &y, state_out: &state);
656
657 if (new_toplevel)
658 {
659 /* w is now toplevel and x,y in toplevel coords */
660 _gdk_display_set_surface_under_pointer (display, device, surface: new_toplevel);
661 info->toplevel_x = x;
662 info->toplevel_y = y;
663 info->state = state;
664 }
665 }
666
667 if (grab == NULL) /* Ungrabbed, send events */
668 {
669 /* We're now ungrabbed, update the surface_under_pointer */
670 _gdk_display_set_surface_under_pointer (display, device, surface: new_toplevel);
671 }
672 }
673
674 g_hash_table_insert (hash_table: display->device_grabs, key: device, value: old_grabs);
675}
676
677void
678_gdk_display_update_last_event (GdkDisplay *display,
679 GdkEvent *event)
680{
681 if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
682 display->last_event_time = gdk_event_get_time (event);
683}
684
685void
686_gdk_display_device_grab_update (GdkDisplay *display,
687 GdkDevice *device,
688 gulong current_serial)
689{
690 GdkDeviceGrabInfo *current_grab, *next_grab;
691 GList *grabs;
692 guint32 time;
693
694 time = display->last_event_time;
695 grabs = g_hash_table_lookup (hash_table: display->device_grabs, key: device);
696
697 while (grabs != NULL)
698 {
699 current_grab = grabs->data;
700
701 if (current_grab->serial_start > current_serial)
702 return; /* Hasn't started yet */
703
704 if (current_grab->serial_end > current_serial)
705 {
706 /* This one hasn't ended yet.
707 its the currently active one or scheduled to be active */
708
709 if (!current_grab->activated)
710 {
711 if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
712 switch_to_pointer_grab (display, device, grab: current_grab, NULL, time, serial: current_serial);
713 }
714
715 break;
716 }
717
718 next_grab = NULL;
719 if (grabs->next)
720 {
721 /* This is the next active grab */
722 next_grab = grabs->next->data;
723
724 if (next_grab->serial_start > current_serial)
725 next_grab = NULL; /* Actually its not yet active */
726 }
727
728 if ((next_grab == NULL && current_grab->implicit_ungrab) ||
729 (next_grab != NULL && current_grab->surface != next_grab->surface))
730 generate_grab_broken_event (display, GDK_SURFACE (current_grab->surface),
731 device,
732 implicit: current_grab->implicit,
733 grab_surface: next_grab? next_grab->surface : NULL);
734
735 /* Remove old grab */
736 grabs = g_list_delete_link (list: grabs, link_: grabs);
737 g_hash_table_insert (hash_table: display->device_grabs, key: device, value: grabs);
738
739 if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
740 switch_to_pointer_grab (display, device,
741 grab: next_grab, last_grab: current_grab,
742 time, serial: current_serial);
743
744 free_device_grab (info: current_grab);
745 }
746}
747
748static GList *
749grab_list_find (GList *grabs,
750 gulong serial)
751{
752 GdkDeviceGrabInfo *grab;
753
754 while (grabs)
755 {
756 grab = grabs->data;
757
758 if (serial >= grab->serial_start && serial < grab->serial_end)
759 return grabs;
760
761 grabs = grabs->next;
762 }
763
764 return NULL;
765}
766
767static GList *
768find_device_grab (GdkDisplay *display,
769 GdkDevice *device,
770 gulong serial)
771{
772 GList *l;
773
774 l = g_hash_table_lookup (hash_table: display->device_grabs, key: device);
775 return grab_list_find (grabs: l, serial);
776}
777
778GdkDeviceGrabInfo *
779_gdk_display_has_device_grab (GdkDisplay *display,
780 GdkDevice *device,
781 gulong serial)
782{
783 GList *l;
784
785 l = find_device_grab (display, device, serial);
786 if (l)
787 return l->data;
788
789 return NULL;
790}
791
792/* Returns true if last grab was ended
793 * If if_child is non-NULL, end the grab only if the grabbed
794 * surface is the same as if_child or a descendant of it */
795gboolean
796_gdk_display_end_device_grab (GdkDisplay *display,
797 GdkDevice *device,
798 gulong serial,
799 GdkSurface *if_child,
800 gboolean implicit)
801{
802 GdkDeviceGrabInfo *grab;
803 GList *l;
804
805 l = find_device_grab (display, device, serial);
806
807 if (l == NULL)
808 return FALSE;
809
810 grab = l->data;
811 if (grab && (if_child == NULL || if_child == grab->surface))
812 {
813 grab->serial_end = serial;
814 grab->implicit_ungrab = implicit;
815 return l->next == NULL;
816 }
817
818 return FALSE;
819}
820
821GdkPointerSurfaceInfo *
822_gdk_display_get_pointer_info (GdkDisplay *display,
823 GdkDevice *device)
824{
825 GdkPointerSurfaceInfo *info;
826 GdkSeat *seat;
827
828 if (device)
829 {
830 seat = gdk_device_get_seat (device);
831
832 if (device == gdk_seat_get_keyboard (seat))
833 device = gdk_seat_get_pointer (seat);
834 }
835
836 if (G_UNLIKELY (!device))
837 return NULL;
838
839 info = g_hash_table_lookup (hash_table: display->pointers_info, key: device);
840
841 if (G_UNLIKELY (!info))
842 {
843 info = g_slice_new0 (GdkPointerSurfaceInfo);
844 g_hash_table_insert (hash_table: display->pointers_info, key: device, value: info);
845 }
846
847 return info;
848}
849
850void
851_gdk_display_pointer_info_foreach (GdkDisplay *display,
852 GdkDisplayPointerInfoForeach func,
853 gpointer user_data)
854{
855 GHashTableIter iter;
856 gpointer key, value;
857
858 g_hash_table_iter_init (iter: &iter, hash_table: display->pointers_info);
859
860 while (g_hash_table_iter_next (iter: &iter, key: &key, value: &value))
861 {
862 GdkPointerSurfaceInfo *info = value;
863 GdkDevice *device = key;
864
865 (func) (display, device, info, user_data);
866 }
867}
868
869/*< private >
870 * gdk_device_grab_info:
871 * @display: the display for which to get the grab information
872 * @device: device to get the grab information from
873 * @grab_surface: (out) (transfer none): location to store current grab surface
874 * @owner_events: (out): location to store boolean indicating whether
875 * the @owner_events flag to gdk_device_grab() was %TRUE.
876 *
877 * Determines information about the current keyboard grab.
878 * This is not public API and must not be used by applications.
879 *
880 * Returns: %TRUE if this application currently has the
881 * keyboard grabbed.
882 */
883gboolean
884gdk_device_grab_info (GdkDisplay *display,
885 GdkDevice *device,
886 GdkSurface **grab_surface,
887 gboolean *owner_events)
888{
889 GdkDeviceGrabInfo *info;
890
891 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
892 g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
893
894 info = _gdk_display_get_last_device_grab (display, device);
895
896 if (info)
897 {
898 if (grab_surface)
899 *grab_surface = info->surface;
900 if (owner_events)
901 *owner_events = info->owner_events;
902
903 return TRUE;
904 }
905 else
906 return FALSE;
907}
908
909/**
910 * gdk_display_device_is_grabbed:
911 * @display: a `GdkDisplay`
912 * @device: a `GdkDevice`
913 *
914 * Returns %TRUE if there is an ongoing grab on @device for @display.
915 *
916 * Returns: %TRUE if there is a grab in effect for @device.
917 */
918gboolean
919gdk_display_device_is_grabbed (GdkDisplay *display,
920 GdkDevice *device)
921{
922 GdkDeviceGrabInfo *info;
923
924 g_return_val_if_fail (GDK_IS_DISPLAY (display), TRUE);
925 g_return_val_if_fail (GDK_IS_DEVICE (device), TRUE);
926
927 /* What we're interested in is the steady state (ie last grab),
928 because we're interested e.g. if we grabbed so that we
929 can ungrab, even if our grab is not active just yet. */
930 info = _gdk_display_get_last_device_grab (display, device);
931
932 return (info && !info->implicit);
933}
934
935/**
936 * gdk_display_get_name:
937 * @display: a `GdkDisplay`
938 *
939 * Gets the name of the display.
940 *
941 * Returns: a string representing the display name. This string is owned
942 * by GDK and should not be modified or freed.
943 */
944const char *
945gdk_display_get_name (GdkDisplay *display)
946{
947 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
948
949 return GDK_DISPLAY_GET_CLASS (display)->get_name (display);
950}
951
952/**
953 * gdk_display_beep:
954 * @display: a `GdkDisplay`
955 *
956 * Emits a short beep on @display
957 */
958void
959gdk_display_beep (GdkDisplay *display)
960{
961 g_return_if_fail (GDK_IS_DISPLAY (display));
962
963 GDK_DISPLAY_GET_CLASS (display)->beep (display);
964}
965
966/**
967 * gdk_display_sync:
968 * @display: a `GdkDisplay`
969 *
970 * Flushes any requests queued for the windowing system and waits until all
971 * requests have been handled.
972 *
973 * This is often used for making sure that the display is synchronized
974 * with the current state of the program. Calling [method@Gdk.Display.sync]
975 * before [method@GdkX11.Display.error_trap_pop] makes sure that any errors
976 * generated from earlier requests are handled before the error trap is removed.
977 *
978 * This is most useful for X11. On windowing systems where requests are
979 * handled synchronously, this function will do nothing.
980 */
981void
982gdk_display_sync (GdkDisplay *display)
983{
984 g_return_if_fail (GDK_IS_DISPLAY (display));
985
986 GDK_DISPLAY_GET_CLASS (display)->sync (display);
987}
988
989/**
990 * gdk_display_flush:
991 * @display: a `GdkDisplay`
992 *
993 * Flushes any requests queued for the windowing system.
994 *
995 * This happens automatically when the main loop blocks waiting for new events,
996 * but if your application is drawing without returning control to the main loop,
997 * you may need to call this function explicitly. A common case where this function
998 * needs to be called is when an application is executing drawing commands
999 * from a thread other than the thread where the main loop is running.
1000 *
1001 * This is most useful for X11. On windowing systems where requests are
1002 * handled synchronously, this function will do nothing.
1003 */
1004void
1005gdk_display_flush (GdkDisplay *display)
1006{
1007 g_return_if_fail (GDK_IS_DISPLAY (display));
1008
1009 GDK_DISPLAY_GET_CLASS (display)->flush (display);
1010}
1011
1012/**
1013 * gdk_display_get_clipboard:
1014 * @display: a `GdkDisplay`
1015 *
1016 * Gets the clipboard used for copy/paste operations.
1017 *
1018 * Returns: (transfer none): the display's clipboard
1019 */
1020GdkClipboard *
1021gdk_display_get_clipboard (GdkDisplay *display)
1022{
1023 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1024
1025 if (display->clipboard == NULL)
1026 display->clipboard = gdk_clipboard_new (display);
1027
1028 return display->clipboard;
1029}
1030
1031/**
1032 * gdk_display_get_primary_clipboard:
1033 * @display: a `GdkDisplay`
1034 *
1035 * Gets the clipboard used for the primary selection.
1036 *
1037 * On backends where the primary clipboard is not supported natively,
1038 * GDK emulates this clipboard locally.
1039 *
1040 * Returns: (transfer none): the primary clipboard
1041 */
1042GdkClipboard *
1043gdk_display_get_primary_clipboard (GdkDisplay *display)
1044{
1045 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1046
1047 if (display->primary_clipboard == NULL)
1048 display->primary_clipboard = gdk_clipboard_new (display);
1049
1050 return display->primary_clipboard;
1051}
1052
1053/**
1054 * gdk_display_supports_input_shapes: (attributes org.gtk.Method.get_property=input-shapes)
1055 * @display: a `GdkDisplay`
1056 *
1057 * Returns %TRUE if the display supports input shapes.
1058 *
1059 * This means that [method@Gdk.Surface.set_input_region] can
1060 * be used to modify the input shape of surfaces on @display.
1061 *
1062 * On modern displays, this value is always %TRUE.
1063 *
1064 * Returns: %TRUE if surfaces with modified input shape are supported
1065 */
1066gboolean
1067gdk_display_supports_input_shapes (GdkDisplay *display)
1068{
1069 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
1070
1071 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
1072
1073 return priv->input_shapes;
1074}
1075
1076void
1077gdk_display_set_input_shapes (GdkDisplay *display,
1078 gboolean input_shapes)
1079{
1080 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
1081
1082 g_return_if_fail (GDK_IS_DISPLAY (display));
1083
1084 if (priv->input_shapes == input_shapes)
1085 return;
1086
1087 priv->input_shapes = input_shapes;
1088
1089 g_object_notify_by_pspec (G_OBJECT (display), pspec: props[PROP_INPUT_SHAPES]);
1090}
1091
1092static GdkAppLaunchContext *
1093gdk_display_real_get_app_launch_context (GdkDisplay *display)
1094{
1095 GdkAppLaunchContext *ctx;
1096
1097 ctx = g_object_new (GDK_TYPE_APP_LAUNCH_CONTEXT,
1098 first_property_name: "display", display,
1099 NULL);
1100
1101 return ctx;
1102}
1103
1104/**
1105 * gdk_display_get_app_launch_context:
1106 * @display: a `GdkDisplay`
1107 *
1108 * Returns a `GdkAppLaunchContext` suitable for launching
1109 * applications on the given display.
1110 *
1111 * Returns: (transfer full): a new `GdkAppLaunchContext` for @display
1112 */
1113GdkAppLaunchContext *
1114gdk_display_get_app_launch_context (GdkDisplay *display)
1115{
1116 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1117
1118 return GDK_DISPLAY_GET_CLASS (display)->get_app_launch_context (display);
1119}
1120
1121/**
1122 * gdk_display_open:
1123 * @display_name: the name of the display to open
1124 *
1125 * Opens a display.
1126 *
1127 * If opening the display fails, `NULL` is returned.
1128 *
1129 * Returns: (nullable) (transfer none): a `GdkDisplay`
1130 */
1131GdkDisplay *
1132gdk_display_open (const char *display_name)
1133{
1134 return gdk_display_manager_open_display (manager: gdk_display_manager_get (),
1135 name: display_name);
1136}
1137
1138gulong
1139_gdk_display_get_next_serial (GdkDisplay *display)
1140{
1141 return GDK_DISPLAY_GET_CLASS (display)->get_next_serial (display);
1142}
1143
1144/**
1145 * gdk_display_notify_startup_complete:
1146 * @display: a `GdkDisplay`
1147 * @startup_id: a startup-notification identifier, for which
1148 * notification process should be completed
1149 *
1150 * Indicates to the GUI environment that the application has
1151 * finished loading, using a given identifier.
1152 *
1153 * GTK will call this function automatically for [class@Gtk.Window]
1154 * with custom startup-notification identifier unless
1155 * [method@Gtk.Window.set_auto_startup_notification]
1156 * is called to disable that feature.
1157 */
1158void
1159gdk_display_notify_startup_complete (GdkDisplay *display,
1160 const char *startup_id)
1161{
1162 g_return_if_fail (GDK_IS_DISPLAY (display));
1163
1164 GDK_DISPLAY_GET_CLASS (display)->notify_startup_complete (display, startup_id);
1165}
1166
1167/**
1168 * gdk_display_get_startup_notification_id:
1169 * @display: a `GdkDisplay`
1170 *
1171 * Gets the startup notification ID for a Wayland display, or %NULL
1172 * if no ID has been defined.
1173 *
1174 * Returns: (nullable): the startup notification ID for @display
1175 */
1176const char *
1177gdk_display_get_startup_notification_id (GdkDisplay *display)
1178{
1179 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1180
1181 if (GDK_DISPLAY_GET_CLASS (display)->get_startup_notification_id == NULL)
1182 return NULL;
1183
1184 return GDK_DISPLAY_GET_CLASS (display)->get_startup_notification_id (display);
1185}
1186
1187void
1188_gdk_display_pause_events (GdkDisplay *display)
1189{
1190 display->event_pause_count++;
1191}
1192
1193void
1194_gdk_display_unpause_events (GdkDisplay *display)
1195{
1196 g_return_if_fail (display->event_pause_count > 0);
1197
1198 display->event_pause_count--;
1199}
1200
1201GdkSurface *
1202gdk_display_create_surface (GdkDisplay *display,
1203 GdkSurfaceType surface_type,
1204 GdkSurface *parent,
1205 int x,
1206 int y,
1207 int width,
1208 int height)
1209{
1210 return GDK_DISPLAY_GET_CLASS (display)->create_surface (display,
1211 surface_type,
1212 parent,
1213 x, y, width, height);
1214}
1215
1216/*< private >
1217 * gdk_display_get_keymap:
1218 * @display: the `GdkDisplay`
1219 *
1220 * Returns the `GdkKeymap` attached to @display.
1221 *
1222 * Returns: (transfer none): the `GdkKeymap` attached to @display.
1223 */
1224GdkKeymap *
1225gdk_display_get_keymap (GdkDisplay *display)
1226{
1227 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1228
1229 return GDK_DISPLAY_GET_CLASS (display)->get_keymap (display);
1230}
1231
1232static void
1233gdk_display_init_gl (GdkDisplay *self)
1234{
1235 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
1236 GdkGLContext *context;
1237 gint64 before G_GNUC_UNUSED;
1238 gint64 before2 G_GNUC_UNUSED;
1239
1240 before = GDK_PROFILER_CURRENT_TIME;
1241
1242 if (GDK_DISPLAY_DEBUG_CHECK (self, GL_DISABLE))
1243 {
1244 g_set_error_literal (err: &priv->gl_error, GDK_GL_ERROR,
1245 code: GDK_GL_ERROR_NOT_AVAILABLE,
1246 _("GL support disabled via GDK_DEBUG"));
1247 return;
1248 }
1249
1250 context = GDK_DISPLAY_GET_CLASS (self)->init_gl (self, &priv->gl_error);
1251 if (context == NULL)
1252 return;
1253
1254 before2 = GDK_PROFILER_CURRENT_TIME;
1255
1256 if (!gdk_gl_context_realize (context, error: &priv->gl_error))
1257 {
1258 g_object_unref (object: context);
1259 return;
1260 }
1261
1262 gdk_profiler_end_mark (before2, "realize OpenGL context", NULL);
1263
1264 /* Only assign after realize, so GdkGLContext::realize() can use
1265 * gdk_display_get_gl_context() == NULL to differentiate between
1266 * the display's context and any other context.
1267 */
1268 priv->gl_context = context;
1269
1270 gdk_gl_backend_use (GDK_GL_CONTEXT_GET_CLASS (context)->backend_type);
1271
1272 gdk_profiler_end_mark (before, "initialize OpenGL", NULL);
1273}
1274
1275/**
1276 * gdk_display_prepare_gl:
1277 * @self: a `GdkDisplay`
1278 * @error: return location for a `GError`
1279 *
1280 * Checks that OpenGL is available for @self and ensures that it is
1281 * properly initialized.
1282 * When this fails, an @error will be set describing the error and this
1283 * function returns %FALSE.
1284 *
1285 * Note that even if this function succeeds, creating a `GdkGLContext`
1286 * may still fail.
1287 *
1288 * This function is idempotent. Calling it multiple times will just
1289 * return the same value or error.
1290 *
1291 * You never need to call this function, GDK will call it automatically
1292 * as needed. But you can use it as a check when setting up code that
1293 * might make use of OpenGL.
1294 *
1295 * Returns: %TRUE if the display supports OpenGL
1296 *
1297 * Since: 4.4
1298 **/
1299gboolean
1300gdk_display_prepare_gl (GdkDisplay *self,
1301 GError **error)
1302{
1303 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
1304
1305 g_return_val_if_fail (GDK_IS_DISPLAY (self), FALSE);
1306 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1307
1308 for (;;)
1309 {
1310 if (priv->gl_context)
1311 return TRUE;
1312
1313 if (priv->gl_error != NULL)
1314 {
1315 if (error)
1316 *error = g_error_copy (error: priv->gl_error);
1317
1318
1319 return FALSE;
1320 }
1321
1322 gdk_display_init_gl (self);
1323
1324 /* try again */
1325 }
1326}
1327
1328/**
1329 * gdk_display_create_gl_context:
1330 * @self: a `GdkDisplay`
1331 * @error: return location for an error
1332 *
1333 * Creates a new `GdkGLContext` for the `GdkDisplay`.
1334 *
1335 * The context is disconnected from any particular surface or surface
1336 * and cannot be used to draw to any surface. It can only be used to
1337 * draw to non-surface framebuffers like textures.
1338 *
1339 * If the creation of the `GdkGLContext` failed, @error will be set.
1340 * Before using the returned `GdkGLContext`, you will need to
1341 * call [method@Gdk.GLContext.make_current] or [method@Gdk.GLContext.realize].
1342 *
1343 * Returns: (transfer full): the newly created `GdkGLContext`
1344 *
1345 * Since: 4.6
1346 */
1347GdkGLContext *
1348gdk_display_create_gl_context (GdkDisplay *self,
1349 GError **error)
1350{
1351 g_return_val_if_fail (GDK_IS_DISPLAY (self), NULL);
1352 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1353
1354 if (!gdk_display_prepare_gl (self, error))
1355 return NULL;
1356
1357 return gdk_gl_context_new (display: self, NULL);
1358}
1359
1360/*< private >
1361 * gdk_display_get_gl_context:
1362 * @self: the `GdkDisplay`
1363 *
1364 * Gets the GL context returned from [vfunc@Gdk.Display.init_gl]
1365 * previously.
1366 *
1367 * If that function has not been called yet or did fail, %NULL is
1368 * returned.
1369 * Call [method@Gdk.Display.prepare_gl] to avoid this.
1370 *
1371 * Returns: The `GdkGLContext`
1372 */
1373GdkGLContext *
1374gdk_display_get_gl_context (GdkDisplay *self)
1375{
1376 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
1377
1378 return priv->gl_context;
1379}
1380
1381#ifdef HAVE_EGL
1382#ifdef G_ENABLE_DEBUG
1383static int
1384strvcmp (gconstpointer p1,
1385 gconstpointer p2)
1386{
1387 const char * const *s1 = p1;
1388 const char * const *s2 = p2;
1389
1390 return strcmp (s1: *s1, s2: *s2);
1391}
1392
1393static char *
1394describe_extensions (EGLDisplay egl_display)
1395{
1396 const char *extensions;
1397 char **exts;
1398 char *ext;
1399
1400 extensions = eglQueryString (egl_display, EGL_EXTENSIONS);
1401
1402 exts = g_strsplit (string: extensions, delimiter: " ", max_tokens: -1);
1403 qsort (base: exts, nmemb: g_strv_length (str_array: exts), size: sizeof (char *), compar: strvcmp);
1404
1405 ext = g_strjoinv (separator: "\n\t", str_array: exts);
1406 if (ext[0] == '\n')
1407 ext[0] = ' ';
1408
1409 g_strfreev (str_array: exts);
1410
1411 return g_strstrip (ext);
1412}
1413
1414static char *
1415describe_egl_config (EGLDisplay egl_display,
1416 EGLConfig egl_config)
1417{
1418 EGLint red, green, blue, alpha, type;
1419
1420 if (egl_config == NULL)
1421 return g_strdup (str: "-");
1422
1423 if (!eglGetConfigAttrib (egl_display, egl_config, EGL_RED_SIZE, &red) ||
1424 !eglGetConfigAttrib (egl_display, egl_config, EGL_GREEN_SIZE, &green) ||
1425 !eglGetConfigAttrib (egl_display, egl_config, EGL_BLUE_SIZE, &blue) ||
1426 !eglGetConfigAttrib (egl_display, egl_config, EGL_ALPHA_SIZE, &alpha))
1427 return g_strdup (str: "Unknown");
1428
1429 if (epoxy_has_egl_extension (dpy: egl_display, extension: "EGL_EXT_pixel_format_float"))
1430 {
1431 if (!eglGetConfigAttrib (egl_display, egl_config, EGL_COLOR_COMPONENT_TYPE_EXT, &type))
1432 type = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
1433 }
1434 else
1435 type = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
1436
1437 return g_strdup_printf (format: "R%dG%dB%dA%d%s", red, green, blue, alpha, type == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT ? "" : " float");
1438}
1439#endif
1440
1441gpointer
1442gdk_display_get_egl_config (GdkDisplay *self)
1443{
1444 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
1445
1446 return priv->egl_config;
1447}
1448
1449gpointer
1450gdk_display_get_egl_config_high_depth (GdkDisplay *self)
1451{
1452 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
1453
1454 return priv->egl_config_high_depth;
1455}
1456
1457static EGLDisplay
1458gdk_display_create_egl_display (EGLenum platform,
1459 gpointer native_display)
1460{
1461 G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
1462 EGLDisplay egl_display = NULL;
1463
1464 if (epoxy_has_egl_extension (NULL, extension: "EGL_KHR_platform_base"))
1465 {
1466 PFNEGLGETPLATFORMDISPLAYPROC getPlatformDisplay =
1467 (void *) eglGetProcAddress ("eglGetPlatformDisplay");
1468
1469 if (getPlatformDisplay != NULL)
1470 egl_display = getPlatformDisplay (platform, native_display, NULL);
1471 if (egl_display != NULL)
1472 goto out;
1473 }
1474
1475 if (epoxy_has_egl_extension (NULL, extension: "EGL_EXT_platform_base"))
1476 {
1477 PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay =
1478 (void *) eglGetProcAddress ("eglGetPlatformDisplayEXT");
1479
1480 if (getPlatformDisplay != NULL)
1481 egl_display = getPlatformDisplay (platform, native_display, NULL);
1482 if (egl_display != NULL)
1483 goto out;
1484 }
1485
1486 egl_display = eglGetDisplay ((EGLNativeDisplayType) native_display);
1487
1488out:
1489 gdk_profiler_end_mark (start_time, "Create EGL display", NULL);
1490
1491 return egl_display;
1492}
1493
1494#define MAX_EGL_ATTRS 30
1495
1496typedef enum {
1497 GDK_EGL_CONFIG_PERFECT = (1 << 0),
1498 GDK_EGL_CONFIG_HDR = (1 << 1),
1499} GdkEGLConfigCreateFlags;
1500
1501static EGLConfig
1502gdk_display_create_egl_config (GdkDisplay *self,
1503 GdkEGLConfigCreateFlags flags,
1504 GError **error)
1505{
1506 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
1507 G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
1508 EGLint attrs[MAX_EGL_ATTRS];
1509 EGLConfig *configs;
1510 EGLint count, alloced;
1511 EGLConfig best_config;
1512 guint best_score;
1513
1514 int i = 0;
1515
1516 attrs[i++] = EGL_SURFACE_TYPE;
1517 attrs[i++] = EGL_WINDOW_BIT;
1518
1519 attrs[i++] = EGL_COLOR_BUFFER_TYPE;
1520 attrs[i++] = EGL_RGB_BUFFER;
1521
1522 attrs[i++] = EGL_RED_SIZE;
1523 attrs[i++] = (flags & GDK_EGL_CONFIG_HDR) ? 9 : 8;
1524 attrs[i++] = EGL_GREEN_SIZE;
1525 attrs[i++] = (flags & GDK_EGL_CONFIG_HDR) ? 9 : 8;
1526 attrs[i++] = EGL_BLUE_SIZE;
1527 attrs[i++] = (flags & GDK_EGL_CONFIG_HDR) ? 9 : 8;
1528 attrs[i++] = EGL_ALPHA_SIZE;
1529 attrs[i++] = 8;
1530
1531 if (flags & GDK_EGL_CONFIG_HDR &&
1532 self->have_egl_pixel_format_float)
1533 {
1534 attrs[i++] = EGL_COLOR_COMPONENT_TYPE_EXT;
1535 attrs[i++] = EGL_DONT_CARE;
1536 }
1537
1538 attrs[i++] = EGL_NONE;
1539 g_assert (i < MAX_EGL_ATTRS);
1540
1541 if (!eglChooseConfig (priv->egl_display, attrs, NULL, -1, &alloced) || alloced == 0)
1542 {
1543 g_set_error_literal (err: error, GDK_GL_ERROR, code: GDK_GL_ERROR_NOT_AVAILABLE,
1544 _("No EGL configuration available"));
1545 return NULL;
1546 }
1547
1548 configs = g_new (EGLConfig, alloced);
1549 if (!eglChooseConfig (priv->egl_display, attrs, configs, alloced, &count))
1550 {
1551 g_set_error_literal (err: error, GDK_GL_ERROR, code: GDK_GL_ERROR_NOT_AVAILABLE,
1552 _("Failed to get EGL configurations"));
1553 return NULL;
1554 }
1555 g_warn_if_fail (alloced == count);
1556
1557 best_score = G_MAXUINT;
1558 best_config = NULL;
1559
1560 for (i = 0; i < count; i++)
1561 {
1562 guint score = GDK_DISPLAY_GET_CLASS (self)->rate_egl_config (self, priv->egl_display, configs[i]);
1563
1564 if (score < best_score)
1565 {
1566 best_score = score;
1567 best_config = configs[i];
1568 }
1569
1570 if (score == 0)
1571 break;
1572 }
1573
1574 g_free (mem: configs);
1575
1576 gdk_profiler_end_mark (start_time, "Create EGL config", NULL);
1577
1578 if (best_score == G_MAXUINT)
1579 {
1580 g_set_error_literal (err: error, GDK_GL_ERROR,
1581 code: GDK_GL_ERROR_NOT_AVAILABLE,
1582 _("No EGL configuration with required features found"));
1583 return NULL;
1584 }
1585 else if ((flags & GDK_EGL_CONFIG_PERFECT) && best_score != 0)
1586 {
1587 g_set_error_literal (err: error, GDK_GL_ERROR,
1588 code: GDK_GL_ERROR_NOT_AVAILABLE,
1589 _("No perfect EGL configuration found"));
1590 return NULL;
1591 }
1592
1593 return best_config;
1594}
1595
1596#undef MAX_EGL_ATTRS
1597
1598static gboolean
1599gdk_display_check_egl_extensions (EGLDisplay egl_display,
1600 const char **extensions,
1601 GError **error)
1602{
1603 GString *missing = NULL;
1604 gsize i, n_missing;
1605
1606 n_missing = 0;
1607
1608 for (i = 0; extensions[i] != NULL; i++)
1609 {
1610 if (!epoxy_has_egl_extension (dpy: egl_display, extension: extensions[i]))
1611 {
1612 if (missing == NULL)
1613 {
1614 missing = g_string_new (init: extensions[i]);
1615 }
1616 else
1617 {
1618 g_string_append (string: missing, val: ", ");
1619 g_string_append (string: missing, val: extensions[i]);
1620 }
1621 n_missing++;
1622 }
1623 }
1624
1625 if (n_missing)
1626 {
1627 g_set_error (err: error, GDK_GL_ERROR, code: GDK_GL_ERROR_UNSUPPORTED_PROFILE,
1628 /* translators: Arguments are the number of missing extensions
1629 * followed by a comma-separated list of their names */
1630 format: g_dngettext (GETTEXT_PACKAGE,
1631 msgid: "EGL implementation is missing extension %s",
1632 msgid_plural: "EGL implementation is missing %2$d extensions: %1$s",
1633 n: n_missing),
1634 missing->str, (int) n_missing);
1635
1636 g_string_free (string: missing, TRUE);
1637 return FALSE;
1638 }
1639
1640 return TRUE;
1641}
1642
1643gboolean
1644gdk_display_init_egl (GdkDisplay *self,
1645 int platform,
1646 gpointer native_display,
1647 gboolean allow_any,
1648 GError **error)
1649{
1650 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
1651 G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
1652 G_GNUC_UNUSED gint64 start_time2;
1653 int major, minor;
1654
1655 if (!gdk_gl_backend_can_be_used (backend_type: GDK_GL_EGL, error))
1656 return FALSE;
1657
1658 if (!epoxy_has_egl ())
1659 {
1660 gboolean sandboxed = gdk_running_in_sandbox ();
1661
1662 g_set_error_literal (err: error, GDK_GL_ERROR,
1663 code: GDK_GL_ERROR_NOT_AVAILABLE,
1664 message: sandboxed ? _("libEGL not available in this sandbox")
1665 : _("libEGL not available"));
1666 return FALSE;
1667 }
1668
1669 priv->egl_display = gdk_display_create_egl_display (platform, native_display);
1670
1671 if (priv->egl_display == NULL)
1672 {
1673 g_set_error_literal (err: error, GDK_GL_ERROR,
1674 code: GDK_GL_ERROR_NOT_AVAILABLE,
1675 _("Failed to create EGL display"));
1676 return FALSE;
1677 }
1678
1679 start_time2 = GDK_PROFILER_CURRENT_TIME;
1680 if (!eglInitialize (priv->egl_display, &major, &minor))
1681 {
1682 priv->egl_display = NULL;
1683 g_set_error_literal (err: error, GDK_GL_ERROR,
1684 code: GDK_GL_ERROR_NOT_AVAILABLE,
1685 _("Could not initialize EGL display"));
1686 return FALSE;
1687 }
1688 gdk_profiler_end_mark (start_time2, "eglInitialize", NULL);
1689
1690 if (major < GDK_EGL_MIN_VERSION_MAJOR ||
1691 (major == GDK_EGL_MIN_VERSION_MAJOR && minor < GDK_EGL_MIN_VERSION_MINOR))
1692 {
1693 g_clear_pointer (&priv->egl_display, eglTerminate);
1694 g_set_error (err: error, GDK_GL_ERROR,
1695 code: GDK_GL_ERROR_NOT_AVAILABLE,
1696 _("EGL version %d.%d is too old. GTK requires %d.%d"),
1697 major, minor, GDK_EGL_MIN_VERSION_MAJOR, GDK_EGL_MIN_VERSION_MINOR);
1698 return FALSE;
1699 }
1700
1701 if (!gdk_display_check_egl_extensions (egl_display: priv->egl_display,
1702 extensions: (const char *[]) {
1703 "EGL_KHR_create_context",
1704 "EGL_KHR_surfaceless_context",
1705 NULL
1706 },
1707 error))
1708 {
1709 g_clear_pointer (&priv->egl_display, eglTerminate);
1710 return FALSE;
1711 }
1712
1713 priv->egl_config = gdk_display_create_egl_config (self,
1714 flags: allow_any ? 0 : GDK_EGL_CONFIG_PERFECT,
1715 error);
1716 if (priv->egl_config == NULL)
1717 {
1718 g_clear_pointer (&priv->egl_display, eglTerminate);
1719 return FALSE;
1720 }
1721
1722 self->have_egl_buffer_age =
1723 epoxy_has_egl_extension (dpy: priv->egl_display, extension: "EGL_EXT_buffer_age");
1724 self->have_egl_no_config_context =
1725 epoxy_has_egl_extension (dpy: priv->egl_display, extension: "EGL_KHR_no_config_context");
1726 self->have_egl_pixel_format_float =
1727 epoxy_has_egl_extension (dpy: priv->egl_display, extension: "EGL_EXT_pixel_format_float");
1728 self->have_egl_win32_libangle =
1729 epoxy_has_egl_extension (dpy: priv->egl_display, extension: "EGL_ANGLE_d3d_share_handle_client_buffer");
1730
1731 if (self->have_egl_no_config_context)
1732 priv->egl_config_high_depth = gdk_display_create_egl_config (self,
1733 flags: GDK_EGL_CONFIG_HDR,
1734 error);
1735 if (priv->egl_config_high_depth == NULL)
1736 priv->egl_config_high_depth = priv->egl_config;
1737
1738 GDK_DISPLAY_NOTE (self, OPENGL, {
1739 char *ext = describe_extensions (priv->egl_display);
1740 char *std_cfg = describe_egl_config (priv->egl_display, priv->egl_config);
1741 char *hd_cfg = describe_egl_config (priv->egl_display, priv->egl_config_high_depth);
1742 g_message ("EGL API version %d.%d found\n"
1743 " - Vendor: %s\n"
1744 " - Version: %s\n"
1745 " - Client APIs: %s\n"
1746 " - Extensions:\n"
1747 "\t%s\n"
1748 " - Selected fbconfig: %s\n"
1749 " high depth: %s",
1750 major, minor,
1751 eglQueryString (priv->egl_display, EGL_VENDOR),
1752 eglQueryString (priv->egl_display, EGL_VERSION),
1753 eglQueryString (priv->egl_display, EGL_CLIENT_APIS),
1754 ext, std_cfg,
1755 priv->egl_config_high_depth == priv->egl_config ? "none" : hd_cfg);
1756 g_free (hd_cfg);
1757 g_free (std_cfg);
1758 g_free (ext);
1759 });
1760
1761 gdk_profiler_end_mark (start_time, "init EGL", NULL);
1762
1763 return TRUE;
1764}
1765#endif
1766
1767/*<private>
1768 * gdk_display_get_egl_display:
1769 * @self: a display
1770 *
1771 * Retrieves the EGL display connection object for the given GDK display.
1772 *
1773 * This function returns `NULL` if GL is not supported or GDK is using
1774 * a different OpenGL framework than EGL.
1775 *
1776 * Returns: (nullable): the EGL display object
1777 */
1778gpointer
1779gdk_display_get_egl_display (GdkDisplay *self)
1780{
1781 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self);
1782
1783 g_return_val_if_fail (GDK_IS_DISPLAY (self), NULL);
1784
1785#ifdef HAVE_EGL
1786 if (!priv->egl_display &&
1787 !gdk_display_prepare_gl (self, NULL))
1788 return NULL;
1789
1790 return priv->egl_display;
1791#else
1792 return NULL;
1793#endif
1794}
1795
1796GdkDebugFlags
1797gdk_display_get_debug_flags (GdkDisplay *display)
1798{
1799 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
1800
1801 return display ? priv->debug_flags : _gdk_debug_flags;
1802}
1803
1804void
1805gdk_display_set_debug_flags (GdkDisplay *display,
1806 GdkDebugFlags flags)
1807{
1808 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
1809
1810 priv->debug_flags = flags;
1811}
1812
1813/**
1814 * gdk_display_is_composited: (attributes org.gtk.Method.get_property=composited)
1815 * @display: a `GdkDisplay`
1816 *
1817 * Returns whether surfaces can reasonably be expected to have
1818 * their alpha channel drawn correctly on the screen.
1819 *
1820 * Check [method@Gdk.Display.is_rgba] for whether the display
1821 * supports an alpha channel.
1822 *
1823 * On X11 this function returns whether a compositing manager is
1824 * compositing on @display.
1825 *
1826 * On modern displays, this value is always %TRUE.
1827 *
1828 * Returns: Whether surfaces with RGBA visuals can reasonably
1829 * be expected to have their alpha channels drawn correctly
1830 * on the screen.
1831 */
1832gboolean
1833gdk_display_is_composited (GdkDisplay *display)
1834{
1835 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
1836
1837 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
1838
1839 return priv->composited;
1840}
1841
1842void
1843gdk_display_set_composited (GdkDisplay *display,
1844 gboolean composited)
1845{
1846 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
1847
1848 g_return_if_fail (GDK_IS_DISPLAY (display));
1849
1850 if (priv->composited == composited)
1851 return;
1852
1853 priv->composited = composited;
1854
1855 g_object_notify_by_pspec (G_OBJECT (display), pspec: props[PROP_COMPOSITED]);
1856}
1857
1858/**
1859 * gdk_display_is_rgba: (attributes org.gtk.Method.get_property=rgba)
1860 * @display: a `GdkDisplay`
1861 *
1862 * Returns whether surfaces on this @display are created with an
1863 * alpha channel.
1864 *
1865 * Even if a %TRUE is returned, it is possible that the
1866 * surface’s alpha channel won’t be honored when displaying the
1867 * surface on the screen: in particular, for X an appropriate
1868 * windowing manager and compositing manager must be running to
1869 * provide appropriate display. Use [method@Gdk.Display.is_composited]
1870 * to check if that is the case.
1871 *
1872 * On modern displays, this value is always %TRUE.
1873 *
1874 * Returns: %TRUE if surfaces are created with an alpha channel or
1875 * %FALSE if the display does not support this functionality.
1876 */
1877gboolean
1878gdk_display_is_rgba (GdkDisplay *display)
1879{
1880 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
1881
1882 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
1883
1884 return priv->rgba;
1885}
1886
1887void
1888gdk_display_set_rgba (GdkDisplay *display,
1889 gboolean rgba)
1890{
1891 GdkDisplayPrivate *priv = gdk_display_get_instance_private (self: display);
1892
1893 g_return_if_fail (GDK_IS_DISPLAY (display));
1894
1895 if (priv->rgba == rgba)
1896 return;
1897
1898 priv->rgba = rgba;
1899
1900 g_object_notify_by_pspec (G_OBJECT (display), pspec: props[PROP_RGBA]);
1901}
1902
1903static void
1904device_removed_cb (GdkSeat *seat,
1905 GdkDevice *device,
1906 GdkDisplay *display)
1907{
1908 g_hash_table_remove (hash_table: display->device_grabs, key: device);
1909 g_hash_table_remove (hash_table: display->pointers_info, key: device);
1910
1911 /* FIXME: change core pointer and remove from device list */
1912}
1913
1914void
1915gdk_display_add_seat (GdkDisplay *display,
1916 GdkSeat *seat)
1917{
1918 g_return_if_fail (GDK_IS_DISPLAY (display));
1919 g_return_if_fail (GDK_IS_SEAT (seat));
1920
1921 display->seats = g_list_append (list: display->seats, g_object_ref (seat));
1922 g_signal_emit (instance: display, signal_id: signals[SEAT_ADDED], detail: 0, seat);
1923
1924 g_signal_connect (seat, "device-removed", G_CALLBACK (device_removed_cb), display);
1925}
1926
1927void
1928gdk_display_remove_seat (GdkDisplay *display,
1929 GdkSeat *seat)
1930{
1931 GList *link;
1932
1933 g_return_if_fail (GDK_IS_DISPLAY (display));
1934 g_return_if_fail (GDK_IS_SEAT (seat));
1935
1936 g_signal_handlers_disconnect_by_func (seat, G_CALLBACK (device_removed_cb), display);
1937
1938 link = g_list_find (list: display->seats, data: seat);
1939
1940 if (link)
1941 {
1942 display->seats = g_list_remove_link (list: display->seats, llink: link);
1943 g_signal_emit (instance: display, signal_id: signals[SEAT_REMOVED], detail: 0, seat);
1944 g_object_unref (object: link->data);
1945 g_list_free (list: link);
1946 }
1947}
1948
1949/**
1950 * gdk_display_get_default_seat:
1951 * @display: a `GdkDisplay`
1952 *
1953 * Returns the default `GdkSeat` for this display.
1954 *
1955 * Note that a display may not have a seat. In this case,
1956 * this function will return %NULL.
1957 *
1958 * Returns: (transfer none) (nullable): the default seat.
1959 **/
1960GdkSeat *
1961gdk_display_get_default_seat (GdkDisplay *display)
1962{
1963 GdkDisplayClass *display_class;
1964
1965 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1966
1967 display_class = GDK_DISPLAY_GET_CLASS (display);
1968
1969 return display_class->get_default_seat (display);
1970}
1971
1972/**
1973 * gdk_display_list_seats:
1974 * @display: a `GdkDisplay`
1975 *
1976 * Returns the list of seats known to @display.
1977 *
1978 * Returns: (transfer container) (element-type GdkSeat): the
1979 * list of seats known to the `GdkDisplay`
1980 */
1981GList *
1982gdk_display_list_seats (GdkDisplay *display)
1983{
1984 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1985
1986 return g_list_copy (list: display->seats);
1987}
1988
1989/**
1990 * gdk_display_get_monitors:
1991 * @self: a `GdkDisplay`
1992 *
1993 * Gets the list of monitors associated with this display.
1994 *
1995 * Subsequent calls to this function will always return the
1996 * same list for the same display.
1997 *
1998 * You can listen to the GListModel::items-changed signal on
1999 * this list to monitor changes to the monitor of this display.
2000 *
2001 * Returns: (transfer none): a `GListModel` of `GdkMonitor`
2002 */
2003GListModel *
2004gdk_display_get_monitors (GdkDisplay *self)
2005{
2006 g_return_val_if_fail (GDK_IS_DISPLAY (self), NULL);
2007
2008 return GDK_DISPLAY_GET_CLASS (self)->get_monitors (self);
2009}
2010
2011/**
2012 * gdk_display_get_monitor_at_surface:
2013 * @display: a `GdkDisplay`
2014 * @surface: a `GdkSurface`
2015 *
2016 * Gets the monitor in which the largest area of @surface
2017 * resides.
2018 *
2019 * Returns a monitor close to @surface if it is outside
2020 * of all monitors.
2021 *
2022 * Returns: (transfer none): the monitor with the largest
2023 * overlap with @surface
2024 */
2025GdkMonitor *
2026gdk_display_get_monitor_at_surface (GdkDisplay *display,
2027 GdkSurface *surface)
2028{
2029 GdkRectangle win;
2030 GListModel *monitors;
2031 guint i;
2032 int area = 0;
2033 GdkMonitor *best = NULL;
2034 GdkDisplayClass *class;
2035
2036 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
2037
2038 class = GDK_DISPLAY_GET_CLASS (display);
2039 if (class->get_monitor_at_surface)
2040 {
2041 best = class->get_monitor_at_surface (display, surface);
2042
2043 if (best)
2044 return best;
2045 }
2046
2047 /* the fallback implementation requires global coordinates */
2048 gdk_surface_get_geometry (surface, x: &win.x, y: &win.y, width: &win.width, height: &win.height);
2049 gdk_surface_get_origin (surface, x: &win.x, y: &win.y);
2050
2051 monitors = gdk_display_get_monitors (self: display);
2052 for (i = 0; i < g_list_model_get_n_items (list: monitors); i++)
2053 {
2054 GdkMonitor *monitor;
2055 GdkRectangle mon, intersect;
2056 int overlap;
2057
2058 monitor = g_list_model_get_item (list: monitors, position: i);
2059 gdk_monitor_get_geometry (monitor, geometry: &mon);
2060 gdk_rectangle_intersect (src1: &win, src2: &mon, dest: &intersect);
2061 overlap = intersect.width *intersect.height;
2062 if (overlap > area)
2063 {
2064 area = overlap;
2065 best = monitor;
2066 }
2067 g_object_unref (object: monitor);
2068 }
2069
2070 return best;
2071}
2072
2073void
2074gdk_display_emit_opened (GdkDisplay *display)
2075{
2076 g_signal_emit (instance: display, signal_id: signals[OPENED], detail: 0);
2077}
2078
2079/**
2080 * gdk_display_get_setting:
2081 * @display: a `GdkDisplay`
2082 * @name: the name of the setting
2083 * @value: location to store the value of the setting
2084 *
2085 * Retrieves a desktop-wide setting such as double-click time
2086 * for the @display.
2087 *
2088 * Returns: %TRUE if the setting existed and a value was stored
2089 * in @value, %FALSE otherwise
2090 */
2091gboolean
2092gdk_display_get_setting (GdkDisplay *display,
2093 const char *name,
2094 GValue *value)
2095{
2096 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
2097 g_return_val_if_fail (name != NULL, FALSE);
2098 g_return_val_if_fail (value != NULL, FALSE);
2099
2100 return GDK_DISPLAY_GET_CLASS (display)->get_setting (display, name, value);
2101}
2102
2103void
2104gdk_display_setting_changed (GdkDisplay *display,
2105 const char *name)
2106{
2107 g_signal_emit (instance: display, signal_id: signals[SETTING_CHANGED], detail: 0, name);
2108}
2109
2110void
2111gdk_display_set_double_click_time (GdkDisplay *display,
2112 guint msec)
2113{
2114 display->double_click_time = msec;
2115}
2116
2117void
2118gdk_display_set_double_click_distance (GdkDisplay *display,
2119 guint distance)
2120{
2121 display->double_click_distance = distance;
2122}
2123
2124void
2125gdk_display_set_cursor_theme (GdkDisplay *display,
2126 const char *name,
2127 int size)
2128{
2129 if (GDK_DISPLAY_GET_CLASS (display)->set_cursor_theme)
2130 GDK_DISPLAY_GET_CLASS (display)->set_cursor_theme (display, name, size);
2131}
2132
2133/**
2134 * gdk_display_map_keyval:
2135 * @display: a `GdkDisplay`
2136 * @keyval: a keyval, such as %GDK_KEY_a, %GDK_KEY_Up, %GDK_KEY_Return, etc.
2137 * @keys: (out) (array length=n_keys) (transfer full): return location
2138 * for an array of `GdkKeymapKey`
2139 * @n_keys: return location for number of elements in returned array
2140 *
2141 * Obtains a list of keycode/group/level combinations that will
2142 * generate @keyval.
2143 *
2144 * Groups and levels are two kinds of keyboard mode; in general, the level
2145 * determines whether the top or bottom symbol on a key is used, and the
2146 * group determines whether the left or right symbol is used.
2147 *
2148 * On US keyboards, the shift key changes the keyboard level, and there
2149 * are no groups. A group switch key might convert a keyboard between
2150 * Hebrew to English modes, for example.
2151 *
2152 * `GdkEventKey` contains a %group field that indicates the active
2153 * keyboard group. The level is computed from the modifier mask.
2154 *
2155 * The returned array should be freed with g_free().
2156 *
2157 * Returns: %TRUE if keys were found and returned
2158 */
2159gboolean
2160gdk_display_map_keyval (GdkDisplay *display,
2161 guint keyval,
2162 GdkKeymapKey **keys,
2163 int *n_keys)
2164{
2165 return gdk_keymap_get_entries_for_keyval (keymap: gdk_display_get_keymap (display),
2166 keyval,
2167 keys,
2168 n_keys);
2169}
2170
2171/**
2172 * gdk_display_map_keycode:
2173 * @display: a `GdkDisplay`
2174 * @keycode: a keycode
2175 * @keys: (out) (array length=n_entries) (transfer full) (optional): return
2176 * location for array of `GdkKeymapKey`
2177 * @keyvals: (out) (array length=n_entries) (transfer full) (optional): return
2178 * location for array of keyvals
2179 * @n_entries: length of @keys and @keyvals
2180 *
2181 * Returns the keyvals bound to @keycode.
2182 *
2183 * The Nth `GdkKeymapKey` in @keys is bound to the Nth keyval in @keyvals.
2184 *
2185 * When a keycode is pressed by the user, the keyval from
2186 * this list of entries is selected by considering the effective
2187 * keyboard group and level.
2188 *
2189 * Free the returned arrays with g_free().
2190 *
2191 * Returns: %TRUE if there were any entries
2192 */
2193gboolean
2194gdk_display_map_keycode (GdkDisplay *display,
2195 guint keycode,
2196 GdkKeymapKey **keys,
2197 guint **keyvals,
2198 int *n_entries)
2199{
2200 return gdk_keymap_get_entries_for_keycode (keymap: gdk_display_get_keymap (display),
2201 hardware_keycode: keycode,
2202 keys,
2203 keyvals,
2204 n_entries);
2205}
2206
2207/**
2208 * gdk_display_translate_key:
2209 * @display: a `GdkDisplay`
2210 * @keycode: a keycode
2211 * @state: a modifier state
2212 * @group: active keyboard group
2213 * @keyval: (out) (optional): return location for keyval
2214 * @effective_group: (out) (optional): return location for effective group
2215 * @level: (out) (optional): return location for level
2216 * @consumed: (out) (optional): return location for modifiers that were used
2217 * to determine the group or level
2218 *
2219 * Translates the contents of a `GdkEventKey` into a keyval, effective group,
2220 * and level.
2221 *
2222 * Modifiers that affected the translation and are thus unavailable for
2223 * application use are returned in @consumed_modifiers.
2224 *
2225 * The @effective_group is the group that was actually used for the
2226 * translation; some keys such as Enter are not affected by the active
2227 * keyboard group. The @level is derived from @state.
2228 *
2229 * @consumed_modifiers gives modifiers that should be masked out
2230 * from @state when comparing this key press to a keyboard shortcut.
2231 * For instance, on a US keyboard, the `plus` symbol is shifted, so
2232 * when comparing a key press to a `<Control>plus` accelerator `<Shift>`
2233 * should be masked out.
2234 *
2235 * This function should rarely be needed, since `GdkEventKey` already
2236 * contains the translated keyval. It is exported for the benefit of
2237 * virtualized test environments.
2238 *
2239 * Returns: %TRUE if there was a keyval bound to keycode/state/group.
2240 */
2241gboolean
2242gdk_display_translate_key (GdkDisplay *display,
2243 guint keycode,
2244 GdkModifierType state,
2245 int group,
2246 guint *keyval,
2247 int *effective_group,
2248 int *level,
2249 GdkModifierType *consumed)
2250{
2251 return gdk_keymap_translate_keyboard_state (keymap: gdk_display_get_keymap (display),
2252 hardware_keycode: keycode, state, group,
2253 keyval,
2254 effective_group,
2255 level,
2256 consumed_modifiers: consumed);
2257}
2258

source code of gtk/gdk/gdkdisplay.c