sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_mouse.c (30810B)


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