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