src/events/SDL_mouse.c
author Alex Szpakowski <slime73@gmail.com>
Sat, 24 Sep 2016 18:46:34 -0300
changeset 10366 e1b52e69578b
parent 9998 f67cf37e9cd4
child 10390 420482621a34
permissions -rw-r--r--
Use OS-provided click counts on macOS and iOS for mouse press and release events.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../SDL_internal.h"
    22 
    23 /* General mouse handling code for SDL */
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_hints.h"
    27 #include "SDL_timer.h"
    28 #include "SDL_events.h"
    29 #include "SDL_events_c.h"
    30 #include "default_cursor.h"
    31 #include "../video/SDL_sysvideo.h"
    32 
    33 /* #define DEBUG_MOUSE */
    34 
    35 /* The mouse state */
    36 static SDL_Mouse SDL_mouse;
    37 static Uint32 SDL_double_click_time = 500;
    38 static int SDL_double_click_radius = 1;
    39 
    40 static int
    41 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y);
    42 
    43 /* Public functions */
    44 int
    45 SDL_MouseInit(void)
    46 {
    47     SDL_Mouse *mouse = SDL_GetMouse();
    48 
    49     mouse->cursor_shown = SDL_TRUE;
    50 
    51     return (0);
    52 }
    53 
    54 void
    55 SDL_SetDefaultCursor(SDL_Cursor * cursor)
    56 {
    57     SDL_Mouse *mouse = SDL_GetMouse();
    58 
    59     mouse->def_cursor = cursor;
    60     if (!mouse->cur_cursor) {
    61         SDL_SetCursor(cursor);
    62     }
    63 }
    64 
    65 SDL_Mouse *
    66 SDL_GetMouse(void)
    67 {
    68     return &SDL_mouse;
    69 }
    70 
    71 void
    72 SDL_SetDoubleClickTime(Uint32 interval)
    73 {
    74     SDL_double_click_time = interval;
    75 }
    76 
    77 SDL_Window *
    78 SDL_GetMouseFocus(void)
    79 {
    80     SDL_Mouse *mouse = SDL_GetMouse();
    81 
    82     return mouse->focus;
    83 }
    84 
    85 void
    86 SDL_ResetMouse(void)
    87 {
    88     SDL_Mouse *mouse = SDL_GetMouse();
    89     Uint8 i;
    90 
    91 #ifdef DEBUG_MOUSE
    92     printf("Resetting mouse\n");
    93 #endif
    94     for (i = 1; i <= sizeof(mouse->buttonstate)*8; ++i) {
    95         if (mouse->buttonstate & SDL_BUTTON(i)) {
    96             SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, i);
    97         }
    98     }
    99     SDL_assert(mouse->buttonstate == 0);
   100 }
   101 
   102 void
   103 SDL_SetMouseFocus(SDL_Window * window)
   104 {
   105     SDL_Mouse *mouse = SDL_GetMouse();
   106 
   107     if (mouse->focus == window) {
   108         return;
   109     }
   110 
   111     /* Actually, this ends up being a bad idea, because most operating
   112        systems have an implicit grab when you press the mouse button down
   113        so you can drag things out of the window and then get the mouse up
   114        when it happens.  So, #if 0...
   115     */
   116 #if 0
   117     if (mouse->focus && !window) {
   118         /* We won't get anymore mouse messages, so reset mouse state */
   119         SDL_ResetMouse();
   120     }
   121 #endif
   122 
   123     /* See if the current window has lost focus */
   124     if (mouse->focus) {
   125         SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_LEAVE, 0, 0);
   126     }
   127 
   128     mouse->focus = window;
   129 
   130     if (mouse->focus) {
   131         SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_ENTER, 0, 0);
   132     }
   133 
   134     /* Update cursor visibility */
   135     SDL_SetCursor(NULL);
   136 }
   137 
   138 /* Check to see if we need to synthesize focus events */
   139 static SDL_bool
   140 SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
   141 {
   142     SDL_Mouse *mouse = SDL_GetMouse();
   143     SDL_bool inWindow = SDL_TRUE;
   144 
   145     if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) {
   146         int w, h;
   147         SDL_GetWindowSize(window, &w, &h);
   148         if (x < 0 || y < 0 || x >= w || y >= h) {
   149             inWindow = SDL_FALSE;
   150         }
   151     }
   152 
   153 /* Linux doesn't give you mouse events outside your window unless you grab
   154    the pointer.
   155 
   156    Windows doesn't give you mouse events outside your window unless you call
   157    SetCapture().
   158 
   159    Both of these are slightly scary changes, so for now we'll punt and if the
   160    mouse leaves the window you'll lose mouse focus and reset button state.
   161 */
   162 #ifdef SUPPORT_DRAG_OUTSIDE_WINDOW
   163     if (!inWindow && !buttonstate) {
   164 #else
   165     if (!inWindow) {
   166 #endif
   167         if (window == mouse->focus) {
   168 #ifdef DEBUG_MOUSE
   169             printf("Mouse left window, synthesizing move & focus lost event\n");
   170 #endif
   171             SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
   172             SDL_SetMouseFocus(NULL);
   173         }
   174         return SDL_FALSE;
   175     }
   176 
   177     if (window != mouse->focus) {
   178 #ifdef DEBUG_MOUSE
   179          printf("Mouse entered window, synthesizing focus gain & move event\n");
   180 #endif
   181          SDL_SetMouseFocus(window);
   182          SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
   183     }
   184     return SDL_TRUE;
   185 }
   186 
   187 int
   188 SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y)
   189 {
   190     if (window && !relative) {
   191         SDL_Mouse *mouse = SDL_GetMouse();
   192         if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate)) {
   193             return 0;
   194         }
   195     }
   196 
   197     return SDL_PrivateSendMouseMotion(window, mouseID, relative, x, y);
   198 }
   199 
   200 static int
   201 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y)
   202 {
   203     SDL_Mouse *mouse = SDL_GetMouse();
   204     int posted;
   205     int xrel;
   206     int yrel;
   207 
   208     if (mouse->relative_mode_warp) {
   209         int center_x = 0, center_y = 0;
   210         SDL_GetWindowSize(window, &center_x, &center_y);
   211         center_x /= 2;
   212         center_y /= 2;
   213         if (x == center_x && y == center_y) {
   214             mouse->last_x = center_x;
   215             mouse->last_y = center_y;
   216             return 0;
   217         }
   218         SDL_WarpMouseInWindow(window, center_x, center_y);
   219     }
   220 
   221     if (relative) {
   222         xrel = x;
   223         yrel = y;
   224         x = (mouse->last_x + xrel);
   225         y = (mouse->last_y + yrel);
   226     } else {
   227         xrel = x - mouse->last_x;
   228         yrel = y - mouse->last_y;
   229     }
   230 
   231     /* Drop events that don't change state */
   232     if (!xrel && !yrel) {
   233 #ifdef DEBUG_MOUSE
   234         printf("Mouse event didn't change state - dropped!\n");
   235 #endif
   236         return 0;
   237     }
   238 
   239     /* Update internal mouse coordinates */
   240     if (!mouse->relative_mode) {
   241         mouse->x = x;
   242         mouse->y = y;
   243     } else {
   244         mouse->x += xrel;
   245         mouse->y += yrel;
   246     }
   247 
   248     /* make sure that the pointers find themselves inside the windows,
   249        unless we have the mouse captured. */
   250     if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) {
   251         int x_max = 0, y_max = 0;
   252 
   253         // !!! FIXME: shouldn't this be (window) instead of (mouse->focus)?
   254         SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
   255         --x_max;
   256         --y_max;
   257 
   258         if (mouse->x > x_max) {
   259             mouse->x = x_max;
   260         }
   261         if (mouse->x < 0) {
   262             mouse->x = 0;
   263         }
   264 
   265         if (mouse->y > y_max) {
   266             mouse->y = y_max;
   267         }
   268         if (mouse->y < 0) {
   269             mouse->y = 0;
   270         }
   271     }
   272 
   273     mouse->xdelta += xrel;
   274     mouse->ydelta += yrel;
   275 
   276     /* Move the mouse cursor, if needed */
   277     if (mouse->cursor_shown && !mouse->relative_mode &&
   278         mouse->MoveCursor && mouse->cur_cursor) {
   279         mouse->MoveCursor(mouse->cur_cursor);
   280     }
   281 
   282     /* Post the event, if desired */
   283     posted = 0;
   284     if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) {
   285         SDL_Event event;
   286         event.motion.type = SDL_MOUSEMOTION;
   287         event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
   288         event.motion.which = mouseID;
   289         event.motion.state = mouse->buttonstate;
   290         event.motion.x = mouse->x;
   291         event.motion.y = mouse->y;
   292         event.motion.xrel = xrel;
   293         event.motion.yrel = yrel;
   294         posted = (SDL_PushEvent(&event) > 0);
   295     }
   296     if (relative) {
   297         mouse->last_x = mouse->x;
   298         mouse->last_y = mouse->y;
   299     } else {
   300         /* Use unclamped values if we're getting events outside the window */
   301         mouse->last_x = x;
   302         mouse->last_y = y;
   303     }
   304     return posted;
   305 }
   306 
   307 static SDL_MouseClickState *GetMouseClickState(SDL_Mouse *mouse, Uint8 button)
   308 {
   309     if (button >= mouse->num_clickstates) {
   310         int i, count = button + 1;
   311         SDL_MouseClickState *clickstate = (SDL_MouseClickState *)SDL_realloc(mouse->clickstate, count * sizeof(*mouse->clickstate));
   312         if (!clickstate) {
   313             return NULL;
   314         }
   315         mouse->clickstate = clickstate;
   316 
   317         for (i = mouse->num_clickstates; i < count; ++i) {
   318             SDL_zero(mouse->clickstate[i]);
   319         }
   320         mouse->num_clickstates = count;
   321     }
   322     return &mouse->clickstate[button];
   323 }
   324 
   325 static int
   326 SDL_PrivateSendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks)
   327 {
   328     SDL_Mouse *mouse = SDL_GetMouse();
   329     int posted;
   330     Uint32 type;
   331     Uint32 buttonstate = mouse->buttonstate;
   332 
   333     /* Figure out which event to perform */
   334     switch (state) {
   335     case SDL_PRESSED:
   336         type = SDL_MOUSEBUTTONDOWN;
   337         buttonstate |= SDL_BUTTON(button);
   338         break;
   339     case SDL_RELEASED:
   340         type = SDL_MOUSEBUTTONUP;
   341         buttonstate &= ~SDL_BUTTON(button);
   342         break;
   343     default:
   344         /* Invalid state -- bail */
   345         return 0;
   346     }
   347 
   348     /* We do this after calculating buttonstate so button presses gain focus */
   349     if (window && state == SDL_PRESSED) {
   350         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   351     }
   352 
   353     if (buttonstate == mouse->buttonstate) {
   354         /* Ignore this event, no state change */
   355         return 0;
   356     }
   357     mouse->buttonstate = buttonstate;
   358 
   359     if (clicks < 0) {
   360         SDL_MouseClickState *clickstate = GetMouseClickState(mouse, button);
   361         if (state == SDL_PRESSED) {
   362             Uint32 now = SDL_GetTicks();
   363 
   364             if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + SDL_double_click_time) ||
   365                 SDL_abs(mouse->x - clickstate->last_x) > SDL_double_click_radius ||
   366                 SDL_abs(mouse->y - clickstate->last_y) > SDL_double_click_radius) {
   367                 clickstate->click_count = 0;
   368             }
   369             clickstate->last_timestamp = now;
   370             clickstate->last_x = mouse->x;
   371             clickstate->last_y = mouse->y;
   372             if (clickstate->click_count < 255) {
   373                 ++clickstate->click_count;
   374             }
   375         }
   376         clicks = clickstate->click_count;
   377     }
   378 
   379     /* Post the event, if desired */
   380     posted = 0;
   381     if (SDL_GetEventState(type) == SDL_ENABLE) {
   382         SDL_Event event;
   383         event.type = type;
   384         event.button.windowID = mouse->focus ? mouse->focus->id : 0;
   385         event.button.which = mouseID;
   386         event.button.state = state;
   387         event.button.button = button;
   388         event.button.clicks = (Uint8) SDL_min(clicks, 255);
   389         event.button.x = mouse->x;
   390         event.button.y = mouse->y;
   391         posted = (SDL_PushEvent(&event) > 0);
   392     }
   393 
   394     /* We do this after dispatching event so button releases can lose focus */
   395     if (window && state == SDL_RELEASED) {
   396         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   397     }
   398     
   399     return posted;
   400 }
   401 
   402 int
   403 SDL_SendMouseButtonClicks(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks)
   404 {
   405     clicks = SDL_max(clicks, 0);
   406     return SDL_PrivateSendMouseButton(window, mouseID, state, button, clicks);
   407 }
   408 
   409 int
   410 SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
   411 {
   412     return SDL_PrivateSendMouseButton(window, mouseID, state, button, -1);
   413 }
   414 
   415 int
   416 SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, int x, int y, SDL_MouseWheelDirection direction)
   417 {
   418     SDL_Mouse *mouse = SDL_GetMouse();
   419     int posted;
   420 
   421     if (window) {
   422         SDL_SetMouseFocus(window);
   423     }
   424 
   425     if (!x && !y) {
   426         return 0;
   427     }
   428 
   429     /* Post the event, if desired */
   430     posted = 0;
   431     if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) {
   432         SDL_Event event;
   433         event.type = SDL_MOUSEWHEEL;
   434         event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
   435         event.wheel.which = mouseID;
   436         event.wheel.x = x;
   437         event.wheel.y = y;
   438         event.wheel.direction = (Uint32)direction;
   439         posted = (SDL_PushEvent(&event) > 0);
   440     }
   441     return posted;
   442 }
   443 
   444 void
   445 SDL_MouseQuit(void)
   446 {
   447     SDL_Cursor *cursor, *next;
   448     SDL_Mouse *mouse = SDL_GetMouse();
   449 
   450     if (mouse->CaptureMouse) {
   451         SDL_CaptureMouse(SDL_FALSE);
   452     }
   453     SDL_SetRelativeMouseMode(SDL_FALSE);
   454     SDL_ShowCursor(1);
   455 
   456     cursor = mouse->cursors;
   457     while (cursor) {
   458         next = cursor->next;
   459         SDL_FreeCursor(cursor);
   460         cursor = next;
   461     }
   462 
   463     if (mouse->def_cursor && mouse->FreeCursor) {
   464         mouse->FreeCursor(mouse->def_cursor);
   465     }
   466 
   467     if (mouse->clickstate) {
   468         SDL_free(mouse->clickstate);
   469     }
   470 
   471     SDL_zerop(mouse);
   472 }
   473 
   474 Uint32
   475 SDL_GetMouseState(int *x, int *y)
   476 {
   477     SDL_Mouse *mouse = SDL_GetMouse();
   478 
   479     if (x) {
   480         *x = mouse->x;
   481     }
   482     if (y) {
   483         *y = mouse->y;
   484     }
   485     return mouse->buttonstate;
   486 }
   487 
   488 Uint32
   489 SDL_GetRelativeMouseState(int *x, int *y)
   490 {
   491     SDL_Mouse *mouse = SDL_GetMouse();
   492 
   493     if (x) {
   494         *x = mouse->xdelta;
   495     }
   496     if (y) {
   497         *y = mouse->ydelta;
   498     }
   499     mouse->xdelta = 0;
   500     mouse->ydelta = 0;
   501     return mouse->buttonstate;
   502 }
   503 
   504 Uint32
   505 SDL_GetGlobalMouseState(int *x, int *y)
   506 {
   507     SDL_Mouse *mouse = SDL_GetMouse();
   508     int tmpx, tmpy;
   509 
   510     /* make sure these are never NULL for the backend implementations... */
   511     if (!x) {
   512         x = &tmpx;
   513     }
   514     if (!y) {
   515         y = &tmpy;
   516     }
   517 
   518     *x = *y = 0;
   519 
   520     if (!mouse->GetGlobalMouseState) {
   521         SDL_assert(0 && "This should really be implemented for every target.");
   522         return 0;
   523     }
   524 
   525     return mouse->GetGlobalMouseState(x, y);
   526 }
   527 
   528 void
   529 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   530 {
   531     SDL_Mouse *mouse = SDL_GetMouse();
   532 
   533     if (window == NULL) {
   534         window = mouse->focus;
   535     }
   536 
   537     if (window == NULL) {
   538         return;
   539     }
   540 
   541     if (mouse->WarpMouse) {
   542         mouse->WarpMouse(window, x, y);
   543     } else {
   544         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
   545     }
   546 }
   547 
   548 int
   549 SDL_WarpMouseGlobal(int x, int y)
   550 {
   551     SDL_Mouse *mouse = SDL_GetMouse();
   552 
   553     if (mouse->WarpMouseGlobal) {
   554         return mouse->WarpMouseGlobal(x, y);
   555     }
   556 
   557     return SDL_Unsupported();
   558 }
   559 
   560 static SDL_bool
   561 ShouldUseRelativeModeWarp(SDL_Mouse *mouse)
   562 {
   563     const char *hint;
   564 
   565     if (!mouse->SetRelativeMouseMode) {
   566         return SDL_TRUE;
   567     }
   568 
   569     hint = SDL_GetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP);
   570     if (hint) {
   571         if (*hint == '0') {
   572             return SDL_FALSE;
   573         } else {
   574             return SDL_TRUE;
   575         }
   576     }
   577     return SDL_FALSE;
   578 }
   579 
   580 int
   581 SDL_SetRelativeMouseMode(SDL_bool enabled)
   582 {
   583     SDL_Mouse *mouse = SDL_GetMouse();
   584     SDL_Window *focusWindow = SDL_GetKeyboardFocus();
   585 
   586     if (enabled == mouse->relative_mode) {
   587         return 0;
   588     }
   589 
   590     if (enabled && focusWindow) {
   591         /* Center it in the focused window to prevent clicks from going through
   592          * to background windows.
   593          */
   594         SDL_SetMouseFocus(focusWindow);
   595         SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
   596     }
   597 
   598     /* Set the relative mode */
   599     if (!enabled && mouse->relative_mode_warp) {
   600         mouse->relative_mode_warp = SDL_FALSE;
   601     } else if (enabled && ShouldUseRelativeModeWarp(mouse)) {
   602         mouse->relative_mode_warp = SDL_TRUE;
   603     } else if (mouse->SetRelativeMouseMode(enabled) < 0) {
   604         if (enabled) {
   605             /* Fall back to warp mode if native relative mode failed */
   606             mouse->relative_mode_warp = SDL_TRUE;
   607         }
   608     }
   609     mouse->relative_mode = enabled;
   610 
   611     if (mouse->focus) {
   612         SDL_UpdateWindowGrab(mouse->focus);
   613 
   614         /* Put the cursor back to where the application expects it */
   615         if (!enabled) {
   616             SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
   617         }
   618     }
   619 
   620     /* Flush pending mouse motion - ideally we would pump events, but that's not always safe */
   621     SDL_FlushEvent(SDL_MOUSEMOTION);
   622 
   623     /* Update cursor visibility */
   624     SDL_SetCursor(NULL);
   625 
   626     return 0;
   627 }
   628 
   629 SDL_bool
   630 SDL_GetRelativeMouseMode()
   631 {
   632     SDL_Mouse *mouse = SDL_GetMouse();
   633 
   634     return mouse->relative_mode;
   635 }
   636 
   637 int
   638 SDL_CaptureMouse(SDL_bool enabled)
   639 {
   640     SDL_Mouse *mouse = SDL_GetMouse();
   641     SDL_Window *focusWindow;
   642     SDL_bool isCaptured;
   643 
   644     if (!mouse->CaptureMouse) {
   645         return SDL_Unsupported();
   646     }
   647 
   648     focusWindow = SDL_GetKeyboardFocus();
   649 
   650     isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE);
   651     if (isCaptured == enabled) {
   652         return 0;  /* already done! */
   653     }
   654 
   655     if (enabled) {
   656         if (!focusWindow) {
   657             return SDL_SetError("No window has focus");
   658         } else if (mouse->CaptureMouse(focusWindow) == -1) {
   659             return -1;  /* CaptureMouse() should call SetError */
   660         }
   661         focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
   662     } else {
   663         if (mouse->CaptureMouse(NULL) == -1) {
   664             return -1;  /* CaptureMouse() should call SetError */
   665         }
   666         focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
   667     }
   668 
   669     return 0;
   670 }
   671 
   672 SDL_Cursor *
   673 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   674                  int w, int h, int hot_x, int hot_y)
   675 {
   676     SDL_Surface *surface;
   677     SDL_Cursor *cursor;
   678     int x, y;
   679     Uint32 *pixel;
   680     Uint8 datab = 0, maskb = 0;
   681     const Uint32 black = 0xFF000000;
   682     const Uint32 white = 0xFFFFFFFF;
   683     const Uint32 transparent = 0x00000000;
   684 
   685     /* Make sure the width is a multiple of 8 */
   686     w = ((w + 7) & ~7);
   687 
   688     /* Create the surface from a bitmap */
   689     surface = SDL_CreateRGBSurface(0, w, h, 32,
   690                                    0x00FF0000,
   691                                    0x0000FF00,
   692                                    0x000000FF,
   693                                    0xFF000000);
   694     if (!surface) {
   695         return NULL;
   696     }
   697     for (y = 0; y < h; ++y) {
   698         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   699         for (x = 0; x < w; ++x) {
   700             if ((x % 8) == 0) {
   701                 datab = *data++;
   702                 maskb = *mask++;
   703             }
   704             if (maskb & 0x80) {
   705                 *pixel++ = (datab & 0x80) ? black : white;
   706             } else {
   707                 *pixel++ = (datab & 0x80) ? black : transparent;
   708             }
   709             datab <<= 1;
   710             maskb <<= 1;
   711         }
   712     }
   713 
   714     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   715 
   716     SDL_FreeSurface(surface);
   717 
   718     return cursor;
   719 }
   720 
   721 SDL_Cursor *
   722 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   723 {
   724     SDL_Mouse *mouse = SDL_GetMouse();
   725     SDL_Surface *temp = NULL;
   726     SDL_Cursor *cursor;
   727 
   728     if (!surface) {
   729         SDL_SetError("Passed NULL cursor surface");
   730         return NULL;
   731     }
   732 
   733     if (!mouse->CreateCursor) {
   734         SDL_SetError("Cursors are not currently supported");
   735         return NULL;
   736     }
   737 
   738     /* Sanity check the hot spot */
   739     if ((hot_x < 0) || (hot_y < 0) ||
   740         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   741         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   742         return NULL;
   743     }
   744 
   745     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   746         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   747         if (!temp) {
   748             return NULL;
   749         }
   750         surface = temp;
   751     }
   752 
   753     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   754     if (cursor) {
   755         cursor->next = mouse->cursors;
   756         mouse->cursors = cursor;
   757     }
   758 
   759     SDL_FreeSurface(temp);
   760 
   761     return cursor;
   762 }
   763 
   764 SDL_Cursor *
   765 SDL_CreateSystemCursor(SDL_SystemCursor id)
   766 {
   767     SDL_Mouse *mouse = SDL_GetMouse();
   768     SDL_Cursor *cursor;
   769 
   770     if (!mouse->CreateSystemCursor) {
   771         SDL_SetError("CreateSystemCursor is not currently supported");
   772         return NULL;
   773     }
   774 
   775     cursor = mouse->CreateSystemCursor(id);
   776     if (cursor) {
   777         cursor->next = mouse->cursors;
   778         mouse->cursors = cursor;
   779     }
   780 
   781     return cursor;
   782 }
   783 
   784 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   785    if this is desired for any reason.  This is used when setting
   786    the video mode and when the SDL window gains the mouse focus.
   787  */
   788 void
   789 SDL_SetCursor(SDL_Cursor * cursor)
   790 {
   791     SDL_Mouse *mouse = SDL_GetMouse();
   792 
   793     /* Set the new cursor */
   794     if (cursor) {
   795         /* Make sure the cursor is still valid for this mouse */
   796         if (cursor != mouse->def_cursor) {
   797             SDL_Cursor *found;
   798             for (found = mouse->cursors; found; found = found->next) {
   799                 if (found == cursor) {
   800                     break;
   801                 }
   802             }
   803             if (!found) {
   804                 SDL_SetError("Cursor not associated with the current mouse");
   805                 return;
   806             }
   807         }
   808         mouse->cur_cursor = cursor;
   809     } else {
   810         if (mouse->focus) {
   811             cursor = mouse->cur_cursor;
   812         } else {
   813             cursor = mouse->def_cursor;
   814         }
   815     }
   816 
   817     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   818         if (mouse->ShowCursor) {
   819             mouse->ShowCursor(cursor);
   820         }
   821     } else {
   822         if (mouse->ShowCursor) {
   823             mouse->ShowCursor(NULL);
   824         }
   825     }
   826 }
   827 
   828 SDL_Cursor *
   829 SDL_GetCursor(void)
   830 {
   831     SDL_Mouse *mouse = SDL_GetMouse();
   832 
   833     if (!mouse) {
   834         return NULL;
   835     }
   836     return mouse->cur_cursor;
   837 }
   838 
   839 SDL_Cursor *
   840 SDL_GetDefaultCursor(void)
   841 {
   842     SDL_Mouse *mouse = SDL_GetMouse();
   843 
   844     if (!mouse) {
   845         return NULL;
   846     }
   847     return mouse->def_cursor;
   848 }
   849 
   850 void
   851 SDL_FreeCursor(SDL_Cursor * cursor)
   852 {
   853     SDL_Mouse *mouse = SDL_GetMouse();
   854     SDL_Cursor *curr, *prev;
   855 
   856     if (!cursor) {
   857         return;
   858     }
   859 
   860     if (cursor == mouse->def_cursor) {
   861         return;
   862     }
   863     if (cursor == mouse->cur_cursor) {
   864         SDL_SetCursor(mouse->def_cursor);
   865     }
   866 
   867     for (prev = NULL, curr = mouse->cursors; curr;
   868          prev = curr, curr = curr->next) {
   869         if (curr == cursor) {
   870             if (prev) {
   871                 prev->next = curr->next;
   872             } else {
   873                 mouse->cursors = curr->next;
   874             }
   875 
   876             if (mouse->FreeCursor) {
   877                 mouse->FreeCursor(curr);
   878             }
   879             return;
   880         }
   881     }
   882 }
   883 
   884 int
   885 SDL_ShowCursor(int toggle)
   886 {
   887     SDL_Mouse *mouse = SDL_GetMouse();
   888     SDL_bool shown;
   889 
   890     if (!mouse) {
   891         return 0;
   892     }
   893 
   894     shown = mouse->cursor_shown;
   895     if (toggle >= 0) {
   896         if (toggle) {
   897             mouse->cursor_shown = SDL_TRUE;
   898         } else {
   899             mouse->cursor_shown = SDL_FALSE;
   900         }
   901         if (mouse->cursor_shown != shown) {
   902             SDL_SetCursor(NULL);
   903         }
   904     }
   905     return shown;
   906 }
   907 
   908 /* vi: set ts=4 sw=4 expandtab: */