src/video/wayland/SDL_waylandevents.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sun, 30 Oct 2016 21:01:33 +0100
changeset 10563 e3d84016cb79
parent 10304 ee83e0b4a36f
child 10583 974f8ebcb819
permissions -rw-r--r--
Fixed outdated info in README.
     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 #include "SDL_log.h"
    29 
    30 #include "../../events/SDL_sysevents.h"
    31 #include "../../events/SDL_events_c.h"
    32 #include "../../events/scancodes_xfree86.h"
    33 
    34 #include "SDL_waylandvideo.h"
    35 #include "SDL_waylandevents_c.h"
    36 #include "SDL_waylandwindow.h"
    37 
    38 #include "SDL_waylanddyn.h"
    39 
    40 #include "pointer-constraints-unstable-v1-client-protocol.h"
    41 #include "relative-pointer-unstable-v1-client-protocol.h"
    42 
    43 #include <linux/input.h>
    44 #include <sys/select.h>
    45 #include <sys/mman.h>
    46 #include <poll.h>
    47 #include <unistd.h>
    48 #include <xkbcommon/xkbcommon.h>
    49 
    50 struct SDL_WaylandInput {
    51     SDL_VideoData *display;
    52     struct wl_seat *seat;
    53     struct wl_pointer *pointer;
    54     struct wl_keyboard *keyboard;
    55     struct zwp_relative_pointer_v1 *relative_pointer;
    56     SDL_WindowData *pointer_focus;
    57     SDL_WindowData *keyboard_focus;
    58 
    59     /* Last motion location */
    60     wl_fixed_t sx_w;
    61     wl_fixed_t sy_w;
    62 
    63     double dx_frac;
    64     double dy_frac;
    65 
    66     struct {
    67         struct xkb_keymap *keymap;
    68         struct xkb_state *state;
    69     } xkb;
    70 };
    71 
    72 void
    73 Wayland_PumpEvents(_THIS)
    74 {
    75     SDL_VideoData *d = _this->driverdata;
    76     struct pollfd pfd[1];
    77 
    78     pfd[0].fd = WAYLAND_wl_display_get_fd(d->display);
    79     pfd[0].events = POLLIN;
    80     poll(pfd, 1, 0);
    81 
    82     if (pfd[0].revents & POLLIN)
    83         WAYLAND_wl_display_dispatch(d->display);
    84     else
    85         WAYLAND_wl_display_dispatch_pending(d->display);
    86 }
    87 
    88 static void
    89 pointer_handle_enter(void *data, struct wl_pointer *pointer,
    90                      uint32_t serial, struct wl_surface *surface,
    91                      wl_fixed_t sx_w, wl_fixed_t sy_w)
    92 {
    93     struct SDL_WaylandInput *input = data;
    94     SDL_WindowData *window;
    95 
    96     if (!surface) {
    97         /* enter event for a window we've just destroyed */
    98         return;
    99     }
   100     
   101     /* This handler will be called twice in Wayland 1.4
   102      * Once for the window surface which has valid user data
   103      * and again for the mouse cursor surface which does not have valid user data
   104      * We ignore the later
   105      */
   106 
   107     window = (SDL_WindowData *)wl_surface_get_user_data(surface);
   108     
   109     if (window) {
   110         input->pointer_focus = window;
   111         SDL_SetMouseFocus(window->sdlwindow);
   112     }
   113 }
   114 
   115 static void
   116 pointer_handle_leave(void *data, struct wl_pointer *pointer,
   117                      uint32_t serial, struct wl_surface *surface)
   118 {
   119     struct SDL_WaylandInput *input = data;
   120 
   121     if (input->pointer_focus) {
   122         SDL_SetMouseFocus(NULL);
   123         input->pointer_focus = NULL;
   124     }
   125 }
   126 
   127 static void
   128 pointer_handle_motion(void *data, struct wl_pointer *pointer,
   129                       uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
   130 {
   131     struct SDL_WaylandInput *input = data;
   132     SDL_WindowData *window = input->pointer_focus;
   133     input->sx_w = sx_w;
   134     input->sy_w = sy_w;
   135     if (input->pointer_focus) {
   136         const int sx = wl_fixed_to_int(sx_w);
   137         const int sy = wl_fixed_to_int(sy_w);
   138         SDL_SendMouseMotion(window->sdlwindow, 0, 0, sx, sy);
   139     }
   140 }
   141 
   142 static SDL_bool
   143 ProcessHitTest(struct SDL_WaylandInput *input, uint32_t serial)
   144 {
   145     SDL_WindowData *window_data = input->pointer_focus;
   146     SDL_Window *window = window_data->sdlwindow;
   147 
   148     if (window->hit_test) {
   149         const SDL_Point point = { wl_fixed_to_int(input->sx_w), wl_fixed_to_int(input->sy_w) };
   150         const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
   151         static const uint32_t directions[] = {
   152             WL_SHELL_SURFACE_RESIZE_TOP_LEFT, WL_SHELL_SURFACE_RESIZE_TOP,
   153             WL_SHELL_SURFACE_RESIZE_TOP_RIGHT, WL_SHELL_SURFACE_RESIZE_RIGHT,
   154             WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT, WL_SHELL_SURFACE_RESIZE_BOTTOM,
   155             WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT, WL_SHELL_SURFACE_RESIZE_LEFT
   156         };
   157         switch (rc) {
   158             case SDL_HITTEST_DRAGGABLE:
   159                 wl_shell_surface_move(window_data->shell_surface, input->seat, serial);
   160                 return SDL_TRUE;
   161 
   162             case SDL_HITTEST_RESIZE_TOPLEFT:
   163             case SDL_HITTEST_RESIZE_TOP:
   164             case SDL_HITTEST_RESIZE_TOPRIGHT:
   165             case SDL_HITTEST_RESIZE_RIGHT:
   166             case SDL_HITTEST_RESIZE_BOTTOMRIGHT:
   167             case SDL_HITTEST_RESIZE_BOTTOM:
   168             case SDL_HITTEST_RESIZE_BOTTOMLEFT:
   169             case SDL_HITTEST_RESIZE_LEFT:
   170                 wl_shell_surface_resize(window_data->shell_surface, input->seat, serial, directions[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
   171                 return SDL_TRUE;
   172 
   173             default: return SDL_FALSE;
   174         }
   175     }
   176 
   177     return SDL_FALSE;
   178 }
   179 
   180 static void
   181 pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial,
   182                              uint32_t time, uint32_t button, uint32_t state_w)
   183 {
   184     SDL_WindowData *window = input->pointer_focus;
   185     enum wl_pointer_button_state state = state_w;
   186     uint32_t sdl_button;
   187     
   188     if  (input->pointer_focus) {
   189         switch (button) {
   190             case BTN_LEFT:
   191                 sdl_button = SDL_BUTTON_LEFT;
   192                 if (ProcessHitTest(input, serial)) {
   193                     return;  /* don't pass this event on to app. */
   194                 }
   195                 break;
   196             case BTN_MIDDLE:
   197                 sdl_button = SDL_BUTTON_MIDDLE;
   198                 break;
   199             case BTN_RIGHT:
   200                 sdl_button = SDL_BUTTON_RIGHT;
   201                 break;
   202             case BTN_SIDE:
   203                 sdl_button = SDL_BUTTON_X1;
   204                 break;
   205             case BTN_EXTRA:
   206                 sdl_button = SDL_BUTTON_X2;
   207                 break;
   208             default:
   209                 return;
   210         }
   211 
   212         SDL_SendMouseButton(window->sdlwindow, 0,
   213                             state ? SDL_PRESSED : SDL_RELEASED, sdl_button);
   214     }
   215 }
   216 
   217 static void
   218 pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
   219                       uint32_t time, uint32_t button, uint32_t state_w)
   220 {
   221     struct SDL_WaylandInput *input = data;
   222 
   223     pointer_handle_button_common(input, serial, time, button, state_w);
   224 }
   225 
   226 static void
   227 pointer_handle_axis_common(struct SDL_WaylandInput *input,
   228                            uint32_t time, uint32_t axis, wl_fixed_t value)
   229 {
   230     SDL_WindowData *window = input->pointer_focus;
   231     enum wl_pointer_axis a = axis;
   232     int x, y;
   233 
   234     if (input->pointer_focus) {
   235         switch (a) {
   236             case WL_POINTER_AXIS_VERTICAL_SCROLL:
   237                 x = 0;
   238                 y = wl_fixed_to_int(value);
   239                 break;
   240             case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
   241                 x = wl_fixed_to_int(value);
   242                 y = 0;
   243                 break;
   244             default:
   245                 return;
   246         }
   247 
   248         SDL_SendMouseWheel(window->sdlwindow, 0, x, y, SDL_MOUSEWHEEL_NORMAL);
   249     }
   250 }
   251 
   252 static void
   253 pointer_handle_axis(void *data, struct wl_pointer *pointer,
   254                     uint32_t time, uint32_t axis, wl_fixed_t value)
   255 {
   256     struct SDL_WaylandInput *input = data;
   257 
   258     pointer_handle_axis_common(input, time, axis, value);
   259 }
   260 
   261 static const struct wl_pointer_listener pointer_listener = {
   262     pointer_handle_enter,
   263     pointer_handle_leave,
   264     pointer_handle_motion,
   265     pointer_handle_button,
   266     pointer_handle_axis,
   267 };
   268 
   269 static void
   270 keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
   271                        uint32_t format, int fd, uint32_t size)
   272 {
   273     struct SDL_WaylandInput *input = data;
   274     char *map_str;
   275 
   276     if (!data) {
   277         close(fd);
   278         return;
   279     }
   280 
   281     if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
   282         close(fd);
   283         return;
   284     }
   285 
   286     map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
   287     if (map_str == MAP_FAILED) {
   288         close(fd);
   289         return;
   290     }
   291 
   292     input->xkb.keymap = WAYLAND_xkb_keymap_new_from_string(input->display->xkb_context,
   293                                                 map_str,
   294                                                 XKB_KEYMAP_FORMAT_TEXT_V1,
   295                                                 0);
   296     munmap(map_str, size);
   297     close(fd);
   298 
   299     if (!input->xkb.keymap) {
   300         fprintf(stderr, "failed to compile keymap\n");
   301         return;
   302     }
   303 
   304     input->xkb.state = WAYLAND_xkb_state_new(input->xkb.keymap);
   305     if (!input->xkb.state) {
   306         fprintf(stderr, "failed to create XKB state\n");
   307         WAYLAND_xkb_keymap_unref(input->xkb.keymap);
   308         input->xkb.keymap = NULL;
   309         return;
   310     }
   311 }
   312 
   313 static void
   314 keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
   315                       uint32_t serial, struct wl_surface *surface,
   316                       struct wl_array *keys)
   317 {
   318     struct SDL_WaylandInput *input = data;
   319     SDL_WindowData *window;
   320 
   321     if (!surface) {
   322         /* enter event for a window we've just destroyed */
   323         return;
   324     }
   325  
   326     window = wl_surface_get_user_data(surface);
   327 
   328     if (window) {
   329         input->keyboard_focus = window;
   330         window->keyboard_device = input;
   331         SDL_SetKeyboardFocus(window->sdlwindow);
   332     }
   333 }
   334 
   335 static void
   336 keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
   337                       uint32_t serial, struct wl_surface *surface)
   338 {
   339     SDL_SetKeyboardFocus(NULL);
   340 }
   341 
   342 static void
   343 keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
   344                     uint32_t serial, uint32_t time, uint32_t key,
   345                     uint32_t state_w)
   346 {
   347     struct SDL_WaylandInput *input = data;
   348     SDL_WindowData *window = input->keyboard_focus;
   349     enum wl_keyboard_key_state state = state_w;
   350     const xkb_keysym_t *syms;
   351     uint32_t scancode;
   352     char text[8];
   353     int size;
   354 
   355     if (key < SDL_arraysize(xfree86_scancode_table2)) {
   356         scancode = xfree86_scancode_table2[key];
   357 
   358         // TODO when do we get WL_KEYBOARD_KEY_STATE_REPEAT?
   359         if (scancode != SDL_SCANCODE_UNKNOWN)
   360             SDL_SendKeyboardKey(state == WL_KEYBOARD_KEY_STATE_PRESSED ?
   361                                 SDL_PRESSED : SDL_RELEASED, scancode);
   362     }
   363 
   364     if (!window || window->keyboard_device != input || !input->xkb.state)
   365         return;
   366 
   367     // TODO can this happen?
   368     if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1)
   369         return;
   370 
   371     if (state) {
   372         size = WAYLAND_xkb_keysym_to_utf8(syms[0], text, sizeof text);
   373 
   374         if (size > 0) {
   375             text[size] = 0;
   376             SDL_SendKeyboardText(text);
   377         }
   378     }
   379 }
   380 
   381 static void
   382 keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
   383                           uint32_t serial, uint32_t mods_depressed,
   384                           uint32_t mods_latched, uint32_t mods_locked,
   385                           uint32_t group)
   386 {
   387     struct SDL_WaylandInput *input = data;
   388 
   389     WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
   390                           mods_locked, 0, 0, group);
   391 }
   392 
   393 static const struct wl_keyboard_listener keyboard_listener = {
   394     keyboard_handle_keymap,
   395     keyboard_handle_enter,
   396     keyboard_handle_leave,
   397     keyboard_handle_key,
   398     keyboard_handle_modifiers,
   399 };
   400 
   401 static void
   402 seat_handle_capabilities(void *data, struct wl_seat *seat,
   403                          enum wl_seat_capability caps)
   404 {
   405     struct SDL_WaylandInput *input = data;
   406 
   407     if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
   408         input->pointer = wl_seat_get_pointer(seat);
   409         input->display->pointer = input->pointer;
   410         wl_pointer_set_user_data(input->pointer, input);
   411         wl_pointer_add_listener(input->pointer, &pointer_listener,
   412                                 input);
   413     } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
   414         wl_pointer_destroy(input->pointer);
   415         input->pointer = NULL;
   416     }
   417 
   418     if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
   419         input->keyboard = wl_seat_get_keyboard(seat);
   420         wl_keyboard_set_user_data(input->keyboard, input);
   421         wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
   422                                  input);
   423     } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
   424         wl_keyboard_destroy(input->keyboard);
   425         input->keyboard = NULL;
   426     }
   427 }
   428 
   429 static const struct wl_seat_listener seat_listener = {
   430     seat_handle_capabilities,
   431 };
   432 
   433 void
   434 Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
   435 {
   436     struct SDL_WaylandInput *input;
   437 
   438     input = SDL_calloc(1, sizeof *input);
   439     if (input == NULL)
   440         return;
   441 
   442     input->display = d;
   443     input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 1);
   444     input->sx_w = wl_fixed_from_int(0);
   445     input->sy_w = wl_fixed_from_int(0);
   446     d->input = input;
   447 
   448     wl_seat_add_listener(input->seat, &seat_listener, input);
   449     wl_seat_set_user_data(input->seat, input);
   450 
   451     WAYLAND_wl_display_flush(d->display);
   452 }
   453 
   454 void Wayland_display_destroy_input(SDL_VideoData *d)
   455 {
   456     struct SDL_WaylandInput *input = d->input;
   457 
   458     if (!input)
   459         return;
   460 
   461     if (input->keyboard)
   462         wl_keyboard_destroy(input->keyboard);
   463 
   464     if (input->pointer)
   465         wl_pointer_destroy(input->pointer);
   466 
   467     if (input->seat)
   468         wl_seat_destroy(input->seat);
   469 
   470     if (input->xkb.state)
   471         WAYLAND_xkb_state_unref(input->xkb.state);
   472 
   473     if (input->xkb.keymap)
   474         WAYLAND_xkb_keymap_unref(input->xkb.keymap);
   475 
   476     SDL_free(input);
   477     d->input = NULL;
   478 }
   479 
   480 void Wayland_display_add_relative_pointer_manager(SDL_VideoData *d, uint32_t id)
   481 {
   482     d->relative_pointer_manager =
   483         wl_registry_bind(d->registry, id,
   484                          &zwp_relative_pointer_manager_v1_interface, 1);
   485 }
   486 
   487 void Wayland_display_destroy_relative_pointer_manager(SDL_VideoData *d)
   488 {
   489     if (d->relative_pointer_manager)
   490         zwp_relative_pointer_manager_v1_destroy(d->relative_pointer_manager);
   491 }
   492 
   493 void Wayland_display_add_pointer_constraints(SDL_VideoData *d, uint32_t id)
   494 {
   495     d->pointer_constraints =
   496         wl_registry_bind(d->registry, id,
   497                          &zwp_pointer_constraints_v1_interface, 1);
   498 }
   499 
   500 void Wayland_display_destroy_pointer_constraints(SDL_VideoData *d)
   501 {
   502     if (d->pointer_constraints)
   503         zwp_pointer_constraints_v1_destroy(d->pointer_constraints);
   504 }
   505 
   506 static void
   507 relative_pointer_handle_relative_motion(void *data,
   508                                         struct zwp_relative_pointer_v1 *pointer,
   509                                         uint32_t time_hi,
   510                                         uint32_t time_lo,
   511                                         wl_fixed_t dx_w,
   512                                         wl_fixed_t dy_w,
   513                                         wl_fixed_t dx_unaccel_w,
   514                                         wl_fixed_t dy_unaccel_w)
   515 {
   516     struct SDL_WaylandInput *input = data;
   517     SDL_VideoData *d = input->display;
   518     SDL_WindowData *window = input->pointer_focus;
   519     double dx_unaccel;
   520     double dy_unaccel;
   521     double dx;
   522     double dy;
   523 
   524     dx_unaccel = wl_fixed_to_double(dx_unaccel_w);
   525     dy_unaccel = wl_fixed_to_double(dy_unaccel_w);
   526 
   527     /* Add left over fraction from last event. */
   528     dx_unaccel += input->dx_frac;
   529     dy_unaccel += input->dy_frac;
   530 
   531     input->dx_frac = modf(dx_unaccel, &dx);
   532     input->dy_frac = modf(dy_unaccel, &dy);
   533 
   534     if (input->pointer_focus && d->relative_mouse_mode) {
   535         SDL_SendMouseMotion(window->sdlwindow, 0, 1, (int)dx, (int)dy);
   536     }
   537 }
   538 
   539 static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
   540     relative_pointer_handle_relative_motion,
   541 };
   542 
   543 static void
   544 locked_pointer_locked(void *data,
   545                       struct zwp_locked_pointer_v1 *locked_pointer)
   546 {
   547 }
   548 
   549 static void
   550 locked_pointer_unlocked(void *data,
   551                         struct zwp_locked_pointer_v1 *locked_pointer)
   552 {
   553 }
   554 
   555 static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
   556     locked_pointer_locked,
   557     locked_pointer_unlocked,
   558 };
   559 
   560 static void
   561 lock_pointer_to_window(SDL_Window *window,
   562                        struct SDL_WaylandInput *input)
   563 {
   564     SDL_WindowData *w = window->driverdata;
   565     SDL_VideoData *d = input->display;
   566     struct zwp_locked_pointer_v1 *locked_pointer;
   567 
   568     if (w->locked_pointer)
   569         return;
   570 
   571     locked_pointer =
   572         zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints,
   573                                                 w->surface,
   574                                                 input->pointer,
   575                                                 NULL,
   576                                                 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
   577     zwp_locked_pointer_v1_add_listener(locked_pointer,
   578                                        &locked_pointer_listener,
   579                                        window);
   580 
   581     w->locked_pointer = locked_pointer;
   582 }
   583 
   584 int Wayland_input_lock_pointer(struct SDL_WaylandInput *input)
   585 {
   586     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   587     SDL_VideoData *d = input->display;
   588     SDL_Window *window;
   589     struct zwp_relative_pointer_v1 *relative_pointer;
   590 
   591     if (!d->relative_pointer_manager)
   592         return -1;
   593 
   594     if (!d->pointer_constraints)
   595         return -1;
   596 
   597     if (!input->relative_pointer) {
   598         relative_pointer =
   599             zwp_relative_pointer_manager_v1_get_relative_pointer(
   600                 d->relative_pointer_manager,
   601                 input->pointer);
   602         zwp_relative_pointer_v1_add_listener(relative_pointer,
   603                                              &relative_pointer_listener,
   604                                              input);
   605         input->relative_pointer = relative_pointer;
   606     }
   607 
   608     for (window = vd->windows; window; window = window->next)
   609         lock_pointer_to_window(window, input);
   610 
   611     d->relative_mouse_mode = 1;
   612 
   613     return 0;
   614 }
   615 
   616 int Wayland_input_unlock_pointer(struct SDL_WaylandInput *input)
   617 {
   618     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   619     SDL_VideoData *d = input->display;
   620     SDL_Window *window;
   621     SDL_WindowData *w;
   622 
   623     for (window = vd->windows; window; window = window->next) {
   624         w = window->driverdata;
   625         if (w->locked_pointer)
   626             zwp_locked_pointer_v1_destroy(w->locked_pointer);
   627         w->locked_pointer = NULL;
   628     }
   629 
   630     zwp_relative_pointer_v1_destroy(input->relative_pointer);
   631     input->relative_pointer = NULL;
   632 
   633     d->relative_mouse_mode = 0;
   634 
   635     return 0;
   636 }
   637 
   638 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
   639 
   640 /* vi: set ts=4 sw=4 expandtab: */