src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 14 Aug 2017 06:28:21 -0700
changeset 11284 3db78361e751
parent 11258 9fc2e83124ba
child 11300 68a80d7afec3
permissions -rw-r--r--
Fixed bug 3745 - specify SDLCALL as the calling convention for API callbacks

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