src/events/SDL_mouse.c
author Jørgen P. Tjernø
Mon, 22 Apr 2013 18:14:55 -0700
changeset 7089 257fc4e541e1
parent 7037 3fedf1f25b94
child 7097 84bbd421d62f
permissions -rw-r--r--
Send mouse leave updates for Windows and X11.

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