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