src/video/wayland/SDL_waylandmouse.c
author Philipp Wiesemann
Wed, 16 Mar 2016 22:09:39 +0100
changeset 10120 f49e8a97c214
parent 10114 6740b15172f0
child 10130 eba8155e32b1
permissions -rw-r--r--
Wayland: Fixed crash if memory mapping failed while creating a custom cursor.
     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 #ifndef _GNU_SOURCE
    27 #define _GNU_SOURCE
    28 #endif
    29 
    30 #include <errno.h>
    31 #include <sys/types.h>
    32 #include <sys/mman.h>
    33 #include <fcntl.h>
    34 #include <unistd.h>
    35 #include <stdlib.h>
    36 #include <limits.h>
    37 
    38 #include "../SDL_sysvideo.h"
    39 
    40 #include "SDL_mouse.h"
    41 #include "../../events/SDL_mouse_c.h"
    42 #include "SDL_waylandvideo.h"
    43 #include "SDL_waylandevents_c.h"
    44 
    45 #include "SDL_waylanddyn.h"
    46 #include "wayland-cursor.h"
    47 
    48 #include "SDL_assert.h"
    49 
    50 
    51 typedef struct {
    52     struct wl_buffer   *buffer;
    53     struct wl_surface  *surface;
    54 
    55     int                hot_x, hot_y;
    56     int                w, h;
    57 
    58     /* Either a preloaded cursor, or one we created ourselves */
    59     struct wl_cursor   *cursor;
    60     void               *shm_data;
    61 } Wayland_CursorData;
    62 
    63 static int
    64 wayland_create_tmp_file(off_t size)
    65 {
    66     static const char template[] = "/sdl-shared-XXXXXX";
    67     char *xdg_path;
    68     char tmp_path[PATH_MAX];
    69     int fd;
    70 
    71     xdg_path = SDL_getenv("XDG_RUNTIME_DIR");
    72     if (!xdg_path) {
    73         errno = ENOENT;
    74         return -1;
    75     }
    76 
    77     SDL_strlcpy(tmp_path, xdg_path, PATH_MAX);
    78     SDL_strlcat(tmp_path, template, PATH_MAX);
    79 
    80     fd = mkostemp(tmp_path, O_CLOEXEC);
    81     if (fd < 0)
    82         return -1;
    83 
    84     if (ftruncate(fd, size) < 0) {
    85         close(fd);
    86         return -1;
    87     }
    88 
    89     return fd;
    90 }
    91 
    92 static void
    93 mouse_buffer_release(void *data, struct wl_buffer *buffer)
    94 {
    95 }
    96 
    97 static const struct wl_buffer_listener mouse_buffer_listener = {
    98     mouse_buffer_release
    99 };
   100 
   101 static int
   102 create_buffer_from_shm(Wayland_CursorData *d,
   103                        int width,
   104                        int height,
   105                        uint32_t format)
   106 {
   107     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   108     SDL_VideoData *data = (SDL_VideoData *) vd->driverdata;
   109     struct wl_shm_pool *shm_pool;
   110 
   111     int stride = width * 4;
   112     int size = stride * height;
   113 
   114     int shm_fd;
   115 
   116     shm_fd = wayland_create_tmp_file(size);
   117     if (shm_fd < 0)
   118     {
   119         fprintf(stderr, "creating mouse cursor buffer failed!\n");
   120         return -1;
   121     }
   122 
   123     d->shm_data = mmap(NULL,
   124                        size,
   125                        PROT_READ | PROT_WRITE,
   126                        MAP_SHARED,
   127                        shm_fd,
   128                        0);
   129     if (d->shm_data == MAP_FAILED) {
   130         d->shm_data = NULL;
   131         fprintf (stderr, "mmap () failed\n");
   132         close (shm_fd);
   133         return -1;
   134     }
   135 
   136     shm_pool = wl_shm_create_pool(data->shm, shm_fd, size);
   137     d->buffer = wl_shm_pool_create_buffer(shm_pool,
   138                                           0,
   139                                           width,
   140                                           height,
   141                                           stride,
   142                                           format);
   143     wl_buffer_add_listener(d->buffer,
   144                            &mouse_buffer_listener,
   145                            d);
   146 
   147     wl_shm_pool_destroy (shm_pool);
   148     close (shm_fd);
   149 
   150     return 0;
   151 }
   152 
   153 static SDL_Cursor *
   154 Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
   155 {
   156     SDL_Cursor *cursor;
   157 
   158     cursor = calloc(1, sizeof (*cursor));
   159     if (cursor) {
   160         SDL_VideoDevice *vd = SDL_GetVideoDevice ();
   161         SDL_VideoData *wd = (SDL_VideoData *) vd->driverdata;
   162         Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
   163         if (!data) {
   164             SDL_OutOfMemory();
   165             free(cursor);
   166             return NULL;
   167         }
   168         cursor->driverdata = (void *) data;
   169 
   170         /* Assume ARGB8888 */
   171         SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
   172         SDL_assert(surface->pitch == surface->w * 4);
   173 
   174         /* Allocate shared memory buffer for this cursor */
   175         if (create_buffer_from_shm (data,
   176                                     surface->w,
   177                                     surface->h,
   178                                     WL_SHM_FORMAT_ARGB8888) < 0)
   179         {
   180             free (cursor->driverdata);
   181             free (cursor);
   182             return NULL;
   183         }
   184 
   185         SDL_memcpy(data->shm_data,
   186                    surface->pixels,
   187                    surface->h * surface->pitch);
   188 
   189         data->surface = wl_compositor_create_surface(wd->compositor);
   190         wl_surface_set_user_data(data->surface, NULL);
   191 
   192         data->hot_x = hot_x;
   193         data->hot_y = hot_y;
   194         data->w = surface->w;
   195         data->h = surface->h;
   196     } else {
   197         SDL_OutOfMemory();
   198     }
   199 
   200     return cursor;
   201 }
   202 
   203 static SDL_Cursor *
   204 CreateCursorFromWlCursor(SDL_VideoData *d, struct wl_cursor *wlcursor)
   205 {
   206     SDL_Cursor *cursor;
   207 
   208     cursor = calloc(1, sizeof (*cursor));
   209     if (cursor) {
   210         Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
   211         if (!data) {
   212             SDL_OutOfMemory();
   213             free(cursor);
   214             return NULL;
   215         }
   216         cursor->driverdata = (void *) data;
   217 
   218         data->buffer = WAYLAND_wl_cursor_image_get_buffer(wlcursor->images[0]);
   219         data->surface = wl_compositor_create_surface(d->compositor);
   220         wl_surface_set_user_data(data->surface, NULL);
   221         data->hot_x = wlcursor->images[0]->hotspot_x;
   222         data->hot_y = wlcursor->images[0]->hotspot_y;
   223         data->w = wlcursor->images[0]->width;
   224         data->h = wlcursor->images[0]->height;
   225         data->cursor= wlcursor;
   226     } else {
   227         SDL_OutOfMemory ();
   228     }
   229 
   230     return cursor;
   231 }
   232 
   233 static SDL_Cursor *
   234 Wayland_CreateDefaultCursor()
   235 {
   236     SDL_VideoDevice *device = SDL_GetVideoDevice();
   237     SDL_VideoData *data = device->driverdata;
   238 
   239     return CreateCursorFromWlCursor (data,
   240                                      WAYLAND_wl_cursor_theme_get_cursor(data->cursor_theme,
   241                                                                 "left_ptr"));
   242 }
   243 
   244 static SDL_Cursor *
   245 Wayland_CreateSystemCursor(SDL_SystemCursor id)
   246 {
   247     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   248     SDL_VideoData *d = vd->driverdata;
   249 
   250     struct wl_cursor *cursor = NULL;
   251 
   252     switch(id)
   253     {
   254     default:
   255         SDL_assert(0);
   256         return NULL;
   257     case SDL_SYSTEM_CURSOR_ARROW:
   258         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
   259         break;
   260     case SDL_SYSTEM_CURSOR_IBEAM:
   261         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   262         break;
   263     case SDL_SYSTEM_CURSOR_WAIT:
   264         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
   265         break;
   266     case SDL_SYSTEM_CURSOR_CROSSHAIR:
   267         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   268         break;
   269     case SDL_SYSTEM_CURSOR_WAITARROW:
   270         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
   271         break;
   272     case SDL_SYSTEM_CURSOR_SIZENWSE:
   273         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   274         break;
   275     case SDL_SYSTEM_CURSOR_SIZENESW:
   276         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   277         break;
   278     case SDL_SYSTEM_CURSOR_SIZEWE:
   279         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   280         break;
   281     case SDL_SYSTEM_CURSOR_SIZENS:
   282         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   283         break;
   284     case SDL_SYSTEM_CURSOR_SIZEALL:
   285         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   286         break;
   287     case SDL_SYSTEM_CURSOR_NO:
   288         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   289         break;
   290     case SDL_SYSTEM_CURSOR_HAND:
   291         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   292         break;
   293     }
   294 
   295     return CreateCursorFromWlCursor(d, cursor);
   296 }
   297 
   298 static void
   299 Wayland_FreeCursor(SDL_Cursor *cursor)
   300 {
   301     Wayland_CursorData *d;
   302 
   303     if (!cursor)
   304         return;
   305 
   306     d = cursor->driverdata;
   307 
   308     /* Probably not a cursor we own */
   309     if (!d)
   310         return;
   311 
   312     if (d->buffer && !d->cursor)
   313         wl_buffer_destroy(d->buffer);
   314 
   315     if (d->surface)
   316         wl_surface_destroy(d->surface);
   317 
   318     /* Not sure what's meant to happen to shm_data */
   319     free (cursor->driverdata);
   320     SDL_free(cursor);
   321 }
   322 
   323 static int
   324 Wayland_ShowCursor(SDL_Cursor *cursor)
   325 {
   326     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   327     SDL_VideoData *d = vd->driverdata;
   328 
   329     struct wl_pointer *pointer = d->pointer;
   330 
   331     if (!pointer)
   332         return -1;
   333 
   334     if (cursor)
   335     {
   336         Wayland_CursorData *data = cursor->driverdata;
   337 
   338         wl_pointer_set_cursor (pointer, 0,
   339                                data->surface,
   340                                data->hot_x,
   341                                data->hot_y);
   342         wl_surface_attach(data->surface, data->buffer, 0, 0);
   343         wl_surface_damage(data->surface, 0, 0, data->w, data->h);
   344         wl_surface_commit(data->surface);
   345     }
   346     else
   347     {
   348         wl_pointer_set_cursor (pointer, 0,
   349                                NULL,
   350                                0,
   351                                0);
   352     }
   353     
   354     return 0;
   355 }
   356 
   357 static void
   358 Wayland_WarpMouse(SDL_Window *window, int x, int y)
   359 {
   360     SDL_Unsupported();
   361 }
   362 
   363 static int
   364 Wayland_WarpMouseGlobal(int x, int y)
   365 {
   366     return SDL_Unsupported();
   367 }
   368 
   369 static int
   370 Wayland_SetRelativeMouseMode(SDL_bool enabled)
   371 {
   372     return SDL_Unsupported();
   373 }
   374 
   375 void
   376 Wayland_InitMouse(void)
   377 {
   378     SDL_Mouse *mouse = SDL_GetMouse();
   379 
   380     mouse->CreateCursor = Wayland_CreateCursor;
   381     mouse->CreateSystemCursor = Wayland_CreateSystemCursor;
   382     mouse->ShowCursor = Wayland_ShowCursor;
   383     mouse->FreeCursor = Wayland_FreeCursor;
   384     mouse->WarpMouse = Wayland_WarpMouse;
   385     mouse->WarpMouseGlobal = Wayland_WarpMouseGlobal;
   386     mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
   387 
   388     SDL_SetDefaultCursor(Wayland_CreateDefaultCursor());
   389 }
   390 
   391 void
   392 Wayland_FiniMouse(void)
   393 {
   394     /* This effectively assumes that nobody else
   395      * touches SDL_Mouse which is effectively
   396      * a singleton */
   397 
   398     SDL_Mouse *mouse = SDL_GetMouse();
   399 
   400     /* Free the current cursor if not the same pointer as
   401      * the default cursor */
   402     if (mouse->def_cursor != mouse->cur_cursor)
   403         Wayland_FreeCursor (mouse->cur_cursor);
   404 
   405     Wayland_FreeCursor (mouse->def_cursor);
   406     mouse->def_cursor = NULL;
   407     mouse->cur_cursor = NULL;
   408 
   409     mouse->CreateCursor =  NULL;
   410     mouse->CreateSystemCursor = NULL;
   411     mouse->ShowCursor = NULL;
   412     mouse->FreeCursor = NULL;
   413     mouse->WarpMouse = NULL;
   414     mouse->SetRelativeMouseMode = NULL;
   415 }
   416 #endif  /* SDL_VIDEO_DRIVER_WAYLAND */