src/video/wayland/SDL_waylandmouse.c
author Philipp Wiesemann
Wed, 02 Mar 2016 20:25:09 +0100
changeset 10098 91b34fbbb88e
parent 9998 f67cf37e9cd4
child 10113 991977532d7f
permissions -rw-r--r--
Wayland: Fixed crash if allocating memory for cursor failed.

Also added missing error message if first allocation failed.
     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     }
   134 
   135     shm_pool = wl_shm_create_pool(data->shm, shm_fd, size);
   136     d->buffer = wl_shm_pool_create_buffer(shm_pool,
   137                                           0,
   138                                           width,
   139                                           height,
   140                                           stride,
   141                                           format);
   142     wl_buffer_add_listener(d->buffer,
   143                            &mouse_buffer_listener,
   144                            d);
   145 
   146     wl_shm_pool_destroy (shm_pool);
   147     close (shm_fd);
   148 
   149     return 0;
   150 }
   151 
   152 static SDL_Cursor *
   153 Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
   154 {
   155     SDL_Cursor *cursor;
   156 
   157     cursor = calloc(1, sizeof (*cursor));
   158     if (cursor) {
   159         SDL_VideoDevice *vd = SDL_GetVideoDevice ();
   160         SDL_VideoData *wd = (SDL_VideoData *) vd->driverdata;
   161         Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
   162         if (!data) {
   163             SDL_OutOfMemory();
   164             free(cursor);
   165             return NULL;
   166         }
   167         cursor->driverdata = (void *) data;
   168 
   169         /* Assume ARGB8888 */
   170         SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
   171         SDL_assert(surface->pitch == surface->w * 4);
   172 
   173         /* Allocate shared memory buffer for this cursor */
   174         if (create_buffer_from_shm (data,
   175                                     surface->w,
   176                                     surface->h,
   177                                     WL_SHM_FORMAT_XRGB8888) < 0)
   178         {
   179             free (cursor->driverdata);
   180             free (cursor);
   181             return NULL;
   182         }
   183 
   184         SDL_memcpy(data->shm_data,
   185                    surface->pixels,
   186                    surface->h * surface->pitch);
   187 
   188         data->surface = wl_compositor_create_surface(wd->compositor);
   189         wl_surface_set_user_data(data->surface, NULL);
   190 
   191         data->hot_x = hot_x;
   192         data->hot_y = hot_y;
   193         data->w = surface->w;
   194         data->h = surface->h;
   195     } else {
   196         SDL_OutOfMemory();
   197     }
   198 
   199     return cursor;
   200 }
   201 
   202 static SDL_Cursor *
   203 CreateCursorFromWlCursor(SDL_VideoData *d, struct wl_cursor *wlcursor)
   204 {
   205     SDL_Cursor *cursor;
   206 
   207     cursor = calloc(1, sizeof (*cursor));
   208     if (cursor) {
   209         Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
   210         if (!data) {
   211             SDL_OutOfMemory();
   212             free(cursor);
   213             return NULL;
   214         }
   215         cursor->driverdata = (void *) data;
   216 
   217         data->buffer = WAYLAND_wl_cursor_image_get_buffer(wlcursor->images[0]);
   218         data->surface = wl_compositor_create_surface(d->compositor);
   219         wl_surface_set_user_data(data->surface, NULL);
   220         data->hot_x = wlcursor->images[0]->hotspot_x;
   221         data->hot_y = wlcursor->images[0]->hotspot_y;
   222         data->w = wlcursor->images[0]->width;
   223         data->h = wlcursor->images[0]->height;
   224         data->cursor= wlcursor;
   225     } else {
   226         SDL_OutOfMemory ();
   227     }
   228 
   229     return cursor;
   230 }
   231 
   232 static SDL_Cursor *
   233 Wayland_CreateDefaultCursor()
   234 {
   235     SDL_VideoDevice *device = SDL_GetVideoDevice();
   236     SDL_VideoData *data = device->driverdata;
   237 
   238     return CreateCursorFromWlCursor (data,
   239                                      WAYLAND_wl_cursor_theme_get_cursor(data->cursor_theme,
   240                                                                 "left_ptr"));
   241 }
   242 
   243 static SDL_Cursor *
   244 Wayland_CreateSystemCursor(SDL_SystemCursor id)
   245 {
   246     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   247     SDL_VideoData *d = vd->driverdata;
   248 
   249     struct wl_cursor *cursor = NULL;
   250 
   251     switch(id)
   252     {
   253     default:
   254         SDL_assert(0);
   255         return NULL;
   256     case SDL_SYSTEM_CURSOR_ARROW:
   257         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
   258         break;
   259     case SDL_SYSTEM_CURSOR_IBEAM:
   260         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   261         break;
   262     case SDL_SYSTEM_CURSOR_WAIT:
   263         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
   264         break;
   265     case SDL_SYSTEM_CURSOR_CROSSHAIR:
   266         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   267         break;
   268     case SDL_SYSTEM_CURSOR_WAITARROW:
   269         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
   270         break;
   271     case SDL_SYSTEM_CURSOR_SIZENWSE:
   272         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   273         break;
   274     case SDL_SYSTEM_CURSOR_SIZENESW:
   275         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   276         break;
   277     case SDL_SYSTEM_CURSOR_SIZEWE:
   278         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   279         break;
   280     case SDL_SYSTEM_CURSOR_SIZENS:
   281         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   282         break;
   283     case SDL_SYSTEM_CURSOR_SIZEALL:
   284         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   285         break;
   286     case SDL_SYSTEM_CURSOR_NO:
   287         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   288         break;
   289     case SDL_SYSTEM_CURSOR_HAND:
   290         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   291         break;
   292     }
   293 
   294     return CreateCursorFromWlCursor(d, cursor);
   295 }
   296 
   297 static void
   298 Wayland_FreeCursor(SDL_Cursor *cursor)
   299 {
   300     Wayland_CursorData *d;
   301 
   302     if (!cursor)
   303         return;
   304 
   305     d = cursor->driverdata;
   306 
   307     /* Probably not a cursor we own */
   308     if (!d)
   309         return;
   310 
   311     if (d->buffer && !d->cursor)
   312         wl_buffer_destroy(d->buffer);
   313 
   314     if (d->surface)
   315         wl_surface_destroy(d->surface);
   316 
   317     /* Not sure what's meant to happen to shm_data */
   318     free (cursor->driverdata);
   319     SDL_free(cursor);
   320 }
   321 
   322 static int
   323 Wayland_ShowCursor(SDL_Cursor *cursor)
   324 {
   325     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   326     SDL_VideoData *d = vd->driverdata;
   327 
   328     struct wl_pointer *pointer = d->pointer;
   329 
   330     if (!pointer)
   331         return -1;
   332 
   333     if (cursor)
   334     {
   335         Wayland_CursorData *data = cursor->driverdata;
   336 
   337         wl_surface_attach(data->surface, data->buffer, 0, 0);
   338         wl_surface_damage(data->surface, 0, 0, data->w, data->h);
   339         wl_surface_commit(data->surface);
   340         wl_pointer_set_cursor (pointer, 0,
   341                                data->surface,
   342                                data->hot_x,
   343                                data->hot_y);
   344     }
   345     else
   346     {
   347         wl_pointer_set_cursor (pointer, 0,
   348                                NULL,
   349                                0,
   350                                0);
   351     }
   352     
   353     return 0;
   354 }
   355 
   356 static void
   357 Wayland_WarpMouse(SDL_Window *window, int x, int y)
   358 {
   359     SDL_Unsupported();
   360 }
   361 
   362 static int
   363 Wayland_WarpMouseGlobal(int x, int y)
   364 {
   365     return SDL_Unsupported();
   366 }
   367 
   368 static int
   369 Wayland_SetRelativeMouseMode(SDL_bool enabled)
   370 {
   371     return SDL_Unsupported();
   372 }
   373 
   374 void
   375 Wayland_InitMouse(void)
   376 {
   377     SDL_Mouse *mouse = SDL_GetMouse();
   378 
   379     mouse->CreateCursor = Wayland_CreateCursor;
   380     mouse->CreateSystemCursor = Wayland_CreateSystemCursor;
   381     mouse->ShowCursor = Wayland_ShowCursor;
   382     mouse->FreeCursor = Wayland_FreeCursor;
   383     mouse->WarpMouse = Wayland_WarpMouse;
   384     mouse->WarpMouseGlobal = Wayland_WarpMouseGlobal;
   385     mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
   386 
   387     SDL_SetDefaultCursor(Wayland_CreateDefaultCursor());
   388 }
   389 
   390 void
   391 Wayland_FiniMouse(void)
   392 {
   393     /* This effectively assumes that nobody else
   394      * touches SDL_Mouse which is effectively
   395      * a singleton */
   396 
   397     SDL_Mouse *mouse = SDL_GetMouse();
   398 
   399     /* Free the current cursor if not the same pointer as
   400      * the default cursor */
   401     if (mouse->def_cursor != mouse->cur_cursor)
   402         Wayland_FreeCursor (mouse->cur_cursor);
   403 
   404     Wayland_FreeCursor (mouse->def_cursor);
   405     mouse->def_cursor = NULL;
   406     mouse->cur_cursor = NULL;
   407 
   408     mouse->CreateCursor =  NULL;
   409     mouse->CreateSystemCursor = NULL;
   410     mouse->ShowCursor = NULL;
   411     mouse->FreeCursor = NULL;
   412     mouse->WarpMouse = NULL;
   413     mouse->SetRelativeMouseMode = NULL;
   414 }
   415 #endif  /* SDL_VIDEO_DRIVER_WAYLAND */