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.
gabomdq@8062
     1
/*
gabomdq@8062
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
gabomdq@8062
     4
gabomdq@8062
     5
  This software is provided 'as-is', without any express or implied
gabomdq@8062
     6
  warranty.  In no event will the authors be held liable for any damages
gabomdq@8062
     7
  arising from the use of this software.
gabomdq@8062
     8
gabomdq@8062
     9
  Permission is granted to anyone to use this software for any purpose,
gabomdq@8062
    10
  including commercial applications, and to alter it and redistribute it
gabomdq@8062
    11
  freely, subject to the following restrictions:
gabomdq@8062
    12
gabomdq@8062
    13
  1. The origin of this software must not be misrepresented; you must not
gabomdq@8062
    14
     claim that you wrote the original software. If you use this software
gabomdq@8062
    15
     in a product, an acknowledgment in the product documentation would be
gabomdq@8062
    16
     appreciated but is not required.
gabomdq@8062
    17
  2. Altered source versions must be plainly marked as such, and must not be
gabomdq@8062
    18
     misrepresented as being the original software.
gabomdq@8062
    19
  3. This notice may not be removed or altered from any source distribution.
gabomdq@8062
    20
*/
gabomdq@8062
    21
icculus@8116
    22
#include "../../SDL_internal.h"
icculus@8116
    23
icculus@8116
    24
#if SDL_VIDEO_DRIVER_WAYLAND
icculus@8116
    25
gabomdq@8062
    26
#ifndef _GNU_SOURCE
gabomdq@8062
    27
#define _GNU_SOURCE
gabomdq@8062
    28
#endif
gabomdq@8062
    29
gabomdq@8062
    30
#include <errno.h>
gabomdq@8062
    31
#include <sys/types.h>
gabomdq@8062
    32
#include <sys/mman.h>
gabomdq@8062
    33
#include <fcntl.h>
gabomdq@8062
    34
#include <unistd.h>
gabomdq@8062
    35
#include <stdlib.h>
gabomdq@8062
    36
#include <limits.h>
gabomdq@8062
    37
gabomdq@8062
    38
#include "../SDL_sysvideo.h"
gabomdq@8062
    39
gabomdq@8062
    40
#include "SDL_mouse.h"
gabomdq@8062
    41
#include "../../events/SDL_mouse_c.h"
gabomdq@8062
    42
#include "SDL_waylandvideo.h"
gabomdq@8062
    43
#include "SDL_waylandevents_c.h"
gabomdq@8062
    44
gabomdq@8104
    45
#include "SDL_waylanddyn.h"
gabomdq@8104
    46
#include "wayland-cursor.h"
gabomdq@8104
    47
gabomdq@8062
    48
#include "SDL_assert.h"
gabomdq@8062
    49
gabomdq@8062
    50
gabomdq@8062
    51
typedef struct {
gabomdq@8062
    52
    struct wl_buffer   *buffer;
gabomdq@8062
    53
    struct wl_surface  *surface;
gabomdq@8062
    54
gabomdq@8062
    55
    int                hot_x, hot_y;
slouken@8711
    56
    int                w, h;
gabomdq@8062
    57
gabomdq@8062
    58
    /* Either a preloaded cursor, or one we created ourselves */
gabomdq@8062
    59
    struct wl_cursor   *cursor;
gabomdq@8062
    60
    void               *shm_data;
gabomdq@8062
    61
} Wayland_CursorData;
gabomdq@8062
    62
gabomdq@8062
    63
static int
gabomdq@8062
    64
wayland_create_tmp_file(off_t size)
gabomdq@8062
    65
{
gabomdq@8062
    66
    static const char template[] = "/sdl-shared-XXXXXX";
gabomdq@8062
    67
    char *xdg_path;
gabomdq@8062
    68
    char tmp_path[PATH_MAX];
gabomdq@8062
    69
    int fd;
gabomdq@8062
    70
gabomdq@8062
    71
    xdg_path = SDL_getenv("XDG_RUNTIME_DIR");
gabomdq@8062
    72
    if (!xdg_path) {
gabomdq@8062
    73
        errno = ENOENT;
gabomdq@8062
    74
        return -1;
gabomdq@8062
    75
    }
gabomdq@8062
    76
gabomdq@8062
    77
    SDL_strlcpy(tmp_path, xdg_path, PATH_MAX);
gabomdq@8062
    78
    SDL_strlcat(tmp_path, template, PATH_MAX);
gabomdq@8062
    79
gabomdq@8062
    80
    fd = mkostemp(tmp_path, O_CLOEXEC);
gabomdq@8062
    81
    if (fd < 0)
gabomdq@8062
    82
        return -1;
gabomdq@8062
    83
gabomdq@8062
    84
    if (ftruncate(fd, size) < 0) {
gabomdq@8062
    85
        close(fd);
gabomdq@8062
    86
        return -1;
gabomdq@8062
    87
    }
gabomdq@8062
    88
gabomdq@8062
    89
    return fd;
gabomdq@8062
    90
}
gabomdq@8062
    91
gabomdq@8062
    92
static void
gabomdq@8062
    93
mouse_buffer_release(void *data, struct wl_buffer *buffer)
gabomdq@8062
    94
{
gabomdq@8062
    95
}
gabomdq@8062
    96
gabomdq@8062
    97
static const struct wl_buffer_listener mouse_buffer_listener = {
gabomdq@8062
    98
    mouse_buffer_release
gabomdq@8062
    99
};
gabomdq@8062
   100
gabomdq@8062
   101
static int
gabomdq@8062
   102
create_buffer_from_shm(Wayland_CursorData *d,
gabomdq@8062
   103
                       int width,
gabomdq@8062
   104
                       int height,
gabomdq@8062
   105
                       uint32_t format)
gabomdq@8062
   106
{
gabomdq@8062
   107
    SDL_VideoDevice *vd = SDL_GetVideoDevice();
gabomdq@8062
   108
    SDL_VideoData *data = (SDL_VideoData *) vd->driverdata;
gabomdq@8062
   109
gabomdq@8062
   110
    int stride = width * 4;
gabomdq@8062
   111
    int size = stride * height;
gabomdq@8062
   112
gabomdq@8062
   113
    int shm_fd;
gabomdq@8062
   114
gabomdq@8062
   115
    shm_fd = wayland_create_tmp_file(size);
gabomdq@8062
   116
    if (shm_fd < 0)
gabomdq@8062
   117
    {
gabomdq@8062
   118
        fprintf(stderr, "creating mouse cursor buffer failed!\n");
gabomdq@8062
   119
        return -1;
gabomdq@8062
   120
    }
gabomdq@8062
   121
gabomdq@8062
   122
    d->shm_data = mmap(NULL,
gabomdq@8062
   123
                       size,
gabomdq@8062
   124
                       PROT_READ | PROT_WRITE,
gabomdq@8062
   125
                       MAP_SHARED,
gabomdq@8062
   126
                       shm_fd,
gabomdq@8062
   127
                       0);
gabomdq@8062
   128
    if (data == MAP_FAILED) {
gabomdq@8062
   129
        d->shm_data = NULL;
gabomdq@8062
   130
        fprintf (stderr, "mmap () failed\n");
gabomdq@8062
   131
        close (shm_fd);
gabomdq@8062
   132
    }
gabomdq@8062
   133
gabomdq@8062
   134
    struct wl_shm_pool *shm_pool = wl_shm_create_pool(data->shm,
gabomdq@8062
   135
                                                      shm_fd,
gabomdq@8062
   136
                                                      size);
gabomdq@8062
   137
    d->buffer = wl_shm_pool_create_buffer(shm_pool,
gabomdq@8062
   138
                                          0,
gabomdq@8062
   139
                                          width,
gabomdq@8062
   140
                                          height,
gabomdq@8062
   141
                                          stride,
gabomdq@8062
   142
                                          format);
gabomdq@8062
   143
    wl_buffer_add_listener(d->buffer,
gabomdq@8062
   144
                           &mouse_buffer_listener,
gabomdq@8062
   145
                           d);
gabomdq@8062
   146
gabomdq@8062
   147
    wl_shm_pool_destroy (shm_pool);
gabomdq@8062
   148
    close (shm_fd);
gabomdq@8062
   149
gabomdq@8062
   150
    return 0;
gabomdq@8062
   151
}
gabomdq@8062
   152
gabomdq@8062
   153
static SDL_Cursor *
gabomdq@8062
   154
Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
gabomdq@8062
   155
{
gabomdq@8062
   156
    SDL_Cursor *cursor;
gabomdq@8062
   157
gabomdq@8062
   158
    cursor = calloc(1, sizeof (*cursor));
gabomdq@8062
   159
    if (cursor) {
gabomdq@8062
   160
        SDL_VideoDevice *vd = SDL_GetVideoDevice ();
gabomdq@8062
   161
        SDL_VideoData *wd = (SDL_VideoData *) vd->driverdata;
gabomdq@8062
   162
        Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
gabomdq@8062
   163
        cursor->driverdata = (void *) data;
gabomdq@8062
   164
gabomdq@8062
   165
        /* Assume ARGB8888 */
gabomdq@8062
   166
        SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
gabomdq@8062
   167
        SDL_assert(surface->pitch == surface->w * 4);
gabomdq@8062
   168
gabomdq@8062
   169
        /* Allocate shared memory buffer for this cursor */
gabomdq@8062
   170
        if (create_buffer_from_shm (data,
gabomdq@8062
   171
                                    surface->w,
gabomdq@8062
   172
                                    surface->h,
gabomdq@8062
   173
                                    WL_SHM_FORMAT_XRGB8888) < 0)
gabomdq@8062
   174
        {
gabomdq@8062
   175
            free (cursor->driverdata);
gabomdq@8062
   176
            free (cursor);
gabomdq@8062
   177
            return NULL;
gabomdq@8062
   178
        }
gabomdq@8062
   179
gabomdq@8062
   180
        SDL_memcpy(data->shm_data,
gabomdq@8062
   181
                   surface->pixels,
gabomdq@8062
   182
                   surface->h * surface->pitch);
gabomdq@8062
   183
gabomdq@8062
   184
        data->surface = wl_compositor_create_surface(wd->compositor);
gabomdq@8135
   185
        wl_surface_set_user_data(data->surface, NULL);
gabomdq@8062
   186
gabomdq@8062
   187
        data->hot_x = hot_x;
gabomdq@8062
   188
        data->hot_y = hot_y;
slouken@8711
   189
        data->w = surface->w;
slouken@8711
   190
        data->h = surface->h;
gabomdq@8062
   191
    }
gabomdq@8062
   192
gabomdq@8062
   193
    return cursor;
gabomdq@8062
   194
}
gabomdq@8062
   195
gabomdq@8062
   196
static SDL_Cursor *
gabomdq@8062
   197
CreateCursorFromWlCursor(SDL_VideoData *d, struct wl_cursor *wlcursor)
gabomdq@8062
   198
{
gabomdq@8062
   199
    SDL_Cursor *cursor;
gabomdq@8062
   200
gabomdq@8062
   201
    cursor = calloc(1, sizeof (*cursor));
gabomdq@8062
   202
    if (cursor) {
gabomdq@8062
   203
        Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
gabomdq@8062
   204
        cursor->driverdata = (void *) data;
gabomdq@8062
   205
slouken@8711
   206
        data->buffer = WAYLAND_wl_cursor_image_get_buffer(wlcursor->images[0]);
gabomdq@8062
   207
        data->surface = wl_compositor_create_surface(d->compositor);
gabomdq@8135
   208
        wl_surface_set_user_data(data->surface, NULL);
gabomdq@8062
   209
        data->hot_x = wlcursor->images[0]->hotspot_x;
gabomdq@8062
   210
        data->hot_y = wlcursor->images[0]->hotspot_y;
slouken@8711
   211
        data->w = wlcursor->images[0]->width;
slouken@8711
   212
        data->h = wlcursor->images[0]->height;
gabomdq@8062
   213
        data->cursor= wlcursor;
gabomdq@8062
   214
    } else {
gabomdq@8062
   215
        SDL_OutOfMemory ();
gabomdq@8062
   216
    }
gabomdq@8062
   217
gabomdq@8062
   218
    return cursor;
gabomdq@8062
   219
}
gabomdq@8062
   220
gabomdq@8062
   221
static SDL_Cursor *
gabomdq@8062
   222
Wayland_CreateDefaultCursor()
gabomdq@8062
   223
{
gabomdq@8062
   224
    SDL_VideoDevice *device = SDL_GetVideoDevice();
gabomdq@8062
   225
    SDL_VideoData *data = device->driverdata;
gabomdq@8062
   226
gabomdq@8062
   227
    return CreateCursorFromWlCursor (data,
gabomdq@8104
   228
                                     WAYLAND_wl_cursor_theme_get_cursor(data->cursor_theme,
gabomdq@8062
   229
                                                                "left_ptr"));
gabomdq@8062
   230
}
gabomdq@8062
   231
gabomdq@8062
   232
static SDL_Cursor *
gabomdq@8062
   233
Wayland_CreateSystemCursor(SDL_SystemCursor id)
gabomdq@8062
   234
{
gabomdq@8062
   235
    SDL_VideoDevice *vd = SDL_GetVideoDevice();
gabomdq@8062
   236
    SDL_VideoData *d = vd->driverdata;
gabomdq@8062
   237
gabomdq@8062
   238
    struct wl_cursor *cursor = NULL;
gabomdq@8062
   239
gabomdq@8062
   240
    switch(id)
gabomdq@8062
   241
    {
gabomdq@8062
   242
    default:
gabomdq@8062
   243
        SDL_assert(0);
gabomdq@8062
   244
        return NULL;
gabomdq@8062
   245
    case SDL_SYSTEM_CURSOR_ARROW:
gabomdq@8104
   246
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
gabomdq@8062
   247
        break;
gabomdq@8062
   248
    case SDL_SYSTEM_CURSOR_IBEAM:
gabomdq@8104
   249
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
gabomdq@8062
   250
        break;
gabomdq@8062
   251
    case SDL_SYSTEM_CURSOR_WAIT:
slouken@8710
   252
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
gabomdq@8062
   253
        break;
gabomdq@8062
   254
    case SDL_SYSTEM_CURSOR_CROSSHAIR:
gabomdq@8104
   255
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
gabomdq@8062
   256
        break;
gabomdq@8062
   257
    case SDL_SYSTEM_CURSOR_WAITARROW:
slouken@8710
   258
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
gabomdq@8062
   259
        break;
gabomdq@8062
   260
    case SDL_SYSTEM_CURSOR_SIZENWSE:
gabomdq@8104
   261
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
gabomdq@8062
   262
        break;
gabomdq@8062
   263
    case SDL_SYSTEM_CURSOR_SIZENESW:
gabomdq@8104
   264
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
gabomdq@8062
   265
        break;
gabomdq@8062
   266
    case SDL_SYSTEM_CURSOR_SIZEWE:
gabomdq@8104
   267
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
gabomdq@8062
   268
        break;
gabomdq@8062
   269
    case SDL_SYSTEM_CURSOR_SIZENS:
gabomdq@8104
   270
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
gabomdq@8062
   271
        break;
gabomdq@8062
   272
    case SDL_SYSTEM_CURSOR_SIZEALL:
gabomdq@8104
   273
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
gabomdq@8062
   274
        break;
gabomdq@8062
   275
    case SDL_SYSTEM_CURSOR_NO:
gabomdq@8104
   276
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
gabomdq@8062
   277
        break;
gabomdq@8062
   278
    case SDL_SYSTEM_CURSOR_HAND:
gabomdq@8104
   279
        cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
gabomdq@8062
   280
        break;
gabomdq@8062
   281
    }
gabomdq@8062
   282
gabomdq@8062
   283
    SDL_Cursor *sdl_cursor = CreateCursorFromWlCursor (d, cursor);
gabomdq@8062
   284
gabomdq@8062
   285
    return sdl_cursor;
gabomdq@8062
   286
}
gabomdq@8062
   287
gabomdq@8062
   288
static void
gabomdq@8062
   289
Wayland_FreeCursor(SDL_Cursor *cursor)
gabomdq@8062
   290
{
gabomdq@8062
   291
    if (!cursor)
gabomdq@8062
   292
        return;
gabomdq@8062
   293
gabomdq@8062
   294
    Wayland_CursorData *d = cursor->driverdata;
gabomdq@8062
   295
gabomdq@8062
   296
    /* Probably not a cursor we own */
gabomdq@8062
   297
    if (!d)
gabomdq@8062
   298
        return;
gabomdq@8062
   299
slouken@8711
   300
    if (d->buffer && !d->cursor)
gabomdq@8062
   301
        wl_buffer_destroy(d->buffer);
gabomdq@8062
   302
gabomdq@8062
   303
    if (d->surface)
gabomdq@8062
   304
        wl_surface_destroy(d->surface);
gabomdq@8062
   305
gabomdq@8062
   306
    /* Not sure what's meant to happen to shm_data */
gabomdq@8062
   307
    free (cursor->driverdata);
gabomdq@8062
   308
    SDL_free(cursor);
gabomdq@8062
   309
}
gabomdq@8062
   310
gabomdq@8062
   311
static int
gabomdq@8062
   312
Wayland_ShowCursor(SDL_Cursor *cursor)
gabomdq@8062
   313
{
gabomdq@8062
   314
    SDL_VideoDevice *vd = SDL_GetVideoDevice();
gabomdq@8062
   315
    SDL_VideoData *d = vd->driverdata;
gabomdq@8062
   316
gabomdq@8062
   317
    struct wl_pointer *pointer = d->pointer;
gabomdq@8062
   318
gabomdq@8062
   319
    if (!pointer)
gabomdq@8062
   320
        return -1;
gabomdq@8062
   321
gabomdq@8062
   322
    if (cursor)
gabomdq@8062
   323
    {
gabomdq@8062
   324
        Wayland_CursorData *data = cursor->driverdata;
gabomdq@8062
   325
slouken@8711
   326
        wl_surface_attach(data->surface, data->buffer, 0, 0);
slouken@8711
   327
        wl_surface_damage(data->surface, 0, 0, data->w, data->h);
slouken@8711
   328
        wl_surface_commit(data->surface);
gabomdq@8062
   329
        wl_pointer_set_cursor (pointer, 0,
gabomdq@8062
   330
                               data->surface,
gabomdq@8062
   331
                               data->hot_x,
gabomdq@8062
   332
                               data->hot_y);
gabomdq@8062
   333
    }
gabomdq@8062
   334
    else
gabomdq@8062
   335
    {
gabomdq@8062
   336
        wl_pointer_set_cursor (pointer, 0,
gabomdq@8062
   337
                               NULL,
gabomdq@8062
   338
                               0,
gabomdq@8062
   339
                               0);
gabomdq@8062
   340
    }
gabomdq@8062
   341
    
gabomdq@8062
   342
    return 0;
gabomdq@8062
   343
}
gabomdq@8062
   344
gabomdq@8062
   345
static void
gabomdq@8062
   346
Wayland_WarpMouse(SDL_Window *window, int x, int y)
gabomdq@8062
   347
{
gabomdq@8062
   348
    SDL_Unsupported();
gabomdq@8062
   349
    return;
gabomdq@8062
   350
}
gabomdq@8062
   351
gabomdq@8062
   352
static int
gabomdq@8062
   353
Wayland_SetRelativeMouseMode(SDL_bool enabled)
gabomdq@8062
   354
{
gabomdq@8062
   355
    SDL_Unsupported();
gabomdq@8062
   356
    return -1;
gabomdq@8062
   357
}
gabomdq@8062
   358
gabomdq@8062
   359
void
gabomdq@8062
   360
Wayland_InitMouse(void)
gabomdq@8062
   361
{
gabomdq@8062
   362
    SDL_Mouse *mouse = SDL_GetMouse();
gabomdq@8062
   363
gabomdq@8062
   364
    mouse->CreateCursor = Wayland_CreateCursor;
gabomdq@8062
   365
    mouse->CreateSystemCursor = Wayland_CreateSystemCursor;
gabomdq@8062
   366
    mouse->ShowCursor = Wayland_ShowCursor;
gabomdq@8062
   367
    mouse->FreeCursor = Wayland_FreeCursor;
gabomdq@8062
   368
    mouse->WarpMouse = Wayland_WarpMouse;
gabomdq@8062
   369
    mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
gabomdq@8062
   370
gabomdq@8062
   371
    SDL_SetDefaultCursor(Wayland_CreateDefaultCursor());
gabomdq@8062
   372
}
gabomdq@8062
   373
gabomdq@8062
   374
void
gabomdq@8062
   375
Wayland_FiniMouse(void)
gabomdq@8062
   376
{
gabomdq@8062
   377
    /* This effectively assumes that nobody else
gabomdq@8062
   378
     * touches SDL_Mouse which is effectively
gabomdq@8062
   379
     * a singleton */
gabomdq@8062
   380
gabomdq@8062
   381
    SDL_Mouse *mouse = SDL_GetMouse();
gabomdq@8062
   382
gabomdq@8062
   383
    /* Free the current cursor if not the same pointer as
gabomdq@8062
   384
     * the default cursor */
gabomdq@8062
   385
    if (mouse->def_cursor != mouse->cur_cursor)
gabomdq@8062
   386
        Wayland_FreeCursor (mouse->cur_cursor);
gabomdq@8062
   387
gabomdq@8062
   388
    Wayland_FreeCursor (mouse->def_cursor);
gabomdq@8062
   389
    mouse->def_cursor = NULL;
gabomdq@8062
   390
    mouse->cur_cursor = NULL;
gabomdq@8062
   391
gabomdq@8062
   392
    mouse->CreateCursor =  NULL;
gabomdq@8062
   393
    mouse->CreateSystemCursor = NULL;
gabomdq@8062
   394
    mouse->ShowCursor = NULL;
gabomdq@8062
   395
    mouse->FreeCursor = NULL;
gabomdq@8062
   396
    mouse->WarpMouse = NULL;
gabomdq@8062
   397
    mouse->SetRelativeMouseMode = NULL;
gabomdq@8062
   398
}
icculus@8116
   399
#endif  /* SDL_VIDEO_DRIVER_WAYLAND */