src/video/wayland/SDL_waylandevents.c
author Gabriel Jacobo
Tue, 28 Jan 2014 11:39:37 -0300
changeset 8135 dd4729596096
parent 8116 f7c2f71251e5
child 8149 681eb46b8ac4
permissions -rw-r--r--
[Wayland] Fixes segfault when mouse enters window
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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     struct {
    56         struct xkb_keymap *keymap;
    57         struct xkb_state *state;
    58     } xkb;
    59 };
    60 
    61 void
    62 Wayland_PumpEvents(_THIS)
    63 {
    64     SDL_VideoData *d = _this->driverdata;
    65     struct pollfd pfd[1];
    66 
    67     pfd[0].fd = WAYLAND_wl_display_get_fd(d->display);
    68     pfd[0].events = POLLIN;
    69     poll(pfd, 1, 0);
    70 
    71     if (pfd[0].revents & POLLIN)
    72         WAYLAND_wl_display_dispatch(d->display);
    73     else
    74         WAYLAND_wl_display_dispatch_pending(d->display);
    75 }
    76 
    77 static void
    78 pointer_handle_enter(void *data, struct wl_pointer *pointer,
    79                      uint32_t serial, struct wl_surface *surface,
    80                      wl_fixed_t sx_w, wl_fixed_t sy_w)
    81 {
    82     struct SDL_WaylandInput *input = data;
    83     SDL_WindowData *window;
    84 
    85     if (!surface) {
    86         /* enter event for a window we've just destroyed */
    87         return;
    88     }
    89     
    90     /* This handler will be called twice in Wayland 1.4
    91      * Once for the window surface which has valid user data
    92      * and again for the mouse cursor surface which does not have valid user data
    93      * We ignore the later
    94      */
    95 
    96     window = (SDL_WindowData *)wl_surface_get_user_data(surface);
    97     
    98     if (window) {
    99         input->pointer_focus = window;
   100         SDL_SetMouseFocus(window->sdlwindow);
   101     }
   102 }
   103 
   104 static void
   105 pointer_handle_leave(void *data, struct wl_pointer *pointer,
   106                      uint32_t serial, struct wl_surface *surface)
   107 {
   108     struct SDL_WaylandInput *input = data;
   109 
   110     if (input->pointer_focus) {
   111         SDL_SetMouseFocus(NULL);
   112         input->pointer_focus = NULL;
   113     }
   114 }
   115 
   116 static void
   117 pointer_handle_motion(void *data, struct wl_pointer *pointer,
   118                       uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
   119 {
   120     struct SDL_WaylandInput *input = data;
   121     SDL_WindowData *window = input->pointer_focus;
   122     int sx = wl_fixed_to_int(sx_w);
   123     int sy = wl_fixed_to_int(sy_w);
   124     if (input->pointer_focus) {
   125         SDL_SendMouseMotion(window->sdlwindow, 0, 0, sx, sy);
   126     }
   127 }
   128 
   129 static void
   130 pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
   131                       uint32_t time, uint32_t button, uint32_t state_w)
   132 {
   133     struct SDL_WaylandInput *input = data;
   134     SDL_WindowData *window = input->pointer_focus;
   135     enum wl_pointer_button_state state = state_w;
   136     uint32_t sdl_button;
   137     
   138     if  (input->pointer_focus) {
   139         switch (button) {
   140             case BTN_LEFT:
   141                 sdl_button = SDL_BUTTON_LEFT;
   142                 break;
   143             case BTN_MIDDLE:
   144                 sdl_button = SDL_BUTTON_MIDDLE;
   145                 break;
   146             case BTN_RIGHT:
   147                 sdl_button = SDL_BUTTON_RIGHT;
   148                 break;
   149             case BTN_SIDE:
   150                 sdl_button = SDL_BUTTON_X1;
   151                 break;
   152             case BTN_EXTRA:
   153                 sdl_button = SDL_BUTTON_X2;
   154                 break;
   155             default:
   156                 return;
   157         }
   158 
   159         SDL_SendMouseButton(window->sdlwindow, 0,
   160                             state ? SDL_PRESSED : SDL_RELEASED, sdl_button);
   161     }
   162 }
   163 
   164 static void
   165 pointer_handle_axis(void *data, struct wl_pointer *pointer,
   166                     uint32_t time, uint32_t axis, wl_fixed_t value)
   167 {
   168     struct SDL_WaylandInput *input = data;
   169     SDL_WindowData *window = input->pointer_focus;
   170     enum wl_pointer_axis a = axis;
   171     int x, y;
   172 
   173     if (input->pointer_focus) {
   174         switch (a) {
   175             case WL_POINTER_AXIS_VERTICAL_SCROLL:
   176                 x = 0;
   177                 y = wl_fixed_to_int(value);
   178                 break;
   179             case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
   180                 x = wl_fixed_to_int(value);
   181                 y = 0;
   182                 break;
   183             default:
   184                 return;
   185         }
   186 
   187         SDL_SendMouseWheel(window->sdlwindow, 0, x, y);
   188     }
   189 }
   190 
   191 static const struct wl_pointer_listener pointer_listener = {
   192     pointer_handle_enter,
   193     pointer_handle_leave,
   194     pointer_handle_motion,
   195     pointer_handle_button,
   196     pointer_handle_axis,
   197 };
   198 
   199 static void
   200 keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
   201                        uint32_t format, int fd, uint32_t size)
   202 {
   203     struct SDL_WaylandInput *input = data;
   204     char *map_str;
   205 
   206     if (!data) {
   207         close(fd);
   208         return;
   209     }
   210 
   211     if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
   212         close(fd);
   213         return;
   214     }
   215 
   216     map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
   217     if (map_str == MAP_FAILED) {
   218         close(fd);
   219         return;
   220     }
   221 
   222     input->xkb.keymap = WAYLAND_xkb_keymap_new_from_string(input->display->xkb_context,
   223                                                 map_str,
   224                                                 XKB_KEYMAP_FORMAT_TEXT_V1,
   225                                                 0);
   226     munmap(map_str, size);
   227     close(fd);
   228 
   229     if (!input->xkb.keymap) {
   230         fprintf(stderr, "failed to compile keymap\n");
   231         return;
   232     }
   233 
   234     input->xkb.state = WAYLAND_xkb_state_new(input->xkb.keymap);
   235     if (!input->xkb.state) {
   236         fprintf(stderr, "failed to create XKB state\n");
   237         WAYLAND_xkb_keymap_unref(input->xkb.keymap);
   238         input->xkb.keymap = NULL;
   239         return;
   240     }
   241 }
   242 
   243 static void
   244 keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
   245                       uint32_t serial, struct wl_surface *surface,
   246                       struct wl_array *keys)
   247 {
   248     struct SDL_WaylandInput *input = data;
   249     SDL_WindowData *window = wl_surface_get_user_data(surface);
   250 
   251     input->keyboard_focus = window;
   252     window->keyboard_device = input;
   253     if (window) {
   254         SDL_SetKeyboardFocus(window->sdlwindow);
   255     }
   256 }
   257 
   258 static void
   259 keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
   260                       uint32_t serial, struct wl_surface *surface)
   261 {
   262     SDL_SetKeyboardFocus(NULL);
   263 }
   264 
   265 static void
   266 keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
   267                     uint32_t serial, uint32_t time, uint32_t key,
   268                     uint32_t state_w)
   269 {
   270     struct SDL_WaylandInput *input = data;
   271     SDL_WindowData *window = input->keyboard_focus;
   272     enum wl_keyboard_key_state state = state_w;
   273     const xkb_keysym_t *syms;
   274     uint32_t scancode;
   275     char text[8];
   276     int size;
   277 
   278     if (key < SDL_arraysize(xfree86_scancode_table2)) {
   279         scancode = xfree86_scancode_table2[key];
   280 
   281         // TODO when do we get WL_KEYBOARD_KEY_STATE_REPEAT?
   282         if (scancode != SDL_SCANCODE_UNKNOWN)
   283             SDL_SendKeyboardKey(state == WL_KEYBOARD_KEY_STATE_PRESSED ?
   284                                 SDL_PRESSED : SDL_RELEASED, scancode);
   285     }
   286 
   287     if (!window || window->keyboard_device != input || !input->xkb.state)
   288         return;
   289 
   290     // TODO can this happen?
   291     if (WAYLAND_xkb_state_key_get_syms(input->xkb.state, key + 8, &syms) != 1)
   292         return;
   293 
   294     if (state) {
   295         size = WAYLAND_xkb_keysym_to_utf8(syms[0], text, sizeof text);
   296 
   297         if (size > 0) {
   298             text[size] = 0;
   299             SDL_SendKeyboardText(text);
   300         }
   301     }
   302 }
   303 
   304 static void
   305 keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
   306                           uint32_t serial, uint32_t mods_depressed,
   307                           uint32_t mods_latched, uint32_t mods_locked,
   308                           uint32_t group)
   309 {
   310     struct SDL_WaylandInput *input = data;
   311 
   312     WAYLAND_xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched,
   313                           mods_locked, 0, 0, group);
   314 }
   315 
   316 static const struct wl_keyboard_listener keyboard_listener = {
   317     keyboard_handle_keymap,
   318     keyboard_handle_enter,
   319     keyboard_handle_leave,
   320     keyboard_handle_key,
   321     keyboard_handle_modifiers,
   322 };
   323 
   324 static void
   325 seat_handle_capabilities(void *data, struct wl_seat *seat,
   326                          enum wl_seat_capability caps)
   327 {
   328     struct SDL_WaylandInput *input = data;
   329 
   330     if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
   331         input->pointer = wl_seat_get_pointer(seat);
   332         input->display->pointer = input->pointer;
   333         wl_pointer_set_user_data(input->pointer, input);
   334         wl_pointer_add_listener(input->pointer, &pointer_listener,
   335                                 input);
   336     } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
   337         wl_pointer_destroy(input->pointer);
   338         input->pointer = NULL;
   339     }
   340 
   341     if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
   342         input->keyboard = wl_seat_get_keyboard(seat);
   343         wl_keyboard_set_user_data(input->keyboard, input);
   344         wl_keyboard_add_listener(input->keyboard, &keyboard_listener,
   345                                  input);
   346     } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
   347         wl_keyboard_destroy(input->keyboard);
   348         input->keyboard = NULL;
   349     }
   350 }
   351 
   352 static const struct wl_seat_listener seat_listener = {
   353     seat_handle_capabilities,
   354 };
   355 
   356 void
   357 Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
   358 {
   359     struct SDL_WaylandInput *input;
   360 
   361     input = SDL_calloc(1, sizeof *input);
   362     if (input == NULL)
   363         return;
   364 
   365     input->display = d;
   366     input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 1);
   367 
   368     d->input = input;
   369 
   370     wl_seat_add_listener(input->seat, &seat_listener, input);
   371     wl_seat_set_user_data(input->seat, input);
   372 
   373     WAYLAND_wl_display_flush(d->display);
   374 }
   375 
   376 void Wayland_display_destroy_input(SDL_VideoData *d)
   377 {
   378     struct SDL_WaylandInput *input = d->input;
   379 
   380     if (!input)
   381         return;
   382 
   383     if (input->keyboard)
   384         wl_keyboard_destroy(input->keyboard);
   385 
   386     if (input->pointer)
   387         wl_pointer_destroy(input->pointer);
   388 
   389     if (input->seat)
   390         wl_seat_destroy(input->seat);
   391 
   392     if (input->xkb.state)
   393         WAYLAND_xkb_state_unref(input->xkb.state);
   394 
   395     if (input->xkb.keymap)
   396         WAYLAND_xkb_keymap_unref(input->xkb.keymap);
   397 
   398     SDL_free(input);
   399     d->input = NULL;
   400 }
   401 
   402 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
   403 
   404 /* vi: set ts=4 sw=4 expandtab: */