src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 16 Mar 2019 19:07:34 -0700
changeset 12651 d188272453ce
parent 12620 0010b9a064d3
child 12687 712d84dd14c9
permissions -rw-r--r--
Fixed bug 4450 - SDL_mouse.c fails to compile with CMake generated Visual Studio files if SDL_VIDEO_VULKAN 0/undefined

Max Waine

SDL_mouse.c, if compiled for Windows, requires GetDoubleClickTime to compile (available from winuser.h). Without Vulkan present this fails to compile as the include chain for winuser.h is the following.

SDL_mouse.c -> SDL_sysvideo.h -> SDL_vulkan_internal.h -> SDL_windows.h -> windows.h -> winuser.h.
Problem is that SDL_vulkan_internal.h doesn't include SDL_windows.h if Vulkan isn't present, so under MinGW/GCC it will give a -Wimplicit-function-declaration warning for GetDoubleClickTime, and under MSVC fails to compile completely.

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