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