Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/platform/linuxbsd/wayland/wayland_thread.cpp
22224 views
1
/**************************************************************************/
2
/* wayland_thread.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 "wayland_thread.h"
32
33
#include "core/config/engine.h"
34
35
#ifdef WAYLAND_ENABLED
36
37
#ifdef __FreeBSD__
38
#include <dev/evdev/input-event-codes.h>
39
#else
40
// Assume Linux.
41
#include <linux/input-event-codes.h>
42
#endif
43
44
// For the actual polling thread.
45
#include <poll.h>
46
47
// For shared memory buffer creation.
48
#include <fcntl.h>
49
#include <sys/mman.h>
50
#include <unistd.h>
51
52
// Fix the wl_array_for_each macro to work with C++. This is based on the
53
// original from `wayland-util.h` in the Wayland client library.
54
#undef wl_array_for_each
55
#define wl_array_for_each(pos, array) \
56
for (pos = (decltype(pos))(array)->data; (const char *)pos < ((const char *)(array)->data + (array)->size); (pos)++)
57
58
#define WAYLAND_THREAD_DEBUG_LOGS_ENABLED
59
#ifdef WAYLAND_THREAD_DEBUG_LOGS_ENABLED
60
#define DEBUG_LOG_WAYLAND_THREAD(...) print_verbose(__VA_ARGS__)
61
#else
62
#define DEBUG_LOG_WAYLAND_THREAD(...)
63
#endif
64
65
// Since we're never going to use this interface directly, it's not worth
66
// generating the whole deal.
67
#define FIFO_INTERFACE_NAME "wp_fifo_manager_v1"
68
69
// Read the content pointed by fd into a Vector<uint8_t>.
70
Vector<uint8_t> WaylandThread::_read_fd(int fd) {
71
// This is pretty much an arbitrary size.
72
uint32_t chunk_size = 2048;
73
74
LocalVector<uint8_t> data;
75
data.resize(chunk_size);
76
77
uint32_t bytes_read = 0;
78
79
while (true) {
80
ssize_t last_bytes_read = read(fd, data.ptr() + bytes_read, chunk_size);
81
if (last_bytes_read < 0) {
82
ERR_PRINT(vformat("Read error %d.", errno));
83
84
data.clear();
85
break;
86
}
87
88
if (last_bytes_read == 0) {
89
// We're done, we've reached the EOF.
90
DEBUG_LOG_WAYLAND_THREAD(vformat("Done reading %d bytes.", bytes_read));
91
92
close(fd);
93
94
data.resize(bytes_read);
95
break;
96
}
97
98
DEBUG_LOG_WAYLAND_THREAD(vformat("Read chunk of %d bytes.", last_bytes_read));
99
100
bytes_read += last_bytes_read;
101
102
// Increase the buffer size by one chunk in preparation of the next read.
103
data.resize(bytes_read + chunk_size);
104
}
105
106
return Vector<uint8_t>(data);
107
}
108
109
// Based on the wayland book's shared memory boilerplate (PD/CC0).
110
// See: https://wayland-book.com/surfaces/shared-memory.html
111
int WaylandThread::_allocate_shm_file(size_t size) {
112
int retries = 100;
113
114
do {
115
// Generate a random name.
116
char name[] = "/wl_shm-godot-XXXXXX";
117
for (long unsigned int i = sizeof(name) - 7; i < sizeof(name) - 1; i++) {
118
name[i] = Math::random('A', 'Z');
119
}
120
121
// Try to open a shared memory object with that name.
122
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
123
if (fd >= 0) {
124
// Success, unlink its name as we just need the file descriptor.
125
shm_unlink(name);
126
127
// Resize the file to the requested length.
128
int ret;
129
do {
130
ret = ftruncate(fd, size);
131
} while (ret < 0 && errno == EINTR);
132
133
if (ret < 0) {
134
close(fd);
135
return -1;
136
}
137
138
return fd;
139
}
140
141
retries--;
142
} while (retries > 0 && errno == EEXIST);
143
144
return -1;
145
}
146
147
// Return the content of a wl_data_offer.
148
Vector<uint8_t> WaylandThread::_wl_data_offer_read(struct wl_display *p_display, const char *p_mime, struct wl_data_offer *p_offer) {
149
if (!p_offer) {
150
return Vector<uint8_t>();
151
}
152
153
int fds[2];
154
if (pipe(fds) == 0) {
155
wl_data_offer_receive(p_offer, p_mime, fds[1]);
156
157
// Let the compositor know about the pipe.
158
// NOTE: It's important to just flush and not roundtrip here as we would risk
159
// running some cleanup event, like for example `wl_data_device::leave`. We're
160
// going to wait for the message anyways as the read will probably block if
161
// the compositor doesn't read from the other end of the pipe.
162
wl_display_flush(p_display);
163
164
// Close the write end of the pipe, which we don't need and would otherwise
165
// just stall our next `read`s.
166
close(fds[1]);
167
168
return _read_fd(fds[0]);
169
}
170
171
return Vector<uint8_t>();
172
}
173
174
// Read the content of a wp_primary_selection_offer.
175
Vector<uint8_t> WaylandThread::_wp_primary_selection_offer_read(struct wl_display *p_display, const char *p_mime, struct zwp_primary_selection_offer_v1 *p_offer) {
176
if (!p_offer) {
177
return Vector<uint8_t>();
178
}
179
180
int fds[2];
181
if (pipe(fds) == 0) {
182
zwp_primary_selection_offer_v1_receive(p_offer, p_mime, fds[1]);
183
184
// NOTE: It's important to just flush and not roundtrip here as we would risk
185
// running some cleanup event, like for example `wl_data_device::leave`. We're
186
// going to wait for the message anyways as the read will probably block if
187
// the compositor doesn't read from the other end of the pipe.
188
wl_display_flush(p_display);
189
190
// Close the write end of the pipe, which we don't need and would otherwise
191
// just stall our next `read`s.
192
close(fds[1]);
193
194
return _read_fd(fds[0]);
195
}
196
197
return Vector<uint8_t>();
198
}
199
200
Ref<InputEventKey> WaylandThread::_seat_state_get_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed) {
201
Ref<InputEventKey> event;
202
203
ERR_FAIL_NULL_V(p_ss, event);
204
205
Key shifted_key = KeyMappingXKB::get_keycode(xkb_state_key_get_one_sym(p_ss->xkb_state, p_keycode));
206
207
Key plain_key = Key::NONE;
208
// NOTE: xkbcommon's API really encourages to apply the modifier state but we
209
// only want a "plain" symbol so that we can convert it into a godot keycode.
210
const xkb_keysym_t *syms = nullptr;
211
int num_sys = xkb_keymap_key_get_syms_by_level(p_ss->xkb_keymap, p_keycode, p_ss->current_layout_index, 0, &syms);
212
if (num_sys > 0 && syms) {
213
plain_key = KeyMappingXKB::get_keycode(syms[0]);
214
}
215
216
Key physical_keycode = KeyMappingXKB::get_scancode(p_keycode);
217
KeyLocation key_location = KeyMappingXKB::get_location(p_keycode);
218
uint32_t unicode = xkb_state_key_get_utf32(p_ss->xkb_state, p_keycode);
219
220
Key keycode = Key::NONE;
221
222
if ((shifted_key & Key::SPECIAL) != Key::NONE || (plain_key & Key::SPECIAL) != Key::NONE) {
223
keycode = shifted_key;
224
}
225
226
if (keycode == Key::NONE) {
227
keycode = plain_key;
228
}
229
230
if (keycode == Key::NONE) {
231
keycode = physical_keycode;
232
}
233
234
if (keycode >= Key::A + 32 && keycode <= Key::Z + 32) {
235
keycode -= 'a' - 'A';
236
}
237
238
if (physical_keycode == Key::NONE && keycode == Key::NONE && unicode == 0) {
239
return event;
240
}
241
242
event.instantiate();
243
244
event->set_window_id(p_ss->focused_id);
245
246
// Set all pressed modifiers.
247
event->set_shift_pressed(p_ss->shift_pressed);
248
event->set_ctrl_pressed(p_ss->ctrl_pressed);
249
event->set_alt_pressed(p_ss->alt_pressed);
250
event->set_meta_pressed(p_ss->meta_pressed);
251
252
event->set_pressed(p_pressed);
253
event->set_keycode(keycode);
254
event->set_physical_keycode(physical_keycode);
255
event->set_location(key_location);
256
257
if (unicode != 0) {
258
event->set_key_label(fix_key_label(unicode, keycode));
259
} else {
260
event->set_key_label(keycode);
261
}
262
263
if (p_pressed) {
264
event->set_unicode(fix_unicode(unicode));
265
}
266
267
// Taken from DisplayServerX11.
268
if (event->get_keycode() == Key::BACKTAB) {
269
// Make it consistent across platforms.
270
event->set_keycode(Key::TAB);
271
event->set_physical_keycode(Key::TAB);
272
event->set_shift_pressed(true);
273
}
274
275
return event;
276
}
277
278
// NOTE: Due to the nature of the way keys are encoded, there's an ambiguity
279
// regarding "special" keys. In other words: there's no reliable way of
280
// switching between a special key and a character key if not marking a
281
// different Godot keycode, even if we're actually using the same XKB raw
282
// keycode. This means that, during this switch, the old key will get "stuck",
283
// as it will never receive a release event. This method returns the necessary
284
// event to fix this if needed.
285
Ref<InputEventKey> WaylandThread::_seat_state_get_unstuck_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed, Key p_key) {
286
Ref<InputEventKey> event;
287
288
if (p_pressed) {
289
Key *old_key = p_ss->pressed_keycodes.getptr(p_keycode);
290
if (old_key != nullptr && *old_key != p_key) {
291
print_verbose(vformat("%s and %s have same keycode. Generating release event for %s", keycode_get_string(*old_key), keycode_get_string(p_key), keycode_get_string(*old_key)));
292
event = _seat_state_get_key_event(p_ss, p_keycode, false);
293
if (event.is_valid()) {
294
event->set_keycode(*old_key);
295
}
296
}
297
p_ss->pressed_keycodes[p_keycode] = p_key;
298
} else {
299
p_ss->pressed_keycodes.erase(p_keycode);
300
}
301
302
return event;
303
}
304
305
void WaylandThread::_seat_state_handle_xkb_keycode(SeatState *p_ss, xkb_keycode_t p_xkb_keycode, bool p_pressed, bool p_echo) {
306
ERR_FAIL_NULL(p_ss);
307
308
WaylandThread *wayland_thread = p_ss->wayland_thread;
309
ERR_FAIL_NULL(wayland_thread);
310
311
Key last_key = Key::NONE;
312
xkb_compose_status compose_status = xkb_compose_state_get_status(p_ss->xkb_compose_state);
313
314
if (p_pressed) {
315
xkb_keysym_t keysym = xkb_state_key_get_one_sym(p_ss->xkb_state, p_xkb_keycode);
316
xkb_compose_feed_result compose_result = xkb_compose_state_feed(p_ss->xkb_compose_state, keysym);
317
compose_status = xkb_compose_state_get_status(p_ss->xkb_compose_state);
318
319
if (compose_result == XKB_COMPOSE_FEED_ACCEPTED && compose_status == XKB_COMPOSE_COMPOSED) {
320
// We need to generate multiple key events to report the composed result, One
321
// per character.
322
char str_xkb[256] = {};
323
int str_xkb_size = xkb_compose_state_get_utf8(p_ss->xkb_compose_state, str_xkb, 255);
324
325
String decoded_str = String::utf8(str_xkb, str_xkb_size);
326
for (int i = 0; i < decoded_str.length(); ++i) {
327
Ref<InputEventKey> k = _seat_state_get_key_event(p_ss, p_xkb_keycode, p_pressed);
328
if (k.is_null()) {
329
continue;
330
}
331
332
k->set_unicode(decoded_str[i]);
333
k->set_echo(p_echo);
334
335
Ref<InputEventMessage> msg;
336
msg.instantiate();
337
msg->event = k;
338
wayland_thread->push_message(msg);
339
340
last_key = k->get_keycode();
341
}
342
}
343
}
344
345
if (last_key == Key::NONE && compose_status == XKB_COMPOSE_NOTHING) {
346
// If we continued with other compose status (e.g. XKB_COMPOSE_COMPOSING) we
347
// would get the composing keys _and_ the result.
348
Ref<InputEventKey> k = _seat_state_get_key_event(p_ss, p_xkb_keycode, p_pressed);
349
if (k.is_valid()) {
350
k->set_echo(p_echo);
351
352
Ref<InputEventMessage> msg;
353
msg.instantiate();
354
msg->event = k;
355
wayland_thread->push_message(msg);
356
357
last_key = k->get_keycode();
358
}
359
}
360
361
if (last_key != Key::NONE) {
362
Ref<InputEventKey> uk = _seat_state_get_unstuck_key_event(p_ss, p_xkb_keycode, p_pressed, last_key);
363
if (uk.is_valid()) {
364
Ref<InputEventMessage> u_msg;
365
u_msg.instantiate();
366
u_msg->event = uk;
367
wayland_thread->push_message(u_msg);
368
}
369
}
370
}
371
372
void WaylandThread::_set_current_seat(struct wl_seat *p_seat) {
373
if (p_seat == wl_seat_current) {
374
return;
375
}
376
377
SeatState *old_state = wl_seat_get_seat_state(wl_seat_current);
378
379
if (old_state) {
380
seat_state_unlock_pointer(old_state);
381
}
382
383
SeatState *new_state = wl_seat_get_seat_state(p_seat);
384
seat_state_unlock_pointer(new_state);
385
386
wl_seat_current = p_seat;
387
pointer_set_constraint(pointer_constraint);
388
}
389
390
// Returns whether it loaded the theme or not.
391
bool WaylandThread::_load_cursor_theme(int p_cursor_size) {
392
if (wl_cursor_theme) {
393
wl_cursor_theme_destroy(wl_cursor_theme);
394
wl_cursor_theme = nullptr;
395
}
396
397
if (cursor_theme_name.is_empty()) {
398
cursor_theme_name = "default";
399
}
400
401
print_verbose(vformat("Loading cursor theme \"%s\" size %d.", cursor_theme_name, p_cursor_size));
402
403
wl_cursor_theme = wl_cursor_theme_load(cursor_theme_name.utf8().get_data(), p_cursor_size, registry.wl_shm);
404
405
ERR_FAIL_NULL_V_MSG(wl_cursor_theme, false, "Can't load any cursor theme.");
406
407
static const char *cursor_names[] = {
408
"left_ptr",
409
"xterm",
410
"hand2",
411
"cross",
412
"watch",
413
"left_ptr_watch",
414
"fleur",
415
"dnd-move",
416
"crossed_circle",
417
"v_double_arrow",
418
"h_double_arrow",
419
"size_bdiag",
420
"size_fdiag",
421
"move",
422
"row_resize",
423
"col_resize",
424
"question_arrow"
425
};
426
427
static const char *cursor_names_fallback[] = {
428
nullptr,
429
nullptr,
430
"pointer",
431
"cross",
432
"wait",
433
"progress",
434
"grabbing",
435
"hand1",
436
"forbidden",
437
"ns-resize",
438
"ew-resize",
439
"fd_double_arrow",
440
"bd_double_arrow",
441
"fleur",
442
"sb_v_double_arrow",
443
"sb_h_double_arrow",
444
"help"
445
};
446
447
for (int i = 0; i < DisplayServer::CURSOR_MAX; i++) {
448
struct wl_cursor *cursor = wl_cursor_theme_get_cursor(wl_cursor_theme, cursor_names[i]);
449
450
if (!cursor && cursor_names_fallback[i]) {
451
cursor = wl_cursor_theme_get_cursor(wl_cursor_theme, cursor_names_fallback[i]);
452
}
453
454
if (cursor && cursor->image_count > 0) {
455
wl_cursors[i] = cursor;
456
} else {
457
wl_cursors[i] = nullptr;
458
print_verbose("Failed loading cursor: " + String(cursor_names[i]));
459
}
460
}
461
462
return true;
463
}
464
465
void WaylandThread::_update_scale(int p_scale) {
466
if (p_scale <= cursor_scale) {
467
return;
468
}
469
470
print_verbose(vformat("Bumping cursor scale to %d", p_scale));
471
472
// There's some display that's bigger than the cache, let's update it.
473
cursor_scale = p_scale;
474
475
if (wl_cursor_theme == nullptr) {
476
// Ugh. Either we're still initializing (this must've been called from the
477
// first roundtrips) or we had some error while doing so. We'll trust that it
478
// will be updated for us if needed.
479
return;
480
}
481
482
int cursor_size = unscaled_cursor_size * p_scale;
483
484
if (_load_cursor_theme(cursor_size)) {
485
for (struct wl_seat *wl_seat : registry.wl_seats) {
486
SeatState *ss = wl_seat_get_seat_state(wl_seat);
487
ERR_FAIL_NULL(ss);
488
489
seat_state_update_cursor(ss);
490
}
491
}
492
}
493
494
void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version) {
495
RegistryState *registry = (RegistryState *)data;
496
ERR_FAIL_NULL(registry);
497
498
if (strcmp(interface, wl_shm_interface.name) == 0) {
499
registry->wl_shm = (struct wl_shm *)wl_registry_bind(wl_registry, name, &wl_shm_interface, 1);
500
registry->wl_shm_name = name;
501
return;
502
}
503
504
// NOTE: Deprecated.
505
if (strcmp(interface, zxdg_exporter_v1_interface.name) == 0) {
506
registry->xdg_exporter_v1 = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1);
507
registry->xdg_exporter_v1_name = name;
508
return;
509
}
510
511
if (strcmp(interface, zxdg_exporter_v2_interface.name) == 0) {
512
registry->xdg_exporter_v2 = (struct zxdg_exporter_v2 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v2_interface, 1);
513
registry->xdg_exporter_v2_name = name;
514
return;
515
}
516
517
if (strcmp(interface, wl_compositor_interface.name) == 0) {
518
registry->wl_compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, CLAMP((int)version, 1, 6));
519
registry->wl_compositor_name = name;
520
return;
521
}
522
523
if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
524
registry->wl_data_device_manager = (struct wl_data_device_manager *)wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, CLAMP((int)version, 1, 3));
525
registry->wl_data_device_manager_name = name;
526
527
// This global creates some seat data. Let's do that for the ones already available.
528
for (struct wl_seat *wl_seat : registry->wl_seats) {
529
SeatState *ss = wl_seat_get_seat_state(wl_seat);
530
ERR_FAIL_NULL(ss);
531
532
if (ss->wl_data_device == nullptr) {
533
ss->wl_data_device = wl_data_device_manager_get_data_device(registry->wl_data_device_manager, wl_seat);
534
wl_data_device_add_listener(ss->wl_data_device, &wl_data_device_listener, ss);
535
}
536
}
537
return;
538
}
539
540
if (strcmp(interface, wl_output_interface.name) == 0) {
541
struct wl_output *wl_output = (struct wl_output *)wl_registry_bind(wl_registry, name, &wl_output_interface, CLAMP((int)version, 1, 4));
542
wl_proxy_tag_godot((struct wl_proxy *)wl_output);
543
544
registry->wl_outputs.push_back(wl_output);
545
546
ScreenState *ss = memnew(ScreenState);
547
ss->wl_output_name = name;
548
ss->wayland_thread = registry->wayland_thread;
549
550
wl_proxy_tag_godot((struct wl_proxy *)wl_output);
551
wl_output_add_listener(wl_output, &wl_output_listener, ss);
552
return;
553
}
554
555
if (strcmp(interface, wl_seat_interface.name) == 0) {
556
struct wl_seat *wl_seat = (struct wl_seat *)wl_registry_bind(wl_registry, name, &wl_seat_interface, CLAMP((int)version, 1, 9));
557
wl_proxy_tag_godot((struct wl_proxy *)wl_seat);
558
559
SeatState *ss = memnew(SeatState);
560
ss->wl_seat = wl_seat;
561
ss->wl_seat_name = name;
562
563
ss->registry = registry;
564
ss->wayland_thread = registry->wayland_thread;
565
566
// Some extra stuff depends on other globals. We'll initialize them if the
567
// globals are already there, otherwise we'll have to do that once and if they
568
// get announced.
569
//
570
// NOTE: Don't forget to also bind/destroy with the respective global.
571
if (!ss->wl_data_device && registry->wl_data_device_manager) {
572
// Clipboard & DnD.
573
ss->wl_data_device = wl_data_device_manager_get_data_device(registry->wl_data_device_manager, wl_seat);
574
wl_data_device_add_listener(ss->wl_data_device, &wl_data_device_listener, ss);
575
}
576
577
if (!ss->wp_primary_selection_device && registry->wp_primary_selection_device_manager) {
578
// Primary selection.
579
ss->wp_primary_selection_device = zwp_primary_selection_device_manager_v1_get_device(registry->wp_primary_selection_device_manager, wl_seat);
580
zwp_primary_selection_device_v1_add_listener(ss->wp_primary_selection_device, &wp_primary_selection_device_listener, ss);
581
}
582
583
if (!ss->wp_tablet_seat && registry->wp_tablet_manager) {
584
// Tablet.
585
ss->wp_tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(registry->wp_tablet_manager, wl_seat);
586
zwp_tablet_seat_v2_add_listener(ss->wp_tablet_seat, &wp_tablet_seat_listener, ss);
587
}
588
589
if (!ss->wp_text_input && registry->wp_text_input_manager) {
590
// IME.
591
ss->wp_text_input = zwp_text_input_manager_v3_get_text_input(registry->wp_text_input_manager, wl_seat);
592
zwp_text_input_v3_add_listener(ss->wp_text_input, &wp_text_input_listener, ss);
593
}
594
595
registry->wl_seats.push_back(wl_seat);
596
597
wl_seat_add_listener(wl_seat, &wl_seat_listener, ss);
598
599
if (registry->wayland_thread->wl_seat_current == nullptr) {
600
registry->wayland_thread->_set_current_seat(wl_seat);
601
}
602
603
return;
604
}
605
606
if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
607
registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, CLAMP((int)version, 1, 6));
608
registry->xdg_wm_base_name = name;
609
610
xdg_wm_base_add_listener(registry->xdg_wm_base, &xdg_wm_base_listener, nullptr);
611
return;
612
}
613
614
if (strcmp(interface, wp_viewporter_interface.name) == 0) {
615
registry->wp_viewporter = (struct wp_viewporter *)wl_registry_bind(wl_registry, name, &wp_viewporter_interface, 1);
616
registry->wp_viewporter_name = name;
617
}
618
619
if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
620
registry->wp_cursor_shape_manager = (struct wp_cursor_shape_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_cursor_shape_manager_v1_interface, 1);
621
registry->wp_cursor_shape_manager_name = name;
622
return;
623
}
624
625
if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) {
626
registry->wp_fractional_scale_manager = (struct wp_fractional_scale_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, 1);
627
registry->wp_fractional_scale_manager_name = name;
628
629
// NOTE: We're not mapping the fractional scale object here because this is
630
// supposed to be a "startup global". If for some reason this isn't true (who
631
// knows), add a conditional branch for creating the add-on object.
632
}
633
634
if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
635
registry->xdg_decoration_manager = (struct zxdg_decoration_manager_v1 *)wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1);
636
registry->xdg_decoration_manager_name = name;
637
return;
638
}
639
640
if (strcmp(interface, xdg_system_bell_v1_interface.name) == 0) {
641
registry->xdg_system_bell = (struct xdg_system_bell_v1 *)wl_registry_bind(wl_registry, name, &xdg_system_bell_v1_interface, 1);
642
registry->xdg_system_bell_name = name;
643
return;
644
}
645
646
if (strcmp(interface, xdg_toplevel_icon_manager_v1_interface.name) == 0) {
647
registry->xdg_toplevel_icon_manager = (struct xdg_toplevel_icon_manager_v1 *)wl_registry_bind(wl_registry, name, &xdg_toplevel_icon_manager_v1_interface, 1);
648
registry->xdg_toplevel_icon_manager_name = name;
649
return;
650
}
651
652
if (strcmp(interface, xdg_activation_v1_interface.name) == 0) {
653
registry->xdg_activation = (struct xdg_activation_v1 *)wl_registry_bind(wl_registry, name, &xdg_activation_v1_interface, 1);
654
registry->xdg_activation_name = name;
655
return;
656
}
657
658
if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
659
registry->wp_primary_selection_device_manager = (struct zwp_primary_selection_device_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
660
661
// This global creates some seat data. Let's do that for the ones already available.
662
for (struct wl_seat *wl_seat : registry->wl_seats) {
663
SeatState *ss = wl_seat_get_seat_state(wl_seat);
664
ERR_FAIL_NULL(ss);
665
666
if (!ss->wp_primary_selection_device && registry->wp_primary_selection_device_manager) {
667
ss->wp_primary_selection_device = zwp_primary_selection_device_manager_v1_get_device(registry->wp_primary_selection_device_manager, wl_seat);
668
zwp_primary_selection_device_v1_add_listener(ss->wp_primary_selection_device, &wp_primary_selection_device_listener, ss);
669
}
670
}
671
}
672
673
if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
674
registry->wp_relative_pointer_manager = (struct zwp_relative_pointer_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_relative_pointer_manager_v1_interface, 1);
675
registry->wp_relative_pointer_manager_name = name;
676
return;
677
}
678
679
if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) {
680
registry->wp_pointer_constraints = (struct zwp_pointer_constraints_v1 *)wl_registry_bind(wl_registry, name, &zwp_pointer_constraints_v1_interface, 1);
681
registry->wp_pointer_constraints_name = name;
682
return;
683
}
684
685
if (strcmp(interface, zwp_pointer_gestures_v1_interface.name) == 0) {
686
registry->wp_pointer_gestures = (struct zwp_pointer_gestures_v1 *)wl_registry_bind(wl_registry, name, &zwp_pointer_gestures_v1_interface, 1);
687
registry->wp_pointer_gestures_name = name;
688
return;
689
}
690
691
if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) {
692
registry->wp_idle_inhibit_manager = (struct zwp_idle_inhibit_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_idle_inhibit_manager_v1_interface, 1);
693
registry->wp_idle_inhibit_manager_name = name;
694
return;
695
}
696
697
if (strcmp(interface, zwp_tablet_manager_v2_interface.name) == 0) {
698
registry->wp_tablet_manager = (struct zwp_tablet_manager_v2 *)wl_registry_bind(wl_registry, name, &zwp_tablet_manager_v2_interface, 1);
699
registry->wp_tablet_manager_name = name;
700
701
// This global creates some seat data. Let's do that for the ones already available.
702
for (struct wl_seat *wl_seat : registry->wl_seats) {
703
SeatState *ss = wl_seat_get_seat_state(wl_seat);
704
ERR_FAIL_NULL(ss);
705
706
ss->wp_tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(registry->wp_tablet_manager, wl_seat);
707
zwp_tablet_seat_v2_add_listener(ss->wp_tablet_seat, &wp_tablet_seat_listener, ss);
708
}
709
710
return;
711
}
712
713
if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
714
registry->wp_text_input_manager = (struct zwp_text_input_manager_v3 *)wl_registry_bind(wl_registry, name, &zwp_text_input_manager_v3_interface, 1);
715
registry->wp_text_input_manager_name = name;
716
717
// This global creates some seat data. Let's do that for the ones already available.
718
for (struct wl_seat *wl_seat : registry->wl_seats) {
719
SeatState *ss = wl_seat_get_seat_state(wl_seat);
720
ERR_FAIL_NULL(ss);
721
722
ss->wp_text_input = zwp_text_input_manager_v3_get_text_input(registry->wp_text_input_manager, wl_seat);
723
zwp_text_input_v3_add_listener(ss->wp_text_input, &wp_text_input_listener, ss);
724
}
725
726
return;
727
}
728
729
if (strcmp(interface, wp_pointer_warp_v1_interface.name) == 0) {
730
registry->wp_pointer_warp = (struct wp_pointer_warp_v1 *)wl_registry_bind(wl_registry, name, &wp_pointer_warp_v1_interface, 1);
731
registry->wp_pointer_warp_name = name;
732
return;
733
}
734
735
if (strcmp(interface, FIFO_INTERFACE_NAME) == 0) {
736
registry->wp_fifo_manager_name = name;
737
}
738
739
if (strcmp(interface, godot_embedding_compositor_interface.name) == 0) {
740
registry->godot_embedding_compositor = (struct godot_embedding_compositor *)wl_registry_bind(wl_registry, name, &godot_embedding_compositor_interface, 1);
741
registry->godot_embedding_compositor_name = name;
742
743
godot_embedding_compositor_add_listener(registry->godot_embedding_compositor, &godot_embedding_compositor_listener, memnew(EmbeddingCompositorState));
744
}
745
}
746
747
void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name) {
748
RegistryState *registry = (RegistryState *)data;
749
ERR_FAIL_NULL(registry);
750
751
if (name == registry->wl_shm_name) {
752
if (registry->wl_shm) {
753
wl_shm_destroy(registry->wl_shm);
754
registry->wl_shm = nullptr;
755
}
756
757
registry->wl_shm_name = 0;
758
759
return;
760
}
761
762
// NOTE: Deprecated.
763
if (name == registry->xdg_exporter_v1_name) {
764
if (registry->xdg_exporter_v1) {
765
zxdg_exporter_v1_destroy(registry->xdg_exporter_v1);
766
registry->xdg_exporter_v1 = nullptr;
767
}
768
769
registry->xdg_exporter_v1_name = 0;
770
771
return;
772
}
773
774
if (name == registry->xdg_exporter_v2_name) {
775
if (registry->xdg_exporter_v2) {
776
zxdg_exporter_v2_destroy(registry->xdg_exporter_v2);
777
registry->xdg_exporter_v2 = nullptr;
778
}
779
780
registry->xdg_exporter_v2_name = 0;
781
782
return;
783
}
784
785
if (name == registry->wl_compositor_name) {
786
if (registry->wl_compositor) {
787
wl_compositor_destroy(registry->wl_compositor);
788
registry->wl_compositor = nullptr;
789
}
790
791
registry->wl_compositor_name = 0;
792
793
return;
794
}
795
796
if (name == registry->wl_data_device_manager_name) {
797
if (registry->wl_data_device_manager) {
798
wl_data_device_manager_destroy(registry->wl_data_device_manager);
799
registry->wl_data_device_manager = nullptr;
800
}
801
802
registry->wl_data_device_manager_name = 0;
803
804
// This global is used to create some seat data. Let's clean it.
805
for (struct wl_seat *wl_seat : registry->wl_seats) {
806
SeatState *ss = wl_seat_get_seat_state(wl_seat);
807
ERR_FAIL_NULL(ss);
808
809
if (ss->wl_data_device) {
810
wl_data_device_destroy(ss->wl_data_device);
811
ss->wl_data_device = nullptr;
812
}
813
814
ss->wl_data_device = nullptr;
815
}
816
817
return;
818
}
819
820
if (name == registry->xdg_wm_base_name) {
821
if (registry->xdg_wm_base) {
822
xdg_wm_base_destroy(registry->xdg_wm_base);
823
registry->xdg_wm_base = nullptr;
824
}
825
826
registry->xdg_wm_base_name = 0;
827
828
return;
829
}
830
831
if (name == registry->wp_viewporter_name) {
832
for (KeyValue<DisplayServer::WindowID, WindowState> &pair : registry->wayland_thread->windows) {
833
WindowState &ws = pair.value;
834
if (registry->wp_viewporter) {
835
wp_viewporter_destroy(registry->wp_viewporter);
836
registry->wp_viewporter = nullptr;
837
}
838
839
if (ws.wp_viewport) {
840
wp_viewport_destroy(ws.wp_viewport);
841
ws.wp_viewport = nullptr;
842
}
843
}
844
845
registry->wp_viewporter_name = 0;
846
847
return;
848
}
849
850
if (name == registry->wp_cursor_shape_manager_name) {
851
if (registry->wp_cursor_shape_manager) {
852
wp_cursor_shape_manager_v1_destroy(registry->wp_cursor_shape_manager);
853
registry->wp_cursor_shape_manager = nullptr;
854
}
855
856
registry->wp_cursor_shape_manager_name = 0;
857
858
for (struct wl_seat *wl_seat : registry->wl_seats) {
859
SeatState *ss = wl_seat_get_seat_state(wl_seat);
860
ERR_FAIL_NULL(ss);
861
862
if (ss->wp_cursor_shape_device) {
863
wp_cursor_shape_device_v1_destroy(ss->wp_cursor_shape_device);
864
ss->wp_cursor_shape_device = nullptr;
865
}
866
}
867
}
868
869
if (name == registry->wp_fractional_scale_manager_name) {
870
for (KeyValue<DisplayServer::WindowID, WindowState> &pair : registry->wayland_thread->windows) {
871
WindowState &ws = pair.value;
872
873
if (registry->wp_fractional_scale_manager) {
874
wp_fractional_scale_manager_v1_destroy(registry->wp_fractional_scale_manager);
875
registry->wp_fractional_scale_manager = nullptr;
876
}
877
878
if (ws.wp_fractional_scale) {
879
wp_fractional_scale_v1_destroy(ws.wp_fractional_scale);
880
ws.wp_fractional_scale = nullptr;
881
}
882
}
883
884
registry->wp_fractional_scale_manager_name = 0;
885
}
886
887
if (name == registry->xdg_decoration_manager_name) {
888
if (registry->xdg_decoration_manager) {
889
zxdg_decoration_manager_v1_destroy(registry->xdg_decoration_manager);
890
registry->xdg_decoration_manager = nullptr;
891
}
892
893
registry->xdg_decoration_manager_name = 0;
894
895
return;
896
}
897
898
if (name == registry->xdg_system_bell_name) {
899
if (registry->xdg_system_bell) {
900
xdg_system_bell_v1_destroy(registry->xdg_system_bell);
901
registry->xdg_system_bell = nullptr;
902
}
903
904
registry->xdg_system_bell_name = 0;
905
906
return;
907
}
908
909
if (name == registry->xdg_toplevel_icon_manager_name) {
910
if (registry->xdg_toplevel_icon_manager) {
911
xdg_toplevel_icon_manager_v1_destroy(registry->xdg_toplevel_icon_manager);
912
registry->xdg_toplevel_icon_manager = nullptr;
913
}
914
915
if (registry->wayland_thread->xdg_icon) {
916
xdg_toplevel_icon_v1_destroy(registry->wayland_thread->xdg_icon);
917
}
918
919
if (registry->wayland_thread->icon_buffer) {
920
wl_buffer_destroy(registry->wayland_thread->icon_buffer);
921
}
922
923
registry->xdg_toplevel_icon_manager_name = 0;
924
925
return;
926
}
927
928
if (name == registry->xdg_activation_name) {
929
if (registry->xdg_activation) {
930
xdg_activation_v1_destroy(registry->xdg_activation);
931
registry->xdg_activation = nullptr;
932
}
933
934
registry->xdg_activation_name = 0;
935
936
return;
937
}
938
939
if (name == registry->wp_primary_selection_device_manager_name) {
940
if (registry->wp_primary_selection_device_manager) {
941
zwp_primary_selection_device_manager_v1_destroy(registry->wp_primary_selection_device_manager);
942
registry->wp_primary_selection_device_manager = nullptr;
943
}
944
945
registry->wp_primary_selection_device_manager_name = 0;
946
947
// This global is used to create some seat data. Let's clean it.
948
for (struct wl_seat *wl_seat : registry->wl_seats) {
949
SeatState *ss = wl_seat_get_seat_state(wl_seat);
950
ERR_FAIL_NULL(ss);
951
952
if (ss->wp_primary_selection_device) {
953
zwp_primary_selection_device_v1_destroy(ss->wp_primary_selection_device);
954
ss->wp_primary_selection_device = nullptr;
955
}
956
957
if (ss->wp_primary_selection_source) {
958
zwp_primary_selection_source_v1_destroy(ss->wp_primary_selection_source);
959
ss->wp_primary_selection_source = nullptr;
960
}
961
962
if (ss->wp_primary_selection_offer) {
963
memfree(wp_primary_selection_offer_get_offer_state(ss->wp_primary_selection_offer));
964
zwp_primary_selection_offer_v1_destroy(ss->wp_primary_selection_offer);
965
ss->wp_primary_selection_offer = nullptr;
966
}
967
}
968
969
return;
970
}
971
972
if (name == registry->wp_relative_pointer_manager_name) {
973
if (registry->wp_relative_pointer_manager) {
974
zwp_relative_pointer_manager_v1_destroy(registry->wp_relative_pointer_manager);
975
registry->wp_relative_pointer_manager = nullptr;
976
}
977
978
registry->wp_relative_pointer_manager_name = 0;
979
980
// This global is used to create some seat data. Let's clean it.
981
for (struct wl_seat *wl_seat : registry->wl_seats) {
982
SeatState *ss = wl_seat_get_seat_state(wl_seat);
983
ERR_FAIL_NULL(ss);
984
985
if (ss->wp_relative_pointer) {
986
zwp_relative_pointer_v1_destroy(ss->wp_relative_pointer);
987
ss->wp_relative_pointer = nullptr;
988
}
989
}
990
991
return;
992
}
993
994
if (name == registry->wp_pointer_constraints_name) {
995
if (registry->wp_pointer_constraints) {
996
zwp_pointer_constraints_v1_destroy(registry->wp_pointer_constraints);
997
registry->wp_pointer_constraints = nullptr;
998
}
999
1000
registry->wp_pointer_constraints_name = 0;
1001
1002
// This global is used to create some seat data. Let's clean it.
1003
for (struct wl_seat *wl_seat : registry->wl_seats) {
1004
SeatState *ss = wl_seat_get_seat_state(wl_seat);
1005
ERR_FAIL_NULL(ss);
1006
1007
if (ss->wp_relative_pointer) {
1008
zwp_relative_pointer_v1_destroy(ss->wp_relative_pointer);
1009
ss->wp_relative_pointer = nullptr;
1010
}
1011
1012
if (ss->wp_locked_pointer) {
1013
zwp_locked_pointer_v1_destroy(ss->wp_locked_pointer);
1014
ss->wp_locked_pointer = nullptr;
1015
}
1016
1017
if (ss->wp_confined_pointer) {
1018
zwp_confined_pointer_v1_destroy(ss->wp_confined_pointer);
1019
ss->wp_confined_pointer = nullptr;
1020
}
1021
}
1022
1023
return;
1024
}
1025
1026
if (name == registry->wp_pointer_gestures_name) {
1027
if (registry->wp_pointer_gestures) {
1028
zwp_pointer_gestures_v1_destroy(registry->wp_pointer_gestures);
1029
}
1030
1031
registry->wp_pointer_gestures = nullptr;
1032
registry->wp_pointer_gestures_name = 0;
1033
1034
// This global is used to create some seat data. Let's clean it.
1035
for (struct wl_seat *wl_seat : registry->wl_seats) {
1036
SeatState *ss = wl_seat_get_seat_state(wl_seat);
1037
ERR_FAIL_NULL(ss);
1038
1039
if (ss->wp_pointer_gesture_pinch) {
1040
zwp_pointer_gesture_pinch_v1_destroy(ss->wp_pointer_gesture_pinch);
1041
ss->wp_pointer_gesture_pinch = nullptr;
1042
}
1043
}
1044
1045
return;
1046
}
1047
1048
if (name == registry->wp_idle_inhibit_manager_name) {
1049
if (registry->wp_idle_inhibit_manager) {
1050
zwp_idle_inhibit_manager_v1_destroy(registry->wp_idle_inhibit_manager);
1051
registry->wp_idle_inhibit_manager = nullptr;
1052
}
1053
1054
registry->wp_idle_inhibit_manager_name = 0;
1055
1056
return;
1057
}
1058
1059
if (name == registry->wp_tablet_manager_name) {
1060
if (registry->wp_tablet_manager) {
1061
zwp_tablet_manager_v2_destroy(registry->wp_tablet_manager);
1062
registry->wp_tablet_manager = nullptr;
1063
}
1064
1065
registry->wp_tablet_manager_name = 0;
1066
1067
// This global is used to create some seat data. Let's clean it.
1068
for (struct wl_seat *wl_seat : registry->wl_seats) {
1069
SeatState *ss = wl_seat_get_seat_state(wl_seat);
1070
ERR_FAIL_NULL(ss);
1071
1072
for (struct zwp_tablet_tool_v2 *tool : ss->tablet_tools) {
1073
TabletToolState *state = wp_tablet_tool_get_state(tool);
1074
if (state) {
1075
memdelete(state);
1076
}
1077
1078
zwp_tablet_tool_v2_destroy(tool);
1079
}
1080
1081
ss->tablet_tools.clear();
1082
}
1083
1084
return;
1085
}
1086
1087
if (name == registry->wp_text_input_manager_name) {
1088
if (registry->wp_text_input_manager) {
1089
zwp_text_input_manager_v3_destroy(registry->wp_text_input_manager);
1090
registry->wp_text_input_manager = nullptr;
1091
}
1092
1093
registry->wp_text_input_manager_name = 0;
1094
1095
for (struct wl_seat *wl_seat : registry->wl_seats) {
1096
SeatState *ss = wl_seat_get_seat_state(wl_seat);
1097
ERR_FAIL_NULL(ss);
1098
1099
zwp_text_input_v3_destroy(ss->wp_text_input);
1100
ss->wp_text_input = nullptr;
1101
}
1102
1103
return;
1104
}
1105
1106
if (name == registry->wp_pointer_warp_name) {
1107
if (registry->wp_pointer_warp) {
1108
wp_pointer_warp_v1_destroy(registry->wp_pointer_warp);
1109
registry->wp_pointer_warp = nullptr;
1110
}
1111
1112
registry->wp_pointer_warp_name = 0;
1113
1114
return;
1115
}
1116
1117
{
1118
// Iterate through all of the seats to find if any got removed.
1119
List<struct wl_seat *>::Element *E = registry->wl_seats.front();
1120
while (E) {
1121
struct wl_seat *wl_seat = E->get();
1122
List<struct wl_seat *>::Element *N = E->next();
1123
1124
SeatState *ss = wl_seat_get_seat_state(wl_seat);
1125
ERR_FAIL_NULL(ss);
1126
1127
if (ss->wl_seat_name == name) {
1128
if (wl_seat) {
1129
wl_seat_destroy(wl_seat);
1130
}
1131
1132
if (ss->wl_data_device) {
1133
wl_data_device_destroy(ss->wl_data_device);
1134
}
1135
1136
if (ss->wp_tablet_seat) {
1137
zwp_tablet_seat_v2_destroy(ss->wp_tablet_seat);
1138
1139
for (struct zwp_tablet_tool_v2 *tool : ss->tablet_tools) {
1140
TabletToolState *state = wp_tablet_tool_get_state(tool);
1141
if (state) {
1142
memdelete(state);
1143
}
1144
1145
zwp_tablet_tool_v2_destroy(tool);
1146
}
1147
}
1148
1149
memdelete(ss);
1150
1151
registry->wl_seats.erase(E);
1152
return;
1153
}
1154
1155
E = N;
1156
}
1157
}
1158
1159
{
1160
// Iterate through all of the outputs to find if any got removed.
1161
// FIXME: This is a very bruteforce approach.
1162
List<struct wl_output *>::Element *it = registry->wl_outputs.front();
1163
while (it) {
1164
// Iterate through all of the screens to find if any got removed.
1165
struct wl_output *wl_output = it->get();
1166
ERR_FAIL_NULL(wl_output);
1167
1168
ScreenState *ss = wl_output_get_screen_state(wl_output);
1169
1170
if (ss->wl_output_name == name) {
1171
registry->wl_outputs.erase(it);
1172
1173
memdelete(ss);
1174
wl_output_destroy(wl_output);
1175
1176
return;
1177
}
1178
1179
it = it->next();
1180
}
1181
}
1182
1183
if (name == registry->wp_fifo_manager_name) {
1184
registry->wp_fifo_manager_name = 0;
1185
}
1186
1187
if (name == registry->godot_embedding_compositor_name) {
1188
registry->godot_embedding_compositor_name = 0;
1189
1190
EmbeddingCompositorState *es = godot_embedding_compositor_get_state(registry->godot_embedding_compositor);
1191
ERR_FAIL_NULL(es);
1192
1193
es->mapped_clients.clear();
1194
1195
for (struct godot_embedded_client *client : es->clients) {
1196
godot_embedded_client_destroy(client);
1197
}
1198
es->clients.clear();
1199
1200
memdelete(es);
1201
1202
godot_embedding_compositor_destroy(registry->godot_embedding_compositor);
1203
registry->godot_embedding_compositor = nullptr;
1204
}
1205
}
1206
1207
void WaylandThread::_wl_surface_on_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) {
1208
if (!wl_output || !wl_proxy_is_godot((struct wl_proxy *)wl_output)) {
1209
// This won't have the right data bound to it. Not worth it and would probably
1210
// just break everything.
1211
return;
1212
}
1213
1214
WindowState *ws = (WindowState *)data;
1215
ERR_FAIL_NULL(ws);
1216
1217
DEBUG_LOG_WAYLAND_THREAD(vformat("Window entered output %x.", (size_t)wl_output));
1218
1219
ws->wl_outputs.insert(wl_output);
1220
1221
// Workaround for buffer scaling as there's no guaranteed way of knowing the
1222
// preferred scale.
1223
// TODO: Skip this branch for newer `wl_surface`s once we add support for
1224
// `wl_surface::preferred_buffer_scale`
1225
if (ws->preferred_fractional_scale == 0) {
1226
window_state_update_size(ws, ws->rect.size.width, ws->rect.size.height);
1227
}
1228
}
1229
1230
void WaylandThread::_frame_wl_callback_on_done(void *data, struct wl_callback *wl_callback, uint32_t callback_data) {
1231
wl_callback_destroy(wl_callback);
1232
1233
WindowState *ws = (WindowState *)data;
1234
ERR_FAIL_NULL(ws);
1235
ERR_FAIL_NULL(ws->wayland_thread);
1236
ERR_FAIL_NULL(ws->wl_surface);
1237
1238
ws->last_frame_time = OS::get_singleton()->get_ticks_usec();
1239
ws->wayland_thread->set_frame();
1240
1241
ws->frame_callback = wl_surface_frame(ws->wl_surface);
1242
wl_callback_add_listener(ws->frame_callback, &frame_wl_callback_listener, ws);
1243
1244
if (ws->wl_surface && ws->buffer_scale_changed) {
1245
// NOTE: We're only now setting the buffer scale as the idea is to get this
1246
// data committed together with the new frame, all by the rendering driver.
1247
// This is important because we might otherwise set an invalid combination of
1248
// buffer size and scale (e.g. odd size and 2x scale). We're pretty much
1249
// guaranteed to get a proper buffer in the next render loop as the rescaling
1250
// method also informs the engine of a "window rect change", triggering
1251
// rendering if needed.
1252
wl_surface_set_buffer_scale(ws->wl_surface, window_state_get_preferred_buffer_scale(ws));
1253
}
1254
}
1255
1256
void WaylandThread::_wl_surface_on_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) {
1257
if (!wl_output || !wl_proxy_is_godot((struct wl_proxy *)wl_output)) {
1258
// This won't have the right data bound to it. Not worth it and would probably
1259
// just break everything.
1260
return;
1261
}
1262
1263
WindowState *ws = (WindowState *)data;
1264
ERR_FAIL_NULL(ws);
1265
1266
ws->wl_outputs.erase(wl_output);
1267
1268
DEBUG_LOG_WAYLAND_THREAD(vformat("Window left output %x.\n", (size_t)wl_output));
1269
}
1270
1271
// TODO: Add support to this event.
1272
void WaylandThread::_wl_surface_on_preferred_buffer_scale(void *data, struct wl_surface *wl_surface, int32_t factor) {
1273
}
1274
1275
// TODO: Add support to this event.
1276
void WaylandThread::_wl_surface_on_preferred_buffer_transform(void *data, struct wl_surface *wl_surface, uint32_t transform) {
1277
}
1278
1279
void WaylandThread::_wl_output_on_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char *make, const char *model, int32_t transform) {
1280
ScreenState *ss = (ScreenState *)data;
1281
ERR_FAIL_NULL(ss);
1282
1283
ss->pending_data.position.x = x;
1284
1285
ss->pending_data.position.x = x;
1286
ss->pending_data.position.y = y;
1287
1288
ss->pending_data.physical_size.width = physical_width;
1289
ss->pending_data.physical_size.height = physical_height;
1290
1291
ss->pending_data.make.clear();
1292
ss->pending_data.make.append_utf8(make);
1293
ss->pending_data.model.clear();
1294
ss->pending_data.model.append_utf8(model);
1295
1296
// `wl_output::done` is a version 2 addition. We'll directly update the data
1297
// for compatibility.
1298
if (wl_output_get_version(wl_output) == 1) {
1299
ss->data = ss->pending_data;
1300
}
1301
}
1302
1303
void WaylandThread::_wl_output_on_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
1304
ScreenState *ss = (ScreenState *)data;
1305
ERR_FAIL_NULL(ss);
1306
1307
if (!(flags & WL_OUTPUT_MODE_CURRENT)) {
1308
return;
1309
}
1310
1311
ss->pending_data.size.width = width;
1312
ss->pending_data.size.height = height;
1313
1314
ss->pending_data.refresh_rate = refresh ? refresh / 1000.0f : -1;
1315
1316
// `wl_output::done` is a version 2 addition. We'll directly update the data
1317
// for compatibility.
1318
if (wl_output_get_version(wl_output) == 1) {
1319
ss->data = ss->pending_data;
1320
}
1321
}
1322
1323
// NOTE: The following `wl_output` events are only for version 2 onwards, so we
1324
// can assume that they're "atomic" (i.e. rely on the `wl_output::done` event).
1325
1326
void WaylandThread::_wl_output_on_done(void *data, struct wl_output *wl_output) {
1327
ScreenState *ss = (ScreenState *)data;
1328
ERR_FAIL_NULL(ss);
1329
1330
ss->data = ss->pending_data;
1331
1332
ss->wayland_thread->_update_scale(ss->data.scale);
1333
1334
DEBUG_LOG_WAYLAND_THREAD(vformat("Output %x done.", (size_t)wl_output));
1335
}
1336
1337
void WaylandThread::_wl_output_on_scale(void *data, struct wl_output *wl_output, int32_t factor) {
1338
ScreenState *ss = (ScreenState *)data;
1339
ERR_FAIL_NULL(ss);
1340
1341
ss->pending_data.scale = factor;
1342
1343
DEBUG_LOG_WAYLAND_THREAD(vformat("Output %x scale %d", (size_t)wl_output, factor));
1344
}
1345
1346
void WaylandThread::_wl_output_on_name(void *data, struct wl_output *wl_output, const char *name) {
1347
}
1348
1349
void WaylandThread::_wl_output_on_description(void *data, struct wl_output *wl_output, const char *description) {
1350
}
1351
1352
void WaylandThread::_xdg_wm_base_on_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) {
1353
xdg_wm_base_pong(xdg_wm_base, serial);
1354
}
1355
1356
void WaylandThread::_xdg_surface_on_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) {
1357
xdg_surface_ack_configure(xdg_surface, serial);
1358
1359
WindowState *ws = (WindowState *)data;
1360
ERR_FAIL_NULL(ws);
1361
1362
DEBUG_LOG_WAYLAND_THREAD(vformat("xdg surface on configure rect %s", ws->rect));
1363
}
1364
1365
void WaylandThread::_xdg_toplevel_on_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states) {
1366
WindowState *ws = (WindowState *)data;
1367
ERR_FAIL_NULL(ws);
1368
1369
// Expect the window to be in a plain state. It will get properly set if the
1370
// compositor reports otherwise below.
1371
ws->mode = DisplayServer::WINDOW_MODE_WINDOWED;
1372
ws->maximized = false;
1373
ws->fullscreen = false;
1374
ws->resizing = false;
1375
ws->tiled_left = false;
1376
ws->tiled_right = false;
1377
ws->tiled_top = false;
1378
ws->tiled_bottom = false;
1379
ws->suspended = false;
1380
1381
uint32_t *state = nullptr;
1382
wl_array_for_each(state, states) {
1383
switch (*state) {
1384
case XDG_TOPLEVEL_STATE_MAXIMIZED: {
1385
ws->mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
1386
ws->maximized = true;
1387
} break;
1388
1389
case XDG_TOPLEVEL_STATE_FULLSCREEN: {
1390
ws->mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
1391
ws->fullscreen = true;
1392
} break;
1393
1394
case XDG_TOPLEVEL_STATE_RESIZING: {
1395
ws->resizing = true;
1396
} break;
1397
1398
case XDG_TOPLEVEL_STATE_TILED_LEFT: {
1399
ws->tiled_left = true;
1400
} break;
1401
1402
case XDG_TOPLEVEL_STATE_TILED_RIGHT: {
1403
ws->tiled_right = true;
1404
} break;
1405
1406
case XDG_TOPLEVEL_STATE_TILED_TOP: {
1407
ws->tiled_top = true;
1408
} break;
1409
1410
case XDG_TOPLEVEL_STATE_TILED_BOTTOM: {
1411
ws->tiled_bottom = true;
1412
} break;
1413
1414
case XDG_TOPLEVEL_STATE_SUSPENDED: {
1415
ws->suspended = true;
1416
} break;
1417
1418
default: {
1419
// We don't care about the other states (for now).
1420
} break;
1421
}
1422
}
1423
1424
if (width != 0 && height != 0) {
1425
window_state_update_size(ws, width, height);
1426
}
1427
1428
DEBUG_LOG_WAYLAND_THREAD(vformat("XDG toplevel on configure width %d height %d.", width, height));
1429
}
1430
1431
void WaylandThread::_xdg_toplevel_on_close(void *data, struct xdg_toplevel *xdg_toplevel) {
1432
WindowState *ws = (WindowState *)data;
1433
ERR_FAIL_NULL(ws);
1434
1435
Ref<WindowEventMessage> msg;
1436
msg.instantiate();
1437
msg->id = ws->id;
1438
msg->event = DisplayServer::WINDOW_EVENT_CLOSE_REQUEST;
1439
ws->wayland_thread->push_message(msg);
1440
}
1441
1442
void WaylandThread::_xdg_toplevel_on_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) {
1443
}
1444
1445
void WaylandThread::_xdg_toplevel_on_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities) {
1446
WindowState *ws = (WindowState *)data;
1447
ERR_FAIL_NULL(ws);
1448
1449
ws->can_maximize = false;
1450
ws->can_fullscreen = false;
1451
ws->can_minimize = false;
1452
1453
uint32_t *capability = nullptr;
1454
wl_array_for_each(capability, capabilities) {
1455
switch (*capability) {
1456
case XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE: {
1457
ws->can_maximize = true;
1458
} break;
1459
case XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN: {
1460
ws->can_fullscreen = true;
1461
} break;
1462
1463
case XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE: {
1464
ws->can_minimize = true;
1465
} break;
1466
1467
default: {
1468
} break;
1469
}
1470
}
1471
}
1472
1473
void WaylandThread::_xdg_popup_on_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height) {
1474
WindowState *ws = (WindowState *)data;
1475
ERR_FAIL_NULL(ws);
1476
1477
if (width != 0 && height != 0) {
1478
window_state_update_size(ws, width, height);
1479
}
1480
1481
WindowState *parent = ws->wayland_thread->window_get_state(ws->parent_id);
1482
ERR_FAIL_NULL(parent);
1483
1484
Point2i pos = Point2i(x, y);
1485
#ifdef LIBDECOR_ENABLED
1486
if (parent->libdecor_frame) {
1487
int translated_x = x;
1488
int translated_y = y;
1489
libdecor_frame_translate_coordinate(parent->libdecor_frame, x, y, &translated_x, &translated_y);
1490
1491
pos.x = translated_x;
1492
pos.y = translated_y;
1493
}
1494
#endif
1495
1496
// Looks like the position returned here is relative to the parent. We have to
1497
// accumulate it or there's gonna be a lot of confusion godot-side.
1498
pos += parent->rect.position;
1499
1500
if (ws->rect.position != pos) {
1501
DEBUG_LOG_WAYLAND_THREAD(vformat("Repositioning popup %d from %s to %s", ws->id, ws->rect.position, pos));
1502
1503
double parent_scale = window_state_get_scale_factor(parent);
1504
1505
ws->rect.position = pos;
1506
1507
Ref<WindowRectMessage> rect_msg;
1508
rect_msg.instantiate();
1509
rect_msg->id = ws->id;
1510
rect_msg->rect.position = scale_vector2i(ws->rect.position, parent_scale);
1511
rect_msg->rect.size = scale_vector2i(ws->rect.size, parent_scale);
1512
1513
ws->wayland_thread->push_message(rect_msg);
1514
}
1515
1516
DEBUG_LOG_WAYLAND_THREAD(vformat("xdg popup on configure x%d y%d w%d h%d", x, y, width, height));
1517
}
1518
1519
void WaylandThread::_xdg_popup_on_popup_done(void *data, struct xdg_popup *xdg_popup) {
1520
WindowState *ws = (WindowState *)data;
1521
ERR_FAIL_NULL(ws);
1522
1523
Ref<WindowEventMessage> ev_msg;
1524
ev_msg.instantiate();
1525
ev_msg->id = ws->id;
1526
ev_msg->event = DisplayServer::WINDOW_EVENT_FORCE_CLOSE;
1527
1528
ws->wayland_thread->push_message(ev_msg);
1529
}
1530
1531
void WaylandThread::_xdg_popup_on_repositioned(void *data, struct xdg_popup *xdg_popup, uint32_t token) {
1532
DEBUG_LOG_WAYLAND_THREAD(vformat("stub xdg popup repositioned %x", token));
1533
}
1534
1535
// NOTE: Deprecated.
1536
void WaylandThread::_xdg_exported_v1_on_handle(void *data, zxdg_exported_v1 *exported, const char *handle) {
1537
WindowState *ws = (WindowState *)data;
1538
ERR_FAIL_NULL(ws);
1539
1540
ws->exported_handle = vformat("wayland:%s", String::utf8(handle));
1541
}
1542
1543
void WaylandThread::_xdg_exported_v2_on_handle(void *data, zxdg_exported_v2 *exported, const char *handle) {
1544
WindowState *ws = (WindowState *)data;
1545
ERR_FAIL_NULL(ws);
1546
1547
ws->exported_handle = vformat("wayland:%s", String::utf8(handle));
1548
}
1549
1550
void WaylandThread::_xdg_toplevel_decoration_on_configure(void *data, struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration, uint32_t mode) {
1551
if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
1552
#ifdef LIBDECOR_ENABLED
1553
WARN_PRINT_ONCE("Native client side decorations are not yet supported without libdecor!");
1554
#else
1555
WARN_PRINT_ONCE("Native client side decorations are not yet supported!");
1556
#endif // LIBDECOR_ENABLED
1557
}
1558
}
1559
1560
#ifdef LIBDECOR_ENABLED
1561
void WaylandThread::libdecor_on_error(struct libdecor *context, enum libdecor_error error, const char *message) {
1562
ERR_PRINT(vformat("libdecor error %d: %s", error, message));
1563
}
1564
1565
// NOTE: This is pretty much a reimplementation of _xdg_surface_on_configure
1566
// and _xdg_toplevel_on_configure. Libdecor really likes wrapping everything,
1567
// forcing us to do stuff like this.
1568
void WaylandThread::libdecor_frame_on_configure(struct libdecor_frame *frame, struct libdecor_configuration *configuration, void *user_data) {
1569
WindowState *ws = (WindowState *)user_data;
1570
ERR_FAIL_NULL(ws);
1571
1572
int width = 0;
1573
int height = 0;
1574
1575
ws->pending_libdecor_configuration = configuration;
1576
1577
if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
1578
// The configuration doesn't have a size. We'll use the one already set in the window.
1579
width = ws->rect.size.width;
1580
height = ws->rect.size.height;
1581
}
1582
1583
ERR_FAIL_COND_MSG(width == 0 || height == 0, "Window has invalid size.");
1584
1585
libdecor_window_state window_state = LIBDECOR_WINDOW_STATE_NONE;
1586
1587
// Expect the window to be in a plain state. It will get properly set if the
1588
// compositor reports otherwise below.
1589
ws->mode = DisplayServer::WINDOW_MODE_WINDOWED;
1590
ws->maximized = false;
1591
ws->fullscreen = false;
1592
ws->resizing = false;
1593
ws->tiled_left = false;
1594
ws->tiled_right = false;
1595
ws->tiled_top = false;
1596
ws->tiled_bottom = false;
1597
ws->suspended = false;
1598
1599
if (libdecor_configuration_get_window_state(configuration, &window_state)) {
1600
if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) {
1601
ws->mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
1602
ws->maximized = true;
1603
}
1604
1605
if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) {
1606
ws->mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
1607
ws->fullscreen = true;
1608
}
1609
1610
// libdecor doesn't have the resizing state for whatever reason.
1611
1612
if (window_state & LIBDECOR_WINDOW_STATE_TILED_LEFT) {
1613
ws->tiled_left = true;
1614
}
1615
1616
if (window_state & LIBDECOR_WINDOW_STATE_TILED_RIGHT) {
1617
ws->tiled_right = true;
1618
}
1619
1620
if (window_state & LIBDECOR_WINDOW_STATE_TILED_TOP) {
1621
ws->tiled_top = true;
1622
}
1623
1624
if (window_state & LIBDECOR_WINDOW_STATE_TILED_BOTTOM) {
1625
ws->tiled_bottom = true;
1626
}
1627
1628
if (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) {
1629
ws->suspended = true;
1630
}
1631
}
1632
1633
window_state_update_size(ws, width, height);
1634
1635
DEBUG_LOG_WAYLAND_THREAD(vformat("libdecor frame on configure rect %s", ws->rect));
1636
}
1637
1638
void WaylandThread::libdecor_frame_on_close(struct libdecor_frame *frame, void *user_data) {
1639
WindowState *ws = (WindowState *)user_data;
1640
ERR_FAIL_NULL(ws);
1641
1642
Ref<WindowEventMessage> winevent_msg;
1643
winevent_msg.instantiate();
1644
winevent_msg->id = ws->id;
1645
winevent_msg->event = DisplayServer::WINDOW_EVENT_CLOSE_REQUEST;
1646
1647
ws->wayland_thread->push_message(winevent_msg);
1648
1649
DEBUG_LOG_WAYLAND_THREAD("libdecor frame on close");
1650
}
1651
1652
void WaylandThread::libdecor_frame_on_commit(struct libdecor_frame *frame, void *user_data) {
1653
// We're skipping this as we don't really care about libdecor's commit for
1654
// atomicity reasons. See `_frame_wl_callback_on_done` for more info.
1655
1656
DEBUG_LOG_WAYLAND_THREAD("libdecor frame on commit");
1657
}
1658
1659
void WaylandThread::libdecor_frame_on_dismiss_popup(struct libdecor_frame *frame, const char *seat_name, void *user_data) {
1660
}
1661
#endif // LIBDECOR_ENABLED
1662
1663
void WaylandThread::_wl_seat_on_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) {
1664
SeatState *ss = (SeatState *)data;
1665
1666
ERR_FAIL_NULL(ss);
1667
1668
// TODO: Handle touch.
1669
1670
// Pointer handling.
1671
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
1672
if (!ss->wl_pointer) {
1673
ss->cursor_surface = wl_compositor_create_surface(ss->registry->wl_compositor);
1674
wl_surface_commit(ss->cursor_surface);
1675
1676
ss->wl_pointer = wl_seat_get_pointer(wl_seat);
1677
wl_pointer_add_listener(ss->wl_pointer, &wl_pointer_listener, ss);
1678
1679
if (ss->registry->wp_cursor_shape_manager) {
1680
ss->wp_cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(ss->registry->wp_cursor_shape_manager, ss->wl_pointer);
1681
}
1682
1683
if (ss->registry->wp_relative_pointer_manager) {
1684
ss->wp_relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(ss->registry->wp_relative_pointer_manager, ss->wl_pointer);
1685
zwp_relative_pointer_v1_add_listener(ss->wp_relative_pointer, &wp_relative_pointer_listener, ss);
1686
}
1687
1688
if (ss->registry->wp_pointer_gestures) {
1689
ss->wp_pointer_gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(ss->registry->wp_pointer_gestures, ss->wl_pointer);
1690
zwp_pointer_gesture_pinch_v1_add_listener(ss->wp_pointer_gesture_pinch, &wp_pointer_gesture_pinch_listener, ss);
1691
}
1692
1693
// TODO: Constrain new pointers if the global mouse mode is constrained.
1694
}
1695
} else {
1696
if (ss->cursor_frame_callback) {
1697
// Just in case. I got bitten by weird race-like conditions already.
1698
wl_callback_set_user_data(ss->cursor_frame_callback, nullptr);
1699
1700
wl_callback_destroy(ss->cursor_frame_callback);
1701
ss->cursor_frame_callback = nullptr;
1702
}
1703
1704
if (ss->cursor_surface) {
1705
wl_surface_destroy(ss->cursor_surface);
1706
ss->cursor_surface = nullptr;
1707
}
1708
1709
if (ss->wl_pointer) {
1710
wl_pointer_destroy(ss->wl_pointer);
1711
ss->wl_pointer = nullptr;
1712
}
1713
1714
if (ss->wp_cursor_shape_device) {
1715
wp_cursor_shape_device_v1_destroy(ss->wp_cursor_shape_device);
1716
ss->wp_cursor_shape_device = nullptr;
1717
}
1718
1719
if (ss->wp_relative_pointer) {
1720
zwp_relative_pointer_v1_destroy(ss->wp_relative_pointer);
1721
ss->wp_relative_pointer = nullptr;
1722
}
1723
1724
if (ss->wp_confined_pointer) {
1725
zwp_confined_pointer_v1_destroy(ss->wp_confined_pointer);
1726
ss->wp_confined_pointer = nullptr;
1727
}
1728
1729
if (ss->wp_locked_pointer) {
1730
zwp_locked_pointer_v1_destroy(ss->wp_locked_pointer);
1731
ss->wp_locked_pointer = nullptr;
1732
}
1733
}
1734
1735
// Keyboard handling.
1736
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
1737
if (!ss->wl_keyboard) {
1738
ss->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1739
ERR_FAIL_NULL(ss->xkb_context);
1740
1741
ss->wl_keyboard = wl_seat_get_keyboard(wl_seat);
1742
wl_keyboard_add_listener(ss->wl_keyboard, &wl_keyboard_listener, ss);
1743
}
1744
} else {
1745
if (ss->xkb_context) {
1746
xkb_context_unref(ss->xkb_context);
1747
ss->xkb_context = nullptr;
1748
}
1749
1750
if (ss->xkb_compose_table) {
1751
xkb_compose_table_unref(ss->xkb_compose_table);
1752
ss->xkb_compose_table = nullptr;
1753
}
1754
1755
if (ss->xkb_compose_state) {
1756
xkb_compose_state_unref(ss->xkb_compose_state);
1757
ss->xkb_compose_state = nullptr;
1758
}
1759
1760
if (ss->xkb_keymap) {
1761
xkb_keymap_unref(ss->xkb_keymap);
1762
ss->xkb_keymap = nullptr;
1763
}
1764
1765
if (ss->xkb_state) {
1766
xkb_state_unref(ss->xkb_state);
1767
ss->xkb_state = nullptr;
1768
}
1769
1770
if (ss->wl_keyboard) {
1771
wl_keyboard_destroy(ss->wl_keyboard);
1772
ss->wl_keyboard = nullptr;
1773
}
1774
}
1775
}
1776
1777
void WaylandThread::_wl_seat_on_name(void *data, struct wl_seat *wl_seat, const char *name) {
1778
}
1779
1780
void WaylandThread::_cursor_frame_callback_on_done(void *data, struct wl_callback *wl_callback, uint32_t time_ms) {
1781
wl_callback_destroy(wl_callback);
1782
1783
SeatState *ss = (SeatState *)data;
1784
ERR_FAIL_NULL(ss);
1785
1786
ss->cursor_frame_callback = nullptr;
1787
1788
ss->cursor_time_ms = time_ms;
1789
1790
seat_state_update_cursor(ss);
1791
}
1792
1793
void WaylandThread::_wl_pointer_on_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
1794
WindowState *ws = wl_surface_get_window_state(surface);
1795
if (!ws) {
1796
return;
1797
}
1798
1799
SeatState *ss = (SeatState *)data;
1800
ERR_FAIL_NULL(ss);
1801
1802
ERR_FAIL_NULL(ss->cursor_surface);
1803
1804
PointerData &pd = ss->pointer_data_buffer;
1805
1806
ss->pointer_enter_serial = serial;
1807
pd.pointed_id = ws->id;
1808
pd.last_pointed_id = ws->id;
1809
pd.position.x = wl_fixed_to_double(surface_x);
1810
pd.position.y = wl_fixed_to_double(surface_y);
1811
1812
seat_state_update_cursor(ss);
1813
1814
DEBUG_LOG_WAYLAND_THREAD(vformat("Pointer entered window %d.", ws->id));
1815
1816
if (wl_pointer_get_version(wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) {
1817
_wl_pointer_on_frame(data, wl_pointer);
1818
}
1819
}
1820
1821
void WaylandThread::_wl_pointer_on_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) {
1822
// NOTE: `surface` will probably be null when the surface is destroyed.
1823
// See: https://gitlab.freedesktop.org/wayland/wayland/-/issues/366
1824
// See: https://gitlab.freedesktop.org/wayland/wayland/-/issues/465
1825
1826
SeatState *ss = (SeatState *)data;
1827
ERR_FAIL_NULL(ss);
1828
1829
PointerData &pd = ss->pointer_data_buffer;
1830
1831
if (pd.pointed_id == DisplayServer::INVALID_WINDOW_ID) {
1832
// We're probably on a decoration or some other third-party thing.
1833
return;
1834
}
1835
1836
DisplayServer::WindowID id = pd.pointed_id;
1837
1838
pd.pointed_id = DisplayServer::INVALID_WINDOW_ID;
1839
pd.pressed_button_mask.clear();
1840
1841
DEBUG_LOG_WAYLAND_THREAD(vformat("Pointer left window %d.", id));
1842
1843
if (wl_pointer_get_version(wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) {
1844
_wl_pointer_on_frame(data, wl_pointer);
1845
}
1846
}
1847
1848
void WaylandThread::_wl_pointer_on_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
1849
SeatState *ss = (SeatState *)data;
1850
ERR_FAIL_NULL(ss);
1851
1852
PointerData &pd = ss->pointer_data_buffer;
1853
1854
pd.position.x = wl_fixed_to_double(surface_x);
1855
pd.position.y = wl_fixed_to_double(surface_y);
1856
1857
pd.motion_time = time;
1858
1859
if (wl_pointer_get_version(wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) {
1860
_wl_pointer_on_frame(data, wl_pointer);
1861
}
1862
}
1863
1864
void WaylandThread::_wl_pointer_on_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
1865
SeatState *ss = (SeatState *)data;
1866
ERR_FAIL_NULL(ss);
1867
1868
PointerData &pd = ss->pointer_data_buffer;
1869
1870
MouseButton button_pressed = MouseButton::NONE;
1871
1872
switch (button) {
1873
case BTN_LEFT:
1874
button_pressed = MouseButton::LEFT;
1875
break;
1876
1877
case BTN_RIGHT:
1878
button_pressed = MouseButton::RIGHT;
1879
break;
1880
1881
case BTN_MIDDLE:
1882
button_pressed = MouseButton::MIDDLE;
1883
break;
1884
1885
case BTN_SIDE:
1886
button_pressed = MouseButton::MB_XBUTTON1;
1887
break;
1888
1889
case BTN_EXTRA:
1890
button_pressed = MouseButton::MB_XBUTTON2;
1891
break;
1892
1893
default: {
1894
}
1895
}
1896
1897
MouseButtonMask mask = mouse_button_to_mask(button_pressed);
1898
1899
if (state & WL_POINTER_BUTTON_STATE_PRESSED) {
1900
pd.pressed_button_mask.set_flag(mask);
1901
pd.last_button_pressed = button_pressed;
1902
pd.double_click_begun = true;
1903
} else {
1904
pd.pressed_button_mask.clear_flag(mask);
1905
}
1906
1907
pd.button_time = time;
1908
pd.button_serial = serial;
1909
1910
if (wl_pointer_get_version(wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) {
1911
_wl_pointer_on_frame(data, wl_pointer);
1912
}
1913
}
1914
1915
void WaylandThread::_wl_pointer_on_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
1916
SeatState *ss = (SeatState *)data;
1917
ERR_FAIL_NULL(ss);
1918
1919
PointerData &pd = ss->pointer_data_buffer;
1920
1921
switch (axis) {
1922
case WL_POINTER_AXIS_VERTICAL_SCROLL: {
1923
pd.scroll_vector.y = wl_fixed_to_double(value);
1924
} break;
1925
1926
case WL_POINTER_AXIS_HORIZONTAL_SCROLL: {
1927
pd.scroll_vector.x = wl_fixed_to_double(value);
1928
} break;
1929
}
1930
1931
pd.button_time = time;
1932
1933
if (wl_pointer_get_version(wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) {
1934
_wl_pointer_on_frame(data, wl_pointer);
1935
}
1936
}
1937
1938
void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_pointer) {
1939
SeatState *ss = (SeatState *)data;
1940
ERR_FAIL_NULL(ss);
1941
1942
WaylandThread *wayland_thread = ss->wayland_thread;
1943
ERR_FAIL_NULL(wayland_thread);
1944
1945
PointerData &old_pd = ss->pointer_data;
1946
PointerData &pd = ss->pointer_data_buffer;
1947
1948
if (pd.pointed_id != old_pd.pointed_id) {
1949
if (old_pd.pointed_id != DisplayServer::INVALID_WINDOW_ID) {
1950
Ref<WindowEventMessage> msg;
1951
msg.instantiate();
1952
msg->id = old_pd.pointed_id;
1953
msg->event = DisplayServer::WINDOW_EVENT_MOUSE_EXIT;
1954
1955
wayland_thread->push_message(msg);
1956
}
1957
1958
if (pd.pointed_id != DisplayServer::INVALID_WINDOW_ID) {
1959
Ref<WindowEventMessage> msg;
1960
msg.instantiate();
1961
msg->id = pd.pointed_id;
1962
msg->event = DisplayServer::WINDOW_EVENT_MOUSE_ENTER;
1963
1964
wayland_thread->push_message(msg);
1965
}
1966
}
1967
1968
WindowState *ws = nullptr;
1969
1970
// NOTE: At least on sway, with wl_pointer version 5 or greater,
1971
// wl_pointer::leave might be emitted with other events (like
1972
// wl_pointer::button) within the same wl_pointer::frame. Because of this, we
1973
// need to account for when the currently pointed window might be invalid
1974
// (third-party or even none) and fall back to the old one.
1975
if (pd.pointed_id != DisplayServer::INVALID_WINDOW_ID) {
1976
ws = ss->wayland_thread->window_get_state(pd.pointed_id);
1977
ERR_FAIL_NULL(ws);
1978
} else if (old_pd.pointed_id != DisplayServer::INVALID_WINDOW_ID) {
1979
ws = ss->wayland_thread->window_get_state(old_pd.pointed_id);
1980
ERR_FAIL_NULL(ws);
1981
}
1982
1983
if (ws == nullptr) {
1984
// We're probably on a decoration or some other third-party thing. Let's
1985
// "commit" the data and call it a day.
1986
old_pd = pd;
1987
return;
1988
}
1989
1990
double scale = window_state_get_scale_factor(ws);
1991
1992
wayland_thread->_set_current_seat(ss->wl_seat);
1993
1994
if (old_pd.motion_time != pd.motion_time || old_pd.relative_motion_time != pd.relative_motion_time) {
1995
Ref<InputEventMouseMotion> mm;
1996
mm.instantiate();
1997
1998
// Set all pressed modifiers.
1999
mm->set_shift_pressed(ss->shift_pressed);
2000
mm->set_ctrl_pressed(ss->ctrl_pressed);
2001
mm->set_alt_pressed(ss->alt_pressed);
2002
mm->set_meta_pressed(ss->meta_pressed);
2003
2004
mm->set_window_id(ws->id);
2005
2006
mm->set_button_mask(pd.pressed_button_mask);
2007
2008
mm->set_position(pd.position * scale);
2009
mm->set_global_position(pd.position * scale);
2010
2011
Vector2 pos_delta = (pd.position - old_pd.position) * scale;
2012
2013
if (old_pd.relative_motion_time != pd.relative_motion_time) {
2014
uint32_t time_delta = pd.relative_motion_time - old_pd.relative_motion_time;
2015
2016
mm->set_relative(pd.relative_motion * scale);
2017
mm->set_velocity((Vector2)pos_delta / time_delta);
2018
} else {
2019
// The spec includes the possibility of having motion events without an
2020
// associated relative motion event. If that's the case, fallback to a
2021
// simple delta of the position. The captured mouse won't report the
2022
// relative speed anymore though.
2023
uint32_t time_delta = pd.motion_time - old_pd.motion_time;
2024
2025
mm->set_relative(pos_delta);
2026
mm->set_velocity((Vector2)pos_delta / time_delta);
2027
}
2028
mm->set_relative_screen_position(mm->get_relative());
2029
mm->set_screen_velocity(mm->get_velocity());
2030
2031
Ref<InputEventMessage> msg;
2032
msg.instantiate();
2033
2034
msg->event = mm;
2035
2036
wayland_thread->push_message(msg);
2037
}
2038
2039
if (pd.discrete_scroll_vector_120 - old_pd.discrete_scroll_vector_120 != Vector2i()) {
2040
// This is a discrete scroll (eg. from a scroll wheel), so we'll just emit
2041
// scroll wheel buttons.
2042
if (pd.scroll_vector.y != 0) {
2043
MouseButton button = pd.scroll_vector.y > 0 ? MouseButton::WHEEL_DOWN : MouseButton::WHEEL_UP;
2044
pd.pressed_button_mask.set_flag(mouse_button_to_mask(button));
2045
}
2046
2047
if (pd.scroll_vector.x != 0) {
2048
MouseButton button = pd.scroll_vector.x > 0 ? MouseButton::WHEEL_RIGHT : MouseButton::WHEEL_LEFT;
2049
pd.pressed_button_mask.set_flag(mouse_button_to_mask(button));
2050
}
2051
} else {
2052
if (pd.scroll_vector - old_pd.scroll_vector != Vector2()) {
2053
// This is a continuous scroll, so we'll emit a pan gesture.
2054
Ref<InputEventPanGesture> pg;
2055
pg.instantiate();
2056
2057
// Set all pressed modifiers.
2058
pg->set_shift_pressed(ss->shift_pressed);
2059
pg->set_ctrl_pressed(ss->ctrl_pressed);
2060
pg->set_alt_pressed(ss->alt_pressed);
2061
pg->set_meta_pressed(ss->meta_pressed);
2062
2063
pg->set_position(pd.position * scale);
2064
2065
pg->set_window_id(ws->id);
2066
2067
pg->set_delta(pd.scroll_vector);
2068
2069
Ref<InputEventMessage> msg;
2070
msg.instantiate();
2071
2072
msg->event = pg;
2073
2074
wayland_thread->push_message(msg);
2075
}
2076
}
2077
2078
if (old_pd.pressed_button_mask != pd.pressed_button_mask) {
2079
BitField<MouseButtonMask> pressed_mask_delta = old_pd.pressed_button_mask.get_different(pd.pressed_button_mask);
2080
2081
const MouseButton buttons_to_test[] = {
2082
MouseButton::LEFT,
2083
MouseButton::MIDDLE,
2084
MouseButton::RIGHT,
2085
MouseButton::WHEEL_UP,
2086
MouseButton::WHEEL_DOWN,
2087
MouseButton::WHEEL_LEFT,
2088
MouseButton::WHEEL_RIGHT,
2089
MouseButton::MB_XBUTTON1,
2090
MouseButton::MB_XBUTTON2,
2091
};
2092
2093
for (MouseButton test_button : buttons_to_test) {
2094
MouseButtonMask test_button_mask = mouse_button_to_mask(test_button);
2095
if (pressed_mask_delta.has_flag(test_button_mask)) {
2096
Ref<InputEventMouseButton> mb;
2097
mb.instantiate();
2098
2099
// Set all pressed modifiers.
2100
mb->set_shift_pressed(ss->shift_pressed);
2101
mb->set_ctrl_pressed(ss->ctrl_pressed);
2102
mb->set_alt_pressed(ss->alt_pressed);
2103
mb->set_meta_pressed(ss->meta_pressed);
2104
2105
mb->set_window_id(ws->id);
2106
mb->set_position(pd.position * scale);
2107
mb->set_global_position(pd.position * scale);
2108
2109
if (test_button == MouseButton::WHEEL_UP || test_button == MouseButton::WHEEL_DOWN) {
2110
// If this is a discrete scroll, specify how many "clicks" it did for this
2111
// pointer frame.
2112
mb->set_factor(Math::abs(pd.discrete_scroll_vector_120.y / (float)120));
2113
}
2114
2115
if (test_button == MouseButton::WHEEL_RIGHT || test_button == MouseButton::WHEEL_LEFT) {
2116
// If this is a discrete scroll, specify how many "clicks" it did for this
2117
// pointer frame.
2118
mb->set_factor(std::abs(pd.discrete_scroll_vector_120.x / (float)120));
2119
}
2120
2121
mb->set_button_mask(pd.pressed_button_mask);
2122
2123
mb->set_button_index(test_button);
2124
mb->set_pressed(pd.pressed_button_mask.has_flag(test_button_mask));
2125
2126
// We have to set the last position pressed here as we can't take for
2127
// granted what the individual events might have seen due to them not having
2128
// a guaranteed order.
2129
if (mb->is_pressed()) {
2130
pd.last_pressed_position = pd.position;
2131
}
2132
2133
if (old_pd.double_click_begun && mb->is_pressed() && pd.last_button_pressed == old_pd.last_button_pressed && (pd.button_time - old_pd.button_time) < 400 && Vector2(old_pd.last_pressed_position * scale).distance_to(Vector2(pd.last_pressed_position * scale)) < 5) {
2134
pd.double_click_begun = false;
2135
mb->set_double_click(true);
2136
}
2137
2138
Ref<InputEventMessage> msg;
2139
msg.instantiate();
2140
2141
msg->event = mb;
2142
2143
wayland_thread->push_message(msg);
2144
2145
// Send an event resetting immediately the wheel key.
2146
// Wayland specification defines axis_stop events as optional and says to
2147
// treat all axis events as unterminated. As such, we have to manually do
2148
// it ourselves.
2149
if (test_button == MouseButton::WHEEL_UP || test_button == MouseButton::WHEEL_DOWN || test_button == MouseButton::WHEEL_LEFT || test_button == MouseButton::WHEEL_RIGHT) {
2150
// FIXME: This is ugly, I can't find a clean way to clone an InputEvent.
2151
// This works for now, despite being horrible.
2152
Ref<InputEventMouseButton> wh_up;
2153
wh_up.instantiate();
2154
2155
wh_up->set_window_id(ws->id);
2156
wh_up->set_position(pd.position * scale);
2157
wh_up->set_global_position(pd.position * scale);
2158
2159
// We have to unset the button to avoid it getting stuck.
2160
pd.pressed_button_mask.clear_flag(test_button_mask);
2161
wh_up->set_button_mask(pd.pressed_button_mask);
2162
2163
wh_up->set_button_index(test_button);
2164
wh_up->set_pressed(false);
2165
2166
Ref<InputEventMessage> msg_up;
2167
msg_up.instantiate();
2168
msg_up->event = wh_up;
2169
wayland_thread->push_message(msg_up);
2170
}
2171
}
2172
}
2173
}
2174
2175
// Reset the scroll vectors as we already handled them.
2176
pd.scroll_vector = Vector2();
2177
pd.discrete_scroll_vector_120 = Vector2i();
2178
2179
// Update the data all getters read. Wayland's specification requires us to do
2180
// this, since all pointer actions are sent in individual events.
2181
old_pd = pd;
2182
}
2183
2184
void WaylandThread::_wl_pointer_on_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) {
2185
SeatState *ss = (SeatState *)data;
2186
ERR_FAIL_NULL(ss);
2187
2188
ss->pointer_data_buffer.scroll_type = axis_source;
2189
}
2190
2191
void WaylandThread::_wl_pointer_on_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
2192
}
2193
2194
// NOTE: This event is deprecated since version 8 and superseded by
2195
// `wl_pointer::axis_value120`. This thus converts the data to its
2196
// fraction-of-120 format.
2197
void WaylandThread::_wl_pointer_on_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
2198
SeatState *ss = (SeatState *)data;
2199
ERR_FAIL_NULL(ss);
2200
2201
PointerData &pd = ss->pointer_data_buffer;
2202
2203
// NOTE: We can allow ourselves to not accumulate this data (and thus just
2204
// assign it) as the spec guarantees only one event per axis type.
2205
2206
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
2207
pd.discrete_scroll_vector_120.y = discrete * 120;
2208
}
2209
2210
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
2211
pd.discrete_scroll_vector_120.x = discrete * 120;
2212
}
2213
}
2214
2215
// Supersedes `wl_pointer::axis_discrete` Since version 8.
2216
void WaylandThread::_wl_pointer_on_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) {
2217
SeatState *ss = (SeatState *)data;
2218
ERR_FAIL_NULL(ss);
2219
2220
PointerData &pd = ss->pointer_data_buffer;
2221
2222
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
2223
pd.discrete_scroll_vector_120.y += value120;
2224
}
2225
2226
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
2227
pd.discrete_scroll_vector_120.x += value120;
2228
}
2229
}
2230
2231
// TODO: Add support to this event.
2232
void WaylandThread::_wl_pointer_on_axis_relative_direction(void *data, struct wl_pointer *wl_pointer, uint32_t axis, uint32_t direction) {
2233
}
2234
2235
void WaylandThread::_wl_keyboard_on_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) {
2236
ERR_FAIL_COND_MSG(format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, "Unsupported keymap format announced from the Wayland compositor.");
2237
2238
SeatState *ss = (SeatState *)data;
2239
ERR_FAIL_NULL(ss);
2240
2241
if (ss->keymap_buffer) {
2242
// We have already a mapped buffer, so we unmap it. There's no need to reset
2243
// its pointer or size, as we're gonna set them below.
2244
munmap((void *)ss->keymap_buffer, ss->keymap_buffer_size);
2245
ss->keymap_buffer = nullptr;
2246
}
2247
2248
ss->keymap_buffer = (const char *)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
2249
ss->keymap_buffer_size = size;
2250
2251
xkb_keymap_unref(ss->xkb_keymap);
2252
ss->xkb_keymap = xkb_keymap_new_from_string(ss->xkb_context, ss->keymap_buffer,
2253
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
2254
2255
xkb_state_unref(ss->xkb_state);
2256
ss->xkb_state = xkb_state_new(ss->xkb_keymap);
2257
2258
xkb_compose_table_unref(ss->xkb_compose_table);
2259
const char *locale = getenv("LC_ALL");
2260
if (!locale || !*locale) {
2261
locale = getenv("LC_CTYPE");
2262
}
2263
if (!locale || !*locale) {
2264
locale = getenv("LANG");
2265
}
2266
if (!locale || !*locale) {
2267
locale = "C";
2268
}
2269
ss->xkb_compose_table = xkb_compose_table_new_from_locale(ss->xkb_context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
2270
2271
xkb_compose_state_unref(ss->xkb_compose_state);
2272
ss->xkb_compose_state = xkb_compose_state_new(ss->xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
2273
2274
xkb_state_update_mask(ss->xkb_state, ss->mods_depressed, ss->mods_latched, ss->mods_locked, 0, 0, ss->current_layout_index);
2275
}
2276
2277
void WaylandThread::_wl_keyboard_on_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
2278
WindowState *ws = wl_surface_get_window_state(surface);
2279
if (!ws) {
2280
return;
2281
}
2282
2283
SeatState *ss = (SeatState *)data;
2284
ERR_FAIL_NULL(ss);
2285
2286
WaylandThread *wayland_thread = ss->wayland_thread;
2287
ERR_FAIL_NULL(wayland_thread);
2288
2289
ss->focused_id = ws->id;
2290
2291
wayland_thread->_set_current_seat(ss->wl_seat);
2292
2293
Ref<WindowEventMessage> msg;
2294
msg.instantiate();
2295
msg->id = ws->id;
2296
msg->event = DisplayServer::WINDOW_EVENT_FOCUS_IN;
2297
wayland_thread->push_message(msg);
2298
2299
DEBUG_LOG_WAYLAND_THREAD(vformat("Keyboard focused window %d.", ws->id));
2300
}
2301
2302
void WaylandThread::_wl_keyboard_on_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) {
2303
// NOTE: `surface` will probably be null when the surface is destroyed.
2304
// See: https://gitlab.freedesktop.org/wayland/wayland/-/issues/366
2305
// See: https://gitlab.freedesktop.org/wayland/wayland/-/issues/465
2306
2307
if (surface && !wl_proxy_is_godot((struct wl_proxy *)surface)) {
2308
return;
2309
}
2310
2311
SeatState *ss = (SeatState *)data;
2312
ERR_FAIL_NULL(ss);
2313
2314
WaylandThread *wayland_thread = ss->wayland_thread;
2315
ERR_FAIL_NULL(wayland_thread);
2316
2317
ss->repeating_keycode = XKB_KEYCODE_INVALID;
2318
2319
if (ss->focused_id == DisplayServer::INVALID_WINDOW_ID) {
2320
// We're probably on a decoration or some other third-party thing.
2321
return;
2322
}
2323
2324
WindowState *ws = wayland_thread->window_get_state(ss->focused_id);
2325
ERR_FAIL_NULL(ws);
2326
2327
ss->focused_id = DisplayServer::INVALID_WINDOW_ID;
2328
2329
Ref<WindowEventMessage> msg;
2330
msg.instantiate();
2331
msg->id = ws->id;
2332
msg->event = DisplayServer::WINDOW_EVENT_FOCUS_OUT;
2333
wayland_thread->push_message(msg);
2334
2335
ss->shift_pressed = false;
2336
ss->ctrl_pressed = false;
2337
ss->alt_pressed = false;
2338
ss->meta_pressed = false;
2339
2340
if (ss->xkb_state != nullptr) {
2341
xkb_state_update_mask(ss->xkb_state, 0, 0, 0, 0, 0, 0);
2342
}
2343
2344
DEBUG_LOG_WAYLAND_THREAD(vformat("Keyboard unfocused window %d.", ws->id));
2345
}
2346
2347
void WaylandThread::_wl_keyboard_on_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
2348
SeatState *ss = (SeatState *)data;
2349
ERR_FAIL_NULL(ss);
2350
2351
if (ss->focused_id == DisplayServer::INVALID_WINDOW_ID) {
2352
return;
2353
}
2354
2355
// We have to add 8 to the scancode to get an XKB-compatible keycode.
2356
xkb_keycode_t xkb_keycode = key + 8;
2357
2358
bool pressed = state & WL_KEYBOARD_KEY_STATE_PRESSED;
2359
2360
if (pressed) {
2361
if (xkb_keymap_key_repeats(ss->xkb_keymap, xkb_keycode)) {
2362
ss->last_repeat_start_msec = OS::get_singleton()->get_ticks_msec();
2363
ss->repeating_keycode = xkb_keycode;
2364
}
2365
2366
ss->last_key_pressed_serial = serial;
2367
} else if (ss->repeating_keycode == xkb_keycode) {
2368
ss->repeating_keycode = XKB_KEYCODE_INVALID;
2369
}
2370
2371
_seat_state_handle_xkb_keycode(ss, xkb_keycode, pressed);
2372
}
2373
2374
void WaylandThread::_wl_keyboard_on_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
2375
SeatState *ss = (SeatState *)data;
2376
ERR_FAIL_NULL(ss);
2377
2378
ss->mods_depressed = mods_depressed;
2379
ss->mods_latched = mods_latched;
2380
ss->mods_locked = mods_locked;
2381
ss->current_layout_index = group;
2382
2383
if (ss->xkb_state == nullptr) {
2384
return;
2385
}
2386
2387
xkb_state_update_mask(ss->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
2388
2389
ss->shift_pressed = xkb_state_mod_name_is_active(ss->xkb_state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE);
2390
ss->ctrl_pressed = xkb_state_mod_name_is_active(ss->xkb_state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE);
2391
ss->alt_pressed = xkb_state_mod_name_is_active(ss->xkb_state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE);
2392
ss->meta_pressed = xkb_state_mod_name_is_active(ss->xkb_state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE);
2393
}
2394
2395
void WaylandThread::_wl_keyboard_on_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) {
2396
SeatState *ss = (SeatState *)data;
2397
ERR_FAIL_NULL(ss);
2398
2399
ss->repeat_key_delay_msec = rate ? 1000 / rate : 0;
2400
ss->repeat_start_delay_msec = delay;
2401
}
2402
2403
// NOTE: Don't forget to `memfree` the offer's state.
2404
void WaylandThread::_wl_data_device_on_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id) {
2405
wl_proxy_tag_godot((struct wl_proxy *)id);
2406
wl_data_offer_add_listener(id, &wl_data_offer_listener, memnew(OfferState));
2407
}
2408
2409
void WaylandThread::_wl_data_device_on_enter(void *data, struct wl_data_device *wl_data_device, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) {
2410
WindowState *ws = wl_surface_get_window_state(surface);
2411
if (!ws) {
2412
return;
2413
}
2414
2415
SeatState *ss = (SeatState *)data;
2416
ERR_FAIL_NULL(ss);
2417
2418
ss->dnd_id = ws->id;
2419
2420
ss->dnd_enter_serial = serial;
2421
ss->wl_data_offer_dnd = id;
2422
2423
// Godot only supports DnD file copying for now.
2424
wl_data_offer_accept(id, serial, "text/uri-list");
2425
wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY);
2426
}
2427
2428
void WaylandThread::_wl_data_device_on_leave(void *data, struct wl_data_device *wl_data_device) {
2429
SeatState *ss = (SeatState *)data;
2430
ERR_FAIL_NULL(ss);
2431
2432
if (ss->wl_data_offer_dnd) {
2433
memdelete(wl_data_offer_get_offer_state(ss->wl_data_offer_dnd));
2434
wl_data_offer_destroy(ss->wl_data_offer_dnd);
2435
ss->wl_data_offer_dnd = nullptr;
2436
ss->dnd_id = DisplayServer::INVALID_WINDOW_ID;
2437
}
2438
}
2439
2440
void WaylandThread::_wl_data_device_on_motion(void *data, struct wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y) {
2441
}
2442
2443
void WaylandThread::_wl_data_device_on_drop(void *data, struct wl_data_device *wl_data_device) {
2444
SeatState *ss = (SeatState *)data;
2445
ERR_FAIL_NULL(ss);
2446
2447
WaylandThread *wayland_thread = ss->wayland_thread;
2448
ERR_FAIL_NULL(wayland_thread);
2449
2450
OfferState *os = wl_data_offer_get_offer_state(ss->wl_data_offer_dnd);
2451
ERR_FAIL_NULL(os);
2452
2453
if (os) {
2454
Ref<DropFilesEventMessage> msg;
2455
msg.instantiate();
2456
msg->id = ss->dnd_id;
2457
2458
Vector<uint8_t> list_data = _wl_data_offer_read(wayland_thread->wl_display, "text/uri-list", ss->wl_data_offer_dnd);
2459
2460
msg->files = String::utf8((const char *)list_data.ptr(), list_data.size()).split("\r\n", false);
2461
for (int i = 0; i < msg->files.size(); i++) {
2462
msg->files.write[i] = msg->files[i].replace("file://", "").uri_file_decode();
2463
}
2464
2465
wayland_thread->push_message(msg);
2466
2467
wl_data_offer_finish(ss->wl_data_offer_dnd);
2468
}
2469
2470
memdelete(wl_data_offer_get_offer_state(ss->wl_data_offer_dnd));
2471
wl_data_offer_destroy(ss->wl_data_offer_dnd);
2472
ss->wl_data_offer_dnd = nullptr;
2473
ss->dnd_id = DisplayServer::INVALID_WINDOW_ID;
2474
}
2475
2476
void WaylandThread::_wl_data_device_on_selection(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id) {
2477
SeatState *ss = (SeatState *)data;
2478
ERR_FAIL_NULL(ss);
2479
2480
if (ss->wl_data_offer_selection) {
2481
memdelete(wl_data_offer_get_offer_state(ss->wl_data_offer_selection));
2482
wl_data_offer_destroy(ss->wl_data_offer_selection);
2483
}
2484
2485
ss->wl_data_offer_selection = id;
2486
}
2487
2488
void WaylandThread::_wl_data_offer_on_offer(void *data, struct wl_data_offer *wl_data_offer, const char *mime_type) {
2489
OfferState *os = (OfferState *)data;
2490
ERR_FAIL_NULL(os);
2491
2492
if (os) {
2493
os->mime_types.insert(String::utf8(mime_type));
2494
}
2495
}
2496
2497
void WaylandThread::_wl_data_offer_on_source_actions(void *data, struct wl_data_offer *wl_data_offer, uint32_t source_actions) {
2498
}
2499
2500
void WaylandThread::_wl_data_offer_on_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action) {
2501
}
2502
2503
void WaylandThread::_wl_data_source_on_target(void *data, struct wl_data_source *wl_data_source, const char *mime_type) {
2504
}
2505
2506
void WaylandThread::_wl_data_source_on_send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, int32_t fd) {
2507
SeatState *ss = (SeatState *)data;
2508
ERR_FAIL_NULL(ss);
2509
2510
Vector<uint8_t> *data_to_send = nullptr;
2511
2512
if (wl_data_source == ss->wl_data_source_selection) {
2513
data_to_send = &ss->selection_data;
2514
DEBUG_LOG_WAYLAND_THREAD("Clipboard: requested selection.");
2515
}
2516
2517
if (data_to_send) {
2518
ssize_t written_bytes = 0;
2519
2520
bool valid_mime = false;
2521
2522
if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
2523
valid_mime = true;
2524
} else if (strcmp(mime_type, "text/plain") == 0) {
2525
valid_mime = true;
2526
}
2527
2528
if (valid_mime) {
2529
written_bytes = write(fd, data_to_send->ptr(), data_to_send->size());
2530
}
2531
2532
if (written_bytes > 0) {
2533
DEBUG_LOG_WAYLAND_THREAD(vformat("Clipboard: sent %d bytes.", written_bytes));
2534
} else if (written_bytes == 0) {
2535
DEBUG_LOG_WAYLAND_THREAD("Clipboard: no bytes sent.");
2536
} else {
2537
ERR_PRINT(vformat("Clipboard: write error %d.", errno));
2538
}
2539
}
2540
2541
close(fd);
2542
}
2543
2544
void WaylandThread::_wl_data_source_on_cancelled(void *data, struct wl_data_source *wl_data_source) {
2545
SeatState *ss = (SeatState *)data;
2546
ERR_FAIL_NULL(ss);
2547
2548
wl_data_source_destroy(wl_data_source);
2549
2550
if (wl_data_source == ss->wl_data_source_selection) {
2551
ss->wl_data_source_selection = nullptr;
2552
ss->selection_data.clear();
2553
2554
DEBUG_LOG_WAYLAND_THREAD("Clipboard: selection set by another program.");
2555
return;
2556
}
2557
}
2558
2559
void WaylandThread::_wl_data_source_on_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source) {
2560
}
2561
2562
void WaylandThread::_wl_data_source_on_dnd_finished(void *data, struct wl_data_source *wl_data_source) {
2563
}
2564
2565
void WaylandThread::_wl_data_source_on_action(void *data, struct wl_data_source *wl_data_source, uint32_t dnd_action) {
2566
}
2567
2568
void WaylandThread::_wp_fractional_scale_on_preferred_scale(void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1, uint32_t scale) {
2569
WindowState *ws = (WindowState *)data;
2570
ERR_FAIL_NULL(ws);
2571
2572
ws->preferred_fractional_scale = (double)scale / 120;
2573
2574
window_state_update_size(ws, ws->rect.size.width, ws->rect.size.height);
2575
}
2576
2577
void WaylandThread::_wp_relative_pointer_on_relative_motion(void *data, struct zwp_relative_pointer_v1 *wp_relative_pointer, uint32_t uptime_hi, uint32_t uptime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) {
2578
SeatState *ss = (SeatState *)data;
2579
ERR_FAIL_NULL(ss);
2580
2581
PointerData &pd = ss->pointer_data_buffer;
2582
2583
pd.relative_motion.x = wl_fixed_to_double(dx);
2584
pd.relative_motion.y = wl_fixed_to_double(dy);
2585
2586
pd.relative_motion_time = uptime_lo;
2587
}
2588
2589
void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) {
2590
SeatState *ss = (SeatState *)data;
2591
ERR_FAIL_NULL(ss);
2592
2593
if (fingers == 2) {
2594
ss->old_pinch_scale = wl_fixed_from_int(1);
2595
ss->active_gesture = Gesture::MAGNIFY;
2596
}
2597
}
2598
2599
void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) {
2600
SeatState *ss = (SeatState *)data;
2601
ERR_FAIL_NULL(ss);
2602
2603
// NOTE: From what I can tell, this and all other pointer gestures are separate
2604
// from the "frame" mechanism of regular pointers. Thus, let's just assume we
2605
// can read from the "committed" state.
2606
const PointerData &pd = ss->pointer_data;
2607
2608
WaylandThread *wayland_thread = ss->wayland_thread;
2609
ERR_FAIL_NULL(wayland_thread);
2610
2611
WindowState *ws = wayland_thread->window_get_state(pd.pointed_id);
2612
ERR_FAIL_NULL(ws);
2613
2614
double win_scale = window_state_get_scale_factor(ws);
2615
2616
if (ss->active_gesture == Gesture::MAGNIFY) {
2617
Ref<InputEventMagnifyGesture> mg;
2618
mg.instantiate();
2619
2620
mg->set_window_id(pd.pointed_id);
2621
2622
if (ws) {
2623
mg->set_window_id(ws->id);
2624
}
2625
2626
// Set all pressed modifiers.
2627
mg->set_shift_pressed(ss->shift_pressed);
2628
mg->set_ctrl_pressed(ss->ctrl_pressed);
2629
mg->set_alt_pressed(ss->alt_pressed);
2630
mg->set_meta_pressed(ss->meta_pressed);
2631
2632
mg->set_position(pd.position * win_scale);
2633
2634
wl_fixed_t scale_delta = scale - ss->old_pinch_scale;
2635
mg->set_factor(1 + wl_fixed_to_double(scale_delta));
2636
2637
Ref<InputEventMessage> magnify_msg;
2638
magnify_msg.instantiate();
2639
magnify_msg->event = mg;
2640
2641
// Since Wayland allows only one gesture at a time and godot instead expects
2642
// both of them, we'll have to create two separate input events: one for
2643
// magnification and one for panning.
2644
2645
Ref<InputEventPanGesture> pg;
2646
pg.instantiate();
2647
2648
// Set all pressed modifiers.
2649
pg->set_shift_pressed(ss->shift_pressed);
2650
pg->set_ctrl_pressed(ss->ctrl_pressed);
2651
pg->set_alt_pressed(ss->alt_pressed);
2652
pg->set_meta_pressed(ss->meta_pressed);
2653
2654
pg->set_position(pd.position * win_scale);
2655
pg->set_delta(Vector2(wl_fixed_to_double(dx), wl_fixed_to_double(dy)));
2656
2657
Ref<InputEventMessage> pan_msg;
2658
pan_msg.instantiate();
2659
pan_msg->event = pg;
2660
2661
wayland_thread->push_message(magnify_msg);
2662
wayland_thread->push_message(pan_msg);
2663
2664
ss->old_pinch_scale = scale;
2665
}
2666
}
2667
2668
void WaylandThread::_wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) {
2669
SeatState *ss = (SeatState *)data;
2670
ERR_FAIL_NULL(ss);
2671
2672
ss->active_gesture = Gesture::NONE;
2673
}
2674
2675
// NOTE: Don't forget to `memfree` the offer's state.
2676
void WaylandThread::_wp_primary_selection_device_on_data_offer(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *offer) {
2677
wl_proxy_tag_godot((struct wl_proxy *)offer);
2678
zwp_primary_selection_offer_v1_add_listener(offer, &wp_primary_selection_offer_listener, memnew(OfferState));
2679
}
2680
2681
void WaylandThread::_wp_primary_selection_device_on_selection(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *id) {
2682
SeatState *ss = (SeatState *)data;
2683
ERR_FAIL_NULL(ss);
2684
2685
if (ss->wp_primary_selection_offer) {
2686
memfree(wp_primary_selection_offer_get_offer_state(ss->wp_primary_selection_offer));
2687
zwp_primary_selection_offer_v1_destroy(ss->wp_primary_selection_offer);
2688
}
2689
2690
ss->wp_primary_selection_offer = id;
2691
}
2692
2693
void WaylandThread::_wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer_v1, const char *mime_type) {
2694
OfferState *os = (OfferState *)data;
2695
ERR_FAIL_NULL(os);
2696
2697
if (os) {
2698
os->mime_types.insert(String::utf8(mime_type));
2699
}
2700
}
2701
2702
void WaylandThread::_wp_primary_selection_source_on_send(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1, const char *mime_type, int32_t fd) {
2703
SeatState *ss = (SeatState *)data;
2704
ERR_FAIL_NULL(ss);
2705
2706
Vector<uint8_t> *data_to_send = nullptr;
2707
2708
if (wp_primary_selection_source_v1 == ss->wp_primary_selection_source) {
2709
data_to_send = &ss->primary_data;
2710
DEBUG_LOG_WAYLAND_THREAD("Clipboard: requested primary selection.");
2711
}
2712
2713
if (data_to_send) {
2714
ssize_t written_bytes = 0;
2715
2716
if (strcmp(mime_type, "text/plain") == 0) {
2717
written_bytes = write(fd, data_to_send->ptr(), data_to_send->size());
2718
}
2719
2720
if (written_bytes > 0) {
2721
DEBUG_LOG_WAYLAND_THREAD(vformat("Clipboard: sent %d bytes.", written_bytes));
2722
} else if (written_bytes == 0) {
2723
DEBUG_LOG_WAYLAND_THREAD("Clipboard: no bytes sent.");
2724
} else {
2725
ERR_PRINT(vformat("Clipboard: write error %d.", errno));
2726
}
2727
}
2728
2729
close(fd);
2730
}
2731
2732
void WaylandThread::_wp_primary_selection_source_on_cancelled(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1) {
2733
SeatState *ss = (SeatState *)data;
2734
ERR_FAIL_NULL(ss);
2735
2736
if (wp_primary_selection_source_v1 == ss->wp_primary_selection_source) {
2737
zwp_primary_selection_source_v1_destroy(ss->wp_primary_selection_source);
2738
ss->wp_primary_selection_source = nullptr;
2739
2740
ss->primary_data.clear();
2741
2742
DEBUG_LOG_WAYLAND_THREAD("Clipboard: primary selection set by another program.");
2743
return;
2744
}
2745
}
2746
2747
void WaylandThread::_wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_v2 *id) {
2748
}
2749
2750
void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id) {
2751
SeatState *ss = (SeatState *)data;
2752
ERR_FAIL_NULL(ss);
2753
2754
TabletToolState *state = memnew(TabletToolState);
2755
state->wl_seat = ss->wl_seat;
2756
2757
wl_proxy_tag_godot((struct wl_proxy *)id);
2758
zwp_tablet_tool_v2_add_listener(id, &wp_tablet_tool_listener, state);
2759
ss->tablet_tools.push_back(id);
2760
}
2761
2762
void WaylandThread::_wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) {
2763
}
2764
2765
void WaylandThread::_wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t tool_type) {
2766
TabletToolState *state = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2767
2768
if (state && tool_type == ZWP_TABLET_TOOL_V2_TYPE_ERASER) {
2769
state->is_eraser = true;
2770
}
2771
}
2772
2773
void WaylandThread::_wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) {
2774
}
2775
2776
void WaylandThread::_wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo) {
2777
}
2778
2779
void WaylandThread::_wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t capability) {
2780
}
2781
2782
void WaylandThread::_wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
2783
}
2784
2785
void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
2786
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2787
if (!ts) {
2788
return;
2789
}
2790
2791
SeatState *ss = wl_seat_get_seat_state(ts->wl_seat);
2792
if (!ss) {
2793
return;
2794
}
2795
2796
List<struct zwp_tablet_tool_v2 *>::Element *E = ss->tablet_tools.find(wp_tablet_tool_v2);
2797
2798
if (E && E->get()) {
2799
struct zwp_tablet_tool_v2 *tool = E->get();
2800
TabletToolState *state = wp_tablet_tool_get_state(tool);
2801
if (state) {
2802
memdelete(state);
2803
}
2804
2805
zwp_tablet_tool_v2_destroy(tool);
2806
ss->tablet_tools.erase(E);
2807
}
2808
}
2809
2810
void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) {
2811
// NOTE: Works pretty much like wl_pointer::enter.
2812
2813
WindowState *ws = wl_surface_get_window_state(surface);
2814
if (!ws) {
2815
return;
2816
}
2817
2818
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2819
ERR_FAIL_NULL(ts);
2820
2821
ts->data_pending.proximity_serial = serial;
2822
ts->data_pending.proximal_id = ws->id;
2823
ts->data_pending.last_proximal_id = ws->id;
2824
2825
DEBUG_LOG_WAYLAND_THREAD(vformat("Tablet tool entered window %d.", ts->data_pending.proximal_id));
2826
}
2827
2828
void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
2829
// NOTE: Works pretty much like wl_pointer::leave.
2830
2831
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2832
ERR_FAIL_NULL(ts);
2833
2834
if (ts->data_pending.proximal_id == DisplayServer::INVALID_WINDOW_ID) {
2835
// We're probably on a decoration or some other third-party thing.
2836
return;
2837
}
2838
2839
DisplayServer::WindowID id = ts->data_pending.proximal_id;
2840
2841
ts->data_pending.proximal_id = DisplayServer::INVALID_WINDOW_ID;
2842
ts->data_pending.pressed_button_mask.clear();
2843
2844
DEBUG_LOG_WAYLAND_THREAD(vformat("Tablet tool left window %d.", id));
2845
}
2846
2847
void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial) {
2848
// NOTE: Works pretty much like wl_pointer::button but only for a pressed left
2849
// button.
2850
2851
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2852
ERR_FAIL_NULL(ts);
2853
2854
TabletToolData &td = ts->data_pending;
2855
2856
td.pressed_button_mask.set_flag(mouse_button_to_mask(MouseButton::LEFT));
2857
td.last_button_pressed = MouseButton::LEFT;
2858
td.double_click_begun = true;
2859
2860
// The protocol doesn't cover this, but we can use this funky hack to make
2861
// double clicking work.
2862
td.button_time = OS::get_singleton()->get_ticks_msec();
2863
}
2864
2865
void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
2866
// NOTE: Works pretty much like wl_pointer::button but only for a released left
2867
// button.
2868
2869
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2870
ERR_FAIL_NULL(ts);
2871
2872
TabletToolData &td = ts->data_pending;
2873
2874
td.pressed_button_mask.clear_flag(mouse_button_to_mask(MouseButton::LEFT));
2875
2876
// The protocol doesn't cover this, but we can use this funky hack to make
2877
// double clicking work.
2878
td.button_time = OS::get_singleton()->get_ticks_msec();
2879
}
2880
2881
void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) {
2882
// NOTE: Works pretty much like wl_pointer::motion.
2883
2884
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2885
ERR_FAIL_NULL(ts);
2886
2887
TabletToolData &td = ts->data_pending;
2888
2889
td.position.x = wl_fixed_to_double(x);
2890
td.position.y = wl_fixed_to_double(y);
2891
}
2892
2893
void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t pressure) {
2894
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2895
ERR_FAIL_NULL(ts);
2896
2897
ts->data_pending.pressure = pressure;
2898
}
2899
2900
void WaylandThread::_wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t distance) {
2901
// Unsupported
2902
}
2903
2904
void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y) {
2905
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2906
ERR_FAIL_NULL(ts);
2907
2908
TabletToolData &td = ts->data_pending;
2909
2910
td.tilt.x = wl_fixed_to_double(tilt_x);
2911
td.tilt.y = wl_fixed_to_double(tilt_y);
2912
}
2913
2914
void WaylandThread::_wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees) {
2915
// Unsupported.
2916
}
2917
2918
void WaylandThread::_wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, int32_t position) {
2919
// Unsupported.
2920
}
2921
2922
void WaylandThread::_wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks) {
2923
// TODO
2924
}
2925
2926
void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state) {
2927
// NOTE: Works pretty much like wl_pointer::button.
2928
2929
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2930
ERR_FAIL_NULL(ts);
2931
2932
TabletToolData &td = ts->data_pending;
2933
2934
MouseButton mouse_button = MouseButton::NONE;
2935
2936
if (button == BTN_STYLUS) {
2937
mouse_button = MouseButton::LEFT;
2938
}
2939
2940
if (button == BTN_STYLUS2) {
2941
mouse_button = MouseButton::RIGHT;
2942
}
2943
2944
if (mouse_button != MouseButton::NONE) {
2945
MouseButtonMask mask = mouse_button_to_mask(mouse_button);
2946
2947
if (state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_PRESSED) {
2948
td.pressed_button_mask.set_flag(mask);
2949
td.last_button_pressed = mouse_button;
2950
td.double_click_begun = true;
2951
} else {
2952
td.pressed_button_mask.clear_flag(mask);
2953
}
2954
2955
// The protocol doesn't cover this, but we can use this funky hack to make
2956
// double clicking work.
2957
td.button_time = OS::get_singleton()->get_ticks_msec();
2958
}
2959
}
2960
2961
void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t time) {
2962
// NOTE: Works pretty much like wl_pointer::frame.
2963
2964
TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
2965
ERR_FAIL_NULL(ts);
2966
2967
SeatState *ss = wl_seat_get_seat_state(ts->wl_seat);
2968
ERR_FAIL_NULL(ss);
2969
2970
WaylandThread *wayland_thread = ss->wayland_thread;
2971
ERR_FAIL_NULL(wayland_thread);
2972
2973
TabletToolData &old_td = ts->data;
2974
TabletToolData &td = ts->data_pending;
2975
2976
if (td.proximal_id != old_td.proximal_id) {
2977
if (old_td.proximal_id != DisplayServer::INVALID_WINDOW_ID) {
2978
Ref<WindowEventMessage> msg;
2979
msg.instantiate();
2980
msg->id = old_td.proximal_id;
2981
msg->event = DisplayServer::WINDOW_EVENT_MOUSE_EXIT;
2982
2983
wayland_thread->push_message(msg);
2984
}
2985
2986
if (td.proximal_id != DisplayServer::INVALID_WINDOW_ID) {
2987
Ref<WindowEventMessage> msg;
2988
msg.instantiate();
2989
msg->id = td.proximal_id;
2990
msg->event = DisplayServer::WINDOW_EVENT_MOUSE_ENTER;
2991
2992
wayland_thread->push_message(msg);
2993
}
2994
}
2995
2996
if (td.proximal_id == DisplayServer::INVALID_WINDOW_ID) {
2997
// We're probably on a decoration or some other third-party thing. Let's
2998
// "commit" the data and call it a day.
2999
old_td = td;
3000
return;
3001
}
3002
3003
WindowState *ws = wayland_thread->window_get_state(td.proximal_id);
3004
ERR_FAIL_NULL(ws);
3005
3006
double scale = window_state_get_scale_factor(ws);
3007
if (old_td.position != td.position || old_td.tilt != td.tilt || old_td.pressure != td.pressure) {
3008
td.motion_time = time;
3009
3010
Ref<InputEventMouseMotion> mm;
3011
mm.instantiate();
3012
3013
mm->set_window_id(td.proximal_id);
3014
3015
// Set all pressed modifiers.
3016
mm->set_shift_pressed(ss->shift_pressed);
3017
mm->set_ctrl_pressed(ss->ctrl_pressed);
3018
mm->set_alt_pressed(ss->alt_pressed);
3019
mm->set_meta_pressed(ss->meta_pressed);
3020
3021
mm->set_button_mask(td.pressed_button_mask);
3022
3023
mm->set_global_position(td.position * scale);
3024
mm->set_position(td.position * scale);
3025
3026
// NOTE: The Godot API expects normalized values and we store them raw,
3027
// straight from the compositor, so we have to normalize them here.
3028
3029
// According to the tablet proto spec, tilt is expressed in degrees relative
3030
// to the Z axis of the tablet, so it shouldn't go over 90 degrees either way,
3031
// I think. We'll clamp it just in case.
3032
td.tilt = td.tilt.clampf(-90, 90);
3033
3034
mm->set_tilt(td.tilt / 90);
3035
3036
// The tablet proto spec explicitly says that pressure is defined as a value
3037
// between 0 to 65535.
3038
mm->set_pressure(td.pressure / (float)65535);
3039
3040
mm->set_pen_inverted(ts->is_eraser);
3041
3042
Vector2 pos_delta = (td.position - old_td.position) * scale;
3043
3044
mm->set_relative(pos_delta);
3045
mm->set_relative_screen_position(pos_delta);
3046
3047
uint32_t time_delta = td.motion_time - old_td.motion_time;
3048
mm->set_velocity((Vector2)pos_delta / time_delta);
3049
3050
Ref<InputEventMessage> inputev_msg;
3051
inputev_msg.instantiate();
3052
3053
inputev_msg->event = mm;
3054
3055
wayland_thread->push_message(inputev_msg);
3056
}
3057
3058
if (old_td.pressed_button_mask != td.pressed_button_mask) {
3059
td.button_time = time;
3060
3061
BitField<MouseButtonMask> pressed_mask_delta = old_td.pressed_button_mask.get_different(td.pressed_button_mask);
3062
3063
for (MouseButton test_button : { MouseButton::LEFT, MouseButton::RIGHT }) {
3064
MouseButtonMask test_button_mask = mouse_button_to_mask(test_button);
3065
3066
if (pressed_mask_delta.has_flag(test_button_mask)) {
3067
Ref<InputEventMouseButton> mb;
3068
mb.instantiate();
3069
3070
// Set all pressed modifiers.
3071
mb->set_shift_pressed(ss->shift_pressed);
3072
mb->set_ctrl_pressed(ss->ctrl_pressed);
3073
mb->set_alt_pressed(ss->alt_pressed);
3074
mb->set_meta_pressed(ss->meta_pressed);
3075
3076
mb->set_window_id(td.proximal_id);
3077
mb->set_position(td.position * scale);
3078
mb->set_global_position(td.position * scale);
3079
3080
mb->set_button_mask(td.pressed_button_mask);
3081
mb->set_button_index(test_button);
3082
mb->set_pressed(td.pressed_button_mask.has_flag(test_button_mask));
3083
3084
// We have to set the last position pressed here as we can't take for
3085
// granted what the individual events might have seen due to them not having
3086
// a garaunteed order.
3087
if (mb->is_pressed()) {
3088
td.last_pressed_position = td.position;
3089
}
3090
3091
if (old_td.double_click_begun && mb->is_pressed() && td.last_button_pressed == old_td.last_button_pressed && (td.button_time - old_td.button_time) < 400 && Vector2(td.last_pressed_position * scale).distance_to(Vector2(old_td.last_pressed_position * scale)) < 5) {
3092
td.double_click_begun = false;
3093
mb->set_double_click(true);
3094
}
3095
3096
Ref<InputEventMessage> msg;
3097
msg.instantiate();
3098
3099
msg->event = mb;
3100
3101
wayland_thread->push_message(msg);
3102
}
3103
}
3104
}
3105
3106
old_td = td;
3107
}
3108
3109
void WaylandThread::_wp_text_input_on_enter(void *data, struct zwp_text_input_v3 *wp_text_input_v3, struct wl_surface *surface) {
3110
SeatState *ss = (SeatState *)data;
3111
if (!ss) {
3112
return;
3113
}
3114
3115
WindowState *ws = wl_surface_get_window_state(surface);
3116
if (!ws) {
3117
return;
3118
}
3119
3120
ss->ime_window_id = ws->id;
3121
ss->ime_enabled = true;
3122
}
3123
3124
// NOTE: From now on, we must ignore all further events until an enter event.
3125
void WaylandThread::_wp_text_input_on_leave(void *data, struct zwp_text_input_v3 *wp_text_input_v3, struct wl_surface *surface) {
3126
SeatState *ss = (SeatState *)data;
3127
if (!ss) {
3128
return;
3129
}
3130
3131
if (ss->ime_window_id == DisplayServer::INVALID_WINDOW_ID) {
3132
return;
3133
}
3134
3135
Ref<IMEUpdateEventMessage> msg;
3136
msg.instantiate();
3137
msg->id = ss->ime_window_id;
3138
msg->text = String();
3139
msg->selection = Vector2i();
3140
ss->wayland_thread->push_message(msg);
3141
3142
ss->ime_window_id = DisplayServer::INVALID_WINDOW_ID;
3143
ss->ime_enabled = false;
3144
ss->ime_active = false;
3145
ss->ime_text = String();
3146
ss->ime_text_commit = String();
3147
ss->ime_cursor = Vector2i();
3148
}
3149
3150
void WaylandThread::_wp_text_input_on_preedit_string(void *data, struct zwp_text_input_v3 *wp_text_input_v3, const char *text, int32_t cursor_begin, int32_t cursor_end) {
3151
SeatState *ss = (SeatState *)data;
3152
if (!ss) {
3153
return;
3154
}
3155
3156
if (ss->ime_window_id == DisplayServer::INVALID_WINDOW_ID) {
3157
return;
3158
}
3159
3160
ss->ime_text = String::utf8(text);
3161
3162
// Convert cursor positions from UTF-8 to UTF-32 offset.
3163
int32_t cursor_begin_utf32 = 0;
3164
int32_t cursor_end_utf32 = 0;
3165
for (int i = 0; i < ss->ime_text.length(); i++) {
3166
uint32_t c = ss->ime_text[i];
3167
if (c <= 0x7f) { // 7 bits.
3168
cursor_begin -= 1;
3169
cursor_end -= 1;
3170
} else if (c <= 0x7ff) { // 11 bits
3171
cursor_begin -= 2;
3172
cursor_end -= 2;
3173
} else if (c <= 0xffff) { // 16 bits
3174
cursor_begin -= 3;
3175
cursor_end -= 3;
3176
} else if (c <= 0x001fffff) { // 21 bits
3177
cursor_begin -= 4;
3178
cursor_end -= 4;
3179
} else if (c <= 0x03ffffff) { // 26 bits
3180
cursor_begin -= 5;
3181
cursor_end -= 5;
3182
} else if (c <= 0x7fffffff) { // 31 bits
3183
cursor_begin -= 6;
3184
cursor_end -= 6;
3185
} else {
3186
cursor_begin -= 1;
3187
cursor_end -= 1;
3188
}
3189
if (cursor_begin == 0) {
3190
cursor_begin_utf32 = i + 1;
3191
}
3192
if (cursor_end == 0) {
3193
cursor_end_utf32 = i + 1;
3194
}
3195
if (cursor_begin <= 0 && cursor_end <= 0) {
3196
break;
3197
}
3198
}
3199
ss->ime_cursor = Vector2i(cursor_begin_utf32, cursor_end_utf32 - cursor_begin_utf32);
3200
}
3201
3202
void WaylandThread::_wp_text_input_on_commit_string(void *data, struct zwp_text_input_v3 *wp_text_input_v3, const char *text) {
3203
SeatState *ss = (SeatState *)data;
3204
if (!ss) {
3205
return;
3206
}
3207
3208
if (ss->ime_window_id == DisplayServer::INVALID_WINDOW_ID) {
3209
return;
3210
}
3211
3212
ss->ime_text_commit = String::utf8(text);
3213
}
3214
3215
void WaylandThread::_wp_text_input_on_delete_surrounding_text(void *data, struct zwp_text_input_v3 *wp_text_input_v3, uint32_t before_length, uint32_t after_length) {
3216
// Not implemented.
3217
}
3218
3219
void WaylandThread::_wp_text_input_on_done(void *data, struct zwp_text_input_v3 *wp_text_input_v3, uint32_t serial) {
3220
SeatState *ss = (SeatState *)data;
3221
if (!ss) {
3222
return;
3223
}
3224
3225
if (ss->ime_window_id == DisplayServer::INVALID_WINDOW_ID) {
3226
return;
3227
}
3228
3229
if (!ss->ime_text_commit.is_empty()) {
3230
Ref<IMECommitEventMessage> msg;
3231
msg.instantiate();
3232
msg->id = ss->ime_window_id;
3233
msg->text = ss->ime_text_commit;
3234
ss->wayland_thread->push_message(msg);
3235
} else {
3236
Ref<IMEUpdateEventMessage> msg;
3237
msg.instantiate();
3238
msg->id = ss->ime_window_id;
3239
msg->text = ss->ime_text;
3240
msg->selection = ss->ime_cursor;
3241
ss->wayland_thread->push_message(msg);
3242
}
3243
3244
ss->ime_text = String();
3245
ss->ime_text_commit = String();
3246
ss->ime_cursor = Vector2i();
3247
}
3248
3249
void WaylandThread::_xdg_activation_token_on_done(void *data, struct xdg_activation_token_v1 *xdg_activation_token, const char *token) {
3250
WindowState *ws = (WindowState *)data;
3251
ERR_FAIL_NULL(ws);
3252
ERR_FAIL_NULL(ws->wayland_thread);
3253
ERR_FAIL_NULL(ws->wl_surface);
3254
3255
xdg_activation_v1_activate(ws->wayland_thread->registry.xdg_activation, token, ws->wl_surface);
3256
xdg_activation_token_v1_destroy(xdg_activation_token);
3257
3258
DEBUG_LOG_WAYLAND_THREAD(vformat("Received activation token and requested window activation."));
3259
}
3260
3261
void WaylandThread::_godot_embedding_compositor_on_client(void *data, struct godot_embedding_compositor *godot_embedding_compositor, struct godot_embedded_client *godot_embedded_client, int32_t pid) {
3262
EmbeddingCompositorState *state = (EmbeddingCompositorState *)data;
3263
ERR_FAIL_NULL(state);
3264
3265
EmbeddedClientState *client_state = memnew(EmbeddedClientState);
3266
client_state->embedding_compositor = godot_embedding_compositor;
3267
client_state->pid = pid;
3268
godot_embedded_client_add_listener(godot_embedded_client, &godot_embedded_client_listener, client_state);
3269
3270
DEBUG_LOG_WAYLAND_THREAD(vformat("New client %d.", pid));
3271
state->clients.push_back(godot_embedded_client);
3272
}
3273
3274
void WaylandThread::_godot_embedded_client_on_disconnected(void *data, struct godot_embedded_client *godot_embedded_client) {
3275
EmbeddedClientState *state = (EmbeddedClientState *)data;
3276
ERR_FAIL_NULL(state);
3277
3278
EmbeddingCompositorState *ecomp_state = godot_embedding_compositor_get_state(state->embedding_compositor);
3279
ERR_FAIL_NULL(ecomp_state);
3280
3281
ecomp_state->clients.erase_unordered(godot_embedded_client);
3282
ecomp_state->mapped_clients.erase(state->pid);
3283
3284
memfree(state);
3285
godot_embedded_client_destroy(godot_embedded_client);
3286
3287
DEBUG_LOG_WAYLAND_THREAD(vformat("Client %d disconnected.", state->pid));
3288
}
3289
3290
void WaylandThread::_godot_embedded_client_on_window_embedded(void *data, struct godot_embedded_client *godot_embedded_client) {
3291
EmbeddedClientState *state = (EmbeddedClientState *)data;
3292
ERR_FAIL_NULL(state);
3293
3294
EmbeddingCompositorState *ecomp_state = godot_embedding_compositor_get_state(state->embedding_compositor);
3295
ERR_FAIL_NULL(ecomp_state);
3296
3297
state->window_mapped = true;
3298
3299
ERR_FAIL_COND_MSG(ecomp_state->mapped_clients.has(state->pid), "More than one Wayland client per PID tried to create a window.");
3300
3301
ecomp_state->mapped_clients[state->pid] = godot_embedded_client;
3302
}
3303
3304
void WaylandThread::_godot_embedded_client_on_window_focus_in(void *data, struct godot_embedded_client *godot_embedded_client) {
3305
EmbeddedClientState *state = (EmbeddedClientState *)data;
3306
ERR_FAIL_NULL(state);
3307
3308
EmbeddingCompositorState *ecomp_state = godot_embedding_compositor_get_state(state->embedding_compositor);
3309
ERR_FAIL_NULL(ecomp_state);
3310
3311
ecomp_state->focused_pid = state->pid;
3312
DEBUG_LOG_WAYLAND_THREAD(vformat("Embedded client pid %d focus in", state->pid));
3313
}
3314
3315
void WaylandThread::_godot_embedded_client_on_window_focus_out(void *data, struct godot_embedded_client *godot_embedded_client) {
3316
EmbeddedClientState *state = (EmbeddedClientState *)data;
3317
ERR_FAIL_NULL(state);
3318
3319
EmbeddingCompositorState *ecomp_state = godot_embedding_compositor_get_state(state->embedding_compositor);
3320
ERR_FAIL_NULL(ecomp_state);
3321
3322
ecomp_state->focused_pid = -1;
3323
DEBUG_LOG_WAYLAND_THREAD(vformat("Embedded client pid %d focus out", state->pid));
3324
}
3325
3326
// NOTE: This must be started after a valid wl_display is loaded.
3327
void WaylandThread::_poll_events_thread(void *p_data) {
3328
Thread::set_name("Wayland Events");
3329
3330
ThreadData *data = (ThreadData *)p_data;
3331
ERR_FAIL_NULL(data);
3332
ERR_FAIL_NULL(data->wl_display);
3333
3334
struct pollfd poll_fd = {};
3335
poll_fd.fd = wl_display_get_fd(data->wl_display);
3336
poll_fd.events = POLLIN;
3337
3338
while (true) {
3339
// Empty the event queue while it's full.
3340
while (wl_display_prepare_read(data->wl_display) != 0) {
3341
// We aren't using wl_display_dispatch(), instead "manually" handling events
3342
// through wl_display_dispatch_pending so that we can use a global mutex and
3343
// be sure that this and the main thread won't race over stuff, as long as
3344
// the main thread locks it too.
3345
//
3346
// Note that the main thread can still call wl_display_roundtrip as that
3347
// method directly handles all events, effectively bypassing this polling
3348
// loop and thus the mutex locking, avoiding a deadlock.
3349
//
3350
// WARNING: Never call `wl_display_roundtrip` inside event handlers or while
3351
// this mutex isn't held! `wl_display_roundtrip` manually handles new events
3352
// and if not properly gated it _will_ cause potentially stall-inducing race
3353
// conditions. Ask me how I know.
3354
MutexLock mutex_lock(data->mutex);
3355
3356
if (wl_display_dispatch_pending(data->wl_display) == -1) {
3357
// Oh no. We'll check and handle any display error below.
3358
break;
3359
}
3360
}
3361
3362
int werror = wl_display_get_error(data->wl_display);
3363
3364
if (werror) {
3365
if (werror == EPROTO) {
3366
struct wl_interface *wl_interface = nullptr;
3367
uint32_t id = 0;
3368
3369
int error_code = wl_display_get_protocol_error(data->wl_display, (const struct wl_interface **)&wl_interface, &id);
3370
CRASH_NOW_MSG(vformat("Wayland protocol error %d on interface %s@%d.", error_code, wl_interface ? wl_interface->name : "unknown", id));
3371
} else {
3372
CRASH_NOW_MSG(vformat("Wayland client error code %d.", werror));
3373
}
3374
}
3375
3376
wl_display_flush(data->wl_display);
3377
3378
// Wait for the event file descriptor to have new data.
3379
poll(&poll_fd, 1, -1);
3380
3381
if (data->thread_done.is_set()) {
3382
wl_display_cancel_read(data->wl_display);
3383
break;
3384
}
3385
3386
if (poll_fd.revents | POLLIN) {
3387
// Load the queues with fresh new data.
3388
wl_display_read_events(data->wl_display);
3389
} else {
3390
// Oh well... Stop signaling that we want to read.
3391
wl_display_cancel_read(data->wl_display);
3392
}
3393
3394
// The docs advise to redispatch unconditionally and it looks like that if we
3395
// don't do this we can't catch protocol errors, which is bad.
3396
MutexLock mutex_lock(data->mutex);
3397
wl_display_dispatch_pending(data->wl_display);
3398
}
3399
}
3400
3401
struct wl_display *WaylandThread::get_wl_display() const {
3402
return wl_display;
3403
}
3404
3405
// NOTE: Stuff like libdecor can (and will) register foreign proxies which
3406
// aren't formatted as we like. This method is needed to detect whether a proxy
3407
// has our tag. Also, be careful! The proxy has to be manually tagged or it
3408
// won't be recognized.
3409
bool WaylandThread::wl_proxy_is_godot(struct wl_proxy *p_proxy) {
3410
ERR_FAIL_NULL_V(p_proxy, false);
3411
3412
return wl_proxy_get_tag(p_proxy) == &proxy_tag;
3413
}
3414
3415
void WaylandThread::wl_proxy_tag_godot(struct wl_proxy *p_proxy) {
3416
ERR_FAIL_NULL(p_proxy);
3417
3418
wl_proxy_set_tag(p_proxy, &proxy_tag);
3419
}
3420
3421
// Returns the wl_surface's `WindowState`, otherwise `nullptr`.
3422
// NOTE: This will fail if the surface isn't tagged as ours.
3423
WaylandThread::WindowState *WaylandThread::wl_surface_get_window_state(struct wl_surface *p_surface) {
3424
if (p_surface && wl_proxy_is_godot((wl_proxy *)p_surface)) {
3425
return (WindowState *)wl_surface_get_user_data(p_surface);
3426
}
3427
3428
return nullptr;
3429
}
3430
3431
// Returns the wl_outputs's `ScreenState`, otherwise `nullptr`.
3432
// NOTE: This will fail if the output isn't tagged as ours.
3433
WaylandThread::ScreenState *WaylandThread::wl_output_get_screen_state(struct wl_output *p_output) {
3434
if (p_output && wl_proxy_is_godot((wl_proxy *)p_output)) {
3435
return (ScreenState *)wl_output_get_user_data(p_output);
3436
}
3437
3438
return nullptr;
3439
}
3440
3441
// Returns the wl_seat's `SeatState`, otherwise `nullptr`.
3442
// NOTE: This will fail if the output isn't tagged as ours.
3443
WaylandThread::SeatState *WaylandThread::wl_seat_get_seat_state(struct wl_seat *p_seat) {
3444
if (p_seat && wl_proxy_is_godot((wl_proxy *)p_seat)) {
3445
return (SeatState *)wl_seat_get_user_data(p_seat);
3446
}
3447
3448
return nullptr;
3449
}
3450
3451
// Returns the wp_tablet_tool's `TabletToolState`, otherwise `nullptr`.
3452
// NOTE: This will fail if the output isn't tagged as ours.
3453
WaylandThread::TabletToolState *WaylandThread::wp_tablet_tool_get_state(struct zwp_tablet_tool_v2 *p_tool) {
3454
if (p_tool && wl_proxy_is_godot((wl_proxy *)p_tool)) {
3455
return (TabletToolState *)zwp_tablet_tool_v2_get_user_data(p_tool);
3456
}
3457
3458
return nullptr;
3459
}
3460
// Returns the wl_data_offer's `OfferState`, otherwise `nullptr`.
3461
// NOTE: This will fail if the output isn't tagged as ours.
3462
WaylandThread::OfferState *WaylandThread::wl_data_offer_get_offer_state(struct wl_data_offer *p_offer) {
3463
if (p_offer && wl_proxy_is_godot((wl_proxy *)p_offer)) {
3464
return (OfferState *)wl_data_offer_get_user_data(p_offer);
3465
}
3466
3467
return nullptr;
3468
}
3469
3470
// Returns the wl_data_offer's `OfferState`, otherwise `nullptr`.
3471
// NOTE: This will fail if the output isn't tagged as ours.
3472
WaylandThread::OfferState *WaylandThread::wp_primary_selection_offer_get_offer_state(struct zwp_primary_selection_offer_v1 *p_offer) {
3473
if (p_offer && wl_proxy_is_godot((wl_proxy *)p_offer)) {
3474
return (OfferState *)zwp_primary_selection_offer_v1_get_user_data(p_offer);
3475
}
3476
3477
return nullptr;
3478
}
3479
3480
WaylandThread::EmbeddingCompositorState *WaylandThread::godot_embedding_compositor_get_state(struct godot_embedding_compositor *p_compositor) {
3481
// NOTE: No need for tag check as it's a "fake" interface - nothing else exposes it.
3482
if (p_compositor) {
3483
return (EmbeddingCompositorState *)godot_embedding_compositor_get_user_data(p_compositor);
3484
}
3485
3486
return nullptr;
3487
}
3488
3489
// This is implemented as a method because this is the simplest way of
3490
// accounting for dynamic output scale changes.
3491
int WaylandThread::window_state_get_preferred_buffer_scale(WindowState *p_ws) {
3492
ERR_FAIL_NULL_V(p_ws, 1);
3493
3494
if (p_ws->preferred_fractional_scale > 0) {
3495
// We're scaling fractionally. Per spec, the buffer scale is always 1.
3496
return 1;
3497
}
3498
3499
if (p_ws->wl_outputs.is_empty()) {
3500
DEBUG_LOG_WAYLAND_THREAD("Window has no output associated, returning buffer scale of 1.");
3501
return 1;
3502
}
3503
3504
// TODO: Cache value?
3505
int max_size = 1;
3506
3507
// ================================ IMPORTANT =================================
3508
// NOTE: Due to a Godot limitation, we can't really rescale the whole UI yet.
3509
// Because of this reason, all platforms have resorted to forcing the highest
3510
// scale possible of a system on any window, despite of what screen it's onto.
3511
// On this backend everything's already in place for dynamic window scale
3512
// handling, but in the meantime we'll just select the biggest _global_ output.
3513
// To restore dynamic scale selection, simply iterate over `p_ws->wl_outputs`
3514
// instead.
3515
for (struct wl_output *wl_output : p_ws->registry->wl_outputs) {
3516
ScreenState *ss = wl_output_get_screen_state(wl_output);
3517
3518
if (ss && ss->pending_data.scale > max_size) {
3519
// NOTE: For some mystical reason, wl_output.done is emitted _after_ windows
3520
// get resized but the scale event gets sent _before_ that. I'm still leaning
3521
// towards the idea that rescaling when a window gets a resolution change is a
3522
// pretty good approach, but this means that we'll have to use the screen data
3523
// before it's "committed".
3524
// FIXME: Use the committed data. Somehow.
3525
max_size = ss->pending_data.scale;
3526
}
3527
}
3528
3529
return max_size;
3530
}
3531
3532
double WaylandThread::window_state_get_scale_factor(const WindowState *p_ws) {
3533
ERR_FAIL_NULL_V(p_ws, 1);
3534
3535
if (p_ws->fractional_scale > 0) {
3536
// The fractional scale amount takes priority.
3537
return p_ws->fractional_scale;
3538
}
3539
3540
return p_ws->buffer_scale;
3541
}
3542
3543
void WaylandThread::window_state_update_size(WindowState *p_ws, int p_width, int p_height) {
3544
ERR_FAIL_NULL(p_ws);
3545
3546
int preferred_buffer_scale = window_state_get_preferred_buffer_scale(p_ws);
3547
bool using_fractional = p_ws->preferred_fractional_scale > 0;
3548
3549
// If neither is true we no-op.
3550
bool scale_changed = false;
3551
bool size_changed = false;
3552
3553
if (p_ws->rect.size.width != p_width || p_ws->rect.size.height != p_height) {
3554
p_ws->rect.size.width = p_width;
3555
p_ws->rect.size.height = p_height;
3556
3557
size_changed = true;
3558
}
3559
3560
if (using_fractional && p_ws->fractional_scale != p_ws->preferred_fractional_scale) {
3561
p_ws->fractional_scale = p_ws->preferred_fractional_scale;
3562
scale_changed = true;
3563
}
3564
3565
if (p_ws->buffer_scale != preferred_buffer_scale) {
3566
// The buffer scale is always important, even if we use frac scaling.
3567
p_ws->buffer_scale = preferred_buffer_scale;
3568
p_ws->buffer_scale_changed = true;
3569
3570
if (!using_fractional) {
3571
// We don't bother updating everything else if it's turned on though.
3572
scale_changed = true;
3573
}
3574
}
3575
3576
if (p_ws->wl_surface) {
3577
if (p_ws->wp_viewport) {
3578
wp_viewport_set_destination(p_ws->wp_viewport, p_width, p_height);
3579
}
3580
3581
if (p_ws->xdg_surface) {
3582
xdg_surface_set_window_geometry(p_ws->xdg_surface, 0, 0, p_width, p_height);
3583
}
3584
}
3585
3586
#ifdef LIBDECOR_ENABLED
3587
if (p_ws->libdecor_frame) {
3588
struct libdecor_state *state = libdecor_state_new(p_width, p_height);
3589
libdecor_frame_commit(p_ws->libdecor_frame, state, p_ws->pending_libdecor_configuration);
3590
libdecor_state_free(state);
3591
p_ws->pending_libdecor_configuration = nullptr;
3592
}
3593
#endif
3594
3595
if (size_changed || scale_changed) {
3596
double win_scale = window_state_get_scale_factor(p_ws);
3597
Size2i scaled_size = scale_vector2i(p_ws->rect.size, win_scale);
3598
3599
if (using_fractional) {
3600
DEBUG_LOG_WAYLAND_THREAD(vformat("Resizing the window from %s to %s (fractional scale x%f).", p_ws->rect.size, scaled_size, p_ws->fractional_scale));
3601
} else {
3602
DEBUG_LOG_WAYLAND_THREAD(vformat("Resizing the window from %s to %s (buffer scale x%d).", p_ws->rect.size, scaled_size, p_ws->buffer_scale));
3603
}
3604
3605
// FIXME: Actually resize the hint instead of centering it.
3606
p_ws->wayland_thread->pointer_set_hint(scaled_size / 2);
3607
3608
Ref<WindowRectMessage> rect_msg;
3609
rect_msg.instantiate();
3610
rect_msg->id = p_ws->id;
3611
rect_msg->rect.position = scale_vector2i(p_ws->rect.position, win_scale);
3612
rect_msg->rect.size = scaled_size;
3613
p_ws->wayland_thread->push_message(rect_msg);
3614
}
3615
3616
if (scale_changed) {
3617
Ref<WindowEventMessage> dpi_msg;
3618
dpi_msg.instantiate();
3619
dpi_msg->id = p_ws->id;
3620
dpi_msg->event = DisplayServer::WINDOW_EVENT_DPI_CHANGE;
3621
p_ws->wayland_thread->push_message(dpi_msg);
3622
}
3623
}
3624
3625
// Scales a vector according to wp_fractional_scale's rules, where coordinates
3626
// must be scaled with away from zero half-rounding.
3627
Vector2i WaylandThread::scale_vector2i(const Vector2i &p_vector, double p_amount) {
3628
// This snippet is tiny, I know, but this is done a lot.
3629
int x = std::round(p_vector.x * p_amount);
3630
int y = std::round(p_vector.y * p_amount);
3631
3632
return Vector2i(x, y);
3633
}
3634
3635
void WaylandThread::seat_state_unlock_pointer(SeatState *p_ss) {
3636
ERR_FAIL_NULL(p_ss);
3637
3638
if (p_ss->wl_pointer == nullptr) {
3639
return;
3640
}
3641
3642
if (p_ss->wp_locked_pointer) {
3643
zwp_locked_pointer_v1_destroy(p_ss->wp_locked_pointer);
3644
p_ss->wp_locked_pointer = nullptr;
3645
}
3646
3647
if (p_ss->wp_confined_pointer) {
3648
zwp_confined_pointer_v1_destroy(p_ss->wp_confined_pointer);
3649
p_ss->wp_confined_pointer = nullptr;
3650
}
3651
}
3652
3653
void WaylandThread::seat_state_lock_pointer(SeatState *p_ss) {
3654
ERR_FAIL_NULL(p_ss);
3655
3656
if (p_ss->wl_pointer == nullptr) {
3657
WARN_PRINT("Can't lock - no pointer?");
3658
return;
3659
}
3660
3661
if (registry.wp_pointer_constraints == nullptr) {
3662
WARN_PRINT("Can't lock - no constraints global.");
3663
return;
3664
}
3665
3666
if (p_ss->wp_locked_pointer == nullptr) {
3667
struct wl_surface *locked_surface = window_get_wl_surface(p_ss->pointer_data.last_pointed_id);
3668
if (locked_surface == nullptr) {
3669
locked_surface = window_get_wl_surface(DisplayServer::MAIN_WINDOW_ID);
3670
}
3671
ERR_FAIL_NULL(locked_surface);
3672
3673
p_ss->wp_locked_pointer = zwp_pointer_constraints_v1_lock_pointer(registry.wp_pointer_constraints, locked_surface, p_ss->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
3674
}
3675
}
3676
3677
void WaylandThread::seat_state_set_hint(SeatState *p_ss, int p_x, int p_y) {
3678
if (p_ss->wp_locked_pointer == nullptr) {
3679
return;
3680
}
3681
3682
zwp_locked_pointer_v1_set_cursor_position_hint(p_ss->wp_locked_pointer, wl_fixed_from_int(p_x), wl_fixed_from_int(p_y));
3683
}
3684
3685
void WaylandThread::seat_state_warp_pointer(SeatState *p_ss, int p_x, int p_y) {
3686
if (registry.wp_pointer_warp == nullptr) {
3687
return;
3688
}
3689
3690
if (p_ss->pointer_data.pointed_id == DisplayServer::INVALID_WINDOW_ID) {
3691
return;
3692
}
3693
3694
struct wl_surface *surface = window_get_wl_surface(p_ss->pointer_data.pointed_id);
3695
ERR_FAIL_NULL(surface);
3696
3697
wp_pointer_warp_v1_warp_pointer(registry.wp_pointer_warp, surface, p_ss->wl_pointer, wl_fixed_from_int(p_x), wl_fixed_from_int(p_y), p_ss->pointer_enter_serial);
3698
}
3699
3700
void WaylandThread::seat_state_confine_pointer(SeatState *p_ss) {
3701
ERR_FAIL_NULL(p_ss);
3702
3703
if (p_ss->wl_pointer == nullptr) {
3704
return;
3705
}
3706
3707
if (registry.wp_pointer_constraints == nullptr) {
3708
return;
3709
}
3710
3711
if (p_ss->wp_confined_pointer == nullptr) {
3712
struct wl_surface *confined_surface = window_get_wl_surface(p_ss->pointer_data.last_pointed_id);
3713
ERR_FAIL_NULL(confined_surface);
3714
3715
p_ss->wp_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(registry.wp_pointer_constraints, confined_surface, p_ss->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
3716
}
3717
}
3718
3719
void WaylandThread::seat_state_update_cursor(SeatState *p_ss) {
3720
ERR_FAIL_NULL(p_ss);
3721
3722
WaylandThread *thread = p_ss->wayland_thread;
3723
ERR_FAIL_NULL(p_ss->wayland_thread);
3724
3725
if (!p_ss->wl_pointer || !p_ss->cursor_surface) {
3726
return;
3727
}
3728
3729
// NOTE: Those values are valid by default and will hide the cursor when
3730
// unchanged.
3731
struct wl_buffer *cursor_buffer = nullptr;
3732
uint32_t hotspot_x = 0;
3733
uint32_t hotspot_y = 0;
3734
int scale = 1;
3735
3736
if (thread->cursor_visible) {
3737
DisplayServer::CursorShape shape = thread->cursor_shape;
3738
3739
struct CustomCursor *custom_cursor = thread->custom_cursors.getptr(shape);
3740
3741
if (custom_cursor) {
3742
cursor_buffer = custom_cursor->wl_buffer;
3743
hotspot_x = custom_cursor->hotspot.x;
3744
hotspot_y = custom_cursor->hotspot.y;
3745
3746
// We can't really reasonably scale custom cursors, so we'll let the
3747
// compositor do it for us (badly).
3748
scale = 1;
3749
} else if (thread->registry.wp_cursor_shape_manager) {
3750
wp_cursor_shape_device_v1_shape wp_shape = thread->standard_cursors[shape];
3751
wp_cursor_shape_device_v1_set_shape(p_ss->wp_cursor_shape_device, p_ss->pointer_enter_serial, wp_shape);
3752
3753
// We should avoid calling the `wl_pointer_set_cursor` at the end of this method.
3754
return;
3755
} else {
3756
struct wl_cursor *wl_cursor = thread->wl_cursors[shape];
3757
3758
if (!wl_cursor) {
3759
return;
3760
}
3761
3762
int frame_idx = 0;
3763
3764
if (wl_cursor->image_count > 1) {
3765
// The cursor is animated.
3766
frame_idx = wl_cursor_frame(wl_cursor, p_ss->cursor_time_ms);
3767
3768
if (!p_ss->cursor_frame_callback) {
3769
// Since it's animated, we'll re-update it the next frame.
3770
p_ss->cursor_frame_callback = wl_surface_frame(p_ss->cursor_surface);
3771
wl_callback_add_listener(p_ss->cursor_frame_callback, &cursor_frame_callback_listener, p_ss);
3772
}
3773
}
3774
3775
struct wl_cursor_image *wl_cursor_image = wl_cursor->images[frame_idx];
3776
3777
scale = thread->cursor_scale;
3778
3779
cursor_buffer = wl_cursor_image_get_buffer(wl_cursor_image);
3780
3781
// As the surface's buffer is scaled (thus the surface is smaller) and the
3782
// hotspot must be expressed in surface-local coordinates, we need to scale
3783
// it down accordingly.
3784
hotspot_x = wl_cursor_image->hotspot_x / scale;
3785
hotspot_y = wl_cursor_image->hotspot_y / scale;
3786
}
3787
}
3788
3789
wl_pointer_set_cursor(p_ss->wl_pointer, p_ss->pointer_enter_serial, p_ss->cursor_surface, hotspot_x, hotspot_y);
3790
wl_surface_set_buffer_scale(p_ss->cursor_surface, scale);
3791
wl_surface_attach(p_ss->cursor_surface, cursor_buffer, 0, 0);
3792
wl_surface_damage_buffer(p_ss->cursor_surface, 0, 0, INT_MAX, INT_MAX);
3793
3794
wl_surface_commit(p_ss->cursor_surface);
3795
}
3796
3797
void WaylandThread::seat_state_echo_keys(SeatState *p_ss) {
3798
ERR_FAIL_NULL(p_ss);
3799
3800
if (p_ss->wl_keyboard == nullptr) {
3801
return;
3802
}
3803
3804
// TODO: Comment and document out properly this block of code.
3805
// In short, this implements key repeating.
3806
if (p_ss->repeat_key_delay_msec && p_ss->repeating_keycode != XKB_KEYCODE_INVALID) {
3807
uint64_t current_ticks = OS::get_singleton()->get_ticks_msec();
3808
uint64_t delayed_start_ticks = p_ss->last_repeat_start_msec + p_ss->repeat_start_delay_msec;
3809
3810
if (p_ss->last_repeat_msec < delayed_start_ticks) {
3811
p_ss->last_repeat_msec = delayed_start_ticks;
3812
}
3813
3814
if (current_ticks >= delayed_start_ticks) {
3815
uint64_t ticks_delta = current_ticks - p_ss->last_repeat_msec;
3816
3817
int keys_amount = (ticks_delta / p_ss->repeat_key_delay_msec);
3818
3819
for (int i = 0; i < keys_amount; i++) {
3820
_seat_state_handle_xkb_keycode(p_ss, p_ss->repeating_keycode, true, true);
3821
}
3822
3823
p_ss->last_repeat_msec += ticks_delta - (ticks_delta % p_ss->repeat_key_delay_msec);
3824
}
3825
}
3826
}
3827
3828
void WaylandThread::push_message(Ref<Message> message) {
3829
messages.push_back(message);
3830
}
3831
3832
bool WaylandThread::has_message() {
3833
return messages.front() != nullptr;
3834
}
3835
3836
Ref<WaylandThread::Message> WaylandThread::pop_message() {
3837
if (messages.front() != nullptr) {
3838
Ref<Message> msg = messages.front()->get();
3839
messages.pop_front();
3840
return msg;
3841
}
3842
3843
// This method should only be called if `has_messages` returns true but if
3844
// that isn't the case we'll just return an invalid `Ref`. After all, due to
3845
// its `InputEvent`-like interface, we still have to dynamically cast and check
3846
// the `Ref`'s validity anyways.
3847
return Ref<Message>();
3848
}
3849
3850
void WaylandThread::window_create(DisplayServer::WindowID p_window_id, const Size2i &p_size, DisplayServer::WindowID p_parent_id) {
3851
ERR_FAIL_COND(windows.has(p_window_id));
3852
WindowState &ws = windows[p_window_id];
3853
3854
ws.id = p_window_id;
3855
3856
ws.registry = &registry;
3857
ws.wayland_thread = this;
3858
3859
ws.rect.size = p_size;
3860
3861
ws.wl_surface = wl_compositor_create_surface(registry.wl_compositor);
3862
wl_proxy_tag_godot((struct wl_proxy *)ws.wl_surface);
3863
wl_surface_add_listener(ws.wl_surface, &wl_surface_listener, &ws);
3864
3865
if (registry.wp_viewporter) {
3866
ws.wp_viewport = wp_viewporter_get_viewport(registry.wp_viewporter, ws.wl_surface);
3867
3868
if (registry.wp_fractional_scale_manager) {
3869
ws.wp_fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale(registry.wp_fractional_scale_manager, ws.wl_surface);
3870
wp_fractional_scale_v1_add_listener(ws.wp_fractional_scale, &wp_fractional_scale_listener, &ws);
3871
}
3872
}
3873
3874
bool decorated = false;
3875
3876
#ifdef LIBDECOR_ENABLED
3877
if (!decorated && libdecor_context) {
3878
ws.libdecor_frame = libdecor_decorate(libdecor_context, ws.wl_surface, (struct libdecor_frame_interface *)&libdecor_frame_interface, &ws);
3879
libdecor_frame_map(ws.libdecor_frame);
3880
3881
if (registry.xdg_toplevel_icon_manager) {
3882
xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel(ws.libdecor_frame);
3883
if (toplevel != nullptr) {
3884
xdg_toplevel_icon_manager_v1_set_icon(registry.xdg_toplevel_icon_manager, toplevel, xdg_icon);
3885
}
3886
}
3887
3888
decorated = true;
3889
}
3890
#endif
3891
3892
if (!decorated) {
3893
// libdecor has failed loading or is disabled, we shall handle xdg_toplevel
3894
// creation and decoration ourselves (and by decorating for now I just mean
3895
// asking for SSDs and hoping for the best).
3896
ws.xdg_surface = xdg_wm_base_get_xdg_surface(registry.xdg_wm_base, ws.wl_surface);
3897
xdg_surface_add_listener(ws.xdg_surface, &xdg_surface_listener, &ws);
3898
3899
ws.xdg_toplevel = xdg_surface_get_toplevel(ws.xdg_surface);
3900
xdg_toplevel_add_listener(ws.xdg_toplevel, &xdg_toplevel_listener, &ws);
3901
3902
if (registry.xdg_decoration_manager) {
3903
ws.xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(registry.xdg_decoration_manager, ws.xdg_toplevel);
3904
zxdg_toplevel_decoration_v1_add_listener(ws.xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, &ws);
3905
3906
decorated = true;
3907
}
3908
3909
if (registry.xdg_toplevel_icon_manager) {
3910
xdg_toplevel_icon_manager_v1_set_icon(registry.xdg_toplevel_icon_manager, ws.xdg_toplevel, xdg_icon);
3911
}
3912
}
3913
3914
if (p_parent_id != DisplayServer::INVALID_WINDOW_ID) {
3915
// NOTE: It's important to set the parent ASAP to avoid misunderstandings with
3916
// the compositor. For example, niri immediately resizes the window to full
3917
// size as soon as it's configured if it's not parented to another toplevel.
3918
window_set_parent(p_window_id, p_parent_id);
3919
}
3920
3921
ws.frame_callback = wl_surface_frame(ws.wl_surface);
3922
wl_callback_add_listener(ws.frame_callback, &frame_wl_callback_listener, &ws);
3923
3924
if (registry.xdg_exporter_v2) {
3925
ws.xdg_exported_v2 = zxdg_exporter_v2_export_toplevel(registry.xdg_exporter_v2, ws.wl_surface);
3926
zxdg_exported_v2_add_listener(ws.xdg_exported_v2, &xdg_exported_v2_listener, &ws);
3927
} else if (registry.xdg_exporter_v1) {
3928
ws.xdg_exported_v1 = zxdg_exporter_v1_export(registry.xdg_exporter_v1, ws.wl_surface);
3929
zxdg_exported_v1_add_listener(ws.xdg_exported_v1, &xdg_exported_v1_listener, &ws);
3930
}
3931
3932
wl_surface_commit(ws.wl_surface);
3933
3934
// Wait for the surface to be configured before continuing.
3935
wl_display_roundtrip(wl_display);
3936
3937
window_state_update_size(&ws, ws.rect.size.width, ws.rect.size.height);
3938
}
3939
3940
void WaylandThread::window_create_popup(DisplayServer::WindowID p_window_id, DisplayServer::WindowID p_parent_id, Rect2i p_rect) {
3941
ERR_FAIL_COND(windows.has(p_window_id));
3942
ERR_FAIL_COND(!windows.has(p_parent_id));
3943
3944
WindowState &ws = windows[p_window_id];
3945
WindowState &parent = windows[p_parent_id];
3946
3947
double parent_scale = window_state_get_scale_factor(&parent);
3948
3949
p_rect.position = scale_vector2i(p_rect.position, 1.0 / parent_scale);
3950
p_rect.size = scale_vector2i(p_rect.size, 1.0 / parent_scale);
3951
3952
// We manually scaled based on the parent. If we don't set the relevant fields,
3953
// the resizing routines will get confused and scale once more.
3954
ws.preferred_fractional_scale = parent.preferred_fractional_scale;
3955
ws.fractional_scale = parent.fractional_scale;
3956
ws.buffer_scale = parent.buffer_scale;
3957
3958
ws.id = p_window_id;
3959
ws.parent_id = p_parent_id;
3960
ws.registry = &registry;
3961
ws.wayland_thread = this;
3962
3963
ws.rect = p_rect;
3964
3965
ws.wl_surface = wl_compositor_create_surface(registry.wl_compositor);
3966
wl_proxy_tag_godot((struct wl_proxy *)ws.wl_surface);
3967
wl_surface_add_listener(ws.wl_surface, &wl_surface_listener, &ws);
3968
3969
if (registry.wp_viewporter) {
3970
ws.wp_viewport = wp_viewporter_get_viewport(registry.wp_viewporter, ws.wl_surface);
3971
3972
if (registry.wp_fractional_scale_manager) {
3973
ws.wp_fractional_scale = wp_fractional_scale_manager_v1_get_fractional_scale(registry.wp_fractional_scale_manager, ws.wl_surface);
3974
wp_fractional_scale_v1_add_listener(ws.wp_fractional_scale, &wp_fractional_scale_listener, &ws);
3975
}
3976
}
3977
3978
ws.xdg_surface = xdg_wm_base_get_xdg_surface(registry.xdg_wm_base, ws.wl_surface);
3979
xdg_surface_add_listener(ws.xdg_surface, &xdg_surface_listener, &ws);
3980
3981
Rect2i positioner_rect;
3982
positioner_rect.size = parent.rect.size;
3983
struct xdg_surface *parent_xdg_surface = parent.xdg_surface;
3984
3985
Point2i offset = ws.rect.position - parent.rect.position;
3986
3987
#ifdef LIBDECOR_ENABLED
3988
if (!parent_xdg_surface && parent.libdecor_frame) {
3989
parent_xdg_surface = libdecor_frame_get_xdg_surface(parent.libdecor_frame);
3990
3991
int corner_x = 0;
3992
int corner_y = 0;
3993
libdecor_frame_translate_coordinate(parent.libdecor_frame, 0, 0, &corner_x, &corner_y);
3994
3995
positioner_rect.position.x = corner_x;
3996
positioner_rect.position.y = corner_y;
3997
3998
positioner_rect.size.width -= corner_x;
3999
positioner_rect.size.height -= corner_y;
4000
}
4001
#endif
4002
4003
ERR_FAIL_NULL(parent_xdg_surface);
4004
4005
struct xdg_positioner *xdg_positioner = xdg_wm_base_create_positioner(registry.xdg_wm_base);
4006
xdg_positioner_set_size(xdg_positioner, ws.rect.size.width, ws.rect.size.height);
4007
xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
4008
xdg_positioner_set_gravity(xdg_positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
4009
xdg_positioner_set_constraint_adjustment(xdg_positioner, XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y);
4010
xdg_positioner_set_anchor_rect(xdg_positioner, positioner_rect.position.x, positioner_rect.position.y, positioner_rect.size.width, positioner_rect.size.height);
4011
xdg_positioner_set_offset(xdg_positioner, offset.x, offset.y);
4012
4013
ws.xdg_popup = xdg_surface_get_popup(ws.xdg_surface, parent_xdg_surface, xdg_positioner);
4014
xdg_popup_add_listener(ws.xdg_popup, &xdg_popup_listener, &ws);
4015
4016
xdg_positioner_destroy(xdg_positioner);
4017
4018
ws.frame_callback = wl_surface_frame(ws.wl_surface);
4019
wl_callback_add_listener(ws.frame_callback, &frame_wl_callback_listener, &ws);
4020
4021
wl_surface_commit(ws.wl_surface);
4022
4023
// Wait for the surface to be configured before continuing.
4024
wl_display_roundtrip(wl_display);
4025
}
4026
4027
void WaylandThread::window_destroy(DisplayServer::WindowID p_window_id) {
4028
ERR_FAIL_COND(!windows.has(p_window_id));
4029
WindowState &ws = windows[p_window_id];
4030
4031
if (ws.xdg_popup) {
4032
xdg_popup_destroy(ws.xdg_popup);
4033
}
4034
4035
if (ws.xdg_toplevel_decoration) {
4036
zxdg_toplevel_decoration_v1_destroy(ws.xdg_toplevel_decoration);
4037
}
4038
4039
if (ws.xdg_toplevel) {
4040
xdg_toplevel_destroy(ws.xdg_toplevel);
4041
}
4042
4043
#ifdef LIBDECOR_ENABLED
4044
if (ws.libdecor_frame) {
4045
libdecor_frame_unref(ws.libdecor_frame);
4046
}
4047
#endif // LIBDECOR_ENABLED
4048
4049
if (ws.wp_fractional_scale) {
4050
wp_fractional_scale_v1_destroy(ws.wp_fractional_scale);
4051
}
4052
4053
if (ws.wp_viewport) {
4054
wp_viewport_destroy(ws.wp_viewport);
4055
}
4056
4057
if (ws.frame_callback) {
4058
wl_callback_destroy(ws.frame_callback);
4059
}
4060
4061
if (ws.xdg_surface) {
4062
xdg_surface_destroy(ws.xdg_surface);
4063
}
4064
4065
if (ws.wl_surface) {
4066
wl_surface_destroy(ws.wl_surface);
4067
}
4068
4069
// Before continuing, let's handle any leftover event that might still refer to
4070
// this window.
4071
wl_display_roundtrip(wl_display);
4072
4073
// We can already clean up here, we're done.
4074
windows.erase(p_window_id);
4075
}
4076
4077
struct wl_surface *WaylandThread::window_get_wl_surface(DisplayServer::WindowID p_window_id) const {
4078
const WindowState *ws = windows.getptr(p_window_id);
4079
if (ws) {
4080
return ws->wl_surface;
4081
}
4082
4083
return nullptr;
4084
}
4085
4086
WaylandThread::WindowState *WaylandThread::window_get_state(DisplayServer::WindowID p_window_id) {
4087
return windows.getptr(p_window_id);
4088
}
4089
4090
const WaylandThread::WindowState *WaylandThread::window_get_state(DisplayServer::WindowID p_window_id) const {
4091
return windows.getptr(p_window_id);
4092
}
4093
4094
Size2i WaylandThread::window_set_size(DisplayServer::WindowID p_window_id, const Size2i &p_size) {
4095
ERR_FAIL_COND_V(!windows.has(p_window_id), p_size);
4096
WindowState &ws = windows[p_window_id];
4097
4098
double window_scale = window_state_get_scale_factor(&ws);
4099
4100
if (ws.maximized) {
4101
// Can't do anything.
4102
return scale_vector2i(ws.rect.size, window_scale);
4103
}
4104
4105
Size2i new_size = scale_vector2i(p_size, 1 / window_scale);
4106
4107
if (ws.tiled_left && ws.tiled_right) {
4108
// Tiled left and right, we shouldn't change from our current width or else
4109
// it'll look wonky.
4110
new_size.width = ws.rect.size.width;
4111
}
4112
4113
if (ws.tiled_top && ws.tiled_bottom) {
4114
// Tiled top and bottom. Same as above, but for the height.
4115
new_size.height = ws.rect.size.height;
4116
}
4117
4118
if (ws.resizing && ws.rect.size.width > 0 && ws.rect.size.height > 0) {
4119
// The spec says that we shall not resize further than the config size. We can
4120
// resize less than that though.
4121
new_size = new_size.min(ws.rect.size);
4122
}
4123
4124
// NOTE: Older versions of libdecor (~2022) do not have a way to get the max
4125
// content size. Let's also check for its pointer so that we can preserve
4126
// compatibility with older distros.
4127
if (ws.libdecor_frame && libdecor_frame_get_max_content_size) {
4128
int max_width = new_size.width;
4129
int max_height = new_size.height;
4130
4131
// NOTE: Max content size is dynamic on libdecor, as plugins can override it
4132
// to accommodate their decorations.
4133
libdecor_frame_get_max_content_size(ws.libdecor_frame, &max_width, &max_height);
4134
4135
if (max_width > 0 && max_height > 0) {
4136
new_size.width = MIN(new_size.width, max_width);
4137
new_size.height = MIN(new_size.height, max_height);
4138
}
4139
}
4140
4141
window_state_update_size(&ws, new_size.width, new_size.height);
4142
4143
return scale_vector2i(new_size, window_scale);
4144
}
4145
4146
void WaylandThread::beep() const {
4147
if (registry.xdg_system_bell) {
4148
xdg_system_bell_v1_ring(registry.xdg_system_bell, nullptr);
4149
}
4150
}
4151
4152
void WaylandThread::window_start_drag(DisplayServer::WindowID p_window_id) {
4153
ERR_FAIL_COND(!windows.has(p_window_id));
4154
WindowState &ws = windows[p_window_id];
4155
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
4156
4157
if (ss && ws.xdg_toplevel) {
4158
xdg_toplevel_move(ws.xdg_toplevel, ss->wl_seat, ss->pointer_data.button_serial);
4159
}
4160
4161
#ifdef LIBDECOR_ENABLED
4162
if (ws.libdecor_frame) {
4163
libdecor_frame_move(ws.libdecor_frame, ss->wl_seat, ss->pointer_data.button_serial);
4164
}
4165
#endif
4166
}
4167
4168
void WaylandThread::window_start_resize(DisplayServer::WindowResizeEdge p_edge, DisplayServer::WindowID p_window) {
4169
ERR_FAIL_COND(!windows.has(p_window));
4170
WindowState &ws = windows[p_window];
4171
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
4172
4173
if (ss && ws.xdg_toplevel) {
4174
xdg_toplevel_resize_edge edge = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
4175
switch (p_edge) {
4176
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
4177
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
4178
} break;
4179
case DisplayServer::WINDOW_EDGE_TOP: {
4180
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
4181
} break;
4182
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
4183
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
4184
} break;
4185
case DisplayServer::WINDOW_EDGE_LEFT: {
4186
edge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
4187
} break;
4188
case DisplayServer::WINDOW_EDGE_RIGHT: {
4189
edge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
4190
} break;
4191
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
4192
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
4193
} break;
4194
case DisplayServer::WINDOW_EDGE_BOTTOM: {
4195
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
4196
} break;
4197
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
4198
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
4199
} break;
4200
default:
4201
break;
4202
}
4203
xdg_toplevel_resize(ws.xdg_toplevel, ss->wl_seat, ss->pointer_data.button_serial, edge);
4204
}
4205
4206
#ifdef LIBDECOR_ENABLED
4207
if (ws.libdecor_frame) {
4208
libdecor_resize_edge edge = LIBDECOR_RESIZE_EDGE_NONE;
4209
switch (p_edge) {
4210
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
4211
edge = LIBDECOR_RESIZE_EDGE_TOP_LEFT;
4212
} break;
4213
case DisplayServer::WINDOW_EDGE_TOP: {
4214
edge = LIBDECOR_RESIZE_EDGE_TOP;
4215
} break;
4216
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
4217
edge = LIBDECOR_RESIZE_EDGE_TOP_RIGHT;
4218
} break;
4219
case DisplayServer::WINDOW_EDGE_LEFT: {
4220
edge = LIBDECOR_RESIZE_EDGE_LEFT;
4221
} break;
4222
case DisplayServer::WINDOW_EDGE_RIGHT: {
4223
edge = LIBDECOR_RESIZE_EDGE_RIGHT;
4224
} break;
4225
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
4226
edge = LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT;
4227
} break;
4228
case DisplayServer::WINDOW_EDGE_BOTTOM: {
4229
edge = LIBDECOR_RESIZE_EDGE_BOTTOM;
4230
} break;
4231
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
4232
edge = LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT;
4233
} break;
4234
default:
4235
break;
4236
}
4237
libdecor_frame_resize(ws.libdecor_frame, ss->wl_seat, ss->pointer_data.button_serial, edge);
4238
}
4239
#endif
4240
}
4241
4242
void WaylandThread::window_set_parent(DisplayServer::WindowID p_window_id, DisplayServer::WindowID p_parent_id) {
4243
ERR_FAIL_COND(!windows.has(p_window_id));
4244
ERR_FAIL_COND(!windows.has(p_parent_id));
4245
4246
WindowState &child = windows[p_window_id];
4247
child.parent_id = p_parent_id;
4248
4249
WindowState &parent = windows[p_parent_id];
4250
4251
// NOTE: We can't really unparent as, at the time of writing, libdecor
4252
// segfaults when trying to set a null parent. Hopefully unparenting is not
4253
// that common. Bummer.
4254
4255
#ifdef LIBDECOR_ENABLED
4256
if (child.libdecor_frame && parent.libdecor_frame) {
4257
libdecor_frame_set_parent(child.libdecor_frame, parent.libdecor_frame);
4258
return;
4259
}
4260
#endif
4261
4262
if (child.xdg_toplevel && parent.xdg_toplevel) {
4263
xdg_toplevel_set_parent(child.xdg_toplevel, parent.xdg_toplevel);
4264
}
4265
}
4266
4267
void WaylandThread::window_set_max_size(DisplayServer::WindowID p_window_id, const Size2i &p_size) {
4268
ERR_FAIL_COND(!windows.has(p_window_id));
4269
WindowState &ws = windows[p_window_id];
4270
4271
Vector2i logical_max_size = scale_vector2i(p_size, 1 / window_state_get_scale_factor(&ws));
4272
4273
if (ws.wl_surface && ws.xdg_toplevel) {
4274
xdg_toplevel_set_max_size(ws.xdg_toplevel, logical_max_size.width, logical_max_size.height);
4275
}
4276
4277
#ifdef LIBDECOR_ENABLED
4278
if (ws.libdecor_frame) {
4279
libdecor_frame_set_max_content_size(ws.libdecor_frame, logical_max_size.width, logical_max_size.height);
4280
}
4281
4282
// FIXME: I'm not sure whether we have to commit the surface for this to apply.
4283
#endif
4284
}
4285
4286
void WaylandThread::window_set_min_size(DisplayServer::WindowID p_window_id, const Size2i &p_size) {
4287
ERR_FAIL_COND(!windows.has(p_window_id));
4288
WindowState &ws = windows[p_window_id];
4289
4290
Size2i logical_min_size = scale_vector2i(p_size, 1 / window_state_get_scale_factor(&ws));
4291
4292
if (ws.wl_surface && ws.xdg_toplevel) {
4293
xdg_toplevel_set_min_size(ws.xdg_toplevel, logical_min_size.width, logical_min_size.height);
4294
}
4295
4296
#ifdef LIBDECOR_ENABLED
4297
if (ws.libdecor_frame) {
4298
libdecor_frame_set_min_content_size(ws.libdecor_frame, logical_min_size.width, logical_min_size.height);
4299
}
4300
4301
// FIXME: I'm not sure whether we have to commit the surface for this to apply.
4302
#endif
4303
}
4304
4305
bool WaylandThread::window_can_set_mode(DisplayServer::WindowID p_window_id, DisplayServer::WindowMode p_window_mode) const {
4306
ERR_FAIL_COND_V(!windows.has(p_window_id), false);
4307
const WindowState &ws = windows[p_window_id];
4308
4309
switch (p_window_mode) {
4310
case DisplayServer::WINDOW_MODE_WINDOWED: {
4311
// Looks like it's guaranteed.
4312
return true;
4313
};
4314
4315
case DisplayServer::WINDOW_MODE_MINIMIZED: {
4316
#ifdef LIBDECOR_ENABLED
4317
if (ws.libdecor_frame) {
4318
return libdecor_frame_has_capability(ws.libdecor_frame, LIBDECOR_ACTION_MINIMIZE);
4319
}
4320
#endif // LIBDECOR_ENABLED
4321
4322
return ws.can_minimize;
4323
};
4324
4325
case DisplayServer::WINDOW_MODE_MAXIMIZED: {
4326
if (ws.libdecor_frame) {
4327
// NOTE: libdecor doesn't seem to have a maximize capability query?
4328
// The fact that there's a fullscreen one makes me suspicious. Anyways,
4329
// let's act as if we always can.
4330
return true;
4331
}
4332
return ws.can_maximize;
4333
};
4334
4335
case DisplayServer::WINDOW_MODE_FULLSCREEN:
4336
case DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN: {
4337
#ifdef LIBDECOR_ENABLED
4338
if (ws.libdecor_frame) {
4339
return libdecor_frame_has_capability(ws.libdecor_frame, LIBDECOR_ACTION_FULLSCREEN);
4340
}
4341
#endif // LIBDECOR_ENABLED
4342
4343
return ws.can_fullscreen;
4344
};
4345
}
4346
4347
return false;
4348
}
4349
4350
void WaylandThread::window_try_set_mode(DisplayServer::WindowID p_window_id, DisplayServer::WindowMode p_window_mode) {
4351
ERR_FAIL_COND(!windows.has(p_window_id));
4352
WindowState &ws = windows[p_window_id];
4353
4354
if (ws.mode == p_window_mode) {
4355
return;
4356
}
4357
4358
// Don't waste time with hidden windows and whatnot. Behave like it worked.
4359
#ifdef LIBDECOR_ENABLED
4360
if ((!ws.wl_surface || !ws.xdg_toplevel) && !ws.libdecor_frame) {
4361
#else
4362
if (!ws.wl_surface || !ws.xdg_toplevel) {
4363
#endif // LIBDECOR_ENABLED
4364
ws.mode = p_window_mode;
4365
return;
4366
}
4367
4368
// Return back to a windowed state so that we can apply what the user asked.
4369
switch (ws.mode) {
4370
case DisplayServer::WINDOW_MODE_WINDOWED: {
4371
// Do nothing.
4372
} break;
4373
4374
case DisplayServer::WINDOW_MODE_MINIMIZED: {
4375
// We can't do much according to the xdg_shell protocol. I have no idea
4376
// whether this implies that we should return or who knows what. For now
4377
// we'll do nothing.
4378
// TODO: Test this properly.
4379
} break;
4380
4381
case DisplayServer::WINDOW_MODE_MAXIMIZED: {
4382
// Try to unmaximize. This isn't garaunteed to work actually, so we'll have
4383
// to check whether something changed.
4384
if (ws.xdg_toplevel) {
4385
xdg_toplevel_unset_maximized(ws.xdg_toplevel);
4386
}
4387
4388
#ifdef LIBDECOR_ENABLED
4389
if (ws.libdecor_frame) {
4390
libdecor_frame_unset_maximized(ws.libdecor_frame);
4391
}
4392
#endif // LIBDECOR_ENABLED
4393
} break;
4394
4395
case DisplayServer::WINDOW_MODE_FULLSCREEN:
4396
case DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN: {
4397
// Same thing as above, unset fullscreen and check later if it worked.
4398
if (ws.xdg_toplevel) {
4399
xdg_toplevel_unset_fullscreen(ws.xdg_toplevel);
4400
}
4401
4402
#ifdef LIBDECOR_ENABLED
4403
if (ws.libdecor_frame) {
4404
libdecor_frame_unset_fullscreen(ws.libdecor_frame);
4405
}
4406
#endif // LIBDECOR_ENABLED
4407
} break;
4408
}
4409
4410
// Wait for a configure event and hope that something changed.
4411
wl_display_roundtrip(wl_display);
4412
4413
if (ws.mode != DisplayServer::WINDOW_MODE_WINDOWED) {
4414
// The compositor refused our "normalization" request. It'd be useless or
4415
// unpredictable to attempt setting a new state. We're done.
4416
return;
4417
}
4418
4419
// Ask the compositor to set the state indicated by the new mode.
4420
switch (p_window_mode) {
4421
case DisplayServer::WINDOW_MODE_WINDOWED: {
4422
// Do nothing. We're already windowed.
4423
} break;
4424
4425
case DisplayServer::WINDOW_MODE_MINIMIZED: {
4426
if (!window_can_set_mode(p_window_id, p_window_mode)) {
4427
// Minimization is special (read below). Better not mess with it if the
4428
// compositor explicitly announces that it doesn't support it.
4429
break;
4430
}
4431
4432
if (ws.xdg_toplevel) {
4433
xdg_toplevel_set_minimized(ws.xdg_toplevel);
4434
}
4435
4436
#ifdef LIBDECOR_ENABLED
4437
if (ws.libdecor_frame) {
4438
libdecor_frame_set_minimized(ws.libdecor_frame);
4439
}
4440
#endif // LIBDECOR_ENABLED
4441
// We have no way to actually detect this state, so we'll have to report it
4442
// manually to the engine (hoping that it worked). In the worst case it'll
4443
// get reset by the next configure event.
4444
ws.mode = DisplayServer::WINDOW_MODE_MINIMIZED;
4445
} break;
4446
4447
case DisplayServer::WINDOW_MODE_MAXIMIZED: {
4448
if (ws.xdg_toplevel) {
4449
xdg_toplevel_set_maximized(ws.xdg_toplevel);
4450
}
4451
4452
#ifdef LIBDECOR_ENABLED
4453
if (ws.libdecor_frame) {
4454
libdecor_frame_set_maximized(ws.libdecor_frame);
4455
}
4456
#endif // LIBDECOR_ENABLED
4457
} break;
4458
4459
case DisplayServer::WINDOW_MODE_FULLSCREEN:
4460
case DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN: {
4461
if (ws.xdg_toplevel) {
4462
xdg_toplevel_set_fullscreen(ws.xdg_toplevel, nullptr);
4463
}
4464
4465
#ifdef LIBDECOR_ENABLED
4466
if (ws.libdecor_frame) {
4467
libdecor_frame_set_fullscreen(ws.libdecor_frame, nullptr);
4468
}
4469
#endif // LIBDECOR_ENABLED
4470
} break;
4471
4472
default: {
4473
} break;
4474
}
4475
}
4476
4477
void WaylandThread::window_set_borderless(DisplayServer::WindowID p_window_id, bool p_borderless) {
4478
ERR_FAIL_COND(!windows.has(p_window_id));
4479
WindowState &ws = windows[p_window_id];
4480
4481
if (ws.xdg_toplevel_decoration) {
4482
if (p_borderless) {
4483
// We implement borderless windows by simply asking the compositor to let
4484
// us handle decorations (we don't).
4485
zxdg_toplevel_decoration_v1_set_mode(ws.xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
4486
} else {
4487
zxdg_toplevel_decoration_v1_set_mode(ws.xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
4488
}
4489
}
4490
4491
#ifdef LIBDECOR_ENABLED
4492
if (ws.libdecor_frame) {
4493
bool visible_current = libdecor_frame_is_visible(ws.libdecor_frame);
4494
bool visible_target = !p_borderless;
4495
4496
// NOTE: We have to do this otherwise we trip on a libdecor bug where it's
4497
// possible to destroy the frame more than once, by setting the visibility
4498
// to false multiple times and thus crashing.
4499
if (visible_current != visible_target) {
4500
print_verbose(vformat("Setting libdecor frame visibility to %s", visible_target));
4501
libdecor_frame_set_visibility(ws.libdecor_frame, visible_target);
4502
}
4503
}
4504
#endif // LIBDECOR_ENABLED
4505
}
4506
4507
void WaylandThread::window_set_title(DisplayServer::WindowID p_window_id, const String &p_title) {
4508
ERR_FAIL_COND(!windows.has(p_window_id));
4509
WindowState &ws = windows[p_window_id];
4510
4511
#ifdef LIBDECOR_ENABLED
4512
if (ws.libdecor_frame) {
4513
libdecor_frame_set_title(ws.libdecor_frame, p_title.utf8().get_data());
4514
}
4515
#endif // LIBDECOR_ENABLE
4516
4517
if (ws.xdg_toplevel) {
4518
xdg_toplevel_set_title(ws.xdg_toplevel, p_title.utf8().get_data());
4519
}
4520
}
4521
4522
void WaylandThread::window_set_app_id(DisplayServer::WindowID p_window_id, const String &p_app_id) {
4523
ERR_FAIL_COND(!windows.has(p_window_id));
4524
WindowState &ws = windows[p_window_id];
4525
4526
#ifdef LIBDECOR_ENABLED
4527
if (ws.libdecor_frame) {
4528
libdecor_frame_set_app_id(ws.libdecor_frame, p_app_id.utf8().get_data());
4529
return;
4530
}
4531
#endif // LIBDECOR_ENABLED
4532
4533
if (ws.xdg_toplevel) {
4534
xdg_toplevel_set_app_id(ws.xdg_toplevel, p_app_id.utf8().get_data());
4535
return;
4536
}
4537
}
4538
4539
void WaylandThread::set_icon(const Ref<Image> &p_icon) {
4540
ERR_FAIL_COND(p_icon.is_null());
4541
4542
Size2i icon_size = p_icon->get_size();
4543
ERR_FAIL_COND(icon_size.width != icon_size.height);
4544
4545
if (!registry.xdg_toplevel_icon_manager) {
4546
return;
4547
}
4548
4549
if (xdg_icon) {
4550
xdg_toplevel_icon_v1_destroy(xdg_icon);
4551
}
4552
4553
if (icon_buffer) {
4554
wl_buffer_destroy(icon_buffer);
4555
}
4556
4557
// NOTE: The stride is the width of the icon in bytes.
4558
uint32_t icon_stride = icon_size.width * 4;
4559
uint32_t data_size = icon_stride * icon_size.height;
4560
4561
// We need a shared memory object file descriptor in order to create a
4562
// wl_buffer through wl_shm.
4563
int fd = WaylandThread::_allocate_shm_file(data_size);
4564
ERR_FAIL_COND(fd == -1);
4565
4566
uint32_t *buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
4567
4568
// Create the Wayland buffer.
4569
struct wl_shm_pool *shm_pool = wl_shm_create_pool(registry.wl_shm, fd, data_size);
4570
icon_buffer = wl_shm_pool_create_buffer(shm_pool, 0, icon_size.width, icon_size.height, icon_stride, WL_SHM_FORMAT_ARGB8888);
4571
wl_shm_pool_destroy(shm_pool);
4572
4573
// Fill the cursor buffer with the image data.
4574
for (uint32_t index = 0; index < (uint32_t)(icon_size.width * icon_size.height); index++) {
4575
int row_index = index / icon_size.width;
4576
int column_index = (index % icon_size.width);
4577
4578
buffer_data[index] = p_icon->get_pixel(column_index, row_index).to_argb32();
4579
4580
// Wayland buffers, unless specified, require associated alpha, so we'll just
4581
// associate the alpha in-place.
4582
uint8_t *pixel_data = (uint8_t *)&buffer_data[index];
4583
pixel_data[0] = pixel_data[0] * pixel_data[3] / 255;
4584
pixel_data[1] = pixel_data[1] * pixel_data[3] / 255;
4585
pixel_data[2] = pixel_data[2] * pixel_data[3] / 255;
4586
}
4587
4588
xdg_icon = xdg_toplevel_icon_manager_v1_create_icon(registry.xdg_toplevel_icon_manager);
4589
xdg_toplevel_icon_v1_add_buffer(xdg_icon, icon_buffer, icon_size.width);
4590
4591
if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
4592
// Setting a name allows the godot icon to be overridden by a system theme.
4593
// We only want the project manager and editor to get themed,
4594
// Games will get icons with the protocol and themed icons with .desktop entries.
4595
// NOTE: should be synced with the icon name in misc/dist/linuxbsd/Godot.desktop
4596
xdg_toplevel_icon_v1_set_name(xdg_icon, "godot");
4597
}
4598
4599
for (KeyValue<DisplayServer::WindowID, WindowState> &pair : windows) {
4600
WindowState &ws = pair.value;
4601
#ifdef LIBDECOR_ENABLED
4602
if (ws.libdecor_frame) {
4603
xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel(ws.libdecor_frame);
4604
ERR_FAIL_NULL(toplevel);
4605
xdg_toplevel_icon_manager_v1_set_icon(registry.xdg_toplevel_icon_manager, toplevel, xdg_icon);
4606
}
4607
#endif
4608
if (ws.xdg_toplevel) {
4609
xdg_toplevel_icon_manager_v1_set_icon(registry.xdg_toplevel_icon_manager, ws.xdg_toplevel, xdg_icon);
4610
}
4611
}
4612
}
4613
4614
DisplayServer::WindowMode WaylandThread::window_get_mode(DisplayServer::WindowID p_window_id) const {
4615
ERR_FAIL_COND_V(!windows.has(p_window_id), DisplayServer::WINDOW_MODE_WINDOWED);
4616
const WindowState &ws = windows[p_window_id];
4617
4618
return ws.mode;
4619
}
4620
4621
void WaylandThread::window_request_attention(DisplayServer::WindowID p_window_id) {
4622
ERR_FAIL_COND(!windows.has(p_window_id));
4623
WindowState &ws = windows[p_window_id];
4624
4625
if (registry.xdg_activation) {
4626
// Window attention requests are done through the XDG activation protocol.
4627
xdg_activation_token_v1 *xdg_activation_token = xdg_activation_v1_get_activation_token(registry.xdg_activation);
4628
xdg_activation_token_v1_add_listener(xdg_activation_token, &xdg_activation_token_listener, &ws);
4629
xdg_activation_token_v1_commit(xdg_activation_token);
4630
}
4631
}
4632
4633
void WaylandThread::window_set_idle_inhibition(DisplayServer::WindowID p_window_id, bool p_enable) {
4634
ERR_FAIL_COND(!windows.has(p_window_id));
4635
WindowState &ws = windows[p_window_id];
4636
4637
if (p_enable) {
4638
if (ws.registry->wp_idle_inhibit_manager && !ws.wp_idle_inhibitor) {
4639
ERR_FAIL_NULL(ws.wl_surface);
4640
ws.wp_idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(ws.registry->wp_idle_inhibit_manager, ws.wl_surface);
4641
}
4642
} else {
4643
if (ws.wp_idle_inhibitor) {
4644
zwp_idle_inhibitor_v1_destroy(ws.wp_idle_inhibitor);
4645
ws.wp_idle_inhibitor = nullptr;
4646
}
4647
}
4648
}
4649
4650
bool WaylandThread::window_get_idle_inhibition(DisplayServer::WindowID p_window_id) const {
4651
ERR_FAIL_COND_V(!windows.has(p_window_id), false);
4652
const WindowState &ws = windows[p_window_id];
4653
4654
return ws.wp_idle_inhibitor != nullptr;
4655
}
4656
4657
WaylandThread::ScreenData WaylandThread::screen_get_data(int p_screen) const {
4658
ERR_FAIL_INDEX_V(p_screen, registry.wl_outputs.size(), ScreenData());
4659
4660
return wl_output_get_screen_state(registry.wl_outputs.get(p_screen))->data;
4661
}
4662
4663
int WaylandThread::get_screen_count() const {
4664
return registry.wl_outputs.size();
4665
}
4666
4667
DisplayServer::WindowID WaylandThread::pointer_get_pointed_window_id() const {
4668
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
4669
4670
if (ss) {
4671
// Let's determine the most recently used tablet tool.
4672
TabletToolState *max_ts = nullptr;
4673
for (struct zwp_tablet_tool_v2 *tool : ss->tablet_tools) {
4674
TabletToolState *ts = wp_tablet_tool_get_state(tool);
4675
ERR_CONTINUE(ts == nullptr);
4676
4677
TabletToolData &td = ts->data;
4678
4679
if (!max_ts) {
4680
max_ts = ts;
4681
continue;
4682
}
4683
4684
if (MAX(td.button_time, td.motion_time) > MAX(max_ts->data.button_time, max_ts->data.motion_time)) {
4685
max_ts = ts;
4686
}
4687
}
4688
4689
const PointerData &pd = ss->pointer_data;
4690
4691
if (max_ts) {
4692
TabletToolData &td = max_ts->data;
4693
if (MAX(td.button_time, td.motion_time) > MAX(pd.button_time, pd.motion_time)) {
4694
return td.proximal_id;
4695
}
4696
}
4697
4698
return ss->pointer_data.pointed_id;
4699
}
4700
4701
return DisplayServer::INVALID_WINDOW_ID;
4702
}
4703
DisplayServer::WindowID WaylandThread::pointer_get_last_pointed_window_id() const {
4704
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
4705
4706
if (ss) {
4707
// Let's determine the most recently used tablet tool.
4708
TabletToolState *max_ts = nullptr;
4709
for (struct zwp_tablet_tool_v2 *tool : ss->tablet_tools) {
4710
TabletToolState *ts = wp_tablet_tool_get_state(tool);
4711
ERR_CONTINUE(ts == nullptr);
4712
4713
TabletToolData &td = ts->data;
4714
4715
if (!max_ts) {
4716
max_ts = ts;
4717
continue;
4718
}
4719
4720
if (MAX(td.button_time, td.motion_time) > MAX(max_ts->data.button_time, max_ts->data.motion_time)) {
4721
max_ts = ts;
4722
}
4723
}
4724
4725
const PointerData &pd = ss->pointer_data;
4726
4727
if (max_ts) {
4728
TabletToolData &td = max_ts->data;
4729
if (MAX(td.button_time, td.motion_time) > MAX(pd.button_time, pd.motion_time)) {
4730
return td.last_proximal_id;
4731
}
4732
}
4733
4734
return ss->pointer_data.last_pointed_id;
4735
}
4736
4737
return DisplayServer::INVALID_WINDOW_ID;
4738
}
4739
4740
void WaylandThread::pointer_set_constraint(PointerConstraint p_constraint) {
4741
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
4742
4743
if (ss) {
4744
seat_state_unlock_pointer(ss);
4745
4746
if (p_constraint == PointerConstraint::LOCKED) {
4747
seat_state_lock_pointer(ss);
4748
} else if (p_constraint == PointerConstraint::CONFINED) {
4749
seat_state_confine_pointer(ss);
4750
}
4751
}
4752
4753
pointer_constraint = p_constraint;
4754
}
4755
4756
void WaylandThread::pointer_set_hint(const Point2i &p_hint) {
4757
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
4758
if (!ss) {
4759
return;
4760
}
4761
4762
WindowState *ws = window_get_state(ss->pointer_data.pointed_id);
4763
if (!ws) {
4764
return;
4765
}
4766
4767
// NOTE: It looks like it's not really recommended to convert from
4768
// "godot-space" to "wayland-space" and in general I received mixed feelings
4769
// discussing about this. I'm not really sure about the maths behind this but,
4770
// oh well, we're setting a cursor hint. ¯\_(ツ)_/¯
4771
// See: https://oftc.irclog.whitequark.org/wayland/2023-08-23#1692756914-1692816818
4772
int hint_x = Math::round(p_hint.x / window_state_get_scale_factor(ws));
4773
int hint_y = Math::round(p_hint.y / window_state_get_scale_factor(ws));
4774
4775
if (ss) {
4776
seat_state_set_hint(ss, hint_x, hint_y);
4777
}
4778
}
4779
4780
void WaylandThread::pointer_warp(const Point2i &p_to) {
4781
// NOTE: This is for compositors that don't support the pointer-warp protocol.
4782
// It's hacked together and not guaranteed to work.
4783
if (registry.wp_pointer_warp == nullptr) {
4784
PointerConstraint old_constraint = pointer_get_constraint();
4785
4786
pointer_set_constraint(PointerConstraint::LOCKED);
4787
pointer_set_hint(p_to);
4788
4789
pointer_set_constraint(old_constraint);
4790
4791
return;
4792
}
4793
4794
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
4795
if (!ss) {
4796
return;
4797
}
4798
4799
WindowState *ws = window_get_state(ss->pointer_data.pointed_id);
4800
if (!ws) {
4801
return;
4802
}
4803
4804
// NOTE: It looks like it's not really recommended to convert from
4805
// "godot-space" to "wayland-space" and in general I received mixed feelings
4806
// discussing about this. I'm not really sure about the maths behind this but,
4807
// oh well. ¯\_(ツ)_/¯
4808
// See: https://oftc.irclog.whitequark.org/wayland/2023-08-23#1692756914-1692816818
4809
int wl_pos_x = Math::round(p_to.x / window_state_get_scale_factor(ws));
4810
int wl_pos_y = Math::round(p_to.y / window_state_get_scale_factor(ws));
4811
4812
if (ss) {
4813
seat_state_warp_pointer(ss, wl_pos_x, wl_pos_y);
4814
}
4815
}
4816
4817
WaylandThread::PointerConstraint WaylandThread::pointer_get_constraint() const {
4818
return pointer_constraint;
4819
}
4820
4821
BitField<MouseButtonMask> WaylandThread::pointer_get_button_mask() const {
4822
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
4823
4824
if (ss) {
4825
return ss->pointer_data.pressed_button_mask;
4826
}
4827
4828
return BitField<MouseButtonMask>();
4829
}
4830
4831
Error WaylandThread::init() {
4832
#ifdef SOWRAP_ENABLED
4833
#ifdef DEBUG_ENABLED
4834
int dylibloader_verbose = 1;
4835
#else
4836
int dylibloader_verbose = 0;
4837
#endif // DEBUG_ENABLED
4838
4839
if (initialize_wayland_client(dylibloader_verbose) != 0) {
4840
WARN_PRINT("Can't load the Wayland client library.");
4841
return ERR_CANT_CREATE;
4842
}
4843
4844
if (initialize_wayland_cursor(dylibloader_verbose) != 0) {
4845
WARN_PRINT("Can't load the Wayland cursor library.");
4846
return ERR_CANT_CREATE;
4847
}
4848
4849
if (initialize_xkbcommon(dylibloader_verbose) != 0) {
4850
WARN_PRINT("Can't load the XKBcommon library.");
4851
return ERR_CANT_CREATE;
4852
}
4853
#endif // SOWRAP_ENABLED
4854
4855
KeyMappingXKB::initialize();
4856
4857
String embedder_socket_path;
4858
4859
#ifdef TOOLS_ENABLED
4860
bool embedder_enabled = true;
4861
4862
if (OS::get_singleton()->get_environment("GODOT_WAYLAND_DISABLE_EMBEDDER") == "1") {
4863
print_verbose("Disabling Wayland embedder as per GODOT_WAYLAND_DISABLE_EMBEDDER.");
4864
embedder_enabled = false;
4865
}
4866
4867
if (embedder_enabled && Engine::get_singleton()->is_editor_hint() && !Engine::get_singleton()->is_project_manager_hint()) {
4868
print_verbose("Initializing Wayland embedder.");
4869
Error embedder_status = embedder.init();
4870
ERR_FAIL_COND_V_MSG(embedder_status != OK, ERR_CANT_CREATE, "Can't initialize Wayland embedder.");
4871
4872
embedder_socket_path = embedder.get_socket_path();
4873
ERR_FAIL_COND_V_MSG(embedder_socket_path.is_empty(), ERR_CANT_CREATE, "Wayland embedder returned invalid path.");
4874
4875
OS::get_singleton()->set_environment("GODOT_WAYLAND_DISPLAY", embedder_socket_path);
4876
}
4877
#endif // TOOLS_ENABLED
4878
4879
if (Engine::get_singleton()->is_embedded_in_editor()) {
4880
embedder_socket_path = OS::get_singleton()->get_environment("GODOT_WAYLAND_DISPLAY");
4881
#if 0
4882
// Debug
4883
OS::get_singleton()->set_environment("WAYLAND_DEBUG", "1");
4884
int fd = open("/tmp/gdembedded.log", O_CREAT | O_RDWR, 0666);
4885
dup2(fd, 1);
4886
dup2(fd, 2);
4887
#endif
4888
}
4889
4890
if (embedder_socket_path.is_empty()) {
4891
print_verbose("Connecting to the default Wayland display.");
4892
wl_display = wl_display_connect(nullptr);
4893
} else {
4894
print_verbose("Connecting to the Wayland embedder display.");
4895
wl_display = wl_display_connect(embedder_socket_path.utf8().get_data());
4896
}
4897
4898
ERR_FAIL_NULL_V_MSG(wl_display, ERR_CANT_CREATE, "Can't connect to a Wayland display.");
4899
4900
thread_data.wl_display = wl_display;
4901
4902
wl_registry = wl_display_get_registry(wl_display);
4903
4904
ERR_FAIL_NULL_V_MSG(wl_registry, ERR_UNAVAILABLE, "Can't obtain the Wayland registry global.");
4905
4906
registry.wayland_thread = this;
4907
4908
wl_registry_add_listener(wl_registry, &wl_registry_listener, &registry);
4909
4910
// Wait for registry to get notified from the compositor.
4911
wl_display_roundtrip(wl_display);
4912
4913
ERR_FAIL_NULL_V_MSG(registry.wl_shm, ERR_UNAVAILABLE, "Can't obtain the Wayland shared memory global.");
4914
ERR_FAIL_NULL_V_MSG(registry.wl_compositor, ERR_UNAVAILABLE, "Can't obtain the Wayland compositor global.");
4915
ERR_FAIL_NULL_V_MSG(registry.xdg_wm_base, ERR_UNAVAILABLE, "Can't obtain the Wayland XDG shell global.");
4916
4917
// Embedded games can't access the decoration and icon protocol.
4918
if (!Engine::get_singleton()->is_embedded_in_editor()) {
4919
if (!registry.xdg_decoration_manager) {
4920
#ifdef LIBDECOR_ENABLED
4921
WARN_PRINT("Can't obtain the XDG decoration manager. Libdecor will be used for drawing CSDs, if available.");
4922
#else
4923
WARN_PRINT("Can't obtain the XDG decoration manager. Decorations won't show up.");
4924
#endif // LIBDECOR_ENABLED
4925
}
4926
4927
if (!registry.xdg_toplevel_icon_manager_name) {
4928
WARN_PRINT("xdg-toplevel-icon protocol not found! Cannot set window icon.");
4929
}
4930
}
4931
4932
if (!registry.xdg_activation) {
4933
WARN_PRINT("Can't obtain the XDG activation global. Attention requesting won't work!");
4934
}
4935
4936
#ifndef DBUS_ENABLED
4937
if (!registry.wp_idle_inhibit_manager) {
4938
WARN_PRINT("Can't obtain the idle inhibition manager. The screen might turn off even after calling screen_set_keep_on()!");
4939
}
4940
#endif // DBUS_ENABLED
4941
4942
if (!registry.wp_fifo_manager_name) {
4943
WARN_PRINT("FIFO protocol not found! Frame pacing will be degraded.");
4944
}
4945
4946
// Wait for seat capabilities.
4947
wl_display_roundtrip(wl_display);
4948
4949
#ifdef LIBDECOR_ENABLED
4950
bool libdecor_found = true;
4951
4952
bool skip_libdecor = OS::get_singleton()->get_environment("GODOT_WAYLAND_DISABLE_LIBDECOR") == "1";
4953
4954
#ifdef SOWRAP_ENABLED
4955
if (!skip_libdecor && initialize_libdecor(dylibloader_verbose) != 0) {
4956
libdecor_found = false;
4957
}
4958
#endif // SOWRAP_ENABLED
4959
4960
if (skip_libdecor) {
4961
print_verbose("Skipping libdecor check because GODOT_WAYLAND_DISABLE_LIBDECOR is set to 1.");
4962
} else {
4963
if (libdecor_found) {
4964
libdecor_context = libdecor_new(wl_display, (struct libdecor_interface *)&libdecor_interface);
4965
} else {
4966
print_verbose("libdecor not found. Client-side decorations disabled.");
4967
}
4968
}
4969
#endif // LIBDECOR_ENABLED
4970
4971
cursor_theme_name = OS::get_singleton()->get_environment("XCURSOR_THEME");
4972
4973
unscaled_cursor_size = OS::get_singleton()->get_environment("XCURSOR_SIZE").to_int();
4974
if (unscaled_cursor_size <= 0) {
4975
print_verbose("Detected invalid cursor size preference, defaulting to 24.");
4976
unscaled_cursor_size = 24;
4977
}
4978
4979
// NOTE: The scale is useful here as it might've been updated by _update_scale.
4980
bool cursor_theme_loaded = _load_cursor_theme(unscaled_cursor_size * cursor_scale);
4981
4982
if (!cursor_theme_loaded) {
4983
return ERR_CANT_CREATE;
4984
}
4985
4986
// Update the cursor.
4987
cursor_set_shape(DisplayServer::CURSOR_ARROW);
4988
4989
events_thread.start(_poll_events_thread, &thread_data);
4990
4991
initialized = true;
4992
return OK;
4993
}
4994
4995
void WaylandThread::cursor_set_visible(bool p_visible) {
4996
cursor_visible = p_visible;
4997
4998
for (struct wl_seat *wl_seat : registry.wl_seats) {
4999
SeatState *ss = wl_seat_get_seat_state(wl_seat);
5000
ERR_FAIL_NULL(ss);
5001
5002
seat_state_update_cursor(ss);
5003
}
5004
}
5005
5006
void WaylandThread::cursor_set_shape(DisplayServer::CursorShape p_cursor_shape) {
5007
cursor_shape = p_cursor_shape;
5008
5009
for (struct wl_seat *wl_seat : registry.wl_seats) {
5010
SeatState *ss = wl_seat_get_seat_state(wl_seat);
5011
ERR_FAIL_NULL(ss);
5012
5013
seat_state_update_cursor(ss);
5014
}
5015
}
5016
5017
void WaylandThread::cursor_shape_set_custom_image(DisplayServer::CursorShape p_cursor_shape, Ref<Image> p_image, const Point2i &p_hotspot) {
5018
ERR_FAIL_COND(p_image.is_null());
5019
5020
Size2i image_size = p_image->get_size();
5021
5022
// NOTE: The stride is the width of the image in bytes.
5023
unsigned int image_stride = image_size.width * 4;
5024
unsigned int data_size = image_stride * image_size.height;
5025
5026
// We need a shared memory object file descriptor in order to create a
5027
// wl_buffer through wl_shm.
5028
int fd = WaylandThread::_allocate_shm_file(data_size);
5029
ERR_FAIL_COND(fd == -1);
5030
5031
CustomCursor &cursor = custom_cursors[p_cursor_shape];
5032
cursor.hotspot = p_hotspot;
5033
5034
if (cursor.wl_buffer) {
5035
// Clean up the old Wayland buffer.
5036
wl_buffer_destroy(cursor.wl_buffer);
5037
}
5038
5039
if (cursor.buffer_data) {
5040
// Clean up the old buffer data.
5041
munmap(cursor.buffer_data, cursor.buffer_data_size);
5042
}
5043
5044
cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
5045
cursor.buffer_data_size = data_size;
5046
5047
// Create the Wayland buffer.
5048
struct wl_shm_pool *wl_shm_pool = wl_shm_create_pool(registry.wl_shm, fd, data_size);
5049
// TODO: Make sure that WL_SHM_FORMAT_ARGB8888 format is supported. It
5050
// technically isn't garaunteed to be supported, but I think that'd be a
5051
// pretty unlikely thing to stumble upon.
5052
cursor.wl_buffer = wl_shm_pool_create_buffer(wl_shm_pool, 0, image_size.width, image_size.height, image_stride, WL_SHM_FORMAT_ARGB8888);
5053
wl_shm_pool_destroy(wl_shm_pool);
5054
5055
// Fill the cursor buffer with the image data.
5056
for (unsigned int index = 0; index < (unsigned int)(image_size.width * image_size.height); index++) {
5057
int row_index = std::floor(index / image_size.width);
5058
int column_index = (index % int(image_size.width));
5059
5060
cursor.buffer_data[index] = p_image->get_pixel(column_index, row_index).to_argb32();
5061
5062
// Wayland buffers, unless specified, require associated alpha, so we'll just
5063
// associate the alpha in-place.
5064
uint8_t *pixel_data = (uint8_t *)&cursor.buffer_data[index];
5065
pixel_data[0] = pixel_data[0] * pixel_data[3] / 255;
5066
pixel_data[1] = pixel_data[1] * pixel_data[3] / 255;
5067
pixel_data[2] = pixel_data[2] * pixel_data[3] / 255;
5068
}
5069
}
5070
5071
void WaylandThread::cursor_shape_clear_custom_image(DisplayServer::CursorShape p_cursor_shape) {
5072
if (custom_cursors.has(p_cursor_shape)) {
5073
CustomCursor cursor = custom_cursors[p_cursor_shape];
5074
custom_cursors.erase(p_cursor_shape);
5075
5076
if (cursor.wl_buffer) {
5077
wl_buffer_destroy(cursor.wl_buffer);
5078
}
5079
5080
if (cursor.buffer_data) {
5081
munmap(cursor.buffer_data, cursor.buffer_data_size);
5082
}
5083
}
5084
}
5085
5086
void WaylandThread::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) {
5087
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5088
5089
if (ss && ss->wp_text_input && ss->ime_enabled) {
5090
if (p_active) {
5091
ss->ime_active = true;
5092
zwp_text_input_v3_enable(ss->wp_text_input);
5093
zwp_text_input_v3_set_cursor_rectangle(ss->wp_text_input, ss->ime_rect.position.x, ss->ime_rect.position.y, ss->ime_rect.size.x, ss->ime_rect.size.y);
5094
} else {
5095
ss->ime_active = false;
5096
ss->ime_text = String();
5097
ss->ime_text_commit = String();
5098
ss->ime_cursor = Vector2i();
5099
zwp_text_input_v3_disable(ss->wp_text_input);
5100
}
5101
zwp_text_input_v3_commit(ss->wp_text_input);
5102
}
5103
}
5104
5105
void WaylandThread::window_set_ime_position(const Point2i &p_pos, DisplayServer::WindowID p_window_id) {
5106
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5107
5108
if (ss && ss->wp_text_input && ss->ime_enabled) {
5109
ss->ime_rect = Rect2i(p_pos, Size2i(1, 10));
5110
zwp_text_input_v3_set_cursor_rectangle(ss->wp_text_input, ss->ime_rect.position.x, ss->ime_rect.position.y, ss->ime_rect.size.x, ss->ime_rect.size.y);
5111
zwp_text_input_v3_commit(ss->wp_text_input);
5112
}
5113
}
5114
5115
int WaylandThread::keyboard_get_layout_count() const {
5116
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5117
5118
if (ss && ss->xkb_keymap) {
5119
return xkb_keymap_num_layouts(ss->xkb_keymap);
5120
}
5121
5122
return 0;
5123
}
5124
5125
int WaylandThread::keyboard_get_current_layout_index() const {
5126
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5127
5128
if (ss) {
5129
return ss->current_layout_index;
5130
}
5131
5132
return 0;
5133
}
5134
5135
void WaylandThread::keyboard_set_current_layout_index(int p_index) {
5136
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5137
5138
if (ss) {
5139
ss->current_layout_index = p_index;
5140
}
5141
}
5142
5143
String WaylandThread::keyboard_get_layout_name(int p_index) const {
5144
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5145
5146
if (ss && ss->xkb_keymap) {
5147
return String::utf8(xkb_keymap_layout_get_name(ss->xkb_keymap, p_index));
5148
}
5149
5150
return "";
5151
}
5152
5153
Key WaylandThread::keyboard_get_key_from_physical(Key p_key) const {
5154
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5155
5156
if (ss && ss->xkb_state) {
5157
Key modifiers = p_key & KeyModifierMask::MODIFIER_MASK;
5158
Key keycode_no_mod = p_key & KeyModifierMask::CODE_MASK;
5159
5160
xkb_keycode_t xkb_keycode = KeyMappingXKB::get_xkb_keycode(keycode_no_mod);
5161
Key key = KeyMappingXKB::get_keycode(xkb_state_key_get_one_sym(ss->xkb_state, xkb_keycode));
5162
return (Key)(key | modifiers);
5163
}
5164
5165
return p_key;
5166
}
5167
5168
Key WaylandThread::keyboard_get_label_from_physical(Key p_key) const {
5169
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5170
5171
if (ss && ss->xkb_state) {
5172
Key modifiers = p_key & KeyModifierMask::MODIFIER_MASK;
5173
Key keycode_no_mod = p_key & KeyModifierMask::CODE_MASK;
5174
5175
xkb_keycode_t xkb_keycode = KeyMappingXKB::get_xkb_keycode(keycode_no_mod);
5176
xkb_keycode_t xkb_keysym = xkb_state_key_get_one_sym(ss->xkb_state, xkb_keycode);
5177
char32_t chr = xkb_keysym_to_utf32(xkb_keysym_to_upper(xkb_keysym));
5178
if (chr != 0) {
5179
String keysym = String::chr(chr);
5180
Key key = fix_key_label(keysym[0], KeyMappingXKB::get_keycode(xkb_keysym));
5181
return (Key)(key | modifiers);
5182
}
5183
}
5184
5185
return p_key;
5186
}
5187
5188
void WaylandThread::keyboard_echo_keys() {
5189
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5190
5191
if (ss) {
5192
seat_state_echo_keys(ss);
5193
}
5194
}
5195
5196
void WaylandThread::selection_set_text(const String &p_text) {
5197
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5198
5199
if (registry.wl_data_device_manager == nullptr) {
5200
DEBUG_LOG_WAYLAND_THREAD("Couldn't set selection, wl_data_device_manager global not available.");
5201
return;
5202
}
5203
5204
if (ss == nullptr) {
5205
DEBUG_LOG_WAYLAND_THREAD("Couldn't set selection, current seat not set.");
5206
return;
5207
}
5208
5209
if (ss->wl_data_device == nullptr) {
5210
DEBUG_LOG_WAYLAND_THREAD("Couldn't set selection, seat doesn't have wl_data_device.");
5211
return;
5212
}
5213
5214
ss->selection_data = p_text.to_utf8_buffer();
5215
5216
if (ss->wl_data_source_selection == nullptr) {
5217
ss->wl_data_source_selection = wl_data_device_manager_create_data_source(registry.wl_data_device_manager);
5218
wl_data_source_add_listener(ss->wl_data_source_selection, &wl_data_source_listener, ss);
5219
wl_data_source_offer(ss->wl_data_source_selection, "text/plain;charset=utf-8");
5220
wl_data_source_offer(ss->wl_data_source_selection, "text/plain");
5221
5222
// TODO: Implement a good way of getting the latest serial from the user.
5223
wl_data_device_set_selection(ss->wl_data_device, ss->wl_data_source_selection, MAX(ss->pointer_data.button_serial, ss->last_key_pressed_serial));
5224
}
5225
5226
// Wait for the message to get to the server before continuing, otherwise the
5227
// clipboard update might come with a delay.
5228
wl_display_roundtrip(wl_display);
5229
}
5230
5231
bool WaylandThread::selection_has_mime(const String &p_mime) const {
5232
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5233
5234
if (ss == nullptr) {
5235
DEBUG_LOG_WAYLAND_THREAD("Couldn't get selection, current seat not set.");
5236
return false;
5237
}
5238
5239
OfferState *os = wl_data_offer_get_offer_state(ss->wl_data_offer_selection);
5240
if (!os) {
5241
return false;
5242
}
5243
5244
return os->mime_types.has(p_mime);
5245
}
5246
5247
Vector<uint8_t> WaylandThread::selection_get_mime(const String &p_mime) const {
5248
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5249
if (ss == nullptr) {
5250
DEBUG_LOG_WAYLAND_THREAD("Couldn't get selection, current seat not set.");
5251
return Vector<uint8_t>();
5252
}
5253
5254
if (ss->wl_data_source_selection) {
5255
// We have a source so the stuff we're pasting is ours. We'll have to pass the
5256
// data directly or we'd stall waiting for Godot (ourselves) to send us the
5257
// data :P
5258
5259
OfferState *os = wl_data_offer_get_offer_state(ss->wl_data_offer_selection);
5260
ERR_FAIL_NULL_V(os, Vector<uint8_t>());
5261
5262
if (os->mime_types.has(p_mime)) {
5263
// All righty, we're offering this type. Let's just return the data as is.
5264
return ss->selection_data;
5265
}
5266
5267
// ... we don't offer that type. Oh well.
5268
return Vector<uint8_t>();
5269
}
5270
5271
return _wl_data_offer_read(wl_display, p_mime.utf8().get_data(), ss->wl_data_offer_selection);
5272
}
5273
5274
bool WaylandThread::primary_has_mime(const String &p_mime) const {
5275
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5276
5277
if (ss == nullptr) {
5278
DEBUG_LOG_WAYLAND_THREAD("Couldn't get selection, current seat not set.");
5279
return false;
5280
}
5281
5282
OfferState *os = wp_primary_selection_offer_get_offer_state(ss->wp_primary_selection_offer);
5283
if (!os) {
5284
return false;
5285
}
5286
5287
return os->mime_types.has(p_mime);
5288
}
5289
5290
Vector<uint8_t> WaylandThread::primary_get_mime(const String &p_mime) const {
5291
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5292
if (ss == nullptr) {
5293
DEBUG_LOG_WAYLAND_THREAD("Couldn't get primary, current seat not set.");
5294
return Vector<uint8_t>();
5295
}
5296
5297
if (ss->wp_primary_selection_source) {
5298
// We have a source so the stuff we're pasting is ours. We'll have to pass the
5299
// data directly or we'd stall waiting for Godot (ourselves) to send us the
5300
// data :P
5301
5302
OfferState *os = wp_primary_selection_offer_get_offer_state(ss->wp_primary_selection_offer);
5303
ERR_FAIL_NULL_V(os, Vector<uint8_t>());
5304
5305
if (os->mime_types.has(p_mime)) {
5306
// All righty, we're offering this type. Let's just return the data as is.
5307
return ss->selection_data;
5308
}
5309
5310
// ... we don't offer that type. Oh well.
5311
return Vector<uint8_t>();
5312
}
5313
5314
return _wp_primary_selection_offer_read(wl_display, p_mime.utf8().get_data(), ss->wp_primary_selection_offer);
5315
}
5316
5317
void WaylandThread::primary_set_text(const String &p_text) {
5318
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
5319
5320
if (registry.wp_primary_selection_device_manager == nullptr) {
5321
DEBUG_LOG_WAYLAND_THREAD("Couldn't set primary, protocol not available.");
5322
return;
5323
}
5324
5325
if (ss == nullptr) {
5326
DEBUG_LOG_WAYLAND_THREAD("Couldn't set primary, current seat not set.");
5327
return;
5328
}
5329
5330
if (ss->wp_primary_selection_device == nullptr) {
5331
DEBUG_LOG_WAYLAND_THREAD("Couldn't set primary selection, seat doesn't have wp_primary_selection_device.");
5332
return;
5333
}
5334
5335
ss->primary_data = p_text.to_utf8_buffer();
5336
5337
if (ss->wp_primary_selection_source == nullptr) {
5338
ss->wp_primary_selection_source = zwp_primary_selection_device_manager_v1_create_source(registry.wp_primary_selection_device_manager);
5339
zwp_primary_selection_source_v1_add_listener(ss->wp_primary_selection_source, &wp_primary_selection_source_listener, ss);
5340
zwp_primary_selection_source_v1_offer(ss->wp_primary_selection_source, "text/plain;charset=utf-8");
5341
zwp_primary_selection_source_v1_offer(ss->wp_primary_selection_source, "text/plain");
5342
5343
// TODO: Implement a good way of getting the latest serial from the user.
5344
zwp_primary_selection_device_v1_set_selection(ss->wp_primary_selection_device, ss->wp_primary_selection_source, MAX(ss->pointer_data.button_serial, ss->last_key_pressed_serial));
5345
}
5346
5347
// Wait for the message to get to the server before continuing, otherwise the
5348
// clipboard update might come with a delay.
5349
wl_display_roundtrip(wl_display);
5350
}
5351
5352
void WaylandThread::commit_surfaces() {
5353
for (KeyValue<DisplayServer::WindowID, WindowState> &pair : windows) {
5354
wl_surface_commit(pair.value.wl_surface);
5355
}
5356
}
5357
5358
void WaylandThread::set_frame() {
5359
frame = true;
5360
}
5361
5362
bool WaylandThread::get_reset_frame() {
5363
bool old_frame = frame;
5364
frame = false;
5365
5366
return old_frame;
5367
}
5368
5369
// Dispatches events until a frame event is received, a window is reported as
5370
// suspended or the timeout expires.
5371
bool WaylandThread::wait_frame_suspend_ms(int p_timeout) {
5372
// This is a bit of a chicken and egg thing... Looks like the main event loop
5373
// has to call its rightfully forever-blocking poll right in between
5374
// `wl_display_prepare_read` and `wl_display_read`. This means, that it will
5375
// basically be guaranteed to stay stuck in a "prepare read" state, where it
5376
// will block any other attempt at reading the display fd, such as ours. The
5377
// solution? Let's make sure the mutex is locked (it should) and unblock the
5378
// main thread with a roundtrip!
5379
MutexLock mutex_lock(mutex);
5380
wl_display_roundtrip(wl_display);
5381
5382
if (is_suspended()) {
5383
// All windows are suspended! The compositor is telling us _explicitly_ that
5384
// we don't need to draw, without letting us guess through the frame event's
5385
// timing and stuff like that. Our job here is done.
5386
return false;
5387
}
5388
5389
if (frame) {
5390
// We already have a frame! Probably it got there while the caller locked :D
5391
frame = false;
5392
return true;
5393
}
5394
5395
struct pollfd poll_fd;
5396
poll_fd.fd = wl_display_get_fd(wl_display);
5397
poll_fd.events = POLLIN | POLLHUP;
5398
5399
int begin_ms = OS::get_singleton()->get_ticks_msec();
5400
int remaining_ms = p_timeout;
5401
5402
while (remaining_ms > 0) {
5403
// Empty the event queue while it's full.
5404
while (wl_display_prepare_read(wl_display) != 0) {
5405
if (wl_display_dispatch_pending(wl_display) == -1) {
5406
// Oh no. We'll check and handle any display error below.
5407
break;
5408
}
5409
5410
if (is_suspended()) {
5411
return false;
5412
}
5413
5414
if (frame) {
5415
// We had a frame event in the queue :D
5416
frame = false;
5417
return true;
5418
}
5419
}
5420
5421
int werror = wl_display_get_error(wl_display);
5422
5423
if (werror) {
5424
if (werror == EPROTO) {
5425
struct wl_interface *wl_interface = nullptr;
5426
uint32_t id = 0;
5427
5428
int error_code = wl_display_get_protocol_error(wl_display, (const struct wl_interface **)&wl_interface, &id);
5429
CRASH_NOW_MSG(vformat("Wayland protocol error %d on interface %s@%d.", error_code, wl_interface ? wl_interface->name : "unknown", id));
5430
} else {
5431
CRASH_NOW_MSG(vformat("Wayland client error code %d.", werror));
5432
}
5433
}
5434
5435
wl_display_flush(wl_display);
5436
5437
// Wait for the event file descriptor to have new data.
5438
poll(&poll_fd, 1, remaining_ms);
5439
5440
if (poll_fd.revents | POLLIN) {
5441
// Load the queues with fresh new data.
5442
wl_display_read_events(wl_display);
5443
} else {
5444
// Oh well... Stop signaling that we want to read.
5445
wl_display_cancel_read(wl_display);
5446
5447
// We've got no new events :(
5448
// We won't even bother with checking the frame flag.
5449
return false;
5450
}
5451
5452
// Let's try dispatching now...
5453
wl_display_dispatch_pending(wl_display);
5454
5455
if (is_suspended()) {
5456
return false;
5457
}
5458
5459
if (frame) {
5460
frame = false;
5461
return true;
5462
}
5463
5464
remaining_ms -= OS::get_singleton()->get_ticks_msec() - begin_ms;
5465
}
5466
5467
DEBUG_LOG_WAYLAND_THREAD("Frame timeout.");
5468
return false;
5469
}
5470
5471
uint64_t WaylandThread::window_get_last_frame_time(DisplayServer::WindowID p_window_id) const {
5472
ERR_FAIL_COND_V(!windows.has(p_window_id), false);
5473
return windows[p_window_id].last_frame_time;
5474
}
5475
5476
bool WaylandThread::window_is_suspended(DisplayServer::WindowID p_window_id) const {
5477
ERR_FAIL_COND_V(!windows.has(p_window_id), false);
5478
return windows[p_window_id].suspended;
5479
}
5480
5481
bool WaylandThread::is_fifo_available() const {
5482
return registry.wp_fifo_manager_name != 0;
5483
}
5484
5485
bool WaylandThread::is_suspended() const {
5486
for (const KeyValue<DisplayServer::WindowID, WindowState> &E : windows) {
5487
if (!E.value.suspended) {
5488
return false;
5489
}
5490
}
5491
5492
return true;
5493
}
5494
5495
struct godot_embedding_compositor *WaylandThread::get_embedding_compositor() {
5496
return registry.godot_embedding_compositor;
5497
}
5498
5499
OS::ProcessID WaylandThread::embedded_compositor_get_focused_pid() {
5500
EmbeddingCompositorState *ecomp_state = godot_embedding_compositor_get_state(registry.godot_embedding_compositor);
5501
ERR_FAIL_NULL_V(ecomp_state, -1);
5502
5503
return ecomp_state->focused_pid;
5504
}
5505
5506
void WaylandThread::destroy() {
5507
if (!initialized) {
5508
return;
5509
}
5510
5511
if (wl_display && events_thread.is_started()) {
5512
thread_data.thread_done.set();
5513
5514
// By sending a roundtrip message we're unblocking the polling thread so that
5515
// it can realize that it's done and also handle every event that's left.
5516
wl_display_roundtrip(wl_display);
5517
5518
events_thread.wait_to_finish();
5519
}
5520
5521
for (KeyValue<DisplayServer::WindowID, WindowState> &pair : windows) {
5522
WindowState &ws = pair.value;
5523
if (ws.wp_fractional_scale) {
5524
wp_fractional_scale_v1_destroy(ws.wp_fractional_scale);
5525
}
5526
5527
if (ws.wp_viewport) {
5528
wp_viewport_destroy(ws.wp_viewport);
5529
}
5530
5531
if (ws.frame_callback) {
5532
wl_callback_destroy(ws.frame_callback);
5533
}
5534
5535
#ifdef LIBDECOR_ENABLED
5536
if (ws.libdecor_frame) {
5537
libdecor_frame_close(ws.libdecor_frame);
5538
}
5539
#endif // LIBDECOR_ENABLED
5540
5541
if (ws.xdg_toplevel_decoration) {
5542
zxdg_toplevel_decoration_v1_destroy(ws.xdg_toplevel_decoration);
5543
}
5544
5545
if (ws.xdg_toplevel) {
5546
xdg_toplevel_destroy(ws.xdg_toplevel);
5547
}
5548
5549
if (ws.xdg_surface) {
5550
xdg_surface_destroy(ws.xdg_surface);
5551
}
5552
5553
if (ws.wl_surface) {
5554
wl_surface_destroy(ws.wl_surface);
5555
}
5556
}
5557
5558
for (struct wl_seat *wl_seat : registry.wl_seats) {
5559
SeatState *ss = wl_seat_get_seat_state(wl_seat);
5560
ERR_FAIL_NULL(ss);
5561
5562
wl_seat_destroy(wl_seat);
5563
5564
xkb_context_unref(ss->xkb_context);
5565
xkb_state_unref(ss->xkb_state);
5566
xkb_keymap_unref(ss->xkb_keymap);
5567
xkb_compose_table_unref(ss->xkb_compose_table);
5568
xkb_compose_state_unref(ss->xkb_compose_state);
5569
5570
if (ss->wl_keyboard) {
5571
wl_keyboard_destroy(ss->wl_keyboard);
5572
}
5573
5574
if (ss->keymap_buffer) {
5575
munmap((void *)ss->keymap_buffer, ss->keymap_buffer_size);
5576
}
5577
5578
if (ss->wl_pointer) {
5579
wl_pointer_destroy(ss->wl_pointer);
5580
}
5581
5582
if (ss->cursor_frame_callback) {
5583
// We don't need to set a null userdata for safety as the thread is done.
5584
wl_callback_destroy(ss->cursor_frame_callback);
5585
}
5586
5587
if (ss->cursor_surface) {
5588
wl_surface_destroy(ss->cursor_surface);
5589
}
5590
5591
if (ss->wl_data_device) {
5592
wl_data_device_destroy(ss->wl_data_device);
5593
}
5594
5595
if (ss->wp_cursor_shape_device) {
5596
wp_cursor_shape_device_v1_destroy(ss->wp_cursor_shape_device);
5597
}
5598
5599
if (ss->wp_relative_pointer) {
5600
zwp_relative_pointer_v1_destroy(ss->wp_relative_pointer);
5601
}
5602
5603
if (ss->wp_locked_pointer) {
5604
zwp_locked_pointer_v1_destroy(ss->wp_locked_pointer);
5605
}
5606
5607
if (ss->wp_confined_pointer) {
5608
zwp_confined_pointer_v1_destroy(ss->wp_confined_pointer);
5609
}
5610
5611
if (ss->wp_tablet_seat) {
5612
zwp_tablet_seat_v2_destroy(ss->wp_tablet_seat);
5613
}
5614
5615
for (struct zwp_tablet_tool_v2 *tool : ss->tablet_tools) {
5616
TabletToolState *state = wp_tablet_tool_get_state(tool);
5617
if (state) {
5618
memdelete(state);
5619
}
5620
5621
zwp_tablet_tool_v2_destroy(tool);
5622
}
5623
5624
if (ss->wp_text_input) {
5625
zwp_text_input_v3_destroy(ss->wp_text_input);
5626
}
5627
5628
memdelete(ss);
5629
}
5630
5631
if (registry.wp_tablet_manager) {
5632
zwp_tablet_manager_v2_destroy(registry.wp_tablet_manager);
5633
}
5634
5635
if (registry.wp_text_input_manager) {
5636
zwp_text_input_manager_v3_destroy(registry.wp_text_input_manager);
5637
}
5638
5639
for (struct wl_output *wl_output : registry.wl_outputs) {
5640
ERR_FAIL_NULL(wl_output);
5641
5642
memdelete(wl_output_get_screen_state(wl_output));
5643
wl_output_destroy(wl_output);
5644
}
5645
5646
if (registry.godot_embedding_compositor) {
5647
EmbeddingCompositorState *es = godot_embedding_compositor_get_state(registry.godot_embedding_compositor);
5648
ERR_FAIL_NULL(es);
5649
5650
es->mapped_clients.clear();
5651
5652
for (struct godot_embedded_client *client : es->clients) {
5653
godot_embedded_client_destroy(client);
5654
}
5655
es->clients.clear();
5656
5657
memdelete(es);
5658
5659
godot_embedding_compositor_destroy(registry.godot_embedding_compositor);
5660
}
5661
5662
if (wl_cursor_theme) {
5663
wl_cursor_theme_destroy(wl_cursor_theme);
5664
}
5665
5666
if (registry.wp_idle_inhibit_manager) {
5667
zwp_idle_inhibit_manager_v1_destroy(registry.wp_idle_inhibit_manager);
5668
}
5669
5670
if (registry.wp_pointer_constraints) {
5671
zwp_pointer_constraints_v1_destroy(registry.wp_pointer_constraints);
5672
}
5673
5674
if (registry.wp_pointer_gestures) {
5675
zwp_pointer_gestures_v1_destroy(registry.wp_pointer_gestures);
5676
}
5677
5678
if (registry.wp_relative_pointer_manager) {
5679
zwp_relative_pointer_manager_v1_destroy(registry.wp_relative_pointer_manager);
5680
}
5681
5682
if (registry.wp_pointer_warp) {
5683
wp_pointer_warp_v1_destroy(registry.wp_pointer_warp);
5684
}
5685
5686
if (registry.xdg_activation) {
5687
xdg_activation_v1_destroy(registry.xdg_activation);
5688
}
5689
5690
if (registry.xdg_system_bell) {
5691
xdg_system_bell_v1_destroy(registry.xdg_system_bell);
5692
}
5693
5694
if (registry.xdg_toplevel_icon_manager) {
5695
xdg_toplevel_icon_manager_v1_destroy(registry.xdg_toplevel_icon_manager);
5696
5697
if (xdg_icon) {
5698
xdg_toplevel_icon_v1_destroy(xdg_icon);
5699
}
5700
5701
if (icon_buffer) {
5702
wl_buffer_destroy(icon_buffer);
5703
}
5704
}
5705
5706
if (registry.xdg_decoration_manager) {
5707
zxdg_decoration_manager_v1_destroy(registry.xdg_decoration_manager);
5708
}
5709
5710
if (registry.wp_cursor_shape_manager) {
5711
wp_cursor_shape_manager_v1_destroy(registry.wp_cursor_shape_manager);
5712
}
5713
5714
if (registry.wp_fractional_scale_manager) {
5715
wp_fractional_scale_manager_v1_destroy(registry.wp_fractional_scale_manager);
5716
}
5717
5718
if (registry.wp_viewporter) {
5719
wp_viewporter_destroy(registry.wp_viewporter);
5720
}
5721
5722
if (registry.xdg_wm_base) {
5723
xdg_wm_base_destroy(registry.xdg_wm_base);
5724
}
5725
5726
// NOTE: Deprecated.
5727
if (registry.xdg_exporter_v1) {
5728
zxdg_exporter_v1_destroy(registry.xdg_exporter_v1);
5729
}
5730
5731
if (registry.xdg_exporter_v2) {
5732
zxdg_exporter_v2_destroy(registry.xdg_exporter_v2);
5733
}
5734
if (registry.wl_shm) {
5735
wl_shm_destroy(registry.wl_shm);
5736
}
5737
5738
if (registry.wl_compositor) {
5739
wl_compositor_destroy(registry.wl_compositor);
5740
}
5741
5742
if (wl_registry) {
5743
wl_registry_destroy(wl_registry);
5744
}
5745
5746
wl_display_roundtrip(wl_display);
5747
5748
if (wl_display) {
5749
wl_display_disconnect(wl_display);
5750
}
5751
}
5752
5753
#endif // WAYLAND_ENABLED
5754
5755