src/events/SDL_mouse.c
author Gabriel Jacobo <gabomdq@gmail.com>
Sat, 28 Sep 2013 13:28:19 -0300
changeset 7753 e4c38f17bfad
parent 7720 f9a649383362
child 8049 eac8f31e9bbd
permissions -rw-r--r--
Raspberry Pi support (also unified UDEV and EVDEV support)
     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_ShowCursor(1);
   366 
   367     cursor = mouse->cursors;
   368     while (cursor) {
   369         next = cursor->next;
   370         SDL_FreeCursor(cursor);
   371         cursor = next;
   372     }
   373 
   374     if (mouse->def_cursor && mouse->FreeCursor) {
   375         mouse->FreeCursor(mouse->def_cursor);
   376     }
   377 
   378     SDL_zerop(mouse);
   379 }
   380 
   381 Uint32
   382 SDL_GetMouseState(int *x, int *y)
   383 {
   384     SDL_Mouse *mouse = SDL_GetMouse();
   385 
   386     if (x) {
   387         *x = mouse->x;
   388     }
   389     if (y) {
   390         *y = mouse->y;
   391     }
   392     return mouse->buttonstate;
   393 }
   394 
   395 Uint32
   396 SDL_GetRelativeMouseState(int *x, int *y)
   397 {
   398     SDL_Mouse *mouse = SDL_GetMouse();
   399 
   400     if (x) {
   401         *x = mouse->xdelta;
   402     }
   403     if (y) {
   404         *y = mouse->ydelta;
   405     }
   406     mouse->xdelta = 0;
   407     mouse->ydelta = 0;
   408     return mouse->buttonstate;
   409 }
   410 
   411 void
   412 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   413 {
   414     SDL_Mouse *mouse = SDL_GetMouse();
   415 
   416     if ( window == NULL )
   417         window = mouse->focus;
   418 
   419     if ( window == NULL )
   420         return;
   421 
   422     if (mouse->WarpMouse) {
   423         mouse->WarpMouse(window, x, y);
   424     } else {
   425         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
   426     }
   427 }
   428 
   429 int
   430 SDL_SetRelativeMouseMode(SDL_bool enabled)
   431 {
   432     SDL_Mouse *mouse = SDL_GetMouse();
   433     SDL_Window *focusWindow = SDL_GetKeyboardFocus();
   434     int original_x = mouse->x, original_y = mouse->y;
   435 
   436     if (enabled == mouse->relative_mode) {
   437         return 0;
   438     }
   439 
   440     if (!mouse->SetRelativeMouseMode) {
   441         return SDL_Unsupported();
   442     }
   443 
   444     if (enabled && focusWindow) {
   445         /* Center it in the focused window to prevent clicks from going through
   446          * to background windows.
   447          */
   448         SDL_SetMouseFocus(focusWindow);
   449         SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
   450     }
   451 
   452     if (mouse->SetRelativeMouseMode(enabled) < 0) {
   453         return -1;
   454     }
   455 
   456     /* Set the relative mode */
   457     mouse->relative_mode = enabled;
   458 
   459     if (enabled) {
   460         /* Save the expected mouse position */
   461         mouse->original_x = original_x;
   462         mouse->original_y = original_y;
   463     } else if (mouse->focus) {
   464         /* Restore the expected mouse position */
   465         SDL_WarpMouseInWindow(mouse->focus, mouse->original_x, mouse->original_y);
   466     }
   467 
   468     /* Flush pending mouse motion */
   469     SDL_FlushEvent(SDL_MOUSEMOTION);
   470 
   471     /* Update cursor visibility */
   472     SDL_SetCursor(NULL);
   473 
   474     return 0;
   475 }
   476 
   477 SDL_bool
   478 SDL_GetRelativeMouseMode()
   479 {
   480     SDL_Mouse *mouse = SDL_GetMouse();
   481 
   482     return mouse->relative_mode;
   483 }
   484 
   485 SDL_Cursor *
   486 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   487                  int w, int h, int hot_x, int hot_y)
   488 {
   489     SDL_Surface *surface;
   490     SDL_Cursor *cursor;
   491     int x, y;
   492     Uint32 *pixel;
   493     Uint8 datab = 0, maskb = 0;
   494     const Uint32 black = 0xFF000000;
   495     const Uint32 white = 0xFFFFFFFF;
   496     const Uint32 transparent = 0x00000000;
   497 
   498     /* Make sure the width is a multiple of 8 */
   499     w = ((w + 7) & ~7);
   500 
   501     /* Create the surface from a bitmap */
   502     surface = SDL_CreateRGBSurface(0, w, h, 32,
   503                                    0x00FF0000,
   504                                    0x0000FF00,
   505                                    0x000000FF,
   506                                    0xFF000000);
   507     if (!surface) {
   508         return NULL;
   509     }
   510     for (y = 0; y < h; ++y) {
   511         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   512         for (x = 0; x < w; ++x) {
   513             if ((x % 8) == 0) {
   514                 datab = *data++;
   515                 maskb = *mask++;
   516             }
   517             if (maskb & 0x80) {
   518                 *pixel++ = (datab & 0x80) ? black : white;
   519             } else {
   520                 *pixel++ = (datab & 0x80) ? black : transparent;
   521             }
   522             datab <<= 1;
   523             maskb <<= 1;
   524         }
   525     }
   526 
   527     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   528 
   529     SDL_FreeSurface(surface);
   530 
   531     return cursor;
   532 }
   533 
   534 SDL_Cursor *
   535 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   536 {
   537     SDL_Mouse *mouse = SDL_GetMouse();
   538     SDL_Surface *temp = NULL;
   539     SDL_Cursor *cursor;
   540 
   541     if (!surface) {
   542         SDL_SetError("Passed NULL cursor surface");
   543         return NULL;
   544     }
   545 
   546     if (!mouse->CreateCursor) {
   547         SDL_SetError("Cursors are not currently supported");
   548         return NULL;
   549     }
   550 
   551     /* Sanity check the hot spot */
   552     if ((hot_x < 0) || (hot_y < 0) ||
   553         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   554         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   555         return NULL;
   556     }
   557 
   558     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   559         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   560         if (!temp) {
   561             return NULL;
   562         }
   563         surface = temp;
   564     }
   565 
   566     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   567     if (cursor) {
   568         cursor->next = mouse->cursors;
   569         mouse->cursors = cursor;
   570     }
   571 
   572     SDL_FreeSurface(temp);
   573 
   574     return cursor;
   575 }
   576 
   577 SDL_Cursor *
   578 SDL_CreateSystemCursor(SDL_SystemCursor id)
   579 {
   580     SDL_Mouse *mouse = SDL_GetMouse();
   581     SDL_Cursor *cursor;
   582 
   583     if (!mouse->CreateSystemCursor) {
   584         SDL_SetError("CreateSystemCursor is not currently supported");
   585         return NULL;
   586     }
   587 
   588     cursor = mouse->CreateSystemCursor(id);
   589     if (cursor) {
   590         cursor->next = mouse->cursors;
   591         mouse->cursors = cursor;
   592     }
   593 
   594     return cursor;
   595 }
   596 
   597 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   598    if this is desired for any reason.  This is used when setting
   599    the video mode and when the SDL window gains the mouse focus.
   600  */
   601 void
   602 SDL_SetCursor(SDL_Cursor * cursor)
   603 {
   604     SDL_Mouse *mouse = SDL_GetMouse();
   605 
   606     /* Set the new cursor */
   607     if (cursor) {
   608         /* Make sure the cursor is still valid for this mouse */
   609         if (cursor != mouse->def_cursor) {
   610             SDL_Cursor *found;
   611             for (found = mouse->cursors; found; found = found->next) {
   612                 if (found == cursor) {
   613                     break;
   614                 }
   615             }
   616             if (!found) {
   617                 SDL_SetError("Cursor not associated with the current mouse");
   618                 return;
   619             }
   620         }
   621         mouse->cur_cursor = cursor;
   622     } else {
   623         if (mouse->focus) {
   624             cursor = mouse->cur_cursor;
   625         } else {
   626             cursor = mouse->def_cursor;
   627         }
   628     }
   629 
   630     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   631         if (mouse->ShowCursor) {
   632             mouse->ShowCursor(cursor);
   633         }
   634     } else {
   635         if (mouse->ShowCursor) {
   636             mouse->ShowCursor(NULL);
   637         }
   638     }
   639 }
   640 
   641 SDL_Cursor *
   642 SDL_GetCursor(void)
   643 {
   644     SDL_Mouse *mouse = SDL_GetMouse();
   645 
   646     if (!mouse) {
   647         return NULL;
   648     }
   649     return mouse->cur_cursor;
   650 }
   651 
   652 SDL_Cursor *
   653 SDL_GetDefaultCursor(void)
   654 {
   655     SDL_Mouse *mouse = SDL_GetMouse();
   656 
   657     if (!mouse) {
   658         return NULL;
   659     }
   660     return mouse->def_cursor;
   661 }
   662 
   663 void
   664 SDL_FreeCursor(SDL_Cursor * cursor)
   665 {
   666     SDL_Mouse *mouse = SDL_GetMouse();
   667     SDL_Cursor *curr, *prev;
   668 
   669     if (!cursor) {
   670         return;
   671     }
   672 
   673     if (cursor == mouse->def_cursor) {
   674         return;
   675     }
   676     if (cursor == mouse->cur_cursor) {
   677         SDL_SetCursor(mouse->def_cursor);
   678     }
   679 
   680     for (prev = NULL, curr = mouse->cursors; curr;
   681          prev = curr, curr = curr->next) {
   682         if (curr == cursor) {
   683             if (prev) {
   684                 prev->next = curr->next;
   685             } else {
   686                 mouse->cursors = curr->next;
   687             }
   688 
   689             if (mouse->FreeCursor) {
   690                 mouse->FreeCursor(curr);
   691             }
   692             return;
   693         }
   694     }
   695 }
   696 
   697 int
   698 SDL_ShowCursor(int toggle)
   699 {
   700     SDL_Mouse *mouse = SDL_GetMouse();
   701     SDL_bool shown;
   702 
   703     if (!mouse) {
   704         return 0;
   705     }
   706 
   707     shown = mouse->cursor_shown;
   708     if (toggle >= 0) {
   709         if (toggle) {
   710             mouse->cursor_shown = SDL_TRUE;
   711         } else {
   712             mouse->cursor_shown = SDL_FALSE;
   713         }
   714         if (mouse->cursor_shown != shown) {
   715             SDL_SetCursor(NULL);
   716         }
   717     }
   718     return shown;
   719 }
   720 
   721 /* vi: set ts=4 sw=4 expandtab: */