Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/platform/linuxbsd/wayland/display_server_wayland.cpp
20801 views
1
/**************************************************************************/
2
/* display_server_wayland.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "display_server_wayland.h"
32
33
#ifdef WAYLAND_ENABLED
34
35
#define WAYLAND_DISPLAY_SERVER_DEBUG_LOGS_ENABLED
36
#ifdef WAYLAND_DISPLAY_SERVER_DEBUG_LOGS_ENABLED
37
#define DEBUG_LOG_WAYLAND(...) print_verbose(__VA_ARGS__)
38
#else
39
#define DEBUG_LOG_WAYLAND(...)
40
#endif
41
42
#include "core/input/input.h"
43
#include "core/os/main_loop.h"
44
#include "servers/rendering/dummy/rasterizer_dummy.h"
45
46
#ifdef VULKAN_ENABLED
47
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
48
#endif
49
50
#ifdef GLES3_ENABLED
51
#include "core/io/file_access.h"
52
#include "detect_prime_egl.h"
53
#include "drivers/gles3/rasterizer_gles3.h"
54
#include "wayland/egl_manager_wayland.h"
55
#include "wayland/egl_manager_wayland_gles.h"
56
#endif
57
58
#ifdef ACCESSKIT_ENABLED
59
#include "drivers/accesskit/accessibility_driver_accesskit.h"
60
#endif
61
62
#ifdef DBUS_ENABLED
63
#ifdef SOWRAP_ENABLED
64
#include "dbus-so_wrap.h"
65
#else
66
#include <dbus/dbus.h>
67
#endif
68
#endif
69
70
#define WAYLAND_MAX_FRAME_TIME_US (1'000'000)
71
72
String DisplayServerWayland::_get_app_id_from_context(Context p_context) {
73
String app_id;
74
75
switch (p_context) {
76
case CONTEXT_EDITOR: {
77
app_id = "org.godotengine.Editor";
78
} break;
79
80
case CONTEXT_PROJECTMAN: {
81
app_id = "org.godotengine.ProjectManager";
82
} break;
83
84
case CONTEXT_ENGINE:
85
default: {
86
String config_name = GLOBAL_GET("application/config/name");
87
if (config_name.length() != 0) {
88
app_id = config_name;
89
} else {
90
app_id = "org.godotengine.Godot";
91
}
92
}
93
}
94
95
return app_id;
96
}
97
98
void DisplayServerWayland::_send_window_event(WindowEvent p_event, WindowID p_window_id) {
99
ERR_FAIL_COND(!windows.has(p_window_id));
100
101
WindowData &wd = windows[p_window_id];
102
103
if (wd.window_event_callback.is_valid()) {
104
Variant event = int(p_event);
105
wd.window_event_callback.call(event);
106
}
107
}
108
109
void DisplayServerWayland::dispatch_input_events(const Ref<InputEvent> &p_event) {
110
static_cast<DisplayServerWayland *>(get_singleton())->_dispatch_input_event(p_event);
111
}
112
113
void DisplayServerWayland::_dispatch_input_event(const Ref<InputEvent> &p_event) {
114
Ref<InputEventFromWindow> event_from_window = p_event;
115
116
if (event_from_window.is_valid()) {
117
WindowID window_id = event_from_window->get_window_id();
118
119
Ref<InputEventKey> key_event = p_event;
120
if (!popup_menu_list.is_empty() && key_event.is_valid()) {
121
// Redirect to the highest popup menu.
122
window_id = popup_menu_list.back()->get();
123
}
124
125
// Send to a single window.
126
if (windows.has(window_id)) {
127
Callable callable = windows[window_id].input_event_callback;
128
if (callable.is_valid()) {
129
callable.call(p_event);
130
}
131
}
132
} else {
133
// Send to all windows. Copy all pending callbacks, since callback can erase window.
134
Vector<Callable> cbs;
135
for (KeyValue<WindowID, WindowData> &E : windows) {
136
Callable callable = E.value.input_event_callback;
137
if (callable.is_valid()) {
138
cbs.push_back(callable);
139
}
140
}
141
142
for (const Callable &cb : cbs) {
143
cb.call(p_event);
144
}
145
}
146
}
147
148
void DisplayServerWayland::_update_window_rect(const Rect2i &p_rect, WindowID p_window_id) {
149
ERR_FAIL_COND(!windows.has(p_window_id));
150
151
WindowData &wd = windows[p_window_id];
152
153
if (wd.rect == p_rect) {
154
return;
155
}
156
157
wd.rect = p_rect;
158
159
#ifdef RD_ENABLED
160
if (wd.visible && rendering_context) {
161
rendering_context->window_set_size(p_window_id, wd.rect.size.width, wd.rect.size.height);
162
}
163
#endif
164
165
#ifdef GLES3_ENABLED
166
if (wd.visible && egl_manager) {
167
wl_egl_window_resize(wd.wl_egl_window, wd.rect.size.width, wd.rect.size.height, 0, 0);
168
}
169
#endif
170
171
if (wd.rect_changed_callback.is_valid()) {
172
wd.rect_changed_callback.call(wd.rect);
173
}
174
}
175
176
// Interface methods.
177
178
bool DisplayServerWayland::has_feature(Feature p_feature) const {
179
switch (p_feature) {
180
#ifndef DISABLE_DEPRECATED
181
case FEATURE_GLOBAL_MENU: {
182
return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));
183
} break;
184
#endif
185
case FEATURE_MOUSE:
186
case FEATURE_MOUSE_WARP:
187
case FEATURE_CLIPBOARD:
188
case FEATURE_CURSOR_SHAPE:
189
case FEATURE_CUSTOM_CURSOR_SHAPE:
190
case FEATURE_WINDOW_TRANSPARENCY:
191
case FEATURE_ICON:
192
case FEATURE_HIDPI:
193
case FEATURE_SWAP_BUFFERS:
194
case FEATURE_KEEP_SCREEN_ON:
195
case FEATURE_IME:
196
case FEATURE_WINDOW_DRAG:
197
case FEATURE_CLIPBOARD_PRIMARY:
198
case FEATURE_SUBWINDOWS:
199
case FEATURE_WINDOW_EMBEDDING:
200
case FEATURE_SELF_FITTING_WINDOWS: {
201
return true;
202
} break;
203
204
//case FEATURE_NATIVE_DIALOG:
205
//case FEATURE_NATIVE_DIALOG_INPUT:
206
#ifdef DBUS_ENABLED
207
case FEATURE_NATIVE_DIALOG_FILE:
208
case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
209
case FEATURE_NATIVE_DIALOG_FILE_MIME: {
210
return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_file_chooser_supported());
211
} break;
212
case FEATURE_NATIVE_COLOR_PICKER: {
213
return (portal_desktop && portal_desktop->is_supported() && portal_desktop->is_screenshot_supported());
214
} break;
215
#endif
216
217
#ifdef SPEECHD_ENABLED
218
case FEATURE_TEXT_TO_SPEECH: {
219
return true;
220
} break;
221
#endif
222
223
#ifdef ACCESSKIT_ENABLED
224
case FEATURE_ACCESSIBILITY_SCREEN_READER: {
225
return (accessibility_driver != nullptr);
226
} break;
227
#endif
228
229
default: {
230
return false;
231
}
232
}
233
}
234
235
String DisplayServerWayland::get_name() const {
236
return "Wayland";
237
}
238
239
#ifdef SPEECHD_ENABLED
240
241
void DisplayServerWayland::initialize_tts() const {
242
const_cast<DisplayServerWayland *>(this)->tts = memnew(TTS_Linux);
243
}
244
245
bool DisplayServerWayland::tts_is_speaking() const {
246
if (unlikely(!tts)) {
247
initialize_tts();
248
}
249
ERR_FAIL_NULL_V(tts, false);
250
return tts->is_speaking();
251
}
252
253
bool DisplayServerWayland::tts_is_paused() const {
254
if (unlikely(!tts)) {
255
initialize_tts();
256
}
257
ERR_FAIL_NULL_V(tts, false);
258
return tts->is_paused();
259
}
260
261
TypedArray<Dictionary> DisplayServerWayland::tts_get_voices() const {
262
if (unlikely(!tts)) {
263
initialize_tts();
264
}
265
ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());
266
return tts->get_voices();
267
}
268
269
void DisplayServerWayland::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int64_t p_utterance_id, bool p_interrupt) {
270
if (unlikely(!tts)) {
271
initialize_tts();
272
}
273
ERR_FAIL_NULL(tts);
274
tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt);
275
}
276
277
void DisplayServerWayland::tts_pause() {
278
if (unlikely(!tts)) {
279
initialize_tts();
280
}
281
ERR_FAIL_NULL(tts);
282
tts->pause();
283
}
284
285
void DisplayServerWayland::tts_resume() {
286
if (unlikely(!tts)) {
287
initialize_tts();
288
}
289
ERR_FAIL_NULL(tts);
290
tts->resume();
291
}
292
293
void DisplayServerWayland::tts_stop() {
294
if (unlikely(!tts)) {
295
initialize_tts();
296
}
297
ERR_FAIL_NULL(tts);
298
tts->stop();
299
}
300
301
#endif
302
303
#ifdef DBUS_ENABLED
304
305
bool DisplayServerWayland::is_dark_mode_supported() const {
306
return portal_desktop && portal_desktop->is_supported() && portal_desktop->is_settings_supported();
307
}
308
309
bool DisplayServerWayland::is_dark_mode() const {
310
if (!is_dark_mode_supported()) {
311
return false;
312
}
313
switch (portal_desktop->get_appearance_color_scheme()) {
314
case 1:
315
// Prefers dark theme.
316
return true;
317
case 2:
318
// Prefers light theme.
319
return false;
320
default:
321
// Preference unknown.
322
return false;
323
}
324
}
325
326
Color DisplayServerWayland::get_accent_color() const {
327
if (!portal_desktop) {
328
return Color();
329
}
330
return portal_desktop->get_appearance_accent_color();
331
}
332
333
void DisplayServerWayland::set_system_theme_change_callback(const Callable &p_callable) {
334
ERR_FAIL_COND(!portal_desktop);
335
portal_desktop->set_system_theme_change_callback(p_callable);
336
}
337
338
Error DisplayServerWayland::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback, WindowID p_window_id) {
339
ERR_FAIL_COND_V(!portal_desktop, ERR_UNAVAILABLE);
340
MutexLock mutex_lock(wayland_thread.mutex);
341
342
WindowID window_id = p_window_id;
343
if (!windows.has(window_id) || window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, window_id)) {
344
window_id = MAIN_WINDOW_ID;
345
}
346
347
WaylandThread::WindowState *ws = wayland_thread.window_get_state(window_id);
348
ERR_FAIL_NULL_V(ws, ERR_BUG);
349
350
return portal_desktop->file_dialog_show(window_id, (ws ? ws->exported_handle : String()), p_title, p_current_directory, String(), p_filename, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false);
351
}
352
353
Error DisplayServerWayland::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, WindowID p_window_id) {
354
ERR_FAIL_COND_V(!portal_desktop, ERR_UNAVAILABLE);
355
MutexLock mutex_lock(wayland_thread.mutex);
356
357
WindowID window_id = p_window_id;
358
if (!windows.has(window_id) || window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, window_id)) {
359
window_id = MAIN_WINDOW_ID;
360
}
361
362
WaylandThread::WindowState *ws = wayland_thread.window_get_state(window_id);
363
ERR_FAIL_NULL_V(ws, ERR_BUG);
364
365
return portal_desktop->file_dialog_show(window_id, (ws ? ws->exported_handle : String()), p_title, p_current_directory, p_root, p_filename, p_mode, p_filters, p_options, p_callback, true);
366
}
367
368
#endif
369
370
void DisplayServerWayland::beep() const {
371
wayland_thread.beep();
372
}
373
374
void DisplayServerWayland::_mouse_update_mode() {
375
MouseMode wanted_mouse_mode = mouse_mode_override_enabled
376
? mouse_mode_override
377
: mouse_mode_base;
378
379
if (wanted_mouse_mode == mouse_mode) {
380
return;
381
}
382
383
MutexLock mutex_lock(wayland_thread.mutex);
384
385
bool show_cursor = (wanted_mouse_mode == MOUSE_MODE_VISIBLE || wanted_mouse_mode == MOUSE_MODE_CONFINED);
386
387
wayland_thread.cursor_set_visible(show_cursor);
388
389
WaylandThread::PointerConstraint constraint = WaylandThread::PointerConstraint::NONE;
390
391
switch (wanted_mouse_mode) {
392
case DisplayServer::MOUSE_MODE_CAPTURED: {
393
constraint = WaylandThread::PointerConstraint::LOCKED;
394
} break;
395
396
case DisplayServer::MOUSE_MODE_CONFINED:
397
case DisplayServer::MOUSE_MODE_CONFINED_HIDDEN: {
398
constraint = WaylandThread::PointerConstraint::CONFINED;
399
} break;
400
401
default: {
402
}
403
}
404
405
wayland_thread.pointer_set_constraint(constraint);
406
407
if (wanted_mouse_mode == DisplayServer::MOUSE_MODE_CAPTURED) {
408
WindowData *pointed_win = windows.getptr(wayland_thread.pointer_get_pointed_window_id());
409
ERR_FAIL_NULL(pointed_win);
410
wayland_thread.pointer_set_hint(pointed_win->rect.size / 2);
411
}
412
413
mouse_mode = wanted_mouse_mode;
414
}
415
416
void DisplayServerWayland::mouse_set_mode(MouseMode p_mode) {
417
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
418
if (p_mode == mouse_mode_base) {
419
return;
420
}
421
mouse_mode_base = p_mode;
422
_mouse_update_mode();
423
}
424
425
DisplayServerWayland::MouseMode DisplayServerWayland::mouse_get_mode() const {
426
return mouse_mode;
427
}
428
429
void DisplayServerWayland::mouse_set_mode_override(MouseMode p_mode) {
430
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
431
if (p_mode == mouse_mode_override) {
432
return;
433
}
434
mouse_mode_override = p_mode;
435
_mouse_update_mode();
436
}
437
438
DisplayServerWayland::MouseMode DisplayServerWayland::mouse_get_mode_override() const {
439
return mouse_mode_override;
440
}
441
442
void DisplayServerWayland::mouse_set_mode_override_enabled(bool p_override_enabled) {
443
if (p_override_enabled == mouse_mode_override_enabled) {
444
return;
445
}
446
mouse_mode_override_enabled = p_override_enabled;
447
_mouse_update_mode();
448
}
449
450
bool DisplayServerWayland::mouse_is_mode_override_enabled() const {
451
return mouse_mode_override_enabled;
452
}
453
454
// NOTE: This is hacked together (and not guaranteed to work in the first place)
455
// as for some reason the there's no proper way to ask the compositor to warp
456
// the pointer, although, at the time of writing, there's a proposal for a
457
// proper protocol for this. See:
458
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/158
459
void DisplayServerWayland::warp_mouse(const Point2i &p_to) {
460
MutexLock mutex_lock(wayland_thread.mutex);
461
462
WaylandThread::PointerConstraint old_constraint = wayland_thread.pointer_get_constraint();
463
464
wayland_thread.pointer_set_constraint(WaylandThread::PointerConstraint::LOCKED);
465
wayland_thread.pointer_set_hint(p_to);
466
467
wayland_thread.pointer_set_constraint(old_constraint);
468
}
469
470
Point2i DisplayServerWayland::mouse_get_position() const {
471
MutexLock mutex_lock(wayland_thread.mutex);
472
473
WindowID pointed_id = wayland_thread.pointer_get_pointed_window_id();
474
475
if (pointed_id != INVALID_WINDOW_ID && windows.has(pointed_id)) {
476
return Input::get_singleton()->get_mouse_position() + windows[pointed_id].rect.position;
477
}
478
479
// We can't properly implement this method by design.
480
// This is the best we can do unfortunately.
481
return Input::get_singleton()->get_mouse_position();
482
}
483
484
BitField<MouseButtonMask> DisplayServerWayland::mouse_get_button_state() const {
485
MutexLock mutex_lock(wayland_thread.mutex);
486
487
// Are we sure this is the only way? This seems sus.
488
// TODO: Handle tablets properly.
489
//mouse_button_mask.set_flag(MouseButtonMask((int64_t)wls.current_seat->tablet_tool_data.pressed_button_mask));
490
491
return wayland_thread.pointer_get_button_mask();
492
}
493
494
// NOTE: According to the Wayland specification, this method will only do
495
// anything if the user has interacted with the application by sending a
496
// "recent enough" input event.
497
// TODO: Add this limitation to the documentation.
498
void DisplayServerWayland::clipboard_set(const String &p_text) {
499
MutexLock mutex_lock(wayland_thread.mutex);
500
501
wayland_thread.selection_set_text(p_text);
502
}
503
504
String DisplayServerWayland::clipboard_get() const {
505
MutexLock mutex_lock(wayland_thread.mutex);
506
507
Vector<uint8_t> data;
508
509
const String text_mimes[] = {
510
"text/plain;charset=utf-8",
511
"text/plain",
512
};
513
514
for (String mime : text_mimes) {
515
if (wayland_thread.selection_has_mime(mime)) {
516
print_verbose(vformat("Selecting media type \"%s\" from offered types.", mime));
517
data = wayland_thread.selection_get_mime(mime);
518
break;
519
}
520
}
521
522
return String::utf8((const char *)data.ptr(), data.size());
523
}
524
525
Ref<Image> DisplayServerWayland::clipboard_get_image() const {
526
MutexLock mutex_lock(wayland_thread.mutex);
527
528
Ref<Image> image;
529
image.instantiate();
530
531
Error err = OK;
532
533
// TODO: Fallback to next media type on missing module or parse error.
534
if (wayland_thread.selection_has_mime("image/png")) {
535
err = image->load_png_from_buffer(wayland_thread.selection_get_mime("image/png"));
536
} else if (wayland_thread.selection_has_mime("image/jpeg")) {
537
err = image->load_jpg_from_buffer(wayland_thread.selection_get_mime("image/jpeg"));
538
} else if (wayland_thread.selection_has_mime("image/webp")) {
539
err = image->load_webp_from_buffer(wayland_thread.selection_get_mime("image/webp"));
540
} else if (wayland_thread.selection_has_mime("image/svg+xml")) {
541
err = image->load_svg_from_buffer(wayland_thread.selection_get_mime("image/svg+xml"));
542
} else if (wayland_thread.selection_has_mime("image/bmp")) {
543
err = image->load_bmp_from_buffer(wayland_thread.selection_get_mime("image/bmp"));
544
} else if (wayland_thread.selection_has_mime("image/x-tga")) {
545
err = image->load_tga_from_buffer(wayland_thread.selection_get_mime("image/x-tga"));
546
} else if (wayland_thread.selection_has_mime("image/x-targa")) {
547
err = image->load_tga_from_buffer(wayland_thread.selection_get_mime("image/x-targa"));
548
} else if (wayland_thread.selection_has_mime("image/ktx")) {
549
err = image->load_ktx_from_buffer(wayland_thread.selection_get_mime("image/ktx"));
550
} else if (wayland_thread.selection_has_mime("image/x-exr")) {
551
err = image->load_exr_from_buffer(wayland_thread.selection_get_mime("image/x-exr"));
552
}
553
554
ERR_FAIL_COND_V(err != OK, Ref<Image>());
555
556
return image;
557
}
558
559
void DisplayServerWayland::clipboard_set_primary(const String &p_text) {
560
MutexLock mutex_lock(wayland_thread.mutex);
561
562
wayland_thread.primary_set_text(p_text);
563
}
564
565
String DisplayServerWayland::clipboard_get_primary() const {
566
MutexLock mutex_lock(wayland_thread.mutex);
567
568
Vector<uint8_t> data;
569
570
const String text_mimes[] = {
571
"text/plain;charset=utf-8",
572
"text/plain",
573
};
574
575
for (String mime : text_mimes) {
576
if (wayland_thread.primary_has_mime(mime)) {
577
print_verbose(vformat("Selecting media type \"%s\" from offered types.", mime));
578
data = wayland_thread.primary_get_mime(mime);
579
break;
580
}
581
}
582
583
return String::utf8((const char *)data.ptr(), data.size());
584
}
585
586
int DisplayServerWayland::get_screen_count() const {
587
MutexLock mutex_lock(wayland_thread.mutex);
588
return wayland_thread.get_screen_count();
589
}
590
591
int DisplayServerWayland::get_primary_screen() const {
592
// AFAIK Wayland doesn't allow knowing (nor we care) about which screen is
593
// primary.
594
return 0;
595
}
596
597
Point2i DisplayServerWayland::screen_get_position(int p_screen) const {
598
MutexLock mutex_lock(wayland_thread.mutex);
599
600
p_screen = _get_screen_index(p_screen);
601
int screen_count = get_screen_count();
602
ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());
603
604
return wayland_thread.screen_get_data(p_screen).position;
605
}
606
607
Size2i DisplayServerWayland::screen_get_size(int p_screen) const {
608
MutexLock mutex_lock(wayland_thread.mutex);
609
610
p_screen = _get_screen_index(p_screen);
611
int screen_count = get_screen_count();
612
ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());
613
614
return wayland_thread.screen_get_data(p_screen).size;
615
}
616
617
Rect2i DisplayServerWayland::screen_get_usable_rect(int p_screen) const {
618
p_screen = _get_screen_index(p_screen);
619
int screen_count = get_screen_count();
620
ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());
621
622
return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));
623
}
624
625
int DisplayServerWayland::screen_get_dpi(int p_screen) const {
626
MutexLock mutex_lock(wayland_thread.mutex);
627
628
p_screen = _get_screen_index(p_screen);
629
int screen_count = get_screen_count();
630
ERR_FAIL_INDEX_V(p_screen, screen_count, 96);
631
632
const WaylandThread::ScreenData &data = wayland_thread.screen_get_data(p_screen);
633
634
int width_mm = data.physical_size.width;
635
int height_mm = data.physical_size.height;
636
637
double xdpi = (width_mm ? data.size.width / (double)width_mm * 25.4 : 0);
638
double ydpi = (height_mm ? data.size.height / (double)height_mm * 25.4 : 0);
639
640
if (xdpi || ydpi) {
641
return (xdpi + ydpi) / (xdpi && ydpi ? 2 : 1);
642
}
643
644
// Could not get DPI.
645
return 96;
646
}
647
648
float DisplayServerWayland::screen_get_scale(int p_screen) const {
649
MutexLock mutex_lock(wayland_thread.mutex);
650
651
if (p_screen == SCREEN_OF_MAIN_WINDOW) {
652
// Wayland does not expose fractional scale factors at the screen-level, but
653
// some code relies on it. Since this special screen is the default and a lot
654
// of code relies on it, we'll return the window's scale, which is what we
655
// really care about. After all, we have very little use of the actual screen
656
// enumeration APIs and we're (for now) in single-window mode anyways.
657
struct wl_surface *wl_surface = wayland_thread.window_get_wl_surface(MAIN_WINDOW_ID);
658
WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wl_surface);
659
660
return wayland_thread.window_state_get_scale_factor(ws);
661
}
662
663
p_screen = _get_screen_index(p_screen);
664
int screen_count = get_screen_count();
665
ERR_FAIL_INDEX_V(p_screen, screen_count, 1.0f);
666
667
return wayland_thread.screen_get_data(p_screen).scale;
668
}
669
670
float DisplayServerWayland::screen_get_refresh_rate(int p_screen) const {
671
MutexLock mutex_lock(wayland_thread.mutex);
672
673
p_screen = _get_screen_index(p_screen);
674
int screen_count = get_screen_count();
675
ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);
676
677
return wayland_thread.screen_get_data(p_screen).refresh_rate;
678
}
679
680
void DisplayServerWayland::screen_set_keep_on(bool p_enable) {
681
MutexLock mutex_lock(wayland_thread.mutex);
682
683
// FIXME: For some reason this does not also windows from the wayland thread.
684
685
if (screen_is_kept_on() == p_enable) {
686
return;
687
}
688
689
wayland_thread.window_set_idle_inhibition(MAIN_WINDOW_ID, p_enable);
690
691
#ifdef DBUS_ENABLED
692
if (portal_desktop && portal_desktop->is_inhibit_supported()) {
693
if (p_enable) {
694
// Attach the inhibit request to the main window, not the last focused window,
695
// on the basis that inhibiting the screensaver is global state for the application.
696
WindowID window_id = MAIN_WINDOW_ID;
697
WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wayland_thread.window_get_wl_surface(window_id));
698
screensaver_inhibited = portal_desktop->inhibit(ws ? ws->exported_handle : String());
699
} else {
700
portal_desktop->uninhibit();
701
screensaver_inhibited = false;
702
}
703
} else if (screensaver) {
704
if (p_enable) {
705
screensaver->inhibit();
706
} else {
707
screensaver->uninhibit();
708
}
709
710
screensaver_inhibited = p_enable;
711
}
712
#endif
713
}
714
715
bool DisplayServerWayland::screen_is_kept_on() const {
716
// FIXME: Multiwindow support.
717
#ifdef DBUS_ENABLED
718
return wayland_thread.window_get_idle_inhibition(MAIN_WINDOW_ID) || screensaver_inhibited;
719
#else
720
return wayland_thread.window_get_idle_inhibition(MAIN_WINDOW_ID);
721
#endif
722
}
723
724
Vector<DisplayServer::WindowID> DisplayServerWayland::get_window_list() const {
725
MutexLock mutex_lock(wayland_thread.mutex);
726
727
Vector<int> ret;
728
for (const KeyValue<WindowID, WindowData> &E : windows) {
729
ret.push_back(E.key);
730
}
731
return ret;
732
}
733
734
DisplayServer::WindowID DisplayServerWayland::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent) {
735
WindowID id = ++window_id_counter;
736
WindowData &wd = windows[id];
737
738
wd.id = id;
739
wd.mode = p_mode;
740
wd.flags = p_flags;
741
wd.vsync_mode = p_vsync_mode;
742
743
#ifdef ACCESSKIT_ENABLED
744
if (accessibility_driver && !accessibility_driver->window_create(wd.id, nullptr)) {
745
if (OS::get_singleton()->is_stdout_verbose()) {
746
ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");
747
}
748
memdelete(accessibility_driver);
749
accessibility_driver = nullptr;
750
}
751
#endif
752
753
// NOTE: Remember to clear its position if this window will be a toplevel. We
754
// can only know once we show it.
755
wd.rect = p_rect;
756
757
wd.title = "Godot";
758
wd.parent_id = p_transient_parent;
759
return id;
760
}
761
762
void DisplayServerWayland::show_window(WindowID p_window_id) {
763
MutexLock mutex_lock(wayland_thread.mutex);
764
765
ERR_FAIL_COND(!windows.has(p_window_id));
766
767
WindowData &wd = windows[p_window_id];
768
769
if (!wd.visible) {
770
DEBUG_LOG_WAYLAND(vformat("Showing window %d", p_window_id));
771
// Showing this window will reset its mode with whatever the compositor
772
// reports. We'll save the mode beforehand so that we can reapply it later.
773
// TODO: Fix/Port/Move/Whatever to `WaylandThread` APIs.
774
WindowMode setup_mode = wd.mode;
775
776
// Let's determine the closest toplevel. For toplevels it will be themselves,
777
// for popups the first toplevel ancestor it finds.
778
WindowID root_id = wd.id;
779
while (root_id != INVALID_WINDOW_ID && window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, root_id)) {
780
root_id = windows[root_id].parent_id;
781
}
782
ERR_FAIL_COND(root_id == INVALID_WINDOW_ID);
783
784
wd.root_id = root_id;
785
786
if (!window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, p_window_id)) {
787
// NOTE: DO **NOT** KEEP THE POSITION SET FOR TOPLEVELS. Wayland does not
788
// track them and we're gonna get our events transformed in unexpected ways.
789
wd.rect.position = Point2i();
790
791
DEBUG_LOG_WAYLAND(vformat("Creating regular window of size %s", wd.rect.size));
792
wayland_thread.window_create(p_window_id, wd.rect.size, wd.parent_id);
793
wayland_thread.window_set_min_size(p_window_id, wd.min_size);
794
wayland_thread.window_set_max_size(p_window_id, wd.max_size);
795
wayland_thread.window_set_app_id(p_window_id, _get_app_id_from_context(context));
796
wayland_thread.window_set_borderless(p_window_id, window_get_flag(WINDOW_FLAG_BORDERLESS, p_window_id));
797
798
// Since it can't have a position. Let's tell the window node the news by
799
// the actual rect to it.
800
if (wd.rect_changed_callback.is_valid()) {
801
wd.rect_changed_callback.call(wd.rect);
802
}
803
} else {
804
DEBUG_LOG_WAYLAND("!!!!! Making popup !!!!!");
805
806
windows[root_id].popup_stack.push_back(p_window_id);
807
808
if (window_get_flag(WINDOW_FLAG_POPUP, p_window_id)) {
809
// Reroutes all input to it.
810
popup_menu_list.push_back(p_window_id);
811
}
812
813
wayland_thread.window_create_popup(p_window_id, wd.parent_id, wd.rect);
814
}
815
816
// NOTE: The XDG shell protocol is built in a way that causes the window to
817
// be immediately shown as soon as a valid buffer is assigned to it. Hence,
818
// the only acceptable way of implementing window showing is to move the
819
// graphics context window creation logic here.
820
#ifdef RD_ENABLED
821
if (rendering_context) {
822
union {
823
#ifdef VULKAN_ENABLED
824
RenderingContextDriverVulkanWayland::WindowPlatformData vulkan;
825
#endif
826
} wpd;
827
#ifdef VULKAN_ENABLED
828
if (rendering_driver == "vulkan") {
829
wpd.vulkan.surface = wayland_thread.window_get_wl_surface(wd.id);
830
ERR_FAIL_NULL(wpd.vulkan.surface);
831
wpd.vulkan.display = wayland_thread.get_wl_display();
832
}
833
#endif
834
Error err = rendering_context->window_create(wd.id, &wpd);
835
ERR_FAIL_COND_MSG(err != OK, vformat("Can't create a %s window", rendering_driver));
836
837
rendering_context->window_set_size(wd.id, wd.rect.size.width, wd.rect.size.height);
838
839
// NOTE: Looks like we have to set the vsync mode before creating the screen
840
// or it won't work. Resist any temptation.
841
window_set_vsync_mode(wd.vsync_mode, p_window_id);
842
}
843
844
if (rendering_device) {
845
rendering_device->screen_create(wd.id);
846
}
847
#endif
848
849
#ifdef GLES3_ENABLED
850
if (egl_manager) {
851
struct wl_surface *wl_surface = wayland_thread.window_get_wl_surface(wd.id);
852
ERR_FAIL_NULL(wl_surface);
853
wd.wl_egl_window = wl_egl_window_create(wl_surface, wd.rect.size.width, wd.rect.size.height);
854
855
Error err = egl_manager->window_create(p_window_id, wayland_thread.get_wl_display(), wd.wl_egl_window, wd.rect.size.width, wd.rect.size.height);
856
ERR_FAIL_COND_MSG(err == ERR_CANT_CREATE, "Can't show a GLES3 window.");
857
858
window_set_vsync_mode(wd.vsync_mode, p_window_id);
859
}
860
#endif
861
862
// NOTE: Some public window-handling methods might depend on this flag being
863
// set. Make sure the method you're calling does not depend on it before this
864
// assignment.
865
wd.visible = true;
866
867
// Actually try to apply the window's mode now that it's visible.
868
window_set_mode(setup_mode, wd.id);
869
870
wayland_thread.window_set_title(p_window_id, wd.title);
871
}
872
}
873
874
void DisplayServerWayland::delete_sub_window(WindowID p_window_id) {
875
MutexLock mutex_lock(wayland_thread.mutex);
876
877
ERR_FAIL_COND(!windows.has(p_window_id));
878
WindowData &wd = windows[p_window_id];
879
880
ERR_FAIL_COND(!windows.has(wd.root_id));
881
WindowData &root_wd = windows[wd.root_id];
882
883
// NOTE: By the time the Wayland thread will send a `WINDOW_EVENT_MOUSE_EXIT`
884
// the window will be gone and the message will be discarded, confusing the
885
// engine. We thus have to send it ourselves.
886
if (wayland_thread.pointer_get_pointed_window_id() == p_window_id) {
887
_send_window_event(WINDOW_EVENT_MOUSE_EXIT, p_window_id);
888
}
889
890
// The XDG shell specification requires us to clear all popups in reverse order.
891
while (!root_wd.popup_stack.is_empty() && root_wd.popup_stack.back()->get() != p_window_id) {
892
_send_window_event(WINDOW_EVENT_FORCE_CLOSE, root_wd.popup_stack.back()->get());
893
}
894
895
if (root_wd.popup_stack.back() && root_wd.popup_stack.back()->get() == p_window_id) {
896
root_wd.popup_stack.pop_back();
897
}
898
899
if (popup_menu_list.back() && popup_menu_list.back()->get() == p_window_id) {
900
popup_menu_list.pop_back();
901
}
902
903
#ifdef ACCESSKIT_ENABLED
904
if (accessibility_driver) {
905
accessibility_driver->window_destroy(p_window_id);
906
}
907
#endif
908
909
if (wd.visible) {
910
#ifdef VULKAN_ENABLED
911
if (rendering_device) {
912
rendering_device->screen_free(p_window_id);
913
}
914
915
if (rendering_context) {
916
rendering_context->window_destroy(p_window_id);
917
}
918
#endif
919
920
#ifdef GLES3_ENABLED
921
if (egl_manager) {
922
egl_manager->window_destroy(p_window_id);
923
}
924
#endif
925
926
wayland_thread.window_destroy(p_window_id);
927
}
928
929
windows.erase(p_window_id);
930
931
DEBUG_LOG_WAYLAND(vformat("Destroyed window %d", p_window_id));
932
}
933
934
DisplayServer::WindowID DisplayServerWayland::window_get_active_popup() const {
935
MutexLock mutex_lock(wayland_thread.mutex);
936
937
if (!popup_menu_list.is_empty()) {
938
return popup_menu_list.back()->get();
939
}
940
941
return INVALID_WINDOW_ID;
942
}
943
944
void DisplayServerWayland::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) {
945
MutexLock mutex_lock(wayland_thread.mutex);
946
947
ERR_FAIL_COND(!windows.has(p_window));
948
949
windows[p_window].safe_rect = p_rect;
950
}
951
952
Rect2i DisplayServerWayland::window_get_popup_safe_rect(WindowID p_window) const {
953
MutexLock mutex_lock(wayland_thread.mutex);
954
955
ERR_FAIL_COND_V(!windows.has(p_window), Rect2i());
956
957
return windows[p_window].safe_rect;
958
}
959
960
int64_t DisplayServerWayland::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
961
MutexLock mutex_lock(wayland_thread.mutex);
962
963
switch (p_handle_type) {
964
case DISPLAY_HANDLE: {
965
return (int64_t)wayland_thread.get_wl_display();
966
} break;
967
968
case WINDOW_HANDLE: {
969
return (int64_t)wayland_thread.window_get_wl_surface(p_window);
970
} break;
971
972
case WINDOW_VIEW: {
973
return 0; // Not supported.
974
} break;
975
976
#ifdef GLES3_ENABLED
977
case OPENGL_CONTEXT: {
978
if (egl_manager) {
979
return (int64_t)egl_manager->get_context(p_window);
980
}
981
return 0;
982
} break;
983
case EGL_DISPLAY: {
984
if (egl_manager) {
985
return (int64_t)egl_manager->get_display(p_window);
986
}
987
return 0;
988
}
989
case EGL_CONFIG: {
990
if (egl_manager) {
991
return (int64_t)egl_manager->get_config(p_window);
992
}
993
return 0;
994
}
995
#endif // GLES3_ENABLED
996
997
default: {
998
return 0;
999
} break;
1000
}
1001
}
1002
1003
DisplayServer::WindowID DisplayServerWayland::get_window_at_screen_position(const Point2i &p_position) const {
1004
// Standard Wayland APIs don't support this.
1005
return MAIN_WINDOW_ID;
1006
}
1007
1008
void DisplayServerWayland::window_attach_instance_id(ObjectID p_instance, WindowID p_window_id) {
1009
MutexLock mutex_lock(wayland_thread.mutex);
1010
1011
ERR_FAIL_COND(!windows.has(p_window_id));
1012
1013
windows[p_window_id].instance_id = p_instance;
1014
}
1015
1016
ObjectID DisplayServerWayland::window_get_attached_instance_id(WindowID p_window_id) const {
1017
MutexLock mutex_lock(wayland_thread.mutex);
1018
1019
ERR_FAIL_COND_V(!windows.has(p_window_id), ObjectID());
1020
1021
return windows[p_window_id].instance_id;
1022
}
1023
1024
void DisplayServerWayland::window_set_title(const String &p_title, DisplayServer::WindowID p_window_id) {
1025
MutexLock mutex_lock(wayland_thread.mutex);
1026
1027
ERR_FAIL_COND(!windows.has(p_window_id));
1028
1029
WindowData &wd = windows[p_window_id];
1030
1031
wd.title = p_title;
1032
1033
if (wd.visible) {
1034
wayland_thread.window_set_title(p_window_id, wd.title);
1035
}
1036
}
1037
1038
void DisplayServerWayland::window_set_mouse_passthrough(const Vector<Vector2> &p_region, DisplayServer::WindowID p_window_id) {
1039
// TODO
1040
DEBUG_LOG_WAYLAND(vformat("wayland stub window_set_mouse_passthrough region %s", p_region));
1041
}
1042
1043
void DisplayServerWayland::window_set_rect_changed_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {
1044
MutexLock mutex_lock(wayland_thread.mutex);
1045
1046
ERR_FAIL_COND(!windows.has(p_window_id));
1047
1048
windows[p_window_id].rect_changed_callback = p_callable;
1049
}
1050
1051
void DisplayServerWayland::window_set_window_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {
1052
MutexLock mutex_lock(wayland_thread.mutex);
1053
1054
ERR_FAIL_COND(!windows.has(p_window_id));
1055
1056
windows[p_window_id].window_event_callback = p_callable;
1057
}
1058
1059
void DisplayServerWayland::window_set_input_event_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {
1060
MutexLock mutex_lock(wayland_thread.mutex);
1061
1062
ERR_FAIL_COND(!windows.has(p_window_id));
1063
1064
windows[p_window_id].input_event_callback = p_callable;
1065
}
1066
1067
void DisplayServerWayland::window_set_input_text_callback(const Callable &p_callable, WindowID p_window_id) {
1068
MutexLock mutex_lock(wayland_thread.mutex);
1069
1070
ERR_FAIL_COND(!windows.has(p_window_id));
1071
1072
windows[p_window_id].input_text_callback = p_callable;
1073
}
1074
1075
void DisplayServerWayland::window_set_drop_files_callback(const Callable &p_callable, DisplayServer::WindowID p_window_id) {
1076
MutexLock mutex_lock(wayland_thread.mutex);
1077
1078
ERR_FAIL_COND(!windows.has(p_window_id));
1079
1080
windows[p_window_id].drop_files_callback = p_callable;
1081
}
1082
1083
int DisplayServerWayland::window_get_current_screen(DisplayServer::WindowID p_window_id) const {
1084
ERR_FAIL_COND_V(!windows.has(p_window_id), INVALID_SCREEN);
1085
// Standard Wayland APIs don't support getting the screen of a window.
1086
return 0;
1087
}
1088
1089
void DisplayServerWayland::window_set_current_screen(int p_screen, DisplayServer::WindowID p_window_id) {
1090
// Standard Wayland APIs don't support setting the screen of a window.
1091
}
1092
1093
Point2i DisplayServerWayland::window_get_position(DisplayServer::WindowID p_window_id) const {
1094
MutexLock mutex_lock(wayland_thread.mutex);
1095
1096
return windows[p_window_id].rect.position;
1097
}
1098
1099
Point2i DisplayServerWayland::window_get_position_with_decorations(DisplayServer::WindowID p_window_id) const {
1100
MutexLock mutex_lock(wayland_thread.mutex);
1101
1102
return windows[p_window_id].rect.position;
1103
}
1104
1105
void DisplayServerWayland::window_set_position(const Point2i &p_position, DisplayServer::WindowID p_window_id) {
1106
// Unsupported with toplevels.
1107
}
1108
1109
void DisplayServerWayland::window_set_max_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {
1110
MutexLock mutex_lock(wayland_thread.mutex);
1111
1112
DEBUG_LOG_WAYLAND(vformat("window max size set to %s", p_size));
1113
1114
if (p_size.x < 0 || p_size.y < 0) {
1115
ERR_FAIL_MSG("Maximum window size can't be negative!");
1116
}
1117
1118
ERR_FAIL_COND(!windows.has(p_window_id));
1119
WindowData &wd = windows[p_window_id];
1120
1121
// FIXME: Is `p_size.x < wd.min_size.x || p_size.y < wd.min_size.y` == `p_size < wd.min_size`?
1122
if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
1123
ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
1124
return;
1125
}
1126
1127
wd.max_size = p_size;
1128
1129
if (wd.visible) {
1130
wayland_thread.window_set_max_size(p_window_id, p_size);
1131
}
1132
}
1133
1134
Size2i DisplayServerWayland::window_get_max_size(DisplayServer::WindowID p_window_id) const {
1135
MutexLock mutex_lock(wayland_thread.mutex);
1136
1137
ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());
1138
return windows[p_window_id].max_size;
1139
}
1140
1141
void DisplayServerWayland::gl_window_make_current(DisplayServer::WindowID p_window_id) {
1142
#ifdef GLES3_ENABLED
1143
if (egl_manager) {
1144
egl_manager->window_make_current(p_window_id);
1145
}
1146
#endif
1147
}
1148
1149
void DisplayServerWayland::window_set_transient(WindowID p_window_id, WindowID p_parent) {
1150
MutexLock mutex_lock(wayland_thread.mutex);
1151
1152
ERR_FAIL_COND(!windows.has(p_window_id));
1153
WindowData &wd = windows[p_window_id];
1154
1155
ERR_FAIL_COND(wd.parent_id == p_parent);
1156
1157
if (p_parent != INVALID_WINDOW_ID) {
1158
ERR_FAIL_COND(!windows.has(p_parent));
1159
ERR_FAIL_COND_MSG(wd.parent_id != INVALID_WINDOW_ID, "Window already has a transient parent");
1160
wd.parent_id = p_parent;
1161
1162
// NOTE: Looks like live unparenting is not really practical unfortunately.
1163
// See WaylandThread::window_set_parent for more info.
1164
if (wd.visible) {
1165
wayland_thread.window_set_parent(p_window_id, p_parent);
1166
}
1167
}
1168
}
1169
1170
void DisplayServerWayland::window_set_min_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {
1171
MutexLock mutex_lock(wayland_thread.mutex);
1172
1173
DEBUG_LOG_WAYLAND(vformat("window minsize set to %s", p_size));
1174
1175
ERR_FAIL_COND(!windows.has(p_window_id));
1176
WindowData &wd = windows[p_window_id];
1177
1178
if (p_size.x < 0 || p_size.y < 0) {
1179
ERR_FAIL_MSG("Minimum window size can't be negative!");
1180
}
1181
1182
// FIXME: Is `p_size.x > wd.max_size.x || p_size.y > wd.max_size.y` == `p_size > wd.max_size`?
1183
if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
1184
ERR_PRINT("Minimum window size can't be larger than maximum window size!");
1185
return;
1186
}
1187
1188
wd.min_size = p_size;
1189
1190
if (wd.visible) {
1191
wayland_thread.window_set_min_size(p_window_id, p_size);
1192
}
1193
}
1194
1195
Size2i DisplayServerWayland::window_get_min_size(DisplayServer::WindowID p_window_id) const {
1196
MutexLock mutex_lock(wayland_thread.mutex);
1197
1198
ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());
1199
return windows[p_window_id].min_size;
1200
}
1201
1202
void DisplayServerWayland::window_set_size(const Size2i p_size, DisplayServer::WindowID p_window_id) {
1203
MutexLock mutex_lock(wayland_thread.mutex);
1204
1205
ERR_FAIL_COND(!windows.has(p_window_id));
1206
WindowData &wd = windows[p_window_id];
1207
1208
Size2i new_size = p_size;
1209
if (wd.visible) {
1210
new_size = wayland_thread.window_set_size(p_window_id, p_size);
1211
}
1212
1213
_update_window_rect(Rect2i(wd.rect.position, new_size), p_window_id);
1214
}
1215
1216
Size2i DisplayServerWayland::window_get_size(DisplayServer::WindowID p_window_id) const {
1217
MutexLock mutex_lock(wayland_thread.mutex);
1218
1219
ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());
1220
return windows[p_window_id].rect.size;
1221
}
1222
1223
Size2i DisplayServerWayland::window_get_size_with_decorations(DisplayServer::WindowID p_window_id) const {
1224
MutexLock mutex_lock(wayland_thread.mutex);
1225
1226
// I don't think there's a way of actually knowing the size of the window
1227
// decoration in Wayland, at least in the case of SSDs, nor that it would be
1228
// that useful in this case. We'll just return the main window's size.
1229
ERR_FAIL_COND_V(!windows.has(p_window_id), Size2i());
1230
return windows[p_window_id].rect.size;
1231
}
1232
1233
float DisplayServerWayland::window_get_scale(WindowID p_window_id) const {
1234
MutexLock mutex_lock(wayland_thread.mutex);
1235
1236
const WaylandThread::WindowState *ws = wayland_thread.window_get_state(p_window_id);
1237
ERR_FAIL_NULL_V(ws, 1);
1238
1239
return wayland_thread.window_state_get_scale_factor(ws);
1240
}
1241
1242
void DisplayServerWayland::window_set_mode(WindowMode p_mode, DisplayServer::WindowID p_window_id) {
1243
MutexLock mutex_lock(wayland_thread.mutex);
1244
1245
ERR_FAIL_COND(!windows.has(p_window_id));
1246
WindowData &wd = windows[p_window_id];
1247
1248
if (!wd.visible) {
1249
return;
1250
}
1251
1252
wayland_thread.window_try_set_mode(p_window_id, p_mode);
1253
}
1254
1255
DisplayServer::WindowMode DisplayServerWayland::window_get_mode(DisplayServer::WindowID p_window_id) const {
1256
MutexLock mutex_lock(wayland_thread.mutex);
1257
1258
ERR_FAIL_COND_V(!windows.has(p_window_id), WINDOW_MODE_WINDOWED);
1259
const WindowData &wd = windows[p_window_id];
1260
1261
if (!wd.visible) {
1262
return WINDOW_MODE_WINDOWED;
1263
}
1264
1265
return wayland_thread.window_get_mode(p_window_id);
1266
}
1267
1268
bool DisplayServerWayland::window_is_maximize_allowed(DisplayServer::WindowID p_window_id) const {
1269
MutexLock mutex_lock(wayland_thread.mutex);
1270
1271
return wayland_thread.window_can_set_mode(p_window_id, WINDOW_MODE_MAXIMIZED);
1272
}
1273
1274
void DisplayServerWayland::window_set_flag(WindowFlags p_flag, bool p_enabled, DisplayServer::WindowID p_window_id) {
1275
MutexLock mutex_lock(wayland_thread.mutex);
1276
1277
ERR_FAIL_COND(!windows.has(p_window_id));
1278
WindowData &wd = windows[p_window_id];
1279
1280
DEBUG_LOG_WAYLAND(vformat("Window set flag %d", p_flag));
1281
1282
switch (p_flag) {
1283
case WINDOW_FLAG_BORDERLESS: {
1284
wayland_thread.window_set_borderless(p_window_id, p_enabled);
1285
} break;
1286
1287
case WINDOW_FLAG_POPUP: {
1288
ERR_FAIL_COND_MSG(p_window_id == MAIN_WINDOW_ID, "Main window can't be popup.");
1289
ERR_FAIL_COND_MSG(wd.visible && (wd.flags & WINDOW_FLAG_POPUP_BIT) != p_enabled, "Popup flag can't changed while window is opened.");
1290
} break;
1291
1292
case WINDOW_FLAG_POPUP_WM_HINT: {
1293
ERR_FAIL_COND_MSG(p_window_id == MAIN_WINDOW_ID, "Main window can't have popup hint.");
1294
ERR_FAIL_COND_MSG(wd.visible && (wd.flags & WINDOW_FLAG_POPUP_WM_HINT_BIT) != p_enabled, "Popup hint can't changed while window is opened.");
1295
} break;
1296
1297
default: {
1298
}
1299
}
1300
1301
if (p_enabled) {
1302
wd.flags |= 1 << p_flag;
1303
} else {
1304
wd.flags &= ~(1 << p_flag);
1305
}
1306
}
1307
1308
bool DisplayServerWayland::window_get_flag(WindowFlags p_flag, DisplayServer::WindowID p_window_id) const {
1309
MutexLock mutex_lock(wayland_thread.mutex);
1310
1311
ERR_FAIL_COND_V(!windows.has(p_window_id), false);
1312
return windows[p_window_id].flags & (1 << p_flag);
1313
}
1314
1315
void DisplayServerWayland::window_request_attention(DisplayServer::WindowID p_window_id) {
1316
MutexLock mutex_lock(wayland_thread.mutex);
1317
1318
DEBUG_LOG_WAYLAND("Requested attention.");
1319
1320
wayland_thread.window_request_attention(p_window_id);
1321
}
1322
1323
void DisplayServerWayland::window_move_to_foreground(DisplayServer::WindowID p_window_id) {
1324
// Standard Wayland APIs don't support this.
1325
}
1326
1327
bool DisplayServerWayland::window_is_focused(WindowID p_window_id) const {
1328
MutexLock mutex_lock(wayland_thread.mutex);
1329
1330
return wayland_thread.pointer_get_pointed_window_id() == p_window_id;
1331
}
1332
1333
bool DisplayServerWayland::window_can_draw(DisplayServer::WindowID p_window_id) const {
1334
MutexLock mutex_lock(wayland_thread.mutex);
1335
1336
uint64_t last_frame_time = wayland_thread.window_get_last_frame_time(p_window_id);
1337
uint64_t time_since_frame = OS::get_singleton()->get_ticks_usec() - last_frame_time;
1338
1339
if (time_since_frame > WAYLAND_MAX_FRAME_TIME_US) {
1340
return false;
1341
}
1342
1343
if (wayland_thread.window_is_suspended(p_window_id)) {
1344
return false;
1345
}
1346
1347
return suspend_state == SuspendState::NONE;
1348
}
1349
1350
bool DisplayServerWayland::can_any_window_draw() const {
1351
return suspend_state == SuspendState::NONE;
1352
}
1353
1354
void DisplayServerWayland::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) {
1355
MutexLock mutex_lock(wayland_thread.mutex);
1356
1357
wayland_thread.window_set_ime_active(p_active, p_window_id);
1358
}
1359
1360
void DisplayServerWayland::window_set_ime_position(const Point2i &p_pos, DisplayServer::WindowID p_window_id) {
1361
MutexLock mutex_lock(wayland_thread.mutex);
1362
1363
wayland_thread.window_set_ime_position(p_pos, p_window_id);
1364
}
1365
1366
int DisplayServerWayland::accessibility_should_increase_contrast() const {
1367
#ifdef DBUS_ENABLED
1368
if (!portal_desktop) {
1369
return -1;
1370
}
1371
return portal_desktop->get_high_contrast();
1372
#endif
1373
return -1;
1374
}
1375
1376
int DisplayServerWayland::accessibility_screen_reader_active() const {
1377
#ifdef DBUS_ENABLED
1378
if (atspi_monitor && atspi_monitor->is_supported()) {
1379
return atspi_monitor->is_active();
1380
}
1381
#endif
1382
return -1;
1383
}
1384
1385
Point2i DisplayServerWayland::ime_get_selection() const {
1386
return ime_selection;
1387
}
1388
1389
String DisplayServerWayland::ime_get_text() const {
1390
return ime_text;
1391
}
1392
1393
// NOTE: While Wayland is supposed to be tear-free, wayland-protocols version
1394
// 1.30 added a protocol for allowing async flips which is supposed to be
1395
// handled by drivers such as Vulkan. We can then just ask to disable v-sync and
1396
// hope for the best. See: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/commit/6394f0b4f3be151076f10a845a2fb131eeb56706
1397
void DisplayServerWayland::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, DisplayServer::WindowID p_window_id) {
1398
MutexLock mutex_lock(wayland_thread.mutex);
1399
1400
WindowData &wd = windows[p_window_id];
1401
1402
#ifdef RD_ENABLED
1403
if (rendering_context) {
1404
rendering_context->window_set_vsync_mode(p_window_id, p_vsync_mode);
1405
1406
wd.emulate_vsync = (!wayland_thread.is_fifo_available() && rendering_context->window_get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED);
1407
1408
if (wd.emulate_vsync) {
1409
print_verbose("VSYNC: manually throttling frames using MAILBOX.");
1410
rendering_context->window_set_vsync_mode(p_window_id, DisplayServer::VSYNC_MAILBOX);
1411
}
1412
}
1413
#endif // VULKAN_ENABLED
1414
1415
#ifdef GLES3_ENABLED
1416
if (egl_manager) {
1417
egl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
1418
1419
// NOTE: Mesa's EGL implementation does not seem to make use of fifo_v1 so
1420
// we'll have to always emulate V-Sync.
1421
wd.emulate_vsync = egl_manager->is_using_vsync();
1422
1423
if (wd.emulate_vsync) {
1424
print_verbose("VSYNC: manually throttling frames with swap delay 0.");
1425
egl_manager->set_use_vsync(false);
1426
}
1427
}
1428
#endif // GLES3_ENABLED
1429
}
1430
1431
DisplayServer::VSyncMode DisplayServerWayland::window_get_vsync_mode(DisplayServer::WindowID p_window_id) const {
1432
const WindowData &wd = windows[p_window_id];
1433
if (wd.emulate_vsync) {
1434
return DisplayServer::VSYNC_ENABLED;
1435
}
1436
1437
#ifdef VULKAN_ENABLED
1438
if (rendering_context) {
1439
return rendering_context->window_get_vsync_mode(p_window_id);
1440
}
1441
#endif // VULKAN_ENABLED
1442
1443
#ifdef GLES3_ENABLED
1444
if (egl_manager) {
1445
return egl_manager->is_using_vsync() ? DisplayServer::VSYNC_ENABLED : DisplayServer::VSYNC_DISABLED;
1446
}
1447
#endif // GLES3_ENABLED
1448
1449
return DisplayServer::VSYNC_ENABLED;
1450
}
1451
1452
void DisplayServerWayland::window_start_drag(WindowID p_window) {
1453
MutexLock mutex_lock(wayland_thread.mutex);
1454
1455
wayland_thread.window_start_drag(p_window);
1456
}
1457
1458
void DisplayServerWayland::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {
1459
MutexLock mutex_lock(wayland_thread.mutex);
1460
1461
ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);
1462
wayland_thread.window_start_resize(p_edge, p_window);
1463
}
1464
1465
void DisplayServerWayland::cursor_set_shape(CursorShape p_shape) {
1466
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
1467
1468
MutexLock mutex_lock(wayland_thread.mutex);
1469
1470
if (p_shape == cursor_shape) {
1471
return;
1472
}
1473
1474
cursor_shape = p_shape;
1475
1476
if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
1477
// Hidden.
1478
return;
1479
}
1480
1481
wayland_thread.cursor_set_shape(p_shape);
1482
}
1483
1484
DisplayServerWayland::CursorShape DisplayServerWayland::cursor_get_shape() const {
1485
MutexLock mutex_lock(wayland_thread.mutex);
1486
1487
return cursor_shape;
1488
}
1489
1490
void DisplayServerWayland::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
1491
MutexLock mutex_lock(wayland_thread.mutex);
1492
1493
if (p_cursor.is_valid()) {
1494
HashMap<CursorShape, CustomCursor>::Iterator cursor_c = custom_cursors.find(p_shape);
1495
1496
if (cursor_c) {
1497
if (cursor_c->value.resource == p_cursor && cursor_c->value.hotspot == p_hotspot) {
1498
// We have a cached cursor. Nice.
1499
wayland_thread.cursor_set_shape(p_shape);
1500
return;
1501
}
1502
1503
// We're changing this cursor; we'll have to rebuild it.
1504
custom_cursors.erase(p_shape);
1505
wayland_thread.cursor_shape_clear_custom_image(p_shape);
1506
}
1507
1508
Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);
1509
ERR_FAIL_COND(image.is_null());
1510
1511
CustomCursor &cursor = custom_cursors[p_shape];
1512
1513
cursor.resource = p_cursor;
1514
cursor.hotspot = p_hotspot;
1515
1516
wayland_thread.cursor_shape_set_custom_image(p_shape, image, p_hotspot);
1517
1518
wayland_thread.cursor_set_shape(p_shape);
1519
} else {
1520
// Clear cache and reset to default system cursor.
1521
wayland_thread.cursor_shape_clear_custom_image(p_shape);
1522
1523
if (cursor_shape == p_shape) {
1524
wayland_thread.cursor_set_shape(p_shape);
1525
}
1526
1527
if (custom_cursors.has(p_shape)) {
1528
custom_cursors.erase(p_shape);
1529
}
1530
}
1531
}
1532
1533
bool DisplayServerWayland::get_swap_cancel_ok() {
1534
return swap_cancel_ok;
1535
}
1536
1537
Error DisplayServerWayland::embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) {
1538
MutexLock mutex_lock(wayland_thread.mutex);
1539
1540
struct godot_embedding_compositor *ec = wayland_thread.get_embedding_compositor();
1541
ERR_FAIL_NULL_V_MSG(ec, ERR_BUG, "Missing embedded compositor interface");
1542
1543
struct WaylandThread::EmbeddingCompositorState *ecs = WaylandThread::godot_embedding_compositor_get_state(ec);
1544
ERR_FAIL_NULL_V(ecs, ERR_BUG);
1545
1546
if (!ecs->mapped_clients.has(p_pid)) {
1547
return ERR_DOES_NOT_EXIST;
1548
}
1549
1550
struct godot_embedded_client *embedded_client = ecs->mapped_clients[p_pid];
1551
WaylandThread::EmbeddedClientState *client_data = (WaylandThread::EmbeddedClientState *)godot_embedded_client_get_user_data(embedded_client);
1552
ERR_FAIL_NULL_V(client_data, ERR_BUG);
1553
1554
if (p_grab_focus) {
1555
godot_embedded_client_focus_window(embedded_client);
1556
}
1557
1558
if (p_visible) {
1559
WaylandThread::WindowState *ws = wayland_thread.window_get_state(p_window);
1560
ERR_FAIL_NULL_V(ws, ERR_BUG);
1561
1562
struct xdg_toplevel *toplevel = ws->xdg_toplevel;
1563
#ifdef LIBDECOR_ENABLED
1564
if (toplevel == nullptr && ws->libdecor_frame) {
1565
toplevel = libdecor_frame_get_xdg_toplevel(ws->libdecor_frame);
1566
}
1567
#endif
1568
1569
ERR_FAIL_NULL_V(toplevel, ERR_CANT_CREATE);
1570
1571
godot_embedded_client_set_embedded_window_parent(embedded_client, toplevel);
1572
1573
double window_scale = WaylandThread::window_state_get_scale_factor(ws);
1574
1575
Rect2i scaled_rect = p_rect;
1576
scaled_rect.position = WaylandThread::scale_vector2i(scaled_rect.position, 1 / window_scale);
1577
scaled_rect.size = WaylandThread::scale_vector2i(scaled_rect.size, 1 / window_scale);
1578
1579
print_verbose(vformat("Scaling embedded rect down by %f from %s to %s.", window_scale, p_rect, scaled_rect));
1580
1581
godot_embedded_client_set_embedded_window_rect(embedded_client, scaled_rect.position.x, scaled_rect.position.y, scaled_rect.size.width, scaled_rect.size.height);
1582
} else {
1583
godot_embedded_client_set_embedded_window_parent(embedded_client, nullptr);
1584
}
1585
1586
return OK;
1587
}
1588
1589
Error DisplayServerWayland::request_close_embedded_process(OS::ProcessID p_pid) {
1590
MutexLock mutex_lock(wayland_thread.mutex);
1591
1592
struct godot_embedding_compositor *ec = wayland_thread.get_embedding_compositor();
1593
ERR_FAIL_NULL_V_MSG(ec, ERR_BUG, "Missing embedded compositor interface");
1594
1595
struct WaylandThread::EmbeddingCompositorState *ecs = WaylandThread::godot_embedding_compositor_get_state(ec);
1596
ERR_FAIL_NULL_V(ecs, ERR_BUG);
1597
1598
if (!ecs->mapped_clients.has(p_pid)) {
1599
return ERR_DOES_NOT_EXIST;
1600
}
1601
1602
struct godot_embedded_client *embedded_client = ecs->mapped_clients[p_pid];
1603
WaylandThread::EmbeddedClientState *client_data = (WaylandThread::EmbeddedClientState *)godot_embedded_client_get_user_data(embedded_client);
1604
ERR_FAIL_NULL_V(client_data, ERR_BUG);
1605
1606
godot_embedded_client_embedded_window_request_close(embedded_client);
1607
return OK;
1608
}
1609
1610
Error DisplayServerWayland::remove_embedded_process(OS::ProcessID p_pid) {
1611
return request_close_embedded_process(p_pid);
1612
}
1613
1614
OS::ProcessID DisplayServerWayland::get_focused_process_id() {
1615
MutexLock mutex_lock(wayland_thread.mutex);
1616
1617
OS::ProcessID embedded_pid = wayland_thread.embedded_compositor_get_focused_pid();
1618
1619
if (embedded_pid < 0) {
1620
return OS::get_singleton()->get_process_id();
1621
}
1622
1623
return embedded_pid;
1624
}
1625
1626
int DisplayServerWayland::keyboard_get_layout_count() const {
1627
MutexLock mutex_lock(wayland_thread.mutex);
1628
1629
return wayland_thread.keyboard_get_layout_count();
1630
}
1631
1632
int DisplayServerWayland::keyboard_get_current_layout() const {
1633
MutexLock mutex_lock(wayland_thread.mutex);
1634
1635
return wayland_thread.keyboard_get_current_layout_index();
1636
}
1637
1638
void DisplayServerWayland::keyboard_set_current_layout(int p_index) {
1639
MutexLock mutex_lock(wayland_thread.mutex);
1640
1641
wayland_thread.keyboard_set_current_layout_index(p_index);
1642
}
1643
1644
String DisplayServerWayland::keyboard_get_layout_language(int p_index) const {
1645
MutexLock mutex_lock(wayland_thread.mutex);
1646
1647
// xkbcommon exposes only the layout's name, which looks like it overlaps with
1648
// its language.
1649
return wayland_thread.keyboard_get_layout_name(p_index);
1650
}
1651
1652
String DisplayServerWayland::keyboard_get_layout_name(int p_index) const {
1653
MutexLock mutex_lock(wayland_thread.mutex);
1654
1655
return wayland_thread.keyboard_get_layout_name(p_index);
1656
}
1657
1658
Key DisplayServerWayland::keyboard_get_keycode_from_physical(Key p_keycode) const {
1659
MutexLock mutex_lock(wayland_thread.mutex);
1660
1661
Key key = wayland_thread.keyboard_get_key_from_physical(p_keycode);
1662
1663
// If not found, fallback to QWERTY.
1664
// This should match the behavior of the event pump.
1665
if (key == Key::NONE) {
1666
return p_keycode;
1667
}
1668
1669
if (key >= Key::A + 32 && key <= Key::Z + 32) {
1670
key -= 'a' - 'A';
1671
}
1672
1673
// Make it consistent with the keys returned by `Input`.
1674
if (key == Key::BACKTAB) {
1675
key = Key::TAB;
1676
}
1677
1678
return key;
1679
}
1680
1681
Key DisplayServerWayland::keyboard_get_label_from_physical(Key p_keycode) const {
1682
MutexLock mutex_lock(wayland_thread.mutex);
1683
1684
return wayland_thread.keyboard_get_label_from_physical(p_keycode);
1685
}
1686
1687
bool DisplayServerWayland::color_picker(const Callable &p_callback) {
1688
#ifdef DBUS_ENABLED
1689
if (!portal_desktop) {
1690
return false;
1691
}
1692
MutexLock mutex_lock(wayland_thread.mutex);
1693
WindowID window_id = MAIN_WINDOW_ID;
1694
// TODO: Use window IDs for multiwindow support.
1695
WaylandThread::WindowState *ws = wayland_thread.wl_surface_get_window_state(wayland_thread.window_get_wl_surface(window_id));
1696
return portal_desktop->color_picker((ws ? ws->exported_handle : String()), p_callback);
1697
#else
1698
return false;
1699
#endif
1700
}
1701
1702
void DisplayServerWayland::try_suspend() {
1703
// Due to various reasons, we manually handle display synchronization by
1704
// waiting for a frame event (request to draw) or, if available, the actual
1705
// window's suspend status. When a window is suspended, we can avoid drawing
1706
// altogether, either because the compositor told us that we don't need to or
1707
// because the pace of the frame events became unreliable.
1708
bool frame = wayland_thread.wait_frame_suspend_ms(WAYLAND_MAX_FRAME_TIME_US / 1000);
1709
if (!frame) {
1710
suspend_state = SuspendState::TIMEOUT;
1711
}
1712
}
1713
1714
void DisplayServerWayland::process_events() {
1715
wayland_thread.mutex.lock();
1716
1717
wayland_thread.keyboard_echo_keys();
1718
1719
while (wayland_thread.has_message()) {
1720
Ref<WaylandThread::Message> msg = wayland_thread.pop_message();
1721
1722
// Generic check. Not actual message handling.
1723
Ref<WaylandThread::WindowMessage> win_msg = msg;
1724
if (win_msg.is_valid()) {
1725
ERR_CONTINUE_MSG(win_msg->id == INVALID_WINDOW_ID, "Invalid window ID received from Wayland thread.");
1726
1727
if (!windows.has(win_msg->id)) {
1728
// Window got probably deleted.
1729
continue;
1730
}
1731
}
1732
1733
Ref<WaylandThread::WindowRectMessage> winrect_msg = msg;
1734
if (winrect_msg.is_valid()) {
1735
_update_window_rect(winrect_msg->rect, winrect_msg->id);
1736
continue;
1737
}
1738
1739
Ref<WaylandThread::WindowEventMessage> winev_msg = msg;
1740
if (winev_msg.is_valid() && windows.has(winev_msg->id)) {
1741
_send_window_event(winev_msg->event, winev_msg->id);
1742
1743
if (winev_msg->event == WINDOW_EVENT_FOCUS_IN) {
1744
#ifdef ACCESSKIT_ENABLED
1745
if (accessibility_driver) {
1746
accessibility_driver->accessibility_set_window_focused(winev_msg->id, true);
1747
}
1748
#endif
1749
if (OS::get_singleton()->get_main_loop()) {
1750
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
1751
}
1752
} else if (winev_msg->event == WINDOW_EVENT_FOCUS_OUT) {
1753
#ifdef ACCESSKIT_ENABLED
1754
if (accessibility_driver) {
1755
accessibility_driver->accessibility_set_window_focused(winev_msg->id, false);
1756
}
1757
#endif
1758
if (OS::get_singleton()->get_main_loop()) {
1759
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
1760
}
1761
Input::get_singleton()->release_pressed_events();
1762
}
1763
continue;
1764
}
1765
1766
Ref<WaylandThread::InputEventMessage> inputev_msg = msg;
1767
if (inputev_msg.is_valid()) {
1768
Ref<InputEventMouseButton> mb = inputev_msg->event;
1769
1770
bool handled = false;
1771
if (mb.is_valid()) {
1772
BitField<MouseButtonMask> mouse_mask = mb->get_button_mask();
1773
if (!popup_menu_list.is_empty() && mb->is_pressed() && mouse_mask != last_mouse_monitor_mask) {
1774
// Popup menu handling.
1775
List<WindowID>::Element *E = popup_menu_list.back();
1776
List<WindowID>::Element *C = nullptr;
1777
1778
// Looking for the oldest popup to close.
1779
while (E) {
1780
WindowData &wd = windows[E->get()];
1781
Point2 global_pos = mb->get_position() + window_get_position(mb->get_window_id());
1782
if (wd.rect.has_point(global_pos)) {
1783
break;
1784
} else if (wd.safe_rect.has_point(global_pos)) {
1785
break;
1786
}
1787
1788
C = E;
1789
E = E->prev();
1790
}
1791
1792
if (C) {
1793
handled = true;
1794
_send_window_event(WINDOW_EVENT_CLOSE_REQUEST, C->get());
1795
}
1796
}
1797
1798
last_mouse_monitor_mask = mouse_mask;
1799
}
1800
1801
if (!handled) {
1802
Input::get_singleton()->parse_input_event(inputev_msg->event);
1803
}
1804
continue;
1805
}
1806
1807
Ref<WaylandThread::DropFilesEventMessage> dropfiles_msg = msg;
1808
if (dropfiles_msg.is_valid()) {
1809
WindowData wd = windows[dropfiles_msg->id];
1810
1811
if (wd.drop_files_callback.is_valid()) {
1812
Variant v_files = dropfiles_msg->files;
1813
const Variant *v_args[1] = { &v_files };
1814
Variant ret;
1815
Callable::CallError ce;
1816
wd.drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
1817
if (ce.error != Callable::CallError::CALL_OK) {
1818
ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(wd.drop_files_callback, v_args, 1, ce)));
1819
}
1820
}
1821
continue;
1822
}
1823
1824
Ref<WaylandThread::IMECommitEventMessage> ime_commit_msg = msg;
1825
if (ime_commit_msg.is_valid()) {
1826
for (int i = 0; i < ime_commit_msg->text.length(); i++) {
1827
const char32_t codepoint = ime_commit_msg->text[i];
1828
1829
Ref<InputEventKey> ke;
1830
ke.instantiate();
1831
ke->set_window_id(ime_commit_msg->id);
1832
ke->set_pressed(true);
1833
ke->set_echo(false);
1834
ke->set_keycode(Key::NONE);
1835
ke->set_physical_keycode(Key::NONE);
1836
ke->set_key_label(Key::NONE);
1837
ke->set_unicode(codepoint);
1838
1839
Input::get_singleton()->parse_input_event(ke);
1840
}
1841
ime_text = String();
1842
ime_selection = Vector2i();
1843
1844
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
1845
continue;
1846
}
1847
1848
Ref<WaylandThread::IMEUpdateEventMessage> ime_update_msg = msg;
1849
if (ime_update_msg.is_valid()) {
1850
if (ime_text != ime_update_msg->text || ime_selection != ime_update_msg->selection) {
1851
ime_text = ime_update_msg->text;
1852
ime_selection = ime_update_msg->selection;
1853
1854
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
1855
}
1856
continue;
1857
}
1858
}
1859
1860
switch (suspend_state) {
1861
case SuspendState::NONE: {
1862
bool emulate_vsync = false;
1863
for (KeyValue<DisplayServer::WindowID, WindowData> &pair : windows) {
1864
if (pair.value.emulate_vsync) {
1865
emulate_vsync = true;
1866
break;
1867
}
1868
}
1869
1870
if (emulate_vsync) {
1871
// Due to the way legacy suspension works, we have to treat low processor
1872
// usage mode very differently than the regular one.
1873
if (OS::get_singleton()->is_in_low_processor_usage_mode()) {
1874
// NOTE: We must avoid committing a surface if we expect a new frame, as we
1875
// might otherwise commit some inconsistent data (e.g. buffer scale). Note
1876
// that if a new frame is expected it's going to be committed by the renderer
1877
// soon anyways.
1878
if (!RenderingServer::get_singleton()->has_changed()) {
1879
// We _can't_ commit in a different thread (such as in the frame callback
1880
// itself) because we would risk to step on the renderer's feet, which would
1881
// cause subtle but severe issues, such as crashes on setups with explicit
1882
// sync. This isn't normally a problem, as the renderer commits at every
1883
// frame (which is what we need for atomic surface updates anyways), but in
1884
// low processor usage mode that expectation is broken. When it's on, our
1885
// frame rate stops being constant. This also reflects in the frame
1886
// information we use for legacy suspension. In order to avoid issues, let's
1887
// manually commit all surfaces, so that we can get fresh frame data.
1888
wayland_thread.commit_surfaces();
1889
try_suspend();
1890
}
1891
} else {
1892
try_suspend();
1893
}
1894
}
1895
1896
if (wayland_thread.is_suspended()) {
1897
suspend_state = SuspendState::CAPABILITY;
1898
}
1899
1900
if (suspend_state == SuspendState::TIMEOUT) {
1901
DEBUG_LOG_WAYLAND("Suspending. Reason: timeout.");
1902
} else if (suspend_state == SuspendState::CAPABILITY) {
1903
DEBUG_LOG_WAYLAND("Suspending. Reason: capability.");
1904
}
1905
} break;
1906
1907
case SuspendState::TIMEOUT: {
1908
// Certain compositors might not report the "suspended" wm_capability flag.
1909
// Because of this we'll wake up at the next frame event, indicating the
1910
// desire for the compositor to let us repaint.
1911
if (wayland_thread.get_reset_frame()) {
1912
suspend_state = SuspendState::NONE;
1913
DEBUG_LOG_WAYLAND("Unsuspending from timeout.");
1914
}
1915
1916
// Since we're not rendering, nothing is committing the windows'
1917
// surfaces. We have to do it ourselves.
1918
wayland_thread.commit_surfaces();
1919
} break;
1920
1921
case SuspendState::CAPABILITY: {
1922
// If we suspended by capability we can assume that it will be reset when
1923
// the compositor wants us to repaint.
1924
if (!wayland_thread.is_suspended()) {
1925
suspend_state = SuspendState::NONE;
1926
DEBUG_LOG_WAYLAND("Unsuspending from capability.");
1927
}
1928
} break;
1929
}
1930
1931
#ifdef DBUS_ENABLED
1932
if (portal_desktop) {
1933
portal_desktop->process_callbacks();
1934
}
1935
#endif
1936
1937
wayland_thread.mutex.unlock();
1938
1939
Input::get_singleton()->flush_buffered_events();
1940
}
1941
1942
void DisplayServerWayland::release_rendering_thread() {
1943
#ifdef GLES3_ENABLED
1944
if (egl_manager) {
1945
egl_manager->release_current();
1946
}
1947
#endif
1948
}
1949
1950
void DisplayServerWayland::swap_buffers() {
1951
#ifdef GLES3_ENABLED
1952
if (egl_manager) {
1953
egl_manager->swap_buffers();
1954
}
1955
#endif
1956
}
1957
1958
void DisplayServerWayland::set_icon(const Ref<Image> &p_icon) {
1959
MutexLock mutex_lock(wayland_thread.mutex);
1960
wayland_thread.set_icon(p_icon);
1961
}
1962
1963
void DisplayServerWayland::set_context(Context p_context) {
1964
MutexLock mutex_lock(wayland_thread.mutex);
1965
1966
DEBUG_LOG_WAYLAND(vformat("Setting context %d.", p_context));
1967
1968
context = p_context;
1969
1970
String app_id = _get_app_id_from_context(p_context);
1971
wayland_thread.window_set_app_id(MAIN_WINDOW_ID, app_id);
1972
}
1973
1974
bool DisplayServerWayland::is_window_transparency_available() const {
1975
#if defined(RD_ENABLED)
1976
if (rendering_device && !rendering_device->is_composite_alpha_supported()) {
1977
return false;
1978
}
1979
#endif
1980
return OS::get_singleton()->is_layered_allowed();
1981
}
1982
1983
Vector<String> DisplayServerWayland::get_rendering_drivers_func() {
1984
Vector<String> drivers;
1985
1986
#ifdef VULKAN_ENABLED
1987
drivers.push_back("vulkan");
1988
#endif
1989
1990
#ifdef GLES3_ENABLED
1991
drivers.push_back("opengl3");
1992
drivers.push_back("opengl3_es");
1993
#endif
1994
drivers.push_back("dummy");
1995
1996
return drivers;
1997
}
1998
1999
DisplayServer *DisplayServerWayland::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
2000
DisplayServer *ds = memnew(DisplayServerWayland(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, p_context, p_parent_window, r_error));
2001
if (r_error != OK) {
2002
ERR_PRINT("Can't create the Wayland display server.");
2003
memdelete(ds);
2004
2005
return nullptr;
2006
}
2007
return ds;
2008
}
2009
2010
DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Context p_context, int64_t p_parent_window, Error &r_error) {
2011
#if defined(GLES3_ENABLED) || defined(DBUS_ENABLED)
2012
#ifdef SOWRAP_ENABLED
2013
#ifdef DEBUG_ENABLED
2014
int dylibloader_verbose = 1;
2015
#else
2016
int dylibloader_verbose = 0;
2017
#endif // DEBUG_ENABLED
2018
#endif // SOWRAP_ENABLED
2019
#endif // defined(GLES3_ENABLED) || defined(DBUS_ENABLED)
2020
2021
r_error = ERR_UNAVAILABLE;
2022
context = p_context;
2023
2024
String current_desk = OS::get_singleton()->get_environment("XDG_CURRENT_DESKTOP").to_lower();
2025
String session_desk = OS::get_singleton()->get_environment("XDG_SESSION_DESKTOP").to_lower();
2026
swap_cancel_ok = (current_desk.contains("kde") || session_desk.contains("kde") || current_desk.contains("lxqt") || session_desk.contains("lxqt"));
2027
2028
Error thread_err = wayland_thread.init();
2029
2030
if (thread_err != OK) {
2031
r_error = thread_err;
2032
ERR_FAIL_MSG("Could not initialize the Wayland thread.");
2033
}
2034
2035
// Input.
2036
Input::get_singleton()->set_event_dispatch_function(dispatch_input_events);
2037
2038
native_menu = memnew(NativeMenu);
2039
2040
#ifdef SPEECHD_ENABLED
2041
// Init TTS
2042
bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
2043
if (tts_enabled) {
2044
initialize_tts();
2045
}
2046
#endif
2047
2048
#ifdef ACCESSKIT_ENABLED
2049
if (accessibility_get_mode() != DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED) {
2050
accessibility_driver = memnew(AccessibilityDriverAccessKit);
2051
if (accessibility_driver->init() != OK) {
2052
memdelete(accessibility_driver);
2053
accessibility_driver = nullptr;
2054
}
2055
}
2056
#endif
2057
2058
rendering_driver = p_rendering_driver;
2059
2060
bool driver_found = false;
2061
String executable_name = OS::get_singleton()->get_executable_path().get_file();
2062
2063
if (rendering_driver == "dummy") {
2064
RasterizerDummy::make_current();
2065
driver_found = true;
2066
}
2067
2068
#ifdef RD_ENABLED
2069
#ifdef VULKAN_ENABLED
2070
if (rendering_driver == "vulkan") {
2071
rendering_context = memnew(RenderingContextDriverVulkanWayland);
2072
}
2073
#endif // VULKAN_ENABLED
2074
2075
if (rendering_context) {
2076
if (rendering_context->initialize() != OK) {
2077
memdelete(rendering_context);
2078
rendering_context = nullptr;
2079
#if defined(GLES3_ENABLED)
2080
bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");
2081
if (fallback_to_opengl3 && rendering_driver != "opengl3") {
2082
WARN_PRINT("Your video card drivers seem not to support the required Vulkan version, switching to OpenGL 3.");
2083
rendering_driver = "opengl3";
2084
OS::get_singleton()->set_current_rendering_method("gl_compatibility", OS::RENDERING_SOURCE_FALLBACK);
2085
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver, OS::RENDERING_SOURCE_FALLBACK);
2086
} else
2087
#endif // GLES3_ENABLED
2088
{
2089
r_error = ERR_CANT_CREATE;
2090
2091
if (p_rendering_driver == "vulkan") {
2092
OS::get_singleton()->alert(
2093
vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
2094
"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
2095
"You can enable the OpenGL 3 driver by starting the engine from the\n"
2096
"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"
2097
"If you recently updated your video card drivers, try rebooting.",
2098
executable_name),
2099
"Unable to initialize Vulkan video driver");
2100
}
2101
2102
ERR_FAIL_MSG(vformat("Could not initialize %s", rendering_driver));
2103
}
2104
}
2105
2106
driver_found = true;
2107
}
2108
#endif // RD_ENABLED
2109
2110
#ifdef GLES3_ENABLED
2111
if (rendering_driver == "opengl3" || rendering_driver == "opengl3_es") {
2112
#ifdef SOWRAP_ENABLED
2113
if (initialize_wayland_egl(dylibloader_verbose) != 0) {
2114
WARN_PRINT("Can't load the Wayland EGL library.");
2115
return;
2116
}
2117
#endif // SOWRAP_ENABLED
2118
2119
if (getenv("DRI_PRIME") == nullptr) {
2120
int prime_idx = -1;
2121
2122
if (getenv("PRIMUS_DISPLAY") ||
2123
getenv("PRIMUS_libGLd") ||
2124
getenv("PRIMUS_libGLa") ||
2125
getenv("PRIMUS_libGL") ||
2126
getenv("PRIMUS_LOAD_GLOBAL") ||
2127
getenv("BUMBLEBEE_SOCKET") ||
2128
getenv("__NV_PRIME_RENDER_OFFLOAD")) {
2129
print_verbose("Optirun/primusrun detected. Skipping GPU detection");
2130
prime_idx = 0;
2131
}
2132
2133
// Some tools use fake libGL libraries and have them override the real one using
2134
// LD_LIBRARY_PATH, so we skip them. *But* Steam also sets LD_LIBRARY_PATH for its
2135
// runtime and includes system `/lib` and `/lib64`... so ignore Steam.
2136
if (prime_idx == -1 && getenv("LD_LIBRARY_PATH") && !getenv("STEAM_RUNTIME_LIBRARY_PATH")) {
2137
String ld_library_path(getenv("LD_LIBRARY_PATH"));
2138
Vector<String> libraries = ld_library_path.split(":");
2139
2140
for (int i = 0; i < libraries.size(); ++i) {
2141
if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||
2142
FileAccess::exists(libraries[i] + "/libGL.so")) {
2143
print_verbose("Custom libGL override detected. Skipping GPU detection");
2144
prime_idx = 0;
2145
}
2146
}
2147
}
2148
2149
if (prime_idx == -1) {
2150
print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
2151
prime_idx = DetectPrimeEGL::detect_prime(EGL_PLATFORM_WAYLAND_KHR);
2152
}
2153
2154
if (prime_idx) {
2155
print_line(vformat("Found discrete GPU, setting DRI_PRIME=%d to use it.", prime_idx));
2156
print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");
2157
setenv("DRI_PRIME", itos(prime_idx).utf8().ptr(), 1);
2158
}
2159
}
2160
2161
if (rendering_driver == "opengl3") {
2162
egl_manager = memnew(EGLManagerWayland);
2163
2164
if (egl_manager->initialize(wayland_thread.get_wl_display()) != OK || egl_manager->open_display(wayland_thread.get_wl_display()) != OK) {
2165
memdelete(egl_manager);
2166
egl_manager = nullptr;
2167
2168
bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_gles");
2169
if (fallback) {
2170
WARN_PRINT("Your video card drivers seem not to support the required OpenGL version, switching to OpenGLES.");
2171
rendering_driver = "opengl3_es";
2172
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver, OS::RENDERING_SOURCE_FALLBACK);
2173
} else {
2174
r_error = ERR_UNAVAILABLE;
2175
2176
OS::get_singleton()->alert(
2177
vformat("Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
2178
"If possible, consider updating your video card drivers or using the Vulkan driver.\n\n"
2179
"You can enable the Vulkan driver by starting the engine from the\n"
2180
"command line with the command:\n\n \"%s\" --rendering-driver vulkan\n\n"
2181
"If you recently updated your video card drivers, try rebooting.",
2182
executable_name),
2183
"Unable to initialize OpenGL video driver");
2184
2185
ERR_FAIL_MSG("Could not initialize OpenGL.");
2186
}
2187
} else {
2188
RasterizerGLES3::make_current(true);
2189
driver_found = true;
2190
}
2191
}
2192
2193
if (rendering_driver == "opengl3_es") {
2194
egl_manager = memnew(EGLManagerWaylandGLES);
2195
2196
if (egl_manager->initialize(wayland_thread.get_wl_display()) != OK || egl_manager->open_display(wayland_thread.get_wl_display()) != OK) {
2197
memdelete(egl_manager);
2198
egl_manager = nullptr;
2199
r_error = ERR_CANT_CREATE;
2200
2201
OS::get_singleton()->alert(
2202
vformat("Your video card drivers seem not to support the required OpenGL ES 3.0 version.\n\n"
2203
"If possible, consider updating your video card drivers or using the Vulkan driver.\n\n"
2204
"You can enable the Vulkan driver by starting the engine from the\n"
2205
"command line with the command:\n\n \"%s\" --rendering-driver vulkan\n\n"
2206
"If you recently updated your video card drivers, try rebooting.",
2207
executable_name),
2208
"Unable to initialize OpenGL ES video driver");
2209
2210
ERR_FAIL_MSG("Could not initialize OpenGL ES.");
2211
}
2212
2213
RasterizerGLES3::make_current(false);
2214
driver_found = true;
2215
}
2216
}
2217
#endif // GLES3_ENABLED
2218
2219
if (!driver_found) {
2220
r_error = ERR_UNAVAILABLE;
2221
ERR_FAIL_MSG("Video driver not found.");
2222
}
2223
2224
cursor_set_shape(CURSOR_BUSY);
2225
2226
WindowData &wd = windows[MAIN_WINDOW_ID];
2227
2228
wd.id = MAIN_WINDOW_ID;
2229
wd.mode = p_mode;
2230
wd.flags = p_flags;
2231
wd.vsync_mode = p_vsync_mode;
2232
wd.rect.size = p_resolution;
2233
wd.title = "Godot";
2234
2235
#ifdef ACCESSKIT_ENABLED
2236
if (accessibility_driver && !accessibility_driver->window_create(wd.id, nullptr)) {
2237
if (OS::get_singleton()->is_stdout_verbose()) {
2238
ERR_PRINT("Can't create an accessibility adapter for window, accessibility support disabled!");
2239
}
2240
memdelete(accessibility_driver);
2241
accessibility_driver = nullptr;
2242
}
2243
#endif
2244
2245
show_window(MAIN_WINDOW_ID);
2246
2247
#ifdef RD_ENABLED
2248
if (rendering_context) {
2249
rendering_device = memnew(RenderingDevice);
2250
if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {
2251
memdelete(rendering_device);
2252
rendering_device = nullptr;
2253
memdelete(rendering_context);
2254
rendering_context = nullptr;
2255
r_error = ERR_UNAVAILABLE;
2256
return;
2257
}
2258
rendering_device->screen_create(MAIN_WINDOW_ID);
2259
2260
RendererCompositorRD::make_current();
2261
}
2262
#endif // RD_ENABLED
2263
2264
#ifdef DBUS_ENABLED
2265
bool dbus_ok = true;
2266
#ifdef SOWRAP_ENABLED
2267
if (initialize_dbus(dylibloader_verbose) != 0) {
2268
print_verbose("Failed to load DBus library!");
2269
dbus_ok = false;
2270
}
2271
#endif
2272
if (dbus_ok) {
2273
bool ver_ok = false;
2274
int version_major = 0;
2275
int version_minor = 0;
2276
int version_rev = 0;
2277
dbus_get_version(&version_major, &version_minor, &version_rev);
2278
ver_ok = (version_major == 1 && version_minor >= 10) || (version_major > 1); // 1.10.0
2279
print_verbose(vformat("DBus %d.%d.%d detected.", version_major, version_minor, version_rev));
2280
if (!ver_ok) {
2281
print_verbose("Unsupported DBus library version!");
2282
dbus_ok = false;
2283
}
2284
}
2285
if (dbus_ok) {
2286
screensaver = memnew(FreeDesktopScreenSaver);
2287
portal_desktop = memnew(FreeDesktopPortalDesktop);
2288
atspi_monitor = memnew(FreeDesktopAtSPIMonitor);
2289
}
2290
#endif // DBUS_ENABLED
2291
2292
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
2293
2294
r_error = OK;
2295
}
2296
2297
DisplayServerWayland::~DisplayServerWayland() {
2298
if (native_menu) {
2299
memdelete(native_menu);
2300
native_menu = nullptr;
2301
}
2302
2303
// Iterating on the window map while we delete stuff from it is a bit
2304
// uncomfortable, plus we can't even delete /all/ windows in an arbitrary order
2305
// (due to popups).
2306
List<WindowID> toplevels;
2307
2308
for (const KeyValue<WindowID, WindowData> &pair : windows) {
2309
WindowID id = pair.key;
2310
2311
if (!window_get_flag(WINDOW_FLAG_POPUP_WM_HINT, id)) {
2312
toplevels.push_back(id);
2313
#ifdef ACCESSKIT_ENABLED
2314
} else if (accessibility_driver) {
2315
accessibility_driver->window_destroy(id);
2316
#endif
2317
}
2318
}
2319
2320
for (WindowID &id : toplevels) {
2321
delete_sub_window(id);
2322
}
2323
windows.clear();
2324
2325
wayland_thread.destroy();
2326
2327
// Destroy all drivers.
2328
#ifdef RD_ENABLED
2329
if (rendering_device) {
2330
memdelete(rendering_device);
2331
}
2332
2333
if (rendering_context) {
2334
memdelete(rendering_context);
2335
}
2336
#endif
2337
2338
#ifdef SPEECHD_ENABLED
2339
if (tts) {
2340
memdelete(tts);
2341
}
2342
#endif
2343
2344
#ifdef ACCESSKIT_ENABLED
2345
if (accessibility_driver) {
2346
memdelete(accessibility_driver);
2347
}
2348
#endif
2349
2350
#ifdef DBUS_ENABLED
2351
if (portal_desktop) {
2352
memdelete(portal_desktop);
2353
}
2354
if (screensaver) {
2355
memdelete(screensaver);
2356
}
2357
if (atspi_monitor) {
2358
memdelete(atspi_monitor);
2359
}
2360
#endif
2361
}
2362
2363
void DisplayServerWayland::register_wayland_driver() {
2364
register_create_function("wayland", create_func, get_rendering_drivers_func);
2365
}
2366
2367
#endif //WAYLAND_ENABLED
2368
2369