src/events/SDL_mouse.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 06 Nov 2017 15:29:24 -0500
changeset 11693 4c8bd26f1aab
parent 11622 7abd358d3cec
child 11811 5d94cb6b24d3
permissions -rw-r--r--
mouse: remove assert for unimplemented platforms (thanks, tomwardio!).

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