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