Atlas - SDL_x11mouse.c

Home / ext / SDL / src / video / x11 Lines: 1 | Size: 20850 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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#include "SDL_internal.h" 22 23#ifdef SDL_VIDEO_DRIVER_X11 24 25#include <X11/cursorfont.h> 26#include "SDL_x11video.h" 27#include "SDL_x11mouse.h" 28#include "SDL_x11xinput2.h" 29#include "SDL_x11xtest.h" 30#include "../SDL_video_c.h" 31#include "../../events/SDL_mouse_c.h" 32 33struct SDL_CursorData 34{ 35 Cursor cursor; 36}; 37 38// FIXME: Find a better place to put this... 39static Cursor x11_empty_cursor = None; 40static bool x11_cursor_visible = true; 41 42static SDL_Cursor *sys_cursors[SDL_HITTEST_RESIZE_LEFT + 1]; 43 44static Display *GetDisplay(void) 45{ 46 return SDL_GetVideoDevice()->internal->display; 47} 48 49static Cursor X11_CreateEmptyCursor(void) 50{ 51 if (x11_empty_cursor == None) { 52 Display *display = GetDisplay(); 53 char data[1]; 54 XColor color; 55 Pixmap pixmap; 56 57 SDL_zeroa(data); 58 color.red = color.green = color.blue = 0; 59 pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display), 60 data, 1, 1); 61 if (pixmap) { 62 x11_empty_cursor = X11_XCreatePixmapCursor(display, pixmap, pixmap, 63 &color, &color, 0, 0); 64 X11_XFreePixmap(display, pixmap); 65 } 66 } 67 return x11_empty_cursor; 68} 69 70static void X11_DestroyEmptyCursor(void) 71{ 72 if (x11_empty_cursor != None) { 73 X11_XFreeCursor(GetDisplay(), x11_empty_cursor); 74 x11_empty_cursor = None; 75 } 76} 77 78static SDL_Cursor *X11_CreateCursorAndData(Cursor x11_cursor) 79{ 80 SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor)); 81 if (cursor) { 82 SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data)); 83 if (!data) { 84 SDL_free(cursor); 85 return NULL; 86 } 87 data->cursor = x11_cursor; 88 cursor->internal = data; 89 } 90 return cursor; 91} 92 93#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR 94static Cursor X11_CreateXCursorCursor(SDL_Surface *surface, int hot_x, int hot_y) 95{ 96 Display *display = GetDisplay(); 97 Cursor cursor = None; 98 XcursorImage *image; 99 100 image = X11_XcursorImageCreate(surface->w, surface->h); 101 if (!image) { 102 SDL_OutOfMemory(); 103 return None; 104 } 105 image->xhot = hot_x; 106 image->yhot = hot_y; 107 image->delay = 0; 108 109 SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888); 110 SDL_assert(surface->pitch == surface->w * 4); 111 SDL_memcpy(image->pixels, surface->pixels, (size_t)surface->h * surface->pitch); 112 113 cursor = X11_XcursorImageLoadCursor(display, image); 114 115 X11_XcursorImageDestroy(image); 116 117 return cursor; 118} 119 120static Cursor X11_CreateAnimatedXCursorCursor(SDL_CursorFrameInfo *frames, int num_frames, int hot_x, int hot_y) 121{ 122 Display *display = GetDisplay(); 123 Cursor cursor = None; 124 XcursorImage *image; 125 XcursorImages *images; 126 127 images = X11_XcursorImagesCreate(num_frames); 128 if (!images) { 129 SDL_OutOfMemory(); 130 return None; 131 } 132 133 for (int i = 0; i < num_frames; ++i) { 134 image = X11_XcursorImageCreate(frames[i].surface->w, frames[i].surface->h); 135 if (!image) { 136 SDL_OutOfMemory(); 137 goto cleanup; 138 } 139 image->xhot = hot_x; 140 image->yhot = hot_y; 141 image->delay = frames[i].duration; 142 143 SDL_assert(frames[i].surface->format == SDL_PIXELFORMAT_ARGB8888); 144 SDL_assert(frames[i].surface->pitch == frames[i].surface->w * 4); 145 SDL_memcpy(image->pixels, frames[i].surface->pixels, (size_t)frames[i].surface->h * frames[i].surface->pitch); 146 147 images->images[i] = image; 148 images->nimage++; 149 } 150 151 cursor = X11_XcursorImagesLoadCursor(display, images); 152 153cleanup: 154 X11_XcursorImagesDestroy(images); 155 return cursor; 156} 157#endif // SDL_VIDEO_DRIVER_X11_XCURSOR 158 159static Cursor X11_CreatePixmapCursor(SDL_Surface *surface, int hot_x, int hot_y) 160{ 161 Display *display = GetDisplay(); 162 XColor fg, bg; 163 Cursor cursor = None; 164 Uint32 *ptr; 165 Uint8 *data_bits, *mask_bits; 166 Pixmap data_pixmap, mask_pixmap; 167 int x, y; 168 unsigned int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits; 169 size_t width_bytes = ((surface->w + 7) & ~((size_t)7)) / 8; 170 171 data_bits = SDL_calloc(1, surface->h * width_bytes); 172 if (!data_bits) { 173 return None; 174 } 175 176 mask_bits = SDL_calloc(1, surface->h * width_bytes); 177 if (!mask_bits) { 178 SDL_free(data_bits); 179 return None; 180 } 181 182 // Code below assumes ARGB pixel format 183 SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888); 184 185 rfg = gfg = bfg = rbg = gbg = bbg = fgBits = bgBits = 0; 186 for (y = 0; y < surface->h; ++y) { 187 ptr = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch); 188 for (x = 0; x < surface->w; ++x) { 189 int alpha = (*ptr >> 24) & 0xff; 190 int red = (*ptr >> 16) & 0xff; 191 int green = (*ptr >> 8) & 0xff; 192 int blue = (*ptr >> 0) & 0xff; 193 if (alpha > 25) { 194 mask_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8)); 195 196 if ((red + green + blue) > 0x40) { 197 fgBits++; 198 rfg += red; 199 gfg += green; 200 bfg += blue; 201 data_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8)); 202 } else { 203 bgBits++; 204 rbg += red; 205 gbg += green; 206 bbg += blue; 207 } 208 } 209 ++ptr; 210 } 211 } 212 213 if (fgBits) { 214 fg.red = rfg * 257 / fgBits; 215 fg.green = gfg * 257 / fgBits; 216 fg.blue = bfg * 257 / fgBits; 217 } else { 218 fg.red = fg.green = fg.blue = 0; 219 } 220 221 if (bgBits) { 222 bg.red = rbg * 257 / bgBits; 223 bg.green = gbg * 257 / bgBits; 224 bg.blue = bbg * 257 / bgBits; 225 } else { 226 bg.red = bg.green = bg.blue = 0; 227 } 228 229 data_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display), 230 (char *)data_bits, 231 surface->w, surface->h); 232 mask_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display), 233 (char *)mask_bits, 234 surface->w, surface->h); 235 cursor = X11_XCreatePixmapCursor(display, data_pixmap, mask_pixmap, 236 &fg, &bg, hot_x, hot_y); 237 X11_XFreePixmap(display, data_pixmap); 238 X11_XFreePixmap(display, mask_pixmap); 239 SDL_free(data_bits); 240 SDL_free(mask_bits); 241 242 return cursor; 243} 244 245static SDL_Cursor *X11_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y) 246{ 247 Cursor x11_cursor = None; 248 249#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR 250 if (SDL_X11_HAVE_XCURSOR) { 251 x11_cursor = X11_CreateXCursorCursor(surface, hot_x, hot_y); 252 } 253#endif 254 if (x11_cursor == None) { 255 x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y); 256 } 257 return X11_CreateCursorAndData(x11_cursor); 258} 259 260static SDL_Cursor *X11_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int num_frames, int hot_x, int hot_y) 261{ 262 Cursor x11_cursor = None; 263 264#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR 265 if (SDL_X11_HAVE_XCURSOR) { 266 x11_cursor = X11_CreateAnimatedXCursorCursor(frames, num_frames, hot_x, hot_y); 267 } 268#endif 269 if (x11_cursor == None) { 270 x11_cursor = X11_CreatePixmapCursor(frames[0].surface, hot_x, hot_y); 271 } 272 273 return X11_CreateCursorAndData(x11_cursor); 274} 275 276static unsigned int GetLegacySystemCursorShape(SDL_SystemCursor id) 277{ 278 switch (id) { 279 // X Font Cursors reference: 280 // http://tronche.com/gui/x/xlib/appendix/b/ 281 case SDL_SYSTEM_CURSOR_DEFAULT: return XC_left_ptr; 282 case SDL_SYSTEM_CURSOR_TEXT: return XC_xterm; 283 case SDL_SYSTEM_CURSOR_WAIT: return XC_watch; 284 case SDL_SYSTEM_CURSOR_CROSSHAIR: return XC_tcross; 285 case SDL_SYSTEM_CURSOR_PROGRESS: return XC_watch; 286 case SDL_SYSTEM_CURSOR_NWSE_RESIZE: return XC_top_left_corner; 287 case SDL_SYSTEM_CURSOR_NESW_RESIZE: return XC_top_right_corner; 288 case SDL_SYSTEM_CURSOR_EW_RESIZE: return XC_sb_h_double_arrow; 289 case SDL_SYSTEM_CURSOR_NS_RESIZE: return XC_sb_v_double_arrow; 290 case SDL_SYSTEM_CURSOR_MOVE: return XC_fleur; 291 case SDL_SYSTEM_CURSOR_NOT_ALLOWED: return XC_pirate; 292 case SDL_SYSTEM_CURSOR_POINTER: return XC_hand2; 293 case SDL_SYSTEM_CURSOR_NW_RESIZE: return XC_top_left_corner; 294 case SDL_SYSTEM_CURSOR_N_RESIZE: return XC_top_side; 295 case SDL_SYSTEM_CURSOR_NE_RESIZE: return XC_top_right_corner; 296 case SDL_SYSTEM_CURSOR_E_RESIZE: return XC_right_side; 297 case SDL_SYSTEM_CURSOR_SE_RESIZE: return XC_bottom_right_corner; 298 case SDL_SYSTEM_CURSOR_S_RESIZE: return XC_bottom_side; 299 case SDL_SYSTEM_CURSOR_SW_RESIZE: return XC_bottom_left_corner; 300 case SDL_SYSTEM_CURSOR_W_RESIZE: return XC_left_side; 301 case SDL_SYSTEM_CURSOR_COUNT: break; // so the compiler might notice if an enum value is missing here. 302 } 303 304 SDL_assert(0); 305 return 0; 306} 307 308static SDL_Cursor *X11_CreateSystemCursor(SDL_SystemCursor id) 309{ 310 SDL_Cursor *cursor = NULL; 311 Display *dpy = GetDisplay(); 312 Cursor x11_cursor = None; 313 314#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR 315 if (SDL_X11_HAVE_XCURSOR) { 316 x11_cursor = X11_XcursorLibraryLoadCursor(dpy, SDL_GetCSSCursorName(id, NULL)); 317 } 318#endif 319 320 if (x11_cursor == None) { 321 x11_cursor = X11_XCreateFontCursor(dpy, GetLegacySystemCursorShape(id)); 322 } 323 324 if (x11_cursor != None) { 325 cursor = X11_CreateCursorAndData(x11_cursor); 326 } 327 328 return cursor; 329} 330 331static SDL_Cursor *X11_CreateDefaultCursor(void) 332{ 333 SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); 334 return X11_CreateSystemCursor(id); 335} 336 337static void X11_FreeCursor(SDL_Cursor *cursor) 338{ 339 Cursor x11_cursor = cursor->internal->cursor; 340 341 if (x11_cursor != None) { 342 X11_XFreeCursor(GetDisplay(), x11_cursor); 343 } 344 SDL_free(cursor->internal); 345 SDL_free(cursor); 346} 347 348static bool X11_ShowCursor(SDL_Cursor *cursor) 349{ 350 Cursor x11_cursor = 0; 351 352 if (cursor) { 353 x11_cursor = cursor->internal->cursor; 354 } else { 355 x11_cursor = X11_CreateEmptyCursor(); 356 } 357 358 // FIXME: Is there a better way than this? 359 { 360 SDL_VideoDevice *video = SDL_GetVideoDevice(); 361 Display *display = GetDisplay(); 362 SDL_Window *window; 363 364 x11_cursor_visible = !!cursor; 365 366 for (window = video->windows; window; window = window->next) { 367 SDL_WindowData *data = window->internal; 368 if (data) { 369 if (x11_cursor != None) { 370 X11_XDefineCursor(display, data->xwindow, x11_cursor); 371 } else { 372 X11_XUndefineCursor(display, data->xwindow); 373 } 374 } 375 } 376 X11_XFlush(display); 377 } 378 return true; 379} 380 381static void X11_WarpMouseInternal(Window xwindow, float x, float y) 382{ 383 SDL_VideoData *videodata = SDL_GetVideoDevice()->internal; 384 Display *display = videodata->display; 385 bool warp_hack = false; 386 387 // XWayland will only warp the cursor if it is hidden, so this workaround is required. 388 if (videodata->is_xwayland && x11_cursor_visible) { 389 warp_hack = true; 390 } 391 392 if (warp_hack) { 393 X11_ShowCursor(NULL); 394 } 395#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 396 int deviceid = 0; 397 if (X11_Xinput2IsInitialized()) { 398 /* It seems XIWarpPointer() doesn't work correctly on multi-head setups: 399 * https://developer.blender.org/rB165caafb99c6846e53d11c4e966990aaffc06cea 400 */ 401 if (ScreenCount(display) == 1) { 402 X11_XIGetClientPointer(display, None, &deviceid); 403 } 404 } 405 if (deviceid != 0) { 406 SDL_assert(SDL_X11_HAVE_XINPUT2); 407 X11_XIWarpPointer(display, deviceid, None, xwindow, 0.0, 0.0, 0, 0, x, y); 408 } else 409#endif 410 { 411 X11_XWarpPointer(display, None, xwindow, 0, 0, 0, 0, (int)x, (int)y); 412 } 413 414 if (warp_hack) { 415 X11_ShowCursor(SDL_GetCursor()); 416 } 417 X11_XSync(display, False); 418 videodata->global_mouse_changed = true; 419} 420 421static bool X11_WarpMouse(SDL_Window *window, float x, float y) 422{ 423 SDL_WindowData *data = window->internal; 424 425 if (X11_WarpMouseXTest(SDL_GetVideoDevice(), window, x, y)) { 426 return true; 427 } 428 429#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 430 // If we have no barrier, we need to warp 431 if (data->pointer_barrier_active == false) { 432 X11_WarpMouseInternal(data->xwindow, x, y); 433 } 434#else 435 X11_WarpMouseInternal(data->xwindow, x, y); 436#endif 437 return true; 438} 439 440static bool X11_WarpMouseGlobal(float x, float y) 441{ 442 if (X11_WarpMouseXTest(SDL_GetVideoDevice(), NULL, x, y)) { 443 return true; 444 } 445 446 X11_WarpMouseInternal(DefaultRootWindow(GetDisplay()), x, y); 447 return true; 448} 449 450static bool X11_SetRelativeMouseMode(bool enabled) 451{ 452 if (!X11_Xinput2IsInitialized()) { 453 return SDL_Unsupported(); 454 } 455 return true; 456} 457 458static bool X11_CaptureMouse(SDL_Window *window) 459{ 460 Display *display = GetDisplay(); 461 SDL_Window *mouse_focus = SDL_GetMouseFocus(); 462 463 if (window) { 464 SDL_WindowData *data = window->internal; 465 466 /* If XInput2 is handling the pointer input, non-confinement grabs will always fail with 'AlreadyGrabbed', 467 * since the pointer is being grabbed by XInput2. 468 */ 469 if (!data->xinput2_mouse_enabled || data->mouse_grabbed) { 470 const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask; 471 Window confined = (data->mouse_grabbed ? data->xwindow : None); 472 const int rc = X11_XGrabPointer(display, data->xwindow, False, 473 mask, GrabModeAsync, GrabModeAsync, 474 confined, None, CurrentTime); 475 if (rc != GrabSuccess) { 476 return SDL_SetError("X server refused mouse capture"); 477 } 478 479 if (data->mouse_grabbed) { 480 // XGrabPointer can warp the cursor when confining, so update the coordinates. 481 data->videodata->global_mouse_changed = true; 482 } 483 } 484 } else if (mouse_focus) { 485 SDL_UpdateWindowGrab(mouse_focus); 486 } else { 487 X11_XUngrabPointer(display, CurrentTime); 488 } 489 490 X11_XSync(display, False); 491 492 return true; 493} 494 495static SDL_MouseButtonFlags X11_GetGlobalMouseState(float *x, float *y) 496{ 497 SDL_VideoData *videodata = SDL_GetVideoDevice()->internal; 498 SDL_DisplayID *displays; 499 Display *display = GetDisplay(); 500 int i; 501 502 // !!! FIXME: should we XSync() here first? 503 504 if (!X11_Xinput2IsInitialized()) { 505 videodata->global_mouse_changed = true; 506 } 507 508 // check if we have this cached since XInput last saw the mouse move. 509 // !!! FIXME: can we just calculate this from XInput's events? 510 if (videodata->global_mouse_changed) { 511 displays = SDL_GetDisplays(NULL); 512 if (displays) { 513 for (i = 0; displays[i]; ++i) { 514 SDL_DisplayData *data = SDL_GetDisplayDriverData(displays[i]); 515 if (data) { 516 Window root, child; 517 int rootx, rooty, winx, winy; 518 unsigned int mask; 519 if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) { 520 XWindowAttributes root_attrs; 521 SDL_MouseButtonFlags buttons = 0; 522 buttons |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0; 523 buttons |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0; 524 buttons |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0; 525 // Use the SDL state for the extended buttons - it's better than nothing 526 buttons |= (SDL_GetMouseState(NULL, NULL) & (SDL_BUTTON_X1MASK | SDL_BUTTON_X2MASK)); 527 /* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing 528 * (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right). 529 * 530 * Adding root position to root-relative coordinates seems to be a better way to get absolute position. */ 531 X11_XGetWindowAttributes(display, root, &root_attrs); 532 videodata->global_mouse_position.x = root_attrs.x + rootx; 533 videodata->global_mouse_position.y = root_attrs.y + rooty; 534 videodata->global_mouse_buttons = buttons; 535 videodata->global_mouse_changed = false; 536 break; 537 } 538 } 539 } 540 SDL_free(displays); 541 } 542 } 543 544 SDL_assert(!videodata->global_mouse_changed); // The pointer wasn't on any X11 screen?! 545 546 *x = (float)videodata->global_mouse_position.x; 547 *y = (float)videodata->global_mouse_position.y; 548 return videodata->global_mouse_buttons; 549} 550 551void X11_InitMouse(SDL_VideoDevice *_this) 552{ 553 SDL_Mouse *mouse = SDL_GetMouse(); 554 555 mouse->CreateCursor = X11_CreateCursor; 556 mouse->CreateAnimatedCursor = X11_CreateAnimatedCursor; 557 mouse->CreateSystemCursor = X11_CreateSystemCursor; 558 mouse->ShowCursor = X11_ShowCursor; 559 mouse->FreeCursor = X11_FreeCursor; 560 mouse->WarpMouse = X11_WarpMouse; 561 mouse->WarpMouseGlobal = X11_WarpMouseGlobal; 562 mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode; 563 mouse->CaptureMouse = X11_CaptureMouse; 564 mouse->GetGlobalMouseState = X11_GetGlobalMouseState; 565 566 SDL_HitTestResult r = SDL_HITTEST_NORMAL; 567 while (r <= SDL_HITTEST_RESIZE_LEFT) { 568 switch (r) { 569 case SDL_HITTEST_NORMAL: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); break; 570 case SDL_HITTEST_DRAGGABLE: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); break; 571 case SDL_HITTEST_RESIZE_TOPLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_NW_RESIZE); break; 572 case SDL_HITTEST_RESIZE_TOP: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_N_RESIZE); break; 573 case SDL_HITTEST_RESIZE_TOPRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_NE_RESIZE); break; 574 case SDL_HITTEST_RESIZE_RIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_E_RESIZE); break; 575 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_SE_RESIZE); break; 576 case SDL_HITTEST_RESIZE_BOTTOM: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_S_RESIZE); break; 577 case SDL_HITTEST_RESIZE_BOTTOMLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_SW_RESIZE); break; 578 case SDL_HITTEST_RESIZE_LEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_W_RESIZE); break; 579 } 580 r++; 581 } 582 583 SDL_SetDefaultCursor(X11_CreateDefaultCursor()); 584} 585 586void X11_QuitMouse(SDL_VideoDevice *_this) 587{ 588 SDL_VideoData *data = _this->internal; 589 SDL_XInput2DeviceInfo *i; 590 SDL_XInput2DeviceInfo *next; 591 int j; 592 593 for (j = 0; j < SDL_arraysize(sys_cursors); j++) { 594 if (sys_cursors[j]) { 595 X11_FreeCursor(sys_cursors[j]); 596 sys_cursors[j] = NULL; 597 } 598 } 599 600 for (i = data->mouse_device_info; i; i = next) { 601 next = i->next; 602 SDL_free(i); 603 } 604 data->mouse_device_info = NULL; 605 606 X11_DestroyEmptyCursor(); 607} 608 609void X11_SetHitTestCursor(SDL_HitTestResult rc) 610{ 611 if (rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) { 612 SDL_RedrawCursor(); 613 } else { 614 X11_ShowCursor(sys_cursors[rc]); 615 } 616} 617 618#endif // SDL_VIDEO_DRIVER_X11 619
[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.