1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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_assert.h"
28 #include "SDL_log.h"
29
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
43 #include <linux/input.h>
44 #include <sys/select.h>
45 #include <sys/mman.h>
46 #include <poll.h>
47 #include <unistd.h>
48 #include <xkbcommon/xkbcommon.h>
49
50 struct SDL_WaylandInput {
51 SDL_VideoData *display;
52 struct wl_seat *seat;
53 struct wl_pointer *pointer;
54 struct wl_keyboard *keyboard;
55 struct zwp_relative_pointer_v1 *relative_pointer;
56 SDL_WindowData *pointer_focus;
57 SDL_WindowData *keyboard_focus;
58
59 /* Last motion location */
60 wl_fixed_t sx_w;
61 wl_fixed_t sy_w;
62
63 double dx_frac;
64 double dy_frac;
65
66 struct {
67 struct xkb_keymap *keymap;
68 struct xkb_state *state;
69 } xkb;
70 };
71
72 void
Wayland_PumpEvents(_THIS)73 Wayland_PumpEvents(_THIS)
74 {
75 SDL_VideoData *d = _this->driverdata;
76 struct pollfd pfd[1];
77
78 pfd[0].fd = WAYLAND_wl_display_get_fd(d->display);
79 pfd[0].events = POLLIN;
80 poll(pfd, 1, 0);
81
82 if (pfd[0].revents & POLLIN)
83 WAYLAND_wl_display_dispatch(d->display);
84 else
85 WAYLAND_wl_display_dispatch_pending(d->display);
86 }
87
88 static void
pointer_handle_enter(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t sx_w,wl_fixed_t sy_w)89 pointer_handle_enter(void *data, struct wl_pointer *pointer,
90 uint32_t serial, struct wl_surface *surface,
91 wl_fixed_t sx_w, wl_fixed_t sy_w)
92 {
93 struct SDL_WaylandInput *input = data;
94 SDL_WindowData *window;
95
96 if (!surface) {
97 /* enter event for a window we've just destroyed */
98 return;
99 }
100
101 /* This handler will be called twice in Wayland 1.4
102 * Once for the window surface which has valid user data
103 * and again for the mouse cursor surface which does not have valid user data
104 * We ignore the later
105 */
106
107 window = (SDL_WindowData *)wl_surface_get_user_data(surface);
108
109 if (window) {
110 input->pointer_focus = window;
111 SDL_SetMouseFocus(window->sdlwindow);
112 }
113 }
114
115 static void
pointer_handle_leave(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface)116 pointer_handle_leave(void *data, struct wl_pointer *pointer,
117 uint32_t serial, struct wl_surface *surface)
118 {
119 struct SDL_WaylandInput *input = data;
120
121 if (input->pointer_focus) {
122 SDL_SetMouseFocus(NULL);
123 input->pointer_focus = NULL;
124 }
125 }
126
127 static void
pointer_handle_motion(void * data,struct wl_pointer * pointer,uint32_t time,wl_fixed_t sx_w,wl_fixed_t sy_w)128 pointer_handle_motion(void *data, struct wl_pointer *pointer,
129 uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
130 {
131 struct SDL_WaylandInput *input = data;
132 SDL_WindowData *window = input->pointer_focus;
133 input->sx_w = sx_w;
134 input->sy_w = sy_w;
135 if (input->pointer_focus) {
136 const int sx = wl_fixed_to_int(sx_w);
137 const int sy = wl_fixed_to_int(sy_w);
138 SDL_SendMouseMotion(window->sdlwindow, 0, 0, sx, sy);
139 }
140 }
141
142 static SDL_bool
ProcessHitTest(struct SDL_WaylandInput * input,uint32_t serial)143 ProcessHitTest(struct SDL_WaylandInput *input, uint32_t serial)
144 {
145 SDL_WindowData *window_data = input->pointer_focus;
146 SDL_Window *window = window_data->sdlwindow;
147
148 if (window->hit_test) {
149 const SDL_Point point = { wl_fixed_to_int(input->sx_w), wl_fixed_to_int(input->sy_w) };
150 const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
151 static const uint32_t directions[] = {
152 WL_SHELL_SURFACE_RESIZE_TOP_LEFT, WL_SHELL_SURFACE_RESIZE_TOP,
153 WL_SHELL_SURFACE_RESIZE_TOP_RIGHT, WL_SHELL_SURFACE_RESIZE_RIGHT,
154 WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT, WL_SHELL_SURFACE_RESIZE_BOTTOM,
155 WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT, WL_SHELL_SURFACE_RESIZE_LEFT
156 };
157 switch (rc) {
158 case SDL_HITTEST_DRAGGABLE:
159 wl_shell_surface_move(window_data->shell_surface, input->seat, serial);
160 return SDL_TRUE;
161
162 case SDL_HITTEST_RESIZE_TOPLEFT:
163 case SDL_HITTEST_RESIZE_TOP:
164 case SDL_HITTEST_RESIZE_TOPRIGHT:
165 case SDL_HITTEST_RESIZE_RIGHT:
166 case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
167 case SDL_HITTEST_RESIZE_BOTTOM:
168 case SDL_HITTEST_RESIZE_BOTTOMLEFT:
169 case SDL_HITTEST_RESIZE_LEFT:
170 wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, directions[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
171 return SDL_TRUE;
172
173 default: return SDL_FALSE;
174 }
175 }
176
177 return SDL_FALSE;
178 }
179
180 static void
pointer_handle_button_common(struct SDL_WaylandInput * input,uint32_t serial,uint32_t time,uint32_t button,uint32_t state_w)181 pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial,
182 uint32_t time, uint32_t button, uint32_t state_w)
183 {
184 SDL_WindowData *window = input->pointer_focus;
185 enum wl_pointer_button_state state = state_w;
186 uint32_t sdl_button;
187
188 if (input->pointer_focus) {
189 switch (button) {
190 case BTN_LEFT:
191 sdl_button = SDL_BUTTON_LEFT;
192 if (ProcessHitTest(input, serial)) {
193 return; /* don't pass this event on to app. */
194 }
195 break;
196 case BTN_MIDDLE:
197 sdl_button = SDL_BUTTON_MIDDLE;
198 break;
199 case BTN_RIGHT:
200 sdl_button = SDL_BUTTON_RIGHT;
201 break;
202 case BTN_SIDE:
203 sdl_button = SDL_BUTTON_X1;
204 break;
205 case BTN_EXTRA:
206 sdl_button = SDL_BUTTON_X2;
207 break;
208 default:
209 return;
210 }
211
212 SDL_SendMouseButton(window->sdlwindow, 0,
213 state ? SDL_PRESSED : SDL_RELEASED, sdl_button);
214 }
215 }
216
217 static void
pointer_handle_button(void * data,struct wl_pointer * pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state_w)218 pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
219 uint32_t time, uint32_t button, uint32_t state_w)
220 {
221 struct SDL_WaylandInput *input = data;
222
223 pointer_handle_button_common(input, serial, time, button, state_w);
224 }
225
226 static void
pointer_handle_axis_common(struct SDL_WaylandInput * input,uint32_t time,uint32_t axis,wl_fixed_t value)227 pointer_handle_axis_common(struct SDL_WaylandInput *input,
228 uint32_t time, uint32_t axis, wl_fixed_t value)
229 {
230 SDL_WindowData *window = input->pointer_focus;
231 enum wl_pointer_axis a = axis;
232 int x, y;
233
234 if (input->pointer_focus) {
235 switch (a) {
236 case WL_POINTER_AXIS_VERTICAL_SCROLL:
237 x = 0;
238 y = wl_fixed_to_int(value);
239 break;
240 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
241 x = wl_fixed_to_int(value);
242 y = 0;
243 break;
244 default:
245 return;
246 }
247
248 SDL_SendMouseWheel(window->sdlwindow, 0, x, y, SDL_MOUSEWHEEL_NORMAL);
249 }
250 }
251
252 static void
pointer_handle_axis(void * data,struct wl_pointer * pointer,uint32_t time,uint32_t axis,wl_fixed_t value)253 pointer_handle_axis(void *data, struct wl_pointer *pointer,
254 uint32_t time, uint32_t axis, wl_fixed_t value)
255 {
256 struct SDL_WaylandInput *input = data;
257
258 pointer_handle_axis_common(input, time, axis, value);
259 }
260
261 static const struct wl_pointer_listener pointer_listener = {
262 pointer_handle_enter,
263 pointer_handle_leave,
264 pointer_handle_motion,
265 pointer_handle_button,
266 pointer_handle_axis,
267 };
268
269 static void
keyboard_handle_keymap(void * data,struct wl_keyboard * keyboard,uint32_t format,int fd,uint32_t size)270 keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
271 uint32_t format, int fd, uint32_t size)
272 {
273 struct SDL_WaylandInput *input = data;
274 char *map_str;
275
276 if (!data) {
277 close(fd);
278 return;
279 }
280
281 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
282 close(fd);
283 return;
284 }
285
286 map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
287 if (map_str == MAP_FAILED) {
288 close(fd);
289 return;
290 }
291
292 input->xkb.keymap = WAYLAND_xkb_keymap_new_from_string(input->display->xkb_context,
293 map_str,
294 XKB_KEYMAP_FORMAT_TEXT_V1,
295 0);
296 munmap(map_str, size);
297 close(fd);
298
299 if (!input->xkb.keymap) {
300 fprintf(stderr, "failed to compile keymap\n");
301 return;
302 }
303
304 input->xkb.state = WAYLAND_xkb_state_new(input->xkb.keymap);
305 if (!input->xkb.state) {
306 fprintf(stderr, "failed to create XKB state\n");
307 WAYLAND_xkb_keymap_unref(input->xkb.keymap);
308 input->xkb.keymap = NULL;
309 return;
310 }
311 }
312
313 static void
keyboard_handle_enter(void * data,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface,struct wl_array * keys)314 keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
315 uint32_t serial, struct wl_surface *surface,
316 struct wl_array *keys)
317 {
318 struct SDL_WaylandInput *input = data;
319 SDL_WindowData *window;
320
321 if (!surface) {
322 /* enter event for a window we've just destroyed */
323 return;
324 }
325
326 window = wl_surface_get_user_data(surface);
327
328 if (window) {
329 input->keyboard_focus = window;
330 window->keyboard_device = input;
331 SDL_SetKeyboardFocus(window->sdlwindow);
332 }
333 }
334
335 static void
keyboard_handle_leave(void * data,struct wl_keyboard * keyboard,uint32_t serial,struct wl_surface * surface)336 keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
337 uint32_t serial, struct wl_surface *surface)
338 {
339 SDL_SetKeyboardFocus(NULL);
340 }
341
342 static void
keyboard_handle_key(void * data,struct wl_keyboard * keyboard,uint32_t serial,uint32_t time,uint32_t key,uint32_t state_w)343 keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
344 uint32_t serial, uint32_t time, uint32_t key,
345 uint32_t state_w)
346 {
347 struct SDL_WaylandInput *input = data;
348 SDL_WindowData *window = input->keyboard_focus;
349 enum wl_keyboard_key_state state = state_w;
350 const xkb_keysym_t *syms;
351 uint32_t scancode;
352 char text[8];
353 int size;
354
355 if (key < SDL_arraysize(xfree86_scancode_table2)) {
356 scancode = xfree86_scancode_table2[key];
357
358 // TODO when do we get WL_KEYBOARD_KEY_STATE_REPEAT?
359 if (scancode != SDL_SCANCODE_UNKNOWN)
360 SDL_SendKeyboardKey(state == WL_KEYBOARD_KEY_STATE_PRESSED ?
361 SDL_PRESSED : SDL_RELEASED, scancode);
362 }
363
364 if (!window || window->keyboard_device != input || !input->xkb.state)
365 return;
366
367 // TODO can this happen?
368 if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1)
369 return;
370
371 if (state) {
372 size = WAYLAND_xkb_keysym_to_utf8(syms[0], text, sizeof text);
373
374 if (size > 0) {
375 text[size] = 0;
376 SDL_SendKeyboardText(text);
377 }
378 }
379 }
380
381 static void
keyboard_handle_modifiers(void * data,struct wl_keyboard * keyboard,uint32_t serial,uint32_t mods_depressed,uint32_t mods_latched,uint32_t mods_locked,uint32_t group)382 keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
383 uint32_t serial, uint32_t mods_depressed,
384 uint32_t mods_latched, uint32_t mods_locked,
385 uint32_t group)
386 {
387 struct SDL_WaylandInput *input = data;
388
389 WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
390 mods_locked, 0, 0, group);
391 }
392
393 static const struct wl_keyboard_listener keyboard_listener = {
394 keyboard_handle_keymap,
395 keyboard_handle_enter,
396 keyboard_handle_leave,
397 keyboard_handle_key,
398 keyboard_handle_modifiers,
399 };
400
401 static void
seat_handle_capabilities(void * data,struct wl_seat * seat,enum wl_seat_capability caps)402 seat_handle_capabilities(void *data, struct wl_seat *seat,
403 enum wl_seat_capability caps)
404 {
405 struct SDL_WaylandInput *input = data;
406
407 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
408 input->pointer = wl_seat_get_pointer(seat);
409 input->display->pointer = input->pointer;
410 wl_pointer_set_user_data(input->pointer, input);
411 wl_pointer_add_listener(input->pointer, &pointer_listener,
412 input);
413 } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
414 wl_pointer_destroy(input->pointer);
415 input->pointer = NULL;
416 }
417
418 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
419 input->keyboard = wl_seat_get_keyboard(seat);
420 wl_keyboard_set_user_data(input->keyboard, input);
421 wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
422 input);
423 } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
424 wl_keyboard_destroy(input->keyboard);
425 input->keyboard = NULL;
426 }
427 }
428
429 static const struct wl_seat_listener seat_listener = {
430 seat_handle_capabilities,
431 };
432
433 void
Wayland_display_add_input(SDL_VideoData * d,uint32_t id)434 Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
435 {
436 struct SDL_WaylandInput *input;
437
438 input = SDL_calloc(1, sizeof *input);
439 if (input == NULL)
440 return;
441
442 input->display = d;
443 input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 1);
444 input->sx_w = wl_fixed_from_int(0);
445 input->sy_w = wl_fixed_from_int(0);
446 d->input = input;
447
448 wl_seat_add_listener(input->seat, &seat_listener, input);
449 wl_seat_set_user_data(input->seat, input);
450
451 WAYLAND_wl_display_flush(d->display);
452 }
453
Wayland_display_destroy_input(SDL_VideoData * d)454 void Wayland_display_destroy_input(SDL_VideoData *d)
455 {
456 struct SDL_WaylandInput *input = d->input;
457
458 if (!input)
459 return;
460
461 if (input->keyboard)
462 wl_keyboard_destroy(input->keyboard);
463
464 if (input->pointer)
465 wl_pointer_destroy(input->pointer);
466
467 if (input->seat)
468 wl_seat_destroy(input->seat);
469
470 if (input->xkb.state)
471 WAYLAND_xkb_state_unref(input->xkb.state);
472
473 if (input->xkb.keymap)
474 WAYLAND_xkb_keymap_unref(input->xkb.keymap);
475
476 SDL_free(input);
477 d->input = NULL;
478 }
479
Wayland_display_add_relative_pointer_manager(SDL_VideoData * d,uint32_t id)480 void Wayland_display_add_relative_pointer_manager(SDL_VideoData *d, uint32_t id)
481 {
482 d->relative_pointer_manager =
483 wl_registry_bind(d->registry, id,
484 &zwp_relative_pointer_manager_v1_interface, 1);
485 }
486
Wayland_display_destroy_relative_pointer_manager(SDL_VideoData * d)487 void Wayland_display_destroy_relative_pointer_manager(SDL_VideoData *d)
488 {
489 if (d->relative_pointer_manager)
490 zwp_relative_pointer_manager_v1_destroy(d->relative_pointer_manager);
491 }
492
Wayland_display_add_pointer_constraints(SDL_VideoData * d,uint32_t id)493 void Wayland_display_add_pointer_constraints(SDL_VideoData *d, uint32_t id)
494 {
495 d->pointer_constraints =
496 wl_registry_bind(d->registry, id,
497 &zwp_pointer_constraints_v1_interface, 1);
498 }
499
Wayland_display_destroy_pointer_constraints(SDL_VideoData * d)500 void Wayland_display_destroy_pointer_constraints(SDL_VideoData *d)
501 {
502 if (d->pointer_constraints)
503 zwp_pointer_constraints_v1_destroy(d->pointer_constraints);
504 }
505
506 static void
relative_pointer_handle_relative_motion(void * data,struct zwp_relative_pointer_v1 * pointer,uint32_t time_hi,uint32_t time_lo,wl_fixed_t dx_w,wl_fixed_t dy_w,wl_fixed_t dx_unaccel_w,wl_fixed_t dy_unaccel_w)507 relative_pointer_handle_relative_motion(void *data,
508 struct zwp_relative_pointer_v1 *pointer,
509 uint32_t time_hi,
510 uint32_t time_lo,
511 wl_fixed_t dx_w,
512 wl_fixed_t dy_w,
513 wl_fixed_t dx_unaccel_w,
514 wl_fixed_t dy_unaccel_w)
515 {
516 struct SDL_WaylandInput *input = data;
517 SDL_VideoData *d = input->display;
518 SDL_WindowData *window = input->pointer_focus;
519 double dx_unaccel;
520 double dy_unaccel;
521 double dx;
522 double dy;
523
524 dx_unaccel = wl_fixed_to_double(dx_unaccel_w);
525 dy_unaccel = wl_fixed_to_double(dy_unaccel_w);
526
527 /* Add left over fraction from last event. */
528 dx_unaccel += input->dx_frac;
529 dy_unaccel += input->dy_frac;
530
531 input->dx_frac = modf(dx_unaccel, &dx);
532 input->dy_frac = modf(dy_unaccel, &dy);
533
534 if (input->pointer_focus && d->relative_mouse_mode) {
535 SDL_SendMouseMotion(window->sdlwindow, 0, 1, (int)dx, (int)dy);
536 }
537 }
538
539 static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
540 relative_pointer_handle_relative_motion,
541 };
542
543 static void
locked_pointer_locked(void * data,struct zwp_locked_pointer_v1 * locked_pointer)544 locked_pointer_locked(void *data,
545 struct zwp_locked_pointer_v1 *locked_pointer)
546 {
547 }
548
549 static void
locked_pointer_unlocked(void * data,struct zwp_locked_pointer_v1 * locked_pointer)550 locked_pointer_unlocked(void *data,
551 struct zwp_locked_pointer_v1 *locked_pointer)
552 {
553 }
554
555 static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
556 locked_pointer_locked,
557 locked_pointer_unlocked,
558 };
559
560 static void
lock_pointer_to_window(SDL_Window * window,struct SDL_WaylandInput * input)561 lock_pointer_to_window(SDL_Window *window,
562 struct SDL_WaylandInput *input)
563 {
564 SDL_WindowData *w = window->driverdata;
565 SDL_VideoData *d = input->display;
566 struct zwp_locked_pointer_v1 *locked_pointer;
567
568 if (w->locked_pointer)
569 return;
570
571 locked_pointer =
572 zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints,
573 w->surface,
574 input->pointer,
575 NULL,
576 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
577 zwp_locked_pointer_v1_add_listener(locked_pointer,
578 &locked_pointer_listener,
579 window);
580
581 w->locked_pointer = locked_pointer;
582 }
583
Wayland_input_lock_pointer(struct SDL_WaylandInput * input)584 int Wayland_input_lock_pointer(struct SDL_WaylandInput *input)
585 {
586 SDL_VideoDevice *vd = SDL_GetVideoDevice();
587 SDL_VideoData *d = input->display;
588 SDL_Window *window;
589 struct zwp_relative_pointer_v1 *relative_pointer;
590
591 if (!d->relative_pointer_manager)
592 return -1;
593
594 if (!d->pointer_constraints)
595 return -1;
596
597 if (!input->relative_pointer) {
598 relative_pointer =
599 zwp_relative_pointer_manager_v1_get_relative_pointer(
600 d->relative_pointer_manager,
601 input->pointer);
602 zwp_relative_pointer_v1_add_listener(relative_pointer,
603 &relative_pointer_listener,
604 input);
605 input->relative_pointer = relative_pointer;
606 }
607
608 for (window = vd->windows; window; window = window->next)
609 lock_pointer_to_window(window, input);
610
611 d->relative_mouse_mode = 1;
612
613 return 0;
614 }
615
Wayland_input_unlock_pointer(struct SDL_WaylandInput * input)616 int Wayland_input_unlock_pointer(struct SDL_WaylandInput *input)
617 {
618 SDL_VideoDevice *vd = SDL_GetVideoDevice();
619 SDL_VideoData *d = input->display;
620 SDL_Window *window;
621 SDL_WindowData *w;
622
623 for (window = vd->windows; window; window = window->next) {
624 w = window->driverdata;
625 if (w->locked_pointer)
626 zwp_locked_pointer_v1_destroy(w->locked_pointer);
627 w->locked_pointer = NULL;
628 }
629
630 zwp_relative_pointer_v1_destroy(input->relative_pointer);
631 input->relative_pointer = NULL;
632
633 d->relative_mouse_mode = 0;
634
635 return 0;
636 }
637
638 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
639
640 /* vi: set ts=4 sw=4 expandtab: */
641