1/*
2 * Copyright © 2010 Intel Corporation
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library 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 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#define VK_USE_PLATFORM_WAYLAND_KHR
21
22#include <stdlib.h>
23#include <string.h>
24#include <errno.h>
25#include <unistd.h>
26#include <fcntl.h>
27
28#ifdef HAVE_LINUX_MEMFD_H
29#include <linux/memfd.h>
30#endif
31
32#include <sys/mman.h>
33#include <sys/syscall.h>
34
35#include <glib.h>
36#include <gio/gio.h>
37#include "gdkwayland.h"
38#include "gdkdisplay.h"
39#include "gdkdisplay-wayland.h"
40#include "gdkmonitor-wayland.h"
41#include "gdkseat-wayland.h"
42#include "gdksurfaceprivate.h"
43#include "gdkdeviceprivate.h"
44#include "gdkkeysprivate.h"
45#include "gdkprivate-wayland.h"
46#include "gdkcairocontext-wayland.h"
47#include "gdkglcontext-wayland.h"
48#include "gdkvulkancontext-wayland.h"
49#include "gdkwaylandmonitor.h"
50#include "gdkprofilerprivate.h"
51#include <wayland/pointer-gestures-unstable-v1-client-protocol.h>
52#include "tablet-unstable-v2-client-protocol.h"
53#include <wayland/xdg-shell-unstable-v6-client-protocol.h>
54#include <wayland/xdg-foreign-unstable-v1-client-protocol.h>
55#include <wayland/server-decoration-client-protocol.h>
56
57#include "wm-button-layout-translation.h"
58
59#include "gdk/gdk-private.h"
60
61/* Keep g_assert() defined even if we disable it globally,
62 * as we use it in many places as a handy mechanism to check
63 * for non-NULL
64 */
65#ifdef G_DISABLE_ASSERT
66# undef g_assert
67# define g_assert(expr) G_STMT_START { \
68 if G_LIKELY (expr) ; else \
69 g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
70 #expr); \
71 } G_STMT_END
72#endif
73
74/**
75 * GdkWaylandDisplay:
76 *
77 * The Wayland implementation of `GdkDisplay`.
78 *
79 * Beyond the regular [class@Gdk.Display] API, the Wayland implementation
80 * provides access to Wayland objects such as the `wl_display` with
81 * [method@GdkWayland.WaylandDisplay.get_wl_display], the `wl_compositor` with
82 * [method@GdkWayland.WaylandDisplay.get_wl_compositor].
83 *
84 * You can find out what Wayland globals are supported by a display
85 * with [method@GdkWayland.WaylandDisplay.query_registry].
86 */
87
88#define MIN_SYSTEM_BELL_DELAY_MS 20
89
90#define GTK_SHELL1_VERSION 5
91#define OUTPUT_VERSION_WITH_DONE 2
92#define NO_XDG_OUTPUT_DONE_SINCE_VERSION 3
93#define XDG_ACTIVATION_VERSION 1
94
95static void _gdk_wayland_display_load_cursor_theme (GdkWaylandDisplay *display_wayland);
96
97G_DEFINE_TYPE (GdkWaylandDisplay, gdk_wayland_display, GDK_TYPE_DISPLAY)
98
99static void
100async_roundtrip_callback (void *data,
101 struct wl_callback *callback,
102 uint32_t time)
103{
104 GdkWaylandDisplay *display_wayland = data;
105
106 display_wayland->async_roundtrips =
107 g_list_remove (list: display_wayland->async_roundtrips, data: callback);
108 wl_callback_destroy (wl_callback: callback);
109}
110
111static const struct wl_callback_listener async_roundtrip_listener = {
112 async_roundtrip_callback
113};
114
115static void
116_gdk_wayland_display_async_roundtrip (GdkWaylandDisplay *display_wayland)
117{
118 struct wl_callback *callback;
119
120 callback = wl_display_sync (wl_display: display_wayland->wl_display);
121 wl_callback_add_listener (wl_callback: callback,
122 listener: &async_roundtrip_listener,
123 data: display_wayland);
124 display_wayland->async_roundtrips =
125 g_list_append (list: display_wayland->async_roundtrips, data: callback);
126}
127
128static void
129xdg_wm_base_ping (void *data,
130 struct xdg_wm_base *xdg_wm_base,
131 uint32_t serial)
132{
133 GDK_NOTE (EVENTS,
134 g_message ("ping, shell %p, serial %u\n", xdg_wm_base, serial));
135
136 xdg_wm_base_pong (xdg_wm_base, serial);
137}
138
139static const struct xdg_wm_base_listener xdg_wm_base_listener = {
140 xdg_wm_base_ping,
141};
142
143static void
144zxdg_shell_v6_ping (void *data,
145 struct zxdg_shell_v6 *xdg_shell,
146 uint32_t serial)
147{
148 GdkWaylandDisplay *display_wayland = data;
149
150 _gdk_wayland_display_update_serial (display_wayland, serial);
151
152 GDK_DISPLAY_NOTE (GDK_DISPLAY (data), EVENTS,
153 g_message ("ping, shell %p, serial %u\n", xdg_shell, serial));
154
155 zxdg_shell_v6_pong (xdg_shell, serial);
156}
157
158static const struct zxdg_shell_v6_listener zxdg_shell_v6_listener = {
159 zxdg_shell_v6_ping,
160};
161
162static gboolean
163is_known_global (gpointer key, gpointer value, gpointer user_data)
164{
165 const char *required_global = user_data;
166 const char *known_global = value;
167
168 return g_strcmp0 (str1: required_global, str2: known_global) == 0;
169}
170
171static gboolean
172has_required_globals (GdkWaylandDisplay *display_wayland,
173 const char *required_globals[])
174{
175 int i = 0;
176
177 while (required_globals[i])
178 {
179 if (g_hash_table_find (hash_table: display_wayland->known_globals,
180 predicate: is_known_global,
181 user_data: (gpointer)required_globals[i]) == NULL)
182 return FALSE;
183
184 i++;
185 }
186
187 return TRUE;
188}
189
190typedef struct _OnHasGlobalsClosure OnHasGlobalsClosure;
191
192typedef void (*HasGlobalsCallback) (GdkWaylandDisplay *display_wayland,
193 OnHasGlobalsClosure *closure);
194
195struct _OnHasGlobalsClosure
196{
197 HasGlobalsCallback handler;
198 const char **required_globals;
199};
200
201static void
202process_on_globals_closures (GdkWaylandDisplay *display_wayland)
203{
204 GList *iter;
205
206 iter = display_wayland->on_has_globals_closures;
207 while (iter != NULL)
208 {
209 GList *next = iter->next;
210 OnHasGlobalsClosure *closure = iter->data;
211
212 if (has_required_globals (display_wayland,
213 required_globals: closure->required_globals))
214 {
215 closure->handler (display_wayland, closure);
216 g_free (mem: closure);
217 display_wayland->on_has_globals_closures =
218 g_list_delete_link (list: display_wayland->on_has_globals_closures, link_: iter);
219 }
220
221 iter = next;
222 }
223}
224
225typedef struct
226{
227 OnHasGlobalsClosure base;
228 uint32_t id;
229 uint32_t version;
230} SeatAddedClosure;
231
232static void
233_gdk_wayland_display_add_seat (GdkWaylandDisplay *display_wayland,
234 uint32_t id,
235 uint32_t version)
236{
237 struct wl_seat *seat;
238
239 display_wayland->seat_version = MIN (version, 7);
240 seat = wl_registry_bind (wl_registry: display_wayland->wl_registry,
241 name: id, interface: &wl_seat_interface,
242 version: display_wayland->seat_version);
243 _gdk_wayland_display_create_seat (display: display_wayland, id, seat);
244 _gdk_wayland_display_async_roundtrip (display_wayland);
245}
246
247static void
248seat_added_closure_run (GdkWaylandDisplay *display_wayland,
249 OnHasGlobalsClosure *closure)
250{
251 SeatAddedClosure *seat_added_closure = (SeatAddedClosure*)closure;
252
253 _gdk_wayland_display_add_seat (display_wayland,
254 id: seat_added_closure->id,
255 version: seat_added_closure->version);
256}
257
258static void
259postpone_on_globals_closure (GdkWaylandDisplay *display_wayland,
260 OnHasGlobalsClosure *closure)
261{
262 display_wayland->on_has_globals_closures =
263 g_list_append (list: display_wayland->on_has_globals_closures, data: closure);
264}
265
266#ifdef G_ENABLE_DEBUG
267
268static const char *
269get_format_name (uint32_t format,
270 char name[10])
271{
272 if (format == 0)
273 g_strlcpy (dest: name, src: "ARGB8888", dest_size: 10);
274 else if (format == 1)
275 g_strlcpy (dest: name, src: "XRGB8888", dest_size: 10);
276 else
277 g_snprintf (string: name, n: 10, format: "4cc %c%c%c%c",
278 (char) (format & 0xff),
279 (char) ((format >> 8) & 0xff),
280 (char) ((format >> 16) & 0xff),
281 (char) ((format >> 24) & 0xff));
282
283 return name;
284}
285
286#endif
287
288static void
289wl_shm_format (void *data,
290 struct wl_shm *wl_shm,
291 uint32_t format)
292{
293 GDK_NOTE (MISC,
294 char buf[10];
295 g_message ("supported pixel format %s (0x%X)", get_format_name (format, buf), (guint) format);
296 );
297}
298
299static const struct wl_shm_listener wl_shm_listener = {
300 wl_shm_format
301};
302
303static void
304server_decoration_manager_default_mode (void *data,
305 struct org_kde_kwin_server_decoration_manager *manager,
306 uint32_t mode)
307{
308 g_assert (mode <= ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER);
309 const char *modes[] = {
310 [ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE] = "none",
311 [ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT] = "client",
312 [ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER] = "server",
313 };
314 GdkWaylandDisplay *display_wayland = data;
315 g_debug ("Compositor prefers decoration mode '%s'", modes[mode]);
316 display_wayland->server_decoration_mode = mode;
317}
318
319static const struct org_kde_kwin_server_decoration_manager_listener server_decoration_listener = {
320 .default_mode = server_decoration_manager_default_mode
321};
322
323/*
324 * gdk_wayland_display_prefers_ssd:
325 * @display: (type GdkWaylandDisplay): a `GdkDisplay`
326 *
327 * Checks whether the Wayland compositor prefers to draw the window
328 * decorations or if it leaves decorations to the application.
329 *
330 * Returns: %TRUE if the compositor prefers server-side decorations
331 */
332gboolean
333gdk_wayland_display_prefers_ssd (GdkDisplay *display)
334{
335 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
336
337 if (display_wayland->server_decoration_manager)
338 return display_wayland->server_decoration_mode == ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER;
339
340 return FALSE;
341}
342
343static void gdk_wayland_display_set_has_gtk_shell (GdkWaylandDisplay *display_wayland);
344static void gdk_wayland_display_add_output (GdkWaylandDisplay *display_wayland,
345 guint32 id,
346 struct wl_output *output,
347 guint32 version);
348static void gdk_wayland_display_remove_output (GdkWaylandDisplay *display_wayland,
349 guint32 id);
350static void gdk_wayland_display_init_xdg_output (GdkWaylandDisplay *display_wayland);
351static void gdk_wayland_display_get_xdg_output (GdkWaylandMonitor *monitor);
352
353static void
354gdk_registry_handle_global (void *data,
355 struct wl_registry *registry,
356 uint32_t id,
357 const char *interface,
358 uint32_t version)
359{
360 GdkWaylandDisplay *display_wayland = data;
361 struct wl_output *output;
362
363 GDK_NOTE (MISC,
364 g_message ("add global %u, interface %s, version %u", id, interface, version));
365
366 if (strcmp (s1: interface, s2: "wl_compositor") == 0)
367 {
368 display_wayland->compositor =
369 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id,
370 interface: &wl_compositor_interface, MIN (version, 5));
371 display_wayland->compositor_version = MIN (version, 4);
372 }
373 else if (strcmp (s1: interface, s2: "wl_shm") == 0)
374 {
375 display_wayland->shm =
376 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id, interface: &wl_shm_interface, version: 1);
377 wl_shm_add_listener (wl_shm: display_wayland->shm, listener: &wl_shm_listener, data: display_wayland);
378 }
379 else if (strcmp (s1: interface, s2: "xdg_wm_base") == 0)
380 {
381 display_wayland->xdg_wm_base_id = id;
382 display_wayland->xdg_wm_base_version = version;
383 }
384 else if (strcmp (s1: interface, s2: "zxdg_shell_v6") == 0)
385 {
386 display_wayland->zxdg_shell_v6_id = id;
387 }
388 else if (strcmp (s1: interface, s2: "gtk_shell1") == 0)
389 {
390 display_wayland->gtk_shell =
391 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id,
392 interface: &gtk_shell1_interface,
393 MIN (version, GTK_SHELL1_VERSION));
394 gdk_wayland_display_set_has_gtk_shell (display_wayland);
395 display_wayland->gtk_shell_version = version;
396 }
397 else if (strcmp (s1: interface, s2: "wl_output") == 0)
398 {
399 output =
400 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id, interface: &wl_output_interface, MIN (version, 2));
401 gdk_wayland_display_add_output (display_wayland, id, output, MIN (version, 2));
402 _gdk_wayland_display_async_roundtrip (display_wayland);
403 }
404 else if (strcmp (s1: interface, s2: "wl_seat") == 0)
405 {
406 SeatAddedClosure *closure;
407 static const char *required_device_manager_globals[] = {
408 "wl_compositor",
409 "wl_data_device_manager",
410 NULL
411 };
412
413 closure = g_new0 (SeatAddedClosure, 1);
414 closure->base.handler = seat_added_closure_run;
415 closure->base.required_globals = required_device_manager_globals;
416 closure->id = id;
417 closure->version = version;
418 postpone_on_globals_closure (display_wayland, closure: &closure->base);
419 }
420 else if (strcmp (s1: interface, s2: "wl_data_device_manager") == 0)
421 {
422 display_wayland->data_device_manager_version = MIN (version, 3);
423 display_wayland->data_device_manager =
424 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id, interface: &wl_data_device_manager_interface,
425 version: display_wayland->data_device_manager_version);
426 }
427 else if (strcmp (s1: interface, s2: "wl_subcompositor") == 0)
428 {
429 display_wayland->subcompositor =
430 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id, interface: &wl_subcompositor_interface, version: 1);
431 }
432 else if (strcmp (s1: interface, s2: "zwp_pointer_gestures_v1") == 0)
433 {
434 display_wayland->pointer_gestures_version =
435 MIN (version, GDK_ZWP_POINTER_GESTURES_V1_VERSION);
436
437 display_wayland->pointer_gestures =
438 wl_registry_bind (wl_registry: display_wayland->wl_registry,
439 name: id, interface: &zwp_pointer_gestures_v1_interface,
440 MIN (version, GDK_ZWP_POINTER_GESTURES_V1_VERSION));
441 }
442 else if (strcmp (s1: interface, s2: "zwp_primary_selection_device_manager_v1") == 0)
443 {
444 display_wayland->primary_selection_manager =
445 wl_registry_bind(wl_registry: display_wayland->wl_registry, name: id,
446 interface: &zwp_primary_selection_device_manager_v1_interface, version: 1);
447 }
448 else if (strcmp (s1: interface, s2: "zwp_tablet_manager_v2") == 0)
449 {
450 display_wayland->tablet_manager =
451 wl_registry_bind(wl_registry: display_wayland->wl_registry, name: id,
452 interface: &zwp_tablet_manager_v2_interface, version: 1);
453 }
454 else if (strcmp (s1: interface, s2: "zxdg_exporter_v1") == 0)
455 {
456 display_wayland->xdg_exporter =
457 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id,
458 interface: &zxdg_exporter_v1_interface, version: 1);
459 }
460 else if (strcmp (s1: interface, s2: "zxdg_importer_v1") == 0)
461 {
462 display_wayland->xdg_importer =
463 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id,
464 interface: &zxdg_importer_v1_interface, version: 1);
465 }
466 else if (strcmp (s1: interface, s2: "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0)
467 {
468 display_wayland->keyboard_shortcuts_inhibit =
469 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id,
470 interface: &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, version: 1);
471 }
472 else if (strcmp (s1: interface, s2: "org_kde_kwin_server_decoration_manager") == 0)
473 {
474 display_wayland->server_decoration_manager =
475 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id,
476 interface: &org_kde_kwin_server_decoration_manager_interface, version: 1);
477 org_kde_kwin_server_decoration_manager_add_listener (display_wayland->server_decoration_manager,
478 &server_decoration_listener,
479 display_wayland);
480 }
481 else if (strcmp(s1: interface, s2: "zxdg_output_manager_v1") == 0)
482 {
483 display_wayland->xdg_output_manager_version = MIN (version, 3);
484 display_wayland->xdg_output_manager =
485 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id,
486 interface: &zxdg_output_manager_v1_interface,
487 version: display_wayland->xdg_output_manager_version);
488 gdk_wayland_display_init_xdg_output (display_wayland);
489 _gdk_wayland_display_async_roundtrip (display_wayland);
490 }
491 else if (strcmp(s1: interface, s2: "zwp_idle_inhibit_manager_v1") == 0)
492 {
493 display_wayland->idle_inhibit_manager =
494 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id,
495 interface: &zwp_idle_inhibit_manager_v1_interface, version: 1);
496 }
497 else if (strcmp (s1: interface, s2: "xdg_activation_v1") == 0)
498 {
499 display_wayland->xdg_activation_version =
500 MIN (version, XDG_ACTIVATION_VERSION);
501 display_wayland->xdg_activation =
502 wl_registry_bind (wl_registry: display_wayland->wl_registry, name: id,
503 interface: &xdg_activation_v1_interface,
504 version: display_wayland->xdg_activation_version);
505 }
506
507 g_hash_table_insert (hash_table: display_wayland->known_globals,
508 GUINT_TO_POINTER (id), value: g_strdup (str: interface));
509}
510
511static void
512gdk_registry_handle_global_remove (void *data,
513 struct wl_registry *registry,
514 uint32_t id)
515{
516 GdkWaylandDisplay *display_wayland = data;
517
518 GDK_NOTE (MISC, g_message ("remove global %u", id));
519 _gdk_wayland_display_remove_seat (display: display_wayland, id);
520 gdk_wayland_display_remove_output (display_wayland, id);
521
522 g_hash_table_remove (hash_table: display_wayland->known_globals, GUINT_TO_POINTER (id));
523
524 /* FIXME: the object needs to be destroyed here, we're leaking */
525}
526
527static const struct wl_registry_listener registry_listener = {
528 gdk_registry_handle_global,
529 gdk_registry_handle_global_remove
530};
531
532G_GNUC_PRINTF (1, 0)
533static void
534log_handler (const char *format, va_list args)
535{
536 g_logv (G_LOG_DOMAIN, log_level: G_LOG_LEVEL_DEBUG, format, args);
537}
538
539static void
540load_cursor_theme_closure_run (GdkWaylandDisplay *display_wayland,
541 OnHasGlobalsClosure *closure)
542{
543 _gdk_wayland_display_load_cursor_theme (display_wayland);
544}
545
546static void
547_gdk_wayland_display_prepare_cursor_themes (GdkWaylandDisplay *display_wayland)
548{
549 OnHasGlobalsClosure *closure;
550 static const char *required_cursor_theme_globals[] = {
551 "wl_shm",
552 NULL
553 };
554
555 closure = g_new0 (OnHasGlobalsClosure, 1);
556 closure->handler = load_cursor_theme_closure_run;
557 closure->required_globals = required_cursor_theme_globals;
558 postpone_on_globals_closure (display_wayland, closure);
559}
560
561static void init_settings (GdkDisplay *display);
562
563GdkDisplay *
564_gdk_wayland_display_open (const char *display_name)
565{
566 struct wl_display *wl_display;
567 GdkDisplay *display;
568 GdkWaylandDisplay *display_wayland;
569
570 GDK_NOTE (MISC, g_message ("opening display %s", display_name ? display_name : ""));
571
572 /* If this variable is unset then wayland initialisation will surely
573 * fail, logging a fatal error in the process. Save ourselves from
574 * that.
575 */
576 if (g_getenv (variable: "XDG_RUNTIME_DIR") == NULL)
577 return NULL;
578
579 wl_log_set_handler_client (handler: log_handler);
580
581 wl_display = wl_display_connect (name: display_name);
582 if (!wl_display)
583 return NULL;
584
585 display = g_object_new (GDK_TYPE_WAYLAND_DISPLAY, NULL);
586 display_wayland = GDK_WAYLAND_DISPLAY (display);
587 display_wayland->wl_display = wl_display;
588 display_wayland->event_source = _gdk_wayland_display_event_source_new (display);
589
590 init_settings (display);
591
592 display_wayland->known_globals =
593 g_hash_table_new_full (NULL, NULL, NULL, value_destroy_func: g_free);
594
595 _gdk_wayland_display_init_cursors (display: display_wayland);
596 _gdk_wayland_display_prepare_cursor_themes (display_wayland);
597
598 display_wayland->wl_registry = wl_display_get_registry (wl_display: display_wayland->wl_display);
599 wl_registry_add_listener (wl_registry: display_wayland->wl_registry, listener: &registry_listener, data: display_wayland);
600 if (wl_display_roundtrip (display: display_wayland->wl_display) < 0)
601 {
602 g_object_unref (object: display);
603 return NULL;
604 }
605
606 process_on_globals_closures (display_wayland);
607
608 /* Wait for initializing to complete. This means waiting for all
609 * asynchronous roundtrips that were triggered during initial roundtrip. */
610 while (display_wayland->async_roundtrips != NULL)
611 {
612 if (wl_display_dispatch (display: display_wayland->wl_display) < 0)
613 {
614 g_object_unref (object: display);
615 return NULL;
616 }
617 }
618
619 if (display_wayland->xdg_wm_base_id)
620 {
621 display_wayland->shell_variant = GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL;
622 display_wayland->xdg_wm_base =
623 wl_registry_bind (wl_registry: display_wayland->wl_registry,
624 name: display_wayland->xdg_wm_base_id,
625 interface: &xdg_wm_base_interface,
626 MIN (display_wayland->xdg_wm_base_version, 3));
627 xdg_wm_base_add_listener (display_wayland->xdg_wm_base,
628 &xdg_wm_base_listener,
629 display_wayland);
630 }
631 else if (display_wayland->zxdg_shell_v6_id)
632 {
633 display_wayland->shell_variant = GDK_WAYLAND_SHELL_VARIANT_ZXDG_SHELL_V6;
634 display_wayland->zxdg_shell_v6 =
635 wl_registry_bind (wl_registry: display_wayland->wl_registry,
636 name: display_wayland->zxdg_shell_v6_id,
637 interface: &zxdg_shell_v6_interface, version: 1);
638 zxdg_shell_v6_add_listener (display_wayland->zxdg_shell_v6,
639 &zxdg_shell_v6_listener,
640 display_wayland);
641 }
642 else
643 {
644 g_warning ("The Wayland compositor does not provide any supported shell interface, "
645 "not using Wayland display");
646 g_object_unref (object: display);
647
648 return NULL;
649 }
650
651 gdk_display_emit_opened (display);
652
653 return display;
654}
655
656static void
657destroy_toplevel (gpointer data)
658{
659 _gdk_surface_destroy (GDK_SURFACE (data), FALSE);
660}
661
662static void
663gdk_wayland_display_dispose (GObject *object)
664{
665 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (object);
666
667 g_list_free_full (list: display_wayland->toplevels, free_func: destroy_toplevel);
668
669 if (display_wayland->event_source)
670 {
671 g_source_destroy (source: display_wayland->event_source);
672 g_source_unref (source: display_wayland->event_source);
673 display_wayland->event_source = NULL;
674 }
675
676 g_list_free_full (list: display_wayland->async_roundtrips, free_func: (GDestroyNotify) wl_callback_destroy);
677
678 if (display_wayland->known_globals)
679 {
680 g_hash_table_destroy (hash_table: display_wayland->known_globals);
681 display_wayland->known_globals = NULL;
682 }
683
684 g_list_free_full (list: display_wayland->on_has_globals_closures, free_func: g_free);
685
686 G_OBJECT_CLASS (gdk_wayland_display_parent_class)->dispose (object);
687}
688
689static void
690gdk_wayland_display_finalize (GObject *object)
691{
692 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (object);
693
694 _gdk_wayland_display_finalize_cursors (display: display_wayland);
695
696 g_free (mem: display_wayland->startup_notification_id);
697 g_free (mem: display_wayland->cursor_theme_name);
698 xkb_context_unref (context: display_wayland->xkb_context);
699
700 g_clear_pointer (&display_wayland->cursor_theme, wl_cursor_theme_destroy);
701
702 g_list_store_remove_all (store: display_wayland->monitors);
703 g_object_unref (object: display_wayland->monitors);
704
705 if (display_wayland->settings)
706 g_hash_table_destroy (hash_table: display_wayland->settings);
707
708 g_clear_object (&display_wayland->settings_portal);
709
710 wl_display_disconnect (display: display_wayland->wl_display);
711
712 G_OBJECT_CLASS (gdk_wayland_display_parent_class)->finalize (object);
713}
714
715static const char *
716gdk_wayland_display_get_name (GdkDisplay *display)
717{
718 const char *name;
719
720 name = g_getenv (variable: "WAYLAND_DISPLAY");
721 if (name == NULL)
722 name = "wayland-0";
723
724 return name;
725}
726
727void
728gdk_wayland_display_system_bell (GdkDisplay *display,
729 GdkSurface *window)
730{
731 GdkWaylandDisplay *display_wayland;
732 struct gtk_surface1 *gtk_surface;
733 gint64 now_ms;
734
735 g_return_if_fail (GDK_IS_DISPLAY (display));
736
737 display_wayland = GDK_WAYLAND_DISPLAY (display);
738
739 if (!display_wayland->gtk_shell)
740 return;
741
742 if (window)
743 gtk_surface = gdk_wayland_surface_get_gtk_surface (surface: window);
744 else
745 gtk_surface = NULL;
746
747 now_ms = g_get_monotonic_time () / 1000;
748 if (now_ms - display_wayland->last_bell_time_ms < MIN_SYSTEM_BELL_DELAY_MS)
749 return;
750
751 display_wayland->last_bell_time_ms = now_ms;
752
753 gtk_shell1_system_bell (display_wayland->gtk_shell, gtk_surface);
754}
755
756static void
757gdk_wayland_display_beep (GdkDisplay *display)
758{
759 gdk_wayland_display_system_bell (display, NULL);
760}
761
762static void
763gdk_wayland_display_sync (GdkDisplay *display)
764{
765 GdkWaylandDisplay *display_wayland;
766
767 g_return_if_fail (GDK_IS_DISPLAY (display));
768
769 display_wayland = GDK_WAYLAND_DISPLAY (display);
770
771 wl_display_roundtrip (display: display_wayland->wl_display);
772}
773
774static void
775gdk_wayland_display_flush (GdkDisplay *display)
776{
777 g_return_if_fail (GDK_IS_DISPLAY (display));
778
779 if (!display->closed)
780 wl_display_flush (GDK_WAYLAND_DISPLAY (display)->wl_display);
781}
782
783static void
784gdk_wayland_display_make_default (GdkDisplay *display)
785{
786 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
787 const char *startup_id;
788
789 g_free (mem: display_wayland->startup_notification_id);
790 display_wayland->startup_notification_id = NULL;
791
792 startup_id = gdk_get_startup_notification_id ();
793 if (startup_id)
794 display_wayland->startup_notification_id = g_strdup (str: startup_id);
795}
796
797static gboolean
798gdk_wayland_display_has_pending (GdkDisplay *display)
799{
800 return FALSE;
801}
802
803static gulong
804gdk_wayland_display_get_next_serial (GdkDisplay *display)
805{
806 static gulong serial = 0;
807 return ++serial;
808}
809
810/**
811 * gdk_wayland_display_get_startup_notification_id:
812 * @display: (type GdkWaylandDisplay): a `GdkDisplay`
813 *
814 * Gets the startup notification ID for a Wayland display, or %NULL
815 * if no ID has been defined.
816 *
817 * Returns: (nullable): the startup notification ID for @display
818 */
819const char *
820gdk_wayland_display_get_startup_notification_id (GdkDisplay *display)
821{
822 return GDK_WAYLAND_DISPLAY (display)->startup_notification_id;
823}
824
825/**
826 * gdk_wayland_display_set_startup_notification_id:
827 * @display: (type GdkWaylandDisplay): a `GdkDisplay`
828 * @startup_id: the startup notification ID (must be valid utf8)
829 *
830 * Sets the startup notification ID for a display.
831 *
832 * This is usually taken from the value of the `DESKTOP_STARTUP_ID`
833 * environment variable, but in some cases (such as the application not
834 * being launched using exec()) it can come from other sources.
835 *
836 * The startup ID is also what is used to signal that the startup is
837 * complete (for example, when opening a window or when calling
838 * [method@Gdk.Display.notify_startup_complete]).
839 */
840void
841gdk_wayland_display_set_startup_notification_id (GdkDisplay *display,
842 const char *startup_id)
843{
844 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
845
846 g_free (mem: display_wayland->startup_notification_id);
847 display_wayland->startup_notification_id = g_strdup (str: startup_id);
848}
849
850static void
851gdk_wayland_display_notify_startup_complete (GdkDisplay *display,
852 const char *startup_id)
853{
854 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
855 char *free_this = NULL;
856
857 /* Will be signaled with focus activation */
858 if (display_wayland->xdg_activation)
859 return;
860
861 if (startup_id == NULL)
862 {
863 startup_id = free_this = display_wayland->startup_notification_id;
864 display_wayland->startup_notification_id = NULL;
865
866 if (startup_id == NULL)
867 return;
868 }
869
870 if (!display_wayland->xdg_activation && display_wayland->gtk_shell)
871 gtk_shell1_set_startup_id (display_wayland->gtk_shell, startup_id);
872
873 g_free (mem: free_this);
874}
875
876static GdkKeymap *
877_gdk_wayland_display_get_keymap (GdkDisplay *display)
878{
879 GdkSeat *seat;
880 GdkDevice *core_keyboard = NULL;
881 static GdkKeymap *tmp_keymap = NULL;
882
883 seat = gdk_display_get_default_seat (display);
884 if (seat)
885 core_keyboard = gdk_seat_get_keyboard (seat);
886
887 if (core_keyboard && tmp_keymap)
888 {
889 g_object_unref (object: tmp_keymap);
890 tmp_keymap = NULL;
891 }
892
893 if (core_keyboard)
894 return _gdk_wayland_device_get_keymap (device: core_keyboard);
895
896 if (!tmp_keymap)
897 tmp_keymap = _gdk_wayland_keymap_new (display);
898
899 return tmp_keymap;
900}
901
902static GListModel *
903gdk_wayland_display_get_monitors (GdkDisplay *display)
904{
905 GdkWaylandDisplay *self = GDK_WAYLAND_DISPLAY (display);
906
907 return G_LIST_MODEL (ptr: self->monitors);
908}
909
910static GdkMonitor *
911gdk_wayland_display_get_monitor_at_surface (GdkDisplay *display,
912 GdkSurface *window)
913{
914 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
915 struct wl_output *output;
916 guint i, n;
917
918 g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL);
919
920 output = gdk_wayland_surface_get_wl_output (surface: window);
921 if (output == NULL)
922 return NULL;
923
924 n = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: display_wayland->monitors));
925 for (i = 0; i < n; i++)
926 {
927 GdkMonitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: display_wayland->monitors), position: i);
928
929 g_object_unref (object: monitor);
930
931 if (gdk_wayland_monitor_get_wl_output (monitor) == output)
932 return monitor;
933 }
934
935 return NULL;
936}
937
938static gboolean gdk_wayland_display_get_setting (GdkDisplay *display,
939 const char *name,
940 GValue *value);
941
942static void
943gdk_wayland_display_class_init (GdkWaylandDisplayClass *class)
944{
945 GObjectClass *object_class = G_OBJECT_CLASS (class);
946 GdkDisplayClass *display_class = GDK_DISPLAY_CLASS (class);
947
948 object_class->dispose = gdk_wayland_display_dispose;
949 object_class->finalize = gdk_wayland_display_finalize;
950
951 display_class->cairo_context_type = GDK_TYPE_WAYLAND_CAIRO_CONTEXT;
952
953#ifdef GDK_RENDERING_VULKAN
954 display_class->vk_context_type = GDK_TYPE_WAYLAND_VULKAN_CONTEXT;
955 display_class->vk_extension_name = VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
956#endif
957
958 display_class->get_name = gdk_wayland_display_get_name;
959 display_class->beep = gdk_wayland_display_beep;
960 display_class->sync = gdk_wayland_display_sync;
961 display_class->flush = gdk_wayland_display_flush;
962 display_class->make_default = gdk_wayland_display_make_default;
963 display_class->has_pending = gdk_wayland_display_has_pending;
964 display_class->queue_events = _gdk_wayland_display_queue_events;
965 display_class->get_app_launch_context = _gdk_wayland_display_get_app_launch_context;
966 display_class->get_next_serial = gdk_wayland_display_get_next_serial;
967 display_class->get_startup_notification_id = gdk_wayland_display_get_startup_notification_id;
968 display_class->notify_startup_complete = gdk_wayland_display_notify_startup_complete;
969 display_class->create_surface = _gdk_wayland_display_create_surface;
970 display_class->get_keymap = _gdk_wayland_display_get_keymap;
971
972 display_class->init_gl = gdk_wayland_display_init_gl;
973
974 display_class->get_monitors = gdk_wayland_display_get_monitors;
975 display_class->get_monitor_at_surface = gdk_wayland_display_get_monitor_at_surface;
976 display_class->get_setting = gdk_wayland_display_get_setting;
977 display_class->set_cursor_theme = gdk_wayland_display_set_cursor_theme;
978}
979
980static void
981gdk_wayland_display_init (GdkWaylandDisplay *display)
982{
983 display->xkb_context = xkb_context_new (flags: 0);
984
985 display->monitors = g_list_store_new (GDK_TYPE_MONITOR);
986}
987
988GList *
989gdk_wayland_display_get_toplevel_surfaces (GdkDisplay *display)
990{
991 return GDK_WAYLAND_DISPLAY (display)->toplevels;
992}
993
994static struct wl_cursor_theme *
995try_load_theme (GdkWaylandDisplay *display_wayland,
996 const char *dir,
997 gboolean dotdir,
998 const char *name,
999 int size)
1000{
1001 struct wl_cursor_theme *theme = NULL;
1002 char *path;
1003
1004 path = g_build_filename (first_element: dir, dotdir ? ".icons" : "icons", name, "cursors", NULL);
1005
1006 if (g_file_test (filename: path, test: G_FILE_TEST_IS_DIR))
1007 theme = wl_cursor_theme_create (name: path, size, shm: display_wayland->shm);
1008
1009 g_free (mem: path);
1010
1011 return theme;
1012}
1013
1014static struct wl_cursor_theme *
1015get_cursor_theme (GdkWaylandDisplay *display_wayland,
1016 const char *name,
1017 int size)
1018{
1019 const char * const *xdg_data_dirs;
1020 struct wl_cursor_theme *theme = NULL;
1021 int i;
1022
1023 theme = try_load_theme (display_wayland, dir: g_get_user_data_dir (), FALSE, name, size);
1024 if (theme)
1025 return theme;
1026
1027 theme = try_load_theme (display_wayland, dir: g_get_home_dir (), TRUE, name, size);
1028 if (theme)
1029 return theme;
1030
1031 xdg_data_dirs = g_get_system_data_dirs ();
1032 for (i = 0; xdg_data_dirs[i]; i++)
1033 {
1034 theme = try_load_theme (display_wayland, dir: xdg_data_dirs[i], FALSE, name, size);
1035 if (theme)
1036 return theme;
1037 }
1038
1039 /* This may fall back to builtin cursors */
1040 return wl_cursor_theme_create (name: "/usr/share/icons/default/cursors", size, shm: display_wayland->shm);
1041}
1042
1043/**
1044 * gdk_wayland_display_set_cursor_theme:
1045 * @display: (type GdkWaylandDisplay): a `GdkDisplay`
1046 * @name: the new cursor theme
1047 * @size: the size to use for cursors
1048 *
1049 * Sets the cursor theme for the given @display.
1050 */
1051void
1052gdk_wayland_display_set_cursor_theme (GdkDisplay *display,
1053 const char *name,
1054 int size)
1055{
1056 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY(display);
1057 struct wl_cursor_theme *theme;
1058
1059 g_assert (display_wayland);
1060 g_assert (display_wayland->shm);
1061
1062 if (g_strcmp0 (str1: name, str2: display_wayland->cursor_theme_name) == 0 &&
1063 display_wayland->cursor_theme_size == size)
1064 return;
1065
1066 theme = get_cursor_theme (display_wayland, name, size);
1067
1068 if (theme == NULL)
1069 {
1070 g_warning ("Failed to load cursor theme %s", name);
1071 return;
1072 }
1073
1074 if (display_wayland->cursor_theme)
1075 {
1076 wl_cursor_theme_destroy (theme: display_wayland->cursor_theme);
1077 display_wayland->cursor_theme = NULL;
1078 }
1079 display_wayland->cursor_theme = theme;
1080 if (display_wayland->cursor_theme_name != NULL)
1081 g_free (mem: display_wayland->cursor_theme_name);
1082 display_wayland->cursor_theme_name = g_strdup (str: name);
1083 display_wayland->cursor_theme_size = size;
1084}
1085
1086struct wl_cursor_theme *
1087_gdk_wayland_display_get_cursor_theme (GdkWaylandDisplay *display_wayland)
1088{
1089 g_assert (display_wayland->cursor_theme_name);
1090
1091 return display_wayland->cursor_theme;
1092}
1093
1094static void
1095_gdk_wayland_display_load_cursor_theme (GdkWaylandDisplay *display_wayland)
1096{
1097 guint size;
1098 const char *name;
1099 GValue v = G_VALUE_INIT;
1100 gint64 before G_GNUC_UNUSED;
1101
1102 before = GDK_PROFILER_CURRENT_TIME;
1103
1104 g_assert (display_wayland);
1105 g_assert (display_wayland->shm);
1106
1107 g_value_init (value: &v, G_TYPE_INT);
1108 if (gdk_display_get_setting (GDK_DISPLAY (display_wayland), name: "gtk-cursor-theme-size", value: &v))
1109 size = g_value_get_int (value: &v);
1110 else
1111 size = 32;
1112 g_value_unset (value: &v);
1113
1114 g_value_init (value: &v, G_TYPE_STRING);
1115 if (gdk_display_get_setting (GDK_DISPLAY (display_wayland), name: "gtk-cursor-theme-name", value: &v))
1116 name = g_value_get_string (value: &v);
1117 else
1118 name = "default";
1119
1120 gdk_wayland_display_set_cursor_theme (GDK_DISPLAY (display_wayland), name, size);
1121 g_value_unset (value: &v);
1122
1123 gdk_profiler_end_mark (before, "wayland", "load cursor theme");
1124
1125}
1126
1127guint32
1128_gdk_wayland_display_get_serial (GdkWaylandDisplay *display_wayland)
1129{
1130 return display_wayland->serial;
1131}
1132
1133void
1134_gdk_wayland_display_update_serial (GdkWaylandDisplay *display_wayland,
1135 guint32 serial)
1136{
1137 display_wayland->serial = serial;
1138}
1139
1140/**
1141 * gdk_wayland_display_get_wl_display: (skip)
1142 * @display: (type GdkWaylandDisplay): a `GdkDisplay`
1143 *
1144 * Returns the Wayland `wl_display` of a `GdkDisplay`.
1145 *
1146 * Returns: (transfer none): a Wayland `wl_display`
1147 */
1148struct wl_display *
1149gdk_wayland_display_get_wl_display (GdkDisplay *display)
1150{
1151 g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), NULL);
1152
1153 return GDK_WAYLAND_DISPLAY (display)->wl_display;
1154}
1155
1156/**
1157 * gdk_wayland_display_get_wl_compositor: (skip)
1158 * @display: (type GdkWaylandDisplay): a `GdkDisplay`
1159 *
1160 * Returns the Wayland `wl_compositor` of a `GdkDisplay`.
1161 *
1162 * Returns: (transfer none): a Wayland `wl_compositor`
1163 */
1164struct wl_compositor *
1165gdk_wayland_display_get_wl_compositor (GdkDisplay *display)
1166{
1167 g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), NULL);
1168
1169 return GDK_WAYLAND_DISPLAY (display)->compositor;
1170}
1171
1172static const cairo_user_data_key_t gdk_wayland_shm_surface_cairo_key;
1173
1174typedef struct _GdkWaylandCairoSurfaceData {
1175 gpointer buf;
1176 size_t buf_length;
1177 struct wl_shm_pool *pool;
1178 struct wl_buffer *buffer;
1179 GdkWaylandDisplay *display;
1180 uint32_t scale;
1181} GdkWaylandCairoSurfaceData;
1182
1183static int
1184open_shared_memory (void)
1185{
1186 static gboolean force_shm_open = FALSE;
1187 int ret = -1;
1188
1189#if !defined (__NR_memfd_create)
1190 force_shm_open = TRUE;
1191#endif
1192
1193 do
1194 {
1195#if defined (__NR_memfd_create)
1196 if (!force_shm_open)
1197 {
1198 int options = MFD_CLOEXEC;
1199#if defined (MFD_ALLOW_SEALING)
1200 options |= MFD_ALLOW_SEALING;
1201#endif
1202 ret = syscall (__NR_memfd_create, "gdk-wayland", options);
1203
1204 /* fall back to shm_open until debian stops shipping 3.16 kernel
1205 * See bug 766341
1206 */
1207 if (ret < 0 && errno == ENOSYS)
1208 force_shm_open = TRUE;
1209#if defined (F_ADD_SEALS) && defined (F_SEAL_SHRINK)
1210 if (ret >= 0)
1211 fcntl (fd: ret, F_ADD_SEALS, F_SEAL_SHRINK);
1212#endif
1213 }
1214#endif
1215
1216 if (force_shm_open)
1217 {
1218#if defined (__FreeBSD__)
1219 ret = shm_open (SHM_ANON, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
1220#else
1221 char name[NAME_MAX - 1] = "";
1222
1223 sprintf (s: name, format: "/gdk-wayland-%x", g_random_int ());
1224
1225 ret = shm_open (name: name, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, mode: 0600);
1226
1227 if (ret >= 0)
1228 shm_unlink (name: name);
1229 else if (errno == EEXIST)
1230 continue;
1231#endif
1232 }
1233 }
1234 while (ret < 0 && errno == EINTR);
1235
1236 if (ret < 0)
1237 g_critical (G_STRLOC ": creating shared memory file (using %s) failed: %m",
1238 force_shm_open? "shm_open" : "memfd_create");
1239
1240 return ret;
1241}
1242
1243static struct wl_shm_pool *
1244create_shm_pool (struct wl_shm *shm,
1245 int size,
1246 size_t *buf_length,
1247 void **data_out)
1248{
1249 struct wl_shm_pool *pool;
1250 int fd;
1251 void *data;
1252
1253 fd = open_shared_memory ();
1254
1255 if (fd < 0)
1256 goto fail;
1257
1258 if (ftruncate (fd: fd, length: size) < 0)
1259 {
1260 g_critical (G_STRLOC ": Truncating shared memory file failed: %m");
1261 close (fd: fd);
1262 goto fail;
1263 }
1264
1265 data = mmap (NULL, len: size, PROT_READ | PROT_WRITE, MAP_SHARED, fd: fd, offset: 0);
1266
1267 if (data == MAP_FAILED)
1268 {
1269 g_critical (G_STRLOC ": mmap'ping shared memory file failed: %m");
1270 close (fd: fd);
1271 goto fail;
1272 }
1273
1274 pool = wl_shm_create_pool (wl_shm: shm, fd, size);
1275
1276 close (fd: fd);
1277
1278 *data_out = data;
1279 *buf_length = size;
1280
1281 return pool;
1282
1283fail:
1284 *data_out = NULL;
1285 *buf_length = 0;
1286 return NULL;
1287}
1288
1289static void
1290gdk_wayland_cairo_surface_destroy (void *p)
1291{
1292 GdkWaylandCairoSurfaceData *data = p;
1293
1294 if (data->buffer)
1295 wl_buffer_destroy (wl_buffer: data->buffer);
1296
1297 if (data->pool)
1298 wl_shm_pool_destroy (wl_shm_pool: data->pool);
1299
1300 munmap (addr: data->buf, len: data->buf_length);
1301 g_free (mem: data);
1302}
1303
1304cairo_surface_t *
1305_gdk_wayland_display_create_shm_surface (GdkWaylandDisplay *display,
1306 int width,
1307 int height,
1308 guint scale)
1309{
1310 GdkWaylandCairoSurfaceData *data;
1311 cairo_surface_t *surface = NULL;
1312 cairo_status_t status;
1313 int stride;
1314
1315 data = g_new (GdkWaylandCairoSurfaceData, 1);
1316 data->display = display;
1317 data->buffer = NULL;
1318 data->scale = scale;
1319
1320 stride = cairo_format_stride_for_width (format: CAIRO_FORMAT_ARGB32, width: width * scale);
1321
1322 data->pool = create_shm_pool (shm: display->shm,
1323 size: height * scale * stride,
1324 buf_length: &data->buf_length,
1325 data_out: &data->buf);
1326 if (G_UNLIKELY (data->pool == NULL))
1327 g_error ("Unable to create shared memory pool");
1328
1329 surface = cairo_image_surface_create_for_data (data: data->buf,
1330 format: CAIRO_FORMAT_ARGB32,
1331 width: width * scale,
1332 height: height * scale,
1333 stride);
1334
1335 data->buffer = wl_shm_pool_create_buffer (wl_shm_pool: data->pool, offset: 0,
1336 width: width * scale, height: height * scale,
1337 stride, format: WL_SHM_FORMAT_ARGB8888);
1338
1339 cairo_surface_set_user_data (surface, key: &gdk_wayland_shm_surface_cairo_key,
1340 user_data: data, destroy: gdk_wayland_cairo_surface_destroy);
1341
1342 cairo_surface_set_device_scale (surface, x_scale: scale, y_scale: scale);
1343
1344 status = cairo_surface_status (surface);
1345 if (status != CAIRO_STATUS_SUCCESS)
1346 {
1347 g_critical (G_STRLOC ": Unable to create Cairo image surface: %s",
1348 cairo_status_to_string (status));
1349 }
1350
1351 return surface;
1352}
1353
1354struct wl_buffer *
1355_gdk_wayland_shm_surface_get_wl_buffer (cairo_surface_t *surface)
1356{
1357 GdkWaylandCairoSurfaceData *data = cairo_surface_get_user_data (surface, key: &gdk_wayland_shm_surface_cairo_key);
1358 return data->buffer;
1359}
1360
1361gboolean
1362_gdk_wayland_is_shm_surface (cairo_surface_t *surface)
1363{
1364 return cairo_surface_get_user_data (surface, key: &gdk_wayland_shm_surface_cairo_key) != NULL;
1365}
1366
1367typedef enum
1368{
1369 GSD_FONT_ANTIALIASING_MODE_NONE,
1370 GSD_FONT_ANTIALIASING_MODE_GRAYSCALE,
1371 GSD_FONT_ANTIALIASING_MODE_RGBA
1372} GsdFontAntialiasingMode;
1373
1374static int
1375get_antialiasing (const char *s)
1376{
1377 const char *names[] = { "none", "grayscale", "rgba" };
1378 int i;
1379
1380 for (i = 0; i < G_N_ELEMENTS (names); i++)
1381 if (strcmp (s1: s, s2: names[i]) == 0)
1382 return i;
1383
1384 return 0;
1385}
1386
1387typedef enum
1388{
1389 GSD_FONT_HINTING_NONE,
1390 GSD_FONT_HINTING_SLIGHT,
1391 GSD_FONT_HINTING_MEDIUM,
1392 GSD_FONT_HINTING_FULL
1393} GsdFontHinting;
1394
1395static int
1396get_hinting (const char *s)
1397{
1398 const char *names[] = { "none", "slight", "medium", "full" };
1399 int i;
1400
1401 for (i = 0; i < G_N_ELEMENTS (names); i++)
1402 if (strcmp (s1: s, s2: names[i]) == 0)
1403 return i;
1404
1405 return 0;
1406}
1407
1408typedef enum
1409{
1410 GSD_FONT_RGBA_ORDER_RGBA,
1411 GSD_FONT_RGBA_ORDER_RGB,
1412 GSD_FONT_RGBA_ORDER_BGR,
1413 GSD_FONT_RGBA_ORDER_VRGB,
1414 GSD_FONT_RGBA_ORDER_VBGR
1415} GsdFontRgbaOrder;
1416
1417static int
1418get_order (const char *s)
1419{
1420 const char *names[] = { "rgba", "rgb", "bgr", "vrgb", "vbgr" };
1421 int i;
1422
1423 for (i = 0; i < G_N_ELEMENTS (names); i++)
1424 if (strcmp (s1: s, s2: names[i]) == 0)
1425 return i;
1426
1427 return 0;
1428}
1429
1430static double
1431get_dpi_from_gsettings (GdkWaylandDisplay *display_wayland)
1432{
1433 GSettings *settings;
1434 double factor;
1435
1436 settings = g_hash_table_lookup (hash_table: display_wayland->settings,
1437 key: "org.gnome.desktop.interface");
1438 if (settings != NULL)
1439 factor = g_settings_get_double (settings, key: "text-scaling-factor");
1440 else
1441 factor = 1.0;
1442
1443 return 96.0 * factor;
1444}
1445
1446/* When using the Settings portal, we cache the value in
1447 * the fallback member, and we ignore the valid field
1448 */
1449typedef struct _TranslationEntry TranslationEntry;
1450struct _TranslationEntry {
1451 gboolean valid;
1452 const char *schema;
1453 const char *key;
1454 const char *setting;
1455 GType type;
1456 union {
1457 const char *s;
1458 int i;
1459 gboolean b;
1460 } fallback;
1461};
1462
1463static TranslationEntry * find_translation_entry_by_schema (const char *schema,
1464 const char *key);
1465
1466static void
1467update_xft_settings (GdkDisplay *display)
1468{
1469 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
1470 GSettings *settings;
1471 GsdFontAntialiasingMode antialiasing;
1472 GsdFontHinting hinting;
1473 GsdFontRgbaOrder order;
1474 gboolean use_rgba = FALSE;
1475 GsdXftSettings xft_settings;
1476 double dpi;
1477
1478 if (display_wayland->settings_portal)
1479 {
1480 TranslationEntry *entry;
1481
1482 entry = find_translation_entry_by_schema (schema: "org.gnome.desktop.interface", key: "font-antialiasing");
1483 g_assert (entry);
1484
1485 if (entry->valid)
1486 {
1487 antialiasing = entry->fallback.i;
1488
1489 entry = find_translation_entry_by_schema (schema: "org.gnome.desktop.interface", key: "font-hinting");
1490 g_assert (entry);
1491 hinting = entry->fallback.i;
1492
1493 entry = find_translation_entry_by_schema (schema: "org.gnome.desktop.interface", key: "font-rgba-order");
1494 g_assert (entry);
1495 order = entry->fallback.i;
1496 }
1497 else
1498 {
1499 entry = find_translation_entry_by_schema (schema: "org.gnome.settings-daemon.plugins.xsettings", key: "antialiasing");
1500 g_assert (entry);
1501 antialiasing = entry->fallback.i;
1502
1503 entry = find_translation_entry_by_schema (schema: "org.gnome.settings-daemon.plugins.xsettings", key: "hinting");
1504 g_assert (entry);
1505 hinting = entry->fallback.i;
1506
1507 entry = find_translation_entry_by_schema (schema: "org.gnome.settings-daemon.plugins.xsettings", key: "rgba-order");
1508 g_assert (entry);
1509 order = entry->fallback.i;
1510 }
1511
1512 entry = find_translation_entry_by_schema (schema: "org.gnome.desktop.interface", key: "text-scaling-factor");
1513 g_assert (entry);
1514 dpi = 96.0 * entry->fallback.i / 65536.0 * 1024; /* Xft wants 1/1024th of an inch */
1515 }
1516 else
1517 {
1518 TranslationEntry *entry;
1519
1520 entry = find_translation_entry_by_schema (schema: "org.gnome.desktop.interface", key: "font-antialiasing");
1521
1522 if (entry && entry->valid)
1523 {
1524 settings = g_hash_table_lookup (hash_table: display_wayland->settings,
1525 key: "org.gnome.desktop.interface");
1526 antialiasing = g_settings_get_enum (settings, key: "font-antialiasing");
1527 hinting = g_settings_get_enum (settings, key: "font-hinting");
1528 order = g_settings_get_enum (settings, key: "font-rgba-order");
1529 }
1530 else if (g_hash_table_contains (hash_table: display_wayland->settings,
1531 key: "org.gnome.settings-daemon.plugins.xsettings"))
1532 {
1533 settings = g_hash_table_lookup (hash_table: display_wayland->settings,
1534 key: "org.gnome.settings-daemon.plugins.xsettings");
1535 antialiasing = g_settings_get_enum (settings, key: "antialiasing");
1536 hinting = g_settings_get_enum (settings, key: "hinting");
1537 order = g_settings_get_enum (settings, key: "rgba-order");
1538 }
1539 else
1540 {
1541 antialiasing = GSD_FONT_ANTIALIASING_MODE_GRAYSCALE;
1542 hinting = GSD_FONT_HINTING_MEDIUM;
1543 order = GSD_FONT_RGBA_ORDER_RGB;
1544 }
1545
1546 dpi = get_dpi_from_gsettings (display_wayland) * 1024;
1547 }
1548
1549 xft_settings.hinting = (hinting != GSD_FONT_HINTING_NONE);
1550 xft_settings.dpi = dpi;
1551
1552 switch (hinting)
1553 {
1554 case GSD_FONT_HINTING_NONE:
1555 xft_settings.hintstyle = "hintnone";
1556 break;
1557 case GSD_FONT_HINTING_SLIGHT:
1558 xft_settings.hintstyle = "hintslight";
1559 break;
1560 case GSD_FONT_HINTING_MEDIUM:
1561 xft_settings.hintstyle = "hintmedium";
1562 break;
1563 case GSD_FONT_HINTING_FULL:
1564 default:
1565 xft_settings.hintstyle = "hintfull";
1566 break;
1567 }
1568
1569 switch (order)
1570 {
1571 case GSD_FONT_RGBA_ORDER_RGBA:
1572 xft_settings.rgba = "rgba";
1573 break;
1574 default:
1575 case GSD_FONT_RGBA_ORDER_RGB:
1576 xft_settings.rgba = "rgb";
1577 break;
1578 case GSD_FONT_RGBA_ORDER_BGR:
1579 xft_settings.rgba = "bgr";
1580 break;
1581 case GSD_FONT_RGBA_ORDER_VRGB:
1582 xft_settings.rgba = "vrgb";
1583 break;
1584 case GSD_FONT_RGBA_ORDER_VBGR:
1585 xft_settings.rgba = "vbgr";
1586 break;
1587 }
1588
1589 switch (antialiasing)
1590 {
1591 default:
1592 case GSD_FONT_ANTIALIASING_MODE_NONE:
1593 xft_settings.antialias = FALSE;
1594 break;
1595 case GSD_FONT_ANTIALIASING_MODE_GRAYSCALE:
1596 xft_settings.antialias = TRUE;
1597 break;
1598 case GSD_FONT_ANTIALIASING_MODE_RGBA:
1599 xft_settings.antialias = TRUE;
1600 use_rgba = TRUE;
1601 }
1602
1603 if (!use_rgba)
1604 xft_settings.rgba = "none";
1605
1606 if (display_wayland->xft_settings.antialias != xft_settings.antialias)
1607 {
1608 display_wayland->xft_settings.antialias = xft_settings.antialias;
1609 gdk_display_setting_changed (display, name: "gtk-xft-antialias");
1610 }
1611
1612 if (display_wayland->xft_settings.hinting != xft_settings.hinting)
1613 {
1614 display_wayland->xft_settings.hinting = xft_settings.hinting;
1615 gdk_display_setting_changed (display, name: "gtk-xft-hinting");
1616 }
1617
1618 if (display_wayland->xft_settings.hintstyle != xft_settings.hintstyle)
1619 {
1620 display_wayland->xft_settings.hintstyle = xft_settings.hintstyle;
1621 gdk_display_setting_changed (display, name: "gtk-xft-hintstyle");
1622 }
1623
1624 if (display_wayland->xft_settings.rgba != xft_settings.rgba)
1625 {
1626 display_wayland->xft_settings.rgba = xft_settings.rgba;
1627 gdk_display_setting_changed (display, name: "gtk-xft-rgba");
1628 }
1629
1630 if (display_wayland->xft_settings.dpi != xft_settings.dpi)
1631 {
1632 display_wayland->xft_settings.dpi = xft_settings.dpi;
1633 gdk_display_setting_changed (display, name: "gtk-xft-dpi");
1634 }
1635}
1636
1637static TranslationEntry translations[] = {
1638 { FALSE, "org.gnome.desktop.interface", "gtk-theme", "gtk-theme-name" , G_TYPE_STRING, { .s = "Adwaita" } },
1639 { FALSE, "org.gnome.desktop.interface", "icon-theme", "gtk-icon-theme-name", G_TYPE_STRING, { .s = "gnome" } },
1640 { FALSE, "org.gnome.desktop.interface", "cursor-theme", "gtk-cursor-theme-name", G_TYPE_STRING, { .s = "Adwaita" } },
1641 { FALSE, "org.gnome.desktop.interface", "cursor-size", "gtk-cursor-theme-size", G_TYPE_INT, { .i = 32 } },
1642 { FALSE, "org.gnome.desktop.interface", "font-name", "gtk-font-name", G_TYPE_STRING, { .s = "Cantarell 11" } },
1643 { FALSE, "org.gnome.desktop.interface", "cursor-blink", "gtk-cursor-blink", G_TYPE_BOOLEAN, { .b = TRUE } },
1644 { FALSE, "org.gnome.desktop.interface", "cursor-blink-time", "gtk-cursor-blink-time", G_TYPE_INT, { .i = 1200 } },
1645 { FALSE, "org.gnome.desktop.interface", "cursor-blink-timeout", "gtk-cursor-blink-timeout", G_TYPE_INT, { .i = 3600 } },
1646 { FALSE, "org.gnome.desktop.interface", "gtk-im-module", "gtk-im-module", G_TYPE_STRING, { .s = "simple" } },
1647 { FALSE, "org.gnome.desktop.interface", "enable-animations", "gtk-enable-animations", G_TYPE_BOOLEAN, { .b = TRUE } },
1648 { FALSE, "org.gnome.desktop.interface", "gtk-enable-primary-paste", "gtk-enable-primary-paste", G_TYPE_BOOLEAN, { .b = TRUE } },
1649 { FALSE, "org.gnome.desktop.interface", "overlay-scrolling", "gtk-overlay-scrolling", G_TYPE_BOOLEAN, { .b = TRUE } },
1650 { FALSE, "org.gnome.desktop.peripherals.mouse", "double-click", "gtk-double-click-time", G_TYPE_INT, { .i = 400 } },
1651 { FALSE, "org.gnome.desktop.peripherals.mouse", "drag-threshold", "gtk-dnd-drag-threshold", G_TYPE_INT, {.i = 8 } },
1652 { FALSE, "org.gnome.settings-daemon.peripherals.mouse", "double-click", "gtk-double-click-time", G_TYPE_INT, { .i = 400 } },
1653 { FALSE, "org.gnome.settings-daemon.peripherals.mouse", "drag-threshold", "gtk-dnd-drag-threshold", G_TYPE_INT, {.i = 8 } },
1654 { FALSE, "org.gnome.desktop.sound", "theme-name", "gtk-sound-theme-name", G_TYPE_STRING, { .s = "freedesktop" } },
1655 { FALSE, "org.gnome.desktop.sound", "event-sounds", "gtk-enable-event-sounds", G_TYPE_BOOLEAN, { .b = TRUE } },
1656 { FALSE, "org.gnome.desktop.sound", "input-feedback-sounds", "gtk-enable-input-feedback-sounds", G_TYPE_BOOLEAN, { . b = FALSE } },
1657 { FALSE, "org.gnome.desktop.privacy", "recent-files-max-age", "gtk-recent-files-max-age", G_TYPE_INT, { .i = 30 } },
1658 { FALSE, "org.gnome.desktop.privacy", "remember-recent-files", "gtk-recent-files-enabled", G_TYPE_BOOLEAN, { .b = TRUE } },
1659 { FALSE, "org.gnome.desktop.wm.preferences", "button-layout", "gtk-decoration-layout", G_TYPE_STRING, { .s = "menu:close" } },
1660 { FALSE, "org.gnome.desktop.interface", "font-antialiasing", "gtk-xft-antialias", G_TYPE_NONE, { .i = 1 } },
1661 { FALSE, "org.gnome.desktop.interface", "font-hinting", "gtk-xft-hinting", G_TYPE_NONE, { .i = 1 } },
1662 { FALSE, "org.gnome.desktop.interface", "font-hinting", "gtk-xft-hintstyle", G_TYPE_NONE, { .i = 1 } },
1663 { FALSE, "org.gnome.desktop.interface", "font-rgba-order", "gtk-xft-rgba", G_TYPE_NONE, { .i = 0 } },
1664 { FALSE, "org.gnome.settings-daemon.plugins.xsettings", "antialiasing", "gtk-xft-antialias", G_TYPE_NONE, { .i = 1 } },
1665 { FALSE, "org.gnome.settings-daemon.plugins.xsettings", "hinting", "gtk-xft-hinting", G_TYPE_NONE, { .i = 1 } },
1666 { FALSE, "org.gnome.settings-daemon.plugins.xsettings", "hinting", "gtk-xft-hintstyle", G_TYPE_NONE, { .i = 1 } },
1667 { FALSE, "org.gnome.settings-daemon.plugins.xsettings", "rgba-order", "gtk-xft-rgba", G_TYPE_NONE, { .i = 0 } },
1668 { FALSE, "org.gnome.desktop.interface", "text-scaling-factor", "gtk-xft-dpi" , G_TYPE_NONE, { .i = 0 } }, /* We store the factor as 16.16 */
1669 { FALSE, "org.gnome.desktop.wm.preferences", "action-double-click-titlebar", "gtk-titlebar-double-click", G_TYPE_STRING, { .s = "toggle-maximize" } },
1670 { FALSE, "org.gnome.desktop.wm.preferences", "action-middle-click-titlebar", "gtk-titlebar-middle-click", G_TYPE_STRING, { .s = "none" } },
1671 { FALSE, "org.gnome.desktop.wm.preferences", "action-right-click-titlebar", "gtk-titlebar-right-click", G_TYPE_STRING, { .s = "menu" } },
1672 { FALSE, "org.gnome.desktop.a11y", "always-show-text-caret", "gtk-keynav-use-caret", G_TYPE_BOOLEAN, { .b = FALSE } },
1673 { FALSE, "org.gnome.desktop.a11y.interface", "high-contrast", "high-contast", G_TYPE_NONE, { .b = FALSE } },
1674 /* Note, this setting doesn't exist, the portal and gsd fake it */
1675 { FALSE, "org.gnome.fontconfig", "serial", "gtk-fontconfig-timestamp", G_TYPE_NONE, { .i = 0 } },
1676};
1677
1678
1679static TranslationEntry *
1680find_translation_entry_by_schema (const char *schema,
1681 const char *key)
1682{
1683 guint i;
1684
1685 for (i = 0; i < G_N_ELEMENTS (translations); i++)
1686 {
1687 if (g_str_equal (v1: schema, v2: translations[i].schema) &&
1688 g_str_equal (v1: key, v2: translations[i].key))
1689 return &translations[i];
1690 }
1691
1692 return NULL;
1693}
1694
1695static TranslationEntry *
1696find_translation_entry_by_key (GSettings *settings,
1697 const char *key)
1698{
1699 char *schema;
1700 TranslationEntry *entry;
1701
1702 g_object_get (object: settings, first_property_name: "schema", &schema, NULL);
1703 entry = find_translation_entry_by_schema (schema, key);
1704 g_free (mem: schema);
1705
1706 return entry;
1707}
1708
1709static TranslationEntry *
1710find_translation_entry_by_setting (const char *setting)
1711{
1712 guint i;
1713
1714 for (i = 0; i < G_N_ELEMENTS (translations); i++)
1715 {
1716 if (g_str_equal (v1: setting, v2: translations[i].setting))
1717 return &translations[i];
1718 }
1719
1720 return NULL;
1721}
1722
1723static void
1724high_contrast_changed (GdkDisplay *display)
1725{
1726 gdk_display_setting_changed (display, name: "gtk-theme-name");
1727 gdk_display_setting_changed (display, name: "gtk-icon-theme-name");
1728}
1729
1730static void
1731settings_changed (GSettings *settings,
1732 const char *key,
1733 GdkDisplay *display)
1734{
1735 TranslationEntry *entry;
1736
1737 entry = find_translation_entry_by_key (settings, key);
1738
1739 if (entry != NULL)
1740 {
1741 if (entry->type != G_TYPE_NONE)
1742 gdk_display_setting_changed (display, name: entry->setting);
1743 else if (strcmp (s1: key, s2: "high-contrast") == 0)
1744 high_contrast_changed (display);
1745 else
1746 update_xft_settings (display);
1747 }
1748}
1749
1750static void
1751apply_portal_setting (TranslationEntry *entry,
1752 GVariant *value,
1753 GdkDisplay *display)
1754{
1755 switch (entry->type)
1756 {
1757 case G_TYPE_STRING:
1758 entry->fallback.s = g_intern_string (string: g_variant_get_string (value, NULL));
1759 break;
1760 case G_TYPE_INT:
1761 entry->fallback.i = g_variant_get_int32 (value);
1762 break;
1763 case G_TYPE_BOOLEAN:
1764 entry->fallback.b = g_variant_get_boolean (value);
1765 break;
1766 case G_TYPE_NONE:
1767 if (strcmp (s1: entry->key, s2: "serial") == 0)
1768 {
1769 entry->fallback.i = g_variant_get_int32 (value);
1770 break;
1771 }
1772 if (strcmp (s1: entry->key, s2: "antialiasing") == 0 ||
1773 strcmp (s1: entry->key, s2: "font-antialiasing") == 0)
1774 entry->fallback.i = get_antialiasing (s: g_variant_get_string (value, NULL));
1775 else if (strcmp (s1: entry->key, s2: "hinting") == 0 ||
1776 strcmp (s1: entry->key, s2: "font-hinting") == 0)
1777 entry->fallback.i = get_hinting (s: g_variant_get_string (value, NULL));
1778 else if (strcmp (s1: entry->key, s2: "rgba-order") == 0 ||
1779 strcmp (s1: entry->key, s2: "font-rgba-order") == 0)
1780 entry->fallback.i = get_order (s: g_variant_get_string (value, NULL));
1781 else if (strcmp (s1: entry->key, s2: "text-scaling-factor") == 0)
1782 entry->fallback.i = (int) (g_variant_get_double (value) * 65536.0);
1783 update_xft_settings (display);
1784 break;
1785 default:
1786 break;
1787 }
1788}
1789
1790static void
1791settings_portal_changed (GDBusProxy *proxy,
1792 const char *sender_name,
1793 const char *signal_name,
1794 GVariant *parameters,
1795 GdkDisplay *display)
1796{
1797 if (strcmp (s1: signal_name, s2: "SettingChanged") == 0)
1798 {
1799 const char *namespace;
1800 const char *name;
1801 GVariant *value;
1802 TranslationEntry *entry;
1803
1804 g_variant_get (value: parameters, format_string: "(&s&sv)", &namespace, &name, &value);
1805
1806 entry = find_translation_entry_by_schema (schema: namespace, key: name);
1807 if (entry != NULL)
1808 {
1809 char *a = g_variant_print (value, FALSE);
1810 g_debug ("Using changed portal setting %s %s: %s", namespace, name, a);
1811 g_free (mem: a);
1812 entry->valid = TRUE;
1813 apply_portal_setting (entry, value, display);
1814 gdk_display_setting_changed (display, name: entry->setting);
1815 }
1816 else
1817 g_debug ("Ignoring portal setting %s %s", namespace, name);
1818
1819 g_variant_unref (value);
1820 }
1821}
1822
1823#define PORTAL_BUS_NAME "org.freedesktop.portal.Desktop"
1824#define PORTAL_OBJECT_PATH "/org/freedesktop/portal/desktop"
1825#define PORTAL_SETTINGS_INTERFACE "org.freedesktop.portal.Settings"
1826
1827static void
1828init_settings (GdkDisplay *display)
1829{
1830 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
1831 GSettingsSchemaSource *source;
1832 GSettingsSchema *schema;
1833 GSettings *settings;
1834 int i;
1835
1836 if (gdk_should_use_portal ())
1837 {
1838 GVariant *ret;
1839 GError *error = NULL;
1840 const char *schema_str;
1841 GVariant *val;
1842 GVariantIter *iter;
1843 const char *patterns[] = { "org.gnome.*", NULL };
1844
1845 display_wayland->settings_portal = g_dbus_proxy_new_for_bus_sync (bus_type: G_BUS_TYPE_SESSION,
1846 flags: G_DBUS_PROXY_FLAGS_NONE,
1847 NULL,
1848 PORTAL_BUS_NAME,
1849 PORTAL_OBJECT_PATH,
1850 PORTAL_SETTINGS_INTERFACE,
1851 NULL,
1852 error: &error);
1853 if (error)
1854 {
1855 g_warning ("Settings portal not found: %s", error->message);
1856 g_error_free (error);
1857
1858 goto fallback;
1859 }
1860
1861 ret = g_dbus_proxy_call_sync (proxy: display_wayland->settings_portal,
1862 method_name: "ReadAll",
1863 parameters: g_variant_new (format_string: "(^as)", patterns),
1864 flags: G_DBUS_CALL_FLAGS_NONE,
1865 G_MAXINT,
1866 NULL,
1867 error: &error);
1868
1869 if (error)
1870 {
1871 g_warning ("Failed to read portal settings: %s", error->message);
1872 g_error_free (error);
1873 g_clear_object (&display_wayland->settings_portal);
1874
1875 goto fallback;
1876 }
1877
1878 g_variant_get (value: ret, format_string: "(a{sa{sv}})", &iter);
1879
1880 if (g_variant_n_children (value: ret) == 0)
1881 {
1882 g_debug ("Received no portal settings");
1883 g_clear_pointer (&ret, g_variant_unref);
1884
1885 goto fallback;
1886 }
1887
1888 while (g_variant_iter_loop (iter, format_string: "{s@a{sv}}", &schema_str, &val))
1889 {
1890 GVariantIter *iter2 = g_variant_iter_new (value: val);
1891 const char *key;
1892 GVariant *v;
1893
1894 while (g_variant_iter_loop (iter: iter2, format_string: "{sv}", &key, &v))
1895 {
1896 TranslationEntry *entry = find_translation_entry_by_schema (schema: schema_str, key);
1897 if (entry)
1898 {
1899 char *a = g_variant_print (value: v, FALSE);
1900 g_debug ("Using portal setting for %s %s: %s\n", schema_str, key, a);
1901 g_free (mem: a);
1902 entry->valid = TRUE;
1903 apply_portal_setting (entry, value: v, display);
1904 }
1905 else
1906 {
1907 g_debug ("Ignoring portal setting for %s %s", schema_str, key);
1908 }
1909 }
1910 g_variant_iter_free (iter: iter2);
1911 }
1912 g_variant_iter_free (iter);
1913
1914 g_variant_unref (value: ret);
1915
1916 g_signal_connect (display_wayland->settings_portal, "g-signal",
1917 G_CALLBACK (settings_portal_changed), display_wayland);
1918
1919 return;
1920
1921fallback:
1922 g_debug ("Failed to use Settings portal; falling back to gsettings");
1923 }
1924
1925 g_intern_static_string (string: "antialiasing");
1926 g_intern_static_string (string: "hinting");
1927 g_intern_static_string (string: "rgba-order");
1928 g_intern_static_string (string: "text-scaling-factor");
1929
1930 display_wayland->settings = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, NULL, value_destroy_func: g_object_unref);
1931
1932 source = g_settings_schema_source_get_default ();
1933 if (source == NULL)
1934 return;
1935
1936 for (i = 0; i < G_N_ELEMENTS (translations); i++)
1937 {
1938 schema = g_settings_schema_source_lookup (source, schema_id: translations[i].schema, TRUE);
1939 if (!schema)
1940 continue;
1941
1942 g_intern_static_string (string: translations[i].key);
1943
1944 if (g_hash_table_lookup (hash_table: display_wayland->settings, key: (gpointer)translations[i].schema) == NULL)
1945 {
1946 settings = g_settings_new_full (schema, NULL, NULL);
1947 g_signal_connect (settings, "changed",
1948 G_CALLBACK (settings_changed), display);
1949 g_hash_table_insert (hash_table: display_wayland->settings, key: (gpointer)translations[i].schema, value: settings);
1950 }
1951
1952 if (g_settings_schema_has_key (schema, name: translations[i].key))
1953 translations[i].valid = TRUE;
1954
1955 g_settings_schema_unref (schema);
1956 }
1957
1958 update_xft_settings (display);
1959}
1960
1961static void
1962gtk_shell_handle_capabilities (void *data,
1963 struct gtk_shell1 *shell,
1964 uint32_t capabilities)
1965{
1966 GdkDisplay *display = data;
1967 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (data);
1968
1969 display_wayland->shell_capabilities = capabilities;
1970
1971 gdk_display_setting_changed (display, name: "gtk-shell-shows-app-menu");
1972 gdk_display_setting_changed (display, name: "gtk-shell-shows-menubar");
1973 gdk_display_setting_changed (display, name: "gtk-shell-shows-desktop");
1974}
1975
1976struct gtk_shell1_listener gdk_display_gtk_shell_listener = {
1977 gtk_shell_handle_capabilities
1978};
1979
1980static void
1981gdk_wayland_display_set_has_gtk_shell (GdkWaylandDisplay *display_wayland)
1982{
1983 gtk_shell1_add_listener (display_wayland->gtk_shell,
1984 &gdk_display_gtk_shell_listener,
1985 display_wayland);
1986}
1987
1988static void
1989set_value_from_entry (GdkDisplay *display,
1990 TranslationEntry *entry,
1991 GValue *value)
1992{
1993 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
1994 GSettings *settings;
1995
1996 if (display_wayland->settings_portal)
1997 {
1998 switch (entry->type)
1999 {
2000 case G_TYPE_STRING:
2001 g_value_set_string (value, v_string: entry->fallback.s);
2002 break;
2003 case G_TYPE_INT:
2004 g_value_set_int (value, v_int: entry->fallback.i);
2005 break;
2006 case G_TYPE_BOOLEAN:
2007 g_value_set_boolean (value, v_boolean: entry->fallback.b);
2008 break;
2009 case G_TYPE_NONE:
2010 if (g_str_equal (v1: entry->setting, v2: "gtk-fontconfig-timestamp"))
2011 g_value_set_uint (value, v_uint: (guint)entry->fallback.i);
2012 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-antialias"))
2013 g_value_set_int (value, v_int: display_wayland->xft_settings.antialias);
2014 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-hinting"))
2015 g_value_set_int (value, v_int: display_wayland->xft_settings.hinting);
2016 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-hintstyle"))
2017 g_value_set_static_string (value, v_string: display_wayland->xft_settings.hintstyle);
2018 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-rgba"))
2019 g_value_set_static_string (value, v_string: display_wayland->xft_settings.rgba);
2020 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-dpi"))
2021 g_value_set_int (value, v_int: display_wayland->xft_settings.dpi);
2022 else
2023 g_assert_not_reached ();
2024 break;
2025 default:
2026 g_assert_not_reached ();
2027 break;
2028 }
2029
2030 return;
2031 }
2032
2033 settings = (GSettings *)g_hash_table_lookup (hash_table: display_wayland->settings, key: entry->schema);
2034 switch (entry->type)
2035 {
2036 case G_TYPE_STRING:
2037 if (settings && entry->valid)
2038 {
2039 char *s;
2040 s = g_settings_get_string (settings, key: entry->key);
2041 g_value_set_string (value, v_string: s);
2042 g_free (mem: s);
2043 }
2044 else
2045 {
2046 g_value_set_static_string (value, v_string: entry->fallback.s);
2047 }
2048 break;
2049 case G_TYPE_INT:
2050 g_value_set_int (value, v_int: settings && entry->valid
2051 ? g_settings_get_int (settings, key: entry->key)
2052 : entry->fallback.i);
2053 break;
2054 case G_TYPE_BOOLEAN:
2055 g_value_set_boolean (value, v_boolean: settings && entry->valid
2056 ? g_settings_get_boolean (settings, key: entry->key)
2057 : entry->fallback.b);
2058 break;
2059 case G_TYPE_NONE:
2060 if (g_str_equal (v1: entry->setting, v2: "gtk-fontconfig-timestamp"))
2061 g_value_set_uint (value, v_uint: (guint)entry->fallback.i);
2062 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-antialias"))
2063 g_value_set_int (value, v_int: display_wayland->xft_settings.antialias);
2064 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-hinting"))
2065 g_value_set_int (value, v_int: display_wayland->xft_settings.hinting);
2066 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-hintstyle"))
2067 g_value_set_static_string (value, v_string: display_wayland->xft_settings.hintstyle);
2068 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-rgba"))
2069 g_value_set_static_string (value, v_string: display_wayland->xft_settings.rgba);
2070 else if (g_str_equal (v1: entry->setting, v2: "gtk-xft-dpi"))
2071 g_value_set_int (value, v_int: display_wayland->xft_settings.dpi);
2072 else
2073 g_assert_not_reached ();
2074 break;
2075 default:
2076 g_assert_not_reached ();
2077 }
2078}
2079
2080static void
2081set_decoration_layout_from_entry (GdkDisplay *display,
2082 TranslationEntry *entry,
2083 GValue *value)
2084{
2085 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
2086 GSettings *settings = NULL;
2087
2088 if (display_wayland->settings_portal)
2089 {
2090 g_value_set_string (value, v_string: entry->fallback.s);
2091 return;
2092 }
2093
2094 settings = (GSettings *)g_hash_table_lookup (hash_table: display_wayland->settings, key: entry->schema);
2095
2096 if (settings)
2097 {
2098 char *s = g_settings_get_string (settings, key: entry->key);
2099
2100 translate_wm_button_layout_to_gtk (layout: s);
2101 g_value_set_string (value, v_string: s);
2102
2103 g_free (mem: s);
2104 }
2105 else
2106 {
2107 g_value_set_static_string (value, v_string: entry->fallback.s);
2108 }
2109}
2110
2111static void
2112set_theme_from_entry (GdkDisplay *display,
2113 TranslationEntry *entry,
2114 GValue *value)
2115{
2116 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
2117 GSettings *settings = NULL;
2118 GSettingsSchema *schema = NULL;
2119 gboolean hc = FALSE;
2120
2121 if (display_wayland->settings_portal == NULL)
2122 {
2123 settings = (GSettings *)g_hash_table_lookup (hash_table: display_wayland->settings,
2124 key: "org.gnome.desktop.a11y.interface");
2125 }
2126
2127 if (settings)
2128 g_object_get (object: settings, first_property_name: "settings-schema", &schema, NULL);
2129
2130 if (schema && g_settings_schema_has_key (schema, name: "high-contrast"))
2131 hc = g_settings_get_boolean (settings, key: "high-contrast");
2132
2133 g_clear_pointer (&schema, g_settings_schema_unref);
2134
2135 if (hc)
2136 g_value_set_static_string (value, v_string: "HighContrast");
2137 else
2138 set_value_from_entry (display, entry, value);
2139}
2140
2141static gboolean
2142set_capability_setting (GdkDisplay *display,
2143 GValue *value,
2144 enum gtk_shell1_capability test)
2145{
2146 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
2147
2148 g_value_set_boolean (value, v_boolean: (display_wayland->shell_capabilities & test) == test);
2149
2150 return TRUE;
2151}
2152
2153static gboolean
2154gdk_wayland_display_get_setting (GdkDisplay *display,
2155 const char *name,
2156 GValue *value)
2157{
2158 TranslationEntry *entry;
2159
2160 if (gdk_display_get_debug_flags (display) & GDK_DEBUG_DEFAULT_SETTINGS)
2161 return FALSE;
2162
2163 if (GDK_WAYLAND_DISPLAY (display)->settings != NULL &&
2164 g_hash_table_size (GDK_WAYLAND_DISPLAY (display)->settings) == 0)
2165 return FALSE;
2166
2167 entry = find_translation_entry_by_setting (setting: name);
2168 if (entry != NULL)
2169 {
2170 if (strcmp (s1: name, s2: "gtk-decoration-layout") == 0)
2171 set_decoration_layout_from_entry (display, entry, value);
2172 else if (strcmp (s1: name, s2: "gtk-theme-name") == 0)
2173 set_theme_from_entry (display, entry, value);
2174 else
2175 set_value_from_entry (display, entry, value);
2176 return TRUE;
2177 }
2178
2179 if (strcmp (s1: name, s2: "gtk-shell-shows-app-menu") == 0)
2180 return set_capability_setting (display, value, test: GTK_SHELL1_CAPABILITY_GLOBAL_APP_MENU);
2181
2182 if (strcmp (s1: name, s2: "gtk-shell-shows-menubar") == 0)
2183 return set_capability_setting (display, value, test: GTK_SHELL1_CAPABILITY_GLOBAL_MENU_BAR);
2184
2185 if (strcmp (s1: name, s2: "gtk-shell-shows-desktop") == 0)
2186 return set_capability_setting (display, value, test: GTK_SHELL1_CAPABILITY_DESKTOP_ICONS);
2187
2188 if (strcmp (s1: name, s2: "gtk-dialogs-use-header") == 0)
2189 {
2190 g_value_set_boolean (value, TRUE);
2191 return TRUE;
2192 }
2193
2194 return FALSE;
2195}
2196
2197#ifdef G_ENABLE_DEBUG
2198
2199static const char *
2200subpixel_to_string (int layout)
2201{
2202 int i;
2203 struct { int layout; const char *name; } layouts[] = {
2204 { WL_OUTPUT_SUBPIXEL_UNKNOWN, "unknown" },
2205 { WL_OUTPUT_SUBPIXEL_NONE, "none" },
2206 { WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB, "rgb" },
2207 { WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR, "bgr" },
2208 { WL_OUTPUT_SUBPIXEL_VERTICAL_RGB, "vrgb" },
2209 { WL_OUTPUT_SUBPIXEL_VERTICAL_BGR, "vbgr" },
2210 { 0xffffffff, NULL }
2211 };
2212
2213 for (i = 0; layouts[i].name; i++)
2214 {
2215 if (layouts[i].layout == layout)
2216 return layouts[i].name;
2217 }
2218 return NULL;
2219}
2220
2221static const char *
2222transform_to_string (int transform)
2223{
2224 int i;
2225 struct { int transform; const char *name; } transforms[] = {
2226 { WL_OUTPUT_TRANSFORM_NORMAL, "normal" },
2227 { WL_OUTPUT_TRANSFORM_90, "90" },
2228 { WL_OUTPUT_TRANSFORM_180, "180" },
2229 { WL_OUTPUT_TRANSFORM_270, "270" },
2230 { WL_OUTPUT_TRANSFORM_FLIPPED, "flipped" },
2231 { WL_OUTPUT_TRANSFORM_FLIPPED_90, "flipped 90" },
2232 { WL_OUTPUT_TRANSFORM_FLIPPED_180, "flipped 180" },
2233 { WL_OUTPUT_TRANSFORM_FLIPPED_270, "flipped 270" },
2234 { 0xffffffff, NULL }
2235 };
2236
2237 for (i = 0; transforms[i].name; i++)
2238 {
2239 if (transforms[i].transform == transform)
2240 return transforms[i].name;
2241 }
2242 return NULL;
2243}
2244
2245#endif
2246
2247static void
2248update_scale (GdkDisplay *display)
2249{
2250 GList *seats;
2251 GList *l;
2252
2253 g_list_foreach (list: gdk_wayland_display_get_toplevel_surfaces (display),
2254 func: (GFunc)gdk_wayland_surface_update_scale,
2255 NULL);
2256 seats = gdk_display_list_seats (display);
2257 for (l = seats; l; l = l->next)
2258 {
2259 GdkSeat *seat = l->data;
2260
2261 gdk_wayland_seat_update_cursor_scale (GDK_WAYLAND_SEAT (seat));
2262 }
2263 g_list_free (list: seats);
2264}
2265
2266static void
2267gdk_wayland_display_init_xdg_output (GdkWaylandDisplay *self)
2268{
2269 guint i, n;
2270
2271 GDK_NOTE (MISC,
2272 g_message ("init xdg-output support, %d monitor(s) already present",
2273 g_list_model_get_n_items (G_LIST_MODEL (self->monitors))));
2274
2275 n = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: self->monitors));
2276 for (i = 0; i < n; i++)
2277 {
2278 GdkWaylandMonitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: self->monitors), position: i);
2279 gdk_wayland_display_get_xdg_output (monitor);
2280 g_object_unref (object: monitor);
2281 }
2282}
2283
2284static gboolean
2285display_has_xdg_output_support (GdkWaylandDisplay *display_wayland)
2286{
2287 return (display_wayland->xdg_output_manager != NULL);
2288}
2289
2290static gboolean
2291monitor_has_xdg_output (GdkWaylandMonitor *monitor)
2292{
2293 return (monitor->xdg_output != NULL);
2294}
2295
2296static gboolean
2297should_update_monitor (GdkWaylandMonitor *monitor)
2298{
2299 return (GDK_MONITOR (monitor)->geometry.width != 0 &&
2300 monitor->version < OUTPUT_VERSION_WITH_DONE);
2301}
2302
2303static gboolean
2304should_expect_xdg_output_done (GdkWaylandMonitor *monitor)
2305{
2306 GdkDisplay *display = GDK_MONITOR (monitor)->display;
2307 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
2308
2309 return (monitor_has_xdg_output (monitor) &&
2310 display_wayland->xdg_output_manager_version < NO_XDG_OUTPUT_DONE_SINCE_VERSION);
2311}
2312
2313static void
2314apply_monitor_change (GdkWaylandMonitor *monitor)
2315{
2316 GDK_NOTE (MISC,
2317 g_message ("monitor %d changed position %d %d, size %d %d",
2318 monitor->id,
2319 monitor->x, monitor->y,
2320 monitor->width, monitor->height));
2321
2322 gdk_monitor_set_geometry (GDK_MONITOR (monitor),
2323 geometry: &(GdkRectangle) {
2324 monitor->x, monitor->y,
2325 monitor->width, monitor->height });
2326 gdk_monitor_set_connector (GDK_MONITOR (monitor), connector: monitor->name);
2327 monitor->wl_output_done = FALSE;
2328 monitor->xdg_output_done = FALSE;
2329
2330 update_scale (GDK_MONITOR (monitor)->display);
2331}
2332
2333static void
2334xdg_output_handle_logical_position (void *data,
2335 struct zxdg_output_v1 *xdg_output,
2336 int32_t x,
2337 int32_t y)
2338{
2339 GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
2340
2341 GDK_NOTE (MISC,
2342 g_message ("handle logical position xdg-output %d, position %d %d",
2343 monitor->id, x, y));
2344 monitor->x = x;
2345 monitor->y = y;
2346}
2347
2348static void
2349xdg_output_handle_logical_size (void *data,
2350 struct zxdg_output_v1 *xdg_output,
2351 int32_t width,
2352 int32_t height)
2353{
2354 GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
2355
2356 GDK_NOTE (MISC,
2357 g_message ("handle logical size xdg-output %d, size %d %d",
2358 monitor->id, width, height));
2359 monitor->width = width;
2360 monitor->height = height;
2361}
2362
2363static void
2364xdg_output_handle_done (void *data,
2365 struct zxdg_output_v1 *xdg_output)
2366{
2367 GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
2368
2369 GDK_NOTE (MISC,
2370 g_message ("handle done xdg-output %d", monitor->id));
2371
2372 monitor->xdg_output_done = TRUE;
2373 if (monitor->wl_output_done && should_expect_xdg_output_done (monitor))
2374 apply_monitor_change (monitor);
2375}
2376
2377static void
2378xdg_output_handle_name (void *data,
2379 struct zxdg_output_v1 *xdg_output,
2380 const char *name)
2381{
2382 GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
2383
2384 GDK_NOTE (MISC,
2385 g_message ("handle name xdg-output %d", monitor->id));
2386
2387 monitor->name = g_strdup (str: name);
2388}
2389
2390static void
2391xdg_output_handle_description (void *data,
2392 struct zxdg_output_v1 *xdg_output,
2393 const char *description)
2394{
2395 GDK_NOTE (MISC,
2396 {
2397 GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
2398 g_message ("handle description xdg-output %d", monitor->id);
2399 });
2400}
2401
2402static const struct zxdg_output_v1_listener xdg_output_listener = {
2403 xdg_output_handle_logical_position,
2404 xdg_output_handle_logical_size,
2405 xdg_output_handle_done,
2406 xdg_output_handle_name,
2407 xdg_output_handle_description,
2408};
2409
2410static void
2411gdk_wayland_display_get_xdg_output (GdkWaylandMonitor *monitor)
2412{
2413 GdkDisplay *display = GDK_MONITOR (monitor)->display;
2414 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
2415
2416 GDK_NOTE (MISC,
2417 g_message ("get xdg-output for monitor %d", monitor->id));
2418
2419 monitor->xdg_output =
2420 zxdg_output_manager_v1_get_xdg_output (display_wayland->xdg_output_manager,
2421 monitor->output);
2422
2423 zxdg_output_v1_add_listener (monitor->xdg_output,
2424 &xdg_output_listener,
2425 monitor);
2426}
2427
2428static void
2429output_handle_geometry (void *data,
2430 struct wl_output *wl_output,
2431 int x,
2432 int y,
2433 int physical_width,
2434 int physical_height,
2435 int subpixel,
2436 const char *make,
2437 const char *model,
2438 int32_t transform)
2439{
2440 GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data;
2441
2442 GDK_NOTE (MISC,
2443 g_message ("handle geometry output %d, position %d %d, phys. size %d %d, subpixel layout %s, manufacturer %s, model %s, transform %s",
2444 monitor->id, x, y, physical_width, physical_height, subpixel_to_string (subpixel), make, model, transform_to_string (transform)));
2445
2446 monitor->x = x;
2447 monitor->y = y;
2448
2449 switch (transform)
2450 {
2451 case WL_OUTPUT_TRANSFORM_90:
2452 case WL_OUTPUT_TRANSFORM_270:
2453 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
2454 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
2455 gdk_monitor_set_physical_size (GDK_MONITOR (monitor),
2456 width_mm: physical_height, height_mm: physical_width);
2457 break;
2458 default:
2459 gdk_monitor_set_physical_size (GDK_MONITOR (monitor),
2460 width_mm: physical_width, height_mm: physical_height);
2461 }
2462
2463 gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor), subpixel);
2464 gdk_monitor_set_manufacturer (GDK_MONITOR (monitor), manufacturer: make);
2465 gdk_monitor_set_model (GDK_MONITOR (monitor), model);
2466
2467 if (should_update_monitor (monitor) || !monitor_has_xdg_output (monitor))
2468 apply_monitor_change (monitor);
2469
2470 if (should_update_monitor (monitor))
2471 update_scale (GDK_MONITOR (monitor)->display);
2472}
2473
2474static void
2475output_handle_done (void *data,
2476 struct wl_output *wl_output)
2477{
2478 GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data;
2479
2480 GDK_NOTE (MISC,
2481 g_message ("handle done output %d", monitor->id));
2482
2483 monitor->wl_output_done = TRUE;
2484
2485 if (!should_expect_xdg_output_done (monitor) || monitor->xdg_output_done)
2486 apply_monitor_change (monitor);
2487}
2488
2489static void
2490output_handle_scale (void *data,
2491 struct wl_output *wl_output,
2492 int32_t scale)
2493{
2494 GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data;
2495 GdkRectangle previous_geometry;
2496 int previous_scale;
2497 int width;
2498 int height;
2499
2500 GDK_NOTE (MISC,
2501 g_message ("handle scale output %d, scale %d", monitor->id, scale));
2502
2503 gdk_monitor_get_geometry (GDK_MONITOR (monitor), geometry: &previous_geometry);
2504 previous_scale = gdk_monitor_get_scale_factor (GDK_MONITOR (monitor));
2505
2506 /* Set the scale from wl_output protocol, regardless of xdg-output support */
2507 gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), scale);
2508
2509 if (monitor_has_xdg_output (monitor))
2510 return;
2511
2512 width = previous_geometry.width * previous_scale;
2513 height = previous_geometry.height * previous_scale;
2514
2515 monitor->width = width / scale;
2516 monitor->height = height / scale;
2517
2518 if (should_update_monitor (monitor))
2519 apply_monitor_change (monitor);
2520}
2521
2522static void
2523output_handle_mode (void *data,
2524 struct wl_output *wl_output,
2525 uint32_t flags,
2526 int width,
2527 int height,
2528 int refresh)
2529{
2530 GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data;
2531 int scale;
2532
2533 GDK_NOTE (MISC,
2534 g_message ("handle mode output %d, size %d %d, rate %d",
2535 monitor->id, width, height, refresh));
2536
2537 if ((flags & WL_OUTPUT_MODE_CURRENT) == 0)
2538 return;
2539
2540 scale = gdk_monitor_get_scale_factor (GDK_MONITOR (monitor));
2541 monitor->width = width / scale;
2542 monitor->height = height / scale;
2543 gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh_rate: refresh);
2544
2545 if (should_update_monitor (monitor) || !monitor_has_xdg_output (monitor))
2546 apply_monitor_change (monitor);
2547}
2548
2549static const struct wl_output_listener output_listener =
2550{
2551 output_handle_geometry,
2552 output_handle_mode,
2553 output_handle_done,
2554 output_handle_scale,
2555};
2556
2557static void
2558gdk_wayland_display_add_output (GdkWaylandDisplay *display_wayland,
2559 guint32 id,
2560 struct wl_output *output,
2561 guint32 version)
2562{
2563 GdkWaylandMonitor *monitor;
2564
2565 monitor = g_object_new (GDK_TYPE_WAYLAND_MONITOR,
2566 first_property_name: "display", GDK_DISPLAY (display_wayland),
2567 NULL);
2568
2569 monitor->id = id;
2570 monitor->output = output;
2571 monitor->version = version;
2572
2573 wl_output_add_listener (wl_output: output, listener: &output_listener, data: monitor);
2574
2575 GDK_NOTE (MISC,
2576 g_message ("xdg_output_manager %p",
2577 display_wayland->xdg_output_manager));
2578
2579 if (display_has_xdg_output_support (display_wayland))
2580 gdk_wayland_display_get_xdg_output (monitor);
2581
2582 g_list_store_append (store: display_wayland->monitors, item: monitor);
2583
2584 g_object_unref (object: monitor);
2585}
2586
2587static GdkWaylandMonitor *
2588get_monitor_for_output (GdkWaylandDisplay *self,
2589 struct wl_output *output)
2590{
2591 guint i, n;
2592
2593 n = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: self->monitors));
2594 for (i = 0; i < n; i++)
2595 {
2596 GdkWaylandMonitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: self->monitors), position: i);
2597
2598 g_object_unref (object: monitor);
2599
2600 if (monitor->output == output)
2601 return monitor;
2602 }
2603
2604 return NULL;
2605}
2606
2607GdkMonitor *
2608gdk_wayland_display_get_monitor_for_output (GdkDisplay *display,
2609 struct wl_output *output)
2610{
2611 return (GdkMonitor *)get_monitor_for_output (GDK_WAYLAND_DISPLAY (display), output);
2612}
2613
2614static void
2615gdk_wayland_display_remove_output (GdkWaylandDisplay *self,
2616 guint32 id)
2617{
2618 guint i, n;
2619
2620 n = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: self->monitors));
2621 for (i = 0; i < n; i++)
2622 {
2623 GdkWaylandMonitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: self->monitors), position: i);
2624
2625 if (monitor->id == id)
2626 {
2627 g_list_store_remove (store: self->monitors, position: i);
2628 gdk_monitor_invalidate (GDK_MONITOR (monitor));
2629 update_scale (GDK_DISPLAY (self));
2630 g_object_unref (object: monitor);
2631 break;
2632 }
2633
2634 g_object_unref (object: monitor);
2635 }
2636}
2637
2638int
2639gdk_wayland_display_get_output_refresh_rate (GdkWaylandDisplay *display_wayland,
2640 struct wl_output *output)
2641{
2642 GdkWaylandMonitor *monitor;
2643
2644 monitor = get_monitor_for_output (self: display_wayland, output);
2645 if (monitor != NULL)
2646 return gdk_monitor_get_refresh_rate (GDK_MONITOR (monitor));
2647
2648 return 0;
2649}
2650
2651guint32
2652gdk_wayland_display_get_output_scale (GdkWaylandDisplay *display_wayland,
2653 struct wl_output *output)
2654{
2655 GdkWaylandMonitor *monitor;
2656
2657 monitor = get_monitor_for_output (self: display_wayland, output);
2658 if (monitor != NULL)
2659 return gdk_monitor_get_scale_factor (GDK_MONITOR (monitor));
2660
2661 return 0;
2662}
2663
2664/**
2665 * gdk_wayland_display_query_registry:
2666 * @display: (type GdkWaylandDisplay): a `GdkDisplay`
2667 * @global: global interface to query in the registry
2668 *
2669 * Returns %TRUE if the interface was found in the display
2670 * `wl_registry.global` handler.
2671 *
2672 * Returns: %TRUE if the global is offered by the compositor
2673 */
2674gboolean
2675gdk_wayland_display_query_registry (GdkDisplay *display,
2676 const char *global)
2677{
2678 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
2679 GHashTableIter iter;
2680 char *value;
2681
2682 g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), FALSE);
2683 g_return_val_if_fail (global != NULL, FALSE);
2684
2685 g_hash_table_iter_init (iter: &iter, hash_table: display_wayland->known_globals);
2686
2687 while (g_hash_table_iter_next (iter: &iter, NULL, value: (gpointer*) &value))
2688 {
2689 if (strcmp (s1: value, s2: global) == 0)
2690 return TRUE;
2691 }
2692
2693 return FALSE;
2694}
2695

source code of gtk/gdk/wayland/gdkdisplay-wayland.c