src/events/SDL_mouse.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 05 Jun 2014 00:02:42 -0400
changeset 8944 045aac36daac
parent 8927 be64f5daf64b
child 8945 04248c700ada
permissions -rw-r--r--
Added some (harmlessly) missing braces.
     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 
   481     if ( window == NULL ) {
   482         return;
   483     }
   484 
   485     if (mouse->WarpMouse) {
   486         mouse->WarpMouse(window, x, y);
   487     } else {
   488         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
   489     }
   490 }
   491 
   492 static SDL_bool
   493 ShouldUseRelativeModeWarp(SDL_Mouse *mouse)
   494 {
   495     const char *hint;
   496 
   497     if (!mouse->SetRelativeMouseMode) {
   498         return SDL_TRUE;
   499     }
   500 
   501     hint = SDL_GetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP);
   502     if (hint) {
   503         if (*hint == '0') {
   504             return SDL_FALSE;
   505         } else {
   506             return SDL_TRUE;
   507         }
   508     }
   509     return SDL_FALSE;
   510 }
   511 
   512 int
   513 SDL_SetRelativeMouseMode(SDL_bool enabled)
   514 {
   515     SDL_Mouse *mouse = SDL_GetMouse();
   516     SDL_Window *focusWindow = SDL_GetKeyboardFocus();
   517 
   518     if (enabled == mouse->relative_mode) {
   519         return 0;
   520     }
   521 
   522     if (enabled && focusWindow) {
   523         /* Center it in the focused window to prevent clicks from going through
   524          * to background windows.
   525          */
   526         SDL_SetMouseFocus(focusWindow);
   527         SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
   528     }
   529 
   530     /* Set the relative mode */
   531     if (!enabled && mouse->relative_mode_warp) {
   532         mouse->relative_mode_warp = SDL_FALSE;
   533     } else if (enabled && ShouldUseRelativeModeWarp(mouse)) {
   534         mouse->relative_mode_warp = SDL_TRUE;
   535     } else if (mouse->SetRelativeMouseMode(enabled) < 0) {
   536         if (enabled) {
   537             /* Fall back to warp mode if native relative mode failed */
   538             mouse->relative_mode_warp = SDL_TRUE;
   539         }
   540     }
   541     mouse->relative_mode = enabled;
   542 
   543     if (mouse->focus) {
   544         SDL_UpdateWindowGrab(mouse->focus);
   545 
   546         /* Put the cursor back to where the application expects it */
   547         if (!enabled) {
   548             SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
   549         }
   550     }
   551 
   552     /* Flush pending mouse motion - ideally we would pump events, but that's not always safe */
   553     SDL_FlushEvent(SDL_MOUSEMOTION);
   554 
   555     /* Update cursor visibility */
   556     SDL_SetCursor(NULL);
   557 
   558     return 0;
   559 }
   560 
   561 SDL_bool
   562 SDL_GetRelativeMouseMode()
   563 {
   564     SDL_Mouse *mouse = SDL_GetMouse();
   565 
   566     return mouse->relative_mode;
   567 }
   568 
   569 int
   570 SDL_CaptureMouse(SDL_bool enabled)
   571 {
   572     SDL_Mouse *mouse = SDL_GetMouse();
   573     SDL_Window *focusWindow;
   574     SDL_bool isCaptured;
   575 
   576     if (!mouse->CaptureMouse) {
   577         return SDL_Unsupported();
   578     }
   579 
   580     focusWindow = SDL_GetKeyboardFocus();
   581 
   582     isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE);
   583     if (isCaptured == enabled) {
   584         return 0;  /* already done! */
   585     }
   586 
   587     if (enabled) {
   588         if (!focusWindow) {
   589             return SDL_SetError("No window has focus");
   590         } else if (mouse->CaptureMouse(focusWindow) == -1) {
   591             return -1;  /* CaptureMouse() should call SetError */
   592         }
   593         focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
   594     } else {
   595         if (mouse->CaptureMouse(NULL) == -1) {
   596             return -1;  /* CaptureMouse() should call SetError */
   597         }
   598         focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
   599     }
   600 
   601     return 0;
   602 }
   603 
   604 
   605 SDL_Cursor *
   606 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   607                  int w, int h, int hot_x, int hot_y)
   608 {
   609     SDL_Surface *surface;
   610     SDL_Cursor *cursor;
   611     int x, y;
   612     Uint32 *pixel;
   613     Uint8 datab = 0, maskb = 0;
   614     const Uint32 black = 0xFF000000;
   615     const Uint32 white = 0xFFFFFFFF;
   616     const Uint32 transparent = 0x00000000;
   617 
   618     /* Make sure the width is a multiple of 8 */
   619     w = ((w + 7) & ~7);
   620 
   621     /* Create the surface from a bitmap */
   622     surface = SDL_CreateRGBSurface(0, w, h, 32,
   623                                    0x00FF0000,
   624                                    0x0000FF00,
   625                                    0x000000FF,
   626                                    0xFF000000);
   627     if (!surface) {
   628         return NULL;
   629     }
   630     for (y = 0; y < h; ++y) {
   631         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   632         for (x = 0; x < w; ++x) {
   633             if ((x % 8) == 0) {
   634                 datab = *data++;
   635                 maskb = *mask++;
   636             }
   637             if (maskb & 0x80) {
   638                 *pixel++ = (datab & 0x80) ? black : white;
   639             } else {
   640                 *pixel++ = (datab & 0x80) ? black : transparent;
   641             }
   642             datab <<= 1;
   643             maskb <<= 1;
   644         }
   645     }
   646 
   647     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   648 
   649     SDL_FreeSurface(surface);
   650 
   651     return cursor;
   652 }
   653 
   654 SDL_Cursor *
   655 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   656 {
   657     SDL_Mouse *mouse = SDL_GetMouse();
   658     SDL_Surface *temp = NULL;
   659     SDL_Cursor *cursor;
   660 
   661     if (!surface) {
   662         SDL_SetError("Passed NULL cursor surface");
   663         return NULL;
   664     }
   665 
   666     if (!mouse->CreateCursor) {
   667         SDL_SetError("Cursors are not currently supported");
   668         return NULL;
   669     }
   670 
   671     /* Sanity check the hot spot */
   672     if ((hot_x < 0) || (hot_y < 0) ||
   673         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   674         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   675         return NULL;
   676     }
   677 
   678     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   679         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   680         if (!temp) {
   681             return NULL;
   682         }
   683         surface = temp;
   684     }
   685 
   686     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   687     if (cursor) {
   688         cursor->next = mouse->cursors;
   689         mouse->cursors = cursor;
   690     }
   691 
   692     SDL_FreeSurface(temp);
   693 
   694     return cursor;
   695 }
   696 
   697 SDL_Cursor *
   698 SDL_CreateSystemCursor(SDL_SystemCursor id)
   699 {
   700     SDL_Mouse *mouse = SDL_GetMouse();
   701     SDL_Cursor *cursor;
   702 
   703     if (!mouse->CreateSystemCursor) {
   704         SDL_SetError("CreateSystemCursor is not currently supported");
   705         return NULL;
   706     }
   707 
   708     cursor = mouse->CreateSystemCursor(id);
   709     if (cursor) {
   710         cursor->next = mouse->cursors;
   711         mouse->cursors = cursor;
   712     }
   713 
   714     return cursor;
   715 }
   716 
   717 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   718    if this is desired for any reason.  This is used when setting
   719    the video mode and when the SDL window gains the mouse focus.
   720  */
   721 void
   722 SDL_SetCursor(SDL_Cursor * cursor)
   723 {
   724     SDL_Mouse *mouse = SDL_GetMouse();
   725 
   726     /* Set the new cursor */
   727     if (cursor) {
   728         /* Make sure the cursor is still valid for this mouse */
   729         if (cursor != mouse->def_cursor) {
   730             SDL_Cursor *found;
   731             for (found = mouse->cursors; found; found = found->next) {
   732                 if (found == cursor) {
   733                     break;
   734                 }
   735             }
   736             if (!found) {
   737                 SDL_SetError("Cursor not associated with the current mouse");
   738                 return;
   739             }
   740         }
   741         mouse->cur_cursor = cursor;
   742     } else {
   743         if (mouse->focus) {
   744             cursor = mouse->cur_cursor;
   745         } else {
   746             cursor = mouse->def_cursor;
   747         }
   748     }
   749 
   750     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   751         if (mouse->ShowCursor) {
   752             mouse->ShowCursor(cursor);
   753         }
   754     } else {
   755         if (mouse->ShowCursor) {
   756             mouse->ShowCursor(NULL);
   757         }
   758     }
   759 }
   760 
   761 SDL_Cursor *
   762 SDL_GetCursor(void)
   763 {
   764     SDL_Mouse *mouse = SDL_GetMouse();
   765 
   766     if (!mouse) {
   767         return NULL;
   768     }
   769     return mouse->cur_cursor;
   770 }
   771 
   772 SDL_Cursor *
   773 SDL_GetDefaultCursor(void)
   774 {
   775     SDL_Mouse *mouse = SDL_GetMouse();
   776 
   777     if (!mouse) {
   778         return NULL;
   779     }
   780     return mouse->def_cursor;
   781 }
   782 
   783 void
   784 SDL_FreeCursor(SDL_Cursor * cursor)
   785 {
   786     SDL_Mouse *mouse = SDL_GetMouse();
   787     SDL_Cursor *curr, *prev;
   788 
   789     if (!cursor) {
   790         return;
   791     }
   792 
   793     if (cursor == mouse->def_cursor) {
   794         return;
   795     }
   796     if (cursor == mouse->cur_cursor) {
   797         SDL_SetCursor(mouse->def_cursor);
   798     }
   799 
   800     for (prev = NULL, curr = mouse->cursors; curr;
   801          prev = curr, curr = curr->next) {
   802         if (curr == cursor) {
   803             if (prev) {
   804                 prev->next = curr->next;
   805             } else {
   806                 mouse->cursors = curr->next;
   807             }
   808 
   809             if (mouse->FreeCursor) {
   810                 mouse->FreeCursor(curr);
   811             }
   812             return;
   813         }
   814     }
   815 }
   816 
   817 int
   818 SDL_ShowCursor(int toggle)
   819 {
   820     SDL_Mouse *mouse = SDL_GetMouse();
   821     SDL_bool shown;
   822 
   823     if (!mouse) {
   824         return 0;
   825     }
   826 
   827     shown = mouse->cursor_shown;
   828     if (toggle >= 0) {
   829         if (toggle) {
   830             mouse->cursor_shown = SDL_TRUE;
   831         } else {
   832             mouse->cursor_shown = SDL_FALSE;
   833         }
   834         if (mouse->cursor_shown != shown) {
   835             SDL_SetCursor(NULL);
   836         }
   837     }
   838     return shown;
   839 }
   840 
   841 /* vi: set ts=4 sw=4 expandtab: */