e23dd1310ee6075a259e96668748ac25286c1417
[platform/upstream/SDL.git] / src / video / kmsdrm / SDL_kmsdrmmouse.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2018 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_KMSDRM
25
26 #include "SDL_kmsdrmvideo.h"
27 #include "SDL_kmsdrmmouse.h"
28 #include "SDL_kmsdrmdyn.h"
29
30 #include "../../events/SDL_mouse_c.h"
31 #include "../../events/default_cursor.h"
32
33 static SDL_Cursor *KMSDRM_CreateDefaultCursor(void);
34 static SDL_Cursor *KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y);
35 static int KMSDRM_ShowCursor(SDL_Cursor * cursor);
36 static void KMSDRM_MoveCursor(SDL_Cursor * cursor);
37 static void KMSDRM_FreeCursor(SDL_Cursor * cursor);
38 static void KMSDRM_WarpMouse(SDL_Window * window, int x, int y);
39 static int KMSDRM_WarpMouseGlobal(int x, int y);
40
41 static SDL_Cursor *
42 KMSDRM_CreateDefaultCursor(void)
43 {
44     return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
45 }
46
47 /* Evaluate if a given cursor size is supported or not. Notably, current Intel gfx only support 64x64 and up. */
48 static SDL_bool
49 KMSDRM_IsCursorSizeSupported (int w, int h, uint32_t bo_format) {
50
51     SDL_VideoDevice *dev = SDL_GetVideoDevice();
52     SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
53     int ret;
54     uint32_t bo_handle;
55     struct gbm_bo *bo = KMSDRM_gbm_bo_create(vdata->gbm, w, h, bo_format,
56                                        GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
57
58     if (bo == NULL) {
59         SDL_SetError("Could not create GBM cursor BO width size %dx%d for size testing", w, h);
60         goto cleanup;
61     }
62
63     bo_handle = KMSDRM_gbm_bo_get_handle(bo).u32;
64     ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, vdata->crtc_id, bo_handle, w, h);
65
66     if (ret) {
67         goto cleanup;
68     }
69     else {
70         KMSDRM_gbm_bo_destroy(bo);
71         return SDL_TRUE;
72     }
73
74 cleanup:
75     if (bo != NULL) {
76         KMSDRM_gbm_bo_destroy(bo);
77     }
78     return SDL_FALSE;
79 }
80
81 /* Create a cursor from a surface */
82 static SDL_Cursor *
83 KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
84 {
85     SDL_VideoDevice *dev = SDL_GetVideoDevice();
86     SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
87     SDL_PixelFormat *pixlfmt = surface->format;
88     KMSDRM_CursorData *curdata;
89     SDL_Cursor *cursor;
90     SDL_bool cursor_supported = SDL_FALSE;
91     int i, ret, usable_cursor_w, usable_cursor_h;
92     uint32_t bo_format, bo_stride;
93     char *buffer = NULL;
94     size_t bufsize;
95
96     switch(pixlfmt->format) {
97     case SDL_PIXELFORMAT_RGB332:
98         bo_format = GBM_FORMAT_RGB332;
99         break;
100     case SDL_PIXELFORMAT_ARGB4444:
101         bo_format = GBM_FORMAT_ARGB4444;
102         break;
103     case SDL_PIXELFORMAT_RGBA4444:
104         bo_format = GBM_FORMAT_RGBA4444;
105         break;
106     case SDL_PIXELFORMAT_ABGR4444:
107         bo_format = GBM_FORMAT_ABGR4444;
108         break;
109     case SDL_PIXELFORMAT_BGRA4444:
110         bo_format = GBM_FORMAT_BGRA4444;
111         break;
112     case SDL_PIXELFORMAT_ARGB1555:
113         bo_format = GBM_FORMAT_ARGB1555;
114         break;
115     case SDL_PIXELFORMAT_RGBA5551:
116         bo_format = GBM_FORMAT_RGBA5551;
117         break;
118     case SDL_PIXELFORMAT_ABGR1555:
119         bo_format = GBM_FORMAT_ABGR1555;
120         break;
121     case SDL_PIXELFORMAT_BGRA5551:
122         bo_format = GBM_FORMAT_BGRA5551;
123         break;
124     case SDL_PIXELFORMAT_RGB565:
125         bo_format = GBM_FORMAT_RGB565;
126         break;
127     case SDL_PIXELFORMAT_BGR565:
128         bo_format = GBM_FORMAT_BGR565;
129         break;
130     case SDL_PIXELFORMAT_RGB888:
131     case SDL_PIXELFORMAT_RGB24:
132         bo_format = GBM_FORMAT_RGB888;
133         break;
134     case SDL_PIXELFORMAT_BGR888:
135     case SDL_PIXELFORMAT_BGR24:
136         bo_format = GBM_FORMAT_BGR888;
137         break;
138     case SDL_PIXELFORMAT_RGBX8888:
139         bo_format = GBM_FORMAT_RGBX8888;
140         break;
141     case SDL_PIXELFORMAT_BGRX8888:
142         bo_format = GBM_FORMAT_BGRX8888;
143         break;
144     case SDL_PIXELFORMAT_ARGB8888:
145         bo_format = GBM_FORMAT_ARGB8888;
146         break;
147     case SDL_PIXELFORMAT_RGBA8888:
148         bo_format = GBM_FORMAT_RGBA8888;
149         break;
150     case SDL_PIXELFORMAT_ABGR8888:
151         bo_format = GBM_FORMAT_ABGR8888;
152         break;
153     case SDL_PIXELFORMAT_BGRA8888:
154         bo_format = GBM_FORMAT_BGRA8888;
155         break;
156     case SDL_PIXELFORMAT_ARGB2101010:
157         bo_format = GBM_FORMAT_ARGB2101010;
158         break;
159     default:
160         SDL_SetError("Unsupported pixel format for cursor");
161         return NULL;
162     }
163
164     if (!KMSDRM_gbm_device_is_format_supported(vdata->gbm, bo_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
165         SDL_SetError("Unsupported pixel format for cursor");
166         return NULL;
167     }
168
169     cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
170     if (cursor == NULL) {
171         SDL_OutOfMemory();
172         return NULL;
173     }
174     curdata = (KMSDRM_CursorData *) SDL_calloc(1, sizeof(*curdata));
175     if (curdata == NULL) {
176         SDL_OutOfMemory();
177         SDL_free(cursor);
178         return NULL;
179     }
180
181     /* We have to know beforehand if a cursor with the same size as the surface is supported.
182      * If it's not, we have to find an usable cursor size and use an intermediate and clean buffer.
183      * If we can't find a cursor size supported by the hardware, we won't go on trying to 
184      * call SDL_SetCursor() later. */
185
186     usable_cursor_w = surface->w;
187     usable_cursor_h = surface->h;
188
189     while (usable_cursor_w <= MAX_CURSOR_W && usable_cursor_h <= MAX_CURSOR_H) { 
190         if (KMSDRM_IsCursorSizeSupported(usable_cursor_w, usable_cursor_h, bo_format)) {
191             cursor_supported = SDL_TRUE;
192             break;
193         }
194         usable_cursor_w += usable_cursor_w;
195         usable_cursor_h += usable_cursor_h;
196     }
197
198     if (!cursor_supported) {
199         SDL_SetError("Could not find a cursor size supported by the kernel driver");
200         goto cleanup;
201     }
202
203     curdata->hot_x = hot_x;
204     curdata->hot_y = hot_y;
205     curdata->w = usable_cursor_w;
206     curdata->h = usable_cursor_h;
207
208     curdata->bo = KMSDRM_gbm_bo_create(vdata->gbm, usable_cursor_w, usable_cursor_h, bo_format,
209                                        GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
210
211     if (curdata->bo == NULL) {
212         SDL_SetError("Could not create GBM cursor BO");
213         goto cleanup;
214     }
215
216     bo_stride = KMSDRM_gbm_bo_get_stride(curdata->bo);
217     bufsize = bo_stride * curdata->h;
218
219     if (surface->pitch != bo_stride) {
220         /* pitch doesn't match stride, must be copied to temp buffer  */
221         buffer = SDL_malloc(bufsize);
222         if (buffer == NULL) {
223             SDL_OutOfMemory();
224             goto cleanup;
225         }
226
227         if (SDL_MUSTLOCK(surface)) {
228             if (SDL_LockSurface(surface) < 0) {
229                 /* Could not lock surface */
230                 goto cleanup;
231             }
232         }
233
234         /* Clean the whole temporary buffer */
235         SDL_memset(buffer, 0x00, bo_stride * curdata->h);
236
237         /* Copy to temporary buffer */
238         for (i = 0; i < surface->h; i++) {
239             SDL_memcpy(buffer + (i * bo_stride),
240                        ((char *)surface->pixels) + (i * surface->pitch),
241                        surface->w * pixlfmt->BytesPerPixel);
242         }
243
244         if (SDL_MUSTLOCK(surface)) {
245             SDL_UnlockSurface(surface);
246         }
247
248         if (KMSDRM_gbm_bo_write(curdata->bo, buffer, bufsize)) {
249             SDL_SetError("Could not write to GBM cursor BO");
250             goto cleanup;
251         }
252
253         /* Free temporary buffer */
254         SDL_free(buffer);
255         buffer = NULL;
256     } else {
257         /* surface matches BO format */
258         if (SDL_MUSTLOCK(surface)) {
259             if (SDL_LockSurface(surface) < 0) {
260                 /* Could not lock surface */
261                 goto cleanup;
262             }
263         }
264
265         ret = KMSDRM_gbm_bo_write(curdata->bo, surface->pixels, bufsize);
266
267         if (SDL_MUSTLOCK(surface)) {
268             SDL_UnlockSurface(surface);
269         }
270
271         if (ret) {
272             SDL_SetError("Could not write to GBM cursor BO");
273             goto cleanup;
274         }
275     }
276
277     cursor->driverdata = curdata;
278
279     return cursor;
280
281 cleanup:
282     if (buffer != NULL) {
283         SDL_free(buffer);
284     }
285     if (cursor != NULL) {
286         SDL_free(cursor);
287     }
288     if (curdata != NULL) {
289         if (curdata->bo != NULL) {
290             KMSDRM_gbm_bo_destroy(curdata->bo);
291         }
292         SDL_free(curdata);
293     }
294     return NULL;
295 }
296
297 /* Show the specified cursor, or hide if cursor is NULL */
298 static int
299 KMSDRM_ShowCursor(SDL_Cursor * cursor)
300 {
301     SDL_VideoDevice *dev = SDL_GetVideoDevice();
302     SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
303     SDL_Mouse *mouse;
304     KMSDRM_CursorData *curdata;
305     SDL_VideoDisplay *display = NULL;
306     SDL_DisplayData *ddata = NULL;
307     int ret;
308     uint32_t bo_handle;
309
310     mouse = SDL_GetMouse();
311     if (mouse == NULL) {
312         return SDL_SetError("No mouse.");
313     }
314
315     if (mouse->focus != NULL) {
316         display = SDL_GetDisplayForWindow(mouse->focus);
317         if (display != NULL) {
318             ddata = (SDL_DisplayData*) display->driverdata;
319         }
320     }
321
322     if (cursor == NULL) {
323         /* Hide current cursor */
324         if ( mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
325             curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
326
327             if (curdata->crtc_id != 0) {
328                 ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, curdata->crtc_id, 0, 0, 0);
329                 if (ret) {
330                     SDL_SetError("Could not hide current cursor with drmModeSetCursor().");
331                     return ret;
332                 }
333                 /* Mark previous cursor as not-displayed */
334                 curdata->crtc_id = 0;
335
336                 return 0;
337             }
338         }
339         /* otherwise if possible, hide global cursor */
340         if (ddata != NULL && ddata->crtc_id != 0) {
341             ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, ddata->crtc_id, 0, 0, 0);
342             if (ret) {
343                 SDL_SetError("Could not hide display's cursor with drmModeSetCursor().");
344                 return ret;
345             }
346             return 0;
347         }
348
349         return SDL_SetError("Couldn't find cursor to hide.");
350     }
351     /* If cursor != NULL, show new cursor on display */
352     if (display == NULL) {
353         return SDL_SetError("Could not get display for mouse.");
354     }
355     if (ddata == NULL) {
356         return SDL_SetError("Could not get display driverdata.");
357     }
358
359     curdata = (KMSDRM_CursorData *) cursor->driverdata;
360     if (curdata == NULL || curdata->bo == NULL) {
361         return SDL_SetError("Cursor not initialized properly.");
362     }
363
364     bo_handle = KMSDRM_gbm_bo_get_handle(curdata->bo).u32;
365     if (curdata->hot_x == 0 && curdata->hot_y == 0) {
366         ret = KMSDRM_drmModeSetCursor(vdata->drm_fd, ddata->crtc_id, bo_handle,
367                                       curdata->w, curdata->h);
368     } else {
369         ret = KMSDRM_drmModeSetCursor2(vdata->drm_fd, ddata->crtc_id, bo_handle,
370                                        curdata->w, curdata->h,
371                                        curdata->hot_x, curdata->hot_y);
372     }
373     if (ret) {
374         SDL_SetError("drmModeSetCursor failed.");
375         return ret;
376     }
377
378     curdata->crtc_id = ddata->crtc_id;
379
380     return 0;
381 }
382
383 /* Free a window manager cursor */
384 static void
385 KMSDRM_FreeCursor(SDL_Cursor * cursor)
386 {
387     KMSDRM_CursorData *curdata;
388     int drm_fd;
389
390     if (cursor != NULL) {
391         curdata = (KMSDRM_CursorData *) cursor->driverdata;
392
393         if (curdata != NULL) {
394             if (curdata->bo != NULL) {
395                 if (curdata->crtc_id != 0) {
396                     drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
397                     /* Hide the cursor if previously shown on a CRTC */
398                     KMSDRM_drmModeSetCursor(drm_fd, curdata->crtc_id, 0, 0, 0);
399                     curdata->crtc_id = 0;
400                 }
401                 KMSDRM_gbm_bo_destroy(curdata->bo);
402                 curdata->bo = NULL;
403             }
404             SDL_free(cursor->driverdata);
405         }
406         SDL_free(cursor);
407     }
408 }
409
410 /* Warp the mouse to (x,y) */
411 static void
412 KMSDRM_WarpMouse(SDL_Window * window, int x, int y)
413 {
414     /* Only one global/fullscreen window is supported */
415     KMSDRM_WarpMouseGlobal(x, y);
416 }
417
418 /* Warp the mouse to (x,y) */
419 static int
420 KMSDRM_WarpMouseGlobal(int x, int y)
421 {
422     KMSDRM_CursorData *curdata;
423     SDL_Mouse *mouse = SDL_GetMouse();
424
425     if (mouse != NULL && mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
426         /* Update internal mouse position. */
427         SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
428
429         /* And now update the cursor graphic position on screen. */
430         curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
431         if (curdata->bo != NULL) {
432
433             if (curdata->crtc_id != 0) {
434                 int ret, drm_fd;
435                 drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
436                 ret = KMSDRM_drmModeMoveCursor(drm_fd, curdata->crtc_id, x, y);
437
438                 if (ret) {
439                     SDL_SetError("drmModeMoveCursor() failed.");
440                 }
441
442                 return ret;
443             } else {
444                 return SDL_SetError("Cursor is not currently shown.");
445             }
446         } else {
447             return SDL_SetError("Cursor not initialized properly.");
448         }
449     } else {
450         return SDL_SetError("No mouse or current cursor.");
451     }
452 }
453
454 void
455 KMSDRM_InitMouse(_THIS)
456 {
457     /* FIXME: Using UDEV it should be possible to scan all mice
458      * but there's no point in doing so as there's no multimice support...yet!
459      */
460     SDL_Mouse *mouse = SDL_GetMouse();
461
462     mouse->CreateCursor = KMSDRM_CreateCursor;
463     mouse->ShowCursor = KMSDRM_ShowCursor;
464     mouse->MoveCursor = KMSDRM_MoveCursor;
465     mouse->FreeCursor = KMSDRM_FreeCursor;
466     mouse->WarpMouse = KMSDRM_WarpMouse;
467     mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal;
468
469     SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor());
470 }
471
472 void
473 KMSDRM_QuitMouse(_THIS)
474 {
475     /* TODO: ? */
476 }
477
478 /* This is called when a mouse motion event occurs */
479 static void
480 KMSDRM_MoveCursor(SDL_Cursor * cursor)
481 {
482     SDL_Mouse *mouse = SDL_GetMouse();
483     KMSDRM_CursorData *curdata;
484     int drm_fd, ret;
485
486     /* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity!
487        That's why we move the cursor graphic ONLY. */
488     if (mouse != NULL && mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
489         curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
490         drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
491         ret = KMSDRM_drmModeMoveCursor(drm_fd, curdata->crtc_id, mouse->x, mouse->y);
492
493         if (ret) {
494             SDL_SetError("drmModeMoveCursor() failed.");
495         }
496     }
497 }
498
499 #endif /* SDL_VIDEO_DRIVER_KMSDRM */
500
501 /* vi: set ts=4 sw=4 expandtab: */