src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 05 Dec 2013 09:14:56 -0800
changeset 8049 eac8f31e9bbd
parent 7753 e4c38f17bfad
child 8066 658b461d81be
permissions -rw-r--r--
Clean up the cursor clipping area when quitting SDL.
This fixes the cursor being clipped after the streaming client quits when streaming Dungeons of Dredmor
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_config.h"
    22 
    23 /* General mouse handling code for SDL */
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_events.h"
    27 #include "SDL_events_c.h"
    28 #include "default_cursor.h"
    29 #include "../video/SDL_sysvideo.h"
    30 
    31 /* #define DEBUG_MOUSE */
    32 
    33 /* The mouse state */
    34 static SDL_Mouse SDL_mouse;
    35 
    36 static int
    37 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y);
    38 
    39 /* Public functions */
    40 int
    41 SDL_MouseInit(void)
    42 {
    43     SDL_Mouse *mouse = SDL_GetMouse();
    44 
    45     mouse->cursor_shown = SDL_TRUE;
    46 
    47     return (0);
    48 }
    49 
    50 void
    51 SDL_SetDefaultCursor(SDL_Cursor * cursor)
    52 {
    53     SDL_Mouse *mouse = SDL_GetMouse();
    54 
    55     mouse->def_cursor = cursor;
    56     if (!mouse->cur_cursor) {
    57         SDL_SetCursor(cursor);
    58     }
    59 }
    60 
    61 SDL_Mouse *
    62 SDL_GetMouse(void)
    63 {
    64     return &SDL_mouse;
    65 }
    66 
    67 SDL_Window *
    68 SDL_GetMouseFocus(void)
    69 {
    70     SDL_Mouse *mouse = SDL_GetMouse();
    71 
    72     return mouse->focus;
    73 }
    74 
    75 void
    76 SDL_ResetMouse(void)
    77 {
    78     SDL_Mouse *mouse = SDL_GetMouse();
    79     Uint8 i;
    80 
    81 #ifdef DEBUG_MOUSE
    82     printf("Resetting mouse\n");
    83 #endif
    84     for (i = 1; i <= sizeof(mouse->buttonstate)*8; ++i) {
    85         if (mouse->buttonstate & SDL_BUTTON(i)) {
    86             SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, i);
    87         }
    88     }
    89     SDL_assert(mouse->buttonstate == 0);
    90 }
    91 
    92 void
    93 SDL_SetMouseFocus(SDL_Window * window)
    94 {
    95     SDL_Mouse *mouse = SDL_GetMouse();
    96 
    97     if (mouse->focus == window) {
    98         return;
    99     }
   100 
   101     /* Actually, this ends up being a bad idea, because most operating
   102        systems have an implicit grab when you press the mouse button down
   103        so you can drag things out of the window and then get the mouse up
   104        when it happens.  So, #if 0...
   105     */
   106 #if 0
   107     if (mouse->focus && !window) {
   108         /* We won't get anymore mouse messages, so reset mouse state */
   109         SDL_ResetMouse();
   110     }
   111 #endif
   112 
   113     /* See if the current window has lost focus */
   114     if (mouse->focus) {
   115         SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_LEAVE, 0, 0);
   116     }
   117 
   118     mouse->focus = window;
   119 
   120     if (mouse->focus) {
   121         SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_ENTER, 0, 0);
   122     }
   123 
   124     /* Update cursor visibility */
   125     SDL_SetCursor(NULL);
   126 }
   127 
   128 /* Check to see if we need to synthesize focus events */
   129 static SDL_bool
   130 SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
   131 {
   132     SDL_Mouse *mouse = SDL_GetMouse();
   133     int w, h;
   134     SDL_bool inWindow;
   135 
   136     SDL_GetWindowSize(window, &w, &h);
   137     if (x < 0 || y < 0 || x >= w || y >= h) {
   138         inWindow = SDL_FALSE;
   139     } else {
   140         inWindow = SDL_TRUE;
   141     }
   142 
   143 /* Linux doesn't give you mouse events outside your window unless you grab
   144    the pointer.
   145 
   146    Windows doesn't give you mouse events outside your window unless you call
   147    SetCapture().
   148 
   149    Both of these are slightly scary changes, so for now we'll punt and if the
   150    mouse leaves the window you'll lose mouse focus and reset button state.
   151 */
   152 #ifdef SUPPORT_DRAG_OUTSIDE_WINDOW
   153     if (!inWindow && !buttonstate) {
   154 #else
   155     if (!inWindow) {
   156 #endif
   157         if (window == mouse->focus) {
   158 #ifdef DEBUG_MOUSE
   159             printf("Mouse left window, synthesizing move & focus lost event\n");
   160 #endif
   161             SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
   162             SDL_SetMouseFocus(NULL);
   163         }
   164         return SDL_FALSE;
   165     }
   166 
   167     if (window != mouse->focus) {
   168 #ifdef DEBUG_MOUSE
   169          printf("Mouse entered window, synthesizing focus gain & move event\n");
   170 #endif
   171          SDL_SetMouseFocus(window);
   172          SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
   173     }
   174     return SDL_TRUE;
   175 }
   176 
   177 int
   178 SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y)
   179 {
   180     if (window && !relative) {
   181         SDL_Mouse *mouse = SDL_GetMouse();
   182         if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate)) {
   183             return 0;
   184         }
   185     }
   186 
   187     return SDL_PrivateSendMouseMotion(window, mouseID, relative, x, y);
   188 }
   189 
   190 static int
   191 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y)
   192 {
   193     SDL_Mouse *mouse = SDL_GetMouse();
   194     int posted;
   195     int xrel;
   196     int yrel;
   197     int x_max = 0, y_max = 0;
   198 
   199     /* relative motion is calculated regarding the system cursor last position */
   200     if (relative) {
   201         xrel = x;
   202         yrel = y;
   203         x = (mouse->last_x + x);
   204         y = (mouse->last_y + y);
   205     } else {
   206         xrel = x - mouse->last_x;
   207         yrel = y - mouse->last_y;
   208     }
   209 
   210     /* Drop events that don't change state */
   211     if (!xrel && !yrel) {
   212 #ifdef DEBUG_MOUSE
   213         printf("Mouse event didn't change state - dropped!\n");
   214 #endif
   215         return 0;
   216     }
   217 
   218     /* Update internal mouse coordinates */
   219     if (mouse->relative_mode == SDL_FALSE) {
   220         mouse->x = x;
   221         mouse->y = y;
   222     } else {
   223         mouse->x += xrel;
   224         mouse->y += yrel;
   225     }
   226 
   227     SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
   228     --x_max;
   229     --y_max;
   230 
   231     /* make sure that the pointers find themselves inside the windows */
   232     if (mouse->x > x_max) {
   233         mouse->x = x_max;
   234     }
   235     if (mouse->x < 0) {
   236         mouse->x = 0;
   237     }
   238 
   239     if (mouse->y > y_max) {
   240         mouse->y = y_max;
   241     }
   242     if (mouse->y < 0) {
   243         mouse->y = 0;
   244     }
   245 
   246     mouse->xdelta += xrel;
   247     mouse->ydelta += yrel;
   248 
   249     /* Move the mouse cursor, if needed */
   250     if (mouse->cursor_shown && !mouse->relative_mode &&
   251         mouse->MoveCursor && mouse->cur_cursor) {
   252         mouse->MoveCursor(mouse->cur_cursor);
   253     }
   254 
   255     /* Post the event, if desired */
   256     posted = 0;
   257     if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) {
   258         SDL_Event event;
   259         event.motion.type = SDL_MOUSEMOTION;
   260         event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
   261         event.motion.which = mouseID;
   262         event.motion.state = mouse->buttonstate;
   263         event.motion.x = mouse->x;
   264         event.motion.y = mouse->y;
   265         event.motion.xrel = xrel;
   266         event.motion.yrel = yrel;
   267         posted = (SDL_PushEvent(&event) > 0);
   268     }
   269     /* Use unclamped values if we're getting events outside the window */
   270     mouse->last_x = x;
   271     mouse->last_y = y;
   272     return posted;
   273 }
   274 
   275 int
   276 SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
   277 {
   278     SDL_Mouse *mouse = SDL_GetMouse();
   279     int posted;
   280     Uint32 type;
   281     Uint32 buttonstate = mouse->buttonstate;
   282 
   283     /* Figure out which event to perform */
   284     switch (state) {
   285     case SDL_PRESSED:
   286         type = SDL_MOUSEBUTTONDOWN;
   287         buttonstate |= SDL_BUTTON(button);
   288         break;
   289     case SDL_RELEASED:
   290         type = SDL_MOUSEBUTTONUP;
   291         buttonstate &= ~SDL_BUTTON(button);
   292         break;
   293     default:
   294         /* Invalid state -- bail */
   295         return 0;
   296     }
   297 
   298     /* We do this after calculating buttonstate so button presses gain focus */
   299     if (window && state == SDL_PRESSED) {
   300         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   301     }
   302 
   303     if (buttonstate == mouse->buttonstate) {
   304         /* Ignore this event, no state change */
   305         return 0;
   306     }
   307     mouse->buttonstate = buttonstate;
   308 
   309     /* Post the event, if desired */
   310     posted = 0;
   311     if (SDL_GetEventState(type) == SDL_ENABLE) {
   312         SDL_Event event;
   313         event.type = type;
   314         event.button.windowID = mouse->focus ? mouse->focus->id : 0;
   315         event.button.which = mouseID;
   316         event.button.state = state;
   317         event.button.button = button;
   318         event.button.x = mouse->x;
   319         event.button.y = mouse->y;
   320         posted = (SDL_PushEvent(&event) > 0);
   321     }
   322 
   323     /* We do this after dispatching event so button releases can lose focus */
   324     if (window && state == SDL_RELEASED) {
   325         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   326     }
   327 
   328     return posted;
   329 }
   330 
   331 int
   332 SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, int x, int y)
   333 {
   334     SDL_Mouse *mouse = SDL_GetMouse();
   335     int posted;
   336 
   337     if (window) {
   338         SDL_SetMouseFocus(window);
   339     }
   340 
   341     if (!x && !y) {
   342         return 0;
   343     }
   344 
   345     /* Post the event, if desired */
   346     posted = 0;
   347     if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) {
   348         SDL_Event event;
   349         event.type = SDL_MOUSEWHEEL;
   350         event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
   351         event.wheel.which = mouseID;
   352         event.wheel.x = x;
   353         event.wheel.y = y;
   354         posted = (SDL_PushEvent(&event) > 0);
   355     }
   356     return posted;
   357 }
   358 
   359 void
   360 SDL_MouseQuit(void)
   361 {
   362     SDL_Cursor *cursor, *next;
   363     SDL_Mouse *mouse = SDL_GetMouse();
   364 
   365     SDL_SetRelativeMouseMode(SDL_FALSE);
   366     SDL_ShowCursor(1);
   367 
   368     cursor = mouse->cursors;
   369     while (cursor) {
   370         next = cursor->next;
   371         SDL_FreeCursor(cursor);
   372         cursor = next;
   373     }
   374 
   375     if (mouse->def_cursor && mouse->FreeCursor) {
   376         mouse->FreeCursor(mouse->def_cursor);
   377     }
   378 
   379     SDL_zerop(mouse);
   380 }
   381 
   382 Uint32
   383 SDL_GetMouseState(int *x, int *y)
   384 {
   385     SDL_Mouse *mouse = SDL_GetMouse();
   386 
   387     if (x) {
   388         *x = mouse->x;
   389     }
   390     if (y) {
   391         *y = mouse->y;
   392     }
   393     return mouse->buttonstate;
   394 }
   395 
   396 Uint32
   397 SDL_GetRelativeMouseState(int *x, int *y)
   398 {
   399     SDL_Mouse *mouse = SDL_GetMouse();
   400 
   401     if (x) {
   402         *x = mouse->xdelta;
   403     }
   404     if (y) {
   405         *y = mouse->ydelta;
   406     }
   407     mouse->xdelta = 0;
   408     mouse->ydelta = 0;
   409     return mouse->buttonstate;
   410 }
   411 
   412 void
   413 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   414 {
   415     SDL_Mouse *mouse = SDL_GetMouse();
   416 
   417     if ( window == NULL )
   418         window = mouse->focus;
   419 
   420     if ( window == NULL )
   421         return;
   422 
   423     if (mouse->WarpMouse) {
   424         mouse->WarpMouse(window, x, y);
   425     } else {
   426         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
   427     }
   428 }
   429 
   430 int
   431 SDL_SetRelativeMouseMode(SDL_bool enabled)
   432 {
   433     SDL_Mouse *mouse = SDL_GetMouse();
   434     SDL_Window *focusWindow = SDL_GetKeyboardFocus();
   435     int original_x = mouse->x, original_y = mouse->y;
   436 
   437     if (enabled == mouse->relative_mode) {
   438         return 0;
   439     }
   440 
   441     if (!mouse->SetRelativeMouseMode) {
   442         return SDL_Unsupported();
   443     }
   444 
   445     if (enabled && focusWindow) {
   446         /* Center it in the focused window to prevent clicks from going through
   447          * to background windows.
   448          */
   449         SDL_SetMouseFocus(focusWindow);
   450         SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
   451     }
   452 
   453     if (mouse->SetRelativeMouseMode(enabled) < 0) {
   454         return -1;
   455     }
   456 
   457     /* Set the relative mode */
   458     mouse->relative_mode = enabled;
   459 
   460     if (enabled) {
   461         /* Save the expected mouse position */
   462         mouse->original_x = original_x;
   463         mouse->original_y = original_y;
   464     } else if (mouse->focus) {
   465         /* Restore the expected mouse position */
   466         SDL_WarpMouseInWindow(mouse->focus, mouse->original_x, mouse->original_y);
   467     }
   468 
   469     /* Flush pending mouse motion */
   470     SDL_FlushEvent(SDL_MOUSEMOTION);
   471 
   472     /* Update cursor visibility */
   473     SDL_SetCursor(NULL);
   474 
   475     return 0;
   476 }
   477 
   478 SDL_bool
   479 SDL_GetRelativeMouseMode()
   480 {
   481     SDL_Mouse *mouse = SDL_GetMouse();
   482 
   483     return mouse->relative_mode;
   484 }
   485 
   486 SDL_Cursor *
   487 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   488                  int w, int h, int hot_x, int hot_y)
   489 {
   490     SDL_Surface *surface;
   491     SDL_Cursor *cursor;
   492     int x, y;
   493     Uint32 *pixel;
   494     Uint8 datab = 0, maskb = 0;
   495     const Uint32 black = 0xFF000000;
   496     const Uint32 white = 0xFFFFFFFF;
   497     const Uint32 transparent = 0x00000000;
   498 
   499     /* Make sure the width is a multiple of 8 */
   500     w = ((w + 7) & ~7);
   501 
   502     /* Create the surface from a bitmap */
   503     surface = SDL_CreateRGBSurface(0, w, h, 32,
   504                                    0x00FF0000,
   505                                    0x0000FF00,
   506                                    0x000000FF,
   507                                    0xFF000000);
   508     if (!surface) {
   509         return NULL;
   510     }
   511     for (y = 0; y < h; ++y) {
   512         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   513         for (x = 0; x < w; ++x) {
   514             if ((x % 8) == 0) {
   515                 datab = *data++;
   516                 maskb = *mask++;
   517             }
   518             if (maskb & 0x80) {
   519                 *pixel++ = (datab & 0x80) ? black : white;
   520             } else {
   521                 *pixel++ = (datab & 0x80) ? black : transparent;
   522             }
   523             datab <<= 1;
   524             maskb <<= 1;
   525         }
   526     }
   527 
   528     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   529 
   530     SDL_FreeSurface(surface);
   531 
   532     return cursor;
   533 }
   534 
   535 SDL_Cursor *
   536 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   537 {
   538     SDL_Mouse *mouse = SDL_GetMouse();
   539     SDL_Surface *temp = NULL;
   540     SDL_Cursor *cursor;
   541 
   542     if (!surface) {
   543         SDL_SetError("Passed NULL cursor surface");
   544         return NULL;
   545     }
   546 
   547     if (!mouse->CreateCursor) {
   548         SDL_SetError("Cursors are not currently supported");
   549         return NULL;
   550     }
   551 
   552     /* Sanity check the hot spot */
   553     if ((hot_x < 0) || (hot_y < 0) ||
   554         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   555         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   556         return NULL;
   557     }
   558 
   559     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   560         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   561         if (!temp) {
   562             return NULL;
   563         }
   564         surface = temp;
   565     }
   566 
   567     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   568     if (cursor) {
   569         cursor->next = mouse->cursors;
   570         mouse->cursors = cursor;
   571     }
   572 
   573     SDL_FreeSurface(temp);
   574 
   575     return cursor;
   576 }
   577 
   578 SDL_Cursor *
   579 SDL_CreateSystemCursor(SDL_SystemCursor id)
   580 {
   581     SDL_Mouse *mouse = SDL_GetMouse();
   582     SDL_Cursor *cursor;
   583 
   584     if (!mouse->CreateSystemCursor) {
   585         SDL_SetError("CreateSystemCursor is not currently supported");
   586         return NULL;
   587     }
   588 
   589     cursor = mouse->CreateSystemCursor(id);
   590     if (cursor) {
   591         cursor->next = mouse->cursors;
   592         mouse->cursors = cursor;
   593     }
   594 
   595     return cursor;
   596 }
   597 
   598 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   599    if this is desired for any reason.  This is used when setting
   600    the video mode and when the SDL window gains the mouse focus.
   601  */
   602 void
   603 SDL_SetCursor(SDL_Cursor * cursor)
   604 {
   605     SDL_Mouse *mouse = SDL_GetMouse();
   606 
   607     /* Set the new cursor */
   608     if (cursor) {
   609         /* Make sure the cursor is still valid for this mouse */
   610         if (cursor != mouse->def_cursor) {
   611             SDL_Cursor *found;
   612             for (found = mouse->cursors; found; found = found->next) {
   613                 if (found == cursor) {
   614                     break;
   615                 }
   616             }
   617             if (!found) {
   618                 SDL_SetError("Cursor not associated with the current mouse");
   619                 return;
   620             }
   621         }
   622         mouse->cur_cursor = cursor;
   623     } else {
   624         if (mouse->focus) {
   625             cursor = mouse->cur_cursor;
   626         } else {
   627             cursor = mouse->def_cursor;
   628         }
   629     }
   630 
   631     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   632         if (mouse->ShowCursor) {
   633             mouse->ShowCursor(cursor);
   634         }
   635     } else {
   636         if (mouse->ShowCursor) {
   637             mouse->ShowCursor(NULL);
   638         }
   639     }
   640 }
   641 
   642 SDL_Cursor *
   643 SDL_GetCursor(void)
   644 {
   645     SDL_Mouse *mouse = SDL_GetMouse();
   646 
   647     if (!mouse) {
   648         return NULL;
   649     }
   650     return mouse->cur_cursor;
   651 }
   652 
   653 SDL_Cursor *
   654 SDL_GetDefaultCursor(void)
   655 {
   656     SDL_Mouse *mouse = SDL_GetMouse();
   657 
   658     if (!mouse) {
   659         return NULL;
   660     }
   661     return mouse->def_cursor;
   662 }
   663 
   664 void
   665 SDL_FreeCursor(SDL_Cursor * cursor)
   666 {
   667     SDL_Mouse *mouse = SDL_GetMouse();
   668     SDL_Cursor *curr, *prev;
   669 
   670     if (!cursor) {
   671         return;
   672     }
   673 
   674     if (cursor == mouse->def_cursor) {
   675         return;
   676     }
   677     if (cursor == mouse->cur_cursor) {
   678         SDL_SetCursor(mouse->def_cursor);
   679     }
   680 
   681     for (prev = NULL, curr = mouse->cursors; curr;
   682          prev = curr, curr = curr->next) {
   683         if (curr == cursor) {
   684             if (prev) {
   685                 prev->next = curr->next;
   686             } else {
   687                 mouse->cursors = curr->next;
   688             }
   689 
   690             if (mouse->FreeCursor) {
   691                 mouse->FreeCursor(curr);
   692             }
   693             return;
   694         }
   695     }
   696 }
   697 
   698 int
   699 SDL_ShowCursor(int toggle)
   700 {
   701     SDL_Mouse *mouse = SDL_GetMouse();
   702     SDL_bool shown;
   703 
   704     if (!mouse) {
   705         return 0;
   706     }
   707 
   708     shown = mouse->cursor_shown;
   709     if (toggle >= 0) {
   710         if (toggle) {
   711             mouse->cursor_shown = SDL_TRUE;
   712         } else {
   713             mouse->cursor_shown = SDL_FALSE;
   714         }
   715         if (mouse->cursor_shown != shown) {
   716             SDL_SetCursor(NULL);
   717         }
   718     }
   719     return shown;
   720 }
   721 
   722 /* vi: set ts=4 sw=4 expandtab: */