src/video/wayland/SDL_waylandmouse.c
author Sam Lantinga
Thu, 17 Apr 2014 20:21:10 -0700
changeset 8710 403c95143f8a
parent 8149 681eb46b8ac4
child 8711 46f85bfe9303
permissions -rw-r--r--
Fixed bug 2482 - Wayland_CreateSystemCursor trying to load nonexistent "wait" cursor

Bryan Cain

Wayland_CreateSystemCursor tries to load a cursor named "wait" for two of the system cursor categories. This causes a segmentation fault when one of these cursors is used, because "wait" is not an actual cursor name in X11/Wayland cursor themes.

I can't attach my patch since I'm on a mobile right now, but I can confirm that simply replacing "wait" with "watch" for both of its uses in Wayland_CreateSystemCursor (in SDL_waylandmouse.c) fixes the bug.
     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 #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 
    57     /* Either a preloaded cursor, or one we created ourselves */
    58     struct wl_cursor   *cursor;
    59     void               *shm_data;
    60 } Wayland_CursorData;
    61 
    62 static int
    63 wayland_create_tmp_file(off_t size)
    64 {
    65     static const char template[] = "/sdl-shared-XXXXXX";
    66     char *xdg_path;
    67     char tmp_path[PATH_MAX];
    68     int fd;
    69 
    70     xdg_path = SDL_getenv("XDG_RUNTIME_DIR");
    71     if (!xdg_path) {
    72         errno = ENOENT;
    73         return -1;
    74     }
    75 
    76     SDL_strlcpy(tmp_path, xdg_path, PATH_MAX);
    77     SDL_strlcat(tmp_path, template, PATH_MAX);
    78 
    79     fd = mkostemp(tmp_path, O_CLOEXEC);
    80     if (fd < 0)
    81         return -1;
    82 
    83     if (ftruncate(fd, size) < 0) {
    84         close(fd);
    85         return -1;
    86     }
    87 
    88     return fd;
    89 }
    90 
    91 static void
    92 mouse_buffer_release(void *data, struct wl_buffer *buffer)
    93 {
    94 }
    95 
    96 static const struct wl_buffer_listener mouse_buffer_listener = {
    97     mouse_buffer_release
    98 };
    99 
   100 static int
   101 create_buffer_from_shm(Wayland_CursorData *d,
   102                        int width,
   103                        int height,
   104                        uint32_t format)
   105 {
   106     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   107     SDL_VideoData *data = (SDL_VideoData *) vd->driverdata;
   108 
   109     int stride = width * 4;
   110     int size = stride * height;
   111 
   112     int shm_fd;
   113 
   114     shm_fd = wayland_create_tmp_file(size);
   115     if (shm_fd < 0)
   116     {
   117         fprintf(stderr, "creating mouse cursor buffer failed!\n");
   118         return -1;
   119     }
   120 
   121     d->shm_data = mmap(NULL,
   122                        size,
   123                        PROT_READ | PROT_WRITE,
   124                        MAP_SHARED,
   125                        shm_fd,
   126                        0);
   127     if (data == MAP_FAILED) {
   128         d->shm_data = NULL;
   129         fprintf (stderr, "mmap () failed\n");
   130         close (shm_fd);
   131     }
   132 
   133     struct wl_shm_pool *shm_pool = wl_shm_create_pool(data->shm,
   134                                                       shm_fd,
   135                                                       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         cursor->driverdata = (void *) data;
   163 
   164         /* Assume ARGB8888 */
   165         SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
   166         SDL_assert(surface->pitch == surface->w * 4);
   167 
   168         /* Allocate shared memory buffer for this cursor */
   169         if (create_buffer_from_shm (data,
   170                                     surface->w,
   171                                     surface->h,
   172                                     WL_SHM_FORMAT_XRGB8888) < 0)
   173         {
   174             free (cursor->driverdata);
   175             free (cursor);
   176             return NULL;
   177         }
   178 
   179         SDL_memcpy(data->shm_data,
   180                    surface->pixels,
   181                    surface->h * surface->pitch);
   182 
   183         data->surface = wl_compositor_create_surface(wd->compositor);
   184         wl_surface_set_user_data(data->surface, NULL);
   185         wl_surface_attach(data->surface,
   186                           data->buffer,
   187                           0,
   188                           0);
   189         wl_surface_damage(data->surface,
   190                           0,
   191                           0,
   192                           surface->w,
   193                           surface->h);
   194         wl_surface_commit(data->surface);
   195 
   196         data->hot_x = hot_x;
   197         data->hot_y = hot_y;
   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         cursor->driverdata = (void *) data;
   212 
   213         /* The wl_buffer here will be destroyed from wl_cursor_theme_destroy
   214          * if we are fetching this from a wl_cursor_theme, so don't store a
   215          * reference to it here */
   216         data->buffer = NULL;
   217         data->surface = wl_compositor_create_surface(d->compositor);
   218         wl_surface_set_user_data(data->surface, NULL);
   219         wl_surface_attach(data->surface,
   220                           WAYLAND_wl_cursor_image_get_buffer(wlcursor->images[0]),
   221                           0,
   222                           0);
   223         wl_surface_damage(data->surface,
   224                           0,
   225                           0,
   226                           wlcursor->images[0]->width,
   227                           wlcursor->images[0]->height);
   228         wl_surface_commit(data->surface);
   229         data->hot_x = wlcursor->images[0]->hotspot_x;
   230         data->hot_y = wlcursor->images[0]->hotspot_y;
   231         data->cursor= wlcursor;
   232     } else {
   233         SDL_OutOfMemory ();
   234     }
   235 
   236     return cursor;
   237 }
   238 
   239 static SDL_Cursor *
   240 Wayland_CreateDefaultCursor()
   241 {
   242     SDL_VideoDevice *device = SDL_GetVideoDevice();
   243     SDL_VideoData *data = device->driverdata;
   244 
   245     return CreateCursorFromWlCursor (data,
   246                                      WAYLAND_wl_cursor_theme_get_cursor(data->cursor_theme,
   247                                                                 "left_ptr"));
   248 }
   249 
   250 static SDL_Cursor *
   251 Wayland_CreateSystemCursor(SDL_SystemCursor id)
   252 {
   253     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   254     SDL_VideoData *d = vd->driverdata;
   255 
   256     struct wl_cursor *cursor = NULL;
   257 
   258     switch(id)
   259     {
   260     default:
   261         SDL_assert(0);
   262         return NULL;
   263     case SDL_SYSTEM_CURSOR_ARROW:
   264         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
   265         break;
   266     case SDL_SYSTEM_CURSOR_IBEAM:
   267         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   268         break;
   269     case SDL_SYSTEM_CURSOR_WAIT:
   270         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
   271         break;
   272     case SDL_SYSTEM_CURSOR_CROSSHAIR:
   273         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   274         break;
   275     case SDL_SYSTEM_CURSOR_WAITARROW:
   276         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
   277         break;
   278     case SDL_SYSTEM_CURSOR_SIZENWSE:
   279         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   280         break;
   281     case SDL_SYSTEM_CURSOR_SIZENESW:
   282         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   283         break;
   284     case SDL_SYSTEM_CURSOR_SIZEWE:
   285         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   286         break;
   287     case SDL_SYSTEM_CURSOR_SIZENS:
   288         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   289         break;
   290     case SDL_SYSTEM_CURSOR_SIZEALL:
   291         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   292         break;
   293     case SDL_SYSTEM_CURSOR_NO:
   294         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   295         break;
   296     case SDL_SYSTEM_CURSOR_HAND:
   297         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   298         break;
   299     }
   300 
   301     SDL_Cursor *sdl_cursor = CreateCursorFromWlCursor (d, cursor);
   302 
   303     return sdl_cursor;
   304 }
   305 
   306 static void
   307 Wayland_FreeCursor(SDL_Cursor *cursor)
   308 {
   309     if (!cursor)
   310         return;
   311 
   312     Wayland_CursorData *d = cursor->driverdata;
   313 
   314     /* Probably not a cursor we own */
   315     if (!d)
   316         return;
   317 
   318     if (d->buffer)
   319         wl_buffer_destroy(d->buffer);
   320 
   321     if (d->surface)
   322         wl_surface_destroy(d->surface);
   323 
   324     /* Not sure what's meant to happen to shm_data */
   325     free (cursor->driverdata);
   326     SDL_free(cursor);
   327 }
   328 
   329 static int
   330 Wayland_ShowCursor(SDL_Cursor *cursor)
   331 {
   332     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   333     SDL_VideoData *d = vd->driverdata;
   334 
   335     struct wl_pointer *pointer = d->pointer;
   336 
   337     if (!pointer)
   338         return -1;
   339 
   340     if (cursor)
   341     {
   342         Wayland_CursorData *data = cursor->driverdata;
   343 
   344         wl_pointer_set_cursor (pointer, 0,
   345                                data->surface,
   346                                data->hot_x,
   347                                data->hot_y);
   348     }
   349     else
   350     {
   351         wl_pointer_set_cursor (pointer, 0,
   352                                NULL,
   353                                0,
   354                                0);
   355     }
   356     
   357     return 0;
   358 }
   359 
   360 static void
   361 Wayland_WarpMouse(SDL_Window *window, int x, int y)
   362 {
   363     SDL_Unsupported();
   364     return;
   365 }
   366 
   367 static int
   368 Wayland_SetRelativeMouseMode(SDL_bool enabled)
   369 {
   370     SDL_Unsupported();
   371     return -1;
   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->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
   385 
   386     SDL_SetDefaultCursor(Wayland_CreateDefaultCursor());
   387 }
   388 
   389 void
   390 Wayland_FiniMouse(void)
   391 {
   392     /* This effectively assumes that nobody else
   393      * touches SDL_Mouse which is effectively
   394      * a singleton */
   395 
   396     SDL_Mouse *mouse = SDL_GetMouse();
   397 
   398     /* Free the current cursor if not the same pointer as
   399      * the default cursor */
   400     if (mouse->def_cursor != mouse->cur_cursor)
   401         Wayland_FreeCursor (mouse->cur_cursor);
   402 
   403     Wayland_FreeCursor (mouse->def_cursor);
   404     mouse->def_cursor = NULL;
   405     mouse->cur_cursor = NULL;
   406 
   407     mouse->CreateCursor =  NULL;
   408     mouse->CreateSystemCursor = NULL;
   409     mouse->ShowCursor = NULL;
   410     mouse->FreeCursor = NULL;
   411     mouse->WarpMouse = NULL;
   412     mouse->SetRelativeMouseMode = NULL;
   413 }
   414 #endif  /* SDL_VIDEO_DRIVER_WAYLAND */