Atlas - SDL_kmsdrmmouse.c

Home / ext / SDL / src / video / kmsdrm Lines: 1 | Size: 18995 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 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#ifdef 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#include "../SDL_pixels_c.h" 34 35// !!! FIXME: atomic cursors are broken right now. 36#define USE_ATOMIC_CURSOR 0 37 38static SDL_Cursor *KMSDRM_CreateDefaultCursor(void); 39static SDL_Cursor *KMSDRM_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y); 40static bool KMSDRM_ShowCursor(SDL_Cursor *cursor); 41static bool KMSDRM_MoveCursor(SDL_Cursor *cursor); 42static void KMSDRM_FreeCursor(SDL_Cursor *cursor); 43 44/**************************************************************************************/ 45// BEFORE CODING ANYTHING MOUSE/CURSOR RELATED, REMEMBER THIS. 46// How does SDL manage cursors internally? First, mouse != cursor. The mouse can have 47// many cursors in mouse->cursors. 48// -SDL tells us to create a cursor with KMSDRM_CreateCursor(). It can create many 49// cursors with this, not only one. 50// -SDL stores those cursors in a cursors array, in mouse->cursors. 51// -Whenever it wants (or the programmer wants) takes a cursor from that array 52// and shows it on screen with KMSDRM_ShowCursor(). 53// KMSDRM_ShowCursor() simply shows or hides the cursor it receives: it does NOT 54// mind if it's mouse->cur_cursor, etc. 55// -If KMSDRM_ShowCursor() returns successfully, that cursor becomes 56// mouse->cur_cursor and mouse->cursor_visible is 1. 57/**************************************************************************************/ 58 59static SDL_Cursor *KMSDRM_CreateDefaultCursor(void) 60{ 61 return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY); 62} 63 64/* Given a display's internal, destroy the cursor BO for it. 65 To be called from KMSDRM_DestroyWindow(), as that's where we 66 destroy the internal for the window's display. */ 67void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display) 68{ 69 SDL_DisplayData *dispdata = display->internal; 70 71 // Destroy the curso GBM BO. 72 if (dispdata->cursor_bo) { 73 SDL_VideoData *viddata = (SDL_VideoData *) _this->internal; 74 if (USE_ATOMIC_CURSOR && viddata->is_atomic) { 75 if (dispdata->cursor_plane) { 76 // Unset the the cursor BO from the cursor plane. 77 KMSDRM_PlaneInfo info; 78 SDL_zero(info); 79 info.plane = dispdata->cursor_plane; 80 drm_atomic_set_plane_props(dispdata, &info); 81 // Wait until the cursor is unset from the cursor plane before destroying it's BO. 82 if (drm_atomic_commit(_this, dispdata, true, false)) { 83 SDL_SetError("Failed atomic commit in KMSDRM_DenitMouse."); 84 } 85 // Free the cursor plane, on which the cursor was being shown. 86 free_plane(&dispdata->cursor_plane); 87 } 88 } 89 90 KMSDRM_gbm_bo_destroy(dispdata->cursor_bo); 91 dispdata->cursor_bo = NULL; 92 dispdata->cursor_bo_drm_fd = -1; 93 } 94} 95 96/* Given a display's internal, create the cursor BO for it. 97 To be called from KMSDRM_CreateWindow(), as that's where we 98 build a window and assign a display to it. */ 99bool KMSDRM_CreateCursorBO(SDL_VideoDisplay *display) 100{ 101 SDL_VideoDevice *dev = SDL_GetVideoDevice(); 102 SDL_VideoData *viddata = dev->internal; 103 SDL_DisplayData *dispdata = display->internal; 104 105 if (USE_ATOMIC_CURSOR && viddata->is_atomic) { 106 setup_plane(dev, dispdata, &dispdata->cursor_plane, DRM_PLANE_TYPE_CURSOR); 107 } 108 109 if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, 110 GBM_FORMAT_ARGB8888, 111 GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) { 112 return SDL_SetError("Unsupported pixel format for cursor"); 113 } 114 115 if (KMSDRM_drmGetCap(viddata->drm_fd, 116 DRM_CAP_CURSOR_WIDTH, &dispdata->cursor_w) || 117 KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT, 118 &dispdata->cursor_h)) { 119 return SDL_SetError("Could not get the recommended GBM cursor size"); 120 } 121 122 if (dispdata->cursor_w == 0 || dispdata->cursor_h == 0) { 123 return SDL_SetError("Could not get an usable GBM cursor size"); 124 } 125 126 dispdata->cursor_bo = KMSDRM_gbm_bo_create(viddata->gbm_dev, 127 dispdata->cursor_w, dispdata->cursor_h, 128 GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE | GBM_BO_USE_LINEAR); 129 130 if (!dispdata->cursor_bo) { 131 return SDL_SetError("Could not create GBM cursor BO"); 132 } 133 134 dispdata->cursor_bo_drm_fd = viddata->drm_fd; 135 return true; 136} 137 138// Remove a cursor buffer from a display's DRM cursor BO. 139static bool KMSDRM_RemoveCursorFromBO(SDL_VideoDisplay *display) 140{ 141 bool result = true; 142 143 SDL_DisplayData *dispdata = display->internal; 144 SDL_VideoDevice *video_device = SDL_GetVideoDevice(); 145 SDL_VideoData *viddata = video_device->internal; 146 147 if (USE_ATOMIC_CURSOR && viddata->is_atomic) { 148 if (dispdata->cursor_plane) { 149 KMSDRM_PlaneInfo info; 150 SDL_zero(info); 151 info.plane = dispdata->cursor_plane; 152 // The rest of the members are zeroed, so this takes away the cursor from the cursor plane. 153 drm_atomic_set_plane_props(dispdata, &info); 154 if (drm_atomic_commit(video_device, dispdata, true, false)) { 155 result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor."); 156 } 157 } 158 } else { 159 const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, 0, 0, 0); 160 if (rc < 0) { 161 result = SDL_SetError("drmModeSetCursor() failed: %s", strerror(-rc)); 162 } 163 } 164 165 return result; 166} 167 168// Dump a cursor buffer to a display's DRM cursor BO. 169static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Mouse *mouse, SDL_Cursor *cursor) 170{ 171 SDL_DisplayData *dispdata = display->internal; 172 SDL_CursorData *curdata = cursor->internal; 173 SDL_VideoDevice *video_device = SDL_GetVideoDevice(); 174 SDL_VideoData *viddata = video_device->internal; 175 176 uint32_t bo_handle; 177 size_t bo_stride; 178 size_t bufsize; 179 uint8_t *ready_buffer = NULL; 180 uint8_t *src_row; 181 182 int i, rc; 183 bool result = true; 184 185 if (!curdata || !dispdata->cursor_bo) { 186 return SDL_SetError("Cursor or display not initialized properly."); 187 } 188 189 /* Prepare a buffer we can dump to our GBM BO (different 190 size, alpha premultiplication...) */ 191 bo_stride = KMSDRM_gbm_bo_get_stride(dispdata->cursor_bo); 192 bufsize = bo_stride * dispdata->cursor_h; 193 194 ready_buffer = (uint8_t *)SDL_calloc(1, bufsize); 195 196 if (!ready_buffer) { 197 result = false; 198 goto cleanup; 199 } 200 201 // Copy from the cursor buffer to a buffer that we can dump to the GBM BO. 202 for (i = 0; i < curdata->h; i++) { 203 src_row = &((uint8_t *)curdata->buffer)[i * curdata->w * 4]; 204 SDL_memcpy(ready_buffer + (i * bo_stride), src_row, (size_t)4 * curdata->w); 205 } 206 207 // Dump the cursor buffer to our GBM BO. 208 if (KMSDRM_gbm_bo_write(dispdata->cursor_bo, ready_buffer, bufsize)) { 209 result = SDL_SetError("Could not write to GBM cursor BO"); 210 goto cleanup; 211 } 212 213 if (USE_ATOMIC_CURSOR && viddata->is_atomic) { 214 // Get the fb_id for the GBM BO so we can show it on the cursor plane. 215 KMSDRM_FBInfo *fb = KMSDRM_FBFromBO(video_device, dispdata->cursor_bo); 216 KMSDRM_PlaneInfo info; 217 218 if (!fb) { 219 result = SDL_SetError("Failed to get cursor FB from BO"); 220 goto cleanup; 221 } 222 223 // Show the GBM BO buffer on the cursor plane. 224 SDL_zero(info); 225 info.plane = dispdata->cursor_plane; 226 info.crtc_id = dispdata->crtc.crtc->crtc_id; 227 info.fb_id = fb->fb_id; 228 info.src_w = dispdata->cursor_w; 229 info.src_h = dispdata->cursor_h; 230 info.crtc_x = ((int32_t) SDL_roundf(mouse->x)) - curdata->hot_x; 231 info.crtc_y = ((int32_t) SDL_roundf(mouse->y)) - curdata->hot_y; 232 info.crtc_w = curdata->w; 233 info.crtc_h = curdata->h; 234 drm_atomic_set_plane_props(dispdata, &info); 235 if (drm_atomic_commit(video_device, dispdata, true, false)) { 236 result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor."); 237 goto cleanup; 238 } 239 } else { 240 // Put the GBM BO buffer on screen using the DRM interface. 241 bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32; 242 if (curdata->hot_x == 0 && curdata->hot_y == 0) { 243 rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, bo_handle, dispdata->cursor_w, dispdata->cursor_h); 244 } else { 245 rc = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y); 246 } 247 if (rc < 0) { 248 result = SDL_SetError("Failed to set DRM cursor: %s", strerror(-rc)); 249 goto cleanup; 250 } 251 } 252 253cleanup: 254 SDL_free(ready_buffer); 255 return result; 256} 257 258// This is only for freeing the SDL_cursor. 259static void KMSDRM_FreeCursor(SDL_Cursor *cursor) 260{ 261 SDL_CursorData *curdata; 262 263 // Even if the cursor is not ours, free it. 264 if (cursor) { 265 curdata = cursor->internal; 266 // Free cursor buffer 267 if (curdata->buffer) { 268 SDL_free(curdata->buffer); 269 curdata->buffer = NULL; 270 } 271 // Free cursor itself 272 SDL_free(cursor->internal); 273 SDL_free(cursor); 274 } 275} 276 277/* This simply gets the cursor soft-buffer ready. 278 We don't copy it to a GBO BO until ShowCursor() because the cusor GBM BO (living 279 in dispata) is destroyed and recreated when we recreate windows, etc. */ 280static SDL_Cursor *KMSDRM_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y) 281{ 282 SDL_CursorData *curdata; 283 SDL_Cursor *cursor, *result; 284 285 curdata = NULL; 286 result = NULL; 287 288 cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor)); 289 if (!cursor) { 290 goto cleanup; 291 } 292 curdata = (SDL_CursorData *)SDL_calloc(1, sizeof(*curdata)); 293 if (!curdata) { 294 goto cleanup; 295 } 296 297 // hox_x and hot_y are the coordinates of the "tip of the cursor" from it's base. 298 curdata->hot_x = hot_x; 299 curdata->hot_y = hot_y; 300 curdata->w = surface->w; 301 curdata->h = surface->h; 302 curdata->buffer = NULL; 303 304 /* Configure the cursor buffer info. 305 This buffer has the original size of the cursor surface we are given. */ 306 curdata->buffer_pitch = surface->w; 307 curdata->buffer_size = (size_t)surface->w * surface->h * 4; 308 curdata->buffer = (uint32_t *)SDL_malloc(curdata->buffer_size); 309 310 if (!curdata->buffer) { 311 goto cleanup; 312 } 313 314 /* All code below assumes ARGB8888 format for the cursor surface, 315 like other backends do. Also, the GBM BO pixels have to be 316 alpha-premultiplied, but the SDL surface we receive has 317 straight-alpha pixels, so we always have to convert. */ 318 SDL_PremultiplyAlpha(surface->w, surface->h, 319 surface->format, surface->pixels, surface->pitch, 320 SDL_PIXELFORMAT_ARGB8888, curdata->buffer, surface->w * 4, true); 321 322 cursor->internal = curdata; 323 324 result = cursor; 325 326cleanup: 327 if (!result) { 328 if (curdata) { 329 SDL_free(curdata->buffer); 330 SDL_free(curdata); 331 } 332 SDL_free(cursor); 333 } 334 335 return result; 336} 337 338// Show the specified cursor, or hide if cursor is NULL or has no focus. 339static bool KMSDRM_ShowCursor(SDL_Cursor *cursor) 340{ 341 SDL_VideoDisplay *display; 342 SDL_Window *window; 343 SDL_Mouse *mouse = SDL_GetMouse(); 344 345 int i; 346 bool result = true; 347 348 // Get the mouse focused window, if any. 349 window = mouse->focus; 350 351 if (!window || !cursor) { 352 /* If no window is focused by mouse or cursor is NULL, 353 since we have no window (no mouse->focus) and hence 354 we have no display, we simply hide mouse on all displays. 355 This happens on video quit, where we get here after 356 the mouse focus has been unset, yet SDL wants to 357 restore the system default cursor (makes no sense here). */ 358 SDL_DisplayID *displays = SDL_GetDisplays(NULL); 359 if (displays) { 360 // Iterate on the displays, hiding the cursor. 361 for (i = 0; i < displays[i]; i++) { 362 display = SDL_GetVideoDisplay(displays[i]); 363 result = KMSDRM_RemoveCursorFromBO(display); 364 } 365 SDL_free(displays); 366 } 367 } else { 368 display = SDL_GetVideoDisplayForWindow(window); 369 if (display) { 370 if (cursor) { 371 /* Dump the cursor to the display DRM cursor BO so it becomes visible 372 on that display. */ 373 result = KMSDRM_DumpCursorToBO(display, mouse, cursor); 374 } else { 375 // Hide the cursor on that display. 376 result = KMSDRM_RemoveCursorFromBO(display); 377 } 378 } 379 } 380 381 return result; 382} 383 384static void drm_atomic_movecursor(SDL_DisplayData *dispdata, const SDL_CursorData *curdata, uint16_t x, uint16_t y) 385{ 386 if (dispdata->cursor_plane) { // We can't move a non-existing cursor, but that's ok. 387 // Do we have a set of changes already in the making? If not, allocate a new one. 388 if (!dispdata->atomic_req) { 389 dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc(); 390 } 391 add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_X", x - curdata->hot_x); 392 add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_Y", y - curdata->hot_y); 393 } 394} 395 396static bool KMSDRM_WarpMouseGlobal(float x, float y) 397{ 398 SDL_Mouse *mouse = SDL_GetMouse(); 399 400 if (mouse && mouse->cur_cursor && mouse->focus) { 401 SDL_Window *window = mouse->focus; 402 SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); 403 404 // Update internal mouse position. 405 SDL_SendMouseMotion(0, mouse->focus, SDL_GLOBAL_MOUSE_ID, false, x, y); 406 407 // And now update the cursor graphic position on screen. 408 if (dispdata->cursor_bo) { 409 SDL_VideoDevice *dev = SDL_GetVideoDevice(); 410 SDL_VideoData *viddata = dev->internal; 411 if (USE_ATOMIC_CURSOR && viddata->is_atomic) { 412 const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal; 413 drm_atomic_movecursor(dispdata, curdata, (uint16_t) (int) x, (uint16_t) (int) y); 414 } else { 415 const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)x, (int)y); 416 if (rc < 0) { 417 return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc)); 418 } 419 } 420 } else { 421 return SDL_SetError("Cursor not initialized properly."); 422 } 423 } else { 424 return SDL_SetError("No mouse or current cursor."); 425 } 426 427 return true; 428} 429 430static bool KMSDRM_WarpMouse(SDL_Window *window, float x, float y) 431{ 432 // Only one global/fullscreen window is supported 433 return KMSDRM_WarpMouseGlobal(x, y); 434} 435 436void KMSDRM_InitMouse(SDL_VideoDevice *_this, SDL_VideoDisplay *display) 437{ 438 SDL_Mouse *mouse = SDL_GetMouse(); 439 SDL_DisplayData *dispdata = display->internal; 440 441 mouse->CreateCursor = KMSDRM_CreateCursor; 442 mouse->ShowCursor = KMSDRM_ShowCursor; 443 mouse->MoveCursor = KMSDRM_MoveCursor; 444 mouse->FreeCursor = KMSDRM_FreeCursor; 445 mouse->WarpMouse = KMSDRM_WarpMouse; 446 mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal; 447 448 /* Only create the default cursor for this display if we haven't done so before, 449 we don't want several cursors to be created for the same display. */ 450 if (!dispdata->default_cursor_init) { 451 SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor()); 452 dispdata->default_cursor_init = true; 453 } 454} 455 456void KMSDRM_QuitMouse(SDL_VideoDevice *_this) 457{ 458 // TODO: ? 459} 460 461// This is called when a mouse motion event occurs 462static bool KMSDRM_MoveCursor(SDL_Cursor *cursor) 463{ 464 SDL_Mouse *mouse = SDL_GetMouse(); 465 466 /* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity! 467 That's why we move the cursor graphic ONLY. */ 468 if (mouse && mouse->cur_cursor && mouse->focus) { 469 SDL_Window *window = mouse->focus; 470 SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window); 471 SDL_VideoDevice *dev = SDL_GetVideoDevice(); 472 SDL_VideoData *viddata = dev->internal; 473 474 if (!dispdata->cursor_bo) { 475 return SDL_SetError("Cursor not initialized properly."); 476 } 477 478 if (USE_ATOMIC_CURSOR && viddata->is_atomic) { 479 /* !!! FIXME: Some programs expect cursor movement even while they don't do SwapWindow() calls, 480 and since we ride on the atomic_commit() in SwapWindow() for cursor movement, 481 cursor won't move in these situations. We could do an atomic_commit() here 482 for each cursor movement request, but it cripples the movement to 30FPS, 483 so a future solution is needed. SDLPoP "QUIT?" menu is an example of this 484 situation. */ 485 const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal; 486 drm_atomic_movecursor(dispdata, curdata, (uint16_t) (int) mouse->x, (uint16_t) (int) mouse->y); 487 } else { 488 const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)mouse->x, (int)mouse->y); 489 if (rc < 0) { 490 return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc)); 491 } 492 } 493 } 494 return true; 495} 496 497#endif // SDL_VIDEO_DRIVER_KMSDRM 498
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.