src/events/SDL_mouse.c
author Gabriel Jacobo <gabomdq@gmail.com>
Sat, 28 Jun 2014 12:36:44 -0300
changeset 8963 f94215b55583
parent 8955 7a74b402790c
child 8965 8eceea447276
permissions -rw-r--r--
Fixes #2611 #2610, Touch events cause crash on Android, thanks Alvin & Sylvain

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