src/events/SDL_mouse.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 13 Jun 2019 01:57:13 -0400
changeset 12854 d7d64c1a3969
parent 12753 ba1a66b57385
child 12855 ac0c65535817
permissions -rw-r--r--
cocoa: Revised synthesized mouse/touch event strategy.

I _think_ I understand what Sylvain is working on here now, so hopefully I
got this right.

Fixes Bugzilla #4576.

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