src/video/wayland/SDL_waylandevents.c
author Philipp Wiesemann
Wed, 02 Mar 2016 20:25:23 +0100
changeset 10099 c7932bb6dcee
parent 9998 f67cf37e9cd4
child 10154 fae27a079fcb
permissions -rw-r--r--
Wayland: Fixed fault in event handling which might have caused a crash someday.

Found by Cppcheck.
     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
    66 Wayland_PumpEvents(_THIS)
    67 {
    68     SDL_VideoData *d = _this->driverdata;
    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;
    87     SDL_WindowData *window;
    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) {
   115         SDL_SetMouseFocus(NULL);
   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) {
   151             case SDL_HITTEST_DRAGGABLE:
   152                 wl_shell_surface_move(window_data->shell_surface, input->seat, serial);
   153                 return SDL_TRUE;
   154 
   155             case SDL_HITTEST_RESIZE_TOPLEFT:
   156             case SDL_HITTEST_RESIZE_TOP:
   157             case SDL_HITTEST_RESIZE_TOPRIGHT:
   158             case SDL_HITTEST_RESIZE_RIGHT:
   159             case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
   160             case SDL_HITTEST_RESIZE_BOTTOM:
   161             case SDL_HITTEST_RESIZE_BOTTOMLEFT:
   162             case SDL_HITTEST_RESIZE_LEFT:
   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 
   234         SDL_SendMouseWheel(window->sdlwindow, 0, x, y, SDL_MOUSEWHEEL_NORMAL);
   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;
   296     SDL_WindowData *window;
   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     if (window) {
   306         input->keyboard_focus = window;
   307         window->keyboard_device = input;
   308         SDL_SetKeyboardFocus(window->sdlwindow);
   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 {
   316     SDL_SetKeyboardFocus(NULL);
   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 
   332     if (key < SDL_arraysize(xfree86_scancode_table2)) {
   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
   411 Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
   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 
   431 void Wayland_display_destroy_input(SDL_VideoData *d)
   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: */