src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 14 Aug 2017 21:28:04 -0700
changeset 11300 68a80d7afec3
parent 11284 3db78361e751
child 11313 14d04f0a4914
permissions -rw-r--r--
Fixed bug 2293 - Precise scrolling events

Martijn Courteaux

I implemented precise scrolling events. I have been through all the folders in /src/video/[platform] to implement where possible. This works on OS X, but I can't speak for others. Build farm will figure that out, I guess. I think this patch should introduce precise scrolling on OS X, Wayland, Mir, Windows, Android, Nacl, Windows RT.

The way I provide precise scrolling events is by adding two float fields to the SDL_MouseWheelScrollEvent datastructure, called "preciseX" and "preciseY". The old integer fields "x" and "y" are still present. The idea is that every platform specific code normalises the scroll amounts and forwards them to the SDL_SendMouseWheel function. It is this function that will now accumulate these (using a static variable, as I have seen how it was implemented in the Windows specific code) and once we hit a unit size, set the traditional integer "x" and "y" fields.

I believe this is pretty solid way of doing it, although I'm not the expert here.

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