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