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