src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 13 Oct 2017 19:30:34 -0700
changeset 11622 7abd358d3cec
parent 11607 db7ee6a1ba6a
child 11693 4c8bd26f1aab
permissions -rw-r--r--
Fixed bug 3880 - X Error upon quit since rev. 11607

Ozkan Sezer

Since changeset 11607:db7ee6a1ba6a, I am getting the following
error upon quit. Running testsprite2, clicking the mouse, and
quiting it is enough to trigger it. This is on my old Fedora9
x86-Linux:

X Error of failed request: BadCursor (invalid Cursor parameter)
Major opcode of failed request: 2 (X_ChangeWindowAttributes)
Resource id in failed request: 0xb057340
Serial number of failed request: 905
Current serial number in output stream: 906

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