src/events/SDL_mouse.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 25 Jun 2014 17:06:12 -0400
changeset 8953 dc80dc0bd22e
parent 8815 c6d0a457f3b2
parent 8952 4bb098814ec4
child 8955 7a74b402790c
permissions -rw-r--r--
Merged Ryan's SDL-gui-backend branch.

Adds three APIs, and implements them on X11, Cocoa, and Windows:

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