1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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
45typedef 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
57static int
58wayland_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
85static void
86mouse_buffer_release(void *data, struct wl_buffer *buffer)
87{
88}
89
90static const struct wl_buffer_listener mouse_buffer_listener = {
91 mouse_buffer_release
92};
93
94static int
95create_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
146static SDL_Cursor *
147Wayland_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
196static SDL_Cursor *
197CreateCursorFromWlCursor(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
226static SDL_Cursor *
227Wayland_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
237static SDL_Cursor *
238Wayland_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
291static void
292Wayland_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
316static int
317Wayland_ShowCursor(SDL_Cursor *cursor)
318{
319 SDL_VideoDevice *vd = SDL_GetVideoDevice();
320 SDL_VideoData *d = vd->driverdata;
321 struct SDL_WaylandInput *input = d->input;
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,
333 input->pointer_enter_serial,
334 data->surface,
335 data->hot_x,
336 data->hot_y);
337 wl_surface_attach(data->surface, data->buffer, 0, 0);
338 wl_surface_damage(data->surface, 0, 0, data->w, data->h);
339 wl_surface_commit(data->surface);
340 }
341 else
342 {
343 wl_pointer_set_cursor (pointer,
344 input->pointer_enter_serial,
345 NULL,
346 0,
347 0);
348 }
349
350 return 0;
351}
352
353static void
354Wayland_WarpMouse(SDL_Window *window, int x, int y)
355{
356 SDL_Unsupported();
357}
358
359static int
360Wayland_WarpMouseGlobal(int x, int y)
361{
362 return SDL_Unsupported();
363}
364
365static int
366Wayland_SetRelativeMouseMode(SDL_bool enabled)
367{
368 SDL_VideoDevice *vd = SDL_GetVideoDevice();
369 SDL_VideoData *data = (SDL_VideoData *) vd->driverdata;
370
371 if (enabled)
372 return Wayland_input_lock_pointer(data->input);
373 else
374 return Wayland_input_unlock_pointer(data->input);
375}
376
377void
378Wayland_InitMouse(void)
379{
380 SDL_Mouse *mouse = SDL_GetMouse();
381
382 mouse->CreateCursor = Wayland_CreateCursor;
383 mouse->CreateSystemCursor = Wayland_CreateSystemCursor;
384 mouse->ShowCursor = Wayland_ShowCursor;
385 mouse->FreeCursor = Wayland_FreeCursor;
386 mouse->WarpMouse = Wayland_WarpMouse;
387 mouse->WarpMouseGlobal = Wayland_WarpMouseGlobal;
388 mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
389
390 SDL_SetDefaultCursor(Wayland_CreateDefaultCursor());
391}
392
393void
394Wayland_FiniMouse(void)
395{
396 /* This effectively assumes that nobody else
397 * touches SDL_Mouse which is effectively
398 * a singleton */
399}
400#endif /* SDL_VIDEO_DRIVER_WAYLAND */
401