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