Blender V4.3
GHOST_WindowWayland.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "GHOST_WaylandUtils.hh"
13#include "GHOST_utildefines.hh"
14
15#include "GHOST_Event.hh"
16
17#include "GHOST_ContextNone.hh"
18#ifdef WITH_OPENGL_BACKEND
19# include "GHOST_ContextEGL.hh"
20#endif
21#ifdef WITH_VULKAN_BACKEND
22# include "GHOST_ContextVK.hh"
23#endif
24
25#include <wayland-client-protocol.h>
26
27#ifdef WITH_OPENGL_BACKEND
28# ifdef WITH_GHOST_WAYLAND_DYNLOAD
29# include <wayland_dynload_egl.h>
30# endif
31# include <wayland-egl.h>
32#endif
33
34#include <algorithm> /* For `std::find`. */
35
36#ifdef WITH_GHOST_WAYLAND_LIBDECOR
37# ifdef WITH_GHOST_WAYLAND_DYNLOAD
39# endif
40# include <libdecor.h>
41
42# ifdef WITH_VULKAN_BACKEND
43# include <unistd.h> /* For `ftruncate`. */
44# endif
45#endif
46
47/* Generated by `wayland-scanner`. */
48#include <fractional-scale-v1-client-protocol.h>
49#include <viewporter-client-protocol.h>
50#include <xdg-activation-v1-client-protocol.h>
51#include <xdg-decoration-unstable-v1-client-protocol.h>
52#include <xdg-shell-client-protocol.h>
53
54#include <atomic>
55
56#include <cstring> /* For `memcpy`. */
57#include <malloc.h> /* For `malloc_usable_size`. */
58
59/* Logging, use `ghost.wl.*` prefix. */
60#include "CLG_log.h"
61
74#ifdef USE_EVENT_BACKGROUND_THREAD
75# ifdef WITH_GHOST_WAYLAND_LIBDECOR
76# ifdef HAVE_MALLOC_USABLE_SIZE
77# define USE_LIBDECOR_CONFIG_COPY_WORKAROUND
78# endif
79# endif
80#endif
81
87#ifdef USE_LIBDECOR_CONFIG_COPY_WORKAROUND
88# define USE_LIBDECOR_CONFIG_COPY_QUEUE
89#endif
90
91#ifdef USE_LIBDECOR_CONFIG_COPY_WORKAROUND
92static libdecor_configuration *ghost_wl_libdecor_configuration_copy(
93 const libdecor_configuration *configuration);
94static void ghost_wl_libdecor_configuration_free(libdecor_configuration *configuration);
95#endif
96
97static const xdg_activation_token_v1_listener *xdg_activation_listener_get();
98
99static constexpr size_t base_dpi = 96;
100
101#ifdef WITH_GHOST_WAYLAND_LIBDECOR
102/* Access `use_libdecor` in #GHOST_SystemWayland. */
103# define use_libdecor GHOST_SystemWayland::use_libdecor_runtime()
104#endif
105
106#ifdef WITH_GHOST_WAYLAND_LIBDECOR
107struct GWL_LibDecor_Window {
108 libdecor_frame *frame = nullptr;
109
114 struct {
116 bool ack_configure = false;
118 int size[2] = {0, 0};
119 libdecor_configuration *configuration = nullptr;
120
121# ifdef USE_LIBDECOR_CONFIG_COPY_WORKAROUND
122 bool configuration_needs_free = false;
123# endif
124
125# ifdef USE_LIBDECOR_CONFIG_COPY_QUEUE
130 std::vector<libdecor_configuration *> configuration_queue;
131# endif
132
133 } pending;
134
136 bool initial_configure_seen = false;
137};
138
139static void gwl_libdecor_window_destroy(GWL_LibDecor_Window *decor)
140{
141# ifdef USE_LIBDECOR_CONFIG_COPY_WORKAROUND
142 if (decor->pending.configuration_needs_free) {
143 ghost_wl_libdecor_configuration_free(decor->pending.configuration);
144 }
145
146# endif /* USE_LIBDECOR_CONFIG_COPY_WORKAROUND */
147
148# ifdef USE_LIBDECOR_CONFIG_COPY_QUEUE
149 for (libdecor_configuration *configuration : decor->pending.configuration_queue) {
150 ghost_wl_libdecor_configuration_free(configuration);
151 }
152 decor->pending.configuration_queue.clear();
153# endif /* USE_LIBDECOR_CONFIG_COPY_QUEUE */
154
155 libdecor_frame_unref(decor->frame);
156 delete decor;
157}
158#endif /* WITH_GHOST_WAYLAND_LIBDECOR */
159
161 xdg_surface *surface = nullptr;
162 zxdg_toplevel_decoration_v1 *toplevel_decor = nullptr;
163 xdg_toplevel *toplevel = nullptr;
164 enum zxdg_toplevel_decoration_v1_mode mode = (enum zxdg_toplevel_decoration_v1_mode)0;
165
170 struct {
172 bool ack_configure = false;
176
180 int initial_bounds[2] = {0, 0};
181};
182
184{
185 if (decor->toplevel_decor) {
186 zxdg_toplevel_decoration_v1_destroy(decor->toplevel_decor);
187 }
188 xdg_toplevel_destroy(decor->toplevel);
189 xdg_surface_destroy(decor->surface);
190 delete decor;
191}
192
193/* -------------------------------------------------------------------- */
197static void gwl_round_int_by(int *value_p, const int round_value)
198{
199 GHOST_ASSERT(round_value > 0, "Invalid rounding value!");
200 *value_p = (*value_p / round_value) * round_value;
201}
202
203static void gwl_round_int2_by(int value_p[2], const int round_value)
204{
205 GHOST_ASSERT(round_value > 0, "Invalid rounding value!");
206 value_p[0] = (value_p[0] / round_value) * round_value;
207 value_p[1] = (value_p[1] / round_value) * round_value;
208}
209
213static bool gwl_round_int_test(int value, const int round_value)
214{
215 return value == ((value / round_value) * round_value);
216}
217
220/* -------------------------------------------------------------------- */
225 bool is_fractional = false;
231 wl_fixed_t scale = 0;
232};
233
235 wl_fixed_t value)
236{
237 if (scale_params.is_fractional) {
238 return (value * scale_params.scale) / FRACTIONAL_DENOMINATOR;
239 }
240 return value * scale_params.scale;
241}
243 wl_fixed_t value)
244{
245 if (scale_params.is_fractional) {
246 return (value * FRACTIONAL_DENOMINATOR) / scale_params.scale;
247 }
248 return value / scale_params.scale;
249}
250
251int gwl_window_scale_int_to(const GWL_WindowScaleParams &scale_params, int value)
252{
253 return wl_fixed_to_int(gwl_window_scale_wl_fixed_to(scale_params, wl_fixed_from_int(value)));
254}
255int gwl_window_scale_int_from(const GWL_WindowScaleParams &scale_params, int value)
256{
257 return wl_fixed_to_int(gwl_window_scale_wl_fixed_from(scale_params, wl_fixed_from_int(value)));
258}
259
262/* -------------------------------------------------------------------- */
267 uint8_t *bitmap = nullptr;
268 uint8_t *mask = nullptr;
269 int32_t hot_spot[2] = {0, 0};
270 int32_t size[2] = {0, 0};
271 bool can_invert_color = false;
272};
273
275{
276 if (ccs.bitmap) {
277 free(ccs.bitmap);
278 }
279 if (ccs.mask) {
280 free(ccs.mask);
281 }
282}
283
289
291 const uint8_t *bitmap,
292 const uint8_t *mask,
293 const int32_t size[2],
294 const int32_t hot_spot[2],
295 bool can_invert_color)
296{
298 /* The width is divided by 8, rounding up. */
299 const size_t bitmap_size = sizeof(uint8_t) * ((size[0] + 7) / 8) * size[1];
300
301 if (bitmap) {
302 ccs.bitmap = static_cast<uint8_t *>(malloc(bitmap_size));
303 memcpy(ccs.bitmap, bitmap, bitmap_size);
304 }
305 if (mask) {
306 ccs.mask = static_cast<uint8_t *>(malloc(bitmap_size));
307 memcpy(ccs.mask, mask, bitmap_size);
308 }
309
310 ccs.size[0] = size[0];
311 ccs.size[1] = size[1];
312
313 ccs.hot_spot[0] = hot_spot[0];
314 ccs.hot_spot[1] = hot_spot[1];
315
316 ccs.can_invert_color = can_invert_color;
317}
318
320 GHOST_SystemWayland *system)
321{
322 return system->cursor_shape_custom_set(ccs.bitmap,
323 ccs.mask,
324 ccs.size[0],
325 ccs.size[1],
326 ccs.hot_spot[0],
327 ccs.hot_spot[1],
328 ccs.can_invert_color);
329}
330
333 GHOST_SystemWayland *system)
334{
335#ifdef USE_EVENT_BACKGROUND_THREAD
336 GHOST_ASSERT(system->main_thread_id == std::this_thread::get_id(), "Only from main thread!");
337#endif
338
339 if (shape == GHOST_kStandardCursorCustom) {
340 const GHOST_TSuccess ok = gwl_window_cursor_custom_load(ccs, system);
341 if (ok == GHOST_kSuccess) {
342 return ok;
343 }
345 system->cursor_shape_set(shape);
346 return GHOST_kFailure;
347 }
348
349 return system->cursor_shape_set(shape);
350}
351
354/* -------------------------------------------------------------------- */
358#ifdef USE_EVENT_BACKGROUND_THREAD
359
395# define PENDING_NUM (PENDING_WINDOW_CURSOR_SHAPE_REFRESH + 1)
396
397#endif /* USE_EVENT_BACKGROUND_THREAD */
398
406 int32_t size[2] = {0, 0};
407 bool is_maximised = false;
408 bool is_fullscreen = false;
409 bool is_active = false;
419
421 bool is_scale_init = false;
422};
423
425
427 struct {
428 wl_surface *surface = nullptr;
429 } wl;
430
432 struct {
433 wp_viewport *viewport = nullptr;
434
439 wp_fractional_scale_v1 *fractional_scale_handle = nullptr;
440 } wp;
441
443 struct {
445 xdg_activation_token_v1 *activation_token = nullptr;
447
448 struct {
449#ifdef WITH_OPENGL_BACKEND
450 wl_egl_window *egl_window = nullptr;
451#endif
452#ifdef WITH_VULKAN_BACKEND
453 GHOST_ContextVK_WindowInfo *vulkan_window_info = nullptr;
454#endif
456
460
467 std::vector<GWL_Output *> outputs;
468
469#ifdef WITH_GHOST_WAYLAND_LIBDECOR
470 GWL_LibDecor_Window *libdecor = nullptr;
471#endif
473
480
481#ifdef USE_EVENT_BACKGROUND_THREAD
487#endif
488
490
491 std::string title;
492
493 bool is_dialog = false;
494
496 bool is_init = false;
497
499 bool is_valid_setup = false;
500
503
504#ifdef USE_EVENT_BACKGROUND_THREAD
509 std::atomic<bool> pending_actions[PENDING_NUM] = {false};
510#endif /* USE_EVENT_BACKGROUND_THREAD */
511};
512
513static void gwl_window_resize_for_backend(GWL_Window *win, const int32_t size[2])
514{
515#ifdef WITH_OPENGL_BACKEND
516 if (win->ghost_context_type == GHOST_kDrawingContextTypeOpenGL) {
517 /* Null on window initialization. */
518 if (win->backend.egl_window) {
519 wl_egl_window_resize(win->backend.egl_window, UNPACK2(size), 0, 0);
520 }
521 }
522#endif
523#ifdef WITH_VULKAN_BACKEND
524 if (win->ghost_context_type == GHOST_kDrawingContextTypeVulkan) {
525 win->backend.vulkan_window_info->size[0] = size[0];
526 win->backend.vulkan_window_info->size[1] = size[1];
527 }
528#endif
529}
530
531static void gwl_window_title_set(GWL_Window *win, const char *title)
532{
533#ifdef WITH_GHOST_WAYLAND_LIBDECOR
534 if (use_libdecor) {
535 GWL_LibDecor_Window &decor = *win->libdecor;
536 libdecor_frame_set_title(decor.frame, title);
537 }
538 else
539#endif
540 {
541 GWL_XDG_Decor_Window &decor = *win->xdg_decor;
542 xdg_toplevel_set_title(decor.toplevel, title);
543 }
544
545 win->title = title;
546}
547
549{
550 if (win->frame.is_fullscreen) {
552 }
553 if (win->frame.is_maximised) {
555 }
557}
558
559#ifdef WITH_GHOST_WAYLAND_LIBDECOR
563static bool gwl_window_state_set_for_libdecor(libdecor_frame *frame,
565 const GHOST_TWindowState state_current)
566{
567 switch (state) {
569 /* Unset states. */
570 switch (state_current) {
573 break;
574 }
577 break;
578 }
579 default: {
580 break;
581 }
582 }
583 break;
586 break;
587 }
590 break;
591 }
593 libdecor_frame_set_fullscreen(frame, nullptr);
594 break;
595 }
596 }
597 return true;
598}
599
600#endif /* WITH_GHOST_WAYLAND_LIBDECOR */
601
605static bool gwl_window_state_set_for_xdg(xdg_toplevel *toplevel,
607 const GHOST_TWindowState state_current)
608{
609 switch (state) {
611 /* Unset states. */
612 switch (state_current) {
614 xdg_toplevel_unset_maximized(toplevel);
615 break;
616 }
618 xdg_toplevel_unset_fullscreen(toplevel);
619 break;
620 }
621 default: {
622 break;
623 }
624 }
625 break;
627 xdg_toplevel_set_maximized(toplevel);
628 break;
629 }
631 xdg_toplevel_set_minimized(toplevel);
632 break;
633 }
635 xdg_toplevel_set_fullscreen(toplevel, nullptr);
636 break;
637 }
638 }
639 return true;
640}
641
643{
644 const GHOST_TWindowState state_current = gwl_window_state_get(win);
645 bool result;
646#ifdef WITH_GHOST_WAYLAND_LIBDECOR
647 if (use_libdecor) {
648 result = gwl_window_state_set_for_libdecor(win->libdecor->frame, state, state_current);
649 }
650 else
651#endif
652 {
653 result = gwl_window_state_set_for_xdg(win->xdg_decor->toplevel, state, state_current);
654 }
655 return result;
656}
657
660/* -------------------------------------------------------------------- */
671static int gwl_window_fractional_to_viewport(const GWL_WindowFrame &frame, int value)
672{
673 GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initialized!");
674 return (value * frame.fractional_scale) / FRACTIONAL_DENOMINATOR;
675}
676
681static int gwl_window_fractional_from_viewport(const GWL_WindowFrame &frame, int value)
682{
683 GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initialized!");
684 return (value * FRACTIONAL_DENOMINATOR) / frame.fractional_scale;
685}
686
687/* NOTE: rounded versions are needed for window-frame dimensions conversions.
688 * (rounding is part of the WAYLAND spec). All other conversions such as cursor coordinates
689 * can used simple integer division as rounding is not defined in this case. */
690
692{
693 GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initialized!");
694 return lroundf(double(value * frame.fractional_scale) / double(FRACTIONAL_DENOMINATOR));
695}
696
698{
699 GHOST_ASSERT(frame.fractional_scale != 0, "Not fractional or called before initialized!");
700 return lroundf(double(value * FRACTIONAL_DENOMINATOR) / double(frame.fractional_scale));
701}
702
704 bool *r_surface_needs_commit,
705 bool *r_surface_needs_buffer_scale)
706{
707 if (win->wp.viewport != nullptr) {
708 return false;
709 }
710 wp_viewporter *viewporter = win->ghost_system->wp_viewporter_get();
711 if (viewporter == nullptr) {
712 return false;
713 }
714 win->wp.viewport = wp_viewporter_get_viewport(viewporter, win->wl.surface);
715 if (win->wp.viewport == nullptr) {
716 return false;
717 }
718
719 /* Set the buffer scale to 1 since a viewport will be used. */
720 if (win->frame.buffer_scale != 1) {
721 win->frame.buffer_scale = 1;
722
723 if (r_surface_needs_buffer_scale) {
724 *r_surface_needs_buffer_scale = true;
725 }
726 else {
727 wl_surface_set_buffer_scale(win->wl.surface, win->frame.buffer_scale);
728 }
729
730 if (r_surface_needs_commit) {
731 *r_surface_needs_commit = true;
732 }
733 else {
734 wl_surface_commit(win->wl.surface);
735 }
736 }
737
738 return true;
739}
740
742 bool *r_surface_needs_commit,
743 bool *r_surface_needs_buffer_scale)
744{
745 if (win->wp.viewport == nullptr) {
746 return false;
747 }
748
749 wp_viewport_destroy(win->wp.viewport);
750 win->wp.viewport = nullptr;
751
752 GHOST_ASSERT(win->frame.buffer_scale == 1, "Unexpected scale!");
753 if (win->frame_pending.buffer_scale != win->frame.buffer_scale) {
755
756 if (r_surface_needs_buffer_scale) {
757 *r_surface_needs_buffer_scale = true;
758 }
759 else {
760 wl_surface_set_buffer_scale(win->wl.surface, win->frame.buffer_scale);
761 }
762
763 if (r_surface_needs_commit) {
764 *r_surface_needs_commit = true;
765 }
766 else {
767 wl_surface_commit(win->wl.surface);
768 }
769 }
770 return true;
771}
772
774{
775 if (win->wp.viewport == nullptr) {
776 return false;
777 }
778
779 /* Setting `wp_viewport_set_source` isn't necessary as an unset value is ensured on creation
780 * and documented to use the entire buffer, further this can crash with NVIDIA, see: #117531. */
781
782 wp_viewport_set_destination(
783 win->wp.viewport,
786 return true;
787}
788
791/* -------------------------------------------------------------------- */
802{
803 GHOST_SystemWayland *system = win->ghost_system;
804 xdg_activation_v1 *activation_manager = system->xdg_activation_manager_get();
805 if (UNLIKELY(activation_manager == nullptr)) {
806 return;
807 }
808
809 if (win->xdg.activation_token) {
810 /* We're about to overwrite this with a new request. */
811 xdg_activation_token_v1_destroy(win->xdg.activation_token);
812 }
813 win->xdg.activation_token = xdg_activation_v1_get_activation_token(activation_manager);
814
815 xdg_activation_token_v1_add_listener(
817
818 xdg_activation_token_v1_set_app_id(win->xdg.activation_token,
820
821 /* The serial of the input device requesting activation. */
822 {
823 uint32_t serial = 0;
824 wl_seat *seat = system->wl_seat_active_get_with_input_serial(serial);
825 if (seat) {
826 xdg_activation_token_v1_set_serial(win->xdg.activation_token, serial, seat);
827 }
828 }
829
830 /* The surface of the window requesting activation. */
831 {
832 GHOST_WindowWayland *ghost_window_active = static_cast<GHOST_WindowWayland *>(
833 system->getWindowManager()->getActiveWindow());
834 if (ghost_window_active) {
835 wl_surface *surface = ghost_window_active->wl_surface_get();
836 if (surface) {
837 xdg_activation_token_v1_set_surface(win->xdg.activation_token, surface);
838 }
839 }
840 }
841
842 xdg_activation_token_v1_commit(win->xdg.activation_token);
843}
844
847/* -------------------------------------------------------------------- */
852 GWL_Window *win, bool *r_surface_needs_commit, bool *r_surface_needs_buffer_scale)
853{
856 gwl_window_viewport_set(win, r_surface_needs_commit, r_surface_needs_buffer_scale);
858 }
859 else {
860 if (win->wp.viewport) {
861 gwl_window_viewport_unset(win, r_surface_needs_commit, r_surface_needs_buffer_scale);
862 }
863 else {
865 if (r_surface_needs_buffer_scale) {
866 *r_surface_needs_buffer_scale = true;
867 }
868 else {
869 wl_surface_set_buffer_scale(win->wl.surface, win->frame.buffer_scale);
870 }
871 if (r_surface_needs_commit) {
872 *r_surface_needs_commit = true;
873 }
874 else {
875 wl_surface_commit(win->wl.surface);
876 }
877 }
878 }
879}
880
882 bool *r_surface_needs_commit,
883 bool *r_surface_needs_buffer_scale)
884{
887 {
888 return;
889 }
891 win, r_surface_needs_commit, r_surface_needs_buffer_scale);
892}
893
895 bool *r_surface_needs_commit,
896 bool *r_surface_needs_resize_for_backend,
897 bool *r_surface_needs_buffer_scale)
898{
899 if (win->frame_pending.size[0] == 0 || win->frame_pending.size[1] == 0) {
900 return;
901 }
902
903 win->frame.size[0] = win->frame_pending.size[0];
904 win->frame.size[1] = win->frame_pending.size[1];
905
908 {
910 win, r_surface_needs_commit, r_surface_needs_buffer_scale);
911 }
912 else {
914 }
915
916 if (r_surface_needs_resize_for_backend) {
917 *r_surface_needs_resize_for_backend = true;
918 }
919 else {
921 }
922
924
925 win->frame_pending.size[0] = 0;
926 win->frame_pending.size[1] = 0;
927}
928
930
931#ifdef USE_EVENT_BACKGROUND_THREAD
932
934{
935 win->pending_actions[int(type)].store(true);
937}
938
940{
941 /* Ensure pending actions always use the state when the function starts
942 * because one actions may trigger other pending actions an in that case
943 * exact behavior depends on the order functions are called here.
944 * Without this, configuring the frame will trigger the surface
945 * commit immediately instead of the next time pending actions are handled. */
946 bool actions[PENDING_NUM];
947 for (size_t i = 0; i < ARRAY_SIZE(actions); i++) {
948 actions[i] = win->pending_actions[i].exchange(false);
949 }
950
951 if (actions[PENDING_WINDOW_FRAME_CONFIGURE]) {
953 }
956 /* Force postponing scale update to ensure all scale information has been taken into account
957 * before the actual update is performed. Failing to do so tends to cause flickering. */
958 actions[PENDING_OUTPUT_SCALE_UPDATE] = false;
959 }
960 if (actions[PENDING_OUTPUT_SCALE_UPDATE]) {
962 }
963 if (actions[PENDING_WINDOW_SURFACE_COMMIT]) {
964 wl_surface_commit(win->wl.surface);
965 }
969 }
970}
971
972#endif /* USE_EVENT_BACKGROUND_THREAD */
973
979{
980#ifdef USE_EVENT_BACKGROUND_THREAD
981 GHOST_ASSERT(win->ghost_system->main_thread_id == std::this_thread::get_id(),
982 "Only from main thread!");
983
984#endif
985
986 const bool dpi_changed = win->frame_pending.fractional_scale != win->frame.fractional_scale;
987 bool surface_needs_commit = false;
988 bool surface_needs_resize_for_backend = false;
989 bool surface_needs_buffer_scale = false;
990
991 if (win->frame_pending.size[0] != 0 && win->frame_pending.size[1] != 0) {
992 if ((win->frame.size[0] != win->frame_pending.size[0]) ||
993 (win->frame.size[1] != win->frame_pending.size[1]))
994 {
996 &surface_needs_commit,
997 &surface_needs_resize_for_backend,
998 &surface_needs_buffer_scale);
999 }
1000 }
1001
1004 win, &surface_needs_commit, &surface_needs_buffer_scale);
1005 }
1006 else {
1007 if (win->frame_pending.buffer_scale != win->frame.buffer_scale) {
1009 surface_needs_buffer_scale = true;
1010 }
1011 }
1012
1013 if (surface_needs_resize_for_backend) {
1015 }
1016
1017 if (surface_needs_buffer_scale) {
1018 wl_surface_set_buffer_scale(win->wl.surface, win->frame.buffer_scale);
1019 }
1020
1021#ifdef WITH_GHOST_WAYLAND_LIBDECOR
1022 if (use_libdecor) {
1023 GWL_LibDecor_Window &decor = *win->libdecor;
1024 if (decor.pending.ack_configure) {
1025 surface_needs_commit = true;
1026
1027 decor.pending.ack_configure = false;
1028
1029 libdecor_state *state = libdecor_state_new(UNPACK2(decor.pending.size));
1030
1031# ifdef USE_LIBDECOR_CONFIG_COPY_QUEUE
1032 GHOST_ASSERT(decor.pending.size[0] != 0 && decor.pending.size[1] != 0, "Invalid size");
1033 for (libdecor_configuration *configuration : decor.pending.configuration_queue) {
1034 libdecor_frame_commit(decor.frame, state, configuration);
1035 ghost_wl_libdecor_configuration_free(configuration);
1036 }
1037 decor.pending.configuration_queue.clear();
1038# endif
1039
1040 libdecor_frame_commit(decor.frame, state, decor.pending.configuration);
1041
1043
1044 decor.pending.size[0] = 0;
1045 decor.pending.size[1] = 0;
1046
1047 decor.initial_configure_seen = true;
1048
1049# ifdef USE_LIBDECOR_CONFIG_COPY_WORKAROUND
1050 if (decor.pending.configuration_needs_free) {
1051 ghost_wl_libdecor_configuration_free(decor.pending.configuration);
1052 decor.pending.configuration_needs_free = false;
1053 }
1054# endif
1055
1056 decor.pending.configuration = nullptr;
1057 }
1058 }
1059 else
1060#endif
1061 if (win->xdg_decor)
1062 {
1063 GWL_XDG_Decor_Window &decor = *win->xdg_decor;
1064 if (decor.pending.ack_configure) {
1065 xdg_surface_ack_configure(decor.surface, decor.pending.ack_configure_serial);
1066 /* The XDG spec states a commit event is required after ACK configure. */
1067 surface_needs_commit = true;
1068
1069 decor.pending.ack_configure = false;
1070 decor.pending.ack_configure_serial = 0;
1071
1072 decor.initial_configure_seen = true;
1073 }
1074 }
1075
1076 if (surface_needs_commit) {
1077#ifdef USE_EVENT_BACKGROUND_THREAD
1078 /* Postponing the commit avoids flickering when moving between monitors of different scale. */
1080#else
1081 wl_surface_commit(win->wl.surface);
1082#endif
1083 }
1084
1085 if (dpi_changed) {
1086 GHOST_SystemWayland *system = win->ghost_system;
1087 system->pushEvent(new GHOST_Event(
1089 }
1090
1091 if (win->frame.is_active != win->frame_pending.is_active) {
1092 if (win->frame_pending.is_active) {
1093 win->ghost_window->activate();
1094 }
1095 else {
1096 win->ghost_window->deactivate();
1097 }
1098 }
1099 else if (false) {
1100 /* Disabled, this can happen during debugging
1101 * when the window changed while the process has been paused. */
1103 win->frame.is_active ==
1105 "GHOST internal active state does not match WAYLAND!");
1106 }
1107
1108 win->frame_pending.size[0] = win->frame.size[0];
1109 win->frame_pending.size[1] = win->frame.size[1];
1110
1111 win->frame = win->frame_pending;
1112
1113 /* Signal not to apply the scale unless it's configured. */
1114 win->frame_pending.size[0] = 0;
1115 win->frame_pending.size[1] = 0;
1116}
1117
1118[[maybe_unused]] static void gwl_window_frame_update_from_pending(GWL_Window *win)
1119{
1120#ifdef USE_EVENT_BACKGROUND_THREAD
1121 std::lock_guard lock_frame_guard{win->frame_pending_mutex};
1122#endif
1124}
1125
1128/* -------------------------------------------------------------------- */
1135static int output_scale_cmp(const GWL_Output *output_a, const GWL_Output *output_b)
1136{
1137 if (output_a->scale < output_b->scale) {
1138 return -1;
1139 }
1140 if (output_a->scale > output_b->scale) {
1141 return 1;
1142 }
1143 if (output_a->has_scale_fractional || output_b->has_scale_fractional) {
1144 const int scale_fractional_a = output_a->has_scale_fractional ?
1145 output_a->scale_fractional :
1146 (output_a->scale * FRACTIONAL_DENOMINATOR);
1147 const int scale_fractional_b = output_b->has_scale_fractional ?
1148 output_b->scale_fractional :
1149 (output_b->scale * FRACTIONAL_DENOMINATOR);
1150 if (scale_fractional_a < scale_fractional_b) {
1151 return -1;
1152 }
1153 if (scale_fractional_a > scale_fractional_b) {
1154 return 1;
1155 }
1156 }
1157 return 0;
1158}
1159
1160static int outputs_max_scale_or_default(const std::vector<GWL_Output *> &outputs,
1161 const int32_t scale_default,
1162 int *r_scale_fractional)
1163{
1164 const GWL_Output *output_max = nullptr;
1165 for (const GWL_Output *reg_output : outputs) {
1166 if (!output_max || (output_scale_cmp(output_max, reg_output) == -1)) {
1167 output_max = reg_output;
1168 }
1169 }
1170
1171 if (output_max) {
1172 if (r_scale_fractional) {
1173 *r_scale_fractional = output_max->has_scale_fractional ?
1174 output_max->scale_fractional :
1175 (output_max->scale * FRACTIONAL_DENOMINATOR);
1176 }
1177 return output_max->scale;
1178 }
1179 if (r_scale_fractional) {
1180 *r_scale_fractional = scale_default * FRACTIONAL_DENOMINATOR;
1181 }
1182 return scale_default;
1183}
1184
1185static int outputs_uniform_scale_or_default(const std::vector<GWL_Output *> &outputs,
1186 const int32_t scale_default,
1187 int *r_scale_fractional)
1188{
1189 const GWL_Output *output_uniform = nullptr;
1190 for (const GWL_Output *reg_output : outputs) {
1191 if (!output_uniform) {
1192 output_uniform = reg_output;
1193 }
1194 else if (output_scale_cmp(output_uniform, reg_output) != 0) {
1195 /* Non-uniform. */
1196 output_uniform = nullptr;
1197 break;
1198 }
1199 }
1200
1201 if (output_uniform) {
1202 if (r_scale_fractional) {
1203 *r_scale_fractional = output_uniform->has_scale_fractional ?
1204 output_uniform->scale_fractional :
1205 (output_uniform->scale * FRACTIONAL_DENOMINATOR);
1206 }
1207 return output_uniform->scale;
1208 }
1209 if (r_scale_fractional) {
1210 *r_scale_fractional = scale_default * FRACTIONAL_DENOMINATOR;
1211 }
1212 return scale_default;
1213}
1214
1217/* -------------------------------------------------------------------- */
1223#ifdef USE_LIBDECOR_CONFIG_COPY_WORKAROUND
1224
1225static libdecor_configuration *ghost_wl_libdecor_configuration_copy(
1226 const libdecor_configuration *configuration)
1227{
1228 size_t configuration_size = malloc_usable_size((void *)configuration);
1229 libdecor_configuration *configuration_copy = (libdecor_configuration *)malloc(
1230 configuration_size);
1231 memcpy((void *)configuration_copy, (const void *)configuration, configuration_size);
1232 return configuration_copy;
1233}
1234
1235static void ghost_wl_libdecor_configuration_free(libdecor_configuration *configuration)
1236{
1237 free((void *)configuration);
1238}
1239
1240#endif /* USE_LIBDECOR_CONFIG_COPY_WORKAROUND */
1241
1244/* -------------------------------------------------------------------- */
1248static CLG_LogRef LOG_WL_XDG_TOPLEVEL = {"ghost.wl.handle.xdg_toplevel"};
1249#define LOG (&LOG_WL_XDG_TOPLEVEL)
1250
1251static void xdg_toplevel_handle_configure(void *data,
1252 xdg_toplevel * /*xdg_toplevel*/,
1253 const int32_t width,
1254 const int32_t height,
1255 wl_array *states)
1256{
1257 /* TODO: log `states`, not urgent. */
1258 CLOG_INFO(LOG, 2, "configure (size=[%d, %d])", width, height);
1259
1260 GWL_Window *win = static_cast<GWL_Window *>(data);
1261
1262#ifdef USE_EVENT_BACKGROUND_THREAD
1263 std::lock_guard lock_frame_guard{win->frame_pending_mutex};
1264#endif
1265
1266 const int32_t size[2] = {width, height};
1267 for (int i = 0; i < 2; i++) {
1268 if (size[i] == 0) {
1269 /* Values may be zero, in this case the client should choose. */
1270 continue;
1271 }
1272 win->frame_pending.size[i] = win->frame.fractional_scale ?
1274 (size[i] * win->frame.buffer_scale);
1275 }
1276
1277 win->frame_pending.is_maximised = false;
1278 win->frame_pending.is_fullscreen = false;
1279 win->frame_pending.is_active = false;
1280
1281 enum xdg_toplevel_state *state;
1282 WL_ARRAY_FOR_EACH (state, states) {
1283 switch (*state) {
1284 case XDG_TOPLEVEL_STATE_MAXIMIZED:
1285 win->frame_pending.is_maximised = true;
1286 break;
1287 case XDG_TOPLEVEL_STATE_FULLSCREEN:
1288 win->frame_pending.is_fullscreen = true;
1289 break;
1290 case XDG_TOPLEVEL_STATE_ACTIVATED:
1291 win->frame_pending.is_active = true;
1292 break;
1293 default:
1294 break;
1295 }
1296 }
1297}
1298
1299static void xdg_toplevel_handle_close(void *data, xdg_toplevel * /*xdg_toplevel*/)
1300{
1301 CLOG_INFO(LOG, 2, "close");
1302
1303 GWL_Window *win = static_cast<GWL_Window *>(data);
1304
1305 win->ghost_window->close();
1306}
1307
1309 xdg_toplevel * /*xdg_toplevel*/,
1310 int32_t width,
1311 int32_t height)
1312{
1313 /* Only available in interface version 4. */
1314 CLOG_INFO(LOG, 2, "configure_bounds (size=[%d, %d])", width, height);
1315
1316 /* No need to lock as this only runs on window creation. */
1317 GWL_Window *win = static_cast<GWL_Window *>(data);
1318 GWL_XDG_Decor_Window &decor = *win->xdg_decor;
1319 if (decor.initial_configure_seen == false) {
1320 decor.initial_bounds[0] = width;
1321 decor.initial_bounds[1] = height;
1322 }
1323}
1324static void xdg_toplevel_handle_wm_capabilities(void * /*data*/,
1325 xdg_toplevel * /*xdg_toplevel*/,
1326 wl_array * /*capabilities*/)
1327{
1328 /* Only available in interface version 5. */
1329 CLOG_INFO(LOG, 2, "wm_capabilities");
1330
1331 /* NOTE: this would be useful if blender had CSD. */
1332}
1333
1335 /*configure*/ xdg_toplevel_handle_configure,
1336 /*close*/ xdg_toplevel_handle_close,
1337 /*configure_bounds*/ xdg_toplevel_handle_configure_bounds,
1338 /*wm_capabilities*/ xdg_toplevel_handle_wm_capabilities,
1339};
1340
1341#undef LOG
1342
1345/* -------------------------------------------------------------------- */
1351static void xdg_activation_handle_done(void *data,
1352 xdg_activation_token_v1 *xdg_activation_token_v1,
1353 const char *token)
1354{
1355 GWL_Window *win = static_cast<GWL_Window *>(data);
1356 if (xdg_activation_token_v1 != win->xdg.activation_token) {
1357 return;
1358 }
1359
1360 GHOST_SystemWayland *system = win->ghost_system;
1361 xdg_activation_v1 *activation_manager = system->xdg_activation_manager_get();
1362 xdg_activation_v1_activate(activation_manager, token, win->wl.surface);
1363 xdg_activation_token_v1_destroy(win->xdg.activation_token);
1364 win->xdg.activation_token = nullptr;
1365}
1366
1367static const xdg_activation_token_v1_listener xdg_activation_listener = {
1369};
1370
1371static const xdg_activation_token_v1_listener *xdg_activation_listener_get()
1372{
1374}
1375
1378/* -------------------------------------------------------------------- */
1384static CLG_LogRef LOG_WL_FRACTIONAL_SCALE = {"ghost.wl.handle.fractional_scale"};
1385#define LOG (&LOG_WL_FRACTIONAL_SCALE)
1386
1388 void *data, wp_fractional_scale_v1 * /*wp_fractional_scale_v1*/, uint preferred_scale)
1389{
1390#ifdef USE_EVENT_BACKGROUND_THREAD
1391 std::lock_guard lock_frame_guard{static_cast<GWL_Window *>(data)->frame_pending_mutex};
1392#endif
1393 CLOG_INFO(LOG,
1394 2,
1395 "preferred_scale (preferred_scale=%.6f)",
1396 double(preferred_scale) / FRACTIONAL_DENOMINATOR);
1397
1398 GWL_Window *win = static_cast<GWL_Window *>(data);
1399
1400 if (win->frame_pending.fractional_scale_preferred != int(preferred_scale)) {
1401 win->frame_pending.fractional_scale_preferred = preferred_scale;
1403 }
1404}
1405
1406static const wp_fractional_scale_v1_listener wp_fractional_scale_listener = {
1408};
1409
1410#undef LOG
1411
1414/* -------------------------------------------------------------------- */
1418#ifdef WITH_GHOST_WAYLAND_LIBDECOR
1419
1420static CLG_LogRef LOG_WL_LIBDECOR_FRAME = {"ghost.wl.handle.libdecor_frame"};
1421# define LOG (&LOG_WL_LIBDECOR_FRAME)
1422
1423static void libdecor_frame_handle_configure(libdecor_frame *frame,
1424 libdecor_configuration *configuration,
1425 void *data)
1426{
1427 CLOG_INFO(LOG, 2, "configure");
1428
1429# ifdef USE_EVENT_BACKGROUND_THREAD
1430 std::lock_guard lock_frame_guard{static_cast<GWL_Window *>(data)->frame_pending_mutex};
1431 const bool is_main_thread = [data] {
1432 const GWL_Window *win = static_cast<GWL_Window *>(data);
1433 const GHOST_SystemWayland *system = win->ghost_system;
1434 return system->main_thread_id == std::this_thread::get_id();
1435 }();
1436# endif
1437
1438 GWL_WindowFrame &frame_pending = static_cast<GWL_Window *>(data)->frame_pending;
1439
1440 /* Set the size. */
1441 int size_next[2] = {0, 0};
1442
1443 {
1444 GWL_Window *win = static_cast<GWL_Window *>(data);
1445 const int fractional_scale = win->frame.fractional_scale;
1446 /* It's important `fractional_scale` has a fractional component or rounding up will fail
1447 * to produce the correct whole-number scale. */
1448 GHOST_ASSERT((fractional_scale == 0) ||
1449 (gwl_round_int_test(fractional_scale, FRACTIONAL_DENOMINATOR) == false),
1450 "Fractional scale has no fractional component!");
1451 /* The size from LIBDECOR wont use the GHOST windows buffer size.
1452 * so it's important to calculate the buffer size that would have been used
1453 * if fractional scaling wasn't supported. */
1454 const int scale = fractional_scale ? (fractional_scale / FRACTIONAL_DENOMINATOR) + 1 :
1455 win->frame.buffer_scale;
1456 const int scale_as_fractional = scale * FRACTIONAL_DENOMINATOR;
1458 configuration, frame, &size_next[0], &size_next[1]))
1459 {
1460 if (fractional_scale) {
1461 frame_pending.size[0] = gwl_window_fractional_to_viewport_round(win->frame, size_next[0]);
1462 frame_pending.size[1] = gwl_window_fractional_to_viewport_round(win->frame, size_next[1]);
1463 }
1464 else if (fractional_scale && (fractional_scale != (scale * FRACTIONAL_DENOMINATOR))) {
1465 /* The windows `preferred_scale` is not yet available,
1466 * set the size as if fractional scale is available. */
1467 frame_pending.size[0] = ((size_next[0] * scale) * fractional_scale) / scale_as_fractional;
1468 frame_pending.size[1] = ((size_next[1] * scale) * fractional_scale) / scale_as_fractional;
1469 }
1470 else {
1471 frame_pending.size[0] = size_next[0] * scale;
1472 frame_pending.size[1] = size_next[1] * scale;
1473 }
1474
1475 /* Account for buffer rounding requirement, once fractional scaling is enabled
1476 * the buffer scale will be 1, rounding is a requirement until then. */
1477 gwl_round_int2_by(frame_pending.size, win->frame.buffer_scale);
1478 }
1479 else {
1480 /* These values are cleared after use & will practically always be zero.
1481 * Read them because it's possible multiple configure calls run before they can be handled.
1482 */
1483 const GWL_LibDecor_Window &decor = *win->libdecor;
1484 size_next[0] = decor.pending.size[0];
1485 size_next[1] = decor.pending.size[1];
1486 }
1487 }
1488
1489 /* Set the state. */
1490 {
1491 enum libdecor_window_state window_state;
1492 if (libdecor_configuration_get_window_state(configuration, &window_state)) {
1493 frame_pending.is_maximised = window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED;
1494 frame_pending.is_fullscreen = window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN;
1495 frame_pending.is_active = window_state & LIBDECOR_WINDOW_STATE_ACTIVE;
1496 }
1497 }
1498
1499 {
1500 GWL_Window *win = static_cast<GWL_Window *>(data);
1501 GWL_LibDecor_Window &decor = *win->libdecor;
1502
1503# ifdef USE_LIBDECOR_CONFIG_COPY_WORKAROUND
1504 /* Unlikely but possible a previous configuration is unhandled. */
1505 if (decor.pending.configuration_needs_free) {
1506 ghost_wl_libdecor_configuration_free(decor.pending.configuration);
1507 decor.pending.configuration_needs_free = false;
1508 }
1509# endif /* USE_LIBDECOR_CONFIG_COPY_WORKAROUND */
1510
1511 decor.pending.size[0] = size_next[0];
1512 decor.pending.size[1] = size_next[1];
1513 decor.pending.configuration = configuration;
1514 decor.pending.ack_configure = true;
1515
1516# ifdef USE_EVENT_BACKGROUND_THREAD
1517 if (!is_main_thread) {
1518# ifdef USE_LIBDECOR_CONFIG_COPY_WORKAROUND
1519 decor.pending.configuration = ghost_wl_libdecor_configuration_copy(configuration);
1520 decor.pending.configuration_needs_free = true;
1521# else
1522 /* Without a way to copy the configuration,
1523 * the configuration will be ignored as it can't be postponed. */
1524 decor.pending.configuration = nullptr;
1525# endif /* !USE_LIBDECOR_CONFIG_COPY_WORKAROUND */
1526 }
1527# endif
1528
1529# ifdef USE_LIBDECOR_CONFIG_COPY_QUEUE
1530 if (!(size_next[0] && size_next[1])) {
1531 /* Always copy. */
1532 if (decor.pending.configuration_needs_free == false) {
1533 decor.pending.configuration = ghost_wl_libdecor_configuration_copy(
1534 decor.pending.configuration);
1535 decor.pending.configuration_needs_free = true;
1536 }
1537 /* Transfer ownership to the queue. */
1538 decor.pending.configuration_queue.push_back(decor.pending.configuration);
1539 decor.pending.configuration = nullptr;
1540 decor.pending.configuration_needs_free = false;
1541 /* Wait until we have a valid size. */
1542 decor.pending.ack_configure = false;
1543 }
1544# endif /* USE_LIBDECOR_CONFIG_COPY_QUEUE */
1545 }
1546
1547 /* Apply & commit the changes. */
1548 {
1549 GWL_Window *win = static_cast<GWL_Window *>(data);
1550# ifdef USE_EVENT_BACKGROUND_THREAD
1551 if (!is_main_thread) {
1553 }
1554 else
1555# endif
1556 {
1558 }
1559 }
1560}
1561
1562static void libdecor_frame_handle_close(libdecor_frame * /*frame*/, void *data)
1563{
1564 CLOG_INFO(LOG, 2, "close");
1565
1566 GWL_Window *win = static_cast<GWL_Window *>(data);
1567
1568 win->ghost_window->close();
1569}
1570
1571static void libdecor_frame_handle_commit(libdecor_frame * /*frame*/, void *data)
1572{
1573 CLOG_INFO(LOG, 2, "commit");
1574
1575# if 0
1576 GWL_Window *win = static_cast<GWL_Window *>(data);
1578# else
1579 (void)data;
1580# endif
1581}
1582
1583/* NOTE: cannot be `const` because of the LIBDECOR API. */
1584static libdecor_frame_interface libdecor_frame_iface = {
1585 /*configure*/ libdecor_frame_handle_configure,
1586 /*close*/ libdecor_frame_handle_close,
1587 /*commit*/ libdecor_frame_handle_commit,
1588};
1589
1590# undef LOG
1591
1592#endif /* WITH_GHOST_WAYLAND_LIBDECOR. */
1593
1596/* -------------------------------------------------------------------- */
1600static CLG_LogRef LOG_WL_XDG_TOPLEVEL_DECORATION = {"ghost.wl.handle.xdg_toplevel_decoration"};
1601#define LOG (&LOG_WL_XDG_TOPLEVEL_DECORATION)
1602
1604 void *data, zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/, const uint32_t mode)
1605{
1606 CLOG_INFO(LOG, 2, "configure (mode=%u)", mode);
1607
1608 GWL_Window *win = static_cast<GWL_Window *>(data);
1609
1610 win->xdg_decor->mode = (zxdg_toplevel_decoration_v1_mode)mode;
1611}
1612
1613static const zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_v1_listener = {
1615};
1616
1617#undef LOG
1618
1621/* -------------------------------------------------------------------- */
1625static CLG_LogRef LOG_WL_XDG_SURFACE = {"ghost.wl.handle.xdg_surface"};
1626#define LOG (&LOG_WL_XDG_SURFACE)
1627
1628static void xdg_surface_handle_configure(void *data,
1629 xdg_surface *xdg_surface,
1630 const uint32_t serial)
1631{
1632 GWL_Window *win = static_cast<GWL_Window *>(data);
1633
1634 if (win->xdg_decor->surface != xdg_surface) {
1635 CLOG_INFO(LOG, 2, "configure (skipped)");
1636 return;
1637 }
1638 CLOG_INFO(LOG, 2, "configure");
1639
1640#ifdef USE_EVENT_BACKGROUND_THREAD
1641 std::lock_guard lock_frame_guard{static_cast<GWL_Window *>(data)->frame_pending_mutex};
1642#endif
1643 win->xdg_decor->pending.ack_configure = true;
1644 win->xdg_decor->pending.ack_configure_serial = serial;
1645
1646#ifdef USE_EVENT_BACKGROUND_THREAD
1647 const GHOST_SystemWayland *system = win->ghost_system;
1648 const bool is_main_thread = system->main_thread_id == std::this_thread::get_id();
1649 if (!is_main_thread) {
1650 /* NOTE(@ideasman42): this only gets one redraw,
1651 * I could not find a case where this causes problems. */
1653 }
1654 else
1655#endif
1656 {
1658 }
1659}
1660
1664
1665#undef LOG
1666
1669/* -------------------------------------------------------------------- */
1673static CLG_LogRef LOG_WL_SURFACE = {"ghost.wl.handle.surface"};
1674#define LOG (&LOG_WL_SURFACE)
1675
1676static void surface_handle_enter(void *data, wl_surface * /*wl_surface*/, wl_output *wl_output)
1677{
1678 if (!ghost_wl_output_own(wl_output)) {
1679 CLOG_INFO(LOG, 2, "enter (skipped)");
1680 return;
1681 }
1682 CLOG_INFO(LOG, 2, "enter");
1683
1684 GWL_Output *reg_output = ghost_wl_output_user_data(wl_output);
1685 GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(data);
1686 if (win->outputs_enter(reg_output)) {
1688 }
1689}
1690
1691static void surface_handle_leave(void *data, wl_surface * /*wl_surface*/, wl_output *wl_output)
1692{
1693 if (!ghost_wl_output_own(wl_output)) {
1694 CLOG_INFO(LOG, 2, "leave (skipped)");
1695 return;
1696 }
1697 CLOG_INFO(LOG, 2, "leave");
1698
1699 GWL_Output *reg_output = ghost_wl_output_user_data(wl_output);
1700 GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(data);
1701 if (win->outputs_leave(reg_output)) {
1703 }
1704}
1705
1706#if defined(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION) && \
1707 defined(WL_SURFACE_PREFERRED_BUFFER_TRANSFORM_SINCE_VERSION)
1708static void surface_handle_preferred_buffer_scale(void * /*data*/,
1709 wl_surface * /*wl_surface*/,
1710 int32_t factor)
1711{
1712 /* Only available in interface version 6. */
1713 CLOG_INFO(LOG, 2, "handle_preferred_buffer_scale (factor=%d)", factor);
1714}
1715
1716static void surface_handle_preferred_buffer_transform(void * /*data*/,
1717 wl_surface * /*wl_surface*/,
1718 uint32_t transform)
1719{
1720 /* Only available in interface version 6. */
1721 CLOG_INFO(LOG, 2, "handle_preferred_buffer_transform (transform=%u)", transform);
1722}
1723#endif /* WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION && \
1724 * WL_SURFACE_PREFERRED_BUFFER_TRANSFORM_SINCE_VERSION */
1725
1727 /*enter*/ surface_handle_enter,
1728 /*leave*/ surface_handle_leave,
1729#if defined(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION) && \
1730 defined(WL_SURFACE_PREFERRED_BUFFER_TRANSFORM_SINCE_VERSION)
1731 /*preferred_buffer_scale*/ surface_handle_preferred_buffer_scale,
1732 /*preferred_buffer_transform*/ surface_handle_preferred_buffer_transform,
1733#endif
1734};
1735
1736#undef LOG
1737
1740/* -------------------------------------------------------------------- */
1747 const char *title,
1748 const int32_t /*left*/,
1749 const int32_t /*top*/,
1750 const uint32_t width,
1751 const uint32_t height,
1753 const GHOST_IWindow *parentWindow,
1754 const GHOST_TDrawingContextType type,
1755 const bool is_dialog,
1756 const bool stereoVisual,
1757 const bool exclusive,
1758 const bool is_debug,
1759 const GHOST_GPUDevice &preferred_device)
1760 : GHOST_Window(width, height, state, stereoVisual, exclusive),
1761 system_(system),
1762 window_(new GWL_Window),
1763 is_debug_context_(is_debug),
1764 preferred_device_(preferred_device)
1765{
1766#ifdef USE_EVENT_BACKGROUND_THREAD
1767 std::lock_guard lock_server_guard{*system->server_mutex};
1768#endif
1769
1770 window_->ghost_window = this;
1771 window_->ghost_system = system;
1772 window_->ghost_context_type = type;
1773
1774 wl_display *display = system->wl_display_get();
1775
1776 /* NOTE(@ideasman42): The scale set here to avoid flickering on startup.
1777 * When all monitors use the same scale (which is quite common) there aren't any problems.
1778 *
1779 * When monitors have different scales there may still be a visible window resize on startup.
1780 * Ideally it would be possible to know the scale this window will use however that's only
1781 * known once #surface_enter callback runs (which isn't guaranteed to run at all).
1782 *
1783 * Using the maximum scale is best as it results in the window first being smaller,
1784 * avoiding a large window flashing before it's made smaller.
1785 *
1786 * For fractional scaling the buffer will eventually be 1. Setting it to 1 now
1787 * (to avoid window size rounding and buffer size switching) has some down-sides.
1788 * It means the window will be drawn larger for a moment then smaller once fractional scaling
1789 * is detected and enabled. Unfortunately, it doesn't seem possible to receive the
1790 * #wp_fractional_scale_v1_listener::preferred_scale information before the window is created
1791 * So leave the buffer scaled up because there is no *guarantee* the fractional scaling support
1792 * will run which could result in an incorrect buffer scale. */
1793 int scale_fractional_from_output;
1794 const int buffer_scale_from_output = outputs_uniform_scale_or_default(
1795 system_->outputs_get(), 0, &scale_fractional_from_output);
1796
1797 window_->frame.size[0] = int32_t(width);
1798 window_->frame.size[1] = int32_t(height);
1799
1800 window_->is_dialog = is_dialog;
1801
1802 /* Window surfaces. */
1803 window_->wl.surface = wl_compositor_create_surface(system_->wl_compositor_get());
1805
1806 wl_surface_add_listener(window_->wl.surface, &wl_surface_listener, window_);
1807
1808 wp_fractional_scale_manager_v1 *fractional_scale_manager =
1810 if (fractional_scale_manager) {
1811 window_->wp.fractional_scale_handle = wp_fractional_scale_manager_v1_get_fractional_scale(
1812 fractional_scale_manager, window_->wl.surface);
1813 wp_fractional_scale_v1_add_listener(
1815 }
1816
1817 /* NOTE: The limit is in points (not pixels) so Hi-DPI will limit to larger number of pixels.
1818 * This has the advantage that the size limit is the same when moving the window between monitors
1819 * with different scales set. If it was important to limit in pixels it could be re-calculated
1820 * when the `window_->frame.buffer_scale` changed. */
1821 const int32_t size_min[2] = {320, 240};
1822
1823 const char *xdg_app_id = GHOST_SystemWayland::xdg_app_id_get();
1824
1825#ifdef WITH_GHOST_WAYLAND_LIBDECOR
1826 if (use_libdecor) {
1827 window_->libdecor = new GWL_LibDecor_Window;
1828 GWL_LibDecor_Window &decor = *window_->libdecor;
1829
1830 /* create window decorations */
1831 decor.frame = libdecor_decorate(
1832 system_->libdecor_context_get(), window_->wl.surface, &libdecor_frame_iface, window_);
1833
1834 libdecor_frame_set_min_content_size(decor.frame, UNPACK2(size_min));
1835 libdecor_frame_set_app_id(decor.frame, xdg_app_id);
1836
1837 if (parentWindow) {
1838 GWL_LibDecor_Window &decor_parent =
1839 *dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->window_->libdecor;
1840 libdecor_frame_set_parent(decor.frame, decor_parent.frame);
1841 }
1842 }
1843 else
1844#endif
1845 {
1846 window_->xdg_decor = new GWL_XDG_Decor_Window;
1847 GWL_XDG_Decor_Window &decor = *window_->xdg_decor;
1848 decor.surface = xdg_wm_base_get_xdg_surface(system_->xdg_decor_shell_get(),
1849 window_->wl.surface);
1850 decor.toplevel = xdg_surface_get_toplevel(decor.surface);
1851
1852 xdg_toplevel_set_min_size(decor.toplevel, UNPACK2(size_min));
1853 xdg_toplevel_set_app_id(decor.toplevel, xdg_app_id);
1854
1855 xdg_surface_add_listener(decor.surface, &xdg_surface_listener, window_);
1856 xdg_toplevel_add_listener(decor.toplevel, &xdg_toplevel_listener, window_);
1857
1858 if (parentWindow && is_dialog) {
1859 GWL_XDG_Decor_Window &decor_parent =
1860 *dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->window_->xdg_decor;
1861 xdg_toplevel_set_parent(decor.toplevel, decor_parent.toplevel);
1862 }
1863 }
1864
1865 gwl_window_title_set(window_, title);
1866
1867#ifdef WITH_GHOST_WAYLAND_LIBDECOR
1868 if (use_libdecor) {
1869 /* Postpone mapping the window until after the app-id & title have been set.
1870 * While this doesn't seem to be a requirement, LIBDECOR example code does this. */
1871 libdecor_frame_map(window_->libdecor->frame);
1872 }
1873#endif
1874
1875 wl_surface_set_user_data(window_->wl.surface, this);
1876
1877 /* NOTE: the method used for XDG & LIBDECOR initialization (using `initial_configure_seen`)
1878 * follows the method used in SDL 3.16. */
1879
1880 /* Causes a glitch with `libdecor` for some reason. */
1881#ifdef WITH_GHOST_WAYLAND_LIBDECOR
1882 if (use_libdecor) {
1883 /* Pass. */
1884 }
1885 else
1886#endif /* WITH_GHOST_WAYLAND_LIBDECOR */
1887 {
1888 GWL_XDG_Decor_Window &decor = *window_->xdg_decor;
1889
1890 if (system_->xdg_decor_manager_get()) {
1891 decor.toplevel_decor = zxdg_decoration_manager_v1_get_toplevel_decoration(
1892 system_->xdg_decor_manager_get(), decor.toplevel);
1893 zxdg_toplevel_decoration_v1_add_listener(
1895 zxdg_toplevel_decoration_v1_set_mode(decor.toplevel_decor,
1896 ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
1897 }
1898
1899 /* Commit needed to so configure callback runs. */
1900 wl_surface_commit(window_->wl.surface);
1901
1902 /* Failure exits with an error, simply prevent an eternal loop. */
1904 wl_display_flush(display);
1905 wl_display_dispatch(display);
1906 }
1907 }
1908
1909 /* If the scale is known early, setup the window scale.
1910 * Otherwise accept an unsightly flicker once the outputs scale can be found. */
1911 int early_buffer_scale = 0;
1912 int early_fractional_scale = 0;
1913
1914 if (const int test_fractional_scale =
1915 fractional_scale_manager ? (window_->frame_pending.fractional_scale_preferred ?
1917 scale_fractional_from_output) :
1918 0)
1919 {
1920 if (!gwl_round_int_test(test_fractional_scale, FRACTIONAL_DENOMINATOR)) {
1921 early_fractional_scale = test_fractional_scale;
1922 }
1923 else {
1924 /* Rounded, use simple integer buffer scaling. */
1925 early_buffer_scale = test_fractional_scale / FRACTIONAL_DENOMINATOR;
1926 early_fractional_scale = 0;
1927 }
1928 }
1929 else if (buffer_scale_from_output) {
1930 early_buffer_scale = buffer_scale_from_output;
1931 }
1932
1933 if (early_fractional_scale != 0) {
1934 /* Fractional scale is known. */
1935
1936 window_->frame.fractional_scale_preferred = early_fractional_scale;
1937 window_->frame.fractional_scale = early_fractional_scale;
1938 window_->frame.buffer_scale = 1;
1939
1940 window_->frame_pending.fractional_scale_preferred = early_fractional_scale;
1941 window_->frame_pending.fractional_scale = early_fractional_scale;
1942 window_->frame_pending.buffer_scale = 1;
1943
1944 /* The scale is considered initialized now. */
1945 window_->frame_pending.is_scale_init = true;
1946
1947 /* Always commit and set the scale. */
1948 bool surface_needs_commit_dummy = false, surface_needs_buffer_scale_dummy = false;
1950 window_, &surface_needs_commit_dummy, &surface_needs_buffer_scale_dummy);
1951 }
1952 else if (early_buffer_scale != 0) {
1953 /* Non-fractional scale is known. */
1954
1955 /* No fractional scale, simple initialization. */
1956 window_->frame.buffer_scale = early_buffer_scale;
1957 window_->frame_pending.buffer_scale = early_buffer_scale;
1958
1959 /* The scale is considered initialized now. */
1960 window_->frame_pending.is_scale_init = true;
1961
1962 /* The window surface must be rounded to the scale,
1963 * failing to do so causes the WAYLAND-server to close the window immediately. */
1964 gwl_round_int2_by(window_->frame.size, window_->frame.buffer_scale);
1965 }
1966 else {
1967 /* Scale isn't known (the windows size may flicker when #outputs_changed_update_scale runs). */
1968 window_->frame.buffer_scale = 1;
1969 window_->frame_pending.buffer_scale = 1;
1970 GHOST_ASSERT(window_->frame_pending.is_scale_init == false,
1971 "An initialized scale is not expected");
1972 }
1973
1974 if (window_->frame_pending.is_scale_init) {
1975 /* If the output scale changes here it means the scale settings were not properly set. */
1977 "Fractional scale was not properly initialized");
1978 }
1979
1980 wl_surface_set_buffer_scale(window_->wl.surface, window_->frame.buffer_scale);
1981
1982 /* Apply Bounds.
1983 * Important to run after the buffer scale is known & before the buffer is created. */
1984#ifdef WITH_GHOST_WAYLAND_LIBDECOR
1985 if (use_libdecor) {
1986 /* Pass (unsupported). */
1987 }
1988 else
1989#endif /* WITH_GHOST_WAYLAND_LIBDECOR */
1990 {
1991 const GWL_XDG_Decor_Window &decor = *window_->xdg_decor;
1992 if (decor.initial_bounds[0] && decor.initial_bounds[1]) {
1993 window_->frame.size[0] = std::min(window_->frame.size[0],
1994 decor.initial_bounds[0] * window_->frame.buffer_scale);
1995 window_->frame.size[1] = std::min(window_->frame.size[1],
1996 decor.initial_bounds[1] * window_->frame.buffer_scale);
1997 }
1998 }
1999
2000/* Postpone binding the buffer until after it's decor has been configured:
2001 * - Ensure the window is sized properly (with XDG window decorations), see: #113059.
2002 * - Avoids flickering on startup.
2003 */
2004#ifdef WITH_OPENGL_BACKEND
2005 if (type == GHOST_kDrawingContextTypeOpenGL) {
2006 window_->backend.egl_window = wl_egl_window_create(
2007 window_->wl.surface, int(window_->frame.size[0]), int(window_->frame.size[1]));
2008 }
2009#endif
2010#ifdef WITH_VULKAN_BACKEND
2011 if (type == GHOST_kDrawingContextTypeVulkan) {
2012 window_->backend.vulkan_window_info = new GHOST_ContextVK_WindowInfo;
2013 window_->backend.vulkan_window_info->size[0] = window_->frame.size[0];
2014 window_->backend.vulkan_window_info->size[1] = window_->frame.size[1];
2015 }
2016#endif
2017
2018 /* Drawing context. */
2020 /* This can happen when repeatedly creating windows, see #123096.
2021 * In this case #GHOST_WindowWayland::getValid will return false. */
2022 GHOST_PRINT("Failed to create drawing context" << std::endl);
2023 }
2024 else {
2025 window_->is_valid_setup = true;
2026 }
2027
2028 if (window_->is_valid_setup == false) {
2029 /* Don't attempt to setup the window if there is no context.
2030 * This window is considered invalid and will be removed. */
2031 }
2032 else
2033#ifdef WITH_GHOST_WAYLAND_LIBDECOR
2034 if (use_libdecor)
2035 {
2036 /* Commit needed so the top-level callbacks run (and `toplevel` can be accessed). */
2037 wl_surface_commit(window_->wl.surface);
2038 GWL_LibDecor_Window &decor = *window_->libdecor;
2039
2040 /* Additional round-trip is needed to ensure `xdg_toplevel` is set. */
2041 wl_display_roundtrip(display);
2042
2043 /* NOTE: LIBDECOR requires the window to be created & configured before the state can be set.
2044 * Workaround this by using the underlying `xdg_toplevel` */
2045
2046# ifdef WITH_VULKAN_BACKEND
2047 /* A dummy buffer is needed for VULKAN & LIBDECOR,
2048 * otherwise `decor.initial_configure_seen` and startup locks up. */
2049 wl_buffer *dummy_buffer = nullptr;
2050 if (window_->ghost_context_type == GHOST_kDrawingContextTypeVulkan) {
2051 const uint32_t format = WL_SHM_FORMAT_ARGB8888;
2052 const int format_size = 4;
2053 const int buffer_size = (window_->frame.size[0] * window_->frame.size[1]) * format_size;
2054 const int fd = memfd_create_sealed_for_vulkan_hack("ghost-wl-dummy-buffer");
2055 ftruncate(fd, buffer_size);
2056 wl_shm *shm = system_->wl_shm_get();
2057 wl_shm_pool *pool = wl_shm_create_pool(shm, fd, buffer_size);
2058 dummy_buffer = wl_shm_pool_create_buffer(pool,
2059 0,
2060 window_->frame.size[0],
2061 window_->frame.size[1],
2062 window_->frame.size[0] * format_size,
2063 format);
2064 wl_shm_pool_destroy(pool);
2065
2066 wl_surface_attach(window_->wl.surface, dummy_buffer, 0, 0);
2067 wl_surface_damage(window_->wl.surface, 0, 0, window_->frame.size[0], window_->frame.size[1]);
2068 wl_surface_commit(window_->wl.surface);
2069 ::close(fd);
2070 }
2071# endif /* WITH_VULKAN_BACKEND */
2072
2073 /* Failure exits with an error, simply prevent an eternal loop. */
2074 while (!decor.initial_configure_seen && !ghost_wl_display_report_error_if_set(display)) {
2075 wl_display_flush(display);
2076 wl_display_dispatch(display);
2077 }
2078
2079# ifdef WITH_VULKAN_BACKEND
2080 if (window_->ghost_context_type == GHOST_kDrawingContextTypeVulkan) {
2081 wl_surface_attach(window_->wl.surface, nullptr, 0, 0);
2082 wl_surface_commit(window_->wl.surface);
2083 wl_buffer_destroy(dummy_buffer);
2084 }
2085# endif /* WITH_GHOST_WAYLAND_LIBDECOR */
2086
2087 xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel(decor.frame);
2089
2090 /* NOTE(@ideasman42): Round trips are necessary with LIBDECOR on GNOME
2091 * because resizing later on and redrawing does *not* update as it should, see #119871.
2092 *
2093 * Without the round-trip here:
2094 * - The window will be created and this function will return using the requested buffer size,
2095 * instead of the window size which ends up being used (causing a visible flicker).
2096 * This has the down side that Blender's internal window state has the outdated size
2097 * which then gets immediately resized, causing a noticeable glitch.
2098 * - The window decorations will be displayed at the wrong size before refreshing
2099 * at the new size.
2100 * - On GNOME-Shell 46 shows the previous buffer-size under some conditions.
2101 *
2102 * In principle this could be used with XDG too however it causes problems with KDE
2103 * and some WLROOTS based compositors.
2104 */
2105 wl_display_roundtrip(display);
2106 }
2107 else
2108#endif /* WITH_GHOST_WAYLAND_LIBDECOR */
2109 {
2110 gwl_window_state_set(window_, state);
2111 }
2112
2113 /* Commit after setting the buffer.
2114 * While postponing until after the buffer drawing is context is set
2115 * isn't essential, it reduces flickering. */
2116 wl_surface_commit(window_->wl.surface);
2117
2118 window_->is_init = true;
2119
2120 /* Set swap interval to 0 to prevent blocking. */
2121 setSwapInterval(0);
2122}
2123
2125{
2126#ifdef USE_EVENT_BACKGROUND_THREAD
2127 std::lock_guard lock_server_guard{*system_->server_mutex};
2128#endif
2129
2131
2132#ifdef WITH_OPENGL_BACKEND
2133 if (window_->ghost_context_type == GHOST_kDrawingContextTypeOpenGL) {
2134 wl_egl_window_destroy(window_->backend.egl_window);
2135 }
2136#endif
2137#ifdef WITH_VULKAN_BACKEND
2138 if (window_->ghost_context_type == GHOST_kDrawingContextTypeVulkan) {
2139 delete window_->backend.vulkan_window_info;
2140 }
2141#endif
2142
2143 if (window_->xdg.activation_token) {
2144 xdg_activation_token_v1_destroy(window_->xdg.activation_token);
2145 window_->xdg.activation_token = nullptr;
2146 }
2147
2148 if (window_->wp.fractional_scale_handle) {
2149 wp_fractional_scale_v1_destroy(window_->wp.fractional_scale_handle);
2150 window_->wp.fractional_scale_handle = nullptr;
2151 }
2152
2153 if (window_->wp.viewport) {
2154 wp_viewport_destroy(window_->wp.viewport);
2155 window_->wp.viewport = nullptr;
2156 }
2157
2158#ifdef WITH_GHOST_WAYLAND_LIBDECOR
2159 if (use_libdecor) {
2160 gwl_libdecor_window_destroy(window_->libdecor);
2161 }
2162 else
2163#endif
2164 {
2166 }
2167
2168 /* Clear any pointers to this window. This is needed because there are no guarantees
2169 * that flushing the display will the "leave" handlers before handling events. */
2170 system_->window_surface_unref(window_->wl.surface);
2171
2172 wl_surface_destroy(window_->wl.surface);
2173
2174 /* NOTE(@ideasman42): Flushing will often run the appropriate handlers event
2175 * (#wl_surface_listener.leave in particular) to avoid attempted access to the freed surfaces.
2176 * This is not fool-proof though, hence the call to #window_surface_unref, see: #99078. */
2177 wl_display_flush(system_->wl_display_get());
2178
2180
2181 delete window_;
2182}
2183
2184#ifdef USE_EVENT_BACKGROUND_THREAD
2186{
2187 GHOST_ASSERT(system_->main_thread_id == std::this_thread::get_id(), "Only from main thread!");
2189}
2190#endif /* USE_EVENT_BACKGROUND_THREAD */
2191
2193{
2194 return system_->cursor_shape_check(cursorShape);
2195}
2196
2198{
2199#ifdef USE_EVENT_BACKGROUND_THREAD
2200 std::lock_guard lock_server_guard{*system_->server_mutex};
2201#endif
2202
2203 GHOST_Rect bounds_buf;
2204 const GHOST_Rect *bounds = nullptr;
2206 if (getCursorGrabBounds(bounds_buf) == GHOST_kFailure) {
2207 getClientBounds(bounds_buf);
2208 }
2209 bounds = &bounds_buf;
2210 }
2211
2212 if (system_->window_cursor_grab_set(mode,
2215 bounds,
2217 window_->wl.surface,
2218 this->scale_params_get()))
2219 {
2220 return GHOST_kSuccess;
2221 }
2222 return GHOST_kFailure;
2223}
2224
2226{
2227#ifdef USE_EVENT_BACKGROUND_THREAD
2228 std::lock_guard lock_server_guard{*system_->server_mutex};
2229#endif
2230
2231 const bool is_active = this == static_cast<const GHOST_WindowWayland *>(
2232 system_->getWindowManager()->getActiveWindow());
2234 m_cursorShape = shape;
2235
2236 GHOST_TSuccess ok;
2237 if (is_active) {
2238 ok = system_->cursor_shape_set(m_cursorShape);
2239 GHOST_TSuccess ok_test = ok;
2240 if (ok == GHOST_kFailure) {
2241 /* Failed, try again with the default cursor. */
2243 ok_test = system_->cursor_shape_set(m_cursorShape);
2244 }
2245
2246 if (ok_test == GHOST_kFailure) {
2247 /* For the cursor to display when the event queue isn't being handled. */
2248 wl_display_flush(system_->wl_display_get());
2249 }
2250 }
2251 else {
2252 /* Set later when activating the window. */
2253 ok = system_->cursor_shape_check(shape);
2254 if (ok == GHOST_kFailure) {
2256 }
2257 }
2258 return ok;
2259}
2260
2262{
2263#ifdef USE_EVENT_BACKGROUND_THREAD
2264 std::lock_guard lock_server_guard{*system_->server_mutex};
2265#endif
2267}
2268
2270 uint8_t *bitmap, uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor)
2271{
2272#ifdef USE_EVENT_BACKGROUND_THREAD
2273 std::lock_guard lock_server_guard{*system_->server_mutex};
2274#endif
2275
2276 const bool is_active = this == static_cast<const GHOST_WindowWayland *>(
2277 system_->getWindowManager()->getActiveWindow());
2278 const int32_t size[2] = {sizex, sizey};
2279 const int32_t hot_spot[2] = {hotX, hotY};
2280
2282 window_->cursor_custom_shape, bitmap, mask, size, hot_spot, canInvertColor);
2284
2285 GHOST_TSuccess ok;
2286 if (is_active) {
2287 ok = gwl_window_cursor_custom_load(window_->cursor_custom_shape, system_);
2288 GHOST_TSuccess ok_test = ok;
2289 if (ok == GHOST_kFailure) {
2290 /* Failed, try again with the default cursor. */
2292 ok_test = system_->cursor_shape_set(m_cursorShape);
2293 }
2294 if (ok_test == GHOST_kSuccess) {
2295 /* For the cursor to display when the event queue isn't being handled. */
2296 wl_display_flush(system_->wl_display_get());
2297 }
2298 }
2299 else {
2300 /* Set later when activating the window. */
2301 ok = GHOST_kSuccess;
2302 }
2303 return ok;
2304}
2305
2307{
2308#ifdef USE_EVENT_BACKGROUND_THREAD
2309 std::lock_guard lock_server_guard{*system_->server_mutex};
2310#endif
2311 return system_->cursor_bitmap_get(bitmap);
2312}
2313
2315{
2316 return GHOST_Window::getValid() && window_->is_valid_setup;
2317}
2318
2319void GHOST_WindowWayland::setTitle(const char *title)
2320{
2321#ifdef USE_EVENT_BACKGROUND_THREAD
2322 std::lock_guard lock_server_guard{*system_->server_mutex};
2323#endif
2324 gwl_window_title_set(window_, title);
2325}
2326
2328{
2329 /* No need to lock `server_mutex` (WAYLAND never changes this). */
2330 return window_->title.empty() ? "untitled" : window_->title;
2331}
2332
2337
2339{
2340 /* No need to lock `server_mutex` (WAYLAND never changes this in a thread). */
2341 bounds.set(0, 0, UNPACK2(window_->frame.size));
2342}
2343
2345{
2346 return setClientSize(width, uint32_t(window_->frame.size[1]));
2347}
2348
2350{
2351 return setClientSize(uint32_t(window_->frame.size[0]), height);
2352}
2353
2355{
2356#ifdef USE_EVENT_BACKGROUND_THREAD
2357 std::lock_guard lock_server_guard{*system_->server_mutex};
2358 std::lock_guard lock_frame_guard{window_->frame_pending_mutex};
2359#endif
2360
2361 GWL_WindowFrame &frame_pending = window_->frame_pending;
2362
2363 frame_pending.size[0] = width;
2364 frame_pending.size[1] = height;
2365 gwl_round_int2_by(frame_pending.size, frame_pending.buffer_scale);
2366
2367 gwl_window_frame_pending_size_set(window_, nullptr, nullptr, nullptr);
2368
2369 return GHOST_kSuccess;
2370}
2371
2373 int32_t inY,
2374 int32_t &outX,
2375 int32_t &outY) const
2376{
2377 outX = inX;
2378 outY = inY;
2379}
2380
2382 int32_t inY,
2383 int32_t &outX,
2384 int32_t &outY) const
2385{
2386 outX = inX;
2387 outY = inY;
2388}
2389
2391{
2392 /* No need to lock `server_mutex`
2393 * (`outputs_changed_update_scale` never changes values in a non-main thread). */
2394
2395 if (window_->frame.fractional_scale) {
2397 }
2398
2399 return window_->frame.buffer_scale * base_dpi;
2400}
2401
2403{
2404#ifdef USE_EVENT_BACKGROUND_THREAD
2405 std::lock_guard lock_server_guard{*system_->server_mutex};
2406#endif
2407 const GHOST_TSuccess ok = system_->cursor_visibility_set(visible);
2408 if (ok == GHOST_kSuccess) {
2409 /* For the cursor to display when the event queue isn't being handled. */
2410 wl_display_flush(system_->wl_display_get());
2411 }
2412 return ok;
2413}
2414
2416{
2417#ifdef USE_EVENT_BACKGROUND_THREAD
2418 std::lock_guard lock_server_guard{*system_->server_mutex};
2419#endif
2421}
2422
2424{
2425#ifdef USE_EVENT_BACKGROUND_THREAD
2426 std::lock_guard lock_server_guard{*system_->server_mutex};
2427#endif
2428 return gwl_window_state_get(window_);
2429}
2430
2435
2437{
2438 /* NOTE(@ideasman42): only activation is supported (on X11 & Cocoa for e.g.)
2439 * both activation and raising is performed. Since WAYLAND only supports activation,
2440 * do that as the compositor will likely raise the window as well.
2441 * Although it's not ideal that raising does something but lowering a window is ignored. */
2442 if (order == GHOST_kWindowOrderTop) {
2443 gwl_window_activate(window_);
2444 }
2445
2446 return GHOST_kSuccess;
2447}
2448
2450{
2451#ifdef USE_EVENT_BACKGROUND_THREAD
2452 std::lock_guard lock_server_guard{*system_->server_mutex};
2453#endif
2454
2455#ifdef WITH_GHOST_WAYLAND_LIBDECOR
2456 if (use_libdecor) {
2457 libdecor_frame_set_fullscreen(window_->libdecor->frame, nullptr);
2458 }
2459 else
2460#endif
2461 {
2462 xdg_toplevel_set_fullscreen(window_->xdg_decor->toplevel, nullptr);
2463 }
2464
2465 return GHOST_kSuccess;
2466}
2467
2469{
2470#ifdef USE_EVENT_BACKGROUND_THREAD
2471 std::lock_guard lock_server_guard{*system_->server_mutex};
2472#endif
2473
2474#ifdef WITH_GHOST_WAYLAND_LIBDECOR
2475 if (use_libdecor) {
2476 libdecor_frame_unset_fullscreen(window_->libdecor->frame);
2477 }
2478 else
2479#endif
2480 {
2481 xdg_toplevel_unset_fullscreen(window_->xdg_decor->toplevel);
2482 }
2483 return GHOST_kSuccess;
2484}
2485
2487{
2488 return window_->is_dialog;
2489}
2490
2491GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType type)
2492{
2493 switch (type) {
2496 return context;
2497 }
2498
2499#ifdef WITH_VULKAN_BACKEND
2500 case GHOST_kDrawingContextTypeVulkan: {
2502 GHOST_kVulkanPlatformWayland,
2503 0,
2504 nullptr,
2505 window_->wl.surface,
2506 system_->wl_display_get(),
2507 window_->backend.vulkan_window_info,
2508 1,
2509 2,
2510 is_debug_context_,
2511 preferred_device_);
2512 if (context->initializeDrawingContext()) {
2513 return context;
2514 }
2515 delete context;
2516 return nullptr;
2517 }
2518#endif
2519
2520#ifdef WITH_OPENGL_BACKEND
2521 case GHOST_kDrawingContextTypeOpenGL: {
2522 for (int minor = 6; minor >= 3; --minor) {
2523 GHOST_Context *context = new GHOST_ContextEGL(
2524 system_,
2526 EGLNativeWindowType(window_->backend.egl_window),
2527 EGLNativeDisplayType(system_->wl_display_get()),
2528 EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
2529 4,
2530 minor,
2532 (is_debug_context_ ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0),
2534 EGL_OPENGL_API);
2535
2536 if (context->initializeDrawingContext()) {
2537 return context;
2538 }
2539 delete context;
2540 }
2541 return nullptr;
2542 }
2543#endif
2544
2545 default:
2546 /* Unsupported backend. */
2547 return nullptr;
2548 }
2549}
2550
2551#ifdef WITH_INPUT_IME
2552
2553void GHOST_WindowWayland::beginIME(int32_t x, int32_t y, int32_t w, int32_t h, bool completed)
2554{
2555 system_->ime_begin(this, x, y, w, h, completed);
2556}
2557
2558void GHOST_WindowWayland::endIME()
2559{
2560 system_->ime_end(this);
2561}
2562
2563#endif
2564
2567/* -------------------------------------------------------------------- */
2574{
2575 return window_->frame.buffer_scale;
2576}
2577
2579{
2580 /* NOTE(@ideasman42): This could be kept initialized,
2581 * since it's such a small struct it's not so important. */
2582 GWL_WindowScaleParams *scale_params = &window_->scale_params;
2583 scale_params->is_fractional = (window_->frame.fractional_scale != 0);
2584 scale_params->scale = scale_params->is_fractional ? window_->frame.fractional_scale :
2585 window_->frame.buffer_scale;
2586 return *scale_params;
2587}
2588
2589wl_fixed_t GHOST_WindowWayland::wl_fixed_from_window(wl_fixed_t value) const
2590{
2591 if (window_->frame.fractional_scale) {
2592 return gwl_window_fractional_from_viewport(window_->frame, value);
2593 }
2594 return value / window_->frame.buffer_scale;
2595}
2596
2597wl_fixed_t GHOST_WindowWayland::wl_fixed_to_window(wl_fixed_t value) const
2598{
2599 if (window_->frame.fractional_scale) {
2600 return gwl_window_fractional_to_viewport(window_->frame, value);
2601 }
2602 return value * window_->frame.buffer_scale;
2603}
2604
2606{
2607 return window_->wl.surface;
2608}
2609
2610const std::vector<GWL_Output *> &GHOST_WindowWayland::outputs_get()
2611{
2612 return window_->outputs;
2613}
2614
2617/* -------------------------------------------------------------------- */
2628
2630{
2631#ifdef USE_EVENT_BACKGROUND_THREAD
2632 const bool is_main_thread = system_->main_thread_id == std::this_thread::get_id();
2633 if (is_main_thread)
2634#endif
2635 {
2636 /* This can run while the window being initialized.
2637 * In this case, skip setting the window active but add the event, see: #120465. */
2638 if (window_->is_init) {
2639 if (system_->getWindowManager()->setActiveWindow(this) == GHOST_kFailure) {
2640 return GHOST_kFailure;
2641 }
2642 }
2643 }
2644 const GHOST_TSuccess success = system_->pushEvent_maybe_pending(
2646#ifdef WITH_GHOST_WAYLAND_LIBDECOR
2647 if (success == GHOST_kSuccess) {
2648 if (use_libdecor) {
2649 /* Ensure there is a swap-buffers, needed for the updated window borders to refresh. */
2651 }
2652 }
2653#endif
2654 return success;
2655}
2656
2658{
2659#ifdef USE_EVENT_BACKGROUND_THREAD
2660 /* Actual activation is handled when processing pending events. */
2661 const bool is_main_thread = system_->main_thread_id == std::this_thread::get_id();
2662 if (is_main_thread)
2663#endif
2664 {
2665 /* See code comments for #GHOST_WindowWayland::activate. */
2666 if (window_->is_init) {
2667 system_->getWindowManager()->setWindowInactive(this);
2668 }
2669 }
2670 const GHOST_TSuccess success = system_->pushEvent_maybe_pending(
2672#ifdef WITH_GHOST_WAYLAND_LIBDECOR
2673 if (success == GHOST_kSuccess) {
2674 if (use_libdecor) {
2675 /* Ensure there is a swap-buffers, needed for the updated window borders to refresh. */
2677 }
2678 }
2679#endif
2680 return success;
2681}
2682
2688
2690{
2691 /* NOTE: we want to `swapBuffers`, however this may run from a thread and
2692 * when this windows OpenGL context is not active, so send and update event instead. */
2693 return system_->pushEvent_maybe_pending(
2695}
2696
2699/* -------------------------------------------------------------------- */
2706{
2707#ifdef USE_EVENT_BACKGROUND_THREAD
2708 if (system_->main_thread_id != std::this_thread::get_id()) {
2710 return GHOST_kSuccess;
2711 }
2712#endif
2714}
2715
2717{
2718#ifdef USE_EVENT_BACKGROUND_THREAD
2719
2720 /* NOTE: if deferring causes problems, it could be isolated to the first scale initialization
2721 * See: #GWL_WindowFrame::is_scale_init. */
2723#else
2725#endif
2726}
2727
2729{
2730#ifdef USE_EVENT_BACKGROUND_THREAD
2731 if (system_->main_thread_id != std::this_thread::get_id()) {
2733 return false;
2734 }
2735#endif
2736 int fractional_scale_next = -1;
2737 int fractional_scale_from_output = 0;
2738
2739 int scale_next = outputs_max_scale_or_default(outputs_get(), 0, &fractional_scale_from_output);
2740
2741 if (UNLIKELY(scale_next == 0)) {
2742 return false;
2743 }
2744
2745#ifdef USE_EVENT_BACKGROUND_THREAD
2746 std::lock_guard lock_frame_guard{window_->frame_pending_mutex};
2747#endif
2748
2749 if (window_->wp.fractional_scale_handle) {
2750 /* Let the #wp_fractional_scale_v1_listener::preferred_scale callback handle
2751 * changes to the windows scale. */
2752 if (window_->frame_pending.fractional_scale_preferred != 0) {
2753 fractional_scale_next = window_->frame_pending.fractional_scale_preferred;
2754 scale_next = fractional_scale_next / FRACTIONAL_DENOMINATOR;
2755 }
2756 }
2757
2758 if (fractional_scale_next == -1) {
2759 fractional_scale_next = fractional_scale_from_output;
2760 scale_next = fractional_scale_next / FRACTIONAL_DENOMINATOR;
2761 }
2762
2763 bool changed = false;
2764
2765 const bool is_fractional_prev = window_->frame.fractional_scale != 0;
2766 const bool is_fractional_next = (fractional_scale_next % FRACTIONAL_DENOMINATOR) != 0;
2767
2768 /* When non-fractional, never use fractional scaling! */
2769 window_->frame_pending.fractional_scale = is_fractional_next ? fractional_scale_next : 0;
2770 window_->frame_pending.buffer_scale = is_fractional_next ?
2771 1 :
2772 fractional_scale_next / FRACTIONAL_DENOMINATOR;
2773
2774 const int fractional_scale_prev = window_->frame.fractional_scale ?
2775 window_->frame.fractional_scale :
2777 const int scale_prev = fractional_scale_prev / FRACTIONAL_DENOMINATOR;
2778
2779 /* Resizing implies updating. */
2780 bool do_frame_resize = false;
2781 bool do_frame_update = false;
2782
2783 if (window_->frame_pending.is_scale_init == false) {
2784 window_->frame_pending.is_scale_init = true;
2785
2786 /* NOTE(@ideasman42): Needed because new windows are created at their previous pixel-dimensions
2787 * as the window doesn't save it's DPI. Restore the window size under the assumption it's
2788 * opening on the same monitor so a window keeps it's previous size on a users system.
2789 *
2790 * To support anything more sophisticated, windows would need to be created with a scale
2791 * argument (representing the scale used when the window was stored, for e.g.). */
2792
2793#ifdef WITH_GHOST_WAYLAND_LIBDECOR
2794 if (use_libdecor) {
2795 /* LIBDECOR needs its own logic. Failing to do this causes the window border
2796 * not to follow the GHOST window on startup - with multiple monitors,
2797 * each with different fractional scale, see: #109194.
2798 *
2799 * Note that the window will show larger, then resize to be smaller soon
2800 * after opening. This would be nice to avoid but would require DPI
2801 * to be stored in the window (as noted above). */
2802 int size_next[2] = {0, 0};
2803 int size_orig[2] = {0, 0};
2804
2805 /* Leave `window_->frame_pending` as-is, only change the window frame. */
2806 for (size_t i = 0; i < ARRAY_SIZE(window_->frame_pending.size); i++) {
2807 const int value = size_next[i] ? window_->frame_pending.size[i] : window_->frame.size[i];
2808 size_orig[i] = value;
2809 if (is_fractional_prev || is_fractional_next) {
2810 size_next[i] = lroundf((value * double(FRACTIONAL_DENOMINATOR)) /
2811 double(fractional_scale_next));
2812 }
2813 else {
2814 size_next[i] = value / scale_prev;
2815 }
2816 if (window_->frame_pending.buffer_scale > 1) {
2817 gwl_round_int_by(&size_next[i], window_->frame_pending.buffer_scale);
2818 }
2819 }
2820
2821 if (size_orig[0] != size_next[0] || size_orig[1] != size_next[1]) {
2822 GWL_LibDecor_Window &decor = *window_->libdecor;
2823 libdecor_state *state = libdecor_state_new(UNPACK2(size_next));
2824 libdecor_frame_commit(decor.frame, state, nullptr);
2826 }
2827 }
2828#endif /* WITH_GHOST_WAYLAND_LIBDECOR */
2829 /* Leave `window_->frame_pending` as-is, so changes are detected and updates are applied. */
2830 do_frame_resize = false;
2831 do_frame_update = true;
2832 }
2833 else {
2834 /* Test if the scale changed. */
2835 if ((fractional_scale_prev != fractional_scale_next) ||
2836 (window_->frame_pending.buffer_scale != window_->frame.buffer_scale))
2837 {
2838 do_frame_resize = true;
2839 }
2840 }
2841
2842 if (do_frame_resize) {
2843 /* Resize the window failing to do so results in severe flickering with a
2844 * multi-monitor setup when multiple monitors have different scales.
2845 *
2846 * NOTE: some flickering is still possible even when resizing this
2847 * happens when dragging the right hand side of the title-bar in KDE
2848 * as expanding changed the size on the RHS, this may be up to the compositor to fix. */
2849 for (size_t i = 0; i < ARRAY_SIZE(window_->frame_pending.size); i++) {
2850 const int value = window_->frame_pending.size[i] ? window_->frame_pending.size[i] :
2851 window_->frame.size[i];
2852 if (is_fractional_prev || is_fractional_next) {
2853 window_->frame_pending.size[i] = lroundf((value * double(fractional_scale_next)) /
2854 double(fractional_scale_prev));
2855 }
2856 else {
2857 window_->frame_pending.size[i] = (value * scale_next) / scale_prev;
2858 }
2859 if (window_->frame_pending.buffer_scale > 1) {
2861 }
2862 }
2863 do_frame_update = true;
2864 }
2865
2866 if (do_frame_update) {
2868 changed = true;
2869 }
2870
2871 return changed;
2872}
2873
2875{
2876 std::vector<GWL_Output *> &outputs = window_->outputs;
2877 auto it = std::find(outputs.begin(), outputs.end(), output);
2878 if (it != outputs.end()) {
2879 return false;
2880 }
2881 outputs.push_back(output);
2882 return true;
2883}
2884
2886{
2887 std::vector<GWL_Output *> &outputs = window_->outputs;
2888 auto it = std::find(outputs.begin(), outputs.end(), output);
2889 if (it == outputs.end()) {
2890 return false;
2891 }
2892 outputs.erase(it);
2893 return true;
2894}
2895
2896#ifdef USE_EVENT_BACKGROUND_THREAD
2897
2899{
2900 /* Caller must lock `server_mutex`, while individual actions could lock,
2901 * it's simpler to lock once when handling all window actions. */
2902 GWL_Window *win = window_;
2903 GHOST_ASSERT(win->ghost_system->main_thread_id == std::this_thread::get_id(),
2904 "Run from main thread!");
2905
2907}
2908
2909#endif /* USE_EVENT_BACKGROUND_THREAD */
2910
void BLI_kdtree_nd_ free(KDTree *tree)
unsigned int uint
#define UNPACK2(a)
#define ARRAY_SIZE(arr)
#define UNLIKELY(x)
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
#define GHOST_OPENGL_EGL_CONTEXT_FLAGS
#define GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY
#define wl_display
#define wl_surface
#define GHOST_ASSERT(x, info)
#define GHOST_PRINT(x)
GWL_Output * ghost_wl_output_user_data(wl_output *wl_output)
void ghost_wl_surface_tag(wl_surface *wl_surface)
bool ghost_wl_display_report_error_if_set(wl_display *display)
bool ghost_wl_output_own(const wl_output *wl_output)
#define FRACTIONAL_DENOMINATOR
GHOST_TWindowState
@ GHOST_kWindowStateMinimized
@ GHOST_kWindowStateMaximized
@ GHOST_kWindowStateNormal
@ GHOST_kWindowStateFullScreen
GHOST_TStandardCursor
@ GHOST_kStandardCursorCustom
@ GHOST_kStandardCursorDefault
@ GHOST_kEventWindowClose
@ GHOST_kEventWindowSize
@ GHOST_kEventWindowActivate
@ GHOST_kEventWindowUpdateDecor
@ GHOST_kEventWindowDeactivate
@ GHOST_kEventWindowDPIHintChanged
GHOST_TDrawingContextType
@ GHOST_kDrawingContextTypeNone
GHOST_TWindowOrder
@ GHOST_kWindowOrderTop
GHOST_TSuccess
Definition GHOST_Types.h:87
@ GHOST_kFailure
Definition GHOST_Types.h:87
@ GHOST_kSuccess
Definition GHOST_Types.h:87
GHOST_TGrabCursorMode
@ GHOST_kGrabWrap
#define WL_ARRAY_FOR_EACH(pos, array)
static void wp_fractional_scale_handle_preferred_scale(void *data, wp_fractional_scale_v1 *, uint preferred_scale)
wl_fixed_t gwl_window_scale_wl_fixed_to(const GWL_WindowScaleParams &scale_params, wl_fixed_t value)
eGWL_PendingWindowActions
@ PENDING_WINDOW_FRAME_CONFIGURE
@ PENDING_OUTPUT_SCALE_UPDATE_DEFERRED
@ PENDING_WINDOW_CURSOR_SHAPE_REFRESH
@ PENDING_WINDOW_SURFACE_COMMIT
@ PENDING_OUTPUT_SCALE_UPDATE
wl_fixed_t gwl_window_scale_wl_fixed_from(const GWL_WindowScaleParams &scale_params, wl_fixed_t value)
#define LOG
static void gwl_round_int2_by(int value_p[2], const int round_value)
static int gwl_window_fractional_from_viewport(const GWL_WindowFrame &frame, int value)
static void surface_handle_leave(void *data, wl_surface *, wl_output *wl_output)
static void gwl_window_frame_pending_fractional_scale_set_notest(GWL_Window *win, bool *r_surface_needs_commit, bool *r_surface_needs_buffer_scale)
static CLG_LogRef LOG_WL_XDG_TOPLEVEL_DECORATION
static void gwl_window_frame_update_from_pending_no_lock(GWL_Window *win)
int gwl_window_scale_int_to(const GWL_WindowScaleParams &scale_params, int value)
static bool gwl_window_viewport_unset(GWL_Window *win, bool *r_surface_needs_commit, bool *r_surface_needs_buffer_scale)
static void gwl_window_cursor_custom_free(GWL_WindowCursorCustomShape &ccs)
static GHOST_TWindowState gwl_window_state_get(const GWL_Window *win)
#define PENDING_NUM
static void xdg_toplevel_handle_configure(void *data, xdg_toplevel *, const int32_t width, const int32_t height, wl_array *states)
static CLG_LogRef LOG_WL_XDG_SURFACE
static void gwl_window_frame_pending_fractional_scale_set(GWL_Window *win, bool *r_surface_needs_commit, bool *r_surface_needs_buffer_scale)
static void xdg_surface_handle_configure(void *data, xdg_surface *xdg_surface, const uint32_t serial)
static void gwl_window_frame_update_from_pending(GWL_Window *win)
static const wp_fractional_scale_v1_listener wp_fractional_scale_listener
static CLG_LogRef LOG_WL_SURFACE
static const xdg_toplevel_listener xdg_toplevel_listener
static const zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_v1_listener
static GHOST_TSuccess gwl_window_cursor_custom_load(const GWL_WindowCursorCustomShape &ccs, GHOST_SystemWayland *system)
static bool gwl_window_state_set(GWL_Window *win, const GHOST_TWindowState state)
static GHOST_TSuccess gwl_window_cursor_shape_refresh(GHOST_TStandardCursor shape, const GWL_WindowCursorCustomShape &ccs, GHOST_SystemWayland *system)
static void gwl_window_frame_pending_size_set(GWL_Window *win, bool *r_surface_needs_commit, bool *r_surface_needs_resize_for_backend, bool *r_surface_needs_buffer_scale)
static bool gwl_window_state_set_for_xdg(xdg_toplevel *toplevel, const GHOST_TWindowState state, const GHOST_TWindowState state_current)
static int outputs_uniform_scale_or_default(const std::vector< GWL_Output * > &outputs, const int32_t scale_default, int *r_scale_fractional)
static void gwl_xdg_decor_window_destroy(GWL_XDG_Decor_Window *decor)
int gwl_window_scale_int_from(const GWL_WindowScaleParams &scale_params, int value)
static CLG_LogRef LOG_WL_FRACTIONAL_SCALE
static void xdg_toplevel_handle_configure_bounds(void *data, xdg_toplevel *, int32_t width, int32_t height)
static int gwl_window_fractional_to_viewport(const GWL_WindowFrame &frame, int value)
static void gwl_window_activate(GWL_Window *win)
static void gwl_window_resize_for_backend(GWL_Window *win, const int32_t size[2])
static int outputs_max_scale_or_default(const std::vector< GWL_Output * > &outputs, const int32_t scale_default, int *r_scale_fractional)
static constexpr size_t base_dpi
static void gwl_window_cursor_custom_clear(GWL_WindowCursorCustomShape &ccs)
static void gwl_window_pending_actions_handle(GWL_Window *win)
static const xdg_surface_listener xdg_surface_listener
static int gwl_window_fractional_to_viewport_round(const GWL_WindowFrame &frame, int value)
static int output_scale_cmp(const GWL_Output *output_a, const GWL_Output *output_b)
static void gwl_round_int_by(int *value_p, const int round_value)
static CLG_LogRef LOG_WL_XDG_TOPLEVEL
static void gwl_window_cursor_custom_store(GWL_WindowCursorCustomShape &ccs, const uint8_t *bitmap, const uint8_t *mask, const int32_t size[2], const int32_t hot_spot[2], bool can_invert_color)
static bool gwl_window_viewport_size_update(GWL_Window *win)
static void xdg_activation_handle_done(void *data, xdg_activation_token_v1 *xdg_activation_token_v1, const char *token)
static const xdg_activation_token_v1_listener xdg_activation_listener
static void xdg_toplevel_handle_wm_capabilities(void *, xdg_toplevel *, wl_array *)
static void gwl_window_pending_actions_tag(GWL_Window *win, enum eGWL_PendingWindowActions type)
static void surface_handle_enter(void *data, wl_surface *, wl_output *wl_output)
static bool gwl_round_int_test(int value, const int round_value)
static bool gwl_window_viewport_set(GWL_Window *win, bool *r_surface_needs_commit, bool *r_surface_needs_buffer_scale)
static void gwl_window_title_set(GWL_Window *win, const char *title)
static int gwl_window_fractional_from_viewport_round(const GWL_WindowFrame &frame, int value)
static void xdg_toplevel_decoration_handle_configure(void *data, zxdg_toplevel_decoration_v1 *, const uint32_t mode)
static const xdg_activation_token_v1_listener * xdg_activation_listener_get()
static const wl_surface_listener wl_surface_listener
static void xdg_toplevel_handle_close(void *data, xdg_toplevel *)
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static const char * xdg_app_id_get()
GHOST_TSuccess pushEvent_maybe_pending(const GHOST_IEvent *event)
bool window_surface_unref(const wl_surface *wl_surface)
uint64_t getMilliSeconds() const override
bool window_cursor_grab_set(const GHOST_TGrabCursorMode mode, const GHOST_TGrabCursorMode mode_current, int32_t init_grab_xy[2], const GHOST_Rect *wrap_bounds, GHOST_TAxisFlag wrap_axis, wl_surface *wl_surface, const struct GWL_WindowScaleParams &scale_params)
GHOST_TSuccess cursor_visibility_set(bool visible)
std::thread::id main_thread_id
struct wl_display * wl_display_get()
GHOST_TSuccess cursor_shape_check(GHOST_TStandardCursor cursorShape)
bool cursor_grab_use_software_display_get(const GHOST_TGrabCursorMode mode)
struct xdg_wm_base * xdg_decor_shell_get()
struct wl_compositor * wl_compositor_get()
struct wp_fractional_scale_manager_v1 * wp_fractional_scale_manager_get()
struct wl_shm * wl_shm_get() const
GHOST_TSuccess cursor_shape_set(GHOST_TStandardCursor shape)
const std::vector< GWL_Output * > & outputs_get() const
struct wp_viewporter * wp_viewporter_get()
struct zxdg_decoration_manager_v1 * xdg_decor_manager_get()
struct wl_seat * wl_seat_active_get_with_input_serial(uint32_t &serial)
GHOST_TSuccess cursor_bitmap_get(GHOST_CursorBitmapRef *bitmap)
GHOST_TSuccess cursor_shape_custom_set(const uint8_t *bitmap, const uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor)
void ime_begin(const GHOST_WindowWayland *win, int32_t x, int32_t y, int32_t w, int32_t h, bool completed) const
std::atomic< bool > has_pending_actions_for_window
struct xdg_activation_v1 * xdg_activation_manager_get()
void ime_end(const GHOST_WindowWayland *win) const
GHOST_WindowManager * getWindowManager() const
GHOST_TSuccess pushEvent(const GHOST_IEvent *event)
GHOST_IWindow * getActiveWindow() const
GHOST_TSuccess setActiveWindow(GHOST_IWindow *window)
void setWindowInactive(const GHOST_IWindow *window)
GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursorShape) override
const struct GWL_WindowScaleParams & scale_params_get() const
GHOST_TSuccess setClientSize(uint32_t width, uint32_t height) override
bool outputs_leave(GWL_Output *output)
GHOST_TSuccess setClientWidth(uint32_t width) override
wl_fixed_t wl_fixed_to_window(wl_fixed_t value) const
GHOST_TSuccess setOrder(GHOST_TWindowOrder order) override
GHOST_TSuccess swapBuffers() override
GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override
GHOST_TSuccess setState(GHOST_TWindowState state) override
GHOST_TSuccess setWindowCursorVisibility(bool visible) override
GHOST_TWindowState getState() const override
bool getCursorGrabUseSoftwareDisplay() override
GHOST_TSuccess beginFullScreen() const override
bool isDialog() const override
struct wl_surface * wl_surface_get() const
GHOST_TSuccess notify_decor_redraw()
bool getValid() const override
wl_fixed_t wl_fixed_from_window(wl_fixed_t value) const
std::string getTitle() const override
void getClientBounds(GHOST_Rect &bounds) const override
uint16_t getDPIHint() override
void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const override
GHOST_TSuccess endFullScreen() const override
GHOST_TSuccess setClientHeight(uint32_t height) override
GHOST_TSuccess cursor_shape_refresh()
void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const override
bool outputs_enter(GWL_Output *output)
GHOST_TSuccess invalidate() override
GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) override
void setTitle(const char *title) override
GHOST_WindowWayland(GHOST_SystemWayland *system, const char *title, int32_t left, int32_t top, uint32_t width, uint32_t height, GHOST_TWindowState state, const GHOST_IWindow *parentWindow, GHOST_TDrawingContextType type, const bool is_dialog, const bool stereoVisual, const bool exclusive, const bool is_debug, const GHOST_GPUDevice &preferred_device)
GHOST_TSuccess setWindowCustomCursorShape(uint8_t *bitmap, uint8_t *mask, int sizex, int sizey, int hotX, int hotY, bool canInvertColor) override
void getWindowBounds(GHOST_Rect &bounds) const override
GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap) override
const std::vector< GWL_Output * > & outputs_get()
GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds) const override
GHOST_TGrabCursorMode m_cursorGrab
bool m_wantStereoVisual
int32_t m_cursorGrabInitPos[2]
GHOST_TStandardCursor getCursorShape() const override
GHOST_TSuccess setDrawingContextType(GHOST_TDrawingContextType type) override
GHOST_TStandardCursor m_cursorShape
GHOST_TSuccess setSwapInterval(int interval) override
GHOST_TSuccess releaseNativeHandles()
GHOST_TAxisFlag m_cursorGrabAxis
virtual GHOST_TSuccess swapBuffers() override
virtual bool getValid() const override
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
format
static ulong state[N]
static blender::bke::bNodeSocketTemplate outputs[]
unsigned short uint16_t
Definition stdint.h:79
unsigned int uint32_t
Definition stdint.h:80
signed int int32_t
Definition stdint.h:77
unsigned char uint8_t
Definition stdint.h:78
struct GWL_Window::@1508 wp
GHOST_SystemWayland * ghost_system
std::atomic< bool > pending_actions[PENDING_NUM]
wp_fractional_scale_v1 * fractional_scale_handle
GWL_XDG_Decor_Window * xdg_decor
wl_surface * surface
struct GWL_Window::@1510 backend
wp_viewport * viewport
GWL_WindowScaleParams scale_params
xdg_activation_token_v1 * activation_token
std::mutex frame_pending_mutex
GWL_WindowFrame frame_pending
struct GWL_Window::@1507 wl
GWL_WindowFrame frame
GHOST_WindowWayland * ghost_window
struct GWL_Window::@1509 xdg
GWL_WindowCursorCustomShape cursor_custom_shape
std::vector< GWL_Output * > outputs
GHOST_TDrawingContextType ghost_context_type
zxdg_toplevel_decoration_v1 * toplevel_decor
struct GWL_XDG_Decor_Window::@1506 pending
enum zxdg_toplevel_decoration_v1_mode mode
#define wl_display_dispatch(...)
#define wl_display_flush(...)
#define wl_display_roundtrip(...)
#define wl_egl_window_resize(...)
#define wl_egl_window_create(...)
#define wl_egl_window_destroy(...)
#define libdecor_frame_set_fullscreen(...)
#define libdecor_frame_map(...)
#define libdecor_state_new(...)
#define libdecor_frame_unset_fullscreen(...)
#define libdecor_configuration_get_content_size(...)
#define libdecor_state_free(...)
#define libdecor_configuration_get_window_state(...)
#define libdecor_frame_unref(...)
#define libdecor_frame_unset_maximized(...)
#define libdecor_frame_get_xdg_toplevel(...)
#define libdecor_frame_set_app_id(...)
#define libdecor_frame_set_min_content_size(...)
#define libdecor_frame_set_parent(...)
#define libdecor_frame_set_title(...)
#define libdecor_frame_set_minimized(...)
#define libdecor_frame_commit(...)
#define libdecor_decorate(...)
#define libdecor_frame_set_maximized(...)