summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/wayland/SDL_waylandevents.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/wayland/SDL_waylandevents.c')
-rw-r--r--contrib/SDL-3.2.8/src/video/wayland/SDL_waylandevents.c3408
1 files changed, 3408 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/wayland/SDL_waylandevents.c b/contrib/SDL-3.2.8/src/video/wayland/SDL_waylandevents.c
new file mode 100644
index 0000000..83c414f
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/wayland/SDL_waylandevents.c
@@ -0,0 +1,3408 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#ifdef SDL_VIDEO_DRIVER_WAYLAND
25
26#include "../../core/unix/SDL_poll.h"
27#include "../../events/SDL_events_c.h"
28#include "../../events/SDL_scancode_tables_c.h"
29#include "../../events/SDL_keysym_to_keycode_c.h"
30#include "../../core/linux/SDL_system_theme.h"
31#include "../SDL_sysvideo.h"
32
33#include "SDL_waylandvideo.h"
34#include "SDL_waylandevents_c.h"
35#include "SDL_waylandwindow.h"
36#include "SDL_waylandmouse.h"
37
38#include "pointer-constraints-unstable-v1-client-protocol.h"
39#include "relative-pointer-unstable-v1-client-protocol.h"
40#include "xdg-shell-client-protocol.h"
41#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
42#include "text-input-unstable-v3-client-protocol.h"
43#include "tablet-v2-client-protocol.h"
44#include "primary-selection-unstable-v1-client-protocol.h"
45#include "input-timestamps-unstable-v1-client-protocol.h"
46
47#ifdef HAVE_LIBDECOR_H
48#include <libdecor.h>
49#endif
50
51#ifdef SDL_INPUT_LINUXEV
52#include <linux/input.h>
53#else
54#define BTN_LEFT (0x110)
55#define BTN_RIGHT (0x111)
56#define BTN_MIDDLE (0x112)
57#define BTN_SIDE (0x113)
58#define BTN_EXTRA (0x114)
59#endif
60#include "../../events/SDL_keysym_to_scancode_c.h"
61#include "../../events/imKStoUCS.h"
62#include <errno.h>
63#include <sys/mman.h>
64#include <unistd.h>
65#include <xkbcommon/xkbcommon-compose.h>
66#include <xkbcommon/xkbcommon.h>
67#include "cursor-shape-v1-client-protocol.h"
68
69// Weston uses a ratio of 10 units per scroll tick
70#define WAYLAND_WHEEL_AXIS_UNIT 10
71
72#ifndef XKB_MOD_NAME_MOD3
73#define XKB_MOD_NAME_MOD3 "Mod3"
74#endif
75
76#ifndef XKB_MOD_NAME_MOD5
77#define XKB_MOD_NAME_MOD5 "Mod5"
78#endif
79
80// Keyboard and mouse names to match XWayland
81#define WAYLAND_DEFAULT_KEYBOARD_NAME "Virtual core keyboard"
82#define WAYLAND_DEFAULT_POINTER_NAME "Virtual core pointer"
83
84// Focus clickthrough timeout
85#define WAYLAND_FOCUS_CLICK_TIMEOUT_NS SDL_MS_TO_NS(10)
86
87struct SDL_WaylandTouchPoint
88{
89 SDL_TouchID id;
90 wl_fixed_t fx;
91 wl_fixed_t fy;
92 struct wl_surface *surface;
93
94 struct wl_list link;
95};
96
97static struct wl_list touch_points;
98
99static void touch_add(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface *surface)
100{
101 struct SDL_WaylandTouchPoint *tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint));
102
103 SDL_zerop(tp);
104 tp->id = id;
105 tp->fx = fx;
106 tp->fy = fy;
107 tp->surface = surface;
108
109 WAYLAND_wl_list_insert(&touch_points, &tp->link);
110}
111
112static void touch_update(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface **surface)
113{
114 struct SDL_WaylandTouchPoint *tp;
115
116 wl_list_for_each (tp, &touch_points, link) {
117 if (tp->id == id) {
118 tp->fx = fx;
119 tp->fy = fy;
120 if (surface) {
121 *surface = tp->surface;
122 }
123 break;
124 }
125 }
126}
127
128static void touch_del(SDL_TouchID id, wl_fixed_t *fx, wl_fixed_t *fy, struct wl_surface **surface)
129{
130 struct SDL_WaylandTouchPoint *tp;
131
132 wl_list_for_each (tp, &touch_points, link) {
133 if (tp->id == id) {
134 if (fx) {
135 *fx = tp->fx;
136 }
137 if (fy) {
138 *fy = tp->fy;
139 }
140 if (surface) {
141 *surface = tp->surface;
142 }
143
144 WAYLAND_wl_list_remove(&tp->link);
145 SDL_free(tp);
146 break;
147 }
148 }
149}
150
151static bool Wayland_SurfaceHasActiveTouches(struct wl_surface *surface)
152{
153 struct SDL_WaylandTouchPoint *tp;
154
155 wl_list_for_each (tp, &touch_points, link) {
156 if (tp->surface == surface) {
157 return true;
158 }
159 }
160
161 return false;
162}
163
164static Uint64 Wayland_GetEventTimestamp(Uint64 nsTimestamp)
165{
166 static Uint64 last;
167 static Uint64 timestamp_offset;
168 const Uint64 now = SDL_GetTicksNS();
169
170 if (nsTimestamp < last) {
171 // 32-bit timer rollover, bump the offset
172 timestamp_offset += SDL_MS_TO_NS(0x100000000LLU);
173 }
174 last = nsTimestamp;
175
176 if (!timestamp_offset) {
177 timestamp_offset = (now - nsTimestamp);
178 }
179 nsTimestamp += timestamp_offset;
180
181 if (nsTimestamp > now) {
182 timestamp_offset -= (nsTimestamp - now);
183 nsTimestamp = now;
184 }
185
186 return nsTimestamp;
187}
188
189static void Wayland_input_timestamp_listener(void *data, struct zwp_input_timestamps_v1 *zwp_input_timestamps_v1,
190 uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec)
191{
192 *((Uint64 *)data) = ((((Uint64)tv_sec_hi << 32) | (Uint64)tv_sec_lo) * SDL_NS_PER_SECOND) + tv_nsec;
193}
194
195static const struct zwp_input_timestamps_v1_listener timestamp_listener = {
196 Wayland_input_timestamp_listener
197};
198
199static Uint64 Wayland_GetKeyboardTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
200{
201 if (wl_timestamp_ms) {
202 return Wayland_GetEventTimestamp(input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms));
203 }
204
205 return 0;
206}
207
208static Uint64 Wayland_GetKeyboardTimestampRaw(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
209{
210 if (wl_timestamp_ms) {
211 return input->keyboard_timestamp_ns ? input->keyboard_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms);
212 }
213
214 return 0;
215}
216
217static Uint64 Wayland_GetPointerTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
218{
219 if (wl_timestamp_ms) {
220 return Wayland_GetEventTimestamp(input->pointer_timestamp_ns ? input->pointer_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms));
221 }
222
223 return 0;
224}
225
226Uint64 Wayland_GetTouchTimestamp(struct SDL_WaylandInput *input, Uint32 wl_timestamp_ms)
227{
228 if (wl_timestamp_ms) {
229 return Wayland_GetEventTimestamp(input->touch_timestamp_ns ? input->touch_timestamp_ns : SDL_MS_TO_NS(wl_timestamp_ms));
230 }
231
232 return 0;
233}
234
235void Wayland_RegisterTimestampListeners(struct SDL_WaylandInput *input)
236{
237 SDL_VideoData *viddata = input->display;
238
239 if (viddata->input_timestamps_manager) {
240 if (input->keyboard && !input->keyboard_timestamps) {
241 input->keyboard_timestamps = zwp_input_timestamps_manager_v1_get_keyboard_timestamps(viddata->input_timestamps_manager, input->keyboard);
242 zwp_input_timestamps_v1_add_listener(input->keyboard_timestamps, &timestamp_listener, &input->keyboard_timestamp_ns);
243 }
244
245 if (input->pointer && !input->pointer_timestamps) {
246 input->pointer_timestamps = zwp_input_timestamps_manager_v1_get_pointer_timestamps(viddata->input_timestamps_manager, input->pointer);
247 zwp_input_timestamps_v1_add_listener(input->pointer_timestamps, &timestamp_listener, &input->pointer_timestamp_ns);
248 }
249
250 if (input->touch && !input->touch_timestamps) {
251 input->touch_timestamps = zwp_input_timestamps_manager_v1_get_touch_timestamps(viddata->input_timestamps_manager, input->touch);
252 zwp_input_timestamps_v1_add_listener(input->touch_timestamps, &timestamp_listener, &input->touch_timestamp_ns);
253 }
254 }
255}
256
257void Wayland_CreateCursorShapeDevice(struct SDL_WaylandInput *input)
258{
259 SDL_VideoData *viddata = input->display;
260
261 if (viddata->cursor_shape_manager) {
262 if (input->pointer && !input->cursor_shape) {
263 input->cursor_shape = wp_cursor_shape_manager_v1_get_pointer(viddata->cursor_shape_manager, input->pointer);
264 }
265 }
266}
267
268// Returns true if a key repeat event was due
269static bool keyboard_repeat_handle(SDL_WaylandKeyboardRepeat *repeat_info, Uint64 elapsed)
270{
271 bool ret = false;
272 while (elapsed >= repeat_info->next_repeat_ns) {
273 if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) {
274 const Uint64 timestamp = repeat_info->wl_press_time_ns + repeat_info->next_repeat_ns;
275 SDL_SendKeyboardKeyIgnoreModifiers(Wayland_GetEventTimestamp(timestamp), repeat_info->keyboard_id, repeat_info->key, repeat_info->scancode, true);
276 }
277 if (repeat_info->text[0]) {
278 SDL_SendKeyboardText(repeat_info->text);
279 }
280 repeat_info->next_repeat_ns += SDL_NS_PER_SECOND / (Uint64)repeat_info->repeat_rate;
281 ret = true;
282 }
283 return ret;
284}
285
286static void keyboard_repeat_clear(SDL_WaylandKeyboardRepeat *repeat_info)
287{
288 if (!repeat_info->is_initialized) {
289 return;
290 }
291 repeat_info->is_key_down = false;
292}
293
294static void keyboard_repeat_set(SDL_WaylandKeyboardRepeat *repeat_info, Uint32 keyboard_id, uint32_t key, Uint64 wl_press_time_ns,
295 uint32_t scancode, bool has_text, char text[8])
296{
297 if (!repeat_info->is_initialized || !repeat_info->repeat_rate) {
298 return;
299 }
300 repeat_info->is_key_down = true;
301 repeat_info->keyboard_id = keyboard_id;
302 repeat_info->key = key;
303 repeat_info->wl_press_time_ns = wl_press_time_ns;
304 repeat_info->sdl_press_time_ns = SDL_GetTicksNS();
305 repeat_info->next_repeat_ns = SDL_MS_TO_NS(repeat_info->repeat_delay_ms);
306 repeat_info->scancode = scancode;
307 if (has_text) {
308 SDL_copyp(repeat_info->text, text);
309 } else {
310 repeat_info->text[0] = '\0';
311 }
312}
313
314static uint32_t keyboard_repeat_get_key(SDL_WaylandKeyboardRepeat *repeat_info)
315{
316 if (repeat_info->is_initialized && repeat_info->is_key_down) {
317 return repeat_info->key;
318 }
319
320 return 0;
321}
322
323static void keyboard_repeat_set_text(SDL_WaylandKeyboardRepeat *repeat_info, const char text[8])
324{
325 if (repeat_info->is_initialized) {
326 SDL_copyp(repeat_info->text, text);
327 }
328}
329
330static bool keyboard_repeat_is_set(SDL_WaylandKeyboardRepeat *repeat_info)
331{
332 return repeat_info->is_initialized && repeat_info->is_key_down;
333}
334
335static bool keyboard_repeat_key_is_set(SDL_WaylandKeyboardRepeat *repeat_info, uint32_t key)
336{
337 return repeat_info->is_initialized && repeat_info->is_key_down && key == repeat_info->key;
338}
339
340static void sync_done_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
341{
342 // Nothing to do, just destroy the callback
343 wl_callback_destroy(callback);
344}
345
346static struct wl_callback_listener sync_listener = {
347 sync_done_handler
348};
349
350void Wayland_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window)
351{
352 SDL_VideoData *d = _this->internal;
353
354 /* Queue a sync event to unblock the event queue fd if it's empty and being waited on.
355 * TODO: Maybe use a pipe to avoid the compositor roundtrip?
356 */
357 struct wl_callback *cb = wl_display_sync(d->display);
358 wl_callback_add_listener(cb, &sync_listener, NULL);
359 WAYLAND_wl_display_flush(d->display);
360}
361
362static int dispatch_queued_events(SDL_VideoData *viddata)
363{
364 int rc;
365
366 /*
367 * NOTE: When reconnection is implemented, check if libdecor needs to be
368 * involved in the reconnection process.
369 */
370#ifdef HAVE_LIBDECOR_H
371 if (viddata->shell.libdecor) {
372 libdecor_dispatch(viddata->shell.libdecor, 0);
373 }
374#endif
375
376 rc = WAYLAND_wl_display_dispatch_pending(viddata->display);
377 return rc >= 0 ? 1 : rc;
378}
379
380int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
381{
382 SDL_VideoData *d = _this->internal;
383 struct SDL_WaylandInput *input = d->input;
384 bool key_repeat_active = false;
385
386 WAYLAND_wl_display_flush(d->display);
387
388#ifdef SDL_USE_IME
389 SDL_Window *keyboard_focus = SDL_GetKeyboardFocus();
390 if (!d->text_input_manager && keyboard_focus && SDL_TextInputActive(keyboard_focus)) {
391 SDL_IME_PumpEvents();
392 }
393#endif
394
395#ifdef SDL_USE_LIBDBUS
396 SDL_DBus_PumpEvents();
397#endif
398
399 // If key repeat is active, we'll need to cap our maximum wait time to handle repeats
400 if (input && keyboard_repeat_is_set(&input->keyboard_repeat)) {
401 const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns;
402 if (keyboard_repeat_handle(&input->keyboard_repeat, elapsed)) {
403 // A repeat key event was already due
404 return 1;
405 } else {
406 const Uint64 next_repeat_wait_time = (input->keyboard_repeat.next_repeat_ns - elapsed) + 1;
407 if (timeoutNS >= 0) {
408 timeoutNS = SDL_min(timeoutNS, next_repeat_wait_time);
409 } else {
410 timeoutNS = next_repeat_wait_time;
411 }
412 key_repeat_active = true;
413 }
414 }
415
416 /* wl_display_prepare_read() will return -1 if the default queue is not empty.
417 * If the default queue is empty, it will prepare us for our SDL_IOReady() call. */
418 if (WAYLAND_wl_display_prepare_read(d->display) == 0) {
419 // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait
420 int err = SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS);
421 if (err > 0) {
422 // There are new events available to read
423 WAYLAND_wl_display_read_events(d->display);
424 return dispatch_queued_events(d);
425 } else if (err == 0) {
426 // No events available within the timeout
427 WAYLAND_wl_display_cancel_read(d->display);
428
429 // If key repeat is active, we might have woken up to generate a key event
430 if (key_repeat_active) {
431 const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns;
432 if (keyboard_repeat_handle(&input->keyboard_repeat, elapsed)) {
433 return 1;
434 }
435 }
436
437 return 0;
438 } else {
439 // Error returned from poll()/select()
440 WAYLAND_wl_display_cancel_read(d->display);
441
442 if (errno == EINTR) {
443 /* If the wait was interrupted by a signal, we may have generated a
444 * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */
445 return 1;
446 } else {
447 return err;
448 }
449 }
450 } else {
451 // We already had pending events
452 return dispatch_queued_events(d);
453 }
454}
455
456void Wayland_PumpEvents(SDL_VideoDevice *_this)
457{
458 SDL_VideoData *d = _this->internal;
459 struct SDL_WaylandInput *input = d->input;
460 int err;
461
462#ifdef SDL_USE_IME
463 SDL_Window *keyboard_focus = SDL_GetKeyboardFocus();
464 if (!d->text_input_manager && keyboard_focus && SDL_TextInputActive(keyboard_focus)) {
465 SDL_IME_PumpEvents();
466 }
467#endif
468
469#ifdef SDL_USE_LIBDBUS
470 SDL_DBus_PumpEvents();
471#endif
472
473#ifdef HAVE_LIBDECOR_H
474 if (d->shell.libdecor) {
475 libdecor_dispatch(d->shell.libdecor, 0);
476 }
477#endif
478
479 WAYLAND_wl_display_flush(d->display);
480
481 /* wl_display_prepare_read() will return -1 if the default queue is not empty.
482 * If the default queue is empty, it will prepare us for our SDL_IOReady() call. */
483 if (WAYLAND_wl_display_prepare_read(d->display) == 0) {
484 if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_IOR_READ, 0) > 0) {
485 WAYLAND_wl_display_read_events(d->display);
486 } else {
487 WAYLAND_wl_display_cancel_read(d->display);
488 }
489 }
490
491 // Dispatch any pre-existing pending events or new events we may have read
492 err = WAYLAND_wl_display_dispatch_pending(d->display);
493
494 if (input && keyboard_repeat_is_set(&input->keyboard_repeat)) {
495 const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns;
496 keyboard_repeat_handle(&input->keyboard_repeat, elapsed);
497 }
498
499 if (err < 0 && !d->display_disconnected) {
500 /* Something has failed with the Wayland connection -- for example,
501 * the compositor may have shut down and closed its end of the socket,
502 * or there is a library-specific error.
503 *
504 * Try to recover once, then quit.
505 */
506 if (!Wayland_VideoReconnect(_this)) {
507 d->display_disconnected = 1;
508 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Wayland display connection closed by server (fatal)");
509
510 /* Only send a single quit message, as application shutdown might call
511 * SDL_PumpEvents
512 */
513 SDL_SendQuit();
514 }
515 }
516}
517
518static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
519 uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
520{
521 struct SDL_WaylandInput *input = data;
522 SDL_WindowData *window_data = input->pointer_focus;
523 SDL_Window *window = window_data ? window_data->sdlwindow : NULL;
524
525 input->sx_w = sx_w;
526 input->sy_w = sy_w;
527 if (input->pointer_focus) {
528 const float sx = (float)(wl_fixed_to_double(sx_w) * window_data->pointer_scale.x);
529 const float sy = (float)(wl_fixed_to_double(sy_w) * window_data->pointer_scale.y);
530 SDL_SendMouseMotion(Wayland_GetPointerTimestamp(input, time), window_data->sdlwindow, input->pointer_id, false, sx, sy);
531 }
532
533 if (window && window->hit_test) {
534 const SDL_Point point = { (int)SDL_floor(wl_fixed_to_double(sx_w) * window_data->pointer_scale.x),
535 (int)SDL_floor(wl_fixed_to_double(sy_w) * window_data->pointer_scale.y) };
536 SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
537 if (rc == window_data->hit_test_result) {
538 return;
539 }
540
541 Wayland_SetHitTestCursor(rc);
542 window_data->hit_test_result = rc;
543 }
544}
545
546static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
547 uint32_t serial, struct wl_surface *surface,
548 wl_fixed_t sx_w, wl_fixed_t sy_w)
549{
550 struct SDL_WaylandInput *input = data;
551 SDL_WindowData *window;
552
553 if (!surface) {
554 // enter event for a window we've just destroyed
555 return;
556 }
557
558 /* This handler will be called twice in Wayland 1.4
559 * Once for the window surface which has valid user data
560 * and again for the mouse cursor surface which does not have valid user data
561 * We ignore the later
562 */
563 window = Wayland_GetWindowDataForOwnedSurface(surface);
564
565 if (window) {
566 input->pointer_focus = window;
567 input->pointer_enter_serial = serial;
568 SDL_SetMouseFocus(window->sdlwindow);
569 /* In the case of e.g. a pointer confine warp, we may receive an enter
570 * event with no following motion event, but with the new coordinates
571 * as part of the enter event.
572 *
573 * FIXME: This causes a movement event with an anomalous timestamp when
574 * the cursor enters the window.
575 */
576 pointer_handle_motion(data, pointer, 0, sx_w, sy_w);
577 /* If the cursor was changed while our window didn't have pointer
578 * focus, we might need to trigger another call to
579 * wl_pointer_set_cursor() for the new cursor to be displayed. */
580 Wayland_SetHitTestCursor(window->hit_test_result);
581 }
582}
583
584static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
585 uint32_t serial, struct wl_surface *surface)
586{
587 struct SDL_WaylandInput *input = data;
588
589 if (!surface) {
590 return;
591 }
592
593 if (input->pointer_focus) {
594 SDL_WindowData *wind = Wayland_GetWindowDataForOwnedSurface(surface);
595
596 if (wind) {
597 // Clear the capture flag and raise all buttons
598 wind->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
599
600 input->buttons_pressed = 0;
601 SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_LEFT, false);
602 SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_RIGHT, false);
603 SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_MIDDLE, false);
604 SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_X1, false);
605 SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, input->pointer_id, SDL_BUTTON_X2, false);
606 }
607
608 /* A pointer leave event may be emitted if the compositor hides the pointer in response to receiving a touch event.
609 * Don't relinquish focus if the surface has active touches, as the compositor is just transitioning from mouse to touch mode.
610 */
611 if (!Wayland_SurfaceHasActiveTouches(surface)) {
612 SDL_SetMouseFocus(NULL);
613 }
614 input->pointer_focus = NULL;
615 }
616}
617
618static bool ProcessHitTest(SDL_WindowData *window_data,
619 struct wl_seat *seat,
620 wl_fixed_t sx_w, wl_fixed_t sy_w,
621 uint32_t serial)
622{
623 SDL_Window *window = window_data->sdlwindow;
624
625 if (window->hit_test) {
626 static const uint32_t directions[] = {
627 XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_TOP,
628 XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT, XDG_TOPLEVEL_RESIZE_EDGE_RIGHT,
629 XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM,
630 XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT, XDG_TOPLEVEL_RESIZE_EDGE_LEFT
631 };
632
633#ifdef HAVE_LIBDECOR_H
634 static const uint32_t directions_libdecor[] = {
635 LIBDECOR_RESIZE_EDGE_TOP_LEFT, LIBDECOR_RESIZE_EDGE_TOP,
636 LIBDECOR_RESIZE_EDGE_TOP_RIGHT, LIBDECOR_RESIZE_EDGE_RIGHT,
637 LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT, LIBDECOR_RESIZE_EDGE_BOTTOM,
638 LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT, LIBDECOR_RESIZE_EDGE_LEFT
639 };
640#endif
641
642 switch (window_data->hit_test_result) {
643 case SDL_HITTEST_DRAGGABLE:
644#ifdef HAVE_LIBDECOR_H
645 if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) {
646 if (window_data->shell_surface.libdecor.frame) {
647 libdecor_frame_move(window_data->shell_surface.libdecor.frame,
648 seat,
649 serial);
650 }
651 } else
652#endif
653 if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) {
654 if (window_data->shell_surface.xdg.toplevel.xdg_toplevel) {
655 xdg_toplevel_move(window_data->shell_surface.xdg.toplevel.xdg_toplevel,
656 seat,
657 serial);
658 }
659 }
660 return true;
661
662 case SDL_HITTEST_RESIZE_TOPLEFT:
663 case SDL_HITTEST_RESIZE_TOP:
664 case SDL_HITTEST_RESIZE_TOPRIGHT:
665 case SDL_HITTEST_RESIZE_RIGHT:
666 case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
667 case SDL_HITTEST_RESIZE_BOTTOM:
668 case SDL_HITTEST_RESIZE_BOTTOMLEFT:
669 case SDL_HITTEST_RESIZE_LEFT:
670#ifdef HAVE_LIBDECOR_H
671 if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_LIBDECOR) {
672 if (window_data->shell_surface.libdecor.frame) {
673 libdecor_frame_resize(window_data->shell_surface.libdecor.frame,
674 seat,
675 serial,
676 directions_libdecor[window_data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]);
677 }
678 } else
679#endif
680 if (window_data->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) {
681 if (window_data->shell_surface.xdg.toplevel.xdg_toplevel) {
682 xdg_toplevel_resize(window_data->shell_surface.xdg.toplevel.xdg_toplevel,
683 seat,
684 serial,
685 directions[window_data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]);
686 }
687 }
688 return true;
689
690 default:
691 return false;
692 }
693 }
694
695 return false;
696}
697
698static void pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial,
699 uint32_t time, uint32_t button, uint32_t state_w)
700{
701 SDL_WindowData *window = input->pointer_focus;
702 enum wl_pointer_button_state state = state_w;
703 Uint64 timestamp = Wayland_GetPointerTimestamp(input, time);
704 Uint8 sdl_button;
705 const bool down = (state != 0);
706
707 switch (button) {
708 case BTN_LEFT:
709 sdl_button = SDL_BUTTON_LEFT;
710 break;
711 case BTN_MIDDLE:
712 sdl_button = SDL_BUTTON_MIDDLE;
713 break;
714 case BTN_RIGHT:
715 sdl_button = SDL_BUTTON_RIGHT;
716 break;
717 case BTN_SIDE:
718 sdl_button = SDL_BUTTON_X1;
719 break;
720 case BTN_EXTRA:
721 sdl_button = SDL_BUTTON_X2;
722 break;
723 default:
724 return;
725 }
726
727 if (window) {
728 SDL_VideoData *viddata = window->waylandData;
729 bool ignore_click = false;
730
731 if (state) {
732 Wayland_UpdateImplicitGrabSerial(input, serial);
733 input->buttons_pressed |= SDL_BUTTON_MASK(sdl_button);
734 } else {
735 input->buttons_pressed &= ~(SDL_BUTTON_MASK(sdl_button));
736 }
737
738 if (sdl_button == SDL_BUTTON_LEFT &&
739 ProcessHitTest(input->pointer_focus, input->seat, input->sx_w, input->sy_w, serial)) {
740 return; // don't pass this event on to app.
741 }
742
743 // Possibly ignore this click if it was to gain focus.
744 if (window->last_focus_event_time_ns) {
745 if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
746 (SDL_GetTicksNS() - window->last_focus_event_time_ns) < WAYLAND_FOCUS_CLICK_TIMEOUT_NS) {
747 ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false);
748 }
749
750 window->last_focus_event_time_ns = 0;
751 }
752
753 /* Wayland won't let you "capture" the mouse, but it will automatically track
754 * the mouse outside the window if you drag outside of it, until you let go
755 * of all buttons (even if you add or remove presses outside the window, as
756 * long as any button is still down, the capture remains).
757 *
758 * The mouse is not captured in relative mode.
759 */
760 if (!viddata->relative_mouse_mode) {
761 if (input->buttons_pressed != 0) {
762 window->sdlwindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
763 } else {
764 window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
765 }
766 }
767
768 if (!ignore_click) {
769 SDL_SendMouseButton(timestamp, window->sdlwindow, input->pointer_id, sdl_button, down);
770 }
771 }
772}
773
774static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
775 uint32_t time, uint32_t button, uint32_t state_w)
776{
777 struct SDL_WaylandInput *input = data;
778
779 pointer_handle_button_common(input, serial, time, button, state_w);
780}
781
782static void pointer_handle_axis_common_v1(struct SDL_WaylandInput *input,
783 uint32_t time, uint32_t axis, wl_fixed_t value)
784{
785 SDL_WindowData *window = input->pointer_focus;
786 const Uint64 timestamp = Wayland_GetPointerTimestamp(input, time);
787 const enum wl_pointer_axis a = axis;
788
789 if (input->pointer_focus) {
790 float x, y;
791
792 switch (a) {
793 case WL_POINTER_AXIS_VERTICAL_SCROLL:
794 x = 0;
795 y = 0 - (float)wl_fixed_to_double(value);
796 break;
797 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
798 x = (float)wl_fixed_to_double(value);
799 y = 0;
800 break;
801 default:
802 return;
803 }
804
805 x /= WAYLAND_WHEEL_AXIS_UNIT;
806 y /= WAYLAND_WHEEL_AXIS_UNIT;
807
808 SDL_SendMouseWheel(timestamp, window->sdlwindow, input->pointer_id, x, y, SDL_MOUSEWHEEL_NORMAL);
809 }
810}
811
812static void pointer_handle_axis_common(struct SDL_WaylandInput *input, enum SDL_WaylandAxisEvent type,
813 uint32_t axis, wl_fixed_t value)
814{
815 const enum wl_pointer_axis a = axis;
816
817 if (input->pointer_focus) {
818 switch (a) {
819 case WL_POINTER_AXIS_VERTICAL_SCROLL:
820 switch (type) {
821 case AXIS_EVENT_VALUE120:
822 /*
823 * High resolution scroll event. The spec doesn't state that axis_value120
824 * events are limited to one per frame, so the values are accumulated.
825 */
826 if (input->pointer_curr_axis_info.y_axis_type != AXIS_EVENT_VALUE120) {
827 input->pointer_curr_axis_info.y_axis_type = AXIS_EVENT_VALUE120;
828 input->pointer_curr_axis_info.y = 0.0f;
829 }
830 input->pointer_curr_axis_info.y += 0 - (float)wl_fixed_to_double(value);
831 break;
832 case AXIS_EVENT_DISCRETE:
833 /*
834 * This is a discrete axis event, so we process it and set the
835 * flag to ignore future continuous axis events in this frame.
836 */
837 if (input->pointer_curr_axis_info.y_axis_type != AXIS_EVENT_DISCRETE) {
838 input->pointer_curr_axis_info.y_axis_type = AXIS_EVENT_DISCRETE;
839 input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value);
840 }
841 break;
842 case AXIS_EVENT_CONTINUOUS:
843 // Only process continuous events if no discrete events have been received.
844 if (input->pointer_curr_axis_info.y_axis_type == AXIS_EVENT_CONTINUOUS) {
845 input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value);
846 }
847 break;
848 }
849 break;
850 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
851 switch (type) {
852 case AXIS_EVENT_VALUE120:
853 /*
854 * High resolution scroll event. The spec doesn't state that axis_value120
855 * events are limited to one per frame, so the values are accumulated.
856 */
857 if (input->pointer_curr_axis_info.x_axis_type != AXIS_EVENT_VALUE120) {
858 input->pointer_curr_axis_info.x_axis_type = AXIS_EVENT_VALUE120;
859 input->pointer_curr_axis_info.x = 0.0f;
860 }
861 input->pointer_curr_axis_info.x += (float)wl_fixed_to_double(value);
862 break;
863 case AXIS_EVENT_DISCRETE:
864 /*
865 * This is a discrete axis event, so we process it and set the
866 * flag to ignore future continuous axis events in this frame.
867 */
868 if (input->pointer_curr_axis_info.x_axis_type != AXIS_EVENT_DISCRETE) {
869 input->pointer_curr_axis_info.x_axis_type = AXIS_EVENT_DISCRETE;
870 input->pointer_curr_axis_info.x = (float)wl_fixed_to_double(value);
871 }
872 break;
873 case AXIS_EVENT_CONTINUOUS:
874 // Only process continuous events if no discrete events have been received.
875 if (input->pointer_curr_axis_info.x_axis_type == AXIS_EVENT_CONTINUOUS) {
876 input->pointer_curr_axis_info.x = (float)wl_fixed_to_double(value);
877 }
878 break;
879 }
880 break;
881 }
882 }
883}
884
885static void pointer_handle_axis(void *data, struct wl_pointer *pointer,
886 uint32_t time, uint32_t axis, wl_fixed_t value)
887{
888 struct SDL_WaylandInput *input = data;
889
890 if (wl_seat_get_version(input->seat) >= WL_POINTER_FRAME_SINCE_VERSION) {
891 input->pointer_curr_axis_info.timestamp_ns = Wayland_GetPointerTimestamp(input, time);
892 pointer_handle_axis_common(input, AXIS_EVENT_CONTINUOUS, axis, value);
893 } else {
894 pointer_handle_axis_common_v1(input, time, axis, value);
895 }
896}
897
898static void pointer_handle_axis_relative_direction(void *data, struct wl_pointer *pointer,
899 uint32_t axis, uint32_t axis_relative_direction)
900{
901 struct SDL_WaylandInput *input = data;
902 if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) {
903 return;
904 }
905 switch (axis_relative_direction) {
906 case WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL:
907 input->pointer_curr_axis_info.direction = SDL_MOUSEWHEEL_NORMAL;
908 break;
909 case WL_POINTER_AXIS_RELATIVE_DIRECTION_INVERTED:
910 input->pointer_curr_axis_info.direction = SDL_MOUSEWHEEL_FLIPPED;
911 break;
912 }
913}
914
915static void pointer_handle_frame(void *data, struct wl_pointer *pointer)
916{
917 struct SDL_WaylandInput *input = data;
918 SDL_WindowData *window = input->pointer_focus;
919 float x, y;
920 SDL_MouseWheelDirection direction = input->pointer_curr_axis_info.direction;
921
922 switch (input->pointer_curr_axis_info.x_axis_type) {
923 case AXIS_EVENT_CONTINUOUS:
924 x = input->pointer_curr_axis_info.x / WAYLAND_WHEEL_AXIS_UNIT;
925 break;
926 case AXIS_EVENT_DISCRETE:
927 x = input->pointer_curr_axis_info.x;
928 break;
929 case AXIS_EVENT_VALUE120:
930 x = input->pointer_curr_axis_info.x / 120.0f;
931 break;
932 default:
933 x = 0.0f;
934 break;
935 }
936
937 switch (input->pointer_curr_axis_info.y_axis_type) {
938 case AXIS_EVENT_CONTINUOUS:
939 y = input->pointer_curr_axis_info.y / WAYLAND_WHEEL_AXIS_UNIT;
940 break;
941 case AXIS_EVENT_DISCRETE:
942 y = input->pointer_curr_axis_info.y;
943 break;
944 case AXIS_EVENT_VALUE120:
945 y = input->pointer_curr_axis_info.y / 120.0f;
946 break;
947 default:
948 y = 0.0f;
949 break;
950 }
951
952 // clear pointer_curr_axis_info for next frame
953 SDL_memset(&input->pointer_curr_axis_info, 0, sizeof(input->pointer_curr_axis_info));
954
955 if (x != 0.0f || y != 0.0f) {
956 SDL_SendMouseWheel(input->pointer_curr_axis_info.timestamp_ns,
957 window->sdlwindow, input->pointer_id, x, y, direction);
958 }
959}
960
961static void pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
962 uint32_t axis_source)
963{
964 // unimplemented
965}
966
967static void pointer_handle_axis_stop(void *data, struct wl_pointer *pointer,
968 uint32_t time, uint32_t axis)
969{
970 // unimplemented
971}
972
973static void pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
974 uint32_t axis, int32_t discrete)
975{
976 struct SDL_WaylandInput *input = data;
977
978 pointer_handle_axis_common(input, AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete));
979}
980
981static void pointer_handle_axis_value120(void *data, struct wl_pointer *pointer,
982 uint32_t axis, int32_t value120)
983{
984 struct SDL_WaylandInput *input = data;
985
986 pointer_handle_axis_common(input, AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120));
987}
988
989static const struct wl_pointer_listener pointer_listener = {
990 pointer_handle_enter,
991 pointer_handle_leave,
992 pointer_handle_motion,
993 pointer_handle_button,
994 pointer_handle_axis,
995 pointer_handle_frame, // Version 5
996 pointer_handle_axis_source, // Version 5
997 pointer_handle_axis_stop, // Version 5
998 pointer_handle_axis_discrete, // Version 5
999 pointer_handle_axis_value120, // Version 8
1000 pointer_handle_axis_relative_direction // Version 9
1001};
1002
1003static void relative_pointer_handle_relative_motion(void *data,
1004 struct zwp_relative_pointer_v1 *pointer,
1005 uint32_t time_hi,
1006 uint32_t time_lo,
1007 wl_fixed_t dx_w,
1008 wl_fixed_t dy_w,
1009 wl_fixed_t dx_unaccel_w,
1010 wl_fixed_t dy_unaccel_w)
1011{
1012 struct SDL_WaylandInput *input = data;
1013 SDL_VideoData *d = input->display;
1014 SDL_WindowData *window = input->pointer_focus;
1015
1016 // Relative pointer event times are in microsecond granularity.
1017 const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo));
1018
1019 if (input->pointer_focus && d->relative_mouse_mode) {
1020 double dx;
1021 double dy;
1022 if (!SDL_GetMouse()->enable_relative_system_scale) {
1023 dx = wl_fixed_to_double(dx_unaccel_w);
1024 dy = wl_fixed_to_double(dy_unaccel_w);
1025 } else {
1026 dx = wl_fixed_to_double(dx_w);
1027 dy = wl_fixed_to_double(dy_w);
1028 }
1029
1030 SDL_SendMouseMotion(timestamp, window->sdlwindow, input->pointer_id, true, (float)dx, (float)dy);
1031 }
1032}
1033
1034static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
1035 relative_pointer_handle_relative_motion,
1036};
1037
1038static void locked_pointer_locked(void *data,
1039 struct zwp_locked_pointer_v1 *locked_pointer)
1040{
1041}
1042
1043static void locked_pointer_unlocked(void *data,
1044 struct zwp_locked_pointer_v1 *locked_pointer)
1045{
1046}
1047
1048static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
1049 locked_pointer_locked,
1050 locked_pointer_unlocked,
1051};
1052
1053bool Wayland_input_lock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
1054{
1055 SDL_WindowData *w = window->internal;
1056 SDL_VideoData *d = input->display;
1057
1058 if (!d->pointer_constraints || !input->pointer) {
1059 return false;
1060 }
1061
1062 if (!w->locked_pointer) {
1063 if (w->confined_pointer) {
1064 // If the pointer is already confined to the surface, the lock will fail with a protocol error.
1065 Wayland_input_unconfine_pointer(input, window);
1066 }
1067
1068 w->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints,
1069 w->surface,
1070 input->pointer,
1071 NULL,
1072 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
1073 zwp_locked_pointer_v1_add_listener(w->locked_pointer,
1074 &locked_pointer_listener,
1075 window);
1076 }
1077
1078 return true;
1079}
1080
1081bool Wayland_input_unlock_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
1082{
1083 SDL_WindowData *w = window->internal;
1084
1085 if (w->locked_pointer) {
1086 zwp_locked_pointer_v1_destroy(w->locked_pointer);
1087 w->locked_pointer = NULL;
1088 }
1089
1090 // Restore existing pointer confinement.
1091 Wayland_input_confine_pointer(input, window);
1092
1093 return true;
1094}
1095
1096static void pointer_confine_destroy(SDL_Window *window)
1097{
1098 SDL_WindowData *w = window->internal;
1099 if (w->confined_pointer) {
1100 zwp_confined_pointer_v1_destroy(w->confined_pointer);
1101 w->confined_pointer = NULL;
1102 }
1103}
1104
1105static void confined_pointer_confined(void *data,
1106 struct zwp_confined_pointer_v1 *confined_pointer)
1107{
1108}
1109
1110static void confined_pointer_unconfined(void *data,
1111 struct zwp_confined_pointer_v1 *confined_pointer)
1112{
1113}
1114
1115static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = {
1116 confined_pointer_confined,
1117 confined_pointer_unconfined,
1118};
1119
1120static void touch_handler_down(void *data, struct wl_touch *touch, uint32_t serial,
1121 uint32_t timestamp, struct wl_surface *surface,
1122 int id, wl_fixed_t fx, wl_fixed_t fy)
1123{
1124 struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data;
1125 SDL_WindowData *window_data;
1126
1127 // Check that this surface is valid.
1128 if (!surface) {
1129 return;
1130 }
1131
1132 touch_add(id, fx, fy, surface);
1133 Wayland_UpdateImplicitGrabSerial(input, serial);
1134 window_data = Wayland_GetWindowDataForOwnedSurface(surface);
1135
1136 if (window_data) {
1137 float x, y;
1138
1139 if (window_data->current.logical_width <= 1) {
1140 x = 0.5f;
1141 } else {
1142 x = (float)wl_fixed_to_double(fx) / (window_data->current.logical_width - 1);
1143 }
1144 if (window_data->current.logical_height <= 1) {
1145 y = 0.5f;
1146 } else {
1147 y = (float)wl_fixed_to_double(fy) / (window_data->current.logical_height - 1);
1148 }
1149
1150 SDL_SetMouseFocus(window_data->sdlwindow);
1151
1152 SDL_SendTouch(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
1153 (SDL_FingerID)(id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_DOWN, x, y, 1.0f);
1154 }
1155}
1156
1157static void touch_handler_up(void *data, struct wl_touch *touch, uint32_t serial,
1158 uint32_t timestamp, int id)
1159{
1160 struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data;
1161 wl_fixed_t fx = 0, fy = 0;
1162 struct wl_surface *surface = NULL;
1163
1164 touch_del(id, &fx, &fy, &surface);
1165
1166 if (surface) {
1167 SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(surface);
1168
1169 if (window_data) {
1170 const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width;
1171 const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height;
1172
1173 SDL_SendTouch(Wayland_GetTouchTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
1174 (SDL_FingerID)(id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_UP, x, y, 0.0f);
1175
1176 /* If the seat lacks pointer focus, the seat's keyboard focus is another window or NULL, this window currently
1177 * has mouse focus, and the surface has no active touch events, consider mouse focus to be lost.
1178 */
1179 if (!input->pointer_focus && input->keyboard_focus != window_data &&
1180 SDL_GetMouseFocus() == window_data->sdlwindow && !Wayland_SurfaceHasActiveTouches(surface)) {
1181 SDL_SetMouseFocus(NULL);
1182 }
1183 }
1184 }
1185}
1186
1187static void touch_handler_motion(void *data, struct wl_touch *touch, uint32_t timestamp,
1188 int id, wl_fixed_t fx, wl_fixed_t fy)
1189{
1190 struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data;
1191 struct wl_surface *surface = NULL;
1192
1193 touch_update(id, fx, fy, &surface);
1194
1195 if (surface) {
1196 SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(surface);
1197
1198 if (window_data) {
1199 const float x = (float)wl_fixed_to_double(fx) / window_data->current.logical_width;
1200 const float y = (float)wl_fixed_to_double(fy) / window_data->current.logical_height;
1201
1202 SDL_SendTouchMotion(Wayland_GetPointerTimestamp(input, timestamp), (SDL_TouchID)(uintptr_t)touch,
1203 (SDL_FingerID)(id + 1), window_data->sdlwindow, x, y, 1.0f);
1204 }
1205 }
1206}
1207
1208static void touch_handler_frame(void *data, struct wl_touch *touch)
1209{
1210}
1211
1212static void touch_handler_cancel(void *data, struct wl_touch *touch)
1213{
1214 struct SDL_WaylandInput *input = (struct SDL_WaylandInput *)data;
1215 struct SDL_WaylandTouchPoint *tp, *temp;
1216
1217 wl_list_for_each_safe (tp, temp, &touch_points, link) {
1218 bool removed = false;
1219
1220 if (tp->surface) {
1221 SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(tp->surface);
1222
1223 if (window_data) {
1224 const float x = (float)(wl_fixed_to_double(tp->fx) / window_data->current.logical_width);
1225 const float y = (float)(wl_fixed_to_double(tp->fy) / window_data->current.logical_height);
1226
1227 SDL_SendTouch(0, (SDL_TouchID)(uintptr_t)touch,
1228 (SDL_FingerID)(tp->id + 1), window_data->sdlwindow, SDL_EVENT_FINGER_CANCELED, x, y, 0.0f);
1229
1230 // Remove the touch from the list before checking for still-active touches on the surface.
1231 WAYLAND_wl_list_remove(&tp->link);
1232 removed = true;
1233
1234 /* If the seat lacks pointer focus, the seat's keyboard focus is another window or NULL, this window currently
1235 * has mouse focus, and the surface has no active touch events, consider mouse focus to be lost.
1236 */
1237 if (!input->pointer_focus && input->keyboard_focus != window_data &&
1238 SDL_GetMouseFocus() == window_data->sdlwindow && !Wayland_SurfaceHasActiveTouches(tp->surface)) {
1239 SDL_SetMouseFocus(NULL);
1240 }
1241 }
1242 }
1243
1244 if (!removed) {
1245 WAYLAND_wl_list_remove(&tp->link);
1246 }
1247 SDL_free(tp);
1248 }
1249}
1250
1251static const struct wl_touch_listener touch_listener = {
1252 touch_handler_down,
1253 touch_handler_up,
1254 touch_handler_motion,
1255 touch_handler_frame,
1256 touch_handler_cancel,
1257 NULL, // shape
1258 NULL, // orientation
1259};
1260
1261typedef struct Wayland_Keymap
1262{
1263 SDL_Keymap *keymap;
1264 struct xkb_state *state;
1265 SDL_Keymod modstate;
1266} Wayland_Keymap;
1267
1268static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data)
1269{
1270 Wayland_Keymap *sdlKeymap = (Wayland_Keymap *)data;
1271 const xkb_keysym_t *syms;
1272 const SDL_Scancode scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, (key - 8));
1273 if (scancode == SDL_SCANCODE_UNKNOWN) {
1274 return;
1275 }
1276
1277 if (WAYLAND_xkb_state_key_get_syms(sdlKeymap->state, key, &syms) > 0) {
1278 SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(syms[0], key, sdlKeymap->modstate);
1279
1280 if (!keycode) {
1281 switch (scancode) {
1282 case SDL_SCANCODE_RETURN:
1283 keycode = SDLK_RETURN;
1284 break;
1285 case SDL_SCANCODE_ESCAPE:
1286 keycode = SDLK_ESCAPE;
1287 break;
1288 case SDL_SCANCODE_BACKSPACE:
1289 keycode = SDLK_BACKSPACE;
1290 break;
1291 case SDL_SCANCODE_DELETE:
1292 keycode = SDLK_DELETE;
1293 break;
1294 default:
1295 keycode = SDL_SCANCODE_TO_KEYCODE(scancode);
1296 break;
1297 }
1298 }
1299
1300 SDL_SetKeymapEntry(sdlKeymap->keymap, scancode, sdlKeymap->modstate, keycode);
1301 }
1302}
1303
1304static void Wayland_UpdateKeymap(struct SDL_WaylandInput *input)
1305{
1306 struct Keymod_masks
1307 {
1308 SDL_Keymod sdl_mask;
1309 xkb_mod_mask_t xkb_mask;
1310 } const keymod_masks[] = {
1311 { SDL_KMOD_NONE, 0 },
1312 { SDL_KMOD_SHIFT, input->xkb.idx_shift },
1313 { SDL_KMOD_CAPS, input->xkb.idx_caps },
1314 { SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_shift | input->xkb.idx_caps },
1315 { SDL_KMOD_MODE, input->xkb.idx_mod5 },
1316 { SDL_KMOD_MODE | SDL_KMOD_SHIFT, input->xkb.idx_mod5 | input->xkb.idx_shift },
1317 { SDL_KMOD_MODE | SDL_KMOD_CAPS, input->xkb.idx_mod5 | input->xkb.idx_caps },
1318 { SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod5 | input->xkb.idx_shift | input->xkb.idx_caps },
1319 { SDL_KMOD_LEVEL5, input->xkb.idx_mod3 },
1320 { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, input->xkb.idx_mod3 | input->xkb.idx_shift },
1321 { SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_caps },
1322 { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_shift | input->xkb.idx_caps },
1323 { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, input->xkb.idx_mod3 | input->xkb.idx_mod5 },
1324 { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_shift },
1325 { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_caps },
1326 { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_shift | input->xkb.idx_caps },
1327 };
1328
1329 if (!input->keyboard_is_virtual) {
1330 Wayland_Keymap keymap;
1331
1332 keymap.keymap = SDL_CreateKeymap();
1333 if (!keymap.keymap) {
1334 return;
1335 }
1336
1337 keymap.state = WAYLAND_xkb_state_new(input->xkb.keymap);
1338 if (!keymap.state) {
1339 SDL_SetError("failed to create XKB state");
1340 SDL_DestroyKeymap(keymap.keymap);
1341 return;
1342 }
1343
1344 for (int i = 0; i < SDL_arraysize(keymod_masks); ++i) {
1345 keymap.modstate = keymod_masks[i].sdl_mask;
1346 WAYLAND_xkb_state_update_mask(keymap.state,
1347 keymod_masks[i].xkb_mask & (input->xkb.idx_shift | input->xkb.idx_mod5 | input->xkb.idx_mod3), 0, keymod_masks[i].xkb_mask & input->xkb.idx_caps,
1348 0, 0, input->xkb.current_group);
1349 WAYLAND_xkb_keymap_key_for_each(input->xkb.keymap,
1350 Wayland_keymap_iter,
1351 &keymap);
1352 }
1353
1354 WAYLAND_xkb_state_unref(keymap.state);
1355 SDL_SetKeymap(keymap.keymap, true);
1356 } else {
1357 // Virtual keyboards use the default keymap.
1358 SDL_SetKeymap(NULL, true);
1359 }
1360}
1361
1362static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
1363 uint32_t format, int fd, uint32_t size)
1364{
1365 struct SDL_WaylandInput *input = data;
1366 char *map_str;
1367 const char *locale;
1368
1369 if (!data) {
1370 close(fd);
1371 return;
1372 }
1373
1374 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
1375 close(fd);
1376 return;
1377 }
1378
1379 map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
1380 if (map_str == MAP_FAILED) {
1381 close(fd);
1382 return;
1383 }
1384
1385 if (input->xkb.keymap != NULL) {
1386 /* if there's already a keymap loaded, throw it away rather than leaking it before
1387 * parsing the new one
1388 */
1389 WAYLAND_xkb_keymap_unref(input->xkb.keymap);
1390 input->xkb.keymap = NULL;
1391 }
1392 input->xkb.keymap = WAYLAND_xkb_keymap_new_from_string(input->display->xkb_context,
1393 map_str,
1394 XKB_KEYMAP_FORMAT_TEXT_V1,
1395 0);
1396 munmap(map_str, size);
1397 close(fd);
1398
1399 if (!input->xkb.keymap) {
1400 SDL_SetError("failed to compile keymap");
1401 return;
1402 }
1403
1404#define GET_MOD_INDEX(mod) \
1405 WAYLAND_xkb_keymap_mod_get_index(input->xkb.keymap, XKB_MOD_NAME_##mod)
1406 input->xkb.idx_shift = 1 << GET_MOD_INDEX(SHIFT);
1407 input->xkb.idx_ctrl = 1 << GET_MOD_INDEX(CTRL);
1408 input->xkb.idx_alt = 1 << GET_MOD_INDEX(ALT);
1409 input->xkb.idx_gui = 1 << GET_MOD_INDEX(LOGO);
1410 input->xkb.idx_mod3 = 1 << GET_MOD_INDEX(MOD3);
1411 input->xkb.idx_mod5 = 1 << GET_MOD_INDEX(MOD5);
1412 input->xkb.idx_num = 1 << GET_MOD_INDEX(NUM);
1413 input->xkb.idx_caps = 1 << GET_MOD_INDEX(CAPS);
1414#undef GET_MOD_INDEX
1415
1416 if (input->xkb.state != NULL) {
1417 /* if there's already a state, throw it away rather than leaking it before
1418 * trying to create a new one with the new keymap.
1419 */
1420 WAYLAND_xkb_state_unref(input->xkb.state);
1421 input->xkb.state = NULL;
1422 }
1423 input->xkb.state = WAYLAND_xkb_state_new(input->xkb.keymap);
1424 if (!input->xkb.state) {
1425 SDL_SetError("failed to create XKB state");
1426 WAYLAND_xkb_keymap_unref(input->xkb.keymap);
1427 input->xkb.keymap = NULL;
1428 return;
1429 }
1430
1431 /*
1432 * Assume that a nameless layout implies a virtual keyboard with an arbitrary layout.
1433 * TODO: Use a better method of detection?
1434 */
1435 input->keyboard_is_virtual = WAYLAND_xkb_keymap_layout_get_name(input->xkb.keymap, 0) == NULL;
1436
1437 // Update the keymap if changed.
1438 if (input->xkb.current_group != XKB_GROUP_INVALID) {
1439 Wayland_UpdateKeymap(input);
1440 }
1441
1442 /*
1443 * See https://blogs.s-osg.org/compose-key-support-weston/
1444 * for further explanation on dead keys in Wayland.
1445 */
1446
1447 // Look up the preferred locale, falling back to "C" as default
1448 locale = SDL_getenv("LC_ALL");
1449 if (!locale) {
1450 locale = SDL_getenv("LC_CTYPE");
1451 if (!locale) {
1452 locale = SDL_getenv("LANG");
1453 if (!locale) {
1454 locale = "C";
1455 }
1456 }
1457 }
1458
1459 // Set up XKB compose table
1460 if (input->xkb.compose_table != NULL) {
1461 WAYLAND_xkb_compose_table_unref(input->xkb.compose_table);
1462 input->xkb.compose_table = NULL;
1463 }
1464 input->xkb.compose_table = WAYLAND_xkb_compose_table_new_from_locale(input->display->xkb_context,
1465 locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
1466 if (input->xkb.compose_table) {
1467 // Set up XKB compose state
1468 if (input->xkb.compose_state != NULL) {
1469 WAYLAND_xkb_compose_state_unref(input->xkb.compose_state);
1470 input->xkb.compose_state = NULL;
1471 }
1472 input->xkb.compose_state = WAYLAND_xkb_compose_state_new(input->xkb.compose_table,
1473 XKB_COMPOSE_STATE_NO_FLAGS);
1474 if (!input->xkb.compose_state) {
1475 SDL_SetError("could not create XKB compose state");
1476 WAYLAND_xkb_compose_table_unref(input->xkb.compose_table);
1477 input->xkb.compose_table = NULL;
1478 }
1479 }
1480}
1481
1482/*
1483 * Virtual keyboards can have arbitrary layouts, arbitrary scancodes/keycodes, etc...
1484 * Key presses from these devices must be looked up by their keysym value.
1485 */
1486static SDL_Scancode Wayland_GetScancodeForKey(struct SDL_WaylandInput *input, uint32_t key)
1487{
1488 SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
1489
1490 if (!input->keyboard_is_virtual) {
1491 scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, key);
1492 } else {
1493 const xkb_keysym_t *syms;
1494 if (WAYLAND_xkb_keymap_key_get_syms_by_level(input->xkb.keymap, key + 8, input->xkb.current_group, 0, &syms) > 0) {
1495 scancode = SDL_GetScancodeFromKeySym(syms[0], key);
1496 }
1497 }
1498
1499 return scancode;
1500}
1501
1502static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input, bool key_pressed)
1503{
1504 /* Handle explicit pressed modifier state. This will correct the modifier state
1505 * if common modifier keys were remapped and the modifiers presumed to be set
1506 * during a key press event were incorrect, or if the modifier was set to the
1507 * pressed state via means other than pressing the physical key.
1508 */
1509 if (!key_pressed) {
1510 if (input->xkb.wl_pressed_modifiers & input->xkb.idx_shift) {
1511 if (!(input->pressed_modifiers & SDL_KMOD_SHIFT)) {
1512 input->pressed_modifiers |= SDL_KMOD_SHIFT;
1513 }
1514 } else {
1515 input->pressed_modifiers &= ~SDL_KMOD_SHIFT;
1516 }
1517
1518 if (input->xkb.wl_pressed_modifiers & input->xkb.idx_ctrl) {
1519 if (!(input->pressed_modifiers & SDL_KMOD_CTRL)) {
1520 input->pressed_modifiers |= SDL_KMOD_CTRL;
1521 }
1522 } else {
1523 input->pressed_modifiers &= ~SDL_KMOD_CTRL;
1524 }
1525
1526 if (input->xkb.wl_pressed_modifiers & input->xkb.idx_alt) {
1527 if (!(input->pressed_modifiers & SDL_KMOD_ALT)) {
1528 input->pressed_modifiers |= SDL_KMOD_ALT;
1529 }
1530 } else {
1531 input->pressed_modifiers &= ~SDL_KMOD_ALT;
1532 }
1533
1534 if (input->xkb.wl_pressed_modifiers & input->xkb.idx_gui) {
1535 if (!(input->pressed_modifiers & SDL_KMOD_GUI)) {
1536 input->pressed_modifiers |= SDL_KMOD_GUI;
1537 }
1538 } else {
1539 input->pressed_modifiers &= ~SDL_KMOD_GUI;
1540 }
1541
1542 /* Note: This is not backwards: in the default keymap, Mod5 is typically
1543 * level 3 shift, and Mod3 is typically level 5 shift.
1544 */
1545 if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mod3) {
1546 if (!(input->pressed_modifiers & SDL_KMOD_LEVEL5)) {
1547 input->pressed_modifiers |= SDL_KMOD_LEVEL5;
1548 }
1549 } else {
1550 input->pressed_modifiers &= ~SDL_KMOD_LEVEL5;
1551 }
1552
1553 if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mod5) {
1554 if (!(input->pressed_modifiers & SDL_KMOD_MODE)) {
1555 input->pressed_modifiers |= SDL_KMOD_MODE;
1556 }
1557 } else {
1558 input->pressed_modifiers &= ~SDL_KMOD_MODE;
1559 }
1560 }
1561
1562 /* If a latch or lock was activated by a keypress, the latch/lock will
1563 * be tied to the specific left/right key that initiated it. Otherwise,
1564 * the ambiguous left/right combo is used.
1565 *
1566 * The modifier will remain active until the latch/lock is released by
1567 * the system.
1568 */
1569 if (input->xkb.wl_locked_modifiers & input->xkb.idx_shift) {
1570 if (input->pressed_modifiers & SDL_KMOD_SHIFT) {
1571 input->locked_modifiers &= ~SDL_KMOD_SHIFT;
1572 input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_SHIFT);
1573 } else if (!(input->locked_modifiers & SDL_KMOD_SHIFT)) {
1574 input->locked_modifiers |= SDL_KMOD_SHIFT;
1575 }
1576 } else {
1577 input->locked_modifiers &= ~SDL_KMOD_SHIFT;
1578 }
1579
1580 if (input->xkb.wl_locked_modifiers & input->xkb.idx_ctrl) {
1581 if (input->pressed_modifiers & SDL_KMOD_CTRL) {
1582 input->locked_modifiers &= ~SDL_KMOD_CTRL;
1583 input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_CTRL);
1584 } else if (!(input->locked_modifiers & SDL_KMOD_CTRL)) {
1585 input->locked_modifiers |= SDL_KMOD_CTRL;
1586 }
1587 } else {
1588 input->locked_modifiers &= ~SDL_KMOD_CTRL;
1589 }
1590
1591 if (input->xkb.wl_locked_modifiers & input->xkb.idx_alt) {
1592 if (input->pressed_modifiers & SDL_KMOD_ALT) {
1593 input->locked_modifiers &= ~SDL_KMOD_ALT;
1594 input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_ALT);
1595 } else if (!(input->locked_modifiers & SDL_KMOD_ALT)) {
1596 input->locked_modifiers |= SDL_KMOD_ALT;
1597 }
1598 } else {
1599 input->locked_modifiers &= ~SDL_KMOD_ALT;
1600 }
1601
1602 if (input->xkb.wl_locked_modifiers & input->xkb.idx_gui) {
1603 if (input->pressed_modifiers & SDL_KMOD_GUI) {
1604 input->locked_modifiers &= ~SDL_KMOD_GUI;
1605 input->locked_modifiers |= (input->pressed_modifiers & SDL_KMOD_GUI);
1606 } else if (!(input->locked_modifiers & SDL_KMOD_GUI)) {
1607 input->locked_modifiers |= SDL_KMOD_GUI;
1608 }
1609 } else {
1610 input->locked_modifiers &= ~SDL_KMOD_GUI;
1611 }
1612
1613 // As above, this is correct: Mod3 is typically level 5 shift, and Mod5 is typically level 3 shift.
1614 if (input->xkb.wl_locked_modifiers & input->xkb.idx_mod3) {
1615 input->locked_modifiers |= SDL_KMOD_LEVEL5;
1616 } else {
1617 input->locked_modifiers &= ~SDL_KMOD_LEVEL5;
1618 }
1619
1620 if (input->xkb.wl_locked_modifiers & input->xkb.idx_mod5) {
1621 input->locked_modifiers |= SDL_KMOD_MODE;
1622 } else {
1623 input->locked_modifiers &= ~SDL_KMOD_MODE;
1624 }
1625
1626 // Capslock and Numlock can only be locked, not pressed.
1627 if (input->xkb.wl_locked_modifiers & input->xkb.idx_caps) {
1628 input->locked_modifiers |= SDL_KMOD_CAPS;
1629 } else {
1630 input->locked_modifiers &= ~SDL_KMOD_CAPS;
1631 }
1632
1633 if (input->xkb.wl_locked_modifiers & input->xkb.idx_num) {
1634 input->locked_modifiers |= SDL_KMOD_NUM;
1635 } else {
1636 input->locked_modifiers &= ~SDL_KMOD_NUM;
1637 }
1638
1639 SDL_SetModState(input->pressed_modifiers | input->locked_modifiers);
1640}
1641
1642static void Wayland_HandleModifierKeys(struct SDL_WaylandInput *input, SDL_Scancode scancode, bool pressed)
1643{
1644 const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
1645 SDL_Keymod mod;
1646
1647 /* SDL clients expect modifier state to be activated at the same time as the
1648 * source keypress, so we set pressed modifier state with the usual modifier
1649 * keys here, as the explicit modifier event won't arrive until after the
1650 * keypress event. If this is wrong, it will be corrected when the explicit
1651 * modifier state is sent at a later time.
1652 */
1653 switch (keycode) {
1654 case SDLK_LSHIFT:
1655 mod = SDL_KMOD_LSHIFT;
1656 break;
1657 case SDLK_RSHIFT:
1658 mod = SDL_KMOD_RSHIFT;
1659 break;
1660 case SDLK_LCTRL:
1661 mod = SDL_KMOD_LCTRL;
1662 break;
1663 case SDLK_RCTRL:
1664 mod = SDL_KMOD_RCTRL;
1665 break;
1666 case SDLK_LALT:
1667 mod = SDL_KMOD_LALT;
1668 break;
1669 case SDLK_RALT:
1670 mod = SDL_KMOD_RALT;
1671 break;
1672 case SDLK_LGUI:
1673 mod = SDL_KMOD_LGUI;
1674 break;
1675 case SDLK_RGUI:
1676 mod = SDL_KMOD_RGUI;
1677 break;
1678 case SDLK_MODE:
1679 mod = SDL_KMOD_MODE;
1680 break;
1681 case SDLK_LEVEL5_SHIFT:
1682 mod = SDL_KMOD_LEVEL5;
1683 break;
1684 default:
1685 return;
1686 }
1687
1688 if (pressed) {
1689 input->pressed_modifiers |= mod;
1690 } else {
1691 input->pressed_modifiers &= ~mod;
1692 }
1693
1694 Wayland_ReconcileModifiers(input, true);
1695}
1696
1697static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
1698 uint32_t serial, struct wl_surface *surface,
1699 struct wl_array *keys)
1700{
1701 struct SDL_WaylandInput *input = data;
1702 SDL_WindowData *window;
1703 uint32_t *key;
1704
1705 if (!surface) {
1706 // enter event for a window we've just destroyed
1707 return;
1708 }
1709
1710 window = Wayland_GetWindowDataForOwnedSurface(surface);
1711
1712 if (!window) {
1713 return;
1714 }
1715
1716 input->keyboard_focus = window;
1717 window->keyboard_device = input;
1718
1719 // Restore the keyboard focus to the child popup that was holding it
1720 SDL_SetKeyboardFocus(window->keyboard_focus ? window->keyboard_focus : window->sdlwindow);
1721
1722#ifdef SDL_USE_IME
1723 if (!input->text_input) {
1724 SDL_IME_SetFocus(true);
1725 }
1726#endif
1727
1728 Uint64 timestamp = SDL_GetTicksNS();
1729 window->last_focus_event_time_ns = timestamp;
1730
1731 wl_array_for_each (key, keys) {
1732 const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, *key);
1733 const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
1734
1735 switch (keycode) {
1736 case SDLK_LSHIFT:
1737 case SDLK_RSHIFT:
1738 case SDLK_LCTRL:
1739 case SDLK_RCTRL:
1740 case SDLK_LALT:
1741 case SDLK_RALT:
1742 case SDLK_LGUI:
1743 case SDLK_RGUI:
1744 case SDLK_MODE:
1745 case SDLK_LEVEL5_SHIFT:
1746 Wayland_HandleModifierKeys(input, scancode, true);
1747 SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, *key, scancode, true);
1748 break;
1749 default:
1750 break;
1751 }
1752 }
1753}
1754
1755static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
1756 uint32_t serial, struct wl_surface *surface)
1757{
1758 struct SDL_WaylandInput *input = data;
1759 SDL_WindowData *wind;
1760 SDL_Window *window = NULL;
1761
1762 if (!surface) {
1763 return;
1764 }
1765
1766 wind = Wayland_GetWindowDataForOwnedSurface(surface);
1767 if (!wind) {
1768 return;
1769 }
1770
1771 wind->keyboard_device = NULL;
1772 window = wind->sdlwindow;
1773
1774 // Stop key repeat before clearing keyboard focus
1775 keyboard_repeat_clear(&input->keyboard_repeat);
1776
1777 // This will release any keys still pressed
1778 SDL_SetKeyboardFocus(NULL);
1779 input->keyboard_focus = NULL;
1780
1781 // Clear the pressed modifiers.
1782 input->pressed_modifiers = SDL_KMOD_NONE;
1783
1784#ifdef SDL_USE_IME
1785 if (!input->text_input) {
1786 SDL_IME_SetFocus(false);
1787 }
1788#endif
1789
1790 /* If the surface had a pointer leave event while still having active touch events, it retained mouse focus.
1791 * Clear it now if all touch events are raised.
1792 */
1793 if (!input->pointer_focus && SDL_GetMouseFocus() == window && !Wayland_SurfaceHasActiveTouches(surface)) {
1794 SDL_SetMouseFocus(NULL);
1795 }
1796}
1797
1798static bool keyboard_input_get_text(char text[8], const struct SDL_WaylandInput *input, uint32_t key, bool down, bool *handled_by_ime)
1799{
1800 SDL_WindowData *window = input->keyboard_focus;
1801 const xkb_keysym_t *syms;
1802 xkb_keysym_t sym;
1803
1804 if (!window || window->keyboard_device != input || !input->xkb.state) {
1805 return false;
1806 }
1807
1808 // TODO: Can this happen?
1809 if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1) {
1810 return false;
1811 }
1812 sym = syms[0];
1813
1814#ifdef SDL_USE_IME
1815 if (SDL_IME_ProcessKeyEvent(sym, key + 8, down)) {
1816 if (handled_by_ime) {
1817 *handled_by_ime = true;
1818 }
1819 return true;
1820 }
1821#endif
1822
1823 if (!down) {
1824 return false;
1825 }
1826
1827 if (input->xkb.compose_state && WAYLAND_xkb_compose_state_feed(input->xkb.compose_state, sym) == XKB_COMPOSE_FEED_ACCEPTED) {
1828 switch (WAYLAND_xkb_compose_state_get_status(input->xkb.compose_state)) {
1829 case XKB_COMPOSE_COMPOSING:
1830 if (handled_by_ime) {
1831 *handled_by_ime = true;
1832 }
1833 return true;
1834 case XKB_COMPOSE_CANCELLED:
1835 default:
1836 sym = XKB_KEY_NoSymbol;
1837 break;
1838 case XKB_COMPOSE_NOTHING:
1839 break;
1840 case XKB_COMPOSE_COMPOSED:
1841 sym = WAYLAND_xkb_compose_state_get_one_sym(input->xkb.compose_state);
1842 break;
1843 }
1844 }
1845
1846 return WAYLAND_xkb_keysym_to_utf8(sym, text, 8) > 0;
1847}
1848
1849static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
1850 uint32_t serial, uint32_t time, uint32_t key,
1851 uint32_t state_w)
1852{
1853 struct SDL_WaylandInput *input = data;
1854 enum wl_keyboard_key_state state = state_w;
1855 char text[8];
1856 bool has_text = false;
1857 bool handled_by_ime = false;
1858 const Uint64 timestamp_raw_ns = Wayland_GetKeyboardTimestampRaw(input, time);
1859
1860 Wayland_UpdateImplicitGrabSerial(input, serial);
1861
1862 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
1863 SDL_Window *keyboard_focus = SDL_GetKeyboardFocus();
1864 if (keyboard_focus && SDL_TextInputActive(keyboard_focus)) {
1865 has_text = keyboard_input_get_text(text, input, key, true, &handled_by_ime);
1866 }
1867 } else {
1868 if (keyboard_repeat_key_is_set(&input->keyboard_repeat, key)) {
1869 /* Send any due key repeat events before stopping the repeat and generating the key up event.
1870 * Compute time based on the Wayland time, as it reports when the release event happened.
1871 * Using SDL_GetTicks would be wrong, as it would report when the release event is processed,
1872 * which may be off if the application hasn't pumped events for a while.
1873 */
1874 keyboard_repeat_handle(&input->keyboard_repeat, timestamp_raw_ns - input->keyboard_repeat.wl_press_time_ns);
1875 keyboard_repeat_clear(&input->keyboard_repeat);
1876 }
1877 keyboard_input_get_text(text, input, key, false, &handled_by_ime);
1878 }
1879
1880 const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, key);
1881 Wayland_HandleModifierKeys(input, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
1882 Uint64 timestamp = Wayland_GetKeyboardTimestamp(input, time);
1883
1884 SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
1885
1886 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
1887 if (has_text && !(SDL_GetModState() & SDL_KMOD_CTRL)) {
1888 if (!handled_by_ime) {
1889 SDL_SendKeyboardText(text);
1890 }
1891 }
1892 if (input->xkb.keymap && WAYLAND_xkb_keymap_key_repeats(input->xkb.keymap, key + 8)) {
1893 keyboard_repeat_set(&input->keyboard_repeat, input->keyboard_id, key, timestamp_raw_ns, scancode, has_text, text);
1894 }
1895 }
1896}
1897
1898static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
1899 uint32_t serial, uint32_t mods_depressed,
1900 uint32_t mods_latched, uint32_t mods_locked,
1901 uint32_t group)
1902{
1903 struct SDL_WaylandInput *input = data;
1904
1905 if (input->xkb.state == NULL) {
1906 /* if we get a modifier notification before the keymap, there's nothing we can do with the information
1907 */
1908 return;
1909 }
1910
1911 WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
1912 mods_locked, 0, 0, group);
1913
1914 input->xkb.wl_pressed_modifiers = mods_depressed;
1915 input->xkb.wl_locked_modifiers = mods_latched | mods_locked;
1916
1917 Wayland_ReconcileModifiers(input, false);
1918
1919 // If a key is repeating, update the text to apply the modifier.
1920 if (keyboard_repeat_is_set(&input->keyboard_repeat)) {
1921 char text[8];
1922 const uint32_t key = keyboard_repeat_get_key(&input->keyboard_repeat);
1923
1924 if (keyboard_input_get_text(text, input, key, true, NULL)) {
1925 keyboard_repeat_set_text(&input->keyboard_repeat, text);
1926 }
1927 }
1928
1929 if (group == input->xkb.current_group) {
1930 return;
1931 }
1932
1933 // The layout changed, remap and fire an event. Virtual keyboards use the default keymap.
1934 input->xkb.current_group = group;
1935 Wayland_UpdateKeymap(input);
1936}
1937
1938static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
1939 int32_t rate, int32_t delay)
1940{
1941 struct SDL_WaylandInput *input = data;
1942 input->keyboard_repeat.repeat_rate = SDL_clamp(rate, 0, 1000);
1943 input->keyboard_repeat.repeat_delay_ms = delay;
1944 input->keyboard_repeat.is_initialized = true;
1945}
1946
1947static const struct wl_keyboard_listener keyboard_listener = {
1948 keyboard_handle_keymap,
1949 keyboard_handle_enter,
1950 keyboard_handle_leave,
1951 keyboard_handle_key,
1952 keyboard_handle_modifiers,
1953 keyboard_handle_repeat_info, // Version 4
1954};
1955
1956void Wayland_input_init_relative_pointer(SDL_VideoData *d)
1957{
1958 struct SDL_WaylandInput *input = d->input;
1959
1960 if (!d->relative_pointer_manager) {
1961 return;
1962 }
1963
1964 if (input->pointer && !input->relative_pointer) {
1965 input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(input->display->relative_pointer_manager, input->pointer);
1966 zwp_relative_pointer_v1_add_listener(input->relative_pointer,
1967 &relative_pointer_listener,
1968 input);
1969 }
1970}
1971
1972static void seat_handle_capabilities(void *data, struct wl_seat *seat,
1973 enum wl_seat_capability caps)
1974{
1975 struct SDL_WaylandInput *input = data;
1976
1977 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
1978 input->pointer = wl_seat_get_pointer(seat);
1979 SDL_memset(&input->pointer_curr_axis_info, 0, sizeof(input->pointer_curr_axis_info));
1980 input->display->pointer = input->pointer;
1981
1982 Wayland_CreateCursorShapeDevice(input);
1983
1984 wl_pointer_set_user_data(input->pointer, input);
1985 wl_pointer_add_listener(input->pointer, &pointer_listener, input);
1986
1987 Wayland_input_init_relative_pointer(input->display);
1988
1989 input->pointer_id = SDL_GetNextObjectID();
1990 SDL_AddMouse(input->pointer_id, WAYLAND_DEFAULT_POINTER_NAME, !input->display->initializing);
1991 } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
1992 if (input->relative_pointer) {
1993 zwp_relative_pointer_v1_destroy(input->relative_pointer);
1994 input->relative_pointer = NULL;
1995 }
1996 if (input->cursor_shape) {
1997 wp_cursor_shape_device_v1_destroy(input->cursor_shape);
1998 input->cursor_shape = NULL;
1999 }
2000 if (wl_pointer_get_version(input->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) {
2001 wl_pointer_release(input->pointer);
2002 } else {
2003 wl_pointer_destroy(input->pointer);
2004 }
2005 input->pointer = NULL;
2006 input->display->pointer = NULL;
2007
2008 SDL_RemoveMouse(input->pointer_id, true);
2009 input->pointer_id = 0;
2010 }
2011
2012 if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
2013 input->touch = wl_seat_get_touch(seat);
2014 SDL_AddTouch((SDL_TouchID)(uintptr_t)input->touch, SDL_TOUCH_DEVICE_DIRECT, "wayland_touch");
2015 wl_touch_set_user_data(input->touch, input);
2016 wl_touch_add_listener(input->touch, &touch_listener,
2017 input);
2018 } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
2019 SDL_DelTouch((SDL_TouchID)(intptr_t)input->touch);
2020 wl_touch_destroy(input->touch);
2021 input->touch = NULL;
2022 }
2023
2024 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
2025 input->keyboard = wl_seat_get_keyboard(seat);
2026 wl_keyboard_set_user_data(input->keyboard, input);
2027 wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
2028 input);
2029
2030 input->keyboard_id = SDL_GetNextObjectID();
2031 SDL_AddKeyboard(input->keyboard_id, WAYLAND_DEFAULT_KEYBOARD_NAME, !input->display->initializing);
2032 } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
2033 wl_keyboard_destroy(input->keyboard);
2034 input->keyboard = NULL;
2035
2036 SDL_RemoveKeyboard(input->keyboard_id, true);
2037 input->keyboard_id = 0;
2038 }
2039
2040 Wayland_RegisterTimestampListeners(input);
2041}
2042
2043static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
2044{
2045 // unimplemented
2046}
2047
2048static const struct wl_seat_listener seat_listener = {
2049 seat_handle_capabilities,
2050 seat_handle_name, // Version 2
2051};
2052
2053static void data_source_handle_target(void *data, struct wl_data_source *wl_data_source,
2054 const char *mime_type)
2055{
2056}
2057
2058static void data_source_handle_send(void *data, struct wl_data_source *wl_data_source,
2059 const char *mime_type, int32_t fd)
2060{
2061 Wayland_data_source_send((SDL_WaylandDataSource *)data, mime_type, fd);
2062}
2063
2064static void data_source_handle_cancelled(void *data, struct wl_data_source *wl_data_source)
2065{
2066 SDL_WaylandDataSource *source = data;
2067 if (source) {
2068 Wayland_data_source_destroy(source);
2069 }
2070}
2071
2072static void data_source_handle_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source)
2073{
2074}
2075
2076static void data_source_handle_dnd_finished(void *data, struct wl_data_source *wl_data_source)
2077{
2078}
2079
2080static void data_source_handle_action(void *data, struct wl_data_source *wl_data_source,
2081 uint32_t dnd_action)
2082{
2083}
2084
2085static const struct wl_data_source_listener data_source_listener = {
2086 data_source_handle_target,
2087 data_source_handle_send,
2088 data_source_handle_cancelled,
2089 data_source_handle_dnd_drop_performed, // Version 3
2090 data_source_handle_dnd_finished, // Version 3
2091 data_source_handle_action, // Version 3
2092};
2093
2094static void primary_selection_source_send(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1,
2095 const char *mime_type, int32_t fd)
2096{
2097 Wayland_primary_selection_source_send((SDL_WaylandPrimarySelectionSource *)data,
2098 mime_type, fd);
2099}
2100
2101static void primary_selection_source_cancelled(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1)
2102{
2103 Wayland_primary_selection_source_destroy(data);
2104}
2105
2106static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener = {
2107 primary_selection_source_send,
2108 primary_selection_source_cancelled,
2109};
2110
2111SDL_WaylandDataSource *Wayland_data_source_create(SDL_VideoDevice *_this)
2112{
2113 SDL_WaylandDataSource *data_source = NULL;
2114 SDL_VideoData *driver_data = NULL;
2115 struct wl_data_source *id = NULL;
2116
2117 if (!_this || !_this->internal) {
2118 SDL_SetError("Video driver uninitialized");
2119 } else {
2120 driver_data = _this->internal;
2121
2122 if (driver_data->data_device_manager) {
2123 id = wl_data_device_manager_create_data_source(
2124 driver_data->data_device_manager);
2125 }
2126
2127 if (!id) {
2128 SDL_SetError("Wayland unable to create data source");
2129 } else {
2130 data_source = SDL_calloc(1, sizeof(*data_source));
2131 if (!data_source) {
2132 wl_data_source_destroy(id);
2133 } else {
2134 data_source->source = id;
2135 wl_data_source_set_user_data(id, data_source);
2136 wl_data_source_add_listener(id, &data_source_listener,
2137 data_source);
2138 }
2139 }
2140 }
2141 return data_source;
2142}
2143
2144SDL_WaylandPrimarySelectionSource *Wayland_primary_selection_source_create(SDL_VideoDevice *_this)
2145{
2146 SDL_WaylandPrimarySelectionSource *primary_selection_source = NULL;
2147 SDL_VideoData *driver_data = NULL;
2148 struct zwp_primary_selection_source_v1 *id = NULL;
2149
2150 if (!_this || !_this->internal) {
2151 SDL_SetError("Video driver uninitialized");
2152 } else {
2153 driver_data = _this->internal;
2154
2155 if (driver_data->primary_selection_device_manager) {
2156 id = zwp_primary_selection_device_manager_v1_create_source(
2157 driver_data->primary_selection_device_manager);
2158 }
2159
2160 if (!id) {
2161 SDL_SetError("Wayland unable to create primary selection source");
2162 } else {
2163 primary_selection_source = SDL_calloc(1, sizeof(*primary_selection_source));
2164 if (!primary_selection_source) {
2165 zwp_primary_selection_source_v1_destroy(id);
2166 } else {
2167 primary_selection_source->source = id;
2168 zwp_primary_selection_source_v1_add_listener(id, &primary_selection_source_listener,
2169 primary_selection_source);
2170 }
2171 }
2172 }
2173 return primary_selection_source;
2174}
2175
2176static void data_offer_handle_offer(void *data, struct wl_data_offer *wl_data_offer,
2177 const char *mime_type)
2178{
2179 SDL_WaylandDataOffer *offer = data;
2180 Wayland_data_offer_add_mime(offer, mime_type);
2181 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2182 ". In wl_data_offer_listener . data_offer_handle_offer on data_offer 0x%08x for MIME '%s'",
2183 (wl_data_offer ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)wl_data_offer) : -1),
2184 mime_type);
2185}
2186
2187static void data_offer_handle_source_actions(void *data, struct wl_data_offer *wl_data_offer,
2188 uint32_t source_actions)
2189{
2190 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2191 ". In wl_data_offer_listener . data_offer_handle_source_actions on data_offer 0x%08x for Source Actions '%d'",
2192 (wl_data_offer ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)wl_data_offer) : -1),
2193 source_actions);
2194}
2195
2196static void data_offer_handle_actions(void *data, struct wl_data_offer *wl_data_offer,
2197 uint32_t dnd_action)
2198{
2199 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2200 ". In wl_data_offer_listener . data_offer_handle_actions on data_offer 0x%08x for DND Actions '%d'",
2201 (wl_data_offer ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)wl_data_offer) : -1),
2202 dnd_action);
2203}
2204
2205static const struct wl_data_offer_listener data_offer_listener = {
2206 data_offer_handle_offer,
2207 data_offer_handle_source_actions, // Version 3
2208 data_offer_handle_actions, // Version 3
2209};
2210
2211static void primary_selection_offer_handle_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1,
2212 const char *mime_type)
2213{
2214 SDL_WaylandPrimarySelectionOffer *offer = data;
2215 Wayland_primary_selection_offer_add_mime(offer, mime_type);
2216 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2217 ". In zwp_primary_selection_offer_v1_listener . primary_selection_offer_handle_offer on primary_selection_offer 0x%08x for MIME '%s'",
2218 (zwp_primary_selection_offer_v1 ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)zwp_primary_selection_offer_v1) : -1),
2219 mime_type);
2220}
2221
2222static const struct zwp_primary_selection_offer_v1_listener primary_selection_offer_listener = {
2223 primary_selection_offer_handle_offer,
2224};
2225
2226static void data_device_handle_data_offer(void *data, struct wl_data_device *wl_data_device,
2227 struct wl_data_offer *id)
2228{
2229 SDL_WaylandDataOffer *data_offer = SDL_calloc(1, sizeof(*data_offer));
2230 if (data_offer) {
2231 data_offer->offer = id;
2232 data_offer->data_device = data;
2233 WAYLAND_wl_list_init(&(data_offer->mimes));
2234 wl_data_offer_set_user_data(id, data_offer);
2235 wl_data_offer_add_listener(id, &data_offer_listener, data_offer);
2236 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2237 ". In wl_data_device_listener . data_device_handle_data_offer on data_offer 0x%08x",
2238 (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
2239 }
2240}
2241
2242static void data_device_handle_enter(void *data, struct wl_data_device *wl_data_device,
2243 uint32_t serial, struct wl_surface *surface,
2244 wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id)
2245{
2246 SDL_WaylandDataDevice *data_device = data;
2247 data_device->has_mime_file = false;
2248 data_device->has_mime_text = false;
2249 uint32_t dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
2250
2251 data_device->drag_serial = serial;
2252
2253 if (id) {
2254 data_device->drag_offer = wl_data_offer_get_user_data(id);
2255
2256 // TODO: SDL Support more mime types
2257#ifdef SDL_USE_LIBDBUS
2258 if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) {
2259 data_device->has_mime_file = true;
2260 wl_data_offer_accept(id, serial, FILE_PORTAL_MIME);
2261 }
2262#endif
2263 if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_MIME)) {
2264 data_device->has_mime_file = true;
2265 wl_data_offer_accept(id, serial, FILE_MIME);
2266 }
2267
2268 if (Wayland_data_offer_has_mime(data_device->drag_offer, TEXT_MIME)) {
2269 data_device->has_mime_text = true;
2270 wl_data_offer_accept(id, serial, TEXT_MIME);
2271 }
2272
2273 // SDL only supports "copy" style drag and drop
2274 if (data_device->has_mime_file || data_device->has_mime_text) {
2275 dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
2276 } else {
2277 // drag_mime is NULL this will decline the offer
2278 wl_data_offer_accept(id, serial, NULL);
2279 }
2280 if (wl_data_offer_get_version(data_device->drag_offer->offer) >=
2281 WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) {
2282 wl_data_offer_set_actions(data_device->drag_offer->offer,
2283 dnd_action, dnd_action);
2284 }
2285
2286 // find the current window
2287 if (surface) {
2288 SDL_WindowData *window = Wayland_GetWindowDataForOwnedSurface(surface);
2289 if (window) {
2290 data_device->dnd_window = window->sdlwindow;
2291 const float dx = (float)wl_fixed_to_double(x);
2292 const float dy = (float)wl_fixed_to_double(y);
2293 SDL_SendDropPosition(data_device->dnd_window, dx, dy);
2294 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2295 ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d into window %d for serial %d",
2296 WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
2297 wl_fixed_to_int(x), wl_fixed_to_int(y), SDL_GetWindowID(data_device->dnd_window), serial);
2298 } else {
2299 data_device->dnd_window = NULL;
2300 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2301 ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d",
2302 WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
2303 wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
2304 }
2305 } else {
2306 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2307 ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d",
2308 WAYLAND_wl_proxy_get_id((struct wl_proxy *)id),
2309 wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
2310 }
2311 } else {
2312 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2313 ". In wl_data_device_listener . data_device_handle_enter on data_offer 0x%08x at %d x %d for serial %d",
2314 -1, wl_fixed_to_int(x), wl_fixed_to_int(y), serial);
2315 }
2316}
2317
2318static void data_device_handle_leave(void *data, struct wl_data_device *wl_data_device)
2319{
2320 SDL_WaylandDataDevice *data_device = data;
2321
2322 if (data_device->drag_offer) {
2323 if (data_device->dnd_window) {
2324 SDL_SendDropComplete(data_device->dnd_window);
2325 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2326 ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x from window %d for serial %d",
2327 WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
2328 SDL_GetWindowID(data_device->dnd_window), data_device->drag_serial);
2329 } else {
2330 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2331 ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x for serial %d",
2332 WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
2333 data_device->drag_serial);
2334 }
2335 Wayland_data_offer_destroy(data_device->drag_offer);
2336 data_device->drag_offer = NULL;
2337 } else {
2338 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2339 ". In wl_data_device_listener . data_device_handle_leave on data_offer 0x%08x for serial %d",
2340 -1, -1);
2341 }
2342 data_device->has_mime_file = false;
2343 data_device->has_mime_text = false;
2344}
2345
2346static void data_device_handle_motion(void *data, struct wl_data_device *wl_data_device,
2347 uint32_t time, wl_fixed_t x, wl_fixed_t y)
2348{
2349 SDL_WaylandDataDevice *data_device = data;
2350
2351 if (data_device->drag_offer && data_device->dnd_window && (data_device->has_mime_file || data_device->has_mime_text)) {
2352 const float dx = (float)wl_fixed_to_double(x);
2353 const float dy = (float)wl_fixed_to_double(y);
2354
2355 /* XXX: Send the filename here if the event system ever starts passing it though.
2356 * Any future implementation should cache the filenames, as otherwise this could
2357 * hammer the DBus interface hundreds or even thousands of times per second.
2358 */
2359 SDL_SendDropPosition(data_device->dnd_window, dx, dy);
2360 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2361 ". In wl_data_device_listener . data_device_handle_motion on data_offer 0x%08x at %d x %d in window %d serial %d",
2362 WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
2363 wl_fixed_to_int(x), wl_fixed_to_int(y),
2364 SDL_GetWindowID(data_device->dnd_window), data_device->drag_serial);
2365 } else {
2366 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2367 ". In wl_data_device_listener . data_device_handle_motion on data_offer 0x%08x at %d x %d serial %d",
2368 -1, wl_fixed_to_int(x), wl_fixed_to_int(y), -1);
2369 }
2370}
2371
2372static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_device)
2373{
2374 SDL_WaylandDataDevice *data_device = data;
2375
2376 if (data_device->drag_offer && data_device->dnd_window && (data_device->has_mime_file || data_device->has_mime_text)) {
2377 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2378 ". In wl_data_device_listener . data_device_handle_drop on data_offer 0x%08x in window %d serial %d",
2379 WAYLAND_wl_proxy_get_id((struct wl_proxy *)data_device->drag_offer->offer),
2380 SDL_GetWindowID(data_device->dnd_window), data_device->drag_serial);
2381 // TODO: SDL Support more mime types
2382 size_t length;
2383 bool drop_handled = false;
2384#ifdef SDL_USE_LIBDBUS
2385 if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) {
2386 void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
2387 FILE_PORTAL_MIME, &length);
2388 if (buffer) {
2389 SDL_DBusContext *dbus = SDL_DBus_GetContext();
2390 if (dbus) {
2391 int path_count = 0;
2392 char **paths = SDL_DBus_DocumentsPortalRetrieveFiles(buffer, &path_count);
2393 // If dropped files contain a directory the list is empty
2394 if (paths && path_count > 0) {
2395 int i;
2396 for (i = 0; i < path_count; i++) {
2397 SDL_SendDropFile(data_device->dnd_window, NULL, paths[i]);
2398 }
2399 dbus->free_string_array(paths);
2400 SDL_SendDropComplete(data_device->dnd_window);
2401 drop_handled = true;
2402 }
2403 }
2404 SDL_free(buffer);
2405 }
2406 }
2407#endif
2408 /* If XDG document portal fails fallback.
2409 * When running a flatpak sandbox this will most likely be a list of
2410 * non paths that are not visible to the application
2411 */
2412 if (!drop_handled) {
2413 const char *mime_type = data_device->has_mime_file ? FILE_MIME : (data_device->has_mime_text ? TEXT_MIME : "");
2414 void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
2415 mime_type, &length);
2416 if (data_device->has_mime_file) {
2417 if (buffer) {
2418 char *saveptr = NULL;
2419 char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);
2420 while (token) {
2421 if (SDL_URIToLocal(token, token) >= 0) {
2422 SDL_SendDropFile(data_device->dnd_window, NULL, token);
2423 }
2424 token = SDL_strtok_r(NULL, "\r\n", &saveptr);
2425 }
2426 SDL_free(buffer);
2427 SDL_SendDropComplete(data_device->dnd_window);
2428 } else {
2429 SDL_SendDropComplete(data_device->dnd_window);
2430 }
2431 drop_handled = true;
2432 } else if (data_device->has_mime_text) {
2433 if (buffer) {
2434 char *saveptr = NULL;
2435 char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);
2436 while (token) {
2437 SDL_SendDropText(data_device->dnd_window, token);
2438 token = SDL_strtok_r(NULL, "\r\n", &saveptr);
2439 }
2440 SDL_free(buffer);
2441 SDL_SendDropComplete(data_device->dnd_window);
2442 } else {
2443 /* Even though there has been a valid data offer,
2444 * and there have been valid Enter, Motion, and Drop callbacks,
2445 * Wayland_data_offer_receive may return an empty buffer,
2446 * because the data is actually in the primary selection device,
2447 * not in the data device.
2448 */
2449 SDL_SendDropComplete(data_device->dnd_window);
2450 }
2451 drop_handled = true;
2452 }
2453 }
2454
2455 if (drop_handled && wl_data_offer_get_version(data_device->drag_offer->offer) >= WL_DATA_OFFER_FINISH_SINCE_VERSION) {
2456 wl_data_offer_finish(data_device->drag_offer->offer);
2457 }
2458 } else {
2459 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2460 ". In wl_data_device_listener . data_device_handle_drop on data_offer 0x%08x serial %d",
2461 -1, -1);
2462 }
2463
2464 Wayland_data_offer_destroy(data_device->drag_offer);
2465 data_device->drag_offer = NULL;
2466}
2467
2468static void notifyFromMimes(struct wl_list *mimes)
2469{
2470 int nformats = 0;
2471 char **new_mime_types = NULL;
2472 if (mimes) {
2473 nformats = WAYLAND_wl_list_length(mimes);
2474 size_t alloc_size = (nformats + 1) * sizeof(char *);
2475
2476 /* do a first pass to compute allocation size */
2477 SDL_MimeDataList *item = NULL;
2478 wl_list_for_each(item, mimes, link) {
2479 alloc_size += SDL_strlen(item->mime_type) + 1;
2480 }
2481
2482 new_mime_types = SDL_AllocateTemporaryMemory(alloc_size);
2483 if (!new_mime_types) {
2484 SDL_LogError(SDL_LOG_CATEGORY_INPUT, "unable to allocate new_mime_types");
2485 return;
2486 }
2487
2488 /* second pass to fill*/
2489 char *strPtr = (char *)(new_mime_types + nformats + 1);
2490 item = NULL;
2491 int i = 0;
2492 wl_list_for_each(item, mimes, link) {
2493 new_mime_types[i] = strPtr;
2494 strPtr = stpcpy(strPtr, item->mime_type) + 1;
2495 i++;
2496 }
2497 new_mime_types[nformats] = NULL;
2498 }
2499
2500 SDL_SendClipboardUpdate(false, new_mime_types, nformats);
2501}
2502
2503static void data_device_handle_selection(void *data, struct wl_data_device *wl_data_device,
2504 struct wl_data_offer *id)
2505{
2506 SDL_WaylandDataDevice *data_device = data;
2507 SDL_WaylandDataOffer *offer = NULL;
2508
2509 if (id) {
2510 offer = wl_data_offer_get_user_data(id);
2511 }
2512
2513 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2514 ". In data_device_listener . data_device_handle_selection on data_offer 0x%08x",
2515 (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
2516 if (data_device->selection_offer != offer) {
2517 Wayland_data_offer_destroy(data_device->selection_offer);
2518 data_device->selection_offer = offer;
2519 }
2520
2521 notifyFromMimes(offer ? &offer->mimes : NULL);
2522}
2523
2524static const struct wl_data_device_listener data_device_listener = {
2525 data_device_handle_data_offer,
2526 data_device_handle_enter,
2527 data_device_handle_leave,
2528 data_device_handle_motion,
2529 data_device_handle_drop,
2530 data_device_handle_selection
2531};
2532
2533static void primary_selection_device_handle_offer(void *data, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1,
2534 struct zwp_primary_selection_offer_v1 *id)
2535{
2536 SDL_WaylandPrimarySelectionOffer *primary_selection_offer = SDL_calloc(1, sizeof(*primary_selection_offer));
2537 if (primary_selection_offer) {
2538 primary_selection_offer->offer = id;
2539 primary_selection_offer->primary_selection_device = data;
2540 WAYLAND_wl_list_init(&(primary_selection_offer->mimes));
2541 zwp_primary_selection_offer_v1_set_user_data(id, primary_selection_offer);
2542 zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, primary_selection_offer);
2543 }
2544 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2545 ". In zwp_primary_selection_device_v1_listener . primary_selection_device_handle_offer on primary_selection_offer 0x%08x",
2546 (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
2547}
2548
2549static void primary_selection_device_handle_selection(void *data, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1,
2550 struct zwp_primary_selection_offer_v1 *id)
2551{
2552 SDL_WaylandPrimarySelectionDevice *primary_selection_device = data;
2553 SDL_WaylandPrimarySelectionOffer *offer = NULL;
2554
2555 if (id) {
2556 offer = zwp_primary_selection_offer_v1_get_user_data(id);
2557 }
2558
2559 if (primary_selection_device->selection_offer != offer) {
2560 Wayland_primary_selection_offer_destroy(primary_selection_device->selection_offer);
2561 primary_selection_device->selection_offer = offer;
2562 }
2563 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT,
2564 ". In zwp_primary_selection_device_v1_listener . primary_selection_device_handle_selection on primary_selection_offer 0x%08x",
2565 (id ? WAYLAND_wl_proxy_get_id((struct wl_proxy *)id) : -1));
2566}
2567
2568static const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = {
2569 primary_selection_device_handle_offer,
2570 primary_selection_device_handle_selection
2571};
2572
2573static void text_input_enter(void *data,
2574 struct zwp_text_input_v3 *zwp_text_input_v3,
2575 struct wl_surface *surface)
2576{
2577 // No-op
2578}
2579
2580static void text_input_leave(void *data,
2581 struct zwp_text_input_v3 *zwp_text_input_v3,
2582 struct wl_surface *surface)
2583{
2584 // No-op
2585}
2586
2587static void text_input_preedit_string(void *data,
2588 struct zwp_text_input_v3 *zwp_text_input_v3,
2589 const char *text,
2590 int32_t cursor_begin,
2591 int32_t cursor_end)
2592{
2593 SDL_WaylandTextInput *text_input = data;
2594 text_input->has_preedit = true;
2595 if (text) {
2596 int cursor_begin_utf8 = cursor_begin >= 0 ? (int)SDL_utf8strnlen(text, cursor_begin) : -1;
2597 int cursor_end_utf8 = cursor_end >= 0 ? (int)SDL_utf8strnlen(text, cursor_end) : -1;
2598 int cursor_size_utf8;
2599 if (cursor_end_utf8 >= 0) {
2600 if (cursor_begin_utf8 >= 0) {
2601 cursor_size_utf8 = cursor_end_utf8 - cursor_begin_utf8;
2602 } else {
2603 cursor_size_utf8 = cursor_end_utf8;
2604 }
2605 } else {
2606 cursor_size_utf8 = -1;
2607 }
2608 SDL_SendEditingText(text, cursor_begin_utf8, cursor_size_utf8);
2609 } else {
2610 SDL_SendEditingText("", 0, 0);
2611 }
2612}
2613
2614static void text_input_commit_string(void *data,
2615 struct zwp_text_input_v3 *zwp_text_input_v3,
2616 const char *text)
2617{
2618 SDL_SendKeyboardText(text);
2619}
2620
2621static void text_input_delete_surrounding_text(void *data,
2622 struct zwp_text_input_v3 *zwp_text_input_v3,
2623 uint32_t before_length,
2624 uint32_t after_length)
2625{
2626 // FIXME: Do we care about this event?
2627}
2628
2629static void text_input_done(void *data,
2630 struct zwp_text_input_v3 *zwp_text_input_v3,
2631 uint32_t serial)
2632{
2633 SDL_WaylandTextInput *text_input = data;
2634 if (!text_input->has_preedit) {
2635 SDL_SendEditingText("", 0, 0);
2636 }
2637 text_input->has_preedit = false;
2638}
2639
2640static const struct zwp_text_input_v3_listener text_input_listener = {
2641 text_input_enter,
2642 text_input_leave,
2643 text_input_preedit_string,
2644 text_input_commit_string,
2645 text_input_delete_surrounding_text,
2646 text_input_done
2647};
2648
2649void Wayland_create_data_device(SDL_VideoData *d)
2650{
2651 SDL_WaylandDataDevice *data_device = NULL;
2652
2653 if (!d->input->seat) {
2654 // No seat yet, will be initialized later.
2655 return;
2656 }
2657
2658 data_device = SDL_calloc(1, sizeof(*data_device));
2659 if (!data_device) {
2660 return;
2661 }
2662
2663 data_device->data_device = wl_data_device_manager_get_data_device(
2664 d->data_device_manager, d->input->seat);
2665 data_device->video_data = d;
2666
2667 if (!data_device->data_device) {
2668 SDL_free(data_device);
2669 } else {
2670 wl_data_device_set_user_data(data_device->data_device, data_device);
2671 wl_data_device_add_listener(data_device->data_device,
2672 &data_device_listener, data_device);
2673 d->input->data_device = data_device;
2674 }
2675}
2676
2677void Wayland_create_primary_selection_device(SDL_VideoData *d)
2678{
2679 SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
2680
2681 if (!d->input->seat) {
2682 // No seat yet, will be initialized later.
2683 return;
2684 }
2685
2686 primary_selection_device = SDL_calloc(1, sizeof(*primary_selection_device));
2687 if (!primary_selection_device) {
2688 return;
2689 }
2690
2691 primary_selection_device->primary_selection_device = zwp_primary_selection_device_manager_v1_get_device(
2692 d->primary_selection_device_manager, d->input->seat);
2693 primary_selection_device->video_data = d;
2694
2695 if (!primary_selection_device->primary_selection_device) {
2696 SDL_free(primary_selection_device);
2697 } else {
2698 zwp_primary_selection_device_v1_set_user_data(primary_selection_device->primary_selection_device,
2699 primary_selection_device);
2700 zwp_primary_selection_device_v1_add_listener(primary_selection_device->primary_selection_device,
2701 &primary_selection_device_listener, primary_selection_device);
2702 d->input->primary_selection_device = primary_selection_device;
2703 }
2704}
2705
2706static void Wayland_create_text_input(SDL_VideoData *d)
2707{
2708 SDL_WaylandTextInput *text_input = NULL;
2709
2710 if (!d->input->seat) {
2711 // No seat yet, will be initialized later.
2712 return;
2713 }
2714
2715 text_input = SDL_calloc(1, sizeof(*text_input));
2716 if (!text_input) {
2717 return;
2718 }
2719
2720 text_input->text_input = zwp_text_input_manager_v3_get_text_input(
2721 d->text_input_manager, d->input->seat);
2722
2723 if (!text_input->text_input) {
2724 SDL_free(text_input);
2725 } else {
2726 zwp_text_input_v3_set_user_data(text_input->text_input, text_input);
2727 zwp_text_input_v3_add_listener(text_input->text_input,
2728 &text_input_listener, text_input);
2729 d->input->text_input = text_input;
2730 }
2731}
2732
2733void Wayland_create_text_input_manager(SDL_VideoData *d, uint32_t id)
2734{
2735#ifdef HAVE_FCITX
2736 const char *im_module = SDL_getenv("SDL_IM_MODULE");
2737 if (im_module && SDL_strcmp(im_module, "fcitx") == 0) {
2738 /* Override the Wayland text-input protocol when Fcitx is enabled, like how GTK_IM_MODULE does.
2739 *
2740 * The Fcitx wiki discourages enabling it under Wayland via SDL_IM_MODULE, so its presence must
2741 * be intentional, and this workaround is needed for fixing key repeat detection.
2742 */
2743 return;
2744 }
2745#endif
2746
2747 d->text_input_manager = wl_registry_bind(d->registry, id, &zwp_text_input_manager_v3_interface, 1);
2748 Wayland_create_text_input(d);
2749}
2750
2751// Pen/Tablet support...
2752
2753typedef struct SDL_WaylandPenTool // a stylus, etc, on a tablet.
2754{
2755 SDL_PenID instance_id;
2756 SDL_PenInfo info;
2757 SDL_Window *tool_focus;
2758 struct zwp_tablet_tool_v2 *wltool;
2759 float x;
2760 float y;
2761 bool frame_motion_set;
2762 float frame_axes[SDL_PEN_AXIS_COUNT];
2763 Uint32 frame_axes_set;
2764 int frame_pen_down;
2765 int frame_buttons[3];
2766} SDL_WaylandPenTool;
2767
2768static void tablet_tool_handle_type(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t type)
2769{
2770 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2771 switch (type) {
2772 #define CASE(typ) case ZWP_TABLET_TOOL_V2_TYPE_##typ: sdltool->info.subtype = SDL_PEN_TYPE_##typ; return
2773 CASE(ERASER);
2774 CASE(PEN);
2775 CASE(PENCIL);
2776 CASE(AIRBRUSH);
2777 CASE(BRUSH);
2778 #undef CASE
2779 default: sdltool->info.subtype = SDL_PEN_TYPE_UNKNOWN; // we'll decline to add this when the `done` event comes through.
2780 }
2781}
2782
2783static void tablet_tool_handle_hardware_serial(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial_hi, uint32_t serial_lo)
2784{
2785 // don't care about this atm.
2786}
2787
2788static void tablet_tool_handle_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t id_hi, uint32_t id_lo)
2789{
2790 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2791 sdltool->info.wacom_id = id_lo;
2792}
2793
2794static void tablet_tool_handle_capability(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t capability)
2795{
2796 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2797 switch (capability) {
2798 #define CASE(wltyp,sdltyp) case ZWP_TABLET_TOOL_V2_CAPABILITY_##wltyp: sdltool->info.capabilities |= sdltyp; return
2799 CASE(TILT, SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT);
2800 CASE(PRESSURE, SDL_PEN_CAPABILITY_PRESSURE);
2801 CASE(DISTANCE, SDL_PEN_CAPABILITY_DISTANCE);
2802 CASE(ROTATION, SDL_PEN_CAPABILITY_ROTATION);
2803 CASE(SLIDER, SDL_PEN_CAPABILITY_SLIDER);
2804 #undef CASE
2805 default: break; // unsupported here.
2806 }
2807}
2808
2809static void tablet_tool_handle_done(void *data, struct zwp_tablet_tool_v2 *tool)
2810{
2811}
2812
2813static void tablet_tool_handle_removed(void *data, struct zwp_tablet_tool_v2 *tool)
2814{
2815 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2816 if (sdltool->instance_id) {
2817 SDL_RemovePenDevice(0, sdltool->instance_id);
2818 }
2819 zwp_tablet_tool_v2_destroy(tool);
2820 SDL_free(sdltool);
2821}
2822
2823static void tablet_tool_handle_proximity_in(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface)
2824{
2825 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2826 SDL_WindowData *windowdata = surface ? Wayland_GetWindowDataForOwnedSurface(surface) : NULL;
2827 sdltool->tool_focus = windowdata ? windowdata->sdlwindow : NULL;
2828
2829 SDL_assert(sdltool->instance_id == 0); // shouldn't be added at this point.
2830 if (sdltool->info.subtype != SDL_PEN_TYPE_UNKNOWN) { // don't tell SDL about it if we don't know its role.
2831 sdltool->instance_id = SDL_AddPenDevice(0, NULL, &sdltool->info, sdltool);
2832 }
2833
2834 // According to the docs, this should be followed by a motion event, where we'll send our SDL events.
2835}
2836
2837static void tablet_tool_handle_proximity_out(void *data, struct zwp_tablet_tool_v2 *tool)
2838{
2839 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2840 sdltool->tool_focus = NULL;
2841
2842 if (sdltool->instance_id) {
2843 SDL_RemovePenDevice(0, sdltool->instance_id);
2844 sdltool->instance_id = 0;
2845 }
2846}
2847
2848static void tablet_tool_handle_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial)
2849{
2850 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2851 sdltool->frame_pen_down = 1;
2852}
2853
2854static void tablet_tool_handle_up(void *data, struct zwp_tablet_tool_v2 *tool)
2855{
2856 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2857 sdltool->frame_pen_down = 0;
2858}
2859
2860static void tablet_tool_handle_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t sx_w, wl_fixed_t sy_w)
2861{
2862 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2863 SDL_Window *window = sdltool->tool_focus;
2864 if (window) {
2865 const SDL_WindowData *windowdata = window->internal;
2866 sdltool->x = (float)(wl_fixed_to_double(sx_w) * windowdata->pointer_scale.x);
2867 sdltool->y = (float)(wl_fixed_to_double(sy_w) * windowdata->pointer_scale.y);
2868 sdltool->frame_motion_set = true;
2869 }
2870}
2871
2872static void tablet_tool_handle_pressure(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t pressure)
2873{
2874 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2875 sdltool->frame_axes[SDL_PEN_AXIS_PRESSURE] = ((float) pressure) / 65535.0f;
2876 sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
2877 if (pressure) {
2878 sdltool->frame_axes[SDL_PEN_AXIS_DISTANCE] = 0.0f;
2879 sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
2880 }
2881}
2882
2883static void tablet_tool_handle_distance(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t distance)
2884{
2885 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2886 sdltool->frame_axes[SDL_PEN_AXIS_DISTANCE] = ((float) distance) / 65535.0f;
2887 sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_DISTANCE);
2888 if (distance) {
2889 sdltool->frame_axes[SDL_PEN_AXIS_PRESSURE] = 0.0f;
2890 sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_PRESSURE);
2891 }
2892}
2893
2894static void tablet_tool_handle_tilt(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t xtilt, wl_fixed_t ytilt)
2895{
2896 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2897 sdltool->frame_axes[SDL_PEN_AXIS_XTILT] = (float)(wl_fixed_to_double(xtilt));
2898 sdltool->frame_axes[SDL_PEN_AXIS_YTILT] = (float)(wl_fixed_to_double(ytilt));
2899 sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_XTILT) | (1u << SDL_PEN_AXIS_YTILT);
2900}
2901
2902static void tablet_tool_handle_button(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, uint32_t button, uint32_t state)
2903{
2904 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2905 int sdlbutton;
2906
2907 switch (button) {
2908 // see %{_includedir}/linux/input-event-codes.h
2909 case 0x14b: // BTN_STYLUS
2910 sdlbutton = 1;
2911 break;
2912 case 0x14c: // BTN_STYLUS2
2913 sdlbutton = 2;
2914 break;
2915 case 0x149: // BTN_STYLUS3
2916 sdlbutton = 3;
2917 break;
2918 default:
2919 return; // don't care about this button, I guess.
2920 }
2921
2922 SDL_assert((sdlbutton >= 1) && (sdlbutton <= SDL_arraysize(sdltool->frame_buttons)));
2923 sdltool->frame_buttons[sdlbutton-1] = (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED) ? 1 : 0;
2924}
2925
2926static void tablet_tool_handle_rotation(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t degrees)
2927{
2928 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2929 const float rotation = (float)(wl_fixed_to_double(degrees));
2930 sdltool->frame_axes[SDL_PEN_AXIS_ROTATION] = (rotation > 180.0f) ? (rotation - 360.0f) : rotation; // map to -180.0f ... 179.0f range
2931 sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_ROTATION);
2932}
2933
2934static void tablet_tool_handle_slider(void *data, struct zwp_tablet_tool_v2 *tool, int32_t position)
2935{
2936 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2937 sdltool->frame_axes[SDL_PEN_AXIS_SLIDER] = position / 65535.f;
2938 sdltool->frame_axes_set |= (1u << SDL_PEN_AXIS_SLIDER);
2939}
2940
2941static void tablet_tool_handle_wheel(void *data, struct zwp_tablet_tool_v2 *tool, int32_t degrees, int32_t clicks)
2942{
2943 // not supported at the moment
2944}
2945
2946static void tablet_tool_handle_frame(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t time)
2947{
2948 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) data;
2949
2950 if (!sdltool->instance_id) {
2951 return; // Not a pen we report on.
2952 }
2953
2954 const Uint64 timestamp = Wayland_GetEventTimestamp(SDL_MS_TO_NS(time));
2955 const SDL_PenID instance_id = sdltool->instance_id;
2956 SDL_Window *window = sdltool->tool_focus;
2957
2958 // I don't know if this is necessary (or makes sense), but send motion before pen downs, but after pen ups, so you don't get unexpected lines drawn.
2959 if (sdltool->frame_motion_set && (sdltool->frame_pen_down != -1)) {
2960 if (sdltool->frame_pen_down) {
2961 SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
2962 SDL_SendPenTouch(timestamp, instance_id, window, false, true); // !!! FIXME: how do we know what tip is in use?
2963 } else {
2964 SDL_SendPenTouch(timestamp, instance_id, window, false, false); // !!! FIXME: how do we know what tip is in use?
2965 SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
2966 }
2967 } else {
2968 if (sdltool->frame_pen_down != -1) {
2969 SDL_SendPenTouch(timestamp, instance_id, window, false, (sdltool->frame_pen_down != 0)); // !!! FIXME: how do we know what tip is in use?
2970 }
2971
2972 if (sdltool->frame_motion_set) {
2973 SDL_SendPenMotion(timestamp, instance_id, window, sdltool->x, sdltool->y);
2974 }
2975 }
2976
2977 for (SDL_PenAxis i = 0; i < SDL_PEN_AXIS_COUNT; i++) {
2978 if (sdltool->frame_axes_set & (1u << i)) {
2979 SDL_SendPenAxis(timestamp, instance_id, window, i, sdltool->frame_axes[i]);
2980 }
2981 }
2982
2983 for (int i = 0; i < SDL_arraysize(sdltool->frame_buttons); i++) {
2984 const int state = sdltool->frame_buttons[i];
2985 if (state != -1) {
2986 SDL_SendPenButton(timestamp, instance_id, window, (Uint8)(i + 1), (state != 0));
2987 sdltool->frame_buttons[i] = -1;
2988 }
2989 }
2990
2991 // reset for next frame.
2992 sdltool->frame_pen_down = -1;
2993 sdltool->frame_motion_set = false;
2994 sdltool->frame_axes_set = 0;
2995}
2996
2997static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
2998 tablet_tool_handle_type,
2999 tablet_tool_handle_hardware_serial,
3000 tablet_tool_handle_hardware_id_wacom,
3001 tablet_tool_handle_capability,
3002 tablet_tool_handle_done,
3003 tablet_tool_handle_removed,
3004 tablet_tool_handle_proximity_in,
3005 tablet_tool_handle_proximity_out,
3006 tablet_tool_handle_down,
3007 tablet_tool_handle_up,
3008 tablet_tool_handle_motion,
3009 tablet_tool_handle_pressure,
3010 tablet_tool_handle_distance,
3011 tablet_tool_handle_tilt,
3012 tablet_tool_handle_rotation,
3013 tablet_tool_handle_slider,
3014 tablet_tool_handle_wheel,
3015 tablet_tool_handle_button,
3016 tablet_tool_handle_frame
3017};
3018
3019
3020static void tablet_seat_handle_tablet_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_v2 *tablet)
3021{
3022 // don't care atm.
3023}
3024
3025static void tablet_seat_handle_tool_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_tool_v2 *tool)
3026{
3027 SDL_WaylandPenTool *sdltool = SDL_calloc(1, sizeof(*sdltool));
3028
3029 if (sdltool) { // if allocation failed, oh well, we won't report this device.
3030 sdltool->wltool = tool;
3031 sdltool->info.max_tilt = -1.0f;
3032 sdltool->info.num_buttons = -1;
3033 sdltool->frame_pen_down = -1;
3034 for (int i = 0; i < SDL_arraysize(sdltool->frame_buttons); i++) {
3035 sdltool->frame_buttons[i] = -1;
3036 }
3037
3038 // this will send a bunch of zwp_tablet_tool_v2 events right up front to tell
3039 // us device details, with a "done" event to let us know we have everything.
3040 zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, sdltool);
3041 }
3042}
3043
3044static void tablet_seat_handle_pad_added(void *data, struct zwp_tablet_seat_v2 *seat, struct zwp_tablet_pad_v2 *pad)
3045{
3046 // we don't care atm.
3047}
3048
3049static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
3050 tablet_seat_handle_tablet_added,
3051 tablet_seat_handle_tool_added,
3052 tablet_seat_handle_pad_added
3053};
3054
3055void Wayland_input_init_tablet_support(struct SDL_WaylandInput *input, struct zwp_tablet_manager_v2 *tablet_manager)
3056{
3057 if (!tablet_manager || !input->seat) {
3058 return;
3059 }
3060
3061 SDL_WaylandTabletInput *tablet_input = SDL_calloc(1, sizeof(*tablet_input));
3062 if (!tablet_input) {
3063 return;
3064 }
3065
3066 tablet_input->input = input;
3067 tablet_input->seat = zwp_tablet_manager_v2_get_tablet_seat(tablet_manager, input->seat);
3068
3069 zwp_tablet_seat_v2_add_listener(tablet_input->seat, &tablet_seat_listener, tablet_input);
3070
3071 input->tablet_input = tablet_input;
3072}
3073
3074static void Wayland_remove_all_pens_callback(SDL_PenID instance_id, void *handle, void *userdata)
3075{
3076 SDL_WaylandPenTool *sdltool = (SDL_WaylandPenTool *) handle;
3077 zwp_tablet_tool_v2_destroy(sdltool->wltool);
3078 SDL_free(sdltool);
3079}
3080
3081void Wayland_input_quit_tablet_support(struct SDL_WaylandInput *input)
3082{
3083 SDL_RemoveAllPenDevices(Wayland_remove_all_pens_callback, NULL);
3084
3085 if (input && input->tablet_input) {
3086 zwp_tablet_seat_v2_destroy(input->tablet_input->seat);
3087 SDL_free(input->tablet_input);
3088 input->tablet_input = NULL;
3089 }
3090}
3091
3092void Wayland_input_initialize_seat(SDL_VideoData *d)
3093{
3094 struct SDL_WaylandInput *input = d->input;
3095
3096 WAYLAND_wl_list_init(&touch_points);
3097
3098 if (d->data_device_manager) {
3099 Wayland_create_data_device(d);
3100 }
3101 if (d->primary_selection_device_manager) {
3102 Wayland_create_primary_selection_device(d);
3103 }
3104 if (d->text_input_manager) {
3105 Wayland_create_text_input(d);
3106 }
3107
3108 wl_seat_add_listener(input->seat, &seat_listener, input);
3109 wl_seat_set_user_data(input->seat, input);
3110
3111 if (d->tablet_manager) {
3112 Wayland_input_init_tablet_support(d->input, d->tablet_manager);
3113 }
3114
3115 WAYLAND_wl_display_flush(d->display);
3116}
3117
3118void Wayland_display_destroy_input(SDL_VideoData *d)
3119{
3120 struct SDL_WaylandInput *input = d->input;
3121
3122 if (input->keyboard_timestamps) {
3123 zwp_input_timestamps_v1_destroy(input->keyboard_timestamps);
3124 }
3125 if (input->pointer_timestamps) {
3126 zwp_input_timestamps_v1_destroy(input->pointer_timestamps);
3127 }
3128 if (input->touch_timestamps) {
3129 zwp_input_timestamps_v1_destroy(input->touch_timestamps);
3130 }
3131
3132 if (input->data_device) {
3133 Wayland_data_device_clear_selection(input->data_device);
3134 if (input->data_device->selection_offer) {
3135 Wayland_data_offer_destroy(input->data_device->selection_offer);
3136 }
3137 if (input->data_device->drag_offer) {
3138 Wayland_data_offer_destroy(input->data_device->drag_offer);
3139 }
3140 if (input->data_device->data_device) {
3141 if (wl_data_device_get_version(input->data_device->data_device) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION) {
3142 wl_data_device_release(input->data_device->data_device);
3143 } else {
3144 wl_data_device_destroy(input->data_device->data_device);
3145 }
3146 }
3147 SDL_free(input->data_device);
3148 }
3149
3150 if (input->primary_selection_device) {
3151 if (input->primary_selection_device->selection_offer) {
3152 Wayland_primary_selection_offer_destroy(input->primary_selection_device->selection_offer);
3153 }
3154 if (input->primary_selection_device->selection_source) {
3155 Wayland_primary_selection_source_destroy(input->primary_selection_device->selection_source);
3156 }
3157 if (input->primary_selection_device->primary_selection_device) {
3158 zwp_primary_selection_device_v1_destroy(input->primary_selection_device->primary_selection_device);
3159 }
3160 SDL_free(input->primary_selection_device);
3161 }
3162
3163 if (input->text_input) {
3164 zwp_text_input_v3_destroy(input->text_input->text_input);
3165 SDL_free(input->text_input);
3166 }
3167
3168 if (input->keyboard) {
3169 if (wl_keyboard_get_version(input->keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) {
3170 wl_keyboard_release(input->keyboard);
3171 } else {
3172 wl_keyboard_destroy(input->keyboard);
3173 }
3174 }
3175
3176 if (input->relative_pointer) {
3177 zwp_relative_pointer_v1_destroy(input->relative_pointer);
3178 }
3179
3180 if (input->cursor_shape) {
3181 wp_cursor_shape_device_v1_destroy(input->cursor_shape);
3182 }
3183
3184 if (input->pointer) {
3185 if (wl_pointer_get_version(input->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) {
3186 wl_pointer_release(input->pointer);
3187 } else {
3188 wl_pointer_destroy(input->pointer);
3189 }
3190 }
3191
3192 if (input->touch) {
3193 struct SDL_WaylandTouchPoint *tp, *tmp;
3194
3195 SDL_DelTouch(1);
3196 if (wl_touch_get_version(input->touch) >= WL_TOUCH_RELEASE_SINCE_VERSION) {
3197 wl_touch_release(input->touch);
3198 } else {
3199 wl_touch_destroy(input->touch);
3200 }
3201
3202 wl_list_for_each_safe (tp, tmp, &touch_points, link) {
3203 WAYLAND_wl_list_remove(&tp->link);
3204 SDL_free(tp);
3205 }
3206 }
3207
3208 if (input->tablet_input) {
3209 Wayland_input_quit_tablet_support(input);
3210 }
3211
3212 if (input->seat) {
3213 if (wl_seat_get_version(input->seat) >= WL_SEAT_RELEASE_SINCE_VERSION) {
3214 wl_seat_release(input->seat);
3215 } else {
3216 wl_seat_destroy(input->seat);
3217 }
3218 }
3219
3220 if (input->xkb.compose_state) {
3221 WAYLAND_xkb_compose_state_unref(input->xkb.compose_state);
3222 }
3223
3224 if (input->xkb.compose_table) {
3225 WAYLAND_xkb_compose_table_unref(input->xkb.compose_table);
3226 }
3227
3228 if (input->xkb.state) {
3229 WAYLAND_xkb_state_unref(input->xkb.state);
3230 }
3231
3232 if (input->xkb.keymap) {
3233 WAYLAND_xkb_keymap_unref(input->xkb.keymap);
3234 }
3235
3236 SDL_free(input);
3237 d->input = NULL;
3238}
3239
3240bool Wayland_input_enable_relative_pointer(struct SDL_WaylandInput *input)
3241{
3242 SDL_VideoDevice *vd = SDL_GetVideoDevice();
3243 SDL_VideoData *d = input->display;
3244 SDL_Window *window;
3245
3246 if (!d->relative_pointer_manager) {
3247 return false;
3248 }
3249
3250 if (!d->pointer_constraints) {
3251 return false;
3252 }
3253
3254 if (!input->pointer) {
3255 return false;
3256 }
3257
3258 /* If we have a pointer confine active, we must destroy it here because
3259 * creating a locked pointer otherwise would be a protocol error.
3260 */
3261 for (window = vd->windows; window; window = window->next) {
3262 pointer_confine_destroy(window);
3263 }
3264
3265 for (window = vd->windows; window; window = window->next) {
3266 Wayland_input_lock_pointer(input, window);
3267 }
3268
3269 d->relative_mouse_mode = 1;
3270
3271 return true;
3272}
3273
3274bool Wayland_input_disable_relative_pointer(struct SDL_WaylandInput *input)
3275{
3276 SDL_VideoDevice *vd = SDL_GetVideoDevice();
3277 SDL_VideoData *d = input->display;
3278 SDL_Window *window;
3279
3280 for (window = vd->windows; window; window = window->next) {
3281 Wayland_input_unlock_pointer(input, window);
3282 }
3283
3284 d->relative_mouse_mode = 0;
3285
3286 for (window = vd->windows; window; window = window->next) {
3287 Wayland_input_confine_pointer(input, window);
3288 }
3289
3290 return true;
3291}
3292
3293bool Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
3294{
3295 SDL_WindowData *w = window->internal;
3296 SDL_VideoData *d = input->display;
3297 struct wl_region *confine_rect;
3298
3299 if (!d->pointer_constraints) {
3300 return SDL_SetError("Failed to confine pointer: compositor lacks support for the required zwp_pointer_constraints_v1 protocol");
3301 }
3302
3303 if (!input->pointer) {
3304 return SDL_SetError("No pointer to confine");
3305 }
3306
3307 /* A confine may already be active, in which case we should destroy it and
3308 * create a new one.
3309 */
3310 pointer_confine_destroy(window);
3311
3312 /* We cannot create a confine if the pointer is already locked. Defer until
3313 * the pointer is unlocked.
3314 */
3315 if (d->relative_mouse_mode) {
3316 return true;
3317 }
3318
3319 // Don't confine the pointer if it shouldn't be confined.
3320 if (SDL_RectEmpty(&window->mouse_rect) && !(window->flags & SDL_WINDOW_MOUSE_GRABBED)) {
3321 return true;
3322 }
3323
3324 if (SDL_RectEmpty(&window->mouse_rect)) {
3325 confine_rect = NULL;
3326 } else {
3327 SDL_Rect scaled_mouse_rect;
3328
3329 scaled_mouse_rect.x = (int)SDL_floor(window->mouse_rect.x / w->pointer_scale.x);
3330 scaled_mouse_rect.y = (int)SDL_floor(window->mouse_rect.y / w->pointer_scale.y);
3331 scaled_mouse_rect.w = (int)SDL_ceil(window->mouse_rect.w / w->pointer_scale.x);
3332 scaled_mouse_rect.h = (int)SDL_ceil(window->mouse_rect.h / w->pointer_scale.y);
3333
3334 confine_rect = wl_compositor_create_region(d->compositor);
3335 wl_region_add(confine_rect,
3336 scaled_mouse_rect.x,
3337 scaled_mouse_rect.y,
3338 scaled_mouse_rect.w,
3339 scaled_mouse_rect.h);
3340 }
3341
3342 struct zwp_confined_pointer_v1 *confined_pointer =
3343 zwp_pointer_constraints_v1_confine_pointer(d->pointer_constraints,
3344 w->surface,
3345 input->pointer,
3346 confine_rect,
3347 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
3348 zwp_confined_pointer_v1_add_listener(confined_pointer,
3349 &confined_pointer_listener,
3350 window);
3351
3352 if (confine_rect) {
3353 wl_region_destroy(confine_rect);
3354 }
3355
3356 w->confined_pointer = confined_pointer;
3357 return true;
3358}
3359
3360bool Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input, SDL_Window *window)
3361{
3362 pointer_confine_destroy(window);
3363 return true;
3364}
3365
3366bool Wayland_input_grab_keyboard(SDL_Window *window, struct SDL_WaylandInput *input)
3367{
3368 SDL_WindowData *w = window->internal;
3369 SDL_VideoData *d = input->display;
3370
3371 if (!d->key_inhibitor_manager) {
3372 return SDL_SetError("Failed to grab keyboard: compositor lacks support for the required zwp_keyboard_shortcuts_inhibit_manager_v1 protocol");
3373 }
3374
3375 if (w->key_inhibitor) {
3376 return true;
3377 }
3378
3379 w->key_inhibitor =
3380 zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(d->key_inhibitor_manager,
3381 w->surface,
3382 input->seat);
3383
3384 return true;
3385}
3386
3387bool Wayland_input_ungrab_keyboard(SDL_Window *window)
3388{
3389 SDL_WindowData *w = window->internal;
3390
3391 if (w->key_inhibitor) {
3392 zwp_keyboard_shortcuts_inhibitor_v1_destroy(w->key_inhibitor);
3393 w->key_inhibitor = NULL;
3394 }
3395
3396 return true;
3397}
3398
3399void Wayland_UpdateImplicitGrabSerial(struct SDL_WaylandInput *input, Uint32 serial)
3400{
3401 if (serial > input->last_implicit_grab_serial) {
3402 input->last_implicit_grab_serial = serial;
3403 Wayland_data_device_set_serial(input->data_device, serial);
3404 Wayland_primary_selection_device_set_serial(input->primary_selection_device, serial);
3405 }
3406}
3407
3408#endif // SDL_VIDEO_DRIVER_WAYLAND