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