src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 15 Jul 2019 09:36:53 -0700
changeset 12943 49190e92b7d1
parent 12934 d03900e991a2
child 12979 bbbb30026158
permissions -rw-r--r--
Fixed bug 4704 - SDL_HINT_ANDROID_SEPERATE_MOUSE_AND_TOUCH on Windows?

superfury

I notice that, somehow, when locking the mouse into place(using SDL_SetRelativeMouseMode), somehow at least the movement information gets through to both mouse movement and touch movement events?

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