src/video/wayland/SDL_waylandevents.c
author Philipp Wiesemann
Thu, 14 Apr 2016 21:11:43 +0200
changeset 10154 fae27a079fcb
parent 10099 c7932bb6dcee
child 10304 ee83e0b4a36f
permissions -rw-r--r--
Wayland: Removed not needed including and setting of errno.

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