src/video/wayland/SDL_waylandmouse.c
author Sam Lantinga
Thu, 17 Apr 2014 20:51:28 -0700
changeset 8711 46f85bfe9303
parent 8710 403c95143f8a
child 8721 d73e451f1dc5
permissions -rw-r--r--
Fixed bug 2485 - [PATCH] Wayland: cursor disappears permanently after window loses mouse focus

Bryan Cain

Using any SDL application with the Wayland backend under Weston, if the application sets a cursor with SDL_SetCursor, the cursor will work until the mouse pointer leaves the window. When the pointer re-enters the window, there will be no cursor displayed at all.

I did some digging, and the reason for this is that SDL attaches the buffer to the cursor surface only once (during cursor creation) and assumes that it will stay attached. This is not how Wayland works, though - once the compositor is done rendering the buffer, it will release it, so it is no longer attached to the surface. When the cursor re-enters the window a second time, SDL sets the cursor to the same surface with no buffer attached, so no cursor is displayed.

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