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