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