src/events/SDL_mouse.c
author Sylvain Becker <sylvain.becker@gmail.com>
Fri, 05 Apr 2019 08:36:31 +0200
changeset 12692 69f24f4ef99e
parent 12688 cc45bcb16ef2
child 12695 0c4d04572622
permissions -rw-r--r--
Android: default SDL_HINT_MOUSE_TOUCH_EVENTS to 1 as previous behaviour
     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__)
   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     /* Ignore relative motion positioning the first touch */
   387     if (mouseID == SDL_TOUCH_MOUSEID && !mouse->buttonstate) {
   388         xrel = 0;
   389         yrel = 0;
   390     }
   391 
   392     /* Update internal mouse coordinates */
   393     if (!mouse->relative_mode) {
   394         mouse->x = x;
   395         mouse->y = y;
   396     } else {
   397         mouse->x += xrel;
   398         mouse->y += yrel;
   399     }
   400 
   401     /* make sure that the pointers find themselves inside the windows,
   402        unless we have the mouse captured. */
   403     if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) {
   404         int x_max = 0, y_max = 0;
   405 
   406         /* !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? */
   407         SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
   408         --x_max;
   409         --y_max;
   410 
   411         if (mouse->x > x_max) {
   412             mouse->x = x_max;
   413         }
   414         if (mouse->x < 0) {
   415             mouse->x = 0;
   416         }
   417 
   418         if (mouse->y > y_max) {
   419             mouse->y = y_max;
   420         }
   421         if (mouse->y < 0) {
   422             mouse->y = 0;
   423         }
   424     }
   425 
   426     mouse->xdelta += xrel;
   427     mouse->ydelta += yrel;
   428 
   429     /* Move the mouse cursor, if needed */
   430     if (mouse->cursor_shown && !mouse->relative_mode &&
   431         mouse->MoveCursor && mouse->cur_cursor) {
   432         mouse->MoveCursor(mouse->cur_cursor);
   433     }
   434 
   435     /* Post the event, if desired */
   436     posted = 0;
   437     if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) {
   438         SDL_Event event;
   439         event.motion.type = SDL_MOUSEMOTION;
   440         event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
   441         event.motion.which = mouseID;
   442         event.motion.state = mouse->buttonstate;
   443         event.motion.x = mouse->x;
   444         event.motion.y = mouse->y;
   445         event.motion.xrel = xrel;
   446         event.motion.yrel = yrel;
   447         posted = (SDL_PushEvent(&event) > 0);
   448     }
   449     if (relative) {
   450         mouse->last_x = mouse->x;
   451         mouse->last_y = mouse->y;
   452     } else {
   453         /* Use unclamped values if we're getting events outside the window */
   454         mouse->last_x = x;
   455         mouse->last_y = y;
   456     }
   457     return posted;
   458 }
   459 
   460 static SDL_MouseClickState *GetMouseClickState(SDL_Mouse *mouse, Uint8 button)
   461 {
   462     if (button >= mouse->num_clickstates) {
   463         int i, count = button + 1;
   464         SDL_MouseClickState *clickstate = (SDL_MouseClickState *)SDL_realloc(mouse->clickstate, count * sizeof(*mouse->clickstate));
   465         if (!clickstate) {
   466             return NULL;
   467         }
   468         mouse->clickstate = clickstate;
   469 
   470         for (i = mouse->num_clickstates; i < count; ++i) {
   471             SDL_zero(mouse->clickstate[i]);
   472         }
   473         mouse->num_clickstates = count;
   474     }
   475     return &mouse->clickstate[button];
   476 }
   477 
   478 static int
   479 SDL_PrivateSendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks)
   480 {
   481     SDL_Mouse *mouse = SDL_GetMouse();
   482     int posted;
   483     Uint32 type;
   484     Uint32 buttonstate = mouse->buttonstate;
   485 
   486     /* SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events */
   487     if (mouse->mouse_touch_events) {
   488         if (mouseID != SDL_TOUCH_MOUSEID && button == SDL_BUTTON_LEFT) {
   489             if (window) {
   490                 float fx = (float)mouse->x / (float)window->w;
   491                 float fy = (float)mouse->y / (float)window->h;
   492                 if (state == SDL_PRESSED) {
   493                     track_mouse_down = SDL_TRUE;
   494                 } else {
   495                     track_mouse_down = SDL_FALSE;
   496                 }
   497                 SDL_SendTouch(SDL_MOUSE_TOUCHID, 0, track_mouse_down, fx, fy, 1.0f);
   498             }
   499         }
   500     }
   501 
   502     /* Figure out which event to perform */
   503     switch (state) {
   504     case SDL_PRESSED:
   505         type = SDL_MOUSEBUTTONDOWN;
   506         buttonstate |= SDL_BUTTON(button);
   507         break;
   508     case SDL_RELEASED:
   509         type = SDL_MOUSEBUTTONUP;
   510         buttonstate &= ~SDL_BUTTON(button);
   511         break;
   512     default:
   513         /* Invalid state -- bail */
   514         return 0;
   515     }
   516 
   517     /* We do this after calculating buttonstate so button presses gain focus */
   518     if (window && state == SDL_PRESSED) {
   519         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   520     }
   521 
   522     if (buttonstate == mouse->buttonstate) {
   523         /* Ignore this event, no state change */
   524         return 0;
   525     }
   526     mouse->buttonstate = buttonstate;
   527 
   528     if (clicks < 0) {
   529         SDL_MouseClickState *clickstate = GetMouseClickState(mouse, button);
   530         if (clickstate) {
   531             if (state == SDL_PRESSED) {
   532                 Uint32 now = SDL_GetTicks();
   533 
   534                 if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + mouse->double_click_time) ||
   535                     SDL_abs(mouse->x - clickstate->last_x) > mouse->double_click_radius ||
   536                     SDL_abs(mouse->y - clickstate->last_y) > mouse->double_click_radius) {
   537                     clickstate->click_count = 0;
   538                 }
   539                 clickstate->last_timestamp = now;
   540                 clickstate->last_x = mouse->x;
   541                 clickstate->last_y = mouse->y;
   542                 if (clickstate->click_count < 255) {
   543                     ++clickstate->click_count;
   544                 }
   545             }
   546             clicks = clickstate->click_count;
   547         } else {
   548             clicks = 1;
   549         }
   550     }
   551 
   552     /* Post the event, if desired */
   553     posted = 0;
   554     if (SDL_GetEventState(type) == SDL_ENABLE) {
   555         SDL_Event event;
   556         event.type = type;
   557         event.button.windowID = mouse->focus ? mouse->focus->id : 0;
   558         event.button.which = mouseID;
   559         event.button.state = state;
   560         event.button.button = button;
   561         event.button.clicks = (Uint8) SDL_min(clicks, 255);
   562         event.button.x = mouse->x;
   563         event.button.y = mouse->y;
   564         posted = (SDL_PushEvent(&event) > 0);
   565     }
   566 
   567     /* We do this after dispatching event so button releases can lose focus */
   568     if (window && state == SDL_RELEASED) {
   569         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   570     }
   571 
   572     return posted;
   573 }
   574 
   575 int
   576 SDL_SendMouseButtonClicks(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks)
   577 {
   578     clicks = SDL_max(clicks, 0);
   579     return SDL_PrivateSendMouseButton(window, mouseID, state, button, clicks);
   580 }
   581 
   582 int
   583 SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
   584 {
   585     return SDL_PrivateSendMouseButton(window, mouseID, state, button, -1);
   586 }
   587 
   588 int
   589 SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction)
   590 {
   591     SDL_Mouse *mouse = SDL_GetMouse();
   592     int posted;
   593     int integral_x, integral_y;
   594 
   595     if (window) {
   596         SDL_SetMouseFocus(window);
   597     }
   598 
   599     if (x == 0.0f && y == 0.0f) {
   600         return 0;
   601     }
   602 
   603     mouse->accumulated_wheel_x += x;
   604     if (mouse->accumulated_wheel_x > 0) {
   605         integral_x = (int)SDL_floor(mouse->accumulated_wheel_x);
   606     } else if (mouse->accumulated_wheel_x < 0) {
   607         integral_x = (int)SDL_ceil(mouse->accumulated_wheel_x);
   608     } else {
   609         integral_x = 0;
   610     }
   611     mouse->accumulated_wheel_x -= integral_x;
   612 
   613     mouse->accumulated_wheel_y += y;
   614     if (mouse->accumulated_wheel_y > 0) {
   615         integral_y = (int)SDL_floor(mouse->accumulated_wheel_y);
   616     } else if (mouse->accumulated_wheel_y < 0) {
   617         integral_y = (int)SDL_ceil(mouse->accumulated_wheel_y);
   618     } else {
   619         integral_y = 0;
   620     }
   621     mouse->accumulated_wheel_y -= integral_y;
   622 
   623     /* Post the event, if desired */
   624     posted = 0;
   625     if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) {
   626         SDL_Event event;
   627         event.type = SDL_MOUSEWHEEL;
   628         event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
   629         event.wheel.which = mouseID;
   630 #if 0 /* Uncomment this when it goes in for SDL 2.1 */
   631         event.wheel.preciseX = x;
   632         event.wheel.preciseY = y;
   633 #endif
   634         event.wheel.x = integral_x;
   635         event.wheel.y = integral_y;
   636         event.wheel.direction = (Uint32)direction;
   637         posted = (SDL_PushEvent(&event) > 0);
   638     }
   639     return posted;
   640 }
   641 
   642 void
   643 SDL_MouseQuit(void)
   644 {
   645     SDL_Cursor *cursor, *next;
   646     SDL_Mouse *mouse = SDL_GetMouse();
   647 
   648     if (mouse->CaptureMouse) {
   649         SDL_CaptureMouse(SDL_FALSE);
   650     }
   651     SDL_SetRelativeMouseMode(SDL_FALSE);
   652     SDL_ShowCursor(1);
   653 
   654     cursor = mouse->cursors;
   655     while (cursor) {
   656         next = cursor->next;
   657         SDL_FreeCursor(cursor);
   658         cursor = next;
   659     }
   660     mouse->cursors = NULL;
   661     mouse->cur_cursor = NULL;
   662 
   663     if (mouse->def_cursor && mouse->FreeCursor) {
   664         mouse->FreeCursor(mouse->def_cursor);
   665         mouse->def_cursor = NULL;
   666     }
   667 
   668     if (mouse->clickstate) {
   669         SDL_free(mouse->clickstate);
   670         mouse->clickstate = NULL;
   671     }
   672 
   673     SDL_DelHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE,
   674                         SDL_MouseNormalSpeedScaleChanged, mouse);
   675 
   676     SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE,
   677                         SDL_MouseRelativeSpeedScaleChanged, mouse);
   678 }
   679 
   680 Uint32
   681 SDL_GetMouseState(int *x, int *y)
   682 {
   683     SDL_Mouse *mouse = SDL_GetMouse();
   684 
   685     if (x) {
   686         *x = mouse->x;
   687     }
   688     if (y) {
   689         *y = mouse->y;
   690     }
   691     return mouse->buttonstate;
   692 }
   693 
   694 Uint32
   695 SDL_GetRelativeMouseState(int *x, int *y)
   696 {
   697     SDL_Mouse *mouse = SDL_GetMouse();
   698 
   699     if (x) {
   700         *x = mouse->xdelta;
   701     }
   702     if (y) {
   703         *y = mouse->ydelta;
   704     }
   705     mouse->xdelta = 0;
   706     mouse->ydelta = 0;
   707     return mouse->buttonstate;
   708 }
   709 
   710 Uint32
   711 SDL_GetGlobalMouseState(int *x, int *y)
   712 {
   713     SDL_Mouse *mouse = SDL_GetMouse();
   714     int tmpx, tmpy;
   715 
   716     /* make sure these are never NULL for the backend implementations... */
   717     if (!x) {
   718         x = &tmpx;
   719     }
   720     if (!y) {
   721         y = &tmpy;
   722     }
   723 
   724     *x = *y = 0;
   725 
   726     if (!mouse->GetGlobalMouseState) {
   727         return 0;
   728     }
   729 
   730     return mouse->GetGlobalMouseState(x, y);
   731 }
   732 
   733 void
   734 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   735 {
   736     SDL_Mouse *mouse = SDL_GetMouse();
   737 
   738     if (window == NULL) {
   739         window = mouse->focus;
   740     }
   741 
   742     if (window == NULL) {
   743         return;
   744     }
   745 
   746     if (mouse->WarpMouse) {
   747         mouse->WarpMouse(window, x, y);
   748     } else {
   749         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
   750     }
   751 }
   752 
   753 int
   754 SDL_WarpMouseGlobal(int x, int y)
   755 {
   756     SDL_Mouse *mouse = SDL_GetMouse();
   757 
   758     if (mouse->WarpMouseGlobal) {
   759         return mouse->WarpMouseGlobal(x, y);
   760     }
   761 
   762     return SDL_Unsupported();
   763 }
   764 
   765 static SDL_bool
   766 ShouldUseRelativeModeWarp(SDL_Mouse *mouse)
   767 {
   768     if (!mouse->SetRelativeMouseMode) {
   769         SDL_assert(mouse->WarpMouse);   /* Need this functionality for relative mode warp implementation */
   770         return SDL_TRUE;
   771     }
   772 
   773     return SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_FALSE);
   774 }
   775 
   776 int
   777 SDL_SetRelativeMouseMode(SDL_bool enabled)
   778 {
   779     SDL_Mouse *mouse = SDL_GetMouse();
   780     SDL_Window *focusWindow = SDL_GetKeyboardFocus();
   781 
   782     if (enabled == mouse->relative_mode) {
   783         return 0;
   784     }
   785 
   786     if (enabled && focusWindow) {
   787         /* Center it in the focused window to prevent clicks from going through
   788          * to background windows.
   789          */
   790         SDL_SetMouseFocus(focusWindow);
   791         SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
   792     }
   793 
   794     /* Set the relative mode */
   795     if (!enabled && mouse->relative_mode_warp) {
   796         mouse->relative_mode_warp = SDL_FALSE;
   797     } else if (enabled && ShouldUseRelativeModeWarp(mouse)) {
   798         mouse->relative_mode_warp = SDL_TRUE;
   799     } else if (mouse->SetRelativeMouseMode(enabled) < 0) {
   800         if (enabled) {
   801             /* Fall back to warp mode if native relative mode failed */
   802             if (!mouse->WarpMouse) {
   803                 return SDL_SetError("No relative mode implementation available");
   804             }
   805             mouse->relative_mode_warp = SDL_TRUE;
   806         }
   807     }
   808     mouse->relative_mode = enabled;
   809     mouse->scale_accum_x = 0.0f;
   810     mouse->scale_accum_y = 0.0f;
   811 
   812     if (mouse->focus) {
   813         SDL_UpdateWindowGrab(mouse->focus);
   814 
   815         /* Put the cursor back to where the application expects it */
   816         if (!enabled) {
   817             SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
   818         }
   819     }
   820 
   821     /* Flush pending mouse motion - ideally we would pump events, but that's not always safe */
   822     SDL_FlushEvent(SDL_MOUSEMOTION);
   823 
   824     /* Update cursor visibility */
   825     SDL_SetCursor(NULL);
   826 
   827     return 0;
   828 }
   829 
   830 SDL_bool
   831 SDL_GetRelativeMouseMode()
   832 {
   833     SDL_Mouse *mouse = SDL_GetMouse();
   834 
   835     return mouse->relative_mode;
   836 }
   837 
   838 int
   839 SDL_CaptureMouse(SDL_bool enabled)
   840 {
   841     SDL_Mouse *mouse = SDL_GetMouse();
   842     SDL_Window *focusWindow;
   843     SDL_bool isCaptured;
   844 
   845     if (!mouse->CaptureMouse) {
   846         return SDL_Unsupported();
   847     }
   848 
   849     focusWindow = SDL_GetKeyboardFocus();
   850 
   851     isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE);
   852     if (isCaptured == enabled) {
   853         return 0;  /* already done! */
   854     }
   855 
   856     if (enabled) {
   857         if (!focusWindow) {
   858             return SDL_SetError("No window has focus");
   859         } else if (mouse->CaptureMouse(focusWindow) == -1) {
   860             return -1;  /* CaptureMouse() should call SetError */
   861         }
   862         focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
   863     } else {
   864         if (mouse->CaptureMouse(NULL) == -1) {
   865             return -1;  /* CaptureMouse() should call SetError */
   866         }
   867         focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
   868     }
   869 
   870     return 0;
   871 }
   872 
   873 SDL_Cursor *
   874 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   875                  int w, int h, int hot_x, int hot_y)
   876 {
   877     SDL_Surface *surface;
   878     SDL_Cursor *cursor;
   879     int x, y;
   880     Uint32 *pixel;
   881     Uint8 datab = 0, maskb = 0;
   882     const Uint32 black = 0xFF000000;
   883     const Uint32 white = 0xFFFFFFFF;
   884     const Uint32 transparent = 0x00000000;
   885 
   886     /* Make sure the width is a multiple of 8 */
   887     w = ((w + 7) & ~7);
   888 
   889     /* Create the surface from a bitmap */
   890     surface = SDL_CreateRGBSurface(0, w, h, 32,
   891                                    0x00FF0000,
   892                                    0x0000FF00,
   893                                    0x000000FF,
   894                                    0xFF000000);
   895     if (!surface) {
   896         return NULL;
   897     }
   898     for (y = 0; y < h; ++y) {
   899         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   900         for (x = 0; x < w; ++x) {
   901             if ((x % 8) == 0) {
   902                 datab = *data++;
   903                 maskb = *mask++;
   904             }
   905             if (maskb & 0x80) {
   906                 *pixel++ = (datab & 0x80) ? black : white;
   907             } else {
   908                 *pixel++ = (datab & 0x80) ? black : transparent;
   909             }
   910             datab <<= 1;
   911             maskb <<= 1;
   912         }
   913     }
   914 
   915     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   916 
   917     SDL_FreeSurface(surface);
   918 
   919     return cursor;
   920 }
   921 
   922 SDL_Cursor *
   923 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   924 {
   925     SDL_Mouse *mouse = SDL_GetMouse();
   926     SDL_Surface *temp = NULL;
   927     SDL_Cursor *cursor;
   928 
   929     if (!surface) {
   930         SDL_SetError("Passed NULL cursor surface");
   931         return NULL;
   932     }
   933 
   934     if (!mouse->CreateCursor) {
   935         SDL_SetError("Cursors are not currently supported");
   936         return NULL;
   937     }
   938 
   939     /* Sanity check the hot spot */
   940     if ((hot_x < 0) || (hot_y < 0) ||
   941         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   942         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   943         return NULL;
   944     }
   945 
   946     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   947         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   948         if (!temp) {
   949             return NULL;
   950         }
   951         surface = temp;
   952     }
   953 
   954     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   955     if (cursor) {
   956         cursor->next = mouse->cursors;
   957         mouse->cursors = cursor;
   958     }
   959 
   960     SDL_FreeSurface(temp);
   961 
   962     return cursor;
   963 }
   964 
   965 SDL_Cursor *
   966 SDL_CreateSystemCursor(SDL_SystemCursor id)
   967 {
   968     SDL_Mouse *mouse = SDL_GetMouse();
   969     SDL_Cursor *cursor;
   970 
   971     if (!mouse->CreateSystemCursor) {
   972         SDL_SetError("CreateSystemCursor is not currently supported");
   973         return NULL;
   974     }
   975 
   976     cursor = mouse->CreateSystemCursor(id);
   977     if (cursor) {
   978         cursor->next = mouse->cursors;
   979         mouse->cursors = cursor;
   980     }
   981 
   982     return cursor;
   983 }
   984 
   985 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   986    if this is desired for any reason.  This is used when setting
   987    the video mode and when the SDL window gains the mouse focus.
   988  */
   989 void
   990 SDL_SetCursor(SDL_Cursor * cursor)
   991 {
   992     SDL_Mouse *mouse = SDL_GetMouse();
   993 
   994     /* Set the new cursor */
   995     if (cursor) {
   996         /* Make sure the cursor is still valid for this mouse */
   997         if (cursor != mouse->def_cursor) {
   998             SDL_Cursor *found;
   999             for (found = mouse->cursors; found; found = found->next) {
  1000                 if (found == cursor) {
  1001                     break;
  1002                 }
  1003             }
  1004             if (!found) {
  1005                 SDL_SetError("Cursor not associated with the current mouse");
  1006                 return;
  1007             }
  1008         }
  1009         mouse->cur_cursor = cursor;
  1010     } else {
  1011         if (mouse->focus) {
  1012             cursor = mouse->cur_cursor;
  1013         } else {
  1014             cursor = mouse->def_cursor;
  1015         }
  1016     }
  1017 
  1018     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
  1019         if (mouse->ShowCursor) {
  1020             mouse->ShowCursor(cursor);
  1021         }
  1022     } else {
  1023         if (mouse->ShowCursor) {
  1024             mouse->ShowCursor(NULL);
  1025         }
  1026     }
  1027 }
  1028 
  1029 SDL_Cursor *
  1030 SDL_GetCursor(void)
  1031 {
  1032     SDL_Mouse *mouse = SDL_GetMouse();
  1033 
  1034     if (!mouse) {
  1035         return NULL;
  1036     }
  1037     return mouse->cur_cursor;
  1038 }
  1039 
  1040 SDL_Cursor *
  1041 SDL_GetDefaultCursor(void)
  1042 {
  1043     SDL_Mouse *mouse = SDL_GetMouse();
  1044 
  1045     if (!mouse) {
  1046         return NULL;
  1047     }
  1048     return mouse->def_cursor;
  1049 }
  1050 
  1051 void
  1052 SDL_FreeCursor(SDL_Cursor * cursor)
  1053 {
  1054     SDL_Mouse *mouse = SDL_GetMouse();
  1055     SDL_Cursor *curr, *prev;
  1056 
  1057     if (!cursor) {
  1058         return;
  1059     }
  1060 
  1061     if (cursor == mouse->def_cursor) {
  1062         return;
  1063     }
  1064     if (cursor == mouse->cur_cursor) {
  1065         SDL_SetCursor(mouse->def_cursor);
  1066     }
  1067 
  1068     for (prev = NULL, curr = mouse->cursors; curr;
  1069          prev = curr, curr = curr->next) {
  1070         if (curr == cursor) {
  1071             if (prev) {
  1072                 prev->next = curr->next;
  1073             } else {
  1074                 mouse->cursors = curr->next;
  1075             }
  1076 
  1077             if (mouse->FreeCursor) {
  1078                 mouse->FreeCursor(curr);
  1079             }
  1080             return;
  1081         }
  1082     }
  1083 }
  1084 
  1085 int
  1086 SDL_ShowCursor(int toggle)
  1087 {
  1088     SDL_Mouse *mouse = SDL_GetMouse();
  1089     SDL_bool shown;
  1090 
  1091     if (!mouse) {
  1092         return 0;
  1093     }
  1094 
  1095     shown = mouse->cursor_shown;
  1096     if (toggle >= 0) {
  1097         if (toggle) {
  1098             mouse->cursor_shown = SDL_TRUE;
  1099         } else {
  1100             mouse->cursor_shown = SDL_FALSE;
  1101         }
  1102         if (mouse->cursor_shown != shown) {
  1103             SDL_SetCursor(NULL);
  1104         }
  1105     }
  1106     return shown;
  1107 }
  1108 
  1109 /* vi: set ts=4 sw=4 expandtab: */