SDL_waylandmouse.c (11078B)
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 51typedef 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 63static int 64wayland_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 92static void 93mouse_buffer_release(void *data, struct wl_buffer *buffer) 94{ 95} 96 97static const struct wl_buffer_listener mouse_buffer_listener = { 98 mouse_buffer_release 99}; 100 101static int 102create_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 fprintf(stderr, "creating mouse cursor buffer failed!\n"); 120 return -1; 121 } 122 123 d->shm_data = mmap(NULL, 124 size, 125 PROT_READ | PROT_WRITE, 126 MAP_SHARED, 127 shm_fd, 128 0); 129 if (d->shm_data == MAP_FAILED) { 130 d->shm_data = NULL; 131 fprintf (stderr, "mmap () failed\n"); 132 close (shm_fd); 133 } 134 135 shm_pool = wl_shm_create_pool(data->shm, shm_fd, 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 152static SDL_Cursor * 153Wayland_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_set_user_data(data->surface, NULL); 185 186 data->hot_x = hot_x; 187 data->hot_y = hot_y; 188 data->w = surface->w; 189 data->h = surface->h; 190 } 191 192 return cursor; 193} 194 195static SDL_Cursor * 196CreateCursorFromWlCursor(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 cursor->driverdata = (void *) data; 204 205 data->buffer = WAYLAND_wl_cursor_image_get_buffer(wlcursor->images[0]); 206 data->surface = wl_compositor_create_surface(d->compositor); 207 wl_surface_set_user_data(data->surface, NULL); 208 data->hot_x = wlcursor->images[0]->hotspot_x; 209 data->hot_y = wlcursor->images[0]->hotspot_y; 210 data->w = wlcursor->images[0]->width; 211 data->h = wlcursor->images[0]->height; 212 data->cursor= wlcursor; 213 } else { 214 SDL_OutOfMemory (); 215 } 216 217 return cursor; 218} 219 220static SDL_Cursor * 221Wayland_CreateDefaultCursor() 222{ 223 SDL_VideoDevice *device = SDL_GetVideoDevice(); 224 SDL_VideoData *data = device->driverdata; 225 226 return CreateCursorFromWlCursor (data, 227 WAYLAND_wl_cursor_theme_get_cursor(data->cursor_theme, 228 "left_ptr")); 229} 230 231static SDL_Cursor * 232Wayland_CreateSystemCursor(SDL_SystemCursor id) 233{ 234 SDL_VideoDevice *vd = SDL_GetVideoDevice(); 235 SDL_VideoData *d = vd->driverdata; 236 237 struct wl_cursor *cursor = NULL; 238 239 switch(id) 240 { 241 default: 242 SDL_assert(0); 243 return NULL; 244 case SDL_SYSTEM_CURSOR_ARROW: 245 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr"); 246 break; 247 case SDL_SYSTEM_CURSOR_IBEAM: 248 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm"); 249 break; 250 case SDL_SYSTEM_CURSOR_WAIT: 251 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch"); 252 break; 253 case SDL_SYSTEM_CURSOR_CROSSHAIR: 254 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1"); 255 break; 256 case SDL_SYSTEM_CURSOR_WAITARROW: 257 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch"); 258 break; 259 case SDL_SYSTEM_CURSOR_SIZENWSE: 260 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1"); 261 break; 262 case SDL_SYSTEM_CURSOR_SIZENESW: 263 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1"); 264 break; 265 case SDL_SYSTEM_CURSOR_SIZEWE: 266 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1"); 267 break; 268 case SDL_SYSTEM_CURSOR_SIZENS: 269 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1"); 270 break; 271 case SDL_SYSTEM_CURSOR_SIZEALL: 272 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1"); 273 break; 274 case SDL_SYSTEM_CURSOR_NO: 275 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm"); 276 break; 277 case SDL_SYSTEM_CURSOR_HAND: 278 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1"); 279 break; 280 } 281 282 return CreateCursorFromWlCursor(d, cursor); 283} 284 285static void 286Wayland_FreeCursor(SDL_Cursor *cursor) 287{ 288 Wayland_CursorData *d; 289 290 if (!cursor) 291 return; 292 293 d = cursor->driverdata; 294 295 /* Probably not a cursor we own */ 296 if (!d) 297 return; 298 299 if (d->buffer && !d->cursor) 300 wl_buffer_destroy(d->buffer); 301 302 if (d->surface) 303 wl_surface_destroy(d->surface); 304 305 /* Not sure what's meant to happen to shm_data */ 306 free (cursor->driverdata); 307 SDL_free(cursor); 308} 309 310static int 311Wayland_ShowCursor(SDL_Cursor *cursor) 312{ 313 SDL_VideoDevice *vd = SDL_GetVideoDevice(); 314 SDL_VideoData *d = vd->driverdata; 315 316 struct wl_pointer *pointer = d->pointer; 317 318 if (!pointer) 319 return -1; 320 321 if (cursor) 322 { 323 Wayland_CursorData *data = cursor->driverdata; 324 325 wl_surface_attach(data->surface, data->buffer, 0, 0); 326 wl_surface_damage(data->surface, 0, 0, data->w, data->h); 327 wl_surface_commit(data->surface); 328 wl_pointer_set_cursor (pointer, 0, 329 data->surface, 330 data->hot_x, 331 data->hot_y); 332 } 333 else 334 { 335 wl_pointer_set_cursor (pointer, 0, 336 NULL, 337 0, 338 0); 339 } 340 341 return 0; 342} 343 344static void 345Wayland_WarpMouse(SDL_Window *window, int x, int y) 346{ 347 SDL_Unsupported(); 348} 349 350static void 351Wayland_WarpMouseGlobal(int x, int y) 352{ 353 SDL_Unsupported(); 354} 355 356static int 357Wayland_SetRelativeMouseMode(SDL_bool enabled) 358{ 359 SDL_Unsupported(); 360 return -1; 361} 362 363void 364Wayland_InitMouse(void) 365{ 366 SDL_Mouse *mouse = SDL_GetMouse(); 367 368 mouse->CreateCursor = Wayland_CreateCursor; 369 mouse->CreateSystemCursor = Wayland_CreateSystemCursor; 370 mouse->ShowCursor = Wayland_ShowCursor; 371 mouse->FreeCursor = Wayland_FreeCursor; 372 mouse->WarpMouse = Wayland_WarpMouse; 373 mouse->WarpMouseGlobal = Wayland_WarpMouseGlobal; 374 mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode; 375 376 SDL_SetDefaultCursor(Wayland_CreateDefaultCursor()); 377} 378 379void 380Wayland_FiniMouse(void) 381{ 382 /* This effectively assumes that nobody else 383 * touches SDL_Mouse which is effectively 384 * a singleton */ 385 386 SDL_Mouse *mouse = SDL_GetMouse(); 387 388 /* Free the current cursor if not the same pointer as 389 * the default cursor */ 390 if (mouse->def_cursor != mouse->cur_cursor) 391 Wayland_FreeCursor (mouse->cur_cursor); 392 393 Wayland_FreeCursor (mouse->def_cursor); 394 mouse->def_cursor = NULL; 395 mouse->cur_cursor = NULL; 396 397 mouse->CreateCursor = NULL; 398 mouse->CreateSystemCursor = NULL; 399 mouse->ShowCursor = NULL; 400 mouse->FreeCursor = NULL; 401 mouse->WarpMouse = NULL; 402 mouse->SetRelativeMouseMode = NULL; 403} 404#endif /* SDL_VIDEO_DRIVER_WAYLAND */