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