SDL_mouse.c (30810B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 #include "../SDL_internal.h" 22 23 /* General mouse handling code for SDL */ 24 25 #include "SDL_hints.h" 26 #include "SDL_timer.h" 27 #include "SDL_events.h" 28 #include "SDL_events_c.h" 29 #include "../SDL_hints_c.h" 30 #include "../video/SDL_sysvideo.h" 31 #ifdef __WIN32__ 32 #include "../core/windows/SDL_windows.h" // For GetDoubleClickTime() 33 #endif 34 #if defined(__OS2__) 35 #define INCL_WIN 36 #include <os2.h> 37 #endif 38 39 /* #define DEBUG_MOUSE */ 40 41 /* The mouse state */ 42 static SDL_Mouse SDL_mouse; 43 44 /* for mapping mouse events to touch */ 45 static SDL_bool track_mouse_down = SDL_FALSE; 46 47 static int 48 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y); 49 50 static void SDLCALL 51 SDL_MouseDoubleClickTimeChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 52 { 53 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 54 55 if (hint && *hint) { 56 mouse->double_click_time = SDL_atoi(hint); 57 } else { 58 #ifdef __WIN32__ 59 mouse->double_click_time = GetDoubleClickTime(); 60 #elif defined(__OS2__) 61 mouse->double_click_time = WinQuerySysValue(HWND_DESKTOP, SV_DBLCLKTIME); 62 #else 63 mouse->double_click_time = 500; 64 #endif 65 } 66 } 67 68 static void SDLCALL 69 SDL_MouseDoubleClickRadiusChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 70 { 71 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 72 73 if (hint && *hint) { 74 mouse->double_click_radius = SDL_atoi(hint); 75 } else { 76 mouse->double_click_radius = 32; /* 32 pixels seems about right for touch interfaces */ 77 } 78 } 79 80 static void SDLCALL 81 SDL_MouseNormalSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 82 { 83 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 84 85 if (hint && *hint) { 86 mouse->normal_speed_scale = (float)SDL_atof(hint); 87 } else { 88 mouse->normal_speed_scale = 1.0f; 89 } 90 } 91 92 static void SDLCALL 93 SDL_MouseRelativeSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 94 { 95 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 96 97 if (hint && *hint) { 98 mouse->relative_speed_scale = (float)SDL_atof(hint); 99 } else { 100 mouse->relative_speed_scale = 1.0f; 101 } 102 } 103 104 static void SDLCALL 105 SDL_TouchMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 106 { 107 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 108 109 mouse->touch_mouse_events = SDL_GetStringBoolean(hint, SDL_TRUE); 110 } 111 112 static void SDLCALL 113 SDL_MouseTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 114 { 115 SDL_Mouse *mouse = (SDL_Mouse *)userdata; 116 SDL_bool default_value; 117 118 #if defined(__ANDROID__) || (defined(__IPHONEOS__) && !defined(__TVOS__)) 119 default_value = SDL_TRUE; 120 #else 121 default_value = SDL_FALSE; 122 #endif 123 mouse->mouse_touch_events = SDL_GetStringBoolean(hint, default_value); 124 125 if (mouse->mouse_touch_events) { 126 SDL_AddTouch(SDL_MOUSE_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "mouse_input"); 127 } 128 } 129 130 /* Public functions */ 131 int 132 SDL_MouseInit(void) 133 { 134 SDL_Mouse *mouse = SDL_GetMouse(); 135 136 SDL_zerop(mouse); 137 138 SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME, 139 SDL_MouseDoubleClickTimeChanged, mouse); 140 141 SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS, 142 SDL_MouseDoubleClickRadiusChanged, mouse); 143 144 SDL_AddHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE, 145 SDL_MouseNormalSpeedScaleChanged, mouse); 146 147 SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE, 148 SDL_MouseRelativeSpeedScaleChanged, mouse); 149 150 SDL_AddHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS, 151 SDL_TouchMouseEventsChanged, mouse); 152 153 SDL_AddHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS, 154 SDL_MouseTouchEventsChanged, mouse); 155 156 mouse->was_touch_mouse_events = SDL_FALSE; /* no touch to mouse movement event pending */ 157 158 mouse->cursor_shown = SDL_TRUE; 159 160 return (0); 161 } 162 163 void 164 SDL_SetDefaultCursor(SDL_Cursor * cursor) 165 { 166 SDL_Mouse *mouse = SDL_GetMouse(); 167 168 mouse->def_cursor = cursor; 169 if (!mouse->cur_cursor) { 170 SDL_SetCursor(cursor); 171 } 172 } 173 174 SDL_Mouse * 175 SDL_GetMouse(void) 176 { 177 return &SDL_mouse; 178 } 179 180 SDL_Window * 181 SDL_GetMouseFocus(void) 182 { 183 SDL_Mouse *mouse = SDL_GetMouse(); 184 185 return mouse->focus; 186 } 187 188 #if 0 189 void 190 SDL_ResetMouse(void) 191 { 192 SDL_Mouse *mouse = SDL_GetMouse(); 193 Uint8 i; 194 195 #ifdef DEBUG_MOUSE 196 printf("Resetting mouse\n"); 197 #endif 198 for (i = 1; i <= sizeof(mouse->buttonstate)*8; ++i) { 199 if (mouse->buttonstate & SDL_BUTTON(i)) { 200 SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, i); 201 } 202 } 203 SDL_assert(mouse->buttonstate == 0); 204 } 205 #endif 206 207 void 208 SDL_SetMouseFocus(SDL_Window * window) 209 { 210 SDL_Mouse *mouse = SDL_GetMouse(); 211 212 if (mouse->focus == window) { 213 return; 214 } 215 216 /* Actually, this ends up being a bad idea, because most operating 217 systems have an implicit grab when you press the mouse button down 218 so you can drag things out of the window and then get the mouse up 219 when it happens. So, #if 0... 220 */ 221 #if 0 222 if (mouse->focus && !window) { 223 /* We won't get anymore mouse messages, so reset mouse state */ 224 SDL_ResetMouse(); 225 } 226 #endif 227 228 /* See if the current window has lost focus */ 229 if (mouse->focus) { 230 SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_LEAVE, 0, 0); 231 } 232 233 mouse->focus = window; 234 mouse->has_position = SDL_FALSE; 235 236 if (mouse->focus) { 237 SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_ENTER, 0, 0); 238 } 239 240 /* Update cursor visibility */ 241 SDL_SetCursor(NULL); 242 } 243 244 /* Check to see if we need to synthesize focus events */ 245 static SDL_bool 246 SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate, SDL_bool send_mouse_motion) 247 { 248 SDL_Mouse *mouse = SDL_GetMouse(); 249 SDL_bool inWindow = SDL_TRUE; 250 251 if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) { 252 int w, h; 253 SDL_GetWindowSize(window, &w, &h); 254 if (x < 0 || y < 0 || x >= w || y >= h) { 255 inWindow = SDL_FALSE; 256 } 257 } 258 259 /* Linux doesn't give you mouse events outside your window unless you grab 260 the pointer. 261 262 Windows doesn't give you mouse events outside your window unless you call 263 SetCapture(). 264 265 Both of these are slightly scary changes, so for now we'll punt and if the 266 mouse leaves the window you'll lose mouse focus and reset button state. 267 */ 268 #ifdef SUPPORT_DRAG_OUTSIDE_WINDOW 269 if (!inWindow && !buttonstate) { 270 #else 271 if (!inWindow) { 272 #endif 273 if (window == mouse->focus) { 274 #ifdef DEBUG_MOUSE 275 printf("Mouse left window, synthesizing move & focus lost event\n"); 276 #endif 277 if (send_mouse_motion) { 278 SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); 279 } 280 SDL_SetMouseFocus(NULL); 281 } 282 return SDL_FALSE; 283 } 284 285 if (window != mouse->focus) { 286 #ifdef DEBUG_MOUSE 287 printf("Mouse entered window, synthesizing focus gain & move event\n"); 288 #endif 289 SDL_SetMouseFocus(window); 290 if (send_mouse_motion) { 291 SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); 292 } 293 } 294 return SDL_TRUE; 295 } 296 297 int 298 SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y) 299 { 300 if (window && !relative) { 301 SDL_Mouse *mouse = SDL_GetMouse(); 302 if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate, (mouseID == SDL_TOUCH_MOUSEID) ? SDL_FALSE : SDL_TRUE)) { 303 return 0; 304 } 305 } 306 307 return SDL_PrivateSendMouseMotion(window, mouseID, relative, x, y); 308 } 309 310 static int 311 GetScaledMouseDelta(float scale, int value, float *accum) 312 { 313 if (scale != 1.0f) { 314 *accum += scale * value; 315 if (*accum >= 0.0f) { 316 value = (int)SDL_floor(*accum); 317 } else { 318 value = (int)SDL_ceil(*accum); 319 } 320 *accum -= value; 321 } 322 return value; 323 } 324 325 static int 326 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y) 327 { 328 SDL_Mouse *mouse = SDL_GetMouse(); 329 int posted; 330 int xrel; 331 int yrel; 332 333 /* SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events */ 334 if (mouse->mouse_touch_events) { 335 if (mouseID != SDL_TOUCH_MOUSEID && !relative && track_mouse_down) { 336 if (window) { 337 float fx = (float)x / (float)window->w; 338 float fy = (float)y / (float)window->h; 339 SDL_SendTouchMotion(SDL_MOUSE_TOUCHID, 0, window, fx, fy, 1.0f); 340 } 341 } 342 } 343 344 /* SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer */ 345 if (mouse->touch_mouse_events == 0) { 346 if (mouseID == SDL_TOUCH_MOUSEID) { 347 return 0; 348 } 349 } 350 351 if (mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp) { 352 int center_x = 0, center_y = 0; 353 SDL_GetWindowSize(window, ¢er_x, ¢er_y); 354 center_x /= 2; 355 center_y /= 2; 356 if (x == center_x && y == center_y) { 357 mouse->last_x = center_x; 358 mouse->last_y = center_y; 359 return 0; 360 } 361 SDL_WarpMouseInWindow(window, center_x, center_y); 362 } 363 364 if (relative) { 365 if (mouse->relative_mode) { 366 x = GetScaledMouseDelta(mouse->relative_speed_scale, x, &mouse->scale_accum_x); 367 y = GetScaledMouseDelta(mouse->relative_speed_scale, y, &mouse->scale_accum_y); 368 } else { 369 x = GetScaledMouseDelta(mouse->normal_speed_scale, x, &mouse->scale_accum_x); 370 y = GetScaledMouseDelta(mouse->normal_speed_scale, y, &mouse->scale_accum_y); 371 } 372 xrel = x; 373 yrel = y; 374 x = (mouse->last_x + xrel); 375 y = (mouse->last_y + yrel); 376 } else { 377 xrel = x - mouse->last_x; 378 yrel = y - mouse->last_y; 379 } 380 381 /* Ignore relative motion when first positioning the mouse */ 382 if (!mouse->has_position) { 383 xrel = 0; 384 yrel = 0; 385 mouse->has_position = SDL_TRUE; 386 } else if (!xrel && !yrel) { /* Drop events that don't change state */ 387 #ifdef DEBUG_MOUSE 388 printf("Mouse event didn't change state - dropped!\n"); 389 #endif 390 return 0; 391 } 392 393 /* Ignore relative motion positioning the first touch */ 394 if (mouseID == SDL_TOUCH_MOUSEID && !mouse->buttonstate) { 395 xrel = 0; 396 yrel = 0; 397 } 398 399 /* Update internal mouse coordinates */ 400 if (!mouse->relative_mode) { 401 mouse->x = x; 402 mouse->y = y; 403 } else { 404 mouse->x += xrel; 405 mouse->y += yrel; 406 } 407 408 /* make sure that the pointers find themselves inside the windows, 409 unless we have the mouse captured. */ 410 if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) { 411 int x_max = 0, y_max = 0; 412 413 /* !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? */ 414 SDL_GetWindowSize(mouse->focus, &x_max, &y_max); 415 --x_max; 416 --y_max; 417 418 if (mouse->x > x_max) { 419 mouse->x = x_max; 420 } 421 if (mouse->x < 0) { 422 mouse->x = 0; 423 } 424 425 if (mouse->y > y_max) { 426 mouse->y = y_max; 427 } 428 if (mouse->y < 0) { 429 mouse->y = 0; 430 } 431 } 432 433 mouse->xdelta += xrel; 434 mouse->ydelta += yrel; 435 436 /* Move the mouse cursor, if needed */ 437 if (mouse->cursor_shown && !mouse->relative_mode && 438 mouse->MoveCursor && mouse->cur_cursor) { 439 mouse->MoveCursor(mouse->cur_cursor); 440 } 441 442 /* Post the event, if desired */ 443 posted = 0; 444 if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) { 445 SDL_Event event; 446 event.motion.type = SDL_MOUSEMOTION; 447 event.motion.windowID = mouse->focus ? mouse->focus->id : 0; 448 event.motion.which = mouseID; 449 /* Set us pending (or clear during a normal mouse movement event) as having triggered */ 450 mouse->was_touch_mouse_events = (mouseID == SDL_TOUCH_MOUSEID)? SDL_TRUE : SDL_FALSE; 451 event.motion.state = mouse->buttonstate; 452 event.motion.x = mouse->x; 453 event.motion.y = mouse->y; 454 event.motion.xrel = xrel; 455 event.motion.yrel = yrel; 456 posted = (SDL_PushEvent(&event) > 0); 457 } 458 if (relative) { 459 mouse->last_x = mouse->x; 460 mouse->last_y = mouse->y; 461 } else { 462 /* Use unclamped values if we're getting events outside the window */ 463 mouse->last_x = x; 464 mouse->last_y = y; 465 } 466 return posted; 467 } 468 469 static SDL_MouseClickState *GetMouseClickState(SDL_Mouse *mouse, Uint8 button) 470 { 471 if (button >= mouse->num_clickstates) { 472 int i, count = button + 1; 473 SDL_MouseClickState *clickstate = (SDL_MouseClickState *)SDL_realloc(mouse->clickstate, count * sizeof(*mouse->clickstate)); 474 if (!clickstate) { 475 return NULL; 476 } 477 mouse->clickstate = clickstate; 478 479 for (i = mouse->num_clickstates; i < count; ++i) { 480 SDL_zero(mouse->clickstate[i]); 481 } 482 mouse->num_clickstates = count; 483 } 484 return &mouse->clickstate[button]; 485 } 486 487 static int 488 SDL_PrivateSendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks) 489 { 490 SDL_Mouse *mouse = SDL_GetMouse(); 491 int posted; 492 Uint32 type; 493 Uint32 buttonstate = mouse->buttonstate; 494 495 /* SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events */ 496 if (mouse->mouse_touch_events) { 497 if (mouseID != SDL_TOUCH_MOUSEID && button == SDL_BUTTON_LEFT) { 498 if (state == SDL_PRESSED) { 499 track_mouse_down = SDL_TRUE; 500 } else { 501 track_mouse_down = SDL_FALSE; 502 } 503 if (window) { 504 float fx = (float)mouse->x / (float)window->w; 505 float fy = (float)mouse->y / (float)window->h; 506 SDL_SendTouch(SDL_MOUSE_TOUCHID, 0, window, track_mouse_down, fx, fy, 1.0f); 507 } 508 } 509 } 510 511 /* SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer */ 512 if (mouse->touch_mouse_events == 0) { 513 if (mouseID == SDL_TOUCH_MOUSEID) { 514 return 0; 515 } 516 } 517 518 /* Figure out which event to perform */ 519 switch (state) { 520 case SDL_PRESSED: 521 type = SDL_MOUSEBUTTONDOWN; 522 buttonstate |= SDL_BUTTON(button); 523 break; 524 case SDL_RELEASED: 525 type = SDL_MOUSEBUTTONUP; 526 buttonstate &= ~SDL_BUTTON(button); 527 break; 528 default: 529 /* Invalid state -- bail */ 530 return 0; 531 } 532 533 /* We do this after calculating buttonstate so button presses gain focus */ 534 if (window && state == SDL_PRESSED) { 535 SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE); 536 } 537 538 if (buttonstate == mouse->buttonstate) { 539 /* Ignore this event, no state change */ 540 return 0; 541 } 542 mouse->buttonstate = buttonstate; 543 544 if (clicks < 0) { 545 SDL_MouseClickState *clickstate = GetMouseClickState(mouse, button); 546 if (clickstate) { 547 if (state == SDL_PRESSED) { 548 Uint32 now = SDL_GetTicks(); 549 550 if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + mouse->double_click_time) || 551 SDL_abs(mouse->x - clickstate->last_x) > mouse->double_click_radius || 552 SDL_abs(mouse->y - clickstate->last_y) > mouse->double_click_radius) { 553 clickstate->click_count = 0; 554 } 555 clickstate->last_timestamp = now; 556 clickstate->last_x = mouse->x; 557 clickstate->last_y = mouse->y; 558 if (clickstate->click_count < 255) { 559 ++clickstate->click_count; 560 } 561 } 562 clicks = clickstate->click_count; 563 } else { 564 clicks = 1; 565 } 566 } 567 568 /* Post the event, if desired */ 569 posted = 0; 570 if (SDL_GetEventState(type) == SDL_ENABLE) { 571 SDL_Event event; 572 event.type = type; 573 event.button.windowID = mouse->focus ? mouse->focus->id : 0; 574 event.button.which = mouseID; 575 event.button.state = state; 576 event.button.button = button; 577 event.button.clicks = (Uint8) SDL_min(clicks, 255); 578 event.button.x = mouse->x; 579 event.button.y = mouse->y; 580 posted = (SDL_PushEvent(&event) > 0); 581 } 582 583 /* We do this after dispatching event so button releases can lose focus */ 584 if (window && state == SDL_RELEASED) { 585 SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE); 586 } 587 588 return posted; 589 } 590 591 int 592 SDL_SendMouseButtonClicks(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks) 593 { 594 clicks = SDL_max(clicks, 0); 595 return SDL_PrivateSendMouseButton(window, mouseID, state, button, clicks); 596 } 597 598 int 599 SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button) 600 { 601 return SDL_PrivateSendMouseButton(window, mouseID, state, button, -1); 602 } 603 604 int 605 SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction) 606 { 607 SDL_Mouse *mouse = SDL_GetMouse(); 608 int posted; 609 int integral_x, integral_y; 610 611 if (window) { 612 SDL_SetMouseFocus(window); 613 } 614 615 if (x == 0.0f && y == 0.0f) { 616 return 0; 617 } 618 619 mouse->accumulated_wheel_x += x; 620 if (mouse->accumulated_wheel_x > 0) { 621 integral_x = (int)SDL_floor(mouse->accumulated_wheel_x); 622 } else if (mouse->accumulated_wheel_x < 0) { 623 integral_x = (int)SDL_ceil(mouse->accumulated_wheel_x); 624 } else { 625 integral_x = 0; 626 } 627 mouse->accumulated_wheel_x -= integral_x; 628 629 mouse->accumulated_wheel_y += y; 630 if (mouse->accumulated_wheel_y > 0) { 631 integral_y = (int)SDL_floor(mouse->accumulated_wheel_y); 632 } else if (mouse->accumulated_wheel_y < 0) { 633 integral_y = (int)SDL_ceil(mouse->accumulated_wheel_y); 634 } else { 635 integral_y = 0; 636 } 637 mouse->accumulated_wheel_y -= integral_y; 638 639 /* Post the event, if desired */ 640 posted = 0; 641 if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) { 642 SDL_Event event; 643 event.type = SDL_MOUSEWHEEL; 644 event.wheel.windowID = mouse->focus ? mouse->focus->id : 0; 645 event.wheel.which = mouseID; 646 #if 0 /* Uncomment this when it goes in for SDL 2.1 */ 647 event.wheel.preciseX = x; 648 event.wheel.preciseY = y; 649 #endif 650 event.wheel.x = integral_x; 651 event.wheel.y = integral_y; 652 event.wheel.direction = (Uint32)direction; 653 posted = (SDL_PushEvent(&event) > 0); 654 } 655 return posted; 656 } 657 658 void 659 SDL_MouseQuit(void) 660 { 661 SDL_Cursor *cursor, *next; 662 SDL_Mouse *mouse = SDL_GetMouse(); 663 664 if (mouse->CaptureMouse) { 665 SDL_CaptureMouse(SDL_FALSE); 666 } 667 SDL_SetRelativeMouseMode(SDL_FALSE); 668 SDL_ShowCursor(1); 669 670 cursor = mouse->cursors; 671 while (cursor) { 672 next = cursor->next; 673 SDL_FreeCursor(cursor); 674 cursor = next; 675 } 676 mouse->cursors = NULL; 677 mouse->cur_cursor = NULL; 678 679 if (mouse->def_cursor && mouse->FreeCursor) { 680 mouse->FreeCursor(mouse->def_cursor); 681 mouse->def_cursor = NULL; 682 } 683 684 if (mouse->clickstate) { 685 SDL_free(mouse->clickstate); 686 mouse->clickstate = NULL; 687 } 688 689 SDL_DelHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE, 690 SDL_MouseNormalSpeedScaleChanged, mouse); 691 692 SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE, 693 SDL_MouseRelativeSpeedScaleChanged, mouse); 694 } 695 696 Uint32 697 SDL_GetMouseState(int *x, int *y) 698 { 699 SDL_Mouse *mouse = SDL_GetMouse(); 700 701 if (x) { 702 *x = mouse->x; 703 } 704 if (y) { 705 *y = mouse->y; 706 } 707 return mouse->buttonstate; 708 } 709 710 Uint32 711 SDL_GetRelativeMouseState(int *x, int *y) 712 { 713 SDL_Mouse *mouse = SDL_GetMouse(); 714 715 if (x) { 716 *x = mouse->xdelta; 717 } 718 if (y) { 719 *y = mouse->ydelta; 720 } 721 mouse->xdelta = 0; 722 mouse->ydelta = 0; 723 return mouse->buttonstate; 724 } 725 726 Uint32 727 SDL_GetGlobalMouseState(int *x, int *y) 728 { 729 SDL_Mouse *mouse = SDL_GetMouse(); 730 731 if (mouse->GetGlobalMouseState) { 732 int tmpx, tmpy; 733 734 /* make sure these are never NULL for the backend implementations... */ 735 if (!x) { 736 x = &tmpx; 737 } 738 if (!y) { 739 y = &tmpy; 740 } 741 742 *x = *y = 0; 743 744 return mouse->GetGlobalMouseState(x, y); 745 } else { 746 return SDL_GetMouseState(x, y); 747 } 748 } 749 750 void 751 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y) 752 { 753 SDL_Mouse *mouse = SDL_GetMouse(); 754 755 if (window == NULL) { 756 window = mouse->focus; 757 } 758 759 if (window == NULL) { 760 return; 761 } 762 763 if (mouse->WarpMouse) { 764 mouse->WarpMouse(window, x, y); 765 } else { 766 SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y); 767 } 768 } 769 770 int 771 SDL_WarpMouseGlobal(int x, int y) 772 { 773 SDL_Mouse *mouse = SDL_GetMouse(); 774 775 if (mouse->WarpMouseGlobal) { 776 return mouse->WarpMouseGlobal(x, y); 777 } 778 779 return SDL_Unsupported(); 780 } 781 782 static SDL_bool 783 ShouldUseRelativeModeWarp(SDL_Mouse *mouse) 784 { 785 if (!mouse->WarpMouse) { 786 /* Need this functionality for relative mode warp implementation */ 787 return SDL_FALSE; 788 } 789 790 return SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_FALSE); 791 } 792 793 int 794 SDL_SetRelativeMouseMode(SDL_bool enabled) 795 { 796 SDL_Mouse *mouse = SDL_GetMouse(); 797 SDL_Window *focusWindow = SDL_GetKeyboardFocus(); 798 799 if (enabled == mouse->relative_mode) { 800 return 0; 801 } 802 803 /* Set the relative mode */ 804 if (!enabled && mouse->relative_mode_warp) { 805 mouse->relative_mode_warp = SDL_FALSE; 806 } else if (enabled && ShouldUseRelativeModeWarp(mouse)) { 807 mouse->relative_mode_warp = SDL_TRUE; 808 } else if (!mouse->SetRelativeMouseMode || mouse->SetRelativeMouseMode(enabled) < 0) { 809 if (enabled) { 810 /* Fall back to warp mode if native relative mode failed */ 811 if (!mouse->WarpMouse) { 812 return SDL_SetError("No relative mode implementation available"); 813 } 814 mouse->relative_mode_warp = SDL_TRUE; 815 } 816 } 817 mouse->relative_mode = enabled; 818 mouse->scale_accum_x = 0.0f; 819 mouse->scale_accum_y = 0.0f; 820 821 if (enabled && focusWindow) { 822 /* Center it in the focused window to prevent clicks from going through 823 * to background windows. 824 */ 825 SDL_SetMouseFocus(focusWindow); 826 SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2); 827 } 828 829 if (mouse->focus) { 830 SDL_UpdateWindowGrab(mouse->focus); 831 832 /* Put the cursor back to where the application expects it */ 833 if (!enabled) { 834 SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y); 835 } 836 } 837 838 /* Flush pending mouse motion - ideally we would pump events, but that's not always safe */ 839 SDL_FlushEvent(SDL_MOUSEMOTION); 840 841 /* Update cursor visibility */ 842 SDL_SetCursor(NULL); 843 844 return 0; 845 } 846 847 SDL_bool 848 SDL_GetRelativeMouseMode() 849 { 850 SDL_Mouse *mouse = SDL_GetMouse(); 851 852 return mouse->relative_mode; 853 } 854 855 int 856 SDL_CaptureMouse(SDL_bool enabled) 857 { 858 SDL_Mouse *mouse = SDL_GetMouse(); 859 SDL_Window *focusWindow; 860 SDL_bool isCaptured; 861 862 if (!mouse->CaptureMouse) { 863 return SDL_Unsupported(); 864 } 865 866 focusWindow = SDL_GetKeyboardFocus(); 867 868 isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE); 869 if (isCaptured == enabled) { 870 return 0; /* already done! */ 871 } 872 873 if (enabled) { 874 if (!focusWindow) { 875 return SDL_SetError("No window has focus"); 876 } else if (mouse->CaptureMouse(focusWindow) == -1) { 877 return -1; /* CaptureMouse() should call SetError */ 878 } 879 focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE; 880 } else { 881 if (mouse->CaptureMouse(NULL) == -1) { 882 return -1; /* CaptureMouse() should call SetError */ 883 } 884 focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE; 885 } 886 887 return 0; 888 } 889 890 SDL_Cursor * 891 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask, 892 int w, int h, int hot_x, int hot_y) 893 { 894 SDL_Surface *surface; 895 SDL_Cursor *cursor; 896 int x, y; 897 Uint32 *pixel; 898 Uint8 datab = 0, maskb = 0; 899 const Uint32 black = 0xFF000000; 900 const Uint32 white = 0xFFFFFFFF; 901 const Uint32 transparent = 0x00000000; 902 903 /* Make sure the width is a multiple of 8 */ 904 w = ((w + 7) & ~7); 905 906 /* Create the surface from a bitmap */ 907 surface = SDL_CreateRGBSurface(0, w, h, 32, 908 0x00FF0000, 909 0x0000FF00, 910 0x000000FF, 911 0xFF000000); 912 if (!surface) { 913 return NULL; 914 } 915 for (y = 0; y < h; ++y) { 916 pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch); 917 for (x = 0; x < w; ++x) { 918 if ((x % 8) == 0) { 919 datab = *data++; 920 maskb = *mask++; 921 } 922 if (maskb & 0x80) { 923 *pixel++ = (datab & 0x80) ? black : white; 924 } else { 925 *pixel++ = (datab & 0x80) ? black : transparent; 926 } 927 datab <<= 1; 928 maskb <<= 1; 929 } 930 } 931 932 cursor = SDL_CreateColorCursor(surface, hot_x, hot_y); 933 934 SDL_FreeSurface(surface); 935 936 return cursor; 937 } 938 939 SDL_Cursor * 940 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y) 941 { 942 SDL_Mouse *mouse = SDL_GetMouse(); 943 SDL_Surface *temp = NULL; 944 SDL_Cursor *cursor; 945 946 if (!surface) { 947 SDL_SetError("Passed NULL cursor surface"); 948 return NULL; 949 } 950 951 if (!mouse->CreateCursor) { 952 SDL_SetError("Cursors are not currently supported"); 953 return NULL; 954 } 955 956 /* Sanity check the hot spot */ 957 if ((hot_x < 0) || (hot_y < 0) || 958 (hot_x >= surface->w) || (hot_y >= surface->h)) { 959 SDL_SetError("Cursor hot spot doesn't lie within cursor"); 960 return NULL; 961 } 962 963 if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) { 964 temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0); 965 if (!temp) { 966 return NULL; 967 } 968 surface = temp; 969 } 970 971 cursor = mouse->CreateCursor(surface, hot_x, hot_y); 972 if (cursor) { 973 cursor->next = mouse->cursors; 974 mouse->cursors = cursor; 975 } 976 977 SDL_FreeSurface(temp); 978 979 return cursor; 980 } 981 982 SDL_Cursor * 983 SDL_CreateSystemCursor(SDL_SystemCursor id) 984 { 985 SDL_Mouse *mouse = SDL_GetMouse(); 986 SDL_Cursor *cursor; 987 988 if (!mouse->CreateSystemCursor) { 989 SDL_SetError("CreateSystemCursor is not currently supported"); 990 return NULL; 991 } 992 993 cursor = mouse->CreateSystemCursor(id); 994 if (cursor) { 995 cursor->next = mouse->cursors; 996 mouse->cursors = cursor; 997 } 998 999 return cursor; 1000 } 1001 1002 /* SDL_SetCursor(NULL) can be used to force the cursor redraw, 1003 if this is desired for any reason. This is used when setting 1004 the video mode and when the SDL window gains the mouse focus. 1005 */ 1006 void 1007 SDL_SetCursor(SDL_Cursor * cursor) 1008 { 1009 SDL_Mouse *mouse = SDL_GetMouse(); 1010 1011 /* Set the new cursor */ 1012 if (cursor) { 1013 /* Make sure the cursor is still valid for this mouse */ 1014 if (cursor != mouse->def_cursor) { 1015 SDL_Cursor *found; 1016 for (found = mouse->cursors; found; found = found->next) { 1017 if (found == cursor) { 1018 break; 1019 } 1020 } 1021 if (!found) { 1022 SDL_SetError("Cursor not associated with the current mouse"); 1023 return; 1024 } 1025 } 1026 mouse->cur_cursor = cursor; 1027 } else { 1028 if (mouse->focus) { 1029 cursor = mouse->cur_cursor; 1030 } else { 1031 cursor = mouse->def_cursor; 1032 } 1033 } 1034 1035 if (cursor && mouse->cursor_shown && !mouse->relative_mode) { 1036 if (mouse->ShowCursor) { 1037 mouse->ShowCursor(cursor); 1038 } 1039 } else { 1040 if (mouse->ShowCursor) { 1041 mouse->ShowCursor(NULL); 1042 } 1043 } 1044 } 1045 1046 SDL_Cursor * 1047 SDL_GetCursor(void) 1048 { 1049 SDL_Mouse *mouse = SDL_GetMouse(); 1050 1051 if (!mouse) { 1052 return NULL; 1053 } 1054 return mouse->cur_cursor; 1055 } 1056 1057 SDL_Cursor * 1058 SDL_GetDefaultCursor(void) 1059 { 1060 SDL_Mouse *mouse = SDL_GetMouse(); 1061 1062 if (!mouse) { 1063 return NULL; 1064 } 1065 return mouse->def_cursor; 1066 } 1067 1068 void 1069 SDL_FreeCursor(SDL_Cursor * cursor) 1070 { 1071 SDL_Mouse *mouse = SDL_GetMouse(); 1072 SDL_Cursor *curr, *prev; 1073 1074 if (!cursor) { 1075 return; 1076 } 1077 1078 if (cursor == mouse->def_cursor) { 1079 return; 1080 } 1081 if (cursor == mouse->cur_cursor) { 1082 SDL_SetCursor(mouse->def_cursor); 1083 } 1084 1085 for (prev = NULL, curr = mouse->cursors; curr; 1086 prev = curr, curr = curr->next) { 1087 if (curr == cursor) { 1088 if (prev) { 1089 prev->next = curr->next; 1090 } else { 1091 mouse->cursors = curr->next; 1092 } 1093 1094 if (mouse->FreeCursor) { 1095 mouse->FreeCursor(curr); 1096 } 1097 return; 1098 } 1099 } 1100 } 1101 1102 int 1103 SDL_ShowCursor(int toggle) 1104 { 1105 SDL_Mouse *mouse = SDL_GetMouse(); 1106 SDL_bool shown; 1107 1108 if (!mouse) { 1109 return 0; 1110 } 1111 1112 shown = mouse->cursor_shown; 1113 if (toggle >= 0) { 1114 if (toggle) { 1115 mouse->cursor_shown = SDL_TRUE; 1116 } else { 1117 mouse->cursor_shown = SDL_FALSE; 1118 } 1119 if (mouse->cursor_shown != shown) { 1120 SDL_SetCursor(NULL); 1121 } 1122 } 1123 return shown; 1124 } 1125 1126 /* vi: set ts=4 sw=4 expandtab: */