src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 14 Sep 2018 19:26:26 -0700
changeset 12188 f081a5675f93
parent 12024 3688283680b1
child 12462 b57435750c5b
permissions -rw-r--r--
Added hints SDL_HINT_MOUSE_DOUBLE_CLICK_TIME and SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS to allow tuning double-click sensitivity.
Also increased the default double-click radius to 32 pixels to be more forgiving for touch interfaces
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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 && !y) {
   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 
   611     if (mouse->def_cursor && mouse->FreeCursor) {
   612         mouse->FreeCursor(mouse->def_cursor);
   613         mouse->def_cursor = NULL;
   614     }
   615 
   616     if (mouse->clickstate) {
   617         SDL_free(mouse->clickstate);
   618         mouse->clickstate = NULL;
   619     }
   620 
   621     SDL_DelHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE,
   622                         SDL_MouseNormalSpeedScaleChanged, mouse);
   623 
   624     SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE,
   625                         SDL_MouseRelativeSpeedScaleChanged, mouse);
   626 }
   627 
   628 Uint32
   629 SDL_GetMouseState(int *x, int *y)
   630 {
   631     SDL_Mouse *mouse = SDL_GetMouse();
   632 
   633     if (x) {
   634         *x = mouse->x;
   635     }
   636     if (y) {
   637         *y = mouse->y;
   638     }
   639     return mouse->buttonstate;
   640 }
   641 
   642 Uint32
   643 SDL_GetRelativeMouseState(int *x, int *y)
   644 {
   645     SDL_Mouse *mouse = SDL_GetMouse();
   646 
   647     if (x) {
   648         *x = mouse->xdelta;
   649     }
   650     if (y) {
   651         *y = mouse->ydelta;
   652     }
   653     mouse->xdelta = 0;
   654     mouse->ydelta = 0;
   655     return mouse->buttonstate;
   656 }
   657 
   658 Uint32
   659 SDL_GetGlobalMouseState(int *x, int *y)
   660 {
   661     SDL_Mouse *mouse = SDL_GetMouse();
   662     int tmpx, tmpy;
   663 
   664     /* make sure these are never NULL for the backend implementations... */
   665     if (!x) {
   666         x = &tmpx;
   667     }
   668     if (!y) {
   669         y = &tmpy;
   670     }
   671 
   672     *x = *y = 0;
   673 
   674     if (!mouse->GetGlobalMouseState) {
   675         return 0;
   676     }
   677 
   678     return mouse->GetGlobalMouseState(x, y);
   679 }
   680 
   681 void
   682 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   683 {
   684     SDL_Mouse *mouse = SDL_GetMouse();
   685 
   686     if (window == NULL) {
   687         window = mouse->focus;
   688     }
   689 
   690     if (window == NULL) {
   691         return;
   692     }
   693 
   694     if (mouse->WarpMouse) {
   695         mouse->WarpMouse(window, x, y);
   696     } else {
   697         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
   698     }
   699 }
   700 
   701 int
   702 SDL_WarpMouseGlobal(int x, int y)
   703 {
   704     SDL_Mouse *mouse = SDL_GetMouse();
   705 
   706     if (mouse->WarpMouseGlobal) {
   707         return mouse->WarpMouseGlobal(x, y);
   708     }
   709 
   710     return SDL_Unsupported();
   711 }
   712 
   713 static SDL_bool
   714 ShouldUseRelativeModeWarp(SDL_Mouse *mouse)
   715 {
   716     if (!mouse->SetRelativeMouseMode) {
   717         SDL_assert(mouse->WarpMouse);   /* Need this functionality for relative mode warp implementation */
   718         return SDL_TRUE;
   719     }
   720 
   721     return SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_FALSE);
   722 }
   723 
   724 int
   725 SDL_SetRelativeMouseMode(SDL_bool enabled)
   726 {
   727     SDL_Mouse *mouse = SDL_GetMouse();
   728     SDL_Window *focusWindow = SDL_GetKeyboardFocus();
   729 
   730     if (enabled == mouse->relative_mode) {
   731         return 0;
   732     }
   733 
   734     if (enabled && focusWindow) {
   735         /* Center it in the focused window to prevent clicks from going through
   736          * to background windows.
   737          */
   738         SDL_SetMouseFocus(focusWindow);
   739         SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
   740     }
   741 
   742     /* Set the relative mode */
   743     if (!enabled && mouse->relative_mode_warp) {
   744         mouse->relative_mode_warp = SDL_FALSE;
   745     } else if (enabled && ShouldUseRelativeModeWarp(mouse)) {
   746         mouse->relative_mode_warp = SDL_TRUE;
   747     } else if (mouse->SetRelativeMouseMode(enabled) < 0) {
   748         if (enabled) {
   749             /* Fall back to warp mode if native relative mode failed */
   750             if (!mouse->WarpMouse) {
   751                 return SDL_SetError("No relative mode implementation available");
   752             }
   753             mouse->relative_mode_warp = SDL_TRUE;
   754         }
   755     }
   756     mouse->relative_mode = enabled;
   757     mouse->scale_accum_x = 0.0f;
   758     mouse->scale_accum_y = 0.0f;
   759 
   760     if (mouse->focus) {
   761         SDL_UpdateWindowGrab(mouse->focus);
   762 
   763         /* Put the cursor back to where the application expects it */
   764         if (!enabled) {
   765             SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
   766         }
   767     }
   768 
   769     /* Flush pending mouse motion - ideally we would pump events, but that's not always safe */
   770     SDL_FlushEvent(SDL_MOUSEMOTION);
   771 
   772     /* Update cursor visibility */
   773     SDL_SetCursor(NULL);
   774 
   775     return 0;
   776 }
   777 
   778 SDL_bool
   779 SDL_GetRelativeMouseMode()
   780 {
   781     SDL_Mouse *mouse = SDL_GetMouse();
   782 
   783     return mouse->relative_mode;
   784 }
   785 
   786 int
   787 SDL_CaptureMouse(SDL_bool enabled)
   788 {
   789     SDL_Mouse *mouse = SDL_GetMouse();
   790     SDL_Window *focusWindow;
   791     SDL_bool isCaptured;
   792 
   793     if (!mouse->CaptureMouse) {
   794         return SDL_Unsupported();
   795     }
   796 
   797     focusWindow = SDL_GetKeyboardFocus();
   798 
   799     isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE);
   800     if (isCaptured == enabled) {
   801         return 0;  /* already done! */
   802     }
   803 
   804     if (enabled) {
   805         if (!focusWindow) {
   806             return SDL_SetError("No window has focus");
   807         } else if (mouse->CaptureMouse(focusWindow) == -1) {
   808             return -1;  /* CaptureMouse() should call SetError */
   809         }
   810         focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
   811     } else {
   812         if (mouse->CaptureMouse(NULL) == -1) {
   813             return -1;  /* CaptureMouse() should call SetError */
   814         }
   815         focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
   816     }
   817 
   818     return 0;
   819 }
   820 
   821 SDL_Cursor *
   822 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   823                  int w, int h, int hot_x, int hot_y)
   824 {
   825     SDL_Surface *surface;
   826     SDL_Cursor *cursor;
   827     int x, y;
   828     Uint32 *pixel;
   829     Uint8 datab = 0, maskb = 0;
   830     const Uint32 black = 0xFF000000;
   831     const Uint32 white = 0xFFFFFFFF;
   832     const Uint32 transparent = 0x00000000;
   833 
   834     /* Make sure the width is a multiple of 8 */
   835     w = ((w + 7) & ~7);
   836 
   837     /* Create the surface from a bitmap */
   838     surface = SDL_CreateRGBSurface(0, w, h, 32,
   839                                    0x00FF0000,
   840                                    0x0000FF00,
   841                                    0x000000FF,
   842                                    0xFF000000);
   843     if (!surface) {
   844         return NULL;
   845     }
   846     for (y = 0; y < h; ++y) {
   847         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   848         for (x = 0; x < w; ++x) {
   849             if ((x % 8) == 0) {
   850                 datab = *data++;
   851                 maskb = *mask++;
   852             }
   853             if (maskb & 0x80) {
   854                 *pixel++ = (datab & 0x80) ? black : white;
   855             } else {
   856                 *pixel++ = (datab & 0x80) ? black : transparent;
   857             }
   858             datab <<= 1;
   859             maskb <<= 1;
   860         }
   861     }
   862 
   863     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   864 
   865     SDL_FreeSurface(surface);
   866 
   867     return cursor;
   868 }
   869 
   870 SDL_Cursor *
   871 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   872 {
   873     SDL_Mouse *mouse = SDL_GetMouse();
   874     SDL_Surface *temp = NULL;
   875     SDL_Cursor *cursor;
   876 
   877     if (!surface) {
   878         SDL_SetError("Passed NULL cursor surface");
   879         return NULL;
   880     }
   881 
   882     if (!mouse->CreateCursor) {
   883         SDL_SetError("Cursors are not currently supported");
   884         return NULL;
   885     }
   886 
   887     /* Sanity check the hot spot */
   888     if ((hot_x < 0) || (hot_y < 0) ||
   889         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   890         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   891         return NULL;
   892     }
   893 
   894     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   895         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   896         if (!temp) {
   897             return NULL;
   898         }
   899         surface = temp;
   900     }
   901 
   902     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   903     if (cursor) {
   904         cursor->next = mouse->cursors;
   905         mouse->cursors = cursor;
   906     }
   907 
   908     SDL_FreeSurface(temp);
   909 
   910     return cursor;
   911 }
   912 
   913 SDL_Cursor *
   914 SDL_CreateSystemCursor(SDL_SystemCursor id)
   915 {
   916     SDL_Mouse *mouse = SDL_GetMouse();
   917     SDL_Cursor *cursor;
   918 
   919     if (!mouse->CreateSystemCursor) {
   920         SDL_SetError("CreateSystemCursor is not currently supported");
   921         return NULL;
   922     }
   923 
   924     cursor = mouse->CreateSystemCursor(id);
   925     if (cursor) {
   926         cursor->next = mouse->cursors;
   927         mouse->cursors = cursor;
   928     }
   929 
   930     return cursor;
   931 }
   932 
   933 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   934    if this is desired for any reason.  This is used when setting
   935    the video mode and when the SDL window gains the mouse focus.
   936  */
   937 void
   938 SDL_SetCursor(SDL_Cursor * cursor)
   939 {
   940     SDL_Mouse *mouse = SDL_GetMouse();
   941 
   942     /* Set the new cursor */
   943     if (cursor) {
   944         /* Make sure the cursor is still valid for this mouse */
   945         if (cursor != mouse->def_cursor) {
   946             SDL_Cursor *found;
   947             for (found = mouse->cursors; found; found = found->next) {
   948                 if (found == cursor) {
   949                     break;
   950                 }
   951             }
   952             if (!found) {
   953                 SDL_SetError("Cursor not associated with the current mouse");
   954                 return;
   955             }
   956         }
   957         mouse->cur_cursor = cursor;
   958     } else {
   959         if (mouse->focus) {
   960             cursor = mouse->cur_cursor;
   961         } else {
   962             cursor = mouse->def_cursor;
   963         }
   964     }
   965 
   966     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   967         if (mouse->ShowCursor) {
   968             mouse->ShowCursor(cursor);
   969         }
   970     } else {
   971         if (mouse->ShowCursor) {
   972             mouse->ShowCursor(NULL);
   973         }
   974     }
   975 }
   976 
   977 SDL_Cursor *
   978 SDL_GetCursor(void)
   979 {
   980     SDL_Mouse *mouse = SDL_GetMouse();
   981 
   982     if (!mouse) {
   983         return NULL;
   984     }
   985     return mouse->cur_cursor;
   986 }
   987 
   988 SDL_Cursor *
   989 SDL_GetDefaultCursor(void)
   990 {
   991     SDL_Mouse *mouse = SDL_GetMouse();
   992 
   993     if (!mouse) {
   994         return NULL;
   995     }
   996     return mouse->def_cursor;
   997 }
   998 
   999 void
  1000 SDL_FreeCursor(SDL_Cursor * cursor)
  1001 {
  1002     SDL_Mouse *mouse = SDL_GetMouse();
  1003     SDL_Cursor *curr, *prev;
  1004 
  1005     if (!cursor) {
  1006         return;
  1007     }
  1008 
  1009     if (cursor == mouse->def_cursor) {
  1010         return;
  1011     }
  1012     if (cursor == mouse->cur_cursor) {
  1013         SDL_SetCursor(mouse->def_cursor);
  1014     }
  1015 
  1016     for (prev = NULL, curr = mouse->cursors; curr;
  1017          prev = curr, curr = curr->next) {
  1018         if (curr == cursor) {
  1019             if (prev) {
  1020                 prev->next = curr->next;
  1021             } else {
  1022                 mouse->cursors = curr->next;
  1023             }
  1024 
  1025             if (mouse->FreeCursor) {
  1026                 mouse->FreeCursor(curr);
  1027             }
  1028             return;
  1029         }
  1030     }
  1031 }
  1032 
  1033 int
  1034 SDL_ShowCursor(int toggle)
  1035 {
  1036     SDL_Mouse *mouse = SDL_GetMouse();
  1037     SDL_bool shown;
  1038 
  1039     if (!mouse) {
  1040         return 0;
  1041     }
  1042 
  1043     shown = mouse->cursor_shown;
  1044     if (toggle >= 0) {
  1045         if (toggle) {
  1046             mouse->cursor_shown = SDL_TRUE;
  1047         } else {
  1048             mouse->cursor_shown = SDL_FALSE;
  1049         }
  1050         if (mouse->cursor_shown != shown) {
  1051             SDL_SetCursor(NULL);
  1052         }
  1053     }
  1054     return shown;
  1055 }
  1056 
  1057 /* vi: set ts=4 sw=4 expandtab: */