src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 07 Oct 2016 23:40:44 -0700
changeset 10499 363c1c7e7a41
parent 10390 420482621a34
child 10609 d702ecbd8ba7
permissions -rw-r--r--
Implemented SDL_GetHintBoolean() to make it easier to check boolean hints
     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     if (!mouse->SetRelativeMouseMode) {
   568         return SDL_TRUE;
   569     }
   570 
   571     return SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_FALSE);
   572 }
   573 
   574 int
   575 SDL_SetRelativeMouseMode(SDL_bool enabled)
   576 {
   577     SDL_Mouse *mouse = SDL_GetMouse();
   578     SDL_Window *focusWindow = SDL_GetKeyboardFocus();
   579 
   580     if (enabled == mouse->relative_mode) {
   581         return 0;
   582     }
   583 
   584     if (enabled && focusWindow) {
   585         /* Center it in the focused window to prevent clicks from going through
   586          * to background windows.
   587          */
   588         SDL_SetMouseFocus(focusWindow);
   589         SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
   590     }
   591 
   592     /* Set the relative mode */
   593     if (!enabled && mouse->relative_mode_warp) {
   594         mouse->relative_mode_warp = SDL_FALSE;
   595     } else if (enabled && ShouldUseRelativeModeWarp(mouse)) {
   596         mouse->relative_mode_warp = SDL_TRUE;
   597     } else if (mouse->SetRelativeMouseMode(enabled) < 0) {
   598         if (enabled) {
   599             /* Fall back to warp mode if native relative mode failed */
   600             mouse->relative_mode_warp = SDL_TRUE;
   601         }
   602     }
   603     mouse->relative_mode = enabled;
   604 
   605     if (mouse->focus) {
   606         SDL_UpdateWindowGrab(mouse->focus);
   607 
   608         /* Put the cursor back to where the application expects it */
   609         if (!enabled) {
   610             SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
   611         }
   612     }
   613 
   614     /* Flush pending mouse motion - ideally we would pump events, but that's not always safe */
   615     SDL_FlushEvent(SDL_MOUSEMOTION);
   616 
   617     /* Update cursor visibility */
   618     SDL_SetCursor(NULL);
   619 
   620     return 0;
   621 }
   622 
   623 SDL_bool
   624 SDL_GetRelativeMouseMode()
   625 {
   626     SDL_Mouse *mouse = SDL_GetMouse();
   627 
   628     return mouse->relative_mode;
   629 }
   630 
   631 int
   632 SDL_CaptureMouse(SDL_bool enabled)
   633 {
   634     SDL_Mouse *mouse = SDL_GetMouse();
   635     SDL_Window *focusWindow;
   636     SDL_bool isCaptured;
   637 
   638     if (!mouse->CaptureMouse) {
   639         return SDL_Unsupported();
   640     }
   641 
   642     focusWindow = SDL_GetKeyboardFocus();
   643 
   644     isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE);
   645     if (isCaptured == enabled) {
   646         return 0;  /* already done! */
   647     }
   648 
   649     if (enabled) {
   650         if (!focusWindow) {
   651             return SDL_SetError("No window has focus");
   652         } else if (mouse->CaptureMouse(focusWindow) == -1) {
   653             return -1;  /* CaptureMouse() should call SetError */
   654         }
   655         focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
   656     } else {
   657         if (mouse->CaptureMouse(NULL) == -1) {
   658             return -1;  /* CaptureMouse() should call SetError */
   659         }
   660         focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
   661     }
   662 
   663     return 0;
   664 }
   665 
   666 SDL_Cursor *
   667 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   668                  int w, int h, int hot_x, int hot_y)
   669 {
   670     SDL_Surface *surface;
   671     SDL_Cursor *cursor;
   672     int x, y;
   673     Uint32 *pixel;
   674     Uint8 datab = 0, maskb = 0;
   675     const Uint32 black = 0xFF000000;
   676     const Uint32 white = 0xFFFFFFFF;
   677     const Uint32 transparent = 0x00000000;
   678 
   679     /* Make sure the width is a multiple of 8 */
   680     w = ((w + 7) & ~7);
   681 
   682     /* Create the surface from a bitmap */
   683     surface = SDL_CreateRGBSurface(0, w, h, 32,
   684                                    0x00FF0000,
   685                                    0x0000FF00,
   686                                    0x000000FF,
   687                                    0xFF000000);
   688     if (!surface) {
   689         return NULL;
   690     }
   691     for (y = 0; y < h; ++y) {
   692         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   693         for (x = 0; x < w; ++x) {
   694             if ((x % 8) == 0) {
   695                 datab = *data++;
   696                 maskb = *mask++;
   697             }
   698             if (maskb & 0x80) {
   699                 *pixel++ = (datab & 0x80) ? black : white;
   700             } else {
   701                 *pixel++ = (datab & 0x80) ? black : transparent;
   702             }
   703             datab <<= 1;
   704             maskb <<= 1;
   705         }
   706     }
   707 
   708     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   709 
   710     SDL_FreeSurface(surface);
   711 
   712     return cursor;
   713 }
   714 
   715 SDL_Cursor *
   716 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   717 {
   718     SDL_Mouse *mouse = SDL_GetMouse();
   719     SDL_Surface *temp = NULL;
   720     SDL_Cursor *cursor;
   721 
   722     if (!surface) {
   723         SDL_SetError("Passed NULL cursor surface");
   724         return NULL;
   725     }
   726 
   727     if (!mouse->CreateCursor) {
   728         SDL_SetError("Cursors are not currently supported");
   729         return NULL;
   730     }
   731 
   732     /* Sanity check the hot spot */
   733     if ((hot_x < 0) || (hot_y < 0) ||
   734         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   735         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   736         return NULL;
   737     }
   738 
   739     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   740         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   741         if (!temp) {
   742             return NULL;
   743         }
   744         surface = temp;
   745     }
   746 
   747     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   748     if (cursor) {
   749         cursor->next = mouse->cursors;
   750         mouse->cursors = cursor;
   751     }
   752 
   753     SDL_FreeSurface(temp);
   754 
   755     return cursor;
   756 }
   757 
   758 SDL_Cursor *
   759 SDL_CreateSystemCursor(SDL_SystemCursor id)
   760 {
   761     SDL_Mouse *mouse = SDL_GetMouse();
   762     SDL_Cursor *cursor;
   763 
   764     if (!mouse->CreateSystemCursor) {
   765         SDL_SetError("CreateSystemCursor is not currently supported");
   766         return NULL;
   767     }
   768 
   769     cursor = mouse->CreateSystemCursor(id);
   770     if (cursor) {
   771         cursor->next = mouse->cursors;
   772         mouse->cursors = cursor;
   773     }
   774 
   775     return cursor;
   776 }
   777 
   778 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   779    if this is desired for any reason.  This is used when setting
   780    the video mode and when the SDL window gains the mouse focus.
   781  */
   782 void
   783 SDL_SetCursor(SDL_Cursor * cursor)
   784 {
   785     SDL_Mouse *mouse = SDL_GetMouse();
   786 
   787     /* Set the new cursor */
   788     if (cursor) {
   789         /* Make sure the cursor is still valid for this mouse */
   790         if (cursor != mouse->def_cursor) {
   791             SDL_Cursor *found;
   792             for (found = mouse->cursors; found; found = found->next) {
   793                 if (found == cursor) {
   794                     break;
   795                 }
   796             }
   797             if (!found) {
   798                 SDL_SetError("Cursor not associated with the current mouse");
   799                 return;
   800             }
   801         }
   802         mouse->cur_cursor = cursor;
   803     } else {
   804         if (mouse->focus) {
   805             cursor = mouse->cur_cursor;
   806         } else {
   807             cursor = mouse->def_cursor;
   808         }
   809     }
   810 
   811     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   812         if (mouse->ShowCursor) {
   813             mouse->ShowCursor(cursor);
   814         }
   815     } else {
   816         if (mouse->ShowCursor) {
   817             mouse->ShowCursor(NULL);
   818         }
   819     }
   820 }
   821 
   822 SDL_Cursor *
   823 SDL_GetCursor(void)
   824 {
   825     SDL_Mouse *mouse = SDL_GetMouse();
   826 
   827     if (!mouse) {
   828         return NULL;
   829     }
   830     return mouse->cur_cursor;
   831 }
   832 
   833 SDL_Cursor *
   834 SDL_GetDefaultCursor(void)
   835 {
   836     SDL_Mouse *mouse = SDL_GetMouse();
   837 
   838     if (!mouse) {
   839         return NULL;
   840     }
   841     return mouse->def_cursor;
   842 }
   843 
   844 void
   845 SDL_FreeCursor(SDL_Cursor * cursor)
   846 {
   847     SDL_Mouse *mouse = SDL_GetMouse();
   848     SDL_Cursor *curr, *prev;
   849 
   850     if (!cursor) {
   851         return;
   852     }
   853 
   854     if (cursor == mouse->def_cursor) {
   855         return;
   856     }
   857     if (cursor == mouse->cur_cursor) {
   858         SDL_SetCursor(mouse->def_cursor);
   859     }
   860 
   861     for (prev = NULL, curr = mouse->cursors; curr;
   862          prev = curr, curr = curr->next) {
   863         if (curr == cursor) {
   864             if (prev) {
   865                 prev->next = curr->next;
   866             } else {
   867                 mouse->cursors = curr->next;
   868             }
   869 
   870             if (mouse->FreeCursor) {
   871                 mouse->FreeCursor(curr);
   872             }
   873             return;
   874         }
   875     }
   876 }
   877 
   878 int
   879 SDL_ShowCursor(int toggle)
   880 {
   881     SDL_Mouse *mouse = SDL_GetMouse();
   882     SDL_bool shown;
   883 
   884     if (!mouse) {
   885         return 0;
   886     }
   887 
   888     shown = mouse->cursor_shown;
   889     if (toggle >= 0) {
   890         if (toggle) {
   891             mouse->cursor_shown = SDL_TRUE;
   892         } else {
   893             mouse->cursor_shown = SDL_FALSE;
   894         }
   895         if (mouse->cursor_shown != shown) {
   896             SDL_SetCursor(NULL);
   897         }
   898     }
   899     return shown;
   900 }
   901 
   902 /* vi: set ts=4 sw=4 expandtab: */