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