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