Atlas - SDL_mouse.c

Home / ext / SDL / src / events Lines: 1 | Size: 59540 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// General mouse handling code for SDL 24 25#include "../SDL_hints_c.h" 26#include "../video/SDL_sysvideo.h" 27#include "SDL_events_c.h" 28#include "SDL_mouse_c.h" 29#if defined(SDL_PLATFORM_WINDOWS) 30#include "../core/windows/SDL_windows.h" // For GetDoubleClickTime() 31#endif 32 33// #define DEBUG_MOUSE 34 35#define WARP_EMULATION_THRESHOLD_NS SDL_MS_TO_NS(30) 36 37// The mouse state 38static SDL_Mouse SDL_mouse; 39static int SDL_mouse_count; 40static SDL_MouseID *SDL_mice; 41static SDL_HashTable *SDL_mouse_names; 42static bool SDL_mouse_initialized; 43 44// for mapping mouse events to touch 45static bool track_mouse_down = false; 46 47static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y); 48 49static void SDLCALL SDL_MouseDoubleClickTimeChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 50{ 51 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 52 53 if (hint && *hint) { 54 mouse->double_click_time = SDL_atoi(hint); 55 } else { 56#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 57 mouse->double_click_time = GetDoubleClickTime(); 58#else 59 mouse->double_click_time = 500; 60#endif 61 } 62} 63 64static void SDLCALL SDL_MouseDoubleClickRadiusChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 65{ 66 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 67 68 if (hint && *hint) { 69 mouse->double_click_radius = SDL_atoi(hint); 70 } else { 71 mouse->double_click_radius = 32; // 32 pixels seems about right for touch interfaces 72 } 73} 74 75static void SDLCALL SDL_MouseNormalSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 76{ 77 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 78 79 if (hint && *hint) { 80 mouse->enable_normal_speed_scale = true; 81 mouse->normal_speed_scale = (float)SDL_atof(hint); 82 } else { 83 mouse->enable_normal_speed_scale = false; 84 mouse->normal_speed_scale = 1.0f; 85 } 86} 87 88static void SDLCALL SDL_MouseRelativeSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 89{ 90 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 91 92 if (hint && *hint) { 93 mouse->enable_relative_speed_scale = true; 94 mouse->relative_speed_scale = (float)SDL_atof(hint); 95 } else { 96 mouse->enable_relative_speed_scale = false; 97 mouse->relative_speed_scale = 1.0f; 98 } 99} 100 101static void SDLCALL SDL_MouseRelativeModeCenterChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 102{ 103 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 104 105 mouse->relative_mode_center = SDL_GetStringBoolean(hint, true); 106} 107 108static void SDLCALL SDL_MouseRelativeSystemScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 109{ 110 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 111 112 mouse->enable_relative_system_scale = SDL_GetStringBoolean(hint, false); 113} 114 115static void SDLCALL SDL_MouseWarpEmulationChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 116{ 117 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 118 119 mouse->warp_emulation_hint = SDL_GetStringBoolean(hint, true); 120 121 if (!mouse->warp_emulation_hint && mouse->warp_emulation_active) { 122 SDL_SetRelativeMouseMode(false); 123 mouse->warp_emulation_active = false; 124 } 125} 126 127static void SDLCALL SDL_TouchMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 128{ 129 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 130 131 mouse->touch_mouse_events = SDL_GetStringBoolean(hint, true); 132} 133 134#ifdef SDL_PLATFORM_VITA 135static void SDLCALL SDL_VitaTouchMouseDeviceChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 136{ 137 Uint8 vita_touch_mouse_device = 1; 138 139 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 140 if (hint) { 141 switch (*hint) { 142 case '0': 143 vita_touch_mouse_device = 1; 144 break; 145 case '1': 146 vita_touch_mouse_device = 2; 147 break; 148 case '2': 149 vita_touch_mouse_device = 3; 150 break; 151 default: 152 break; 153 } 154 } 155 mouse->vita_touch_mouse_device = vita_touch_mouse_device; 156} 157#endif 158 159static void SDLCALL SDL_MouseTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 160{ 161 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 162 bool default_value; 163 164#if defined(SDL_PLATFORM_ANDROID) || (defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_TVOS)) 165 default_value = true; 166#else 167 default_value = false; 168#endif 169 mouse->mouse_touch_events = SDL_GetStringBoolean(hint, default_value); 170 171 if (mouse->mouse_touch_events) { 172 if (!mouse->added_mouse_touch_device) { 173 SDL_AddTouch(SDL_MOUSE_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "mouse_input"); 174 mouse->added_mouse_touch_device = true; 175 } 176 } else { 177 if (mouse->added_mouse_touch_device) { 178 SDL_DelTouch(SDL_MOUSE_TOUCHID); 179 mouse->added_mouse_touch_device = false; 180 } 181 } 182} 183 184static void SDLCALL SDL_PenMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 185{ 186 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 187 188 mouse->pen_mouse_events = SDL_GetStringBoolean(hint, true); 189} 190 191static void SDLCALL SDL_PenTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 192{ 193 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 194 195 mouse->pen_touch_events = SDL_GetStringBoolean(hint, true); 196 197 if (mouse->pen_touch_events) { 198 if (!mouse->added_pen_touch_device) { 199 SDL_AddTouch(SDL_PEN_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "pen_input"); 200 mouse->added_pen_touch_device = true; 201 } 202 } else { 203 if (mouse->added_pen_touch_device) { 204 SDL_DelTouch(SDL_PEN_TOUCHID); 205 mouse->added_pen_touch_device = false; 206 } 207 } 208} 209 210static void SDLCALL SDL_MouseAutoCaptureChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 211{ 212 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 213 bool auto_capture = SDL_GetStringBoolean(hint, true); 214 215 if (auto_capture != mouse->auto_capture) { 216 mouse->auto_capture = auto_capture; 217 SDL_UpdateMouseCapture(false); 218 } 219} 220 221static void SDLCALL SDL_MouseRelativeWarpMotionChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 222{ 223 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 224 225 mouse->relative_mode_warp_motion = SDL_GetStringBoolean(hint, false); 226} 227 228static void SDLCALL SDL_MouseRelativeCursorVisibleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 229{ 230 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 231 232 mouse->relative_mode_hide_cursor = !(SDL_GetStringBoolean(hint, false)); 233 234 SDL_RedrawCursor(); // Update cursor visibility 235} 236 237static void SDLCALL SDL_MouseIntegerModeChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 238{ 239 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 240 241 if (hint && *hint) { 242 mouse->integer_mode_flags = (Uint8)SDL_atoi(hint); 243 } else { 244 mouse->integer_mode_flags = 0; 245 } 246} 247 248// Public functions 249bool SDL_PreInitMouse(void) 250{ 251 SDL_Mouse *mouse = SDL_GetMouse(); 252 253 SDL_mouse_initialized = true; 254 255 SDL_zerop(mouse); 256 257 SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME, 258 SDL_MouseDoubleClickTimeChanged, mouse); 259 260 SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS, 261 SDL_MouseDoubleClickRadiusChanged, mouse); 262 263 SDL_AddHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE, 264 SDL_MouseNormalSpeedScaleChanged, mouse); 265 266 SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE, 267 SDL_MouseRelativeSpeedScaleChanged, mouse); 268 269 SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, 270 SDL_MouseRelativeSystemScaleChanged, mouse); 271 272 SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, 273 SDL_MouseRelativeModeCenterChanged, mouse); 274 275 SDL_AddHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE, 276 SDL_MouseWarpEmulationChanged, mouse); 277 278 SDL_AddHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS, 279 SDL_TouchMouseEventsChanged, mouse); 280 281#ifdef SDL_PLATFORM_VITA 282 SDL_AddHintCallback(SDL_HINT_VITA_TOUCH_MOUSE_DEVICE, 283 SDL_VitaTouchMouseDeviceChanged, mouse); 284#endif 285 286 SDL_AddHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS, 287 SDL_MouseTouchEventsChanged, mouse); 288 289 SDL_AddHintCallback(SDL_HINT_PEN_MOUSE_EVENTS, 290 SDL_PenMouseEventsChanged, mouse); 291 292 SDL_AddHintCallback(SDL_HINT_PEN_TOUCH_EVENTS, 293 SDL_PenTouchEventsChanged, mouse); 294 295 SDL_AddHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE, 296 SDL_MouseAutoCaptureChanged, mouse); 297 298 SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, 299 SDL_MouseRelativeWarpMotionChanged, mouse); 300 301 SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, 302 SDL_MouseRelativeCursorVisibleChanged, mouse); 303 304 SDL_AddHintCallback("SDL_MOUSE_INTEGER_MODE", 305 SDL_MouseIntegerModeChanged, mouse); 306 307 mouse->was_touch_mouse_events = false; // no touch to mouse movement event pending 308 309 mouse->cursor_visible = true; 310 311 SDL_mouse_names = SDL_CreateHashTable(0, true, SDL_HashID, SDL_KeyMatchID, SDL_DestroyHashValue, NULL); 312 313 return true; 314} 315 316void SDL_PostInitMouse(void) 317{ 318 SDL_Mouse *mouse = SDL_GetMouse(); 319 320 /* Create a dummy mouse cursor for video backends that don't support true cursors, 321 * so that mouse grab and focus functionality will work. 322 */ 323 if (!mouse->def_cursor) { 324 SDL_Surface *surface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_ARGB8888); 325 if (surface) { 326 SDL_memset(surface->pixels, 0, (size_t)surface->h * surface->pitch); 327 SDL_SetDefaultCursor(SDL_CreateColorCursor(surface, 0, 0)); 328 SDL_DestroySurface(surface); 329 } 330 } 331} 332 333bool SDL_IsMouse(Uint16 vendor, Uint16 product) 334{ 335 // Eventually we'll have a blacklist of devices that enumerate as mice but aren't really 336 return true; 337} 338 339static int SDL_GetMouseIndex(SDL_MouseID mouseID) 340{ 341 for (int i = 0; i < SDL_mouse_count; ++i) { 342 if (mouseID == SDL_mice[i]) { 343 return i; 344 } 345 } 346 return -1; 347} 348 349void SDL_AddMouse(SDL_MouseID mouseID, const char *name) 350{ 351 int mouse_index = SDL_GetMouseIndex(mouseID); 352 if (mouse_index >= 0) { 353 // We already know about this mouse 354 return; 355 } 356 357 SDL_assert(mouseID != 0); 358 359 SDL_MouseID *mice = (SDL_MouseID *)SDL_realloc(SDL_mice, (SDL_mouse_count + 1) * sizeof(*mice)); 360 if (!mice) { 361 return; 362 } 363 mice[SDL_mouse_count] = mouseID; 364 SDL_mice = mice; 365 ++SDL_mouse_count; 366 367 if (!name) { 368 name = "Mouse"; 369 } 370 SDL_InsertIntoHashTable(SDL_mouse_names, (const void *)(uintptr_t)mouseID, SDL_strdup(name), true); 371 372 SDL_Event event; 373 SDL_zero(event); 374 event.type = SDL_EVENT_MOUSE_ADDED; 375 event.mdevice.which = mouseID; 376 SDL_PushEvent(&event); 377} 378 379void SDL_RemoveMouse(SDL_MouseID mouseID) 380{ 381 int mouse_index = SDL_GetMouseIndex(mouseID); 382 if (mouse_index < 0) { 383 // We don't know about this mouse 384 return; 385 } 386 387 if (mouse_index != SDL_mouse_count - 1) { 388 SDL_memmove(&SDL_mice[mouse_index], &SDL_mice[mouse_index + 1], (SDL_mouse_count - mouse_index - 1) * sizeof(SDL_mice[mouse_index])); 389 } 390 --SDL_mouse_count; 391 392 // Remove any mouse input sources for this mouseID 393 SDL_Mouse *mouse = SDL_GetMouse(); 394 for (int i = 0; i < mouse->num_sources; ++i) { 395 SDL_MouseInputSource *source = &mouse->sources[i]; 396 if (source->mouseID == mouseID) { 397 SDL_free(source->clickstate); 398 if (i != mouse->num_sources - 1) { 399 SDL_memmove(&mouse->sources[i], &mouse->sources[i + 1], (mouse->num_sources - i - 1) * sizeof(mouse->sources[i])); 400 } 401 --mouse->num_sources; 402 break; 403 } 404 } 405 406 if (SDL_mouse_initialized) { 407 SDL_Event event; 408 SDL_zero(event); 409 event.type = SDL_EVENT_MOUSE_REMOVED; 410 event.mdevice.which = mouseID; 411 SDL_PushEvent(&event); 412 } 413} 414 415bool SDL_HasMouse(void) 416{ 417 return (SDL_mouse_count > 0); 418} 419 420SDL_MouseID *SDL_GetMice(int *count) 421{ 422 int i; 423 SDL_MouseID *mice; 424 425 mice = (SDL_JoystickID *)SDL_malloc((SDL_mouse_count + 1) * sizeof(*mice)); 426 if (mice) { 427 if (count) { 428 *count = SDL_mouse_count; 429 } 430 431 for (i = 0; i < SDL_mouse_count; ++i) { 432 mice[i] = SDL_mice[i]; 433 } 434 mice[i] = 0; 435 } else { 436 if (count) { 437 *count = 0; 438 } 439 } 440 441 return mice; 442} 443 444const char *SDL_GetMouseNameForID(SDL_MouseID instance_id) 445{ 446 const char *name = NULL; 447 if (!SDL_FindInHashTable(SDL_mouse_names, (const void *)(uintptr_t)instance_id, (const void **)&name)) { 448 SDL_SetError("Mouse %" SDL_PRIu32 " not found", instance_id); 449 return NULL; 450 } 451 if (!name) { 452 // SDL_strdup() failed during insert 453 SDL_OutOfMemory(); 454 return NULL; 455 } 456 return name; 457} 458 459void SDL_SetDefaultCursor(SDL_Cursor *cursor) 460{ 461 SDL_Mouse *mouse = SDL_GetMouse(); 462 463 if (cursor == mouse->def_cursor) { 464 return; 465 } 466 467 if (mouse->def_cursor) { 468 SDL_Cursor *default_cursor = mouse->def_cursor; 469 SDL_Cursor *prev, *curr; 470 471 if (mouse->cur_cursor == mouse->def_cursor) { 472 mouse->cur_cursor = NULL; 473 } 474 mouse->def_cursor = NULL; 475 476 for (prev = NULL, curr = mouse->cursors; curr; 477 prev = curr, curr = curr->next) { 478 if (curr == default_cursor) { 479 if (prev) { 480 prev->next = curr->next; 481 } else { 482 mouse->cursors = curr->next; 483 } 484 485 break; 486 } 487 } 488 489 if (mouse->FreeCursor && default_cursor->internal) { 490 mouse->FreeCursor(default_cursor); 491 } else { 492 SDL_free(default_cursor); 493 } 494 } 495 496 mouse->def_cursor = cursor; 497 498 if (!mouse->cur_cursor) { 499 SDL_SetCursor(cursor); 500 } 501} 502 503SDL_SystemCursor SDL_GetDefaultSystemCursor(void) 504{ 505 SDL_SystemCursor id = SDL_SYSTEM_CURSOR_DEFAULT; 506 const char *value = SDL_GetHint(SDL_HINT_MOUSE_DEFAULT_SYSTEM_CURSOR); 507 if (value) { 508 int index = SDL_atoi(value); 509 if (0 <= index && index < SDL_SYSTEM_CURSOR_COUNT) { 510 id = (SDL_SystemCursor)index; 511 } 512 } 513 return id; 514} 515 516SDL_Mouse *SDL_GetMouse(void) 517{ 518 return &SDL_mouse; 519} 520 521static SDL_MouseButtonFlags SDL_GetMouseButtonState(SDL_Mouse *mouse, SDL_MouseID mouseID, bool include_touch) 522{ 523 int i; 524 SDL_MouseButtonFlags buttonstate = 0; 525 526 for (i = 0; i < mouse->num_sources; ++i) { 527 if (mouseID == SDL_GLOBAL_MOUSE_ID || mouseID == SDL_TOUCH_MOUSEID) { 528 if (include_touch || mouse->sources[i].mouseID != SDL_TOUCH_MOUSEID) { 529 buttonstate |= mouse->sources[i].buttonstate; 530 } 531 } else { 532 if (mouseID == mouse->sources[i].mouseID) { 533 buttonstate |= mouse->sources[i].buttonstate; 534 break; 535 } 536 } 537 } 538 return buttonstate; 539} 540 541SDL_Window *SDL_GetMouseFocus(void) 542{ 543 SDL_Mouse *mouse = SDL_GetMouse(); 544 545 return mouse->focus; 546} 547 548/* TODO RECONNECT: Hello from the Wayland video driver! 549 * This was once removed from SDL, but it's been added back in comment form 550 * because we will need it when Wayland adds compositor reconnect support. 551 * If you need this before we do, great! Otherwise, leave this alone, we'll 552 * uncomment it at the right time. 553 * -flibit 554 */ 555#if 0 556void SDL_ResetMouse(void) 557{ 558 SDL_Mouse *mouse = SDL_GetMouse(); 559 Uint32 buttonState = SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, false); 560 int i; 561 562 for (i = 1; i <= sizeof(buttonState)*8; ++i) { 563 if (buttonState & SDL_BUTTON_MASK(i)) { 564 SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, i, false); 565 } 566 } 567 SDL_assert(SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, false) == 0); 568} 569#endif // 0 570 571void SDL_SetMouseFocus(SDL_Window *window) 572{ 573 SDL_Mouse *mouse = SDL_GetMouse(); 574 575 if (mouse->focus == window) { 576 return; 577 } 578 579 /* Actually, this ends up being a bad idea, because most operating 580 systems have an implicit grab when you press the mouse button down 581 so you can drag things out of the window and then get the mouse up 582 when it happens. So, #if 0... 583 */ 584#if 0 585 if (mouse->focus && !window) { 586 // We won't get anymore mouse messages, so reset mouse state 587 SDL_ResetMouse(); 588 } 589#endif 590 591 // See if the current window has lost focus 592 if (mouse->focus) { 593 SDL_SendWindowEvent(mouse->focus, SDL_EVENT_WINDOW_MOUSE_LEAVE, 0, 0); 594 } 595 596 mouse->focus = window; 597 mouse->has_position = false; 598 599 if (mouse->focus) { 600 SDL_SendWindowEvent(mouse->focus, SDL_EVENT_WINDOW_MOUSE_ENTER, 0, 0); 601 } 602 603 // Update cursor visibility 604 SDL_RedrawCursor(); 605} 606 607bool SDL_MousePositionInWindow(SDL_Window *window, float x, float y) 608{ 609 if (!window) { 610 return false; 611 } 612 613 if (window && !(window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { 614 if (x < 0.0f || y < 0.0f || x >= (float)window->w || y >= (float)window->h) { 615 return false; 616 } 617 } 618 return true; 619} 620 621// Check to see if we need to synthesize focus events 622static bool SDL_UpdateMouseFocus(SDL_Window *window, float x, float y, Uint32 buttonstate, bool send_mouse_motion) 623{ 624 SDL_Mouse *mouse = SDL_GetMouse(); 625 bool inWindow = SDL_MousePositionInWindow(window, x, y); 626 627 if (!inWindow) { 628 if (window == mouse->focus) { 629#ifdef DEBUG_MOUSE 630 SDL_Log("Mouse left window, synthesizing move & focus lost event"); 631#endif 632 if (send_mouse_motion) { 633 SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); 634 } 635 SDL_SetMouseFocus(NULL); 636 } 637 return false; 638 } 639 640 if (window != mouse->focus) { 641#ifdef DEBUG_MOUSE 642 SDL_Log("Mouse entered window, synthesizing focus gain & move event"); 643#endif 644 SDL_SetMouseFocus(window); 645 if (send_mouse_motion) { 646 SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); 647 } 648 } 649 return true; 650} 651 652void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y) 653{ 654 if (window && !relative) { 655 SDL_Mouse *mouse = SDL_GetMouse(); 656 if (!SDL_UpdateMouseFocus(window, x, y, SDL_GetMouseButtonState(mouse, mouseID, true), (mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID))) { 657 return; 658 } 659 } 660 661 SDL_PrivateSendMouseMotion(timestamp, window, mouseID, relative, x, y); 662} 663 664static void ConstrainMousePosition(SDL_Mouse *mouse, SDL_Window *window, float *x, float *y) 665{ 666 /* make sure that the pointers find themselves inside the windows, 667 unless we have the mouse captured. */ 668 if (window && !(window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { 669 int x_min = 0, x_max = window->w - 1; 670 int y_min = 0, y_max = window->h - 1; 671 const SDL_Rect *confine = SDL_GetWindowMouseRect(window); 672 673 if (confine) { 674 SDL_Rect window_rect; 675 SDL_Rect mouse_rect; 676 677 window_rect.x = 0; 678 window_rect.y = 0; 679 window_rect.w = x_max + 1; 680 window_rect.h = y_max + 1; 681 if (SDL_GetRectIntersection(confine, &window_rect, &mouse_rect)) { 682 x_min = mouse_rect.x; 683 y_min = mouse_rect.y; 684 x_max = x_min + mouse_rect.w - 1; 685 y_max = y_min + mouse_rect.h - 1; 686 } 687 } 688 689 if (*x >= (float)(x_max + 1)) { 690 *x = SDL_max((float)x_max, mouse->last_x); 691 } 692 if (*x < (float)x_min) { 693 *x = (float)x_min; 694 } 695 696 if (*y >= (float)(y_max + 1)) { 697 *y = SDL_max((float)y_max, mouse->last_y); 698 } 699 if (*y < (float)y_min) { 700 *y = (float)y_min; 701 } 702 } 703} 704 705static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y) 706{ 707 SDL_Mouse *mouse = SDL_GetMouse(); 708 float xrel = 0.0f; 709 float yrel = 0.0f; 710 bool window_is_relative = mouse->focus && (mouse->focus->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE); 711 712 // SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events 713 if (mouse->mouse_touch_events) { 714 if (mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID && !relative && track_mouse_down) { 715 if (window) { 716 float normalized_x = x / (float)window->w; 717 float normalized_y = y / (float)window->h; 718 SDL_SendTouchMotion(timestamp, SDL_MOUSE_TOUCHID, SDL_BUTTON_LEFT, window, normalized_x, normalized_y, 1.0f); 719 } 720 } 721 } 722 723 // SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer 724 if (!mouse->touch_mouse_events && mouseID == SDL_TOUCH_MOUSEID) { 725 return; 726 } 727 728 if (relative) { 729 if (mouse->relative_mode) { 730 if (mouse->InputTransform) { 731 void *data = mouse->input_transform_data; 732 mouse->InputTransform(data, timestamp, window, mouseID, &x, &y); 733 } else { 734 if (mouse->enable_relative_system_scale) { 735 if (mouse->ApplySystemScale) { 736 mouse->ApplySystemScale(mouse->system_scale_data, timestamp, window, mouseID, &x, &y); 737 } 738 } 739 if (mouse->enable_relative_speed_scale) { 740 x *= mouse->relative_speed_scale; 741 y *= mouse->relative_speed_scale; 742 } 743 } 744 } else { 745 if (mouse->enable_normal_speed_scale) { 746 x *= mouse->normal_speed_scale; 747 y *= mouse->normal_speed_scale; 748 } 749 } 750 if (mouse->integer_mode_flags & 1) { 751 // Accumulate the fractional relative motion and only process the integer portion 752 mouse->integer_mode_residual_motion_x = SDL_modff(mouse->integer_mode_residual_motion_x + x, &x); 753 mouse->integer_mode_residual_motion_y = SDL_modff(mouse->integer_mode_residual_motion_y + y, &y); 754 } 755 xrel = x; 756 yrel = y; 757 x = (mouse->last_x + xrel); 758 y = (mouse->last_y + yrel); 759 ConstrainMousePosition(mouse, window, &x, &y); 760 } else { 761 if (mouse->integer_mode_flags & 1) { 762 // Discard the fractional component from absolute coordinates 763 x = SDL_truncf(x); 764 y = SDL_truncf(y); 765 } 766 ConstrainMousePosition(mouse, window, &x, &y); 767 if (mouse->has_position) { 768 xrel = x - mouse->last_x; 769 yrel = y - mouse->last_y; 770 } 771 } 772 773 if (mouse->has_position && xrel == 0.0f && yrel == 0.0f) { // Drop events that don't change state 774#ifdef DEBUG_MOUSE 775 SDL_Log("Mouse event didn't change state - dropped!"); 776#endif 777 return; 778 } 779 780 // Ignore relative motion positioning the first touch 781 if (mouseID == SDL_TOUCH_MOUSEID && !SDL_GetMouseButtonState(mouse, mouseID, true)) { 782 xrel = 0.0f; 783 yrel = 0.0f; 784 } 785 786 // modify internal state 787 { 788 mouse->x_accu += xrel; 789 mouse->y_accu += yrel; 790 791 if (relative && mouse->has_position) { 792 mouse->x += xrel; 793 mouse->y += yrel; 794 ConstrainMousePosition(mouse, window, &mouse->x, &mouse->y); 795 } else { 796 mouse->x = x; 797 mouse->y = y; 798 } 799 mouse->has_position = true; 800 801 // Use unclamped values if we're getting events outside the window 802 mouse->last_x = relative ? mouse->x : x; 803 mouse->last_y = relative ? mouse->y : y; 804 805 mouse->click_motion_x += xrel; 806 mouse->click_motion_y += yrel; 807 } 808 809 // Move the mouse cursor, if needed 810 if (mouse->cursor_visible && !mouse->relative_mode && 811 mouse->MoveCursor && mouse->cur_cursor) { 812 mouse->MoveCursor(mouse->cur_cursor); 813 } 814 815 // Post the event, if desired 816 if (SDL_EventEnabled(SDL_EVENT_MOUSE_MOTION)) { 817 if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID) { 818 // We're not in relative mode, so all mouse events are global mouse events 819 mouseID = SDL_GLOBAL_MOUSE_ID; 820 } 821 822 if (!relative && window_is_relative) { 823 if (!mouse->relative_mode_warp_motion) { 824 return; 825 } 826 xrel = 0.0f; 827 yrel = 0.0f; 828 } 829 830 SDL_Event event; 831 event.type = SDL_EVENT_MOUSE_MOTION; 832 event.common.timestamp = timestamp; 833 event.motion.windowID = mouse->focus ? mouse->focus->id : 0; 834 event.motion.which = mouseID; 835 // Set us pending (or clear during a normal mouse movement event) as having triggered 836 mouse->was_touch_mouse_events = (mouseID == SDL_TOUCH_MOUSEID); 837 event.motion.state = SDL_GetMouseButtonState(mouse, mouseID, true); 838 event.motion.x = mouse->x; 839 event.motion.y = mouse->y; 840 event.motion.xrel = xrel; 841 event.motion.yrel = yrel; 842 SDL_PushEvent(&event); 843 } 844} 845 846static SDL_MouseInputSource *GetMouseInputSource(SDL_Mouse *mouse, SDL_MouseID mouseID, bool down, Uint8 button) 847{ 848 SDL_MouseInputSource *source, *match = NULL, *sources; 849 int i; 850 851 if (SDL_mouse_initialized) { 852 for (i = 0; i < mouse->num_sources; ++i) { 853 source = &mouse->sources[i]; 854 if (source->mouseID == mouseID) { 855 match = source; 856 break; 857 } 858 } 859 860 if (!down && (!match || !(match->buttonstate & SDL_BUTTON_MASK(button)))) { 861 /* This might be a button release from a transition between mouse messages and raw input. 862 * See if there's another mouse source that already has that button down and use that. 863 */ 864 for (i = 0; i < mouse->num_sources; ++i) { 865 source = &mouse->sources[i]; 866 if ((source->buttonstate & SDL_BUTTON_MASK(button))) { 867 match = source; 868 break; 869 } 870 } 871 } 872 if (match) { 873 return match; 874 } 875 876 sources = (SDL_MouseInputSource *)SDL_realloc(mouse->sources, (mouse->num_sources + 1) * sizeof(*mouse->sources)); 877 if (sources) { 878 mouse->sources = sources; 879 ++mouse->num_sources; 880 source = &sources[mouse->num_sources - 1]; 881 SDL_zerop(source); 882 source->mouseID = mouseID; 883 return source; 884 } 885 } 886 return NULL; 887} 888 889static SDL_MouseClickState *GetMouseClickState(SDL_MouseInputSource *source, Uint8 button) 890{ 891 if (button >= source->num_clickstates) { 892 int i, count = button + 1; 893 SDL_MouseClickState *clickstate = (SDL_MouseClickState *)SDL_realloc(source->clickstate, count * sizeof(*source->clickstate)); 894 if (!clickstate) { 895 return NULL; 896 } 897 source->clickstate = clickstate; 898 899 for (i = source->num_clickstates; i < count; ++i) { 900 SDL_zero(source->clickstate[i]); 901 } 902 source->num_clickstates = count; 903 } 904 return &source->clickstate[button]; 905} 906 907static void SDL_PrivateSendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down, int clicks) 908{ 909 SDL_Mouse *mouse = SDL_GetMouse(); 910 SDL_EventType type; 911 Uint32 buttonstate; 912 SDL_MouseInputSource *source; 913 914 source = GetMouseInputSource(mouse, mouseID, down, button); 915 if (!source) { 916 return; 917 } 918 buttonstate = source->buttonstate; 919 920 // SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events 921 if (mouse->mouse_touch_events) { 922 if (mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID && button == SDL_BUTTON_LEFT) { 923 if (down) { 924 track_mouse_down = true; 925 } else { 926 track_mouse_down = false; 927 } 928 if (window) { 929 type = track_mouse_down ? SDL_EVENT_FINGER_DOWN : SDL_EVENT_FINGER_UP; 930 float normalized_x = mouse->x / (float)window->w; 931 float normalized_y = mouse->y / (float)window->h; 932 SDL_SendTouch(timestamp, SDL_MOUSE_TOUCHID, SDL_BUTTON_LEFT, window, type, normalized_x, normalized_y, 1.0f); 933 } 934 } 935 } 936 937 // SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer 938 if (mouse->touch_mouse_events == 0) { 939 if (mouseID == SDL_TOUCH_MOUSEID) { 940 return; 941 } 942 } 943 944 // Figure out which event to perform 945 if (down) { 946 type = SDL_EVENT_MOUSE_BUTTON_DOWN; 947 buttonstate |= SDL_BUTTON_MASK(button); 948 } else { 949 type = SDL_EVENT_MOUSE_BUTTON_UP; 950 buttonstate &= ~SDL_BUTTON_MASK(button); 951 } 952 953 // We do this after calculating buttonstate so button presses gain focus 954 if (window && down) { 955 SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, true); 956 } 957 958 if (buttonstate == source->buttonstate) { 959 // Ignore this event, no state change 960 return; 961 } 962 source->buttonstate = buttonstate; 963 964 if (clicks < 0) { 965 SDL_MouseClickState *clickstate = GetMouseClickState(source, button); 966 if (clickstate) { 967 if (down) { 968 Uint64 now = SDL_GetTicks(); 969 970 if (now >= (clickstate->last_timestamp + mouse->double_click_time) || 971 SDL_fabs(mouse->click_motion_x - clickstate->click_motion_x) > mouse->double_click_radius || 972 SDL_fabs(mouse->click_motion_y - clickstate->click_motion_y) > mouse->double_click_radius) { 973 clickstate->click_count = 0; 974 } 975 clickstate->last_timestamp = now; 976 clickstate->click_motion_x = mouse->click_motion_x; 977 clickstate->click_motion_y = mouse->click_motion_y; 978 if (clickstate->click_count < 255) { 979 ++clickstate->click_count; 980 } 981 } 982 clicks = clickstate->click_count; 983 } else { 984 clicks = 1; 985 } 986 } 987 988 // Post the event, if desired 989 if (SDL_EventEnabled(type)) { 990 if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID) { 991 // We're not in relative mode, so all mouse events are global mouse events 992 mouseID = SDL_GLOBAL_MOUSE_ID; 993 } else { 994 mouseID = source->mouseID; 995 } 996 997 SDL_Event event; 998 event.type = type; 999 event.common.timestamp = timestamp; 1000 event.button.windowID = mouse->focus ? mouse->focus->id : 0; 1001 event.button.which = mouseID; 1002 event.button.down = down; 1003 event.button.button = button; 1004 event.button.clicks = (Uint8)SDL_min(clicks, 255); 1005 event.button.x = mouse->x; 1006 event.button.y = mouse->y; 1007 SDL_PushEvent(&event); 1008 } 1009 1010 // We do this after dispatching event so button releases can lose focus 1011 if (window && !down) { 1012 SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, true); 1013 } 1014 1015 // Automatically capture the mouse while buttons are pressed 1016 if (mouse->auto_capture) { 1017 SDL_UpdateMouseCapture(false); 1018 } 1019} 1020 1021void SDL_SendMouseButtonClicks(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down, int clicks) 1022{ 1023 clicks = SDL_max(clicks, 0); 1024 SDL_PrivateSendMouseButton(timestamp, window, mouseID, button, down, clicks); 1025} 1026 1027void SDL_SendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down) 1028{ 1029 SDL_PrivateSendMouseButton(timestamp, window, mouseID, button, down, -1); 1030} 1031 1032void SDL_SendMouseWheel(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction) 1033{ 1034 SDL_Mouse *mouse = SDL_GetMouse(); 1035 1036 if (window) { 1037 SDL_SetMouseFocus(window); 1038 } 1039 1040 if (x == 0.0f && y == 0.0f) { 1041 return; 1042 } 1043 1044 // Post the event, if desired 1045 if (SDL_EventEnabled(SDL_EVENT_MOUSE_WHEEL)) { 1046 float integer_x, integer_y; 1047 1048 if (!mouse->relative_mode || mouse->warp_emulation_active) { 1049 // We're not in relative mode, so all mouse events are global mouse events 1050 mouseID = SDL_GLOBAL_MOUSE_ID; 1051 } 1052 1053 SDL_Event event; 1054 event.type = SDL_EVENT_MOUSE_WHEEL; 1055 event.common.timestamp = timestamp; 1056 event.wheel.windowID = mouse->focus ? mouse->focus->id : 0; 1057 event.wheel.which = mouseID; 1058 event.wheel.direction = direction; 1059 event.wheel.mouse_x = mouse->x; 1060 event.wheel.mouse_y = mouse->y; 1061 1062 mouse->residual_scroll_x = SDL_modff(mouse->residual_scroll_x + x, &integer_x); 1063 event.wheel.integer_x = (Sint32)integer_x; 1064 1065 mouse->residual_scroll_y = SDL_modff(mouse->residual_scroll_y + y, &integer_y); 1066 event.wheel.integer_y = (Sint32)integer_y; 1067 1068 // Return the accumulated values in x/y when integer wheel mode is enabled. 1069 // This is necessary for compatibility with sdl2-compat 2.32.54. 1070 if (mouse->integer_mode_flags & 2) { 1071 event.wheel.x = integer_x; 1072 event.wheel.y = integer_y; 1073 } else { 1074 event.wheel.x = x; 1075 event.wheel.y = y; 1076 } 1077 1078 SDL_PushEvent(&event); 1079 } 1080} 1081 1082void SDL_QuitMouse(void) 1083{ 1084 SDL_Cursor *cursor, *next; 1085 SDL_Mouse *mouse = SDL_GetMouse(); 1086 1087 SDL_mouse_initialized = false; 1088 1089 if (mouse->added_mouse_touch_device) { 1090 SDL_DelTouch(SDL_MOUSE_TOUCHID); 1091 mouse->added_mouse_touch_device = false; 1092 } 1093 1094 if (mouse->added_pen_touch_device) { 1095 SDL_DelTouch(SDL_PEN_TOUCHID); 1096 mouse->added_pen_touch_device = false; 1097 } 1098 1099 if (mouse->CaptureMouse) { 1100 SDL_CaptureMouse(false); 1101 SDL_UpdateMouseCapture(true); 1102 } 1103 SDL_SetRelativeMouseMode(false); 1104 SDL_ShowCursor(); 1105 1106 if (mouse->def_cursor) { 1107 SDL_SetDefaultCursor(NULL); 1108 } 1109 1110 cursor = mouse->cursors; 1111 while (cursor) { 1112 next = cursor->next; 1113 SDL_DestroyCursor(cursor); 1114 cursor = next; 1115 } 1116 mouse->cursors = NULL; 1117 mouse->cur_cursor = NULL; 1118 1119 if (mouse->sources) { 1120 for (int i = 0; i < mouse->num_sources; ++i) { 1121 SDL_MouseInputSource *source = &mouse->sources[i]; 1122 SDL_free(source->clickstate); 1123 } 1124 SDL_free(mouse->sources); 1125 mouse->sources = NULL; 1126 } 1127 mouse->num_sources = 0; 1128 1129 SDL_RemoveHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME, 1130 SDL_MouseDoubleClickTimeChanged, mouse); 1131 1132 SDL_RemoveHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS, 1133 SDL_MouseDoubleClickRadiusChanged, mouse); 1134 1135 SDL_RemoveHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE, 1136 SDL_MouseNormalSpeedScaleChanged, mouse); 1137 1138 SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE, 1139 SDL_MouseRelativeSpeedScaleChanged, mouse); 1140 1141 SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, 1142 SDL_MouseRelativeSystemScaleChanged, mouse); 1143 1144 SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, 1145 SDL_MouseRelativeModeCenterChanged, mouse); 1146 1147 SDL_RemoveHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE, 1148 SDL_MouseWarpEmulationChanged, mouse); 1149 1150 SDL_RemoveHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS, 1151 SDL_TouchMouseEventsChanged, mouse); 1152 1153 SDL_RemoveHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS, 1154 SDL_MouseTouchEventsChanged, mouse); 1155 1156 SDL_RemoveHintCallback(SDL_HINT_PEN_MOUSE_EVENTS, 1157 SDL_PenMouseEventsChanged, mouse); 1158 1159 SDL_RemoveHintCallback(SDL_HINT_PEN_TOUCH_EVENTS, 1160 SDL_PenTouchEventsChanged, mouse); 1161 1162 SDL_RemoveHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE, 1163 SDL_MouseAutoCaptureChanged, mouse); 1164 1165 SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, 1166 SDL_MouseRelativeWarpMotionChanged, mouse); 1167 1168 SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, 1169 SDL_MouseRelativeCursorVisibleChanged, mouse); 1170 1171 SDL_RemoveHintCallback("SDL_MOUSE_INTEGER_MODE", 1172 SDL_MouseIntegerModeChanged, mouse); 1173 1174 for (int i = SDL_mouse_count; i--; ) { 1175 SDL_RemoveMouse(SDL_mice[i]); 1176 } 1177 SDL_free(SDL_mice); 1178 SDL_mice = NULL; 1179 1180 SDL_DestroyHashTable(SDL_mouse_names); 1181 SDL_mouse_names = NULL; 1182 1183 if (mouse->internal) { 1184 SDL_free(mouse->internal); 1185 mouse->internal = NULL; 1186 } 1187 SDL_zerop(mouse); 1188} 1189 1190bool SDL_SetRelativeMouseTransform(SDL_MouseMotionTransformCallback transform, void *userdata) 1191{ 1192 SDL_Mouse *mouse = SDL_GetMouse(); 1193 if (mouse->relative_mode) { 1194 return SDL_SetError("Can't set mouse transform while relative mode is active"); 1195 } 1196 mouse->InputTransform = transform; 1197 mouse->input_transform_data = userdata; 1198 return true; 1199} 1200 1201SDL_MouseButtonFlags SDL_GetMouseState(float *x, float *y) 1202{ 1203 SDL_Mouse *mouse = SDL_GetMouse(); 1204 1205 if (x) { 1206 *x = mouse->x; 1207 } 1208 if (y) { 1209 *y = mouse->y; 1210 } 1211 return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, true); 1212} 1213 1214SDL_MouseButtonFlags SDL_GetRelativeMouseState(float *x, float *y) 1215{ 1216 SDL_Mouse *mouse = SDL_GetMouse(); 1217 1218 if (x) { 1219 *x = mouse->x_accu; 1220 } 1221 if (y) { 1222 *y = mouse->y_accu; 1223 } 1224 mouse->x_accu = 0.0f; 1225 mouse->y_accu = 0.0f; 1226 return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, true); 1227} 1228 1229SDL_MouseButtonFlags SDL_GetGlobalMouseState(float *x, float *y) 1230{ 1231 SDL_Mouse *mouse = SDL_GetMouse(); 1232 1233 if (mouse->GetGlobalMouseState) { 1234 float tmpx, tmpy; 1235 1236 // make sure these are never NULL for the backend implementations... 1237 if (!x) { 1238 x = &tmpx; 1239 } 1240 if (!y) { 1241 y = &tmpy; 1242 } 1243 1244 *x = *y = 0.0f; 1245 1246 return mouse->GetGlobalMouseState(x, y); 1247 } else { 1248 return SDL_GetMouseState(x, y); 1249 } 1250} 1251 1252void SDL_PerformWarpMouseInWindow(SDL_Window *window, float x, float y, bool ignore_relative_mode) 1253{ 1254 SDL_Mouse *mouse = SDL_GetMouse(); 1255 1256 if (!window) { 1257 window = mouse->focus; 1258 } 1259 1260 if (!window) { 1261 return; 1262 } 1263 1264 if ((window->flags & SDL_WINDOW_MINIMIZED) == SDL_WINDOW_MINIMIZED) { 1265 return; 1266 } 1267 1268 // Ignore the previous position when we warp 1269 mouse->last_x = x; 1270 mouse->last_y = y; 1271 mouse->has_position = false; 1272 1273 if (mouse->relative_mode && !ignore_relative_mode) { 1274 /* 2.0.22 made warping in relative mode actually functional, which 1275 * surprised many applications that weren't expecting the additional 1276 * mouse motion. 1277 * 1278 * So for now, warping in relative mode adjusts the absolution position 1279 * but doesn't generate motion events, unless SDL_HINT_MOUSE_RELATIVE_WARP_MOTION is set. 1280 */ 1281 if (!mouse->relative_mode_warp_motion) { 1282 mouse->x = x; 1283 mouse->y = y; 1284 mouse->has_position = true; 1285 return; 1286 } 1287 } 1288 1289 if (mouse->WarpMouse && !mouse->relative_mode) { 1290 mouse->WarpMouse(window, x, y); 1291 } else { 1292 SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y); 1293 } 1294} 1295 1296void SDL_DisableMouseWarpEmulation(void) 1297{ 1298 SDL_Mouse *mouse = SDL_GetMouse(); 1299 1300 if (mouse->warp_emulation_active) { 1301 SDL_SetRelativeMouseMode(false); 1302 } 1303 1304 mouse->warp_emulation_prohibited = true; 1305} 1306 1307static void SDL_MaybeEnableWarpEmulation(SDL_Window *window, float x, float y) 1308{ 1309 SDL_Mouse *mouse = SDL_GetMouse(); 1310 1311 if (!mouse->warp_emulation_prohibited && mouse->warp_emulation_hint && !mouse->cursor_visible && !mouse->warp_emulation_active) { 1312 if (!window) { 1313 window = mouse->focus; 1314 } 1315 1316 if (window) { 1317 const float cx = window->w / 2.f; 1318 const float cy = window->h / 2.f; 1319 if (x >= SDL_floorf(cx) && x <= SDL_ceilf(cx) && 1320 y >= SDL_floorf(cy) && y <= SDL_ceilf(cy)) { 1321 1322 // Require two consecutive warps to the center within a certain timespan to enter warp emulation mode. 1323 const Uint64 now = SDL_GetTicksNS(); 1324 if (now - mouse->last_center_warp_time_ns < WARP_EMULATION_THRESHOLD_NS) { 1325 mouse->warp_emulation_active = true; 1326 if (!SDL_SetRelativeMouseMode(true)) { 1327 mouse->warp_emulation_active = false; 1328 } 1329 } 1330 1331 mouse->last_center_warp_time_ns = now; 1332 return; 1333 } 1334 } 1335 1336 mouse->last_center_warp_time_ns = 0; 1337 } 1338} 1339 1340void SDL_WarpMouseInWindow(SDL_Window *window, float x, float y) 1341{ 1342 SDL_Mouse *mouse = SDL_GetMouse(); 1343 SDL_MaybeEnableWarpEmulation(window, x, y); 1344 1345 SDL_PerformWarpMouseInWindow(window, x, y, mouse->warp_emulation_active); 1346} 1347 1348bool SDL_WarpMouseGlobal(float x, float y) 1349{ 1350 SDL_Mouse *mouse = SDL_GetMouse(); 1351 1352 if (mouse->WarpMouseGlobal) { 1353 return mouse->WarpMouseGlobal(x, y); 1354 } 1355 1356 return SDL_Unsupported(); 1357} 1358 1359bool SDL_SetRelativeMouseMode(bool enabled) 1360{ 1361 SDL_Mouse *mouse = SDL_GetMouse(); 1362 SDL_Window *focusWindow = SDL_GetKeyboardFocus(); 1363 1364 if (!enabled) { 1365 // If warps were being emulated, reset the flag. 1366 mouse->warp_emulation_active = false; 1367 } 1368 1369 if (enabled == mouse->relative_mode) { 1370 return true; 1371 } 1372 1373 // Set the relative mode 1374 if (!mouse->SetRelativeMouseMode) { 1375 return SDL_Unsupported(); 1376 } 1377 if (!mouse->SetRelativeMouseMode(enabled)) { 1378 return false; 1379 } 1380 mouse->relative_mode = enabled; 1381 1382 if (enabled) { 1383 // Update cursor visibility before we potentially warp the mouse 1384 SDL_RedrawCursor(); 1385 } 1386 1387 if (enabled && focusWindow) { 1388 SDL_SetMouseFocus(focusWindow); 1389 } 1390 1391 if (focusWindow) { 1392 SDL_UpdateWindowGrab(focusWindow); 1393 1394 // Put the cursor back to where the application expects it 1395 if (!enabled) { 1396 SDL_PerformWarpMouseInWindow(focusWindow, mouse->x, mouse->y, true); 1397 } 1398 1399 SDL_UpdateMouseCapture(false); 1400 } 1401 1402 if (!enabled) { 1403 // Update cursor visibility after we restore the mouse position 1404 SDL_RedrawCursor(); 1405 } 1406 1407 // Flush pending mouse motion - ideally we would pump events, but that's not always safe 1408 SDL_FlushEvent(SDL_EVENT_MOUSE_MOTION); 1409 1410 return true; 1411} 1412 1413bool SDL_GetRelativeMouseMode(void) 1414{ 1415 SDL_Mouse *mouse = SDL_GetMouse(); 1416 1417 return mouse->relative_mode; 1418} 1419 1420bool SDL_UpdateRelativeMouseMode(void) 1421{ 1422 SDL_Mouse *mouse = SDL_GetMouse(); 1423 SDL_Window *focus = SDL_GetKeyboardFocus(); 1424 bool relative_mode = (focus && (focus->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE)); 1425 1426 if (relative_mode == mouse->relative_mode) { 1427 return true; 1428 } 1429 return SDL_SetRelativeMouseMode(relative_mode); 1430} 1431 1432bool SDL_UpdateMouseCapture(bool force_release) 1433{ 1434 SDL_Mouse *mouse = SDL_GetMouse(); 1435 SDL_Window *capture_window = NULL; 1436 1437 if (!mouse->CaptureMouse) { 1438 return true; 1439 } 1440 1441 if (!force_release) { 1442 if (SDL_GetMessageBoxCount() == 0 && 1443 (mouse->capture_desired || (mouse->auto_capture && SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, false) != 0))) { 1444 if (!mouse->relative_mode) { 1445 capture_window = mouse->focus; 1446 } 1447 } 1448 } 1449 1450 if (capture_window != mouse->capture_window) { 1451 /* We can get here recursively on Windows, so make sure we complete 1452 * all of the window state operations before we change the capture state 1453 * (e.g. https://github.com/libsdl-org/SDL/pull/5608) 1454 */ 1455 SDL_Window *previous_capture = mouse->capture_window; 1456 1457 if (previous_capture) { 1458 previous_capture->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; 1459 } 1460 1461 if (capture_window) { 1462 capture_window->flags |= SDL_WINDOW_MOUSE_CAPTURE; 1463 } 1464 1465 mouse->capture_window = capture_window; 1466 1467 if (!mouse->CaptureMouse(capture_window)) { 1468 // CaptureMouse() will have set an error, just restore the state 1469 if (previous_capture) { 1470 previous_capture->flags |= SDL_WINDOW_MOUSE_CAPTURE; 1471 } 1472 if (capture_window) { 1473 capture_window->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; 1474 } 1475 mouse->capture_window = previous_capture; 1476 1477 return false; 1478 } 1479 } 1480 return true; 1481} 1482 1483bool SDL_CaptureMouse(bool enabled) 1484{ 1485 SDL_Mouse *mouse = SDL_GetMouse(); 1486 1487 if (!mouse->CaptureMouse) { 1488 return SDL_Unsupported(); 1489 } 1490 1491#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 1492 /* Windows mouse capture is tied to the current thread, and must be called 1493 * from the thread that created the window being captured. Since we update 1494 * the mouse capture state from the event processing, any application state 1495 * changes must be processed on that thread as well. 1496 */ 1497 if (!SDL_OnVideoThread()) { 1498 return SDL_SetError("SDL_CaptureMouse() must be called on the main thread"); 1499 } 1500#endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK) 1501 1502 if (enabled && SDL_GetKeyboardFocus() == NULL) { 1503 return SDL_SetError("No window has focus"); 1504 } 1505 mouse->capture_desired = enabled; 1506 1507 return SDL_UpdateMouseCapture(false); 1508} 1509 1510SDL_Cursor *SDL_CreateCursor(const Uint8 *data, const Uint8 *mask, int w, int h, int hot_x, int hot_y) 1511{ 1512 SDL_Surface *surface; 1513 SDL_Cursor *cursor; 1514 int x, y; 1515 Uint32 *pixels; 1516 Uint8 datab = 0, maskb = 0; 1517 const Uint32 black = 0xFF000000; 1518 const Uint32 white = 0xFFFFFFFF; 1519 const Uint32 transparent = 0x00000000; 1520#if defined(SDL_PLATFORM_WIN32) 1521 // Only Windows backend supports inverted pixels in mono cursors. 1522 const Uint32 inverted = 0x00FFFFFF; 1523#else 1524 const Uint32 inverted = 0xFF000000; 1525#endif // defined(SDL_PLATFORM_WIN32) 1526 1527 // Make sure the width is a multiple of 8 1528 w = ((w + 7) & ~7); 1529 1530 // Create the surface from a bitmap 1531 surface = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_ARGB8888); 1532 if (!surface) { 1533 return NULL; 1534 } 1535 for (y = 0; y < h; ++y) { 1536 pixels = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch); 1537 for (x = 0; x < w; ++x) { 1538 if ((x % 8) == 0) { 1539 datab = *data++; 1540 maskb = *mask++; 1541 } 1542 if (maskb & 0x80) { 1543 *pixels++ = (datab & 0x80) ? black : white; 1544 } else { 1545 *pixels++ = (datab & 0x80) ? inverted : transparent; 1546 } 1547 datab <<= 1; 1548 maskb <<= 1; 1549 } 1550 } 1551 1552 cursor = SDL_CreateColorCursor(surface, hot_x, hot_y); 1553 1554 SDL_DestroySurface(surface); 1555 1556 return cursor; 1557} 1558 1559static void SDL_DestroyCursorAnimation(SDL_CursorAnimation *animation) 1560{ 1561 if (!animation) { 1562 return; 1563 } 1564 1565 for (int i = 0; i < animation->num_frames; ++i) { 1566 SDL_DestroyCursor(animation->frames[i]); 1567 } 1568 SDL_free(animation->frames); 1569 SDL_free(animation->durations); 1570 SDL_free(animation); 1571} 1572 1573static SDL_CursorAnimation *SDL_CreateCursorAnimation(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y) 1574{ 1575 SDL_CursorAnimation *animation = (SDL_CursorAnimation *)SDL_calloc(1, sizeof(*animation)); 1576 if (!animation) { 1577 return NULL; 1578 } 1579 1580 animation->frames = (SDL_Cursor **)SDL_calloc(frame_count, sizeof(*animation->frames)); 1581 animation->durations = (Uint32 *)SDL_calloc(frame_count, sizeof(*animation->durations)); 1582 if (!animation->frames || !animation->durations) { 1583 SDL_DestroyCursorAnimation(animation); 1584 return NULL; 1585 } 1586 1587 for (int i = 0; i < frame_count; ++i) { 1588 animation->frames[i] = SDL_CreateColorCursor(frames[i].surface, hot_x, hot_y); 1589 if (!animation->frames[i]) { 1590 SDL_DestroyCursorAnimation(animation); 1591 return NULL; 1592 } 1593 1594 animation->durations[i] = frames[i].duration; 1595 } 1596 animation->num_frames = frame_count; 1597 1598 return animation; 1599} 1600 1601void SDL_UpdateCursorAnimation(void) 1602{ 1603 SDL_Mouse *mouse = SDL_GetMouse(); 1604 SDL_Cursor *cursor = mouse->cur_cursor; 1605 1606 if (!cursor || !cursor->animation) { 1607 return; 1608 } 1609 1610 if (!mouse->focus) { 1611 return; 1612 } 1613 1614 SDL_CursorAnimation *animation = cursor->animation; 1615 Uint32 duration = animation->durations[animation->current_frame]; 1616 if (!duration) { 1617 // We've reached the stop frame of the animation 1618 return; 1619 } 1620 1621 Uint64 now = SDL_GetTicks(); 1622 if (now < (animation->last_update + duration)) { 1623 return; 1624 } 1625 1626 animation->current_frame = (animation->current_frame + 1) % animation->num_frames; 1627 animation->last_update = now; 1628 1629 SDL_RedrawCursor(); 1630} 1631 1632SDL_Cursor *SDL_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y) 1633{ 1634 SDL_Mouse *mouse = SDL_GetMouse(); 1635 SDL_Cursor *cursor = NULL; 1636 1637 CHECK_PARAM(!frames) { 1638 SDL_InvalidParamError("frames"); 1639 return NULL; 1640 } 1641 1642 CHECK_PARAM(!frames[0].surface) { 1643 SDL_SetError("NULL surface in frame 0"); 1644 return NULL; 1645 } 1646 1647 CHECK_PARAM(frame_count <= 0) { 1648 SDL_InvalidParamError("frame_count"); 1649 return NULL; 1650 } 1651 1652 if (frame_count == 1) { 1653 return SDL_CreateColorCursor(frames[0].surface, hot_x, hot_y); 1654 } 1655 1656 // Allow specifying the hot spot via properties on the surface 1657 SDL_PropertiesID props = SDL_GetSurfaceProperties(frames[0].surface); 1658 hot_x = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_X_NUMBER, hot_x); 1659 hot_y = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER, hot_y); 1660 1661 // Sanity check the hot spot 1662 CHECK_PARAM((hot_x < 0) || (hot_y < 0) || 1663 (hot_x >= frames[0].surface->w) || (hot_y >= frames[0].surface->h)) { 1664 SDL_SetError("Cursor hot spot doesn't lie within cursor"); 1665 return NULL; 1666 } 1667 1668 bool isstack; 1669 SDL_CursorFrameInfo *temp_frames = SDL_small_alloc(SDL_CursorFrameInfo, frame_count, &isstack); 1670 if (!temp_frames) { 1671 return NULL; 1672 } 1673 SDL_memset(temp_frames, 0, sizeof(SDL_CursorFrameInfo) * frame_count); 1674 1675 const int w = frames[0].surface->w; 1676 const int h = frames[0].surface->h; 1677 1678 for (int i = 0; i < frame_count; ++i) { 1679 CHECK_PARAM(!frames[i].surface) { 1680 SDL_SetError("Null surface in frame %i", i); 1681 goto cleanup; 1682 } 1683 1684 // All cursor images should be the same size. 1685 CHECK_PARAM(frames[i].surface->w != w || frames[i].surface->h != h) { 1686 SDL_SetError("All frames in an animated sequence must have the same dimensions"); 1687 goto cleanup; 1688 } 1689 1690 if (frames[i].surface->format == SDL_PIXELFORMAT_ARGB8888) { 1691 temp_frames[i].surface = frames[i].surface; 1692 } else { 1693 SDL_Surface *temp = SDL_ConvertSurface(frames[i].surface, SDL_PIXELFORMAT_ARGB8888); 1694 if (!temp) { 1695 goto cleanup; 1696 } 1697 temp_frames[i].surface = temp; 1698 } 1699 temp_frames[i].duration = frames[i].duration; 1700 } 1701 1702 if (mouse->CreateAnimatedCursor) { 1703 cursor = mouse->CreateAnimatedCursor(temp_frames, frame_count, hot_x, hot_y); 1704 } else { 1705 SDL_CursorAnimation *animation = SDL_CreateCursorAnimation(temp_frames, frame_count, hot_x, hot_y); 1706 if (!animation) { 1707 goto cleanup; 1708 } 1709 1710 cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor)); 1711 if (!cursor) { 1712 SDL_DestroyCursorAnimation(animation); 1713 goto cleanup; 1714 } 1715 cursor->animation = animation; 1716 } 1717 1718 if (cursor) { 1719 cursor->next = mouse->cursors; 1720 mouse->cursors = cursor; 1721 } 1722 1723cleanup: 1724 // Clean up any temporary converted surfaces. 1725 for (int i = 0; i < frame_count; ++i) { 1726 if (temp_frames[i].surface && frames[i].surface != temp_frames[i].surface) { 1727 SDL_DestroySurface(temp_frames[i].surface); 1728 } 1729 } 1730 1731 SDL_small_free(temp_frames, isstack); 1732 1733 return cursor; 1734} 1735 1736SDL_Cursor *SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y) 1737{ 1738 SDL_Mouse *mouse = SDL_GetMouse(); 1739 SDL_Surface *temp = NULL; 1740 SDL_Cursor *cursor; 1741 1742 CHECK_PARAM(!surface) { 1743 SDL_InvalidParamError("surface"); 1744 return NULL; 1745 } 1746 1747 // Allow specifying the hot spot via properties on the surface 1748 SDL_PropertiesID props = SDL_GetSurfaceProperties(surface); 1749 hot_x = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_X_NUMBER, hot_x); 1750 hot_y = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER, hot_y); 1751 1752 // Sanity check the hot spot 1753 CHECK_PARAM((hot_x < 0) || (hot_y < 0) || 1754 (hot_x >= surface->w) || (hot_y >= surface->h)) { 1755 SDL_SetError("Cursor hot spot doesn't lie within cursor"); 1756 return NULL; 1757 } 1758 1759 if (surface->format != SDL_PIXELFORMAT_ARGB8888) { 1760 temp = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 1761 if (!temp) { 1762 return NULL; 1763 } 1764 surface = temp; 1765 } 1766 1767 if (mouse->CreateCursor) { 1768 cursor = mouse->CreateCursor(surface, hot_x, hot_y); 1769 } else { 1770 cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor)); 1771 } 1772 if (cursor) { 1773 cursor->next = mouse->cursors; 1774 mouse->cursors = cursor; 1775 } 1776 1777 SDL_DestroySurface(temp); 1778 1779 return cursor; 1780} 1781 1782SDL_Cursor *SDL_CreateSystemCursor(SDL_SystemCursor id) 1783{ 1784 SDL_Mouse *mouse = SDL_GetMouse(); 1785 SDL_Cursor *cursor; 1786 1787 if (!mouse->CreateSystemCursor) { 1788 SDL_SetError("CreateSystemCursor is not currently supported"); 1789 return NULL; 1790 } 1791 1792 cursor = mouse->CreateSystemCursor(id); 1793 if (cursor) { 1794 cursor->next = mouse->cursors; 1795 mouse->cursors = cursor; 1796 } 1797 1798 return cursor; 1799} 1800 1801void SDL_RedrawCursor(void) 1802{ 1803 SDL_Mouse *mouse = SDL_GetMouse(); 1804 SDL_Cursor *cursor; 1805 1806 if (mouse->focus) { 1807 cursor = mouse->cur_cursor; 1808 } else { 1809 cursor = mouse->def_cursor; 1810 } 1811 1812 if (mouse->focus && (!mouse->cursor_visible || (mouse->relative_mode && mouse->relative_mode_hide_cursor))) { 1813 cursor = NULL; 1814 } 1815 1816 if (cursor && cursor->animation) { 1817 SDL_CursorAnimation *animation = cursor->animation; 1818 cursor = animation->frames[animation->current_frame]; 1819 } 1820 1821 if (mouse->ShowCursor) { 1822 mouse->ShowCursor(cursor); 1823 } 1824} 1825 1826/* SDL_SetCursor(NULL) can be used to force the cursor redraw, 1827 if this is desired for any reason. This is used when setting 1828 the video mode and when the SDL window gains the mouse focus. 1829 */ 1830bool SDL_SetCursor(SDL_Cursor *cursor) 1831{ 1832 SDL_Mouse *mouse = SDL_GetMouse(); 1833 1834 // already on this cursor, no further action required 1835 if (cursor == mouse->cur_cursor) { 1836 return true; 1837 } 1838 1839 // Set the new cursor 1840 if (cursor) { 1841 // Make sure the cursor is still valid for this mouse 1842 if (cursor != mouse->def_cursor) { 1843 SDL_Cursor *found; 1844 for (found = mouse->cursors; found; found = found->next) { 1845 if (found == cursor) { 1846 break; 1847 } 1848 } 1849 if (!found) { 1850 return SDL_SetError("Cursor not associated with the current mouse"); 1851 } 1852 } 1853 if (cursor->animation) { 1854 SDL_CursorAnimation *animation = cursor->animation; 1855 animation->current_frame = 0; 1856 animation->last_update = SDL_GetTicks(); 1857 } 1858 mouse->cur_cursor = cursor; 1859 } 1860 1861 SDL_RedrawCursor(); 1862 1863 return true; 1864} 1865 1866SDL_Cursor *SDL_GetCursor(void) 1867{ 1868 SDL_Mouse *mouse = SDL_GetMouse(); 1869 1870 if (!mouse) { 1871 return NULL; 1872 } 1873 return mouse->cur_cursor; 1874} 1875 1876SDL_Cursor *SDL_GetDefaultCursor(void) 1877{ 1878 SDL_Mouse *mouse = SDL_GetMouse(); 1879 1880 if (!mouse) { 1881 return NULL; 1882 } 1883 return mouse->def_cursor; 1884} 1885 1886void SDL_DestroyCursor(SDL_Cursor *cursor) 1887{ 1888 SDL_Mouse *mouse = SDL_GetMouse(); 1889 SDL_Cursor *curr, *prev; 1890 1891 if (!cursor) { 1892 return; 1893 } 1894 1895 if (cursor == mouse->def_cursor) { 1896 return; 1897 } 1898 if (cursor == mouse->cur_cursor) { 1899 SDL_SetCursor(mouse->def_cursor); 1900 } 1901 1902 for (prev = NULL, curr = mouse->cursors; curr; 1903 prev = curr, curr = curr->next) { 1904 if (curr == cursor) { 1905 if (prev) { 1906 prev->next = curr->next; 1907 } else { 1908 mouse->cursors = curr->next; 1909 } 1910 1911 if (curr->animation) { 1912 SDL_DestroyCursorAnimation(curr->animation); 1913 } 1914 1915 if (mouse->FreeCursor && curr->internal) { 1916 mouse->FreeCursor(curr); 1917 } else { 1918 SDL_free(curr); 1919 } 1920 return; 1921 } 1922 } 1923} 1924 1925bool SDL_ShowCursor(void) 1926{ 1927 SDL_Mouse *mouse = SDL_GetMouse(); 1928 1929 if (mouse->warp_emulation_active) { 1930 SDL_SetRelativeMouseMode(false); 1931 mouse->warp_emulation_active = false; 1932 } 1933 1934 if (!mouse->cursor_visible) { 1935 mouse->cursor_visible = true; 1936 SDL_RedrawCursor(); 1937 } 1938 return true; 1939} 1940 1941bool SDL_HideCursor(void) 1942{ 1943 SDL_Mouse *mouse = SDL_GetMouse(); 1944 1945 if (mouse->cursor_visible) { 1946 mouse->cursor_visible = false; 1947 SDL_RedrawCursor(); 1948 } 1949 return true; 1950} 1951 1952bool SDL_CursorVisible(void) 1953{ 1954 SDL_Mouse *mouse = SDL_GetMouse(); 1955 1956 return mouse->cursor_visible; 1957} 1958
[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.