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 #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
wayland_create_tmp_file(off_t size)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
mouse_buffer_release(void * data,struct wl_buffer * buffer)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
create_buffer_from_shm(Wayland_CursorData * d,int width,int height,uint32_t format)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 SDL_assert(d->shm_data != NULL);
129
130 shm_pool = wl_shm_create_pool(data->shm, shm_fd, size);
131 d->buffer = wl_shm_pool_create_buffer(shm_pool,
132 0,
133 width,
134 height,
135 stride,
136 format);
137 wl_buffer_add_listener(d->buffer,
138 &mouse_buffer_listener,
139 d);
140
141 wl_shm_pool_destroy (shm_pool);
142 close (shm_fd);
143
144 return 0;
145 }
146
147 static SDL_Cursor *
Wayland_CreateCursor(SDL_Surface * surface,int hot_x,int hot_y)148 Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
149 {
150 SDL_Cursor *cursor;
151
152 cursor = calloc(1, sizeof (*cursor));
153 if (cursor) {
154 SDL_VideoDevice *vd = SDL_GetVideoDevice ();
155 SDL_VideoData *wd = (SDL_VideoData *) vd->driverdata;
156 Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
157 if (!data) {
158 SDL_OutOfMemory();
159 free(cursor);
160 return NULL;
161 }
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_ARGB8888) < 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 } else {
191 SDL_OutOfMemory();
192 }
193
194 return cursor;
195 }
196
197 static SDL_Cursor *
CreateCursorFromWlCursor(SDL_VideoData * d,struct wl_cursor * wlcursor)198 CreateCursorFromWlCursor(SDL_VideoData *d, struct wl_cursor *wlcursor)
199 {
200 SDL_Cursor *cursor;
201
202 cursor = calloc(1, sizeof (*cursor));
203 if (cursor) {
204 Wayland_CursorData *data = calloc (1, sizeof (Wayland_CursorData));
205 if (!data) {
206 SDL_OutOfMemory();
207 free(cursor);
208 return NULL;
209 }
210 cursor->driverdata = (void *) data;
211
212 data->buffer = WAYLAND_wl_cursor_image_get_buffer(wlcursor->images[0]);
213 data->surface = wl_compositor_create_surface(d->compositor);
214 wl_surface_set_user_data(data->surface, NULL);
215 data->hot_x = wlcursor->images[0]->hotspot_x;
216 data->hot_y = wlcursor->images[0]->hotspot_y;
217 data->w = wlcursor->images[0]->width;
218 data->h = wlcursor->images[0]->height;
219 data->cursor= wlcursor;
220 } else {
221 SDL_OutOfMemory ();
222 }
223
224 return cursor;
225 }
226
227 static SDL_Cursor *
Wayland_CreateDefaultCursor()228 Wayland_CreateDefaultCursor()
229 {
230 SDL_VideoDevice *device = SDL_GetVideoDevice();
231 SDL_VideoData *data = device->driverdata;
232
233 return CreateCursorFromWlCursor (data,
234 WAYLAND_wl_cursor_theme_get_cursor(data->cursor_theme,
235 "left_ptr"));
236 }
237
238 static SDL_Cursor *
Wayland_CreateSystemCursor(SDL_SystemCursor id)239 Wayland_CreateSystemCursor(SDL_SystemCursor id)
240 {
241 SDL_VideoDevice *vd = SDL_GetVideoDevice();
242 SDL_VideoData *d = vd->driverdata;
243
244 struct wl_cursor *cursor = NULL;
245
246 switch(id)
247 {
248 default:
249 SDL_assert(0);
250 return NULL;
251 case SDL_SYSTEM_CURSOR_ARROW:
252 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
253 break;
254 case SDL_SYSTEM_CURSOR_IBEAM:
255 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
256 break;
257 case SDL_SYSTEM_CURSOR_WAIT:
258 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
259 break;
260 case SDL_SYSTEM_CURSOR_CROSSHAIR:
261 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
262 break;
263 case SDL_SYSTEM_CURSOR_WAITARROW:
264 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "watch");
265 break;
266 case SDL_SYSTEM_CURSOR_SIZENWSE:
267 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
268 break;
269 case SDL_SYSTEM_CURSOR_SIZENESW:
270 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
271 break;
272 case SDL_SYSTEM_CURSOR_SIZEWE:
273 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
274 break;
275 case SDL_SYSTEM_CURSOR_SIZENS:
276 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
277 break;
278 case SDL_SYSTEM_CURSOR_SIZEALL:
279 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
280 break;
281 case SDL_SYSTEM_CURSOR_NO:
282 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "xterm");
283 break;
284 case SDL_SYSTEM_CURSOR_HAND:
285 cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "hand1");
286 break;
287 }
288
289 return CreateCursorFromWlCursor(d, cursor);
290 }
291
292 static void
Wayland_FreeCursor(SDL_Cursor * cursor)293 Wayland_FreeCursor(SDL_Cursor *cursor)
294 {
295 Wayland_CursorData *d;
296
297 if (!cursor)
298 return;
299
300 d = cursor->driverdata;
301
302 /* Probably not a cursor we own */
303 if (!d)
304 return;
305
306 if (d->buffer && !d->cursor)
307 wl_buffer_destroy(d->buffer);
308
309 if (d->surface)
310 wl_surface_destroy(d->surface);
311
312 /* Not sure what's meant to happen to shm_data */
313 free (cursor->driverdata);
314 SDL_free(cursor);
315 }
316
317 static int
Wayland_ShowCursor(SDL_Cursor * cursor)318 Wayland_ShowCursor(SDL_Cursor *cursor)
319 {
320 SDL_VideoDevice *vd = SDL_GetVideoDevice();
321 SDL_VideoData *d = vd->driverdata;
322
323 struct wl_pointer *pointer = d->pointer;
324
325 if (!pointer)
326 return -1;
327
328 if (cursor)
329 {
330 Wayland_CursorData *data = cursor->driverdata;
331
332 wl_pointer_set_cursor (pointer, 0,
333 data->surface,
334 data->hot_x,
335 data->hot_y);
336 wl_surface_attach(data->surface, data->buffer, 0, 0);
337 wl_surface_damage(data->surface, 0, 0, data->w, data->h);
338 wl_surface_commit(data->surface);
339 }
340 else
341 {
342 wl_pointer_set_cursor (pointer, 0,
343 NULL,
344 0,
345 0);
346 }
347
348 return 0;
349 }
350
351 static void
Wayland_WarpMouse(SDL_Window * window,int x,int y)352 Wayland_WarpMouse(SDL_Window *window, int x, int y)
353 {
354 SDL_Unsupported();
355 }
356
357 static int
Wayland_WarpMouseGlobal(int x,int y)358 Wayland_WarpMouseGlobal(int x, int y)
359 {
360 return SDL_Unsupported();
361 }
362
363 static int
Wayland_SetRelativeMouseMode(SDL_bool enabled)364 Wayland_SetRelativeMouseMode(SDL_bool enabled)
365 {
366 SDL_VideoDevice *vd = SDL_GetVideoDevice();
367 SDL_VideoData *data = (SDL_VideoData *) vd->driverdata;
368
369 if (enabled)
370 return Wayland_input_lock_pointer(data->input);
371 else
372 return Wayland_input_unlock_pointer(data->input);
373 }
374
375 void
Wayland_InitMouse(void)376 Wayland_InitMouse(void)
377 {
378 SDL_Mouse *mouse = SDL_GetMouse();
379
380 mouse->CreateCursor = Wayland_CreateCursor;
381 mouse->CreateSystemCursor = Wayland_CreateSystemCursor;
382 mouse->ShowCursor = Wayland_ShowCursor;
383 mouse->FreeCursor = Wayland_FreeCursor;
384 mouse->WarpMouse = Wayland_WarpMouse;
385 mouse->WarpMouseGlobal = Wayland_WarpMouseGlobal;
386 mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
387
388 SDL_SetDefaultCursor(Wayland_CreateDefaultCursor());
389 }
390
391 void
Wayland_FiniMouse(void)392 Wayland_FiniMouse(void)
393 {
394 /* This effectively assumes that nobody else
395 * touches SDL_Mouse which is effectively
396 * a singleton */
397 }
398 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
399