src/video/wayland/SDL_waylandevents.c
author Ryan C. Gordon
Sun, 12 Apr 2015 20:59:48 -0400
changeset 9555 fff4b6354b99
parent 9554 879f71e1478b
child 9556 db92b9d74129
permissions -rw-r--r--
Make X11 and Wayland ProcessHitTest() code less verbose.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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     int sx = wl_fixed_to_int(sx_w);
   129     int sy = wl_fixed_to_int(sy_w);
   130     if (input->pointer_focus) {
   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 = wl_surface_get_user_data(surface);
   297 
   298     input->keyboard_focus = window;
   299     window->keyboard_device = input;
   300     if (window) {
   301         SDL_SetKeyboardFocus(window->sdlwindow);
   302     }
   303 }
   304 
   305 static void
   306 keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
   307                       uint32_t serial, struct wl_surface *surface)
   308 {
   309     SDL_SetKeyboardFocus(NULL);
   310 }
   311 
   312 static void
   313 keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
   314                     uint32_t serial, uint32_t time, uint32_t key,
   315                     uint32_t state_w)
   316 {
   317     struct SDL_WaylandInput *input = data;
   318     SDL_WindowData *window = input->keyboard_focus;
   319     enum wl_keyboard_key_state state = state_w;
   320     const xkb_keysym_t *syms;
   321     uint32_t scancode;
   322     char text[8];
   323     int size;
   324 
   325     if (key < SDL_arraysize(xfree86_scancode_table2)) {
   326         scancode = xfree86_scancode_table2[key];
   327 
   328         // TODO when do we get WL_KEYBOARD_KEY_STATE_REPEAT?
   329         if (scancode != SDL_SCANCODE_UNKNOWN)
   330             SDL_SendKeyboardKey(state == WL_KEYBOARD_KEY_STATE_PRESSED ?
   331                                 SDL_PRESSED : SDL_RELEASED, scancode);
   332     }
   333 
   334     if (!window || window->keyboard_device != input || !input->xkb.state)
   335         return;
   336 
   337     // TODO can this happen?
   338     if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1)
   339         return;
   340 
   341     if (state) {
   342         size = WAYLAND_xkb_keysym_to_utf8(syms[0], text, sizeof text);
   343 
   344         if (size > 0) {
   345             text[size] = 0;
   346             SDL_SendKeyboardText(text);
   347         }
   348     }
   349 }
   350 
   351 static void
   352 keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
   353                           uint32_t serial, uint32_t mods_depressed,
   354                           uint32_t mods_latched, uint32_t mods_locked,
   355                           uint32_t group)
   356 {
   357     struct SDL_WaylandInput *input = data;
   358 
   359     WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
   360                           mods_locked, 0, 0, group);
   361 }
   362 
   363 static const struct wl_keyboard_listener keyboard_listener = {
   364     keyboard_handle_keymap,
   365     keyboard_handle_enter,
   366     keyboard_handle_leave,
   367     keyboard_handle_key,
   368     keyboard_handle_modifiers,
   369 };
   370 
   371 static void
   372 seat_handle_capabilities(void *data, struct wl_seat *seat,
   373                          enum wl_seat_capability caps)
   374 {
   375     struct SDL_WaylandInput *input = data;
   376 
   377     if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
   378         input->pointer = wl_seat_get_pointer(seat);
   379         input->display->pointer = input->pointer;
   380         wl_pointer_set_user_data(input->pointer, input);
   381         wl_pointer_add_listener(input->pointer, &pointer_listener,
   382                                 input);
   383     } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
   384         wl_pointer_destroy(input->pointer);
   385         input->pointer = NULL;
   386     }
   387 
   388     if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
   389         input->keyboard = wl_seat_get_keyboard(seat);
   390         wl_keyboard_set_user_data(input->keyboard, input);
   391         wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
   392                                  input);
   393     } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
   394         wl_keyboard_destroy(input->keyboard);
   395         input->keyboard = NULL;
   396     }
   397 }
   398 
   399 static const struct wl_seat_listener seat_listener = {
   400     seat_handle_capabilities,
   401 };
   402 
   403 void
   404 Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
   405 {
   406     struct SDL_WaylandInput *input;
   407 
   408     input = SDL_calloc(1, sizeof *input);
   409     if (input == NULL)
   410         return;
   411 
   412     input->display = d;
   413     input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 1);
   414     input->sx_w = wl_fixed_from_int(0);
   415     input->sy_w = wl_fixed_from_int(0);
   416     d->input = input;
   417 
   418     wl_seat_add_listener(input->seat, &seat_listener, input);
   419     wl_seat_set_user_data(input->seat, input);
   420 
   421     WAYLAND_wl_display_flush(d->display);
   422 }
   423 
   424 void Wayland_display_destroy_input(SDL_VideoData *d)
   425 {
   426     struct SDL_WaylandInput *input = d->input;
   427 
   428     if (!input)
   429         return;
   430 
   431     if (input->keyboard)
   432         wl_keyboard_destroy(input->keyboard);
   433 
   434     if (input->pointer)
   435         wl_pointer_destroy(input->pointer);
   436 
   437     if (input->seat)
   438         wl_seat_destroy(input->seat);
   439 
   440     if (input->xkb.state)
   441         WAYLAND_xkb_state_unref(input->xkb.state);
   442 
   443     if (input->xkb.keymap)
   444         WAYLAND_xkb_keymap_unref(input->xkb.keymap);
   445 
   446     SDL_free(input);
   447     d->input = NULL;
   448 }
   449 
   450 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
   451 
   452 /* vi: set ts=4 sw=4 expandtab: */