src/events/SDL_mouse.c
author Sylvain Becker <sylvain.becker@gmail.com>
Thu, 04 Apr 2019 16:51:50 +0200
changeset 12688 cc45bcb16ef2
parent 12687 712d84dd14c9
child 12692 69f24f4ef99e
permissions -rw-r--r--
Add hint SDL_HINT_MOUSE_TOUCH_EVENTS for mouse events to generate touch events

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