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