src/video/wayland/SDL_waylandmouse.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 16 Oct 2019 13:54:35 -0400
changeset 13132 d1a5e3cd0ae8
parent 12503 806492103856
permissions -rw-r--r--
emscripten: Patched to compile (thanks, Caleb!).

Fixes Bugzilla #4827.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 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 #include <sys/types.h>
    27 #include <sys/mman.h>
    28 #include <fcntl.h>
    29 #include <unistd.h>
    30 #include <stdlib.h>
    31 #include <limits.h>
    32 
    33 #include "../SDL_sysvideo.h"
    34 
    35 #include "SDL_mouse.h"
    36 #include "../../events/SDL_mouse_c.h"
    37 #include "SDL_waylandvideo.h"
    38 #include "SDL_waylandevents_c.h"
    39 
    40 #include "SDL_waylanddyn.h"
    41 #include "wayland-cursor.h"
    42 
    43 #include "SDL_assert.h"
    44 
    45 
    46 typedef struct {
    47     struct wl_buffer   *buffer;
    48     struct wl_surface  *surface;
    49 
    50     int                hot_x, hot_y;
    51     int                w, h;
    52 
    53     /* Either a preloaded cursor, or one we created ourselves */
    54     struct wl_cursor   *cursor;
    55     void               *shm_data;
    56 } Wayland_CursorData;
    57 
    58 static int
    59 wayland_create_tmp_file(off_t size)
    60 {
    61     static const char template[] = "/sdl-shared-XXXXXX";
    62     char *xdg_path;
    63     char tmp_path[PATH_MAX];
    64     int fd;
    65 
    66     xdg_path = SDL_getenv("XDG_RUNTIME_DIR");
    67     if (!xdg_path) {
    68         return -1;
    69     }
    70 
    71     SDL_strlcpy(tmp_path, xdg_path, PATH_MAX);
    72     SDL_strlcat(tmp_path, template, PATH_MAX);
    73 
    74     fd = mkostemp(tmp_path, O_CLOEXEC);
    75     if (fd < 0)
    76         return -1;
    77 
    78     if (ftruncate(fd, size) < 0) {
    79         close(fd);
    80         return -1;
    81     }
    82 
    83     return fd;
    84 }
    85 
    86 static void
    87 mouse_buffer_release(void *data, struct wl_buffer *buffer)
    88 {
    89 }
    90 
    91 static const struct wl_buffer_listener mouse_buffer_listener = {
    92     mouse_buffer_release
    93 };
    94 
    95 static int
    96 create_buffer_from_shm(Wayland_CursorData *d,
    97                        int width,
    98                        int height,
    99                        uint32_t format)
   100 {
   101     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   102     SDL_VideoData *data = (SDL_VideoData *) vd->driverdata;
   103     struct wl_shm_pool *shm_pool;
   104 
   105     int stride = width * 4;
   106     int size = stride * height;
   107 
   108     int shm_fd;
   109 
   110     shm_fd = wayland_create_tmp_file(size);
   111     if (shm_fd < 0)
   112     {
   113         return SDL_SetError("Creating mouse cursor buffer failed.");
   114     }
   115 
   116     d->shm_data = mmap(NULL,
   117                        size,
   118                        PROT_READ | PROT_WRITE,
   119                        MAP_SHARED,
   120                        shm_fd,
   121                        0);
   122     if (d->shm_data == MAP_FAILED) {
   123         d->shm_data = NULL;
   124         close (shm_fd);
   125         return SDL_SetError("mmap() failed.");
   126     }
   127 
   128     shm_pool = wl_shm_create_pool(data->shm, shm_fd, size);
   129     d->buffer = wl_shm_pool_create_buffer(shm_pool,
   130                                           0,
   131                                           width,
   132                                           height,
   133                                           stride,
   134                                           format);
   135     wl_buffer_add_listener(d->buffer,
   136                            &mouse_buffer_listener,
   137                            d);
   138 
   139     wl_shm_pool_destroy (shm_pool);
   140     close (shm_fd);
   141 
   142     return 0;
   143 }
   144 
   145 static SDL_Cursor *
   146 Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
   147 {
   148     SDL_Cursor *cursor;
   149 
   150     cursor = calloc(1, sizeof (*cursor));
   151     if (cursor) {
   152         SDL_VideoDevice *vd = SDL_GetVideoDevice ();
   153         SDL_VideoData *wd = (SDL_VideoData *) vd->driverdata;
   154         Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
   155         if (!data) {
   156             SDL_OutOfMemory();
   157             free(cursor);
   158             return NULL;
   159         }
   160         cursor->driverdata = (void *) data;
   161 
   162         /* Assume ARGB8888 */
   163         SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
   164         SDL_assert(surface->pitch == surface->w * 4);
   165 
   166         /* Allocate shared memory buffer for this cursor */
   167         if (create_buffer_from_shm (data,
   168                                     surface->w,
   169                                     surface->h,
   170                                     WL_SHM_FORMAT_ARGB8888) < 0)
   171         {
   172             free (cursor->driverdata);
   173             free (cursor);
   174             return NULL;
   175         }
   176 
   177         SDL_memcpy(data->shm_data,
   178                    surface->pixels,
   179                    surface->h * surface->pitch);
   180 
   181         data->surface = wl_compositor_create_surface(wd->compositor);
   182         wl_surface_set_user_data(data->surface, NULL);
   183 
   184         data->hot_x = hot_x;
   185         data->hot_y = hot_y;
   186         data->w = surface->w;
   187         data->h = surface->h;
   188     } else {
   189         SDL_OutOfMemory();
   190     }
   191 
   192     return cursor;
   193 }
   194 
   195 static SDL_Cursor *
   196 CreateCursorFromWlCursor(SDL_VideoData *d, struct wl_cursor *wlcursor)
   197 {
   198     SDL_Cursor *cursor;
   199 
   200     cursor = calloc(1, sizeof (*cursor));
   201     if (cursor) {
   202         Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
   203         if (!data) {
   204             SDL_OutOfMemory();
   205             free(cursor);
   206             return NULL;
   207         }
   208         cursor->driverdata = (void *) data;
   209 
   210         data->buffer = WAYLAND_wl_cursor_image_get_buffer(wlcursor->images[0]);
   211         data->surface = wl_compositor_create_surface(d->compositor);
   212         wl_surface_set_user_data(data->surface, NULL);
   213         data->hot_x = wlcursor->images[0]->hotspot_x;
   214         data->hot_y = wlcursor->images[0]->hotspot_y;
   215         data->w = wlcursor->images[0]->width;
   216         data->h = wlcursor->images[0]->height;
   217         data->cursor= wlcursor;
   218     } else {
   219         SDL_OutOfMemory ();
   220     }
   221 
   222     return cursor;
   223 }
   224 
   225 static SDL_Cursor *
   226 Wayland_CreateDefaultCursor()
   227 {
   228     SDL_VideoDevice *device = SDL_GetVideoDevice();
   229     SDL_VideoData *data = device->driverdata;
   230 
   231     return CreateCursorFromWlCursor (data,
   232                                      WAYLAND_wl_cursor_theme_get_cursor(data->cursor_theme,
   233                                                                 "left_ptr"));
   234 }
   235 
   236 static SDL_Cursor *
   237 Wayland_CreateSystemCursor(SDL_SystemCursor id)
   238 {
   239     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   240     SDL_VideoData *d = vd->driverdata;
   241 
   242     struct wl_cursor *cursor = NULL;
   243 
   244     switch(id)
   245     {
   246     default:
   247         SDL_assert(0);
   248         return NULL;
   249     case SDL_SYSTEM_CURSOR_ARROW:
   250         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
   251         break;
   252     case SDL_SYSTEM_CURSOR_IBEAM:
   253         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   254         break;
   255     case SDL_SYSTEM_CURSOR_WAIT:
   256         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
   257         break;
   258     case SDL_SYSTEM_CURSOR_CROSSHAIR:
   259         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   260         break;
   261     case SDL_SYSTEM_CURSOR_WAITARROW:
   262         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
   263         break;
   264     case SDL_SYSTEM_CURSOR_SIZENWSE:
   265         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   266         break;
   267     case SDL_SYSTEM_CURSOR_SIZENESW:
   268         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   269         break;
   270     case SDL_SYSTEM_CURSOR_SIZEWE:
   271         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   272         break;
   273     case SDL_SYSTEM_CURSOR_SIZENS:
   274         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   275         break;
   276     case SDL_SYSTEM_CURSOR_SIZEALL:
   277         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   278         break;
   279     case SDL_SYSTEM_CURSOR_NO:
   280         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
   281         break;
   282     case SDL_SYSTEM_CURSOR_HAND:
   283         cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
   284         break;
   285     }
   286 
   287     return CreateCursorFromWlCursor(d, cursor);
   288 }
   289 
   290 static void
   291 Wayland_FreeCursor(SDL_Cursor *cursor)
   292 {
   293     Wayland_CursorData *d;
   294 
   295     if (!cursor)
   296         return;
   297 
   298     d = cursor->driverdata;
   299 
   300     /* Probably not a cursor we own */
   301     if (!d)
   302         return;
   303 
   304     if (d->buffer && !d->cursor)
   305         wl_buffer_destroy(d->buffer);
   306 
   307     if (d->surface)
   308         wl_surface_destroy(d->surface);
   309 
   310     /* Not sure what's meant to happen to shm_data */
   311     free (cursor->driverdata);
   312     SDL_free(cursor);
   313 }
   314 
   315 static int
   316 Wayland_ShowCursor(SDL_Cursor *cursor)
   317 {
   318     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   319     SDL_VideoData *d = vd->driverdata;
   320 
   321     struct wl_pointer *pointer = d->pointer;
   322 
   323     if (!pointer)
   324         return -1;
   325 
   326     if (cursor)
   327     {
   328         Wayland_CursorData *data = cursor->driverdata;
   329 
   330         wl_pointer_set_cursor (pointer, 0,
   331                                data->surface,
   332                                data->hot_x,
   333                                data->hot_y);
   334         wl_surface_attach(data->surface, data->buffer, 0, 0);
   335         wl_surface_damage(data->surface, 0, 0, data->w, data->h);
   336         wl_surface_commit(data->surface);
   337     }
   338     else
   339     {
   340         wl_pointer_set_cursor (pointer, 0,
   341                                NULL,
   342                                0,
   343                                0);
   344     }
   345     
   346     return 0;
   347 }
   348 
   349 static void
   350 Wayland_WarpMouse(SDL_Window *window, int x, int y)
   351 {
   352     SDL_Unsupported();
   353 }
   354 
   355 static int
   356 Wayland_WarpMouseGlobal(int x, int y)
   357 {
   358     return SDL_Unsupported();
   359 }
   360 
   361 static int
   362 Wayland_SetRelativeMouseMode(SDL_bool enabled)
   363 {
   364     SDL_VideoDevice *vd = SDL_GetVideoDevice();
   365     SDL_VideoData *data = (SDL_VideoData *) vd->driverdata;
   366 
   367     if (enabled)
   368         return Wayland_input_lock_pointer(data->input);
   369     else
   370         return Wayland_input_unlock_pointer(data->input);
   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 #endif  /* SDL_VIDEO_DRIVER_WAYLAND */