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