SDL  2.0
SDL_kmsdrmmouse.c
Go to the documentation of this file.
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 {
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 
52  SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
53  int ret;
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 {
86  SDL_VideoData *vdata = ((SDL_VideoData *)dev->driverdata);
87  SDL_PixelFormat *pixlfmt = surface->format;
88  KMSDRM_CursorData *curdata;
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) {
98  bo_format = GBM_FORMAT_RGB332;
99  break;
101  bo_format = GBM_FORMAT_ARGB4444;
102  break;
104  bo_format = GBM_FORMAT_RGBA4444;
105  break;
107  bo_format = GBM_FORMAT_ABGR4444;
108  break;
110  bo_format = GBM_FORMAT_BGRA4444;
111  break;
113  bo_format = GBM_FORMAT_ARGB1555;
114  break;
116  bo_format = GBM_FORMAT_RGBA5551;
117  break;
119  bo_format = GBM_FORMAT_ABGR1555;
120  break;
122  bo_format = GBM_FORMAT_BGRA5551;
123  break;
125  bo_format = GBM_FORMAT_RGB565;
126  break;
128  bo_format = GBM_FORMAT_BGR565;
129  break;
132  bo_format = GBM_FORMAT_RGB888;
133  break;
136  bo_format = GBM_FORMAT_BGR888;
137  break;
139  bo_format = GBM_FORMAT_RGBX8888;
140  break;
142  bo_format = GBM_FORMAT_BGRX8888;
143  break;
145  bo_format = GBM_FORMAT_ARGB8888;
146  break;
148  bo_format = GBM_FORMAT_RGBA8888;
149  break;
151  bo_format = GBM_FORMAT_ABGR8888;
152  break;
154  bo_format = GBM_FORMAT_BGRA8888;
155  break;
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 {
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;
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
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
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: */
#define DEFAULT_CWIDTH
SDL_Mouse * SDL_GetMouse(void)
Definition: SDL_mouse.c:112
void KMSDRM_QuitMouse(_THIS)
int(* ShowCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:52
#define SDL_UnlockSurface
#define MAX_CURSOR_H
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
Uint8 BytesPerPixel
Definition: SDL_pixels.h:320
SDL_Window * focus
Definition: SDL_mouse_c.h:77
void KMSDRM_InitMouse(_THIS)
GLfloat GLfloat GLfloat GLfloat h
EGLSurface surface
Definition: eglext.h:248
A collection of pixels used in software blitting.
Definition: SDL_surface.h:69
#define DEFAULT_CHOTY
#define DEFAULT_CHOTX
GLenum GLuint GLsizei bufsize
SDL_MouseID mouseID
Definition: SDL_mouse_c.h:76
SDL_Cursor *(* CreateCursor)(SDL_Surface *surface, int hot_x, int hot_y)
Definition: SDL_mouse_c.h:46
#define SDL_memcpy
int SDL_SendMouseMotion(SDL_Window *window, SDL_MouseID mouseID, int relative, int x, int y)
Definition: SDL_mouse.c:237
void * pixels
Definition: SDL_surface.h:75
#define _THIS
#define SDL_free
GLubyte GLubyte GLubyte GLubyte w
int uint32_t uint32_t uint32_t uint32_t uint32_t int drmModeModeInfoPtr mode int uint32_t uint32_t bo_handle
Definition: SDL_kmsdrmsym.h:55
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
#define DEFAULT_CHEIGHT
void SDL_SetDefaultCursor(SDL_Cursor *cursor)
Definition: SDL_mouse.c:101
int(* WarpMouseGlobal)(int x, int y)
Definition: SDL_mouse_c.h:64
SDL_Cursor * cursor
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
struct gbm_device * gbm
void(* FreeCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:58
#define MAX_CURSOR_W
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:139
GLuint buffer
unsigned int uint32_t
SDL_PixelFormat * format
Definition: SDL_surface.h:72
uint32_t crtc_id
#define SDL_SetError
#define SDL_LockSurface
static const unsigned char default_cmask[]
#define SDL_CreateCursor
#define SDL_calloc
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1073
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
#define SDL_MUSTLOCK(S)
Definition: SDL_surface.h:61
The type used to identify a window.
Definition: SDL_sysvideo.h:73
struct gbm_bo * bo
void(* WarpMouse)(SDL_Window *window, int x, int y)
Definition: SDL_mouse_c.h:61
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:586
#define SDL_malloc
static const unsigned char default_cdata[]
SDL_Cursor * cur_cursor
Definition: SDL_mouse_c.h:101
void(* MoveCursor)(SDL_Cursor *cursor)
Definition: SDL_mouse_c.h:55
static void cleanup(void)
Definition: testfile.c:44
void * driverdata
Definition: SDL_mouse_c.h:33
#define SDL_memset
int uint32_t uint32_t uint32_t uint32_t uint32_t int drmModeModeInfoPtr mode int uint32_t uint32_t uint32_t uint32_t int32_t hot_x
Definition: SDL_kmsdrmsym.h:55