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