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