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