/
SDL_kmsdrmmouse.c
447 lines (375 loc) · 15.2 KB
1
2
/*
Simple DirectMedia Layer
3
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
22
23
24
25
26
27
28
29
#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_KMSDRM
#include "SDL_kmsdrmvideo.h"
#include "SDL_kmsdrmmouse.h"
#include "SDL_kmsdrmdyn.h"
30
#include "SDL_assert.h"
31
32
33
34
35
36
37
38
39
40
41
42
#include "../../events/SDL_mouse_c.h"
#include "../../events/default_cursor.h"
static SDL_Cursor *KMSDRM_CreateDefaultCursor(void);
static SDL_Cursor *KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y);
static int KMSDRM_ShowCursor(SDL_Cursor * cursor);
static void KMSDRM_MoveCursor(SDL_Cursor * cursor);
static void KMSDRM_FreeCursor(SDL_Cursor * cursor);
static void KMSDRM_WarpMouse(SDL_Window * window, int x, int y);
static int KMSDRM_WarpMouseGlobal(int x, int y);
43
44
45
46
47
/**********************************/
/* Atomic helper functions block. */
/**********************************/
int
48
drm_atomic_movecursor(KMSDRM_CursorData *curdata, uint16_t x, uint16_t y)
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
{
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
if (!dispdata->cursor_plane) /* We can't move a non-existing cursor, but that's ok. */
return 0;
/* Do we have a set of changes already in the making? If not, allocate a new one. */
if (!dispdata->atomic_req)
dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_X", x - curdata->hot_x);
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_Y", y - curdata->hot_y);
return 0;
}
/***************************************/
/* Atomic helper functions block ends. */
/***************************************/
69
70
71
72
73
/* Converts a pixel from straight-alpha [AA, RR, GG, BB], which the SDL cursor surface has,
to premultiplied-alpha [AA. AA*RR, AA*GG, AA*BB].
These multiplications have to be done with floats instead of uint32_t's, and the resulting values have
to be converted to be relative to the 0-255 interval, where 255 is 1.00 and anything between 0 and 255 is 0.xx. */
void alpha_premultiply_ARGB8888 (uint32_t *pixel) {
74
75
uint32_t A, R, G, B;
76
77
78
79
80
81
/* Component bytes extraction. */
A = (*pixel >> (3 << 3)) & 0xFF;
R = (*pixel >> (2 << 3)) & 0xFF;
G = (*pixel >> (1 << 3)) & 0xFF;
B = (*pixel >> (0 << 3)) & 0xFF;
82
83
84
85
86
/* Alpha pre-multiplication of each component. */
R = (float)A * ((float)R /255);
G = (float)A * ((float)G /255);
B = (float)A * ((float)B /255);
87
88
89
90
/* ARGB8888 pixel recomposition. */
(*pixel) = (((uint32_t)A << 24) | ((uint32_t)R << 16) | ((uint32_t)G << 8)) | ((uint32_t)B << 0);
}
91
92
93
94
95
static SDL_Cursor *
KMSDRM_CreateDefaultCursor(void)
{
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
96
97
}
98
99
100
/* Create a GBM cursor from a surface, which means creating a hardware cursor.
Most programs use software cursors, but protracker-clone for example uses
an optional hardware cursor. */
101
102
103
104
static SDL_Cursor *
KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
{
SDL_VideoDevice *dev = SDL_GetVideoDevice();
105
SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
106
107
KMSDRM_CursorData *curdata;
SDL_Cursor *cursor;
108
109
110
uint64_t usable_cursor_w, usable_cursor_h;
uint32_t bo_stride, pixel;
uint32_t *buffer = NULL;
111
size_t bufsize;
112
unsigned int i, j;
113
114
115
116
117
118
/* All code below assumes ARGB8888 format for the cursor surface, like other backends do.
Also, the GBM BO pixels have to be alpha-premultiplied, but the SDL surface we receive has
straight-alpha pixels, so we always have to convert. */
SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
SDL_assert(surface->pitch == surface->w * 4);
119
120
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
121
SDL_SetError("Unsupported pixel format for cursor");
122
123
124
125
return NULL;
}
cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
126
if (!cursor) {
127
128
129
130
SDL_OutOfMemory();
return NULL;
}
curdata = (KMSDRM_CursorData *) SDL_calloc(1, sizeof(*curdata));
131
if (!curdata) {
132
133
134
135
136
SDL_OutOfMemory();
SDL_free(cursor);
return NULL;
}
137
138
139
140
141
/* Find out what GBM cursor size is recommended by the driver. */
if (KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_WIDTH, &usable_cursor_w) ||
KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT, &usable_cursor_h)) {
SDL_SetError("Could not get the recommended GBM cursor size");
goto cleanup;
142
143
}
144
if (usable_cursor_w == 0 || usable_cursor_h == 0) {
145
146
SDL_SetError("Could not get an usable GBM cursor size");
goto cleanup;
147
148
}
149
/* hox_x and hot_y are the coordinates of the "tip of the cursor" from it's base. */
150
151
curdata->hot_x = hot_x;
curdata->hot_y = hot_y;
152
153
curdata->w = usable_cursor_w;
curdata->h = usable_cursor_h;
154
155
curdata->bo = KMSDRM_gbm_bo_create(viddata->gbm_dev, usable_cursor_w, usable_cursor_h, GBM_FORMAT_ARGB8888,
156
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
157
158
if (!curdata->bo) {
159
160
161
162
163
SDL_SetError("Could not create GBM cursor BO");
goto cleanup;
}
bo_stride = KMSDRM_gbm_bo_get_stride(curdata->bo);
164
bufsize = bo_stride * curdata->h;
165
166
167
168
/* Always use a temp buffer: it serves the purpose of storing the
alpha-premultiplied pixels (so we can copy them to the gbm BO
with a single gb_bo_write() call), and also copying from the
169
170
171
172
173
174
SDL surface, line by line, to a gbm BO with different pitch. */
buffer = (uint32_t*)SDL_malloc(bufsize);
if (!buffer) {
SDL_OutOfMemory();
goto cleanup;
}
175
176
177
178
179
180
181
if (SDL_MUSTLOCK(surface)) {
if (SDL_LockSurface(surface) < 0) {
/* Could not lock surface */
goto cleanup;
}
}
182
183
184
/* Clean the whole temporary buffer. */
SDL_memset(buffer, 0x00, bo_stride * curdata->h);
185
186
/* Copy from SDL surface to buffer, pre-multiplying by alpha each pixel as we go. */
187
188
for (i = 0; i < surface->h; i++) {
for (j = 0; j < surface->w; j++) {
189
190
191
pixel = ((uint32_t*)surface->pixels)[i * surface->w + j];
alpha_premultiply_ARGB8888 (&pixel);
SDL_memcpy(buffer + (i * curdata->w) + j, &pixel, 4);
192
}
193
}
194
195
196
197
if (SDL_MUSTLOCK(surface)) {
SDL_UnlockSurface(surface);
}
198
199
200
201
if (KMSDRM_gbm_bo_write(curdata->bo, buffer, bufsize)) {
SDL_SetError("Could not write to GBM cursor BO");
goto cleanup;
202
203
}
204
205
206
207
/* Free temporary buffer */
SDL_free(buffer);
buffer = NULL;
208
209
210
211
212
cursor->driverdata = curdata;
return cursor;
cleanup:
213
if (buffer) {
214
215
SDL_free(buffer);
}
216
if (cursor) {
217
218
SDL_free(cursor);
}
219
220
if (curdata) {
if (curdata->bo) {
221
222
223
224
225
226
227
KMSDRM_gbm_bo_destroy(curdata->bo);
}
SDL_free(curdata);
}
return NULL;
}
228
229
230
231
232
/* Show the specified cursor, or hide if cursor is NULL.
cur_cursor is the current cursor, and cursor is the new cursor.
A cursor is displayed on a display, so we have to add a pointer to dispdata
to the driverdata
*/
233
234
235
static int
KMSDRM_ShowCursor(SDL_Cursor * cursor)
{
236
237
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
//SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
238
239
240
SDL_Mouse *mouse;
KMSDRM_CursorData *curdata;
SDL_VideoDisplay *display = NULL;
241
SDL_DisplayData *dispdata = NULL;
242
243
KMSDRM_FBInfo *fb;
KMSDRM_PlaneInfo info = {0};
244
245
mouse = SDL_GetMouse();
246
if (!mouse) {
247
248
249
return SDL_SetError("No mouse.");
}
250
if (mouse->focus) {
251
display = SDL_GetDisplayForWindow(mouse->focus);
252
253
if (display) {
dispdata = (SDL_DisplayData*) display->driverdata;
254
255
256
}
}
257
258
259
/**********************************/
/* if cursor == NULL, HIDE cursor */
/**********************************/
260
if (!cursor) {
261
/* Hide CURRENT cursor, a cursor that is already on screen
262
and SDL is stored in mouse->cur_cursor. */
263
if (mouse->cur_cursor && mouse->cur_cursor->driverdata) {
264
265
266
267
268
if (dispdata && dispdata->cursor_plane) {
info.plane = dispdata->cursor_plane; /* The rest of the members are zeroed. */
drm_atomic_set_plane_props(&info);
if (drm_atomic_commit(display->device, SDL_TRUE))
return SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
269
}
270
271
272
273
return 0;
}
return SDL_SetError("Couldn't find cursor to hide.");
}
274
275
276
277
/************************************************/
/* If cursor != NULL, DO show cursor on display */
/************************************************/
278
if (!display) {
279
280
return SDL_SetError("Could not get display for mouse.");
}
281
if (!dispdata) {
282
283
return SDL_SetError("Could not get display driverdata.");
}
284
285
286
if (!dispdata->cursor_plane) {
return SDL_SetError("Hardware cursor plane not initialized.");
}
287
288
curdata = (KMSDRM_CursorData *) cursor->driverdata;
289
290
if (!curdata || !curdata->bo) {
291
292
293
return SDL_SetError("Cursor not initialized properly.");
}
294
curdata->crtc_id = dispdata->crtc->crtc->crtc_id;
295
296
297
298
299
300
301
302
303
304
305
306
307
308
curdata->plane = dispdata->cursor_plane;
curdata->video = video_device;
fb = KMSDRM_FBFromBO(curdata->video, curdata->bo);
info.plane = dispdata->cursor_plane;
info.crtc_id = curdata->crtc_id;
info.fb_id = fb->fb_id;
info.src_w = curdata->w;
info.src_h = curdata->h;
info.crtc_x = mouse->x - curdata->hot_x;
info.crtc_y = mouse->y - curdata->hot_y;
info.crtc_w = curdata->w;
info.crtc_h = curdata->h;
309
310
drm_atomic_set_plane_props(&info);
311
312
313
if (drm_atomic_commit(display->device, SDL_TRUE)) {
return SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
314
315
316
317
}
return 0;
}
318
/* Unset the cursor from the cursor plane, and ONLY WHEN THAT'S DONE,
319
320
DONE FOR REAL, and not only requested, destroy it by destroying the curso BO.
Destroying the cursor BO is an special an delicate situation,
321
because drm_atomic_set_plane_props() returns immediately, and we DON'T
322
want to get to gbm_bo_destroy() before the prop changes requested
323
in drm_atomic_set_plane_props() have effectively been done. So we
324
325
326
327
issue a BLOCKING atomic_commit here to avoid that situation.
REMEMBER you yan issue an atomic_commit whenever you want, and
the changes requested until that moment (for any planes, crtcs, etc.)
will be done. */
328
329
330
static void
KMSDRM_FreeCursor(SDL_Cursor * cursor)
{
331
KMSDRM_CursorData *curdata = NULL;
332
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
333
KMSDRM_PlaneInfo info = {0};
334
if (cursor) {
335
curdata = (KMSDRM_CursorData *) cursor->driverdata;
336
if (video_device && curdata->bo && curdata->plane) {
337
338
info.plane = curdata->plane; /* The other members are zeroed. */
drm_atomic_set_plane_props(&info);
339
/* Wait until the cursor is unset from the cursor plane before destroying it's BO. */
340
341
342
if (drm_atomic_commit(video_device, SDL_TRUE)) {
SDL_SetError("Failed atomic commit in KMSDRM_FreeCursor.");
}
343
344
KMSDRM_gbm_bo_destroy(curdata->bo);
curdata->bo = NULL;
345
}
346
347
348
349
/* Even if the cursor is not ours, free it. */
SDL_free(cursor->driverdata);
SDL_free(cursor);
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
}
}
/* Warp the mouse to (x,y) */
static void
KMSDRM_WarpMouse(SDL_Window * window, int x, int y)
{
/* Only one global/fullscreen window is supported */
KMSDRM_WarpMouseGlobal(x, y);
}
/* Warp the mouse to (x,y) */
static int
KMSDRM_WarpMouseGlobal(int x, int y)
{
KMSDRM_CursorData *curdata;
SDL_Mouse *mouse = SDL_GetMouse();
368
if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
369
370
371
372
/* Update internal mouse position. */
SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
/* And now update the cursor graphic position on screen. */
373
curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
374
if (curdata->bo) {
375
376
377
if (drm_atomic_movecursor(curdata, x, y)) {
return SDL_SetError("drm_atomic_movecursor() failed.");
378
379
}
380
381
382
383
384
385
} else {
return SDL_SetError("Cursor not initialized properly.");
}
} else {
return SDL_SetError("No mouse or current cursor.");
}
386
return 0;
387
388
389
390
391
392
393
394
}
void
KMSDRM_InitMouse(_THIS)
{
/* FIXME: Using UDEV it should be possible to scan all mice
* but there's no point in doing so as there's no multimice support...yet!
*/
395
396
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
397
398
399
400
401
402
403
404
405
SDL_Mouse *mouse = SDL_GetMouse();
mouse->CreateCursor = KMSDRM_CreateCursor;
mouse->ShowCursor = KMSDRM_ShowCursor;
mouse->MoveCursor = KMSDRM_MoveCursor;
mouse->FreeCursor = KMSDRM_FreeCursor;
mouse->WarpMouse = KMSDRM_WarpMouse;
mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal;
406
407
408
409
410
/* Init cursor plane, if we haven't yet. */
if (!dispdata->cursor_plane) {
setup_plane(_this, &(dispdata->cursor_plane), DRM_PLANE_TYPE_CURSOR);
}
411
412
413
414
415
416
SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor());
}
void
KMSDRM_QuitMouse(_THIS)
{
417
418
419
/* Free the plane on which the cursor was being shown. */
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
free_plane(&dispdata->cursor_plane);
420
421
422
423
424
425
426
}
/* This is called when a mouse motion event occurs */
static void
KMSDRM_MoveCursor(SDL_Cursor * cursor)
{
SDL_Mouse *mouse = SDL_GetMouse();
427
428
429
430
KMSDRM_CursorData *curdata;
/* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity!
That's why we move the cursor graphic ONLY. */
431
if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
432
curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
433
434
435
436
437
/* Some programs expect cursor movement even while they don't do SwapWindow() calls,
and since we ride on the atomic_commit() in SwapWindow() for cursor movement,
cursor won't move in these situations. We could do an atomic_commit() for each
cursor movement request, but it cripples the movement to 30FPS, so a future solution
is needed. SDLPoP "QUIT?" menu is an example of this situation. */
438
439
if (drm_atomic_movecursor(curdata, mouse->x, mouse->y)) {
440
441
SDL_SetError("drm_atomic_movecursor() failed.");
}
442
}
443
444
445
446
447
}
#endif /* SDL_VIDEO_DRIVER_KMSDRM */
/* vi: set ts=4 sw=4 expandtab: */