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