src/video/wayland/SDL_waylandmouse.c
author Philipp Wiesemann
Mon, 28 Mar 2016 21:03:04 +0200
changeset 10130 eba8155e32b1
parent 10120 f49e8a97c214
child 10154 fae27a079fcb
permissions -rw-r--r--
Wayland: Fixed missing error message if creating a custom cursor failed.

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