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