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