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