1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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#if SDL_VIDEO_DRIVER_WAYLAND
25
26#include "SDL_stdinc.h"
27#include "SDL_timer.h"
28
29#include "../../core/unix/SDL_poll.h"
30#include "../../events/SDL_sysevents.h"
31#include "../../events/SDL_events_c.h"
32#include "../../events/scancodes_xfree86.h"
33
34#include "SDL_waylandvideo.h"
35#include "SDL_waylandevents_c.h"
36#include "SDL_waylandwindow.h"
37
38#include "SDL_waylanddyn.h"
39
40#include "pointer-constraints-unstable-v1-client-protocol.h"
41#include "relative-pointer-unstable-v1-client-protocol.h"
42#include "xdg-shell-client-protocol.h"
43#include "xdg-shell-unstable-v6-client-protocol.h"
44#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
45
46#ifdef SDL_INPUT_LINUXEV
47#include <linux/input.h>
48#else
49#define BTN_LEFT (0x110)
50#define BTN_RIGHT (0x111)
51#define BTN_MIDDLE (0x112)
52#define BTN_SIDE (0x113)
53#define BTN_EXTRA (0x114)
54#endif
55#include <sys/select.h>
56#include <sys/mman.h>
57#include <poll.h>
58#include <unistd.h>
59#include <xkbcommon/xkbcommon.h>
60#include <xkbcommon/xkbcommon-compose.h>
61
62/* Weston uses a ratio of 10 units per scroll tick */
63#define WAYLAND_WHEEL_AXIS_UNIT 10
64
65struct SDL_WaylandTouchPoint {
66 SDL_TouchID id;
67 float x;
68 float y;
69 struct wl_surface* surface;
70
71 struct SDL_WaylandTouchPoint* prev;
72 struct SDL_WaylandTouchPoint* next;
73};
74
75struct SDL_WaylandTouchPointList {
76 struct SDL_WaylandTouchPoint* head;
77 struct SDL_WaylandTouchPoint* tail;
78};
79
80static struct SDL_WaylandTouchPointList touch_points = {NULL, NULL};
81
82static void
83touch_add(SDL_TouchID id, float x, float y, struct wl_surface *surface)
84{
85 struct SDL_WaylandTouchPoint* tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint));
86
87 tp->id = id;
88 tp->x = x;
89 tp->y = y;
90 tp->surface = surface;
91
92 if (touch_points.tail) {
93 touch_points.tail->next = tp;
94 tp->prev = touch_points.tail;
95 } else {
96 touch_points.head = tp;
97 tp->prev = NULL;
98 }
99
100 touch_points.tail = tp;
101 tp->next = NULL;
102}
103
104static void
105touch_update(SDL_TouchID id, float x, float y)
106{
107 struct SDL_WaylandTouchPoint* tp = touch_points.head;
108
109 while (tp) {
110 if (tp->id == id) {
111 tp->x = x;
112 tp->y = y;
113 }
114
115 tp = tp->next;
116 }
117}
118
119static void
120touch_del(SDL_TouchID id, float* x, float* y, struct wl_surface **surface)
121{
122 struct SDL_WaylandTouchPoint* tp = touch_points.head;
123
124 while (tp) {
125 if (tp->id == id) {
126 *x = tp->x;
127 *y = tp->y;
128 *surface = tp->surface;
129
130 if (tp->prev) {
131 tp->prev->next = tp->next;
132 } else {
133 touch_points.head = tp->next;
134 }
135
136 if (tp->next) {
137 tp->next->prev = tp->prev;
138 } else {
139 touch_points.tail = tp->prev;
140 }
141
142 {
143 struct SDL_WaylandTouchPoint *next = tp->next;
144 SDL_free(tp);
145 tp = next;
146 }
147 } else {
148 tp = tp->next;
149 }
150 }
151}
152
153static struct wl_surface*
154touch_surface(SDL_TouchID id)
155{
156 struct SDL_WaylandTouchPoint* tp = touch_points.head;
157
158 while (tp) {
159 if (tp->id == id) {
160 return tp->surface;
161 }
162
163 tp = tp->next;
164 }
165
166 return NULL;
167}
168
169/* Returns the time till next repeat, or 0 if no key is down. */
170static void
171keyboard_repeat_handle(SDL_WaylandKeyboardRepeat* repeat_info, uint32_t now)
172{
173 if (!repeat_info->is_key_down || !repeat_info->is_initialized) {
174 return;
175 }
176 while (repeat_info->next_repeat_ms <= now) {
177 if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) {
178 SDL_SendKeyboardKey(SDL_PRESSED, repeat_info->scancode);
179 }
180 if (repeat_info->text[0]) {
181 SDL_SendKeyboardText(repeat_info->text);
182 }
183 repeat_info->next_repeat_ms += 1000 / repeat_info->repeat_rate;
184 }
185}
186
187static void
188keyboard_repeat_clear(SDL_WaylandKeyboardRepeat* repeat_info) {
189 if (!repeat_info->is_initialized) {
190 return;
191 }
192 repeat_info->is_key_down = SDL_FALSE;
193}
194
195static void
196keyboard_repeat_set(SDL_WaylandKeyboardRepeat* repeat_info,
197 uint32_t scancode, SDL_bool has_text, char text[8]) {
198 if (!repeat_info->is_initialized) {
199 return;
200 }
201 repeat_info->is_key_down = SDL_TRUE;
202 repeat_info->next_repeat_ms = SDL_GetTicks() + repeat_info->repeat_delay;
203 repeat_info->scancode = scancode;
204 if (has_text) {
205 memcpy(repeat_info->text, text, 8);
206 } else {
207 repeat_info->text[0] = '\0';
208 }
209}
210
211void
212Wayland_PumpEvents(_THIS)
213{
214 SDL_VideoData *d = _this->driverdata;
215 struct SDL_WaylandInput *input = d->input;
216 int err;
217
218 WAYLAND_wl_display_flush(d->display);
219
220#ifdef SDL_USE_IME
221 if (SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE) {
222 SDL_IME_PumpEvents();
223 }
224#endif
225
226 if (input) {
227 uint32_t now = SDL_GetTicks();
228 keyboard_repeat_handle(&input->keyboard_repeat, now);
229 }
230
231 if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_FALSE, 0)) {
232 err = WAYLAND_wl_display_dispatch(d->display);
233 } else {
234 err = WAYLAND_wl_display_dispatch_pending(d->display);
235 }
236 if (err == -1 && !d->display_disconnected) {
237 /* Something has failed with the Wayland connection -- for example,
238 * the compositor may have shut down and closed its end of the socket,
239 * or there is a library-specific error. No recovery is possible. */
240 d->display_disconnected = 1;
241 /* Only send a single quit message, as application shutdown might call
242 * SDL_PumpEvents */
243 SDL_SendQuit();
244 }
245}
246
247static void
248pointer_handle_motion(void *data, struct wl_pointer *pointer,
249 uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
250{
251 struct SDL_WaylandInput *input = data;
252 SDL_WindowData *window = input->pointer_focus;
253 input->sx_w = sx_w;
254 input->sy_w = sy_w;
255 if (input->pointer_focus) {
256 const int sx = wl_fixed_to_int(sx_w);
257 const int sy = wl_fixed_to_int(sy_w);
258 SDL_SendMouseMotion(window->sdlwindow, 0, 0, sx, sy);
259 }
260}
261
262static void
263pointer_handle_enter(void *data, struct wl_pointer *pointer,
264 uint32_t serial, struct wl_surface *surface,
265 wl_fixed_t sx_w, wl_fixed_t sy_w)
266{
267 struct SDL_WaylandInput *input = data;
268 SDL_WindowData *window;
269
270 if (!surface) {
271 /* enter event for a window we've just destroyed */
272 return;
273 }
274
275 /* This handler will be called twice in Wayland 1.4
276 * Once for the window surface which has valid user data
277 * and again for the mouse cursor surface which does not have valid user data
278 * We ignore the later
279 */
280
281 window = (SDL_WindowData *)wl_surface_get_user_data(surface);
282
283 if (window) {
284 input->pointer_focus = window;
285 input->pointer_enter_serial = serial;
286 SDL_SetMouseFocus(window->sdlwindow);
287 /* In the case of e.g. a pointer confine warp, we may receive an enter
288 * event with no following motion event, but with the new coordinates
289 * as part of the enter event. */
290 pointer_handle_motion(data, pointer, serial, sx_w, sy_w);
291 /* If the cursor was changed while our window didn't have pointer
292 * focus, we might need to trigger another call to
293 * wl_pointer_set_cursor() for the new cursor to be displayed. */
294 SDL_SetCursor(NULL);
295 }
296}
297
298static void
299pointer_handle_leave(void *data, struct wl_pointer *pointer,
300 uint32_t serial, struct wl_surface *surface)
301{
302 struct SDL_WaylandInput *input = data;
303
304 if (input->pointer_focus) {
305 SDL_SetMouseFocus(NULL);
306 input->pointer_focus = NULL;
307 }
308}
309
310static SDL_bool
311ProcessHitTest(struct SDL_WaylandInput *input, uint32_t serial)
312{
313 SDL_WindowData *window_data = input->pointer_focus;
314 SDL_Window *window = window_data->sdlwindow;
315
316 if (window->hit_test) {
317 const SDL_Point point = { wl_fixed_to_int(input->sx_w), wl_fixed_to_int(input->sy_w) };
318 const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
319
320 static const uint32_t directions_wl[] = {
321 WL_SHELL_SURFACE_RESIZE_TOP_LEFT, WL_SHELL_SURFACE_RESIZE_TOP,
322 WL_SHELL_SURFACE_RESIZE_TOP_RIGHT, WL_SHELL_SURFACE_RESIZE_RIGHT,
323 WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT, WL_SHELL_SURFACE_RESIZE_BOTTOM,
324 WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT, WL_SHELL_SURFACE_RESIZE_LEFT
325 };
326
327 /* the names are different (ZXDG_TOPLEVEL_V6_RESIZE_EDGE_* vs
328 WL_SHELL_SURFACE_RESIZE_*), but the values are the same. */
329 const uint32_t *directions_zxdg = directions_wl;
330
331 switch (rc) {
332 case SDL_HITTEST_DRAGGABLE:
333 if (input->display->shell.xdg) {
334 xdg_toplevel_move(window_data->shell_surface.xdg.roleobj.toplevel, input->seat, serial);
335 } else if (input->display->shell.zxdg) {
336 zxdg_toplevel_v6_move(window_data->shell_surface.zxdg.roleobj.toplevel, input->seat, serial);
337 } else {
338 wl_shell_surface_move(window_data->shell_surface.wl, input->seat, serial);
339 }
340 return SDL_TRUE;
341
342 case SDL_HITTEST_RESIZE_TOPLEFT:
343 case SDL_HITTEST_RESIZE_TOP:
344 case SDL_HITTEST_RESIZE_TOPRIGHT:
345 case SDL_HITTEST_RESIZE_RIGHT:
346 case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
347 case SDL_HITTEST_RESIZE_BOTTOM:
348 case SDL_HITTEST_RESIZE_BOTTOMLEFT:
349 case SDL_HITTEST_RESIZE_LEFT:
350 if (input->display->shell.xdg) {
351 xdg_toplevel_resize(window_data->shell_surface.xdg.roleobj.toplevel, input->seat, serial, directions_zxdg[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
352 } else if (input->display->shell.zxdg) {
353 zxdg_toplevel_v6_resize(window_data->shell_surface.zxdg.roleobj.toplevel, input->seat, serial, directions_zxdg[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
354 } else {
355 wl_shell_surface_resize(window_data->shell_surface.wl, input->seat, serial, directions_wl[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
356 }
357 return SDL_TRUE;
358
359 default: return SDL_FALSE;
360 }
361 }
362
363 return SDL_FALSE;
364}
365
366static void
367pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial,
368 uint32_t time, uint32_t button, uint32_t state_w)
369{
370 SDL_WindowData *window = input->pointer_focus;
371 enum wl_pointer_button_state state = state_w;
372 uint32_t sdl_button;
373
374 if (input->pointer_focus) {
375 switch (button) {
376 case BTN_LEFT:
377 sdl_button = SDL_BUTTON_LEFT;
378 if (ProcessHitTest(input, serial)) {
379 return; /* don't pass this event on to app. */
380 }
381 break;
382 case BTN_MIDDLE:
383 sdl_button = SDL_BUTTON_MIDDLE;
384 break;
385 case BTN_RIGHT:
386 sdl_button = SDL_BUTTON_RIGHT;
387 break;
388 case BTN_SIDE:
389 sdl_button = SDL_BUTTON_X1;
390 break;
391 case BTN_EXTRA:
392 sdl_button = SDL_BUTTON_X2;
393 break;
394 default:
395 return;
396 }
397
398 Wayland_data_device_set_serial(input->data_device, serial);
399
400 SDL_SendMouseButton(window->sdlwindow, 0,
401 state ? SDL_PRESSED : SDL_RELEASED, sdl_button);
402 }
403}
404
405static void
406pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
407 uint32_t time, uint32_t button, uint32_t state_w)
408{
409 struct SDL_WaylandInput *input = data;
410
411 pointer_handle_button_common(input, serial, time, button, state_w);
412}
413
414static void
415pointer_handle_axis_common_v1(struct SDL_WaylandInput *input,
416 uint32_t time, uint32_t axis, wl_fixed_t value)
417{
418 SDL_WindowData *window = input->pointer_focus;
419 enum wl_pointer_axis a = axis;
420 float x, y;
421
422 if (input->pointer_focus) {
423 switch (a) {
424 case WL_POINTER_AXIS_VERTICAL_SCROLL:
425 x = 0;
426 y = 0 - (float)wl_fixed_to_double(value);
427 break;
428 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
429 x = 0 - (float)wl_fixed_to_double(value);
430 y = 0;
431 break;
432 default:
433 return;
434 }
435
436 x /= WAYLAND_WHEEL_AXIS_UNIT;
437 y /= WAYLAND_WHEEL_AXIS_UNIT;
438
439 SDL_SendMouseWheel(window->sdlwindow, 0, x, y, SDL_MOUSEWHEEL_NORMAL);
440 }
441}
442
443static void
444pointer_handle_axis_common(struct SDL_WaylandInput *input, SDL_bool discrete,
445 uint32_t axis, wl_fixed_t value)
446{
447 enum wl_pointer_axis a = axis;
448
449 if (input->pointer_focus) {
450 switch (a) {
451 case WL_POINTER_AXIS_VERTICAL_SCROLL:
452 if (discrete) {
453 /* this is a discrete axis event so we process it and flag
454 * to ignore future continuous axis events in this frame */
455 input->pointer_curr_axis_info.is_y_discrete = SDL_TRUE;
456 } else if(input->pointer_curr_axis_info.is_y_discrete) {
457 /* this is a continuous axis event and we have already
458 * processed a discrete axis event before so we ignore it */
459 break;
460 }
461 input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value);
462 break;
463 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
464 if (discrete) {
465 /* this is a discrete axis event so we process it and flag
466 * to ignore future continuous axis events in this frame */
467 input->pointer_curr_axis_info.is_x_discrete = SDL_TRUE;
468 } else if(input->pointer_curr_axis_info.is_x_discrete) {
469 /* this is a continuous axis event and we have already
470 * processed a discrete axis event before so we ignore it */
471 break;
472 }
473 input->pointer_curr_axis_info.x = 0 - (float)wl_fixed_to_double(value);
474 break;
475 }
476 }
477}
478
479static void
480pointer_handle_axis(void *data, struct wl_pointer *pointer,
481 uint32_t time, uint32_t axis, wl_fixed_t value)
482{
483 struct SDL_WaylandInput *input = data;
484
485 if(wl_seat_get_version(input->seat) >= 5)
486 pointer_handle_axis_common(input, SDL_FALSE, axis, value);
487 else
488 pointer_handle_axis_common_v1(input, time, axis, value);
489}
490
491static void
492pointer_handle_frame(void *data, struct wl_pointer *pointer)
493{
494 struct SDL_WaylandInput *input = data;
495 SDL_WindowData *window = input->pointer_focus;
496 float x, y;
497
498 if (input->pointer_curr_axis_info.is_x_discrete)
499 x = input->pointer_curr_axis_info.x;
500 else
501 x = input->pointer_curr_axis_info.x / WAYLAND_WHEEL_AXIS_UNIT;
502
503 if (input->pointer_curr_axis_info.is_y_discrete)
504 y = input->pointer_curr_axis_info.y;
505 else
506 y = input->pointer_curr_axis_info.y / WAYLAND_WHEEL_AXIS_UNIT;
507
508 /* clear pointer_curr_axis_info for next frame */
509 memset(&input->pointer_curr_axis_info, 0, sizeof input->pointer_curr_axis_info);
510
511 if(x == 0.0f && y == 0.0f)
512 return;
513 else
514 SDL_SendMouseWheel(window->sdlwindow, 0, x, y, SDL_MOUSEWHEEL_NORMAL);
515}
516
517static void
518pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
519 uint32_t axis_source)
520{
521 /* unimplemented */
522}
523
524static void
525pointer_handle_axis_stop(void *data, struct wl_pointer *pointer,
526 uint32_t time, uint32_t axis)
527{
528 /* unimplemented */
529}
530
531static void
532pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
533 uint32_t axis, int32_t discrete)
534{
535 struct SDL_WaylandInput *input = data;
536
537 pointer_handle_axis_common(input, SDL_TRUE, axis, wl_fixed_from_int(discrete));
538}
539
540
541static const struct wl_pointer_listener pointer_listener = {
542 pointer_handle_enter,
543 pointer_handle_leave,
544 pointer_handle_motion,
545 pointer_handle_button,
546 pointer_handle_axis,
547 pointer_handle_frame, // Version 5
548 pointer_handle_axis_source, // Version 5
549 pointer_handle_axis_stop, // Version 5
550 pointer_handle_axis_discrete, // Version 5
551};
552
553static void
554touch_handler_down(void *data, struct wl_touch *touch, unsigned int serial,
555 unsigned int timestamp, struct wl_surface *surface,
556 int id, wl_fixed_t fx, wl_fixed_t fy)
557{
558 SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(surface);
559 const double dblx = wl_fixed_to_double(fx);
560 const double dbly = wl_fixed_to_double(fy);
561 const float x = dblx / window_data->sdlwindow->w;
562 const float y = dbly / window_data->sdlwindow->h;
563
564 touch_add(id, x, y, surface);
565
566 SDL_SendTouch(1, (SDL_FingerID)id, window_data->sdlwindow, SDL_TRUE, x, y, 1.0f);
567}
568
569static void
570touch_handler_up(void *data, struct wl_touch *touch, unsigned int serial,
571 unsigned int timestamp, int id)
572{
573 float x = 0, y = 0;
574 struct wl_surface *surface = NULL;
575 SDL_Window *window = NULL;
576
577 touch_del(id, &x, &y, &surface);
578
579 if (surface) {
580 SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(surface);
581 window = window_data->sdlwindow;
582 }
583
584 SDL_SendTouch(1, (SDL_FingerID)id, window, SDL_FALSE, x, y, 0.0f);
585}
586
587static void
588touch_handler_motion(void *data, struct wl_touch *touch, unsigned int timestamp,
589 int id, wl_fixed_t fx, wl_fixed_t fy)
590{
591 SDL_WindowData *window_data = (SDL_WindowData *)wl_surface_get_user_data(touch_surface(id));
592 const double dblx = wl_fixed_to_double(fx);
593 const double dbly = wl_fixed_to_double(fy);
594 const float x = dblx / window_data->sdlwindow->w;
595 const float y = dbly / window_data->sdlwindow->h;
596
597 touch_update(id, x, y);
598 SDL_SendTouchMotion(1, (SDL_FingerID)id, window_data->sdlwindow, x, y, 1.0f);
599}
600
601static void
602touch_handler_frame(void *data, struct wl_touch *touch)
603{
604
605}
606
607static void
608touch_handler_cancel(void *data, struct wl_touch *touch)
609{
610
611}
612
613static const struct wl_touch_listener touch_listener = {
614 touch_handler_down,
615 touch_handler_up,
616 touch_handler_motion,
617 touch_handler_frame,
618 touch_handler_cancel,
619 NULL, /* shape */
620 NULL, /* orientation */
621};
622
623static void
624keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
625 uint32_t format, int fd, uint32_t size)
626{
627 struct SDL_WaylandInput *input = data;
628 char *map_str, *locale;
629
630 if (!data) {
631 close(fd);
632 return;
633 }
634
635 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
636 close(fd);
637 return;
638 }
639
640 map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
641 if (map_str == MAP_FAILED) {
642 close(fd);
643 return;
644 }
645
646 input->xkb.keymap = WAYLAND_xkb_keymap_new_from_string(input->display->xkb_context,
647 map_str,
648 XKB_KEYMAP_FORMAT_TEXT_V1,
649 0);
650 munmap(map_str, size);
651 close(fd);
652
653 if (!input->xkb.keymap) {
654 fprintf(stderr, "failed to compile keymap\n");
655 return;
656 }
657
658 input->xkb.state = WAYLAND_xkb_state_new(input->xkb.keymap);
659 if (!input->xkb.state) {
660 fprintf(stderr, "failed to create XKB state\n");
661 WAYLAND_xkb_keymap_unref(input->xkb.keymap);
662 input->xkb.keymap = NULL;
663 return;
664 }
665
666 /*
667 * See https://blogs.s-osg.org/compose-key-support-weston/
668 * for further explanation on dead keys in Wayland.
669 */
670
671 /* Look up the preferred locale, falling back to "C" as default */
672 if (!(locale = SDL_getenv("LC_ALL")))
673 if (!(locale = SDL_getenv("LC_CTYPE")))
674 if (!(locale = SDL_getenv("LANG")))
675 locale = "C";
676 /* Set up XKB compose table */
677 input->xkb.compose_table = WAYLAND_xkb_compose_table_new_from_locale(input->display->xkb_context,
678 locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
679 if (input->xkb.compose_table) {
680 /* Set up XKB compose state */
681 input->xkb.compose_state = WAYLAND_xkb_compose_state_new(input->xkb.compose_table,
682 XKB_COMPOSE_STATE_NO_FLAGS);
683 if (!input->xkb.compose_state) {
684 fprintf(stderr, "could not create XKB compose state\n");
685 WAYLAND_xkb_compose_table_unref(input->xkb.compose_table);
686 input->xkb.compose_table = NULL;
687 }
688 }
689}
690
691static void
692keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
693 uint32_t serial, struct wl_surface *surface,
694 struct wl_array *keys)
695{
696 struct SDL_WaylandInput *input = data;
697 SDL_WindowData *window;
698
699 if (!surface) {
700 /* enter event for a window we've just destroyed */
701 return;
702 }
703
704 window = wl_surface_get_user_data(surface);
705
706 if (window) {
707 input->keyboard_focus = window;
708 window->keyboard_device = input;
709 SDL_SetKeyboardFocus(window->sdlwindow);
710 }
711#ifdef SDL_USE_IME
712 SDL_IME_SetFocus(SDL_TRUE);
713#endif
714}
715
716static void
717keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
718 uint32_t serial, struct wl_surface *surface)
719{
720 struct SDL_WaylandInput *input = data;
721
722 /* Stop key repeat before clearing keyboard focus */
723 keyboard_repeat_clear(&input->keyboard_repeat);
724
725 /* This will release any keys still pressed */
726 SDL_SetKeyboardFocus(NULL);
727#ifdef SDL_USE_IME
728 SDL_IME_SetFocus(SDL_FALSE);
729#endif
730}
731
732static SDL_bool
733keyboard_input_get_text(char text[8], const struct SDL_WaylandInput *input, uint32_t key, SDL_bool *handled_by_ime)
734{
735 SDL_WindowData *window = input->keyboard_focus;
736 const xkb_keysym_t *syms;
737 xkb_keysym_t sym;
738
739 if (!window || window->keyboard_device != input || !input->xkb.state) {
740 return SDL_FALSE;
741 }
742
743 // TODO can this happen?
744 if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1) {
745 return SDL_FALSE;
746 }
747 sym = syms[0];
748
749#ifdef SDL_USE_IME
750 if (SDL_IME_ProcessKeyEvent(sym, key + 8)) {
751 *handled_by_ime = SDL_TRUE;
752 return SDL_TRUE;
753 }
754#endif
755
756 if (WAYLAND_xkb_compose_state_feed(input->xkb.compose_state, sym) == XKB_COMPOSE_FEED_ACCEPTED) {
757 switch(WAYLAND_xkb_compose_state_get_status(input->xkb.compose_state)) {
758 case XKB_COMPOSE_COMPOSING:
759 *handled_by_ime = SDL_TRUE;
760 return SDL_TRUE;
761 case XKB_COMPOSE_CANCELLED:
762 default:
763 sym = XKB_KEY_NoSymbol;
764 break;
765 case XKB_COMPOSE_NOTHING:
766 break;
767 case XKB_COMPOSE_COMPOSED:
768 sym = WAYLAND_xkb_compose_state_get_one_sym(input->xkb.compose_state);
769 break;
770 }
771 }
772
773 return WAYLAND_xkb_keysym_to_utf8(sym, text, 8) > 0;
774}
775
776static void
777keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
778 uint32_t serial, uint32_t time, uint32_t key,
779 uint32_t state_w)
780{
781 struct SDL_WaylandInput *input = data;
782 enum wl_keyboard_key_state state = state_w;
783 uint32_t scancode = SDL_SCANCODE_UNKNOWN;
784 char text[8];
785 SDL_bool has_text = SDL_FALSE;
786 SDL_bool handled_by_ime = SDL_FALSE;
787
788 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
789 has_text = keyboard_input_get_text(text, input, key, &handled_by_ime);
790 }
791
792 if (!handled_by_ime && key < SDL_arraysize(xfree86_scancode_table2)) {
793 scancode = xfree86_scancode_table2[key];
794
795 if (scancode != SDL_SCANCODE_UNKNOWN) {
796 SDL_SendKeyboardKey(state == WL_KEYBOARD_KEY_STATE_PRESSED ?
797 SDL_PRESSED : SDL_RELEASED, scancode);
798 }
799 }
800
801 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
802 if (has_text) {
803 Wayland_data_device_set_serial(input->data_device, serial);
804 if (!handled_by_ime) {
805 SDL_SendKeyboardText(text);
806 }
807 }
808 keyboard_repeat_set(&input->keyboard_repeat, scancode, has_text, text);
809 } else {
810 keyboard_repeat_clear(&input->keyboard_repeat);
811 }
812}
813
814typedef struct Wayland_Keymap
815{
816 xkb_layout_index_t layout;
817 SDL_Keycode keymap[SDL_NUM_SCANCODES];
818} Wayland_Keymap;
819
820static void
821Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data)
822{
823 const xkb_keysym_t *syms;
824 Wayland_Keymap *sdlKeymap = (Wayland_Keymap *)data;
825
826 if ((key - 8) < SDL_arraysize(xfree86_scancode_table2)) {
827 SDL_Scancode scancode = xfree86_scancode_table2[key - 8];
828 if (scancode == SDL_SCANCODE_UNKNOWN) {
829 return;
830 }
831
832 if (WAYLAND_xkb_keymap_key_get_syms_by_level(keymap, key, sdlKeymap->layout, 0, &syms) > 0) {
833 uint32_t keycode = WAYLAND_xkb_keysym_to_utf32(syms[0]);
834 if (keycode) {
835 sdlKeymap->keymap[scancode] = keycode;
836 } else {
837 switch (scancode) {
838 case SDL_SCANCODE_RETURN:
839 sdlKeymap->keymap[scancode] = SDLK_RETURN;
840 break;
841 case SDL_SCANCODE_ESCAPE:
842 sdlKeymap->keymap[scancode] = SDLK_ESCAPE;
843 break;
844 case SDL_SCANCODE_BACKSPACE:
845 sdlKeymap->keymap[scancode] = SDLK_BACKSPACE;
846 break;
847 case SDL_SCANCODE_TAB:
848 sdlKeymap->keymap[scancode] = SDLK_TAB;
849 break;
850 case SDL_SCANCODE_DELETE:
851 sdlKeymap->keymap[scancode] = SDLK_DELETE;
852 break;
853 default:
854 sdlKeymap->keymap[scancode] = SDL_SCANCODE_TO_KEYCODE(scancode);
855 break;
856 }
857 }
858 }
859 }
860}
861
862static void
863keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
864 uint32_t serial, uint32_t mods_depressed,
865 uint32_t mods_latched, uint32_t mods_locked,
866 uint32_t group)
867{
868 struct SDL_WaylandInput *input = data;
869 Wayland_Keymap keymap;
870
871 WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
872 mods_locked, 0, 0, group);
873
874 keymap.layout = group;
875 SDL_GetDefaultKeymap(keymap.keymap);
876 WAYLAND_xkb_keymap_key_for_each(input->xkb.keymap,
877 Wayland_keymap_iter,
878 &keymap);
879 SDL_SetKeymap(0, keymap.keymap, SDL_NUM_SCANCODES);
880 SDL_SendKeymapChangedEvent();
881}
882
883static void
884keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
885 int32_t rate, int32_t delay)
886{
887 struct SDL_WaylandInput *input = data;
888 input->keyboard_repeat.repeat_rate = SDL_max(0, SDL_min(rate, 1000));
889 input->keyboard_repeat.repeat_delay = delay;
890 input->keyboard_repeat.is_initialized = SDL_TRUE;
891}
892
893static const struct wl_keyboard_listener keyboard_listener = {
894 keyboard_handle_keymap,
895 keyboard_handle_enter,
896 keyboard_handle_leave,
897 keyboard_handle_key,
898 keyboard_handle_modifiers,
899 keyboard_handle_repeat_info, // Version 4
900};
901
902static void
903seat_handle_capabilities(void *data, struct wl_seat *seat,
904 enum wl_seat_capability caps)
905{
906 struct SDL_WaylandInput *input = data;
907
908 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
909 input->pointer = wl_seat_get_pointer(seat);
910 memset(&input->pointer_curr_axis_info, 0, sizeof input->pointer_curr_axis_info);
911 input->display->pointer = input->pointer;
912 wl_pointer_set_user_data(input->pointer, input);
913 wl_pointer_add_listener(input->pointer, &pointer_listener,
914 input);
915 } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
916 wl_pointer_destroy(input->pointer);
917 input->pointer = NULL;
918 input->display->pointer = NULL;
919 }
920
921 if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
922 SDL_AddTouch(1, SDL_TOUCH_DEVICE_DIRECT, "wayland_touch");
923 input->touch = wl_seat_get_touch(seat);
924 wl_touch_set_user_data(input->touch, input);
925 wl_touch_add_listener(input->touch, &touch_listener,
926 input);
927 } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
928 SDL_DelTouch(1);
929 wl_touch_destroy(input->touch);
930 input->touch = NULL;
931 }
932
933 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
934 input->keyboard = wl_seat_get_keyboard(seat);
935 wl_keyboard_set_user_data(input->keyboard, input);
936 wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
937 input);
938 } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
939 wl_keyboard_destroy(input->keyboard);
940 input->keyboard = NULL;
941 }
942}
943
944static void
945seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
946{
947 /* unimplemented */
948}
949
950static const struct wl_seat_listener seat_listener = {
951 seat_handle_capabilities,
952 seat_handle_name, // Version 2
953};
954
955static void
956data_source_handle_target(void *data, struct wl_data_source *wl_data_source,
957 const char *mime_type)
958{
959}
960
961static void
962data_source_handle_send(void *data, struct wl_data_source *wl_data_source,
963 const char *mime_type, int32_t fd)
964{
965 Wayland_data_source_send((SDL_WaylandDataSource *)data, mime_type, fd);
966}
967
968static void
969data_source_handle_cancelled(void *data, struct wl_data_source *wl_data_source)
970{
971 Wayland_data_source_destroy(data);
972}
973
974static void
975data_source_handle_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source)
976{
977}
978
979static void
980data_source_handle_dnd_finished(void *data, struct wl_data_source *wl_data_source)
981{
982}
983
984static void
985data_source_handle_action(void *data, struct wl_data_source *wl_data_source,
986 uint32_t dnd_action)
987{
988}
989
990static const struct wl_data_source_listener data_source_listener = {
991 data_source_handle_target,
992 data_source_handle_send,
993 data_source_handle_cancelled,
994 data_source_handle_dnd_drop_performed, // Version 3
995 data_source_handle_dnd_finished, // Version 3
996 data_source_handle_action, // Version 3
997};
998
999SDL_WaylandDataSource*
1000Wayland_data_source_create(_THIS)
1001{
1002 SDL_WaylandDataSource *data_source = NULL;
1003 SDL_VideoData *driver_data = NULL;
1004 struct wl_data_source *id = NULL;
1005
1006 if (_this == NULL || _this->driverdata == NULL) {
1007 SDL_SetError("Video driver uninitialized");
1008 } else {
1009 driver_data = _this->driverdata;
1010
1011 if (driver_data->data_device_manager != NULL) {
1012 id = wl_data_device_manager_create_data_source(
1013 driver_data->data_device_manager);
1014 }
1015
1016 if (id == NULL) {
1017 SDL_SetError("Wayland unable to create data source");
1018 } else {
1019 data_source = SDL_calloc(1, sizeof *data_source);
1020 if (data_source == NULL) {
1021 SDL_OutOfMemory();
1022 wl_data_source_destroy(id);
1023 } else {
1024 WAYLAND_wl_list_init(&(data_source->mimes));
1025 data_source->source = id;
1026 wl_data_source_set_user_data(id, data_source);
1027 wl_data_source_add_listener(id, &data_source_listener,
1028 data_source);
1029 }
1030 }
1031 }
1032 return data_source;
1033}
1034
1035static void
1036data_offer_handle_offer(void *data, struct wl_data_offer *wl_data_offer,
1037 const char *mime_type)
1038{
1039 SDL_WaylandDataOffer *offer = data;
1040 Wayland_data_offer_add_mime(offer, mime_type);
1041}
1042
1043static void
1044data_offer_handle_source_actions(void *data, struct wl_data_offer *wl_data_offer,
1045 uint32_t source_actions)
1046{
1047}
1048
1049static void
1050data_offer_handle_actions(void *data, struct wl_data_offer *wl_data_offer,
1051 uint32_t dnd_action)
1052{
1053}
1054
1055static const struct wl_data_offer_listener data_offer_listener = {
1056 data_offer_handle_offer,
1057 data_offer_handle_source_actions, // Version 3
1058 data_offer_handle_actions, // Version 3
1059};
1060
1061static void
1062data_device_handle_data_offer(void *data, struct wl_data_device *wl_data_device,
1063 struct wl_data_offer *id)
1064{
1065 SDL_WaylandDataOffer *data_offer = NULL;
1066
1067 data_offer = SDL_calloc(1, sizeof *data_offer);
1068 if (data_offer == NULL) {
1069 SDL_OutOfMemory();
1070 } else {
1071 data_offer->offer = id;
1072 data_offer->data_device = data;
1073 WAYLAND_wl_list_init(&(data_offer->mimes));
1074 wl_data_offer_set_user_data(id, data_offer);
1075 wl_data_offer_add_listener(id, &data_offer_listener, data_offer);
1076 }
1077}
1078
1079static void
1080data_device_handle_enter(void *data, struct wl_data_device *wl_data_device,
1081 uint32_t serial, struct wl_surface *surface,
1082 wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id)
1083{
1084 SDL_WaylandDataDevice *data_device = data;
1085 SDL_bool has_mime = SDL_FALSE;
1086 uint32_t dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
1087
1088 data_device->drag_serial = serial;
1089
1090 if (id != NULL) {
1091 data_device->drag_offer = wl_data_offer_get_user_data(id);
1092
1093 /* TODO: SDL Support more mime types */
1094 has_mime = Wayland_data_offer_has_mime(
1095 data_device->drag_offer, FILE_MIME);
1096
1097 /* If drag_mime is NULL this will decline the offer */
1098 wl_data_offer_accept(id, serial,
1099 (has_mime == SDL_TRUE) ? FILE_MIME : NULL);
1100
1101 /* SDL only supports "copy" style drag and drop */
1102 if (has_mime == SDL_TRUE) {
1103 dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
1104 }
1105 if (wl_data_offer_get_version(data_device->drag_offer->offer) >= 3) {
1106 wl_data_offer_set_actions(data_device->drag_offer->offer,
1107 dnd_action, dnd_action);
1108 }
1109 }
1110}
1111
1112static void
1113data_device_handle_leave(void *data, struct wl_data_device *wl_data_device)
1114{
1115 SDL_WaylandDataDevice *data_device = data;
1116 SDL_WaylandDataOffer *offer = NULL;
1117
1118 if (data_device->selection_offer != NULL) {
1119 data_device->selection_offer = NULL;
1120 Wayland_data_offer_destroy(offer);
1121 }
1122}
1123
1124static void
1125data_device_handle_motion(void *data, struct wl_data_device *wl_data_device,
1126 uint32_t time, wl_fixed_t x, wl_fixed_t y)
1127{
1128}
1129
1130static void
1131data_device_handle_drop(void *data, struct wl_data_device *wl_data_device)
1132{
1133 SDL_WaylandDataDevice *data_device = data;
1134 void *buffer = NULL;
1135 size_t length = 0;
1136
1137 const char *current_uri = NULL;
1138 const char *last_char = NULL;
1139 char *current_char = NULL;
1140
1141 if (data_device->drag_offer != NULL) {
1142 /* TODO: SDL Support more mime types */
1143 buffer = Wayland_data_offer_receive(data_device->drag_offer,
1144 &length, FILE_MIME, SDL_FALSE);
1145
1146 /* uri-list */
1147 current_uri = (const char *)buffer;
1148 last_char = (const char *)buffer + length;
1149 for (current_char = buffer; current_char < last_char; ++current_char) {
1150 if (*current_char == '\n' || *current_char == 0) {
1151 if (*current_uri != 0 && *current_uri != '#') {
1152 *current_char = 0;
1153 SDL_SendDropFile(NULL, current_uri);
1154 }
1155 current_uri = (const char *)current_char + 1;
1156 }
1157 }
1158
1159 SDL_free(buffer);
1160 }
1161}
1162
1163static void
1164data_device_handle_selection(void *data, struct wl_data_device *wl_data_device,
1165 struct wl_data_offer *id)
1166{
1167 SDL_WaylandDataDevice *data_device = data;
1168 SDL_WaylandDataOffer *offer = NULL;
1169
1170 if (id != NULL) {
1171 offer = wl_data_offer_get_user_data(id);
1172 }
1173
1174 if (data_device->selection_offer != offer) {
1175 Wayland_data_offer_destroy(data_device->selection_offer);
1176 data_device->selection_offer = offer;
1177 }
1178
1179 SDL_SendClipboardUpdate();
1180}
1181
1182static const struct wl_data_device_listener data_device_listener = {
1183 data_device_handle_data_offer,
1184 data_device_handle_enter,
1185 data_device_handle_leave,
1186 data_device_handle_motion,
1187 data_device_handle_drop,
1188 data_device_handle_selection
1189};
1190
1191static void
1192Wayland_create_data_device(SDL_VideoData *d)
1193{
1194 SDL_WaylandDataDevice *data_device = NULL;
1195
1196 data_device = SDL_calloc(1, sizeof *data_device);
1197 if (data_device == NULL) {
1198 return;
1199 }
1200
1201 data_device->data_device = wl_data_device_manager_get_data_device(
1202 d->data_device_manager, d->input->seat
1203 );
1204 data_device->video_data = d;
1205
1206 if (data_device->data_device == NULL) {
1207 SDL_free(data_device);
1208 } else {
1209 wl_data_device_set_user_data(data_device->data_device, data_device);
1210 wl_data_device_add_listener(data_device->data_device,
1211 &data_device_listener, data_device);
1212 d->input->data_device = data_device;
1213 }
1214}
1215
1216void
1217Wayland_add_data_device_manager(SDL_VideoData *d, uint32_t id, uint32_t version)
1218{
1219 d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, SDL_min(3, version));
1220
1221 if (d->input != NULL) {
1222 Wayland_create_data_device(d);
1223 }
1224}
1225
1226void
1227Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version)
1228{
1229 struct SDL_WaylandInput *input;
1230
1231 input = SDL_calloc(1, sizeof *input);
1232 if (input == NULL)
1233 return;
1234
1235 input->display = d;
1236 input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, SDL_min(5, version));
1237 input->sx_w = wl_fixed_from_int(0);
1238 input->sy_w = wl_fixed_from_int(0);
1239 d->input = input;
1240
1241 if (d->data_device_manager != NULL) {
1242 Wayland_create_data_device(d);
1243 }
1244
1245 wl_seat_add_listener(input->seat, &seat_listener, input);
1246 wl_seat_set_user_data(input->seat, input);
1247
1248 WAYLAND_wl_display_flush(d->display);
1249}
1250
1251void Wayland_display_destroy_input(SDL_VideoData *d)
1252{
1253 struct SDL_WaylandInput *input = d->input;
1254
1255 if (!input)
1256 return;
1257
1258 if (input->data_device != NULL) {
1259 Wayland_data_device_clear_selection(input->data_device);
1260 if (input->data_device->selection_offer != NULL) {
1261 Wayland_data_offer_destroy(input->data_device->selection_offer);
1262 }
1263 if (input->data_device->drag_offer != NULL) {
1264 Wayland_data_offer_destroy(input->data_device->drag_offer);
1265 }
1266 if (input->data_device->data_device != NULL) {
1267 wl_data_device_release(input->data_device->data_device);
1268 }
1269 SDL_free(input->data_device);
1270 }
1271
1272 if (input->keyboard)
1273 wl_keyboard_destroy(input->keyboard);
1274
1275 if (input->pointer)
1276 wl_pointer_destroy(input->pointer);
1277
1278 if (input->touch) {
1279 SDL_DelTouch(1);
1280 wl_touch_destroy(input->touch);
1281 }
1282
1283 if (input->seat)
1284 wl_seat_destroy(input->seat);
1285
1286 if (input->xkb.compose_state)
1287 WAYLAND_xkb_compose_state_unref(input->xkb.compose_state);
1288
1289 if (input->xkb.compose_table)
1290 WAYLAND_xkb_compose_table_unref(input->xkb.compose_table);
1291
1292 if (input->xkb.state)
1293 WAYLAND_xkb_state_unref(input->xkb.state);
1294
1295 if (input->xkb.keymap)
1296 WAYLAND_xkb_keymap_unref(input->xkb.keymap);
1297
1298 SDL_free(input);
1299 d->input = NULL;
1300}
1301
1302/* !!! FIXME: just merge these into display_handle_global(). */
1303void Wayland_display_add_relative_pointer_manager(SDL_VideoData *d, uint32_t id)
1304{
1305 d->relative_pointer_manager =
1306 wl_registry_bind(d->registry, id,
1307 &zwp_relative_pointer_manager_v1_interface, 1);
1308}
1309
1310void Wayland_display_destroy_relative_pointer_manager(SDL_VideoData *d)
1311{
1312 if (d->relative_pointer_manager)
1313 zwp_relative_pointer_manager_v1_destroy(d->relative_pointer_manager);
1314}
1315
1316void Wayland_display_add_pointer_constraints(SDL_VideoData *d, uint32_t id)
1317{
1318 d->pointer_constraints =
1319 wl_registry_bind(d->registry, id,
1320 &zwp_pointer_constraints_v1_interface, 1);
1321}
1322
1323void Wayland_display_destroy_pointer_constraints(SDL_VideoData *d)
1324{
1325 if (d->pointer_constraints)
1326 zwp_pointer_constraints_v1_destroy(d->pointer_constraints);
1327}
1328
1329static void
1330relative_pointer_handle_relative_motion(void *data,
1331 struct zwp_relative_pointer_v1 *pointer,
1332 uint32_t time_hi,
1333 uint32_t time_lo,
1334 wl_fixed_t dx_w,
1335 wl_fixed_t dy_w,
1336 wl_fixed_t dx_unaccel_w,
1337 wl_fixed_t dy_unaccel_w)
1338{
1339 struct SDL_WaylandInput *input = data;
1340 SDL_VideoData *d = input->display;
1341 SDL_WindowData *window = input->pointer_focus;
1342 double dx_unaccel;
1343 double dy_unaccel;
1344 double dx;
1345 double dy;
1346
1347 dx_unaccel = wl_fixed_to_double(dx_unaccel_w);
1348 dy_unaccel = wl_fixed_to_double(dy_unaccel_w);
1349
1350 /* Add left over fraction from last event. */
1351 dx_unaccel += input->dx_frac;
1352 dy_unaccel += input->dy_frac;
1353
1354 input->dx_frac = modf(dx_unaccel, &dx);
1355 input->dy_frac = modf(dy_unaccel, &dy);
1356
1357 if (input->pointer_focus && d->relative_mouse_mode) {
1358 SDL_SendMouseMotion(window->sdlwindow, 0, 1, (int)dx, (int)dy);
1359 }
1360}
1361
1362static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
1363 relative_pointer_handle_relative_motion,
1364};
1365
1366static void
1367locked_pointer_locked(void *data,
1368 struct zwp_locked_pointer_v1 *locked_pointer)
1369{
1370}
1371
1372static void
1373locked_pointer_unlocked(void *data,
1374 struct zwp_locked_pointer_v1 *locked_pointer)
1375{
1376}
1377
1378static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
1379 locked_pointer_locked,
1380 locked_pointer_unlocked,
1381};
1382
1383static void
1384lock_pointer_to_window(SDL_Window *window,
1385 struct SDL_WaylandInput *input)
1386{
1387 SDL_WindowData *w = window->driverdata;
1388 SDL_VideoData *d = input->display;
1389 struct zwp_locked_pointer_v1 *locked_pointer;
1390
1391 if (w->locked_pointer)
1392 return;
1393
1394 locked_pointer =
1395 zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints,
1396 w->surface,
1397 input->pointer,
1398 NULL,
1399 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
1400 zwp_locked_pointer_v1_add_listener(locked_pointer,
1401 &locked_pointer_listener,
1402 window);
1403
1404 w->locked_pointer = locked_pointer;
1405}
1406
1407static void pointer_confine_destroy(struct SDL_WaylandInput *input)
1408{
1409 if (input->confined_pointer) {
1410 zwp_confined_pointer_v1_destroy(input->confined_pointer);
1411 input->confined_pointer = NULL;
1412 }
1413}
1414
1415int Wayland_input_lock_pointer(struct SDL_WaylandInput *input)
1416{
1417 SDL_VideoDevice *vd = SDL_GetVideoDevice();
1418 SDL_VideoData *d = input->display;
1419 SDL_Window *window;
1420 struct zwp_relative_pointer_v1 *relative_pointer;
1421
1422 if (!d->relative_pointer_manager)
1423 return -1;
1424
1425 if (!d->pointer_constraints)
1426 return -1;
1427
1428 if (!input->pointer)
1429 return -1;
1430
1431 /* If we have a pointer confine active, we must destroy it here because
1432 * creating a locked pointer otherwise would be a protocol error. */
1433 pointer_confine_destroy(input);
1434
1435 if (!input->relative_pointer) {
1436 relative_pointer =
1437 zwp_relative_pointer_manager_v1_get_relative_pointer(
1438 d->relative_pointer_manager,
1439 input->pointer);
1440 zwp_relative_pointer_v1_add_listener(relative_pointer,
1441 &relative_pointer_listener,
1442 input);
1443 input->relative_pointer = relative_pointer;
1444 }
1445
1446 for (window = vd->windows; window; window = window->next)
1447 lock_pointer_to_window(window, input);
1448
1449 d->relative_mouse_mode = 1;
1450
1451 return 0;
1452}
1453
1454int Wayland_input_unlock_pointer(struct SDL_WaylandInput *input)
1455{
1456 SDL_VideoDevice *vd = SDL_GetVideoDevice();
1457 SDL_VideoData *d = input->display;
1458 SDL_Window *window;
1459 SDL_WindowData *w;
1460
1461 for (window = vd->windows; window; window = window->next) {
1462 w = window->driverdata;
1463 if (w->locked_pointer)
1464 zwp_locked_pointer_v1_destroy(w->locked_pointer);
1465 w->locked_pointer = NULL;
1466 }
1467
1468 zwp_relative_pointer_v1_destroy(input->relative_pointer);
1469 input->relative_pointer = NULL;
1470
1471 d->relative_mouse_mode = 0;
1472
1473 if (input->confined_pointer_window)
1474 Wayland_input_confine_pointer(input->confined_pointer_window, input);
1475
1476 return 0;
1477}
1478
1479static void
1480confined_pointer_confined(void *data,
1481 struct zwp_confined_pointer_v1 *confined_pointer)
1482{
1483}
1484
1485static void
1486confined_pointer_unconfined(void *data,
1487 struct zwp_confined_pointer_v1 *confined_pointer)
1488{
1489}
1490
1491static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = {
1492 confined_pointer_confined,
1493 confined_pointer_unconfined,
1494};
1495
1496int Wayland_input_confine_pointer(SDL_Window *window, struct SDL_WaylandInput *input)
1497{
1498 SDL_WindowData *w = window->driverdata;
1499 SDL_VideoData *d = input->display;
1500 struct zwp_confined_pointer_v1 *confined_pointer;
1501
1502 if (!d->pointer_constraints)
1503 return -1;
1504
1505 if (!input->pointer)
1506 return -1;
1507
1508 /* A confine may already be active, in which case we should destroy it and
1509 * create a new one. */
1510 if (input->confined_pointer)
1511 Wayland_input_unconfine_pointer(input);
1512
1513 input->confined_pointer_window = window;
1514
1515 /* We cannot create a confine if the pointer is already locked. Defer until
1516 * the pointer is unlocked. */
1517 if (d->relative_mouse_mode)
1518 return 0;
1519
1520 confined_pointer =
1521 zwp_pointer_constraints_v1_confine_pointer(d->pointer_constraints,
1522 w->surface,
1523 input->pointer,
1524 NULL,
1525 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
1526 zwp_confined_pointer_v1_add_listener(confined_pointer,
1527 &confined_pointer_listener,
1528 window);
1529
1530 input->confined_pointer = confined_pointer;
1531 return 0;
1532}
1533
1534int Wayland_input_unconfine_pointer(struct SDL_WaylandInput *input)
1535{
1536 pointer_confine_destroy(input);
1537 input->confined_pointer_window = NULL;
1538 return 0;
1539}
1540
1541int Wayland_input_grab_keyboard(SDL_Window *window, struct SDL_WaylandInput *input)
1542{
1543 SDL_WindowData *w = window->driverdata;
1544 SDL_VideoData *d = input->display;
1545
1546 if (!d->key_inhibitor_manager)
1547 return -1;
1548
1549 if (w->key_inhibitor)
1550 return 0;
1551
1552 w->key_inhibitor =
1553 zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(d->key_inhibitor_manager,
1554 w->surface,
1555 input->seat);
1556
1557 return 0;
1558}
1559
1560int Wayland_input_ungrab_keyboard(SDL_Window *window)
1561{
1562 SDL_WindowData *w = window->driverdata;
1563
1564 if (w->key_inhibitor) {
1565 zwp_keyboard_shortcuts_inhibitor_v1_destroy(w->key_inhibitor);
1566 w->key_inhibitor = NULL;
1567 }
1568
1569 return 0;
1570}
1571
1572#endif /* SDL_VIDEO_DRIVER_WAYLAND */
1573
1574/* vi: set ts=4 sw=4 expandtab: */
1575