src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 12 Oct 2017 13:28:48 -0700
changeset 11607 db7ee6a1ba6a
parent 11313 14d04f0a4914
child 11622 7abd358d3cec
permissions -rw-r--r--
Fixed memory leak in Cocoa mouse code
The video quit call cleans up the mouse cursor driver data, which happens after mouse quit
     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 
   584     if (mouse->def_cursor && mouse->FreeCursor) {
   585         mouse->FreeCursor(mouse->def_cursor);
   586     }
   587 
   588     if (mouse->clickstate) {
   589         SDL_free(mouse->clickstate);
   590     }
   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: */