src/events/SDL_mouse.c
author Edward Rudd <urkle@outoforder.cc>
Sun, 23 Nov 2014 21:09:54 -0500
changeset 9257 6f41196c2d6b
parent 9161 6e34073ef024
child 9334 5eb5ab33286e
permissions -rw-r--r--
add in support for passing down the "natural" (or flipped) scrolling direction in the MouseWheelEvent event
     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, SDL_MouseWheelDirection direction)
   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         event.wheel.direction = (Uint32)direction;
   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     if (mouse->CaptureMouse) {
   435         SDL_CaptureMouse(SDL_FALSE);
   436     }
   437     SDL_SetRelativeMouseMode(SDL_FALSE);
   438     SDL_ShowCursor(1);
   439 
   440     cursor = mouse->cursors;
   441     while (cursor) {
   442         next = cursor->next;
   443         SDL_FreeCursor(cursor);
   444         cursor = next;
   445     }
   446 
   447     if (mouse->def_cursor && mouse->FreeCursor) {
   448         mouse->FreeCursor(mouse->def_cursor);
   449     }
   450 
   451     if (mouse->clickstate) {
   452         SDL_free(mouse->clickstate);
   453     }
   454 
   455     SDL_zerop(mouse);
   456 }
   457 
   458 Uint32
   459 SDL_GetMouseState(int *x, int *y)
   460 {
   461     SDL_Mouse *mouse = SDL_GetMouse();
   462 
   463     if (x) {
   464         *x = mouse->x;
   465     }
   466     if (y) {
   467         *y = mouse->y;
   468     }
   469     return mouse->buttonstate;
   470 }
   471 
   472 Uint32
   473 SDL_GetRelativeMouseState(int *x, int *y)
   474 {
   475     SDL_Mouse *mouse = SDL_GetMouse();
   476 
   477     if (x) {
   478         *x = mouse->xdelta;
   479     }
   480     if (y) {
   481         *y = mouse->ydelta;
   482     }
   483     mouse->xdelta = 0;
   484     mouse->ydelta = 0;
   485     return mouse->buttonstate;
   486 }
   487 
   488 Uint32
   489 SDL_GetGlobalMouseState(int *x, int *y)
   490 {
   491     SDL_Mouse *mouse = SDL_GetMouse();
   492     int tmpx, tmpy;
   493 
   494     /* make sure these are never NULL for the backend implementations... */
   495     if (!x) {
   496         x = &tmpx;
   497     }
   498     if (!y) {
   499         y = &tmpy;
   500     }
   501 
   502     *x = *y = 0;
   503 
   504     if (!mouse->GetGlobalMouseState) {
   505         SDL_assert(0 && "This should really be implemented for every target.");
   506         return 0;
   507     }
   508 
   509     return mouse->GetGlobalMouseState(x, y);
   510 }
   511 
   512 void
   513 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   514 {
   515     SDL_Mouse *mouse = SDL_GetMouse();
   516 
   517     if (window == NULL) {
   518         window = mouse->focus;
   519     }
   520 
   521     if (window == NULL) {
   522         return;
   523     }
   524 
   525     if (mouse->WarpMouse) {
   526         mouse->WarpMouse(window, x, y);
   527     } else {
   528         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
   529     }
   530 }
   531 
   532 void
   533 SDL_WarpMouseGlobal(int x, int y)
   534 {
   535     SDL_Mouse *mouse = SDL_GetMouse();
   536 
   537     if (mouse->WarpMouseGlobal) {
   538         mouse->WarpMouseGlobal(x, y);
   539     }
   540 }
   541 
   542 static SDL_bool
   543 ShouldUseRelativeModeWarp(SDL_Mouse *mouse)
   544 {
   545     const char *hint;
   546 
   547     if (!mouse->SetRelativeMouseMode) {
   548         return SDL_TRUE;
   549     }
   550 
   551     hint = SDL_GetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP);
   552     if (hint) {
   553         if (*hint == '0') {
   554             return SDL_FALSE;
   555         } else {
   556             return SDL_TRUE;
   557         }
   558     }
   559     return SDL_FALSE;
   560 }
   561 
   562 int
   563 SDL_SetRelativeMouseMode(SDL_bool enabled)
   564 {
   565     SDL_Mouse *mouse = SDL_GetMouse();
   566     SDL_Window *focusWindow = SDL_GetKeyboardFocus();
   567 
   568     if (enabled == mouse->relative_mode) {
   569         return 0;
   570     }
   571 
   572     if (enabled && focusWindow) {
   573         /* Center it in the focused window to prevent clicks from going through
   574          * to background windows.
   575          */
   576         SDL_SetMouseFocus(focusWindow);
   577         SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
   578     }
   579 
   580     /* Set the relative mode */
   581     if (!enabled && mouse->relative_mode_warp) {
   582         mouse->relative_mode_warp = SDL_FALSE;
   583     } else if (enabled && ShouldUseRelativeModeWarp(mouse)) {
   584         mouse->relative_mode_warp = SDL_TRUE;
   585     } else if (mouse->SetRelativeMouseMode(enabled) < 0) {
   586         if (enabled) {
   587             /* Fall back to warp mode if native relative mode failed */
   588             mouse->relative_mode_warp = SDL_TRUE;
   589         }
   590     }
   591     mouse->relative_mode = enabled;
   592 
   593     if (mouse->focus) {
   594         SDL_UpdateWindowGrab(mouse->focus);
   595 
   596         /* Put the cursor back to where the application expects it */
   597         if (!enabled) {
   598             SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
   599         }
   600     }
   601 
   602     /* Flush pending mouse motion - ideally we would pump events, but that's not always safe */
   603     SDL_FlushEvent(SDL_MOUSEMOTION);
   604 
   605     /* Update cursor visibility */
   606     SDL_SetCursor(NULL);
   607 
   608     return 0;
   609 }
   610 
   611 SDL_bool
   612 SDL_GetRelativeMouseMode()
   613 {
   614     SDL_Mouse *mouse = SDL_GetMouse();
   615 
   616     return mouse->relative_mode;
   617 }
   618 
   619 int
   620 SDL_CaptureMouse(SDL_bool enabled)
   621 {
   622     SDL_Mouse *mouse = SDL_GetMouse();
   623     SDL_Window *focusWindow;
   624     SDL_bool isCaptured;
   625 
   626     if (!mouse->CaptureMouse) {
   627         return SDL_Unsupported();
   628     }
   629 
   630     focusWindow = SDL_GetKeyboardFocus();
   631 
   632     isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE);
   633     if (isCaptured == enabled) {
   634         return 0;  /* already done! */
   635     }
   636 
   637     if (enabled) {
   638         if (!focusWindow) {
   639             return SDL_SetError("No window has focus");
   640         } else if (mouse->CaptureMouse(focusWindow) == -1) {
   641             return -1;  /* CaptureMouse() should call SetError */
   642         }
   643         focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
   644     } else {
   645         if (mouse->CaptureMouse(NULL) == -1) {
   646             return -1;  /* CaptureMouse() should call SetError */
   647         }
   648         focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
   649     }
   650 
   651     return 0;
   652 }
   653 
   654 SDL_Cursor *
   655 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   656                  int w, int h, int hot_x, int hot_y)
   657 {
   658     SDL_Surface *surface;
   659     SDL_Cursor *cursor;
   660     int x, y;
   661     Uint32 *pixel;
   662     Uint8 datab = 0, maskb = 0;
   663     const Uint32 black = 0xFF000000;
   664     const Uint32 white = 0xFFFFFFFF;
   665     const Uint32 transparent = 0x00000000;
   666 
   667     /* Make sure the width is a multiple of 8 */
   668     w = ((w + 7) & ~7);
   669 
   670     /* Create the surface from a bitmap */
   671     surface = SDL_CreateRGBSurface(0, w, h, 32,
   672                                    0x00FF0000,
   673                                    0x0000FF00,
   674                                    0x000000FF,
   675                                    0xFF000000);
   676     if (!surface) {
   677         return NULL;
   678     }
   679     for (y = 0; y < h; ++y) {
   680         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   681         for (x = 0; x < w; ++x) {
   682             if ((x % 8) == 0) {
   683                 datab = *data++;
   684                 maskb = *mask++;
   685             }
   686             if (maskb & 0x80) {
   687                 *pixel++ = (datab & 0x80) ? black : white;
   688             } else {
   689                 *pixel++ = (datab & 0x80) ? black : transparent;
   690             }
   691             datab <<= 1;
   692             maskb <<= 1;
   693         }
   694     }
   695 
   696     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   697 
   698     SDL_FreeSurface(surface);
   699 
   700     return cursor;
   701 }
   702 
   703 SDL_Cursor *
   704 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   705 {
   706     SDL_Mouse *mouse = SDL_GetMouse();
   707     SDL_Surface *temp = NULL;
   708     SDL_Cursor *cursor;
   709 
   710     if (!surface) {
   711         SDL_SetError("Passed NULL cursor surface");
   712         return NULL;
   713     }
   714 
   715     if (!mouse->CreateCursor) {
   716         SDL_SetError("Cursors are not currently supported");
   717         return NULL;
   718     }
   719 
   720     /* Sanity check the hot spot */
   721     if ((hot_x < 0) || (hot_y < 0) ||
   722         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   723         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   724         return NULL;
   725     }
   726 
   727     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   728         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   729         if (!temp) {
   730             return NULL;
   731         }
   732         surface = temp;
   733     }
   734 
   735     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   736     if (cursor) {
   737         cursor->next = mouse->cursors;
   738         mouse->cursors = cursor;
   739     }
   740 
   741     SDL_FreeSurface(temp);
   742 
   743     return cursor;
   744 }
   745 
   746 SDL_Cursor *
   747 SDL_CreateSystemCursor(SDL_SystemCursor id)
   748 {
   749     SDL_Mouse *mouse = SDL_GetMouse();
   750     SDL_Cursor *cursor;
   751 
   752     if (!mouse->CreateSystemCursor) {
   753         SDL_SetError("CreateSystemCursor is not currently supported");
   754         return NULL;
   755     }
   756 
   757     cursor = mouse->CreateSystemCursor(id);
   758     if (cursor) {
   759         cursor->next = mouse->cursors;
   760         mouse->cursors = cursor;
   761     }
   762 
   763     return cursor;
   764 }
   765 
   766 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   767    if this is desired for any reason.  This is used when setting
   768    the video mode and when the SDL window gains the mouse focus.
   769  */
   770 void
   771 SDL_SetCursor(SDL_Cursor * cursor)
   772 {
   773     SDL_Mouse *mouse = SDL_GetMouse();
   774 
   775     /* Set the new cursor */
   776     if (cursor) {
   777         /* Make sure the cursor is still valid for this mouse */
   778         if (cursor != mouse->def_cursor) {
   779             SDL_Cursor *found;
   780             for (found = mouse->cursors; found; found = found->next) {
   781                 if (found == cursor) {
   782                     break;
   783                 }
   784             }
   785             if (!found) {
   786                 SDL_SetError("Cursor not associated with the current mouse");
   787                 return;
   788             }
   789         }
   790         mouse->cur_cursor = cursor;
   791     } else {
   792         if (mouse->focus) {
   793             cursor = mouse->cur_cursor;
   794         } else {
   795             cursor = mouse->def_cursor;
   796         }
   797     }
   798 
   799     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   800         if (mouse->ShowCursor) {
   801             mouse->ShowCursor(cursor);
   802         }
   803     } else {
   804         if (mouse->ShowCursor) {
   805             mouse->ShowCursor(NULL);
   806         }
   807     }
   808 }
   809 
   810 SDL_Cursor *
   811 SDL_GetCursor(void)
   812 {
   813     SDL_Mouse *mouse = SDL_GetMouse();
   814 
   815     if (!mouse) {
   816         return NULL;
   817     }
   818     return mouse->cur_cursor;
   819 }
   820 
   821 SDL_Cursor *
   822 SDL_GetDefaultCursor(void)
   823 {
   824     SDL_Mouse *mouse = SDL_GetMouse();
   825 
   826     if (!mouse) {
   827         return NULL;
   828     }
   829     return mouse->def_cursor;
   830 }
   831 
   832 void
   833 SDL_FreeCursor(SDL_Cursor * cursor)
   834 {
   835     SDL_Mouse *mouse = SDL_GetMouse();
   836     SDL_Cursor *curr, *prev;
   837 
   838     if (!cursor) {
   839         return;
   840     }
   841 
   842     if (cursor == mouse->def_cursor) {
   843         return;
   844     }
   845     if (cursor == mouse->cur_cursor) {
   846         SDL_SetCursor(mouse->def_cursor);
   847     }
   848 
   849     for (prev = NULL, curr = mouse->cursors; curr;
   850          prev = curr, curr = curr->next) {
   851         if (curr == cursor) {
   852             if (prev) {
   853                 prev->next = curr->next;
   854             } else {
   855                 mouse->cursors = curr->next;
   856             }
   857 
   858             if (mouse->FreeCursor) {
   859                 mouse->FreeCursor(curr);
   860             }
   861             return;
   862         }
   863     }
   864 }
   865 
   866 int
   867 SDL_ShowCursor(int toggle)
   868 {
   869     SDL_Mouse *mouse = SDL_GetMouse();
   870     SDL_bool shown;
   871 
   872     if (!mouse) {
   873         return 0;
   874     }
   875 
   876     shown = mouse->cursor_shown;
   877     if (toggle >= 0) {
   878         if (toggle) {
   879             mouse->cursor_shown = SDL_TRUE;
   880         } else {
   881             mouse->cursor_shown = SDL_FALSE;
   882         }
   883         if (mouse->cursor_shown != shown) {
   884             SDL_SetCursor(NULL);
   885         }
   886     }
   887     return shown;
   888 }
   889 
   890 /* vi: set ts=4 sw=4 expandtab: */