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