src/video/wayland/SDL_waylandmouse.c
author Ryan C. Gordon
Mon, 20 Jan 2014 12:53:44 -0500
changeset 8116 f7c2f71251e5
parent 8104 2e4f1bd21196
child 8135 dd4729596096
permissions -rw-r--r--
Patched to compile if Wayland is disabled via SDL_config.h (thanks, Martin!).

Fixes Bugzilla #2351.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_attach(data->surface,
   185                           data->buffer,
   186                           0,
   187                           0);
   188         wl_surface_damage(data->surface,
   189                           0,
   190                           0,
   191                           surface->w,
   192                           surface->h);
   193         wl_surface_commit(data->surface);
   194 
   195         data->hot_x = hot_x;
   196         data->hot_y = hot_y;
   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         cursor->driverdata = (void *) data;
   211 
   212         /* The wl_buffer here will be destroyed from wl_cursor_theme_destroy
   213          * if we are fetching this from a wl_cursor_theme, so don't store a
   214          * reference to it here */
   215         data->buffer = NULL;
   216         data->surface = wl_compositor_create_surface(d->compositor);
   217         wl_surface_attach(data->surface,
   218                           WAYLAND_wl_cursor_image_get_buffer(wlcursor->images[0]),
   219                           0,
   220                           0);
   221         wl_surface_damage(data->surface,
   222                           0,
   223                           0,
   224                           wlcursor->images[0]->width,
   225                           wlcursor->images[0]->height);
   226         wl_surface_commit(data->surface);
   227         data->hot_x = wlcursor->images[0]->hotspot_x;
   228         data->hot_y = wlcursor->images[0]->hotspot_y;
   229         data->cursor= wlcursor;
   230     } else {
   231         SDL_OutOfMemory ();
   232     }
   233 
   234     return cursor;
   235 }
   236 
   237 static SDL_Cursor *
   238 Wayland_CreateDefaultCursor()
   239 {
   240     SDL_VideoDevice *device = SDL_GetVideoDevice();
   241     SDL_VideoData *data = device->driverdata;
   242 
   243     return CreateCursorFromWlCursor (data,
   244                                      WAYLAND_wl_cursor_theme_get_cursor(data->cursor_theme,
   245                                                                 "left_ptr"));
   246 }
   247 
   248 static SDL_Cursor *
   249 Wayland_CreateSystemCursor(SDL_SystemCursor id)
   250 {
   251     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   252     SDL_VideoData *d = vd->driverdata;
   253 
   254     struct wl_cursor *cursor = NULL;
   255 
   256     switch(id)
   257     {
   258     default:
   259         SDL_assert(0);
   260         return NULL;
   261     case SDL_SYSTEM_CURSOR_ARROW:
   262         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
   263         break;
   264     case SDL_SYSTEM_CURSOR_IBEAM:
   265         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   266         break;
   267     case SDL_SYSTEM_CURSOR_WAIT:
   268         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "wait");
   269         break;
   270     case SDL_SYSTEM_CURSOR_CROSSHAIR:
   271         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   272         break;
   273     case SDL_SYSTEM_CURSOR_WAITARROW:
   274         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "wait");
   275         break;
   276     case SDL_SYSTEM_CURSOR_SIZENWSE:
   277         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   278         break;
   279     case SDL_SYSTEM_CURSOR_SIZENESW:
   280         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   281         break;
   282     case SDL_SYSTEM_CURSOR_SIZEWE:
   283         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   284         break;
   285     case SDL_SYSTEM_CURSOR_SIZENS:
   286         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   287         break;
   288     case SDL_SYSTEM_CURSOR_SIZEALL:
   289         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   290         break;
   291     case SDL_SYSTEM_CURSOR_NO:
   292         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   293         break;
   294     case SDL_SYSTEM_CURSOR_HAND:
   295         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   296         break;
   297     }
   298 
   299     SDL_Cursor *sdl_cursor = CreateCursorFromWlCursor (d, cursor);
   300 
   301     return sdl_cursor;
   302 }
   303 
   304 static void
   305 Wayland_FreeCursor(SDL_Cursor *cursor)
   306 {
   307     if (!cursor)
   308         return;
   309 
   310     Wayland_CursorData *d = cursor->driverdata;
   311 
   312     /* Probably not a cursor we own */
   313     if (!d)
   314         return;
   315 
   316     if (d->buffer)
   317         wl_buffer_destroy(d->buffer);
   318 
   319     if (d->surface)
   320         wl_surface_destroy(d->surface);
   321 
   322     /* Not sure what's meant to happen to shm_data */
   323     free (cursor->driverdata);
   324     SDL_free(cursor);
   325 }
   326 
   327 static int
   328 Wayland_ShowCursor(SDL_Cursor *cursor)
   329 {
   330     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   331     SDL_VideoData *d = vd->driverdata;
   332 
   333     struct wl_pointer *pointer = d->pointer;
   334 
   335     if (!pointer)
   336         return -1;
   337 
   338     if (cursor)
   339     {
   340         Wayland_CursorData *data = cursor->driverdata;
   341 
   342         wl_pointer_set_cursor (pointer, 0,
   343                                data->surface,
   344                                data->hot_x,
   345                                data->hot_y);
   346     }
   347     else
   348     {
   349         wl_pointer_set_cursor (pointer, 0,
   350                                NULL,
   351                                0,
   352                                0);
   353     }
   354     
   355     return 0;
   356 }
   357 
   358 static void
   359 Wayland_WarpMouse(SDL_Window *window, int x, int y)
   360 {
   361     SDL_Unsupported();
   362     return;
   363 }
   364 
   365 static int
   366 Wayland_SetRelativeMouseMode(SDL_bool enabled)
   367 {
   368     SDL_Unsupported();
   369     return -1;
   370 }
   371 
   372 void
   373 Wayland_InitMouse(void)
   374 {
   375     SDL_Mouse *mouse = SDL_GetMouse();
   376 
   377     mouse->CreateCursor = Wayland_CreateCursor;
   378     mouse->CreateSystemCursor = Wayland_CreateSystemCursor;
   379     mouse->ShowCursor = Wayland_ShowCursor;
   380     mouse->FreeCursor = Wayland_FreeCursor;
   381     mouse->WarpMouse = Wayland_WarpMouse;
   382     mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
   383 
   384     SDL_SetDefaultCursor(Wayland_CreateDefaultCursor());
   385 }
   386 
   387 void
   388 Wayland_FiniMouse(void)
   389 {
   390     /* This effectively assumes that nobody else
   391      * touches SDL_Mouse which is effectively
   392      * a singleton */
   393 
   394     SDL_Mouse *mouse = SDL_GetMouse();
   395 
   396     /* Free the current cursor if not the same pointer as
   397      * the default cursor */
   398     if (mouse->def_cursor != mouse->cur_cursor)
   399         Wayland_FreeCursor (mouse->cur_cursor);
   400 
   401     Wayland_FreeCursor (mouse->def_cursor);
   402     mouse->def_cursor = NULL;
   403     mouse->cur_cursor = NULL;
   404 
   405     mouse->CreateCursor =  NULL;
   406     mouse->CreateSystemCursor = NULL;
   407     mouse->ShowCursor = NULL;
   408     mouse->FreeCursor = NULL;
   409     mouse->WarpMouse = NULL;
   410     mouse->SetRelativeMouseMode = NULL;
   411 }
   412 #endif  /* SDL_VIDEO_DRIVER_WAYLAND */