SDL  2.0
SDL_waylandevents.c
Go to the documentation of this file.
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 
29 #include "../../events/SDL_sysevents.h"
30 #include "../../events/SDL_events_c.h"
31 #include "../../events/scancodes_xfree86.h"
32 
33 #include "SDL_waylandvideo.h"
34 #include "SDL_waylandevents_c.h"
35 #include "SDL_waylandwindow.h"
36 
37 #include "SDL_waylanddyn.h"
38 
39 #include <linux/input.h>
40 #include <sys/select.h>
41 #include <sys/mman.h>
42 #include <poll.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <xkbcommon/xkbcommon.h>
46 
47 struct SDL_WaylandInput {
48  SDL_VideoData *display;
49  struct wl_seat *seat;
50  struct wl_pointer *pointer;
51  struct wl_keyboard *keyboard;
52  SDL_WindowData *pointer_focus;
53  SDL_WindowData *keyboard_focus;
54 
55  /* Last motion location */
56  wl_fixed_t sx_w;
57  wl_fixed_t sy_w;
58 
59  struct {
60  struct xkb_keymap *keymap;
61  struct xkb_state *state;
62  } xkb;
63 };
64 
65 void
67 {
69  struct pollfd pfd[1];
70 
71  pfd[0].fd = WAYLAND_wl_display_get_fd(d->display);
72  pfd[0].events = POLLIN;
73  poll(pfd, 1, 0);
74 
75  if (pfd[0].revents & POLLIN)
76  WAYLAND_wl_display_dispatch(d->display);
77  else
78  WAYLAND_wl_display_dispatch_pending(d->display);
79 }
80 
81 static void
82 pointer_handle_enter(void *data, struct wl_pointer *pointer,
83  uint32_t serial, struct wl_surface *surface,
84  wl_fixed_t sx_w, wl_fixed_t sy_w)
85 {
86  struct SDL_WaylandInput *input = data;
88 
89  if (!surface) {
90  /* enter event for a window we've just destroyed */
91  return;
92  }
93 
94  /* This handler will be called twice in Wayland 1.4
95  * Once for the window surface which has valid user data
96  * and again for the mouse cursor surface which does not have valid user data
97  * We ignore the later
98  */
99 
100  window = (SDL_WindowData *)wl_surface_get_user_data(surface);
101 
102  if (window) {
103  input->pointer_focus = window;
104  SDL_SetMouseFocus(window->sdlwindow);
105  }
106 }
107 
108 static void
109 pointer_handle_leave(void *data, struct wl_pointer *pointer,
110  uint32_t serial, struct wl_surface *surface)
111 {
112  struct SDL_WaylandInput *input = data;
113 
114  if (input->pointer_focus) {
116  input->pointer_focus = NULL;
117  }
118 }
119 
120 static void
121 pointer_handle_motion(void *data, struct wl_pointer *pointer,
122  uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
123 {
124  struct SDL_WaylandInput *input = data;
125  SDL_WindowData *window = input->pointer_focus;
126  input->sx_w = sx_w;
127  input->sy_w = sy_w;
128  if (input->pointer_focus) {
129  const int sx = wl_fixed_to_int(sx_w);
130  const int sy = wl_fixed_to_int(sy_w);
131  SDL_SendMouseMotion(window->sdlwindow, 0, 0, sx, sy);
132  }
133 }
134 
135 static SDL_bool
136 ProcessHitTest(struct SDL_WaylandInput *input, uint32_t serial)
137 {
138  SDL_WindowData *window_data = input->pointer_focus;
139  SDL_Window *window = window_data->sdlwindow;
140 
141  if (window->hit_test) {
142  const SDL_Point point = { wl_fixed_to_int(input->sx_w), wl_fixed_to_int(input->sy_w) };
143  const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
144  static const uint32_t directions[] = {
145  WL_SHELL_SURFACE_RESIZE_TOP_LEFT, WL_SHELL_SURFACE_RESIZE_TOP,
146  WL_SHELL_SURFACE_RESIZE_TOP_RIGHT, WL_SHELL_SURFACE_RESIZE_RIGHT,
147  WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT, WL_SHELL_SURFACE_RESIZE_BOTTOM,
148  WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT, WL_SHELL_SURFACE_RESIZE_LEFT
149  };
150  switch (rc) {
152  wl_shell_surface_move(window_data->shell_surface, input->seat, serial);
153  return SDL_TRUE;
154 
163  wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, directions[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
164  return SDL_TRUE;
165 
166  default: return SDL_FALSE;
167  }
168  }
169 
170  return SDL_FALSE;
171 }
172 
173 static void
174 pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
175  uint32_t time, uint32_t button, uint32_t state_w)
176 {
177  struct SDL_WaylandInput *input = data;
178  SDL_WindowData *window = input->pointer_focus;
179  enum wl_pointer_button_state state = state_w;
180  uint32_t sdl_button;
181 
182  if (input->pointer_focus) {
183  switch (button) {
184  case BTN_LEFT:
185  sdl_button = SDL_BUTTON_LEFT;
186  if (ProcessHitTest(data, serial)) {
187  return; /* don't pass this event on to app. */
188  }
189  break;
190  case BTN_MIDDLE:
191  sdl_button = SDL_BUTTON_MIDDLE;
192  break;
193  case BTN_RIGHT:
194  sdl_button = SDL_BUTTON_RIGHT;
195  break;
196  case BTN_SIDE:
197  sdl_button = SDL_BUTTON_X1;
198  break;
199  case BTN_EXTRA:
200  sdl_button = SDL_BUTTON_X2;
201  break;
202  default:
203  return;
204  }
205 
206  SDL_SendMouseButton(window->sdlwindow, 0,
207  state ? SDL_PRESSED : SDL_RELEASED, sdl_button);
208  }
209 }
210 
211 static void
212 pointer_handle_axis(void *data, struct wl_pointer *pointer,
213  uint32_t time, uint32_t axis, wl_fixed_t value)
214 {
215  struct SDL_WaylandInput *input = data;
216  SDL_WindowData *window = input->pointer_focus;
217  enum wl_pointer_axis a = axis;
218  int x, y;
219 
220  if (input->pointer_focus) {
221  switch (a) {
222  case WL_POINTER_AXIS_VERTICAL_SCROLL:
223  x = 0;
224  y = wl_fixed_to_int(value);
225  break;
226  case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
227  x = wl_fixed_to_int(value);
228  y = 0;
229  break;
230  default:
231  return;
232  }
233 
235  }
236 }
237 
238 static const struct wl_pointer_listener pointer_listener = {
239  pointer_handle_enter,
240  pointer_handle_leave,
241  pointer_handle_motion,
242  pointer_handle_button,
243  pointer_handle_axis,
244 };
245 
246 static void
247 keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
248  uint32_t format, int fd, uint32_t size)
249 {
250  struct SDL_WaylandInput *input = data;
251  char *map_str;
252 
253  if (!data) {
254  close(fd);
255  return;
256  }
257 
258  if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
259  close(fd);
260  return;
261  }
262 
263  map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
264  if (map_str == MAP_FAILED) {
265  close(fd);
266  return;
267  }
268 
269  input->xkb.keymap = WAYLAND_xkb_keymap_new_from_string(input->display->xkb_context,
270  map_str,
271  XKB_KEYMAP_FORMAT_TEXT_V1,
272  0);
273  munmap(map_str, size);
274  close(fd);
275 
276  if (!input->xkb.keymap) {
277  fprintf(stderr, "failed to compile keymap\n");
278  return;
279  }
280 
281  input->xkb.state = WAYLAND_xkb_state_new(input->xkb.keymap);
282  if (!input->xkb.state) {
283  fprintf(stderr, "failed to create XKB state\n");
284  WAYLAND_xkb_keymap_unref(input->xkb.keymap);
285  input->xkb.keymap = NULL;
286  return;
287  }
288 }
289 
290 static void
291 keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
292  uint32_t serial, struct wl_surface *surface,
293  struct wl_array *keys)
294 {
295  struct SDL_WaylandInput *input = data;
297 
298  if (!surface) {
299  /* enter event for a window we've just destroyed */
300  return;
301  }
302 
303  window = wl_surface_get_user_data(surface);
304 
305  input->keyboard_focus = window;
306  window->keyboard_device = input;
307  if (window) {
309  }
310 }
311 
312 static void
313 keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
314  uint32_t serial, struct wl_surface *surface)
315 {
317 }
318 
319 static void
320 keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
321  uint32_t serial, uint32_t time, uint32_t key,
322  uint32_t state_w)
323 {
324  struct SDL_WaylandInput *input = data;
325  SDL_WindowData *window = input->keyboard_focus;
326  enum wl_keyboard_key_state state = state_w;
327  const xkb_keysym_t *syms;
328  uint32_t scancode;
329  char text[8];
330  int size;
331 
333  scancode = xfree86_scancode_table2[key];
334 
335  // TODO when do we get WL_KEYBOARD_KEY_STATE_REPEAT?
336  if (scancode != SDL_SCANCODE_UNKNOWN)
337  SDL_SendKeyboardKey(state == WL_KEYBOARD_KEY_STATE_PRESSED ?
338  SDL_PRESSED : SDL_RELEASED, scancode);
339  }
340 
341  if (!window || window->keyboard_device != input || !input->xkb.state)
342  return;
343 
344  // TODO can this happen?
345  if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1)
346  return;
347 
348  if (state) {
349  size = WAYLAND_xkb_keysym_to_utf8(syms[0], text, sizeof text);
350 
351  if (size > 0) {
352  text[size] = 0;
353  SDL_SendKeyboardText(text);
354  }
355  }
356 }
357 
358 static void
359 keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
360  uint32_t serial, uint32_t mods_depressed,
361  uint32_t mods_latched, uint32_t mods_locked,
362  uint32_t group)
363 {
364  struct SDL_WaylandInput *input = data;
365 
366  WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
367  mods_locked, 0, 0, group);
368 }
369 
370 static const struct wl_keyboard_listener keyboard_listener = {
371  keyboard_handle_keymap,
372  keyboard_handle_enter,
373  keyboard_handle_leave,
374  keyboard_handle_key,
375  keyboard_handle_modifiers,
376 };
377 
378 static void
379 seat_handle_capabilities(void *data, struct wl_seat *seat,
380  enum wl_seat_capability caps)
381 {
382  struct SDL_WaylandInput *input = data;
383 
384  if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
385  input->pointer = wl_seat_get_pointer(seat);
386  input->display->pointer = input->pointer;
387  wl_pointer_set_user_data(input->pointer, input);
388  wl_pointer_add_listener(input->pointer, &pointer_listener,
389  input);
390  } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
391  wl_pointer_destroy(input->pointer);
392  input->pointer = NULL;
393  }
394 
395  if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
396  input->keyboard = wl_seat_get_keyboard(seat);
397  wl_keyboard_set_user_data(input->keyboard, input);
398  wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
399  input);
400  } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
401  wl_keyboard_destroy(input->keyboard);
402  input->keyboard = NULL;
403  }
404 }
405 
406 static const struct wl_seat_listener seat_listener = {
407  seat_handle_capabilities,
408 };
409 
410 void
412 {
413  struct SDL_WaylandInput *input;
414 
415  input = SDL_calloc(1, sizeof *input);
416  if (input == NULL)
417  return;
418 
419  input->display = d;
420  input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 1);
421  input->sx_w = wl_fixed_from_int(0);
422  input->sy_w = wl_fixed_from_int(0);
423  d->input = input;
424 
425  wl_seat_add_listener(input->seat, &seat_listener, input);
426  wl_seat_set_user_data(input->seat, input);
427 
428  WAYLAND_wl_display_flush(d->display);
429 }
430 
432 {
433  struct SDL_WaylandInput *input = d->input;
434 
435  if (!input)
436  return;
437 
438  if (input->keyboard)
439  wl_keyboard_destroy(input->keyboard);
440 
441  if (input->pointer)
442  wl_pointer_destroy(input->pointer);
443 
444  if (input->seat)
445  wl_seat_destroy(input->seat);
446 
447  if (input->xkb.state)
448  WAYLAND_xkb_state_unref(input->xkb.state);
449 
450  if (input->xkb.keymap)
451  WAYLAND_xkb_keymap_unref(input->xkb.keymap);
452 
453  SDL_free(input);
454  d->input = NULL;
455 }
456 
457 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
458 
459 /* vi: set ts=4 sw=4 expandtab: */
GLboolean GLuint group
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1565
SDL_Texture * button
void SDL_SetKeyboardFocus(SDL_Window *window)
Definition: SDL_keyboard.c:612
void * hit_test_data
Definition: SDL_sysvideo.h:102
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1567
struct wl_display * display
SDL_Window * window
#define SDL_BUTTON_RIGHT
Definition: SDL_mouse.h:282
struct xkb_state * state
The structure that defines a point.
Definition: SDL_rect.h:48
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
static const SDL_Scancode xfree86_scancode_table2[]
SDL_Texture * axis
#define SDL_BUTTON_X1
Definition: SDL_mouse.h:283
void SDL_SetMouseFocus(SDL_Window *window)
Definition: SDL_mouse.c:103
struct SDL_WaylandInput * input
GLsizeiptr size
GLsizei const void * pointer
int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
Definition: SDL_keyboard.c:661
static SDL_VideoDevice * _this
Definition: SDL_video.c:114
SDL_HitTestResult
Possible return values from the SDL_HitTest callback.
Definition: SDL_video.h:852
SDL_bool
Definition: SDL_stdinc.h:126
struct SDL_WaylandInput * keyboard_device
void * SDL_calloc(size_t nmemb, size_t size)
GLenum GLenum GLenum input
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
int SDL_SendMouseMotion(SDL_Window *window, SDL_MouseID mouseID, int relative, int x, int y)
Definition: SDL_mouse.c:188
#define SDL_BUTTON_LEFT
Definition: SDL_mouse.h:280
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 SDL_AssertionHandler void SDL_SpinLock SDL_atomic_t int int return SDL_atomic_t return void void void return void return int return SDL_AudioSpec SDL_AudioSpec return int int return return int SDL_RWops int SDL_AudioSpec Uint8 ** d
GLsizei const GLfloat * value
#define _THIS
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:774
void SDL_free(void *mem)
SDL_Window * sdlwindow
struct wl_shell_surface * shell_surface
#define SDL_BUTTON_MIDDLE
Definition: SDL_mouse.h:281
void Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
#define NULL
Definition: begin_code.h:143
SDL_HitTest hit_test
Definition: SDL_sysvideo.h:101
unsigned int uint32_t
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:34
void Wayland_display_destroy_input(SDL_VideoData *d)
void Wayland_PumpEvents(_THIS)
The type used to identify a window.
Definition: SDL_sysvideo.h:71
struct wl_registry * registry
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
int SDL_SendMouseWheel(SDL_Window *window, SDL_MouseID mouseID, int x, int y, SDL_MouseWheelDirection direction)
Definition: SDL_mouse.c:406
#define SDL_PRESSED
Definition: SDL_events.h:50
#define SDL_BUTTON_X2
Definition: SDL_mouse.h:284
GLboolean GLboolean GLboolean GLboolean a
#define SDL_RELEASED
Definition: SDL_events.h:49
int SDL_SendMouseButton(SDL_Window *window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
Definition: SDL_mouse.c:326