src/video/wayland/SDL_waylandevents.c
author Ryan C. Gordon
Sun, 12 Apr 2015 20:40:06 -0400
changeset 9554 879f71e1478b
parent 9257 6f41196c2d6b
child 9555 fff4b6354b99
permissions -rw-r--r--
Implemented SetWindowHitTest() for Wayland (thanks, x414e54!).

Fixes Bugzilla #2941.
     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     SDL_bool ret = SDL_FALSE;
   141 
   142     if (window->hit_test) {
   143         const SDL_Point point = { wl_fixed_to_int(input->sx_w), wl_fixed_to_int(input->sy_w) };
   144         const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
   145         switch (rc) {
   146             case SDL_HITTEST_DRAGGABLE: {
   147                     wl_shell_surface_move(window_data->shell_surface, input->seat, serial);
   148                     ret = SDL_TRUE;
   149                 }
   150                 break;
   151             case SDL_HITTEST_RESIZE_TOPLEFT: {
   152                     wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, WL_SHELL_SURFACE_RESIZE_TOP_LEFT);
   153                     ret = SDL_TRUE;
   154                 }
   155                 break;
   156             case SDL_HITTEST_RESIZE_TOP: {
   157                     wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, WL_SHELL_SURFACE_RESIZE_TOP);
   158                     ret = SDL_TRUE;
   159                 }
   160                 break;
   161             case SDL_HITTEST_RESIZE_TOPRIGHT: {
   162                     wl_shell_surface_resize(window_data->shell_surface, input->seat, serial,  WL_SHELL_SURFACE_RESIZE_TOP_RIGHT);
   163                     ret = SDL_TRUE;
   164                 }
   165                 break;
   166             case SDL_HITTEST_RESIZE_RIGHT: {
   167                     wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, WL_SHELL_SURFACE_RESIZE_RIGHT);
   168                     ret = SDL_TRUE;
   169                 }
   170                 break;
   171             case SDL_HITTEST_RESIZE_BOTTOMRIGHT: {
   172                     wl_shell_surface_resize(window_data->shell_surface, input->seat, serial,  WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT);
   173                     ret = SDL_TRUE;
   174                 }
   175                 break;
   176             case SDL_HITTEST_RESIZE_BOTTOM: {
   177                     wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, WL_SHELL_SURFACE_RESIZE_BOTTOM);
   178                     ret = SDL_TRUE;
   179                 }
   180                 break;
   181             case SDL_HITTEST_RESIZE_BOTTOMLEFT: {
   182                     wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT);
   183                     ret = SDL_TRUE;
   184                 }
   185                 break;
   186             case SDL_HITTEST_RESIZE_LEFT: {
   187                     wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, WL_SHELL_SURFACE_RESIZE_LEFT);
   188                     ret = SDL_TRUE;
   189                 }
   190                 break;
   191             default:
   192                 break;
   193         }
   194     }
   195 
   196     return ret;
   197 }
   198 
   199 static void
   200 pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
   201                       uint32_t time, uint32_t button, uint32_t state_w)
   202 {
   203     struct SDL_WaylandInput *input = data;
   204     SDL_WindowData *window = input->pointer_focus;
   205     enum wl_pointer_button_state state = state_w;
   206     uint32_t sdl_button;
   207     
   208     if  (input->pointer_focus) {
   209         switch (button) {
   210             case BTN_LEFT:
   211                 sdl_button = SDL_BUTTON_LEFT;
   212                 if (ProcessHitTest(data, serial)) {
   213                     return;  /* don't pass this event on to app. */
   214                 }
   215                 break;
   216             case BTN_MIDDLE:
   217                 sdl_button = SDL_BUTTON_MIDDLE;
   218                 break;
   219             case BTN_RIGHT:
   220                 sdl_button = SDL_BUTTON_RIGHT;
   221                 break;
   222             case BTN_SIDE:
   223                 sdl_button = SDL_BUTTON_X1;
   224                 break;
   225             case BTN_EXTRA:
   226                 sdl_button = SDL_BUTTON_X2;
   227                 break;
   228             default:
   229                 return;
   230         }
   231 
   232         SDL_SendMouseButton(window->sdlwindow, 0,
   233                             state ? SDL_PRESSED : SDL_RELEASED, sdl_button);
   234     }
   235 }
   236 
   237 static void
   238 pointer_handle_axis(void *data, struct wl_pointer *pointer,
   239                     uint32_t time, uint32_t axis, wl_fixed_t value)
   240 {
   241     struct SDL_WaylandInput *input = data;
   242     SDL_WindowData *window = input->pointer_focus;
   243     enum wl_pointer_axis a = axis;
   244     int x, y;
   245 
   246     if (input->pointer_focus) {
   247         switch (a) {
   248             case WL_POINTER_AXIS_VERTICAL_SCROLL:
   249                 x = 0;
   250                 y = wl_fixed_to_int(value);
   251                 break;
   252             case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
   253                 x = wl_fixed_to_int(value);
   254                 y = 0;
   255                 break;
   256             default:
   257                 return;
   258         }
   259 
   260         SDL_SendMouseWheel(window->sdlwindow, 0, x, y, SDL_MOUSEWHEEL_NORMAL);
   261     }
   262 }
   263 
   264 static const struct wl_pointer_listener pointer_listener = {
   265     pointer_handle_enter,
   266     pointer_handle_leave,
   267     pointer_handle_motion,
   268     pointer_handle_button,
   269     pointer_handle_axis,
   270 };
   271 
   272 static void
   273 keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
   274                        uint32_t format, int fd, uint32_t size)
   275 {
   276     struct SDL_WaylandInput *input = data;
   277     char *map_str;
   278 
   279     if (!data) {
   280         close(fd);
   281         return;
   282     }
   283 
   284     if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
   285         close(fd);
   286         return;
   287     }
   288 
   289     map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
   290     if (map_str == MAP_FAILED) {
   291         close(fd);
   292         return;
   293     }
   294 
   295     input->xkb.keymap = WAYLAND_xkb_keymap_new_from_string(input->display->xkb_context,
   296                                                 map_str,
   297                                                 XKB_KEYMAP_FORMAT_TEXT_V1,
   298                                                 0);
   299     munmap(map_str, size);
   300     close(fd);
   301 
   302     if (!input->xkb.keymap) {
   303         fprintf(stderr, "failed to compile keymap\n");
   304         return;
   305     }
   306 
   307     input->xkb.state = WAYLAND_xkb_state_new(input->xkb.keymap);
   308     if (!input->xkb.state) {
   309         fprintf(stderr, "failed to create XKB state\n");
   310         WAYLAND_xkb_keymap_unref(input->xkb.keymap);
   311         input->xkb.keymap = NULL;
   312         return;
   313     }
   314 }
   315 
   316 static void
   317 keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
   318                       uint32_t serial, struct wl_surface *surface,
   319                       struct wl_array *keys)
   320 {
   321     struct SDL_WaylandInput *input = data;
   322     SDL_WindowData *window = wl_surface_get_user_data(surface);
   323 
   324     input->keyboard_focus = window;
   325     window->keyboard_device = input;
   326     if (window) {
   327         SDL_SetKeyboardFocus(window->sdlwindow);
   328     }
   329 }
   330 
   331 static void
   332 keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
   333                       uint32_t serial, struct wl_surface *surface)
   334 {
   335     SDL_SetKeyboardFocus(NULL);
   336 }
   337 
   338 static void
   339 keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
   340                     uint32_t serial, uint32_t time, uint32_t key,
   341                     uint32_t state_w)
   342 {
   343     struct SDL_WaylandInput *input = data;
   344     SDL_WindowData *window = input->keyboard_focus;
   345     enum wl_keyboard_key_state state = state_w;
   346     const xkb_keysym_t *syms;
   347     uint32_t scancode;
   348     char text[8];
   349     int size;
   350 
   351     if (key < SDL_arraysize(xfree86_scancode_table2)) {
   352         scancode = xfree86_scancode_table2[key];
   353 
   354         // TODO when do we get WL_KEYBOARD_KEY_STATE_REPEAT?
   355         if (scancode != SDL_SCANCODE_UNKNOWN)
   356             SDL_SendKeyboardKey(state == WL_KEYBOARD_KEY_STATE_PRESSED ?
   357                                 SDL_PRESSED : SDL_RELEASED, scancode);
   358     }
   359 
   360     if (!window || window->keyboard_device != input || !input->xkb.state)
   361         return;
   362 
   363     // TODO can this happen?
   364     if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1)
   365         return;
   366 
   367     if (state) {
   368         size = WAYLAND_xkb_keysym_to_utf8(syms[0], text, sizeof text);
   369 
   370         if (size > 0) {
   371             text[size] = 0;
   372             SDL_SendKeyboardText(text);
   373         }
   374     }
   375 }
   376 
   377 static void
   378 keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
   379                           uint32_t serial, uint32_t mods_depressed,
   380                           uint32_t mods_latched, uint32_t mods_locked,
   381                           uint32_t group)
   382 {
   383     struct SDL_WaylandInput *input = data;
   384 
   385     WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
   386                           mods_locked, 0, 0, group);
   387 }
   388 
   389 static const struct wl_keyboard_listener keyboard_listener = {
   390     keyboard_handle_keymap,
   391     keyboard_handle_enter,
   392     keyboard_handle_leave,
   393     keyboard_handle_key,
   394     keyboard_handle_modifiers,
   395 };
   396 
   397 static void
   398 seat_handle_capabilities(void *data, struct wl_seat *seat,
   399                          enum wl_seat_capability caps)
   400 {
   401     struct SDL_WaylandInput *input = data;
   402 
   403     if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
   404         input->pointer = wl_seat_get_pointer(seat);
   405         input->display->pointer = input->pointer;
   406         wl_pointer_set_user_data(input->pointer, input);
   407         wl_pointer_add_listener(input->pointer, &pointer_listener,
   408                                 input);
   409     } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
   410         wl_pointer_destroy(input->pointer);
   411         input->pointer = NULL;
   412     }
   413 
   414     if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
   415         input->keyboard = wl_seat_get_keyboard(seat);
   416         wl_keyboard_set_user_data(input->keyboard, input);
   417         wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
   418                                  input);
   419     } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
   420         wl_keyboard_destroy(input->keyboard);
   421         input->keyboard = NULL;
   422     }
   423 }
   424 
   425 static const struct wl_seat_listener seat_listener = {
   426     seat_handle_capabilities,
   427 };
   428 
   429 void
   430 Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
   431 {
   432     struct SDL_WaylandInput *input;
   433 
   434     input = SDL_calloc(1, sizeof *input);
   435     if (input == NULL)
   436         return;
   437 
   438     input->display = d;
   439     input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 1);
   440     input->sx_w = wl_fixed_from_int(0);
   441     input->sy_w = wl_fixed_from_int(0);
   442     d->input = input;
   443 
   444     wl_seat_add_listener(input->seat, &seat_listener, input);
   445     wl_seat_set_user_data(input->seat, input);
   446 
   447     WAYLAND_wl_display_flush(d->display);
   448 }
   449 
   450 void Wayland_display_destroy_input(SDL_VideoData *d)
   451 {
   452     struct SDL_WaylandInput *input = d->input;
   453 
   454     if (!input)
   455         return;
   456 
   457     if (input->keyboard)
   458         wl_keyboard_destroy(input->keyboard);
   459 
   460     if (input->pointer)
   461         wl_pointer_destroy(input->pointer);
   462 
   463     if (input->seat)
   464         wl_seat_destroy(input->seat);
   465 
   466     if (input->xkb.state)
   467         WAYLAND_xkb_state_unref(input->xkb.state);
   468 
   469     if (input->xkb.keymap)
   470         WAYLAND_xkb_keymap_unref(input->xkb.keymap);
   471 
   472     SDL_free(input);
   473     d->input = NULL;
   474 }
   475 
   476 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
   477 
   478 /* vi: set ts=4 sw=4 expandtab: */