src/events/SDL_mouse.c
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Wed, 24 Apr 2013 10:42:44 -0700
changeset 7104 4e4ca313000c
parent 7097 84bbd421d62f
child 7107 2fcf7bf1d2b2
permissions -rw-r--r--
Add SDL_GetDefaultCursor.

This fixes http://bugzilla.libsdl.org/show_bug.cgi?id=1798
Thanks to Alex Szpakowski for suggestion & patch.
     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 #if 0 /* FIXME */
   250     /* Move the mouse cursor, if needed */
   251     if (mouse->cursor_shown && !mouse->relative_mode &&
   252         mouse->MoveCursor && mouse->cur_cursor) {
   253         mouse->MoveCursor(mouse->cur_cursor);
   254     }
   255 #endif
   256 
   257     /* Post the event, if desired */
   258     posted = 0;
   259     if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) {
   260         SDL_Event event;
   261         event.motion.type = SDL_MOUSEMOTION;
   262         event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
   263         event.motion.which = mouseID;
   264         event.motion.state = mouse->buttonstate;
   265         event.motion.x = mouse->x;
   266         event.motion.y = mouse->y;
   267         event.motion.xrel = xrel;
   268         event.motion.yrel = yrel;
   269         posted = (SDL_PushEvent(&event) > 0);
   270     }
   271     /* Use unclamped values if we're getting events outside the window */
   272     mouse->last_x = x;
   273     mouse->last_y = y;
   274     return posted;
   275 }
   276 
   277 int
   278 SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
   279 {
   280     SDL_Mouse *mouse = SDL_GetMouse();
   281     int posted;
   282     Uint32 type;
   283     Uint32 buttonstate = mouse->buttonstate;
   284 
   285     /* Figure out which event to perform */
   286     switch (state) {
   287     case SDL_PRESSED:
   288         type = SDL_MOUSEBUTTONDOWN;
   289         buttonstate |= SDL_BUTTON(button);
   290         break;
   291     case SDL_RELEASED:
   292         type = SDL_MOUSEBUTTONUP;
   293         buttonstate &= ~SDL_BUTTON(button);
   294         break;
   295     default:
   296         /* Invalid state -- bail */
   297         return 0;
   298     }
   299 
   300     /* We do this after calculating buttonstate so button presses gain focus */
   301     if (window && state == SDL_PRESSED) {
   302         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   303     }
   304 
   305     if (buttonstate == mouse->buttonstate) {
   306         /* Ignore this event, no state change */
   307         return 0;
   308     }
   309     mouse->buttonstate = buttonstate;
   310 
   311     /* Post the event, if desired */
   312     posted = 0;
   313     if (SDL_GetEventState(type) == SDL_ENABLE) {
   314         SDL_Event event;
   315         event.type = type;
   316         event.button.windowID = mouse->focus ? mouse->focus->id : 0;
   317         event.button.which = mouseID;
   318         event.button.state = state;
   319         event.button.button = button;
   320         event.button.x = mouse->x;
   321         event.button.y = mouse->y;
   322         posted = (SDL_PushEvent(&event) > 0);
   323     }
   324 
   325     /* We do this after dispatching event so button releases can lose focus */
   326     if (window && state == SDL_RELEASED) {
   327         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   328     }
   329 
   330     return posted;
   331 }
   332 
   333 int
   334 SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, int x, int y)
   335 {
   336     SDL_Mouse *mouse = SDL_GetMouse();
   337     int posted;
   338 
   339     if (window) {
   340         SDL_SetMouseFocus(window);
   341     }
   342 
   343     if (!x && !y) {
   344         return 0;
   345     }
   346 
   347     /* Post the event, if desired */
   348     posted = 0;
   349     if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) {
   350         SDL_Event event;
   351         event.type = SDL_MOUSEWHEEL;
   352         event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
   353         event.wheel.which = mouseID;
   354         event.wheel.x = x;
   355         event.wheel.y = y;
   356         posted = (SDL_PushEvent(&event) > 0);
   357     }
   358     return posted;
   359 }
   360 
   361 void
   362 SDL_MouseQuit(void)
   363 {
   364 }
   365 
   366 Uint32
   367 SDL_GetMouseState(int *x, int *y)
   368 {
   369     SDL_Mouse *mouse = SDL_GetMouse();
   370 
   371     if (x) {
   372         *x = mouse->x;
   373     }
   374     if (y) {
   375         *y = mouse->y;
   376     }
   377     return mouse->buttonstate;
   378 }
   379 
   380 Uint32
   381 SDL_GetRelativeMouseState(int *x, int *y)
   382 {
   383     SDL_Mouse *mouse = SDL_GetMouse();
   384 
   385     if (x) {
   386         *x = mouse->xdelta;
   387     }
   388     if (y) {
   389         *y = mouse->ydelta;
   390     }
   391     mouse->xdelta = 0;
   392     mouse->ydelta = 0;
   393     return mouse->buttonstate;
   394 }
   395 
   396 void
   397 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   398 {
   399     SDL_Mouse *mouse = SDL_GetMouse();
   400 	
   401 	if ( window == NULL )
   402 		window = mouse->focus;
   403 	
   404 	if ( window == NULL )
   405 		return;
   406 
   407     if (mouse->WarpMouse) {
   408         mouse->WarpMouse(window, x, y);
   409     } else {
   410         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
   411     }
   412 }
   413 
   414 int
   415 SDL_SetRelativeMouseMode(SDL_bool enabled)
   416 {
   417     SDL_Mouse *mouse = SDL_GetMouse();
   418 
   419     if (enabled == mouse->relative_mode) {
   420         return 0;
   421     }
   422 
   423     if (!mouse->SetRelativeMouseMode) {
   424         return SDL_Unsupported();
   425     }
   426 
   427     if (mouse->SetRelativeMouseMode(enabled) < 0) {
   428         return -1;
   429     }
   430 
   431     /* Set the relative mode */
   432     mouse->relative_mode = enabled;
   433 
   434     if (enabled) {
   435         /* Save the expected mouse position */
   436         mouse->original_x = mouse->x;
   437         mouse->original_y = mouse->y;
   438     } else if (mouse->focus) {
   439         /* Restore the expected mouse position */
   440         SDL_WarpMouseInWindow(mouse->focus, mouse->original_x, mouse->original_y);
   441     }
   442 
   443     /* Flush pending mouse motion */
   444     SDL_FlushEvent(SDL_MOUSEMOTION);
   445 
   446     /* Update cursor visibility */
   447     SDL_SetCursor(NULL);
   448 
   449     return 0;
   450 }
   451 
   452 SDL_bool
   453 SDL_GetRelativeMouseMode()
   454 {
   455     SDL_Mouse *mouse = SDL_GetMouse();
   456 
   457     return mouse->relative_mode;
   458 }
   459 
   460 SDL_Cursor *
   461 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   462                  int w, int h, int hot_x, int hot_y)
   463 {
   464     SDL_Surface *surface;
   465     SDL_Cursor *cursor;
   466     int x, y;
   467     Uint32 *pixel;
   468     Uint8 datab = 0, maskb = 0;
   469     const Uint32 black = 0xFF000000;
   470     const Uint32 white = 0xFFFFFFFF;
   471     const Uint32 transparent = 0x00000000;
   472 
   473     /* Make sure the width is a multiple of 8 */
   474     w = ((w + 7) & ~7);
   475 
   476     /* Create the surface from a bitmap */
   477     surface = SDL_CreateRGBSurface(0, w, h, 32,
   478                                    0x00FF0000,
   479                                    0x0000FF00,
   480                                    0x000000FF,
   481                                    0xFF000000);
   482     if (!surface) {
   483         return NULL;
   484     }
   485     for (y = 0; y < h; ++y) {
   486         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   487         for (x = 0; x < w; ++x) {
   488             if ((x % 8) == 0) {
   489                 datab = *data++;
   490                 maskb = *mask++;
   491             }
   492             if (maskb & 0x80) {
   493                 *pixel++ = (datab & 0x80) ? black : white;
   494             } else {
   495                 *pixel++ = (datab & 0x80) ? black : transparent;
   496             }
   497             datab <<= 1;
   498             maskb <<= 1;
   499         }
   500     }
   501 
   502     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   503 
   504     SDL_FreeSurface(surface);
   505 
   506     return cursor;
   507 }
   508 
   509 SDL_Cursor *
   510 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   511 {
   512     SDL_Mouse *mouse = SDL_GetMouse();
   513     SDL_Surface *temp = NULL;
   514     SDL_Cursor *cursor;
   515 
   516     if (!surface) {
   517         SDL_SetError("Passed NULL cursor surface");
   518         return NULL;
   519     }
   520 
   521     if (!mouse->CreateCursor) {
   522         SDL_SetError("Cursors are not currently supported");
   523         return NULL;
   524     }
   525 
   526     /* Sanity check the hot spot */
   527     if ((hot_x < 0) || (hot_y < 0) ||
   528         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   529         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   530         return NULL;
   531     }
   532 
   533     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   534         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   535         if (!temp) {
   536             return NULL;
   537         }
   538         surface = temp;
   539     }
   540 
   541     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   542     if (cursor) {
   543         cursor->next = mouse->cursors;
   544         mouse->cursors = cursor;
   545     }
   546 
   547     if (temp) {
   548         SDL_FreeSurface(temp);
   549     }
   550 
   551     return cursor;
   552 }
   553 
   554 SDL_Cursor *
   555 SDL_CreateSystemCursor(SDL_SystemCursor id)
   556 {
   557     SDL_Mouse *mouse = SDL_GetMouse();
   558     SDL_Cursor *cursor;
   559 
   560     if (!mouse->CreateSystemCursor) {
   561         SDL_SetError("CreateSystemCursor is not currently supported");
   562         return NULL;
   563     }
   564 
   565 	cursor = mouse->CreateSystemCursor(id);
   566     if (cursor) {
   567         cursor->next = mouse->cursors;
   568         mouse->cursors = cursor;
   569     }
   570 
   571 	return cursor;
   572 }
   573 
   574 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   575    if this is desired for any reason.  This is used when setting
   576    the video mode and when the SDL window gains the mouse focus.
   577  */
   578 void
   579 SDL_SetCursor(SDL_Cursor * cursor)
   580 {
   581     SDL_Mouse *mouse = SDL_GetMouse();
   582 
   583     /* Set the new cursor */
   584     if (cursor) {
   585         /* Make sure the cursor is still valid for this mouse */
   586         if (cursor != mouse->def_cursor) {
   587             SDL_Cursor *found;
   588             for (found = mouse->cursors; found; found = found->next) {
   589                 if (found == cursor) {
   590                     break;
   591                 }
   592             }
   593             if (!found) {
   594                 SDL_SetError("Cursor not associated with the current mouse");
   595                 return;
   596             }
   597         }
   598         mouse->cur_cursor = cursor;
   599     } else {
   600         if (mouse->focus) {
   601             cursor = mouse->cur_cursor;
   602         } else {
   603             cursor = mouse->def_cursor;
   604         }
   605     }
   606 
   607     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   608         if (mouse->ShowCursor) {
   609             mouse->ShowCursor(cursor);
   610         }
   611     } else {
   612         if (mouse->ShowCursor) {
   613             mouse->ShowCursor(NULL);
   614         }
   615     }
   616 }
   617 
   618 SDL_Cursor *
   619 SDL_GetCursor(void)
   620 {
   621     SDL_Mouse *mouse = SDL_GetMouse();
   622 
   623     if (!mouse) {
   624         return NULL;
   625     }
   626     return mouse->cur_cursor;
   627 }
   628 
   629 SDL_Cursor *
   630 SDL_GetDefaultCursor(void)
   631 {
   632     SDL_Mouse *mouse = SDL_GetMouse();
   633 
   634     if (!mouse) {
   635         return NULL;
   636     }
   637     return mouse->def_cursor;
   638 }
   639 
   640 void
   641 SDL_FreeCursor(SDL_Cursor * cursor)
   642 {
   643     SDL_Mouse *mouse = SDL_GetMouse();
   644     SDL_Cursor *curr, *prev;
   645 
   646     if (!cursor) {
   647         return;
   648     }
   649 
   650     if (cursor == mouse->def_cursor) {
   651         return;
   652     }
   653     if (cursor == mouse->cur_cursor) {
   654         SDL_SetCursor(mouse->def_cursor);
   655     }
   656 
   657     for (prev = NULL, curr = mouse->cursors; curr;
   658          prev = curr, curr = curr->next) {
   659         if (curr == cursor) {
   660             if (prev) {
   661                 prev->next = curr->next;
   662             } else {
   663                 mouse->cursors = curr->next;
   664             }
   665 
   666             if (mouse->FreeCursor) {
   667                 mouse->FreeCursor(curr);
   668             }
   669             return;
   670         }
   671     }
   672 }
   673 
   674 int
   675 SDL_ShowCursor(int toggle)
   676 {
   677     SDL_Mouse *mouse = SDL_GetMouse();
   678     SDL_bool shown;
   679 
   680     if (!mouse) {
   681         return 0;
   682     }
   683 
   684     shown = mouse->cursor_shown;
   685     if (toggle >= 0) {
   686         if (toggle) {
   687             mouse->cursor_shown = SDL_TRUE;
   688         } else {
   689             mouse->cursor_shown = SDL_FALSE;
   690         }
   691         if (mouse->cursor_shown != shown) {
   692             SDL_SetCursor(NULL);
   693         }
   694     }
   695     return shown;
   696 }
   697 
   698 /* vi: set ts=4 sw=4 expandtab: */