src/events/SDL_mouse.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 02 Mar 2013 20:44:16 -0800
changeset 6950 1ddb72193079
parent 6885 700f1b25f77f
child 7037 3fedf1f25b94
permissions -rw-r--r--
Added a mouse ID to the mouse events, which set to the special value SDL_TOUCH_MOUSEID for mouse events simulated by touch input.
     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 
    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, mouse->mouseID, 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, SDL_MouseID mouseID, 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.which = mouseID;
   256         event.motion.state = mouse->buttonstate;
   257         event.motion.x = mouse->x;
   258         event.motion.y = mouse->y;
   259         event.motion.xrel = xrel;
   260         event.motion.yrel = yrel;
   261         posted = (SDL_PushEvent(&event) > 0);
   262     }
   263     /* Use unclamped values if we're getting events outside the window */
   264     mouse->last_x = x;
   265     mouse->last_y = y;
   266     return posted;
   267 }
   268 
   269 int
   270 SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
   271 {
   272     SDL_Mouse *mouse = SDL_GetMouse();
   273     int posted;
   274     Uint32 type;
   275     Uint32 buttonstate = mouse->buttonstate;
   276 
   277     /* Figure out which event to perform */
   278     switch (state) {
   279     case SDL_PRESSED:
   280         type = SDL_MOUSEBUTTONDOWN;
   281         buttonstate |= SDL_BUTTON(button);
   282         break;
   283     case SDL_RELEASED:
   284         type = SDL_MOUSEBUTTONUP;
   285         buttonstate &= ~SDL_BUTTON(button);
   286         break;
   287     default:
   288         /* Invalid state -- bail */
   289         return 0;
   290     }
   291 
   292     /* We do this after calculating buttonstate so button presses gain focus */
   293     if (window && state == SDL_PRESSED) {
   294         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   295     }
   296 
   297     if (buttonstate == mouse->buttonstate) {
   298         /* Ignore this event, no state change */
   299         return 0;
   300     }
   301     mouse->buttonstate = buttonstate;
   302 
   303     /* Post the event, if desired */
   304     posted = 0;
   305     if (SDL_GetEventState(type) == SDL_ENABLE) {
   306         SDL_Event event;
   307         event.type = type;
   308         event.button.windowID = mouse->focus ? mouse->focus->id : 0;
   309         event.button.which = mouseID;
   310         event.button.state = state;
   311         event.button.button = button;
   312         event.button.x = mouse->x;
   313         event.button.y = mouse->y;
   314         posted = (SDL_PushEvent(&event) > 0);
   315     }
   316 
   317     /* We do this after dispatching event so button releases can lose focus */
   318     if (window && state == SDL_RELEASED) {
   319         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
   320     }
   321 
   322     return posted;
   323 }
   324 
   325 int
   326 SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, int x, int y)
   327 {
   328     SDL_Mouse *mouse = SDL_GetMouse();
   329     int posted;
   330 
   331     if (window) {
   332         SDL_SetMouseFocus(window);
   333     }
   334 
   335     if (!x && !y) {
   336         return 0;
   337     }
   338 
   339     /* Post the event, if desired */
   340     posted = 0;
   341     if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) {
   342         SDL_Event event;
   343         event.type = SDL_MOUSEWHEEL;
   344         event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
   345         event.wheel.which = mouseID;
   346         event.wheel.x = x;
   347         event.wheel.y = y;
   348         posted = (SDL_PushEvent(&event) > 0);
   349     }
   350     return posted;
   351 }
   352 
   353 void
   354 SDL_MouseQuit(void)
   355 {
   356 }
   357 
   358 Uint32
   359 SDL_GetMouseState(int *x, int *y)
   360 {
   361     SDL_Mouse *mouse = SDL_GetMouse();
   362 
   363     if (x) {
   364         *x = mouse->x;
   365     }
   366     if (y) {
   367         *y = mouse->y;
   368     }
   369     return mouse->buttonstate;
   370 }
   371 
   372 Uint32
   373 SDL_GetRelativeMouseState(int *x, int *y)
   374 {
   375     SDL_Mouse *mouse = SDL_GetMouse();
   376 
   377     if (x) {
   378         *x = mouse->xdelta;
   379     }
   380     if (y) {
   381         *y = mouse->ydelta;
   382     }
   383     mouse->xdelta = 0;
   384     mouse->ydelta = 0;
   385     return mouse->buttonstate;
   386 }
   387 
   388 void
   389 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   390 {
   391     SDL_Mouse *mouse = SDL_GetMouse();
   392 	
   393 	if ( window == NULL )
   394 		window = mouse->focus;
   395 	
   396 	if ( window == NULL )
   397 		return;
   398 
   399     if (mouse->WarpMouse) {
   400         mouse->WarpMouse(window, x, y);
   401     } else {
   402         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
   403     }
   404 }
   405 
   406 int
   407 SDL_SetRelativeMouseMode(SDL_bool enabled)
   408 {
   409     SDL_Mouse *mouse = SDL_GetMouse();
   410 
   411     if (enabled == mouse->relative_mode) {
   412         return 0;
   413     }
   414 
   415     if (!mouse->SetRelativeMouseMode) {
   416         SDL_Unsupported();
   417         return -1;
   418     }
   419 
   420     if (mouse->SetRelativeMouseMode(enabled) < 0) {
   421         return -1;
   422     }
   423 
   424     /* Set the relative mode */
   425     mouse->relative_mode = enabled;
   426 
   427     if (enabled) {
   428         /* Save the expected mouse position */
   429         mouse->original_x = mouse->x;
   430         mouse->original_y = mouse->y;
   431     } else if (mouse->focus) {
   432         /* Restore the expected mouse position */
   433         SDL_WarpMouseInWindow(mouse->focus, mouse->original_x, mouse->original_y);
   434     }
   435 
   436     /* Flush pending mouse motion */
   437     SDL_FlushEvent(SDL_MOUSEMOTION);
   438 
   439     /* Update cursor visibility */
   440     SDL_SetCursor(NULL);
   441 
   442     return 0;
   443 }
   444 
   445 SDL_bool
   446 SDL_GetRelativeMouseMode()
   447 {
   448     SDL_Mouse *mouse = SDL_GetMouse();
   449 
   450     return mouse->relative_mode;
   451 }
   452 
   453 SDL_Cursor *
   454 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   455                  int w, int h, int hot_x, int hot_y)
   456 {
   457     SDL_Surface *surface;
   458     SDL_Cursor *cursor;
   459     int x, y;
   460     Uint32 *pixel;
   461     Uint8 datab = 0, maskb = 0;
   462     const Uint32 black = 0xFF000000;
   463     const Uint32 white = 0xFFFFFFFF;
   464     const Uint32 transparent = 0x00000000;
   465 
   466     /* Make sure the width is a multiple of 8 */
   467     w = ((w + 7) & ~7);
   468 
   469     /* Create the surface from a bitmap */
   470     surface = SDL_CreateRGBSurface(0, w, h, 32,
   471                                    0x00FF0000,
   472                                    0x0000FF00,
   473                                    0x000000FF,
   474                                    0xFF000000);
   475     if (!surface) {
   476         return NULL;
   477     }
   478     for (y = 0; y < h; ++y) {
   479         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   480         for (x = 0; x < w; ++x) {
   481             if ((x % 8) == 0) {
   482                 datab = *data++;
   483                 maskb = *mask++;
   484             }
   485             if (maskb & 0x80) {
   486                 *pixel++ = (datab & 0x80) ? black : white;
   487             } else {
   488                 *pixel++ = (datab & 0x80) ? black : transparent;
   489             }
   490             datab <<= 1;
   491             maskb <<= 1;
   492         }
   493     }
   494 
   495     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   496 
   497     SDL_FreeSurface(surface);
   498 
   499     return cursor;
   500 }
   501 
   502 SDL_Cursor *
   503 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   504 {
   505     SDL_Mouse *mouse = SDL_GetMouse();
   506     SDL_Surface *temp = NULL;
   507     SDL_Cursor *cursor;
   508 
   509     if (!surface) {
   510         SDL_SetError("Passed NULL cursor surface");
   511         return NULL;
   512     }
   513 
   514     if (!mouse->CreateCursor) {
   515         SDL_SetError("Cursors are not currently supported");
   516         return NULL;
   517     }
   518 
   519     /* Sanity check the hot spot */
   520     if ((hot_x < 0) || (hot_y < 0) ||
   521         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   522         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   523         return NULL;
   524     }
   525 
   526     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   527         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   528         if (!temp) {
   529             return NULL;
   530         }
   531         surface = temp;
   532     }
   533 
   534     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   535     if (cursor) {
   536         cursor->next = mouse->cursors;
   537         mouse->cursors = cursor;
   538     }
   539 
   540     if (temp) {
   541         SDL_FreeSurface(temp);
   542     }
   543 
   544     return cursor;
   545 }
   546 
   547 SDL_Cursor *
   548 SDL_CreateSystemCursor(SDL_SystemCursor id)
   549 {
   550     SDL_Mouse *mouse = SDL_GetMouse();
   551     SDL_Cursor *cursor;
   552 
   553     if (!mouse->CreateSystemCursor) {
   554         SDL_SetError("CreateSystemCursor is not currently supported");
   555         return NULL;
   556     }
   557 
   558 	cursor = mouse->CreateSystemCursor(id);
   559     if (cursor) {
   560         cursor->next = mouse->cursors;
   561         mouse->cursors = cursor;
   562     }
   563 
   564 	return cursor;
   565 }
   566 
   567 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   568    if this is desired for any reason.  This is used when setting
   569    the video mode and when the SDL window gains the mouse focus.
   570  */
   571 void
   572 SDL_SetCursor(SDL_Cursor * cursor)
   573 {
   574     SDL_Mouse *mouse = SDL_GetMouse();
   575 
   576     /* Set the new cursor */
   577     if (cursor) {
   578         /* Make sure the cursor is still valid for this mouse */
   579         if (cursor != mouse->def_cursor) {
   580             SDL_Cursor *found;
   581             for (found = mouse->cursors; found; found = found->next) {
   582                 if (found == cursor) {
   583                     break;
   584                 }
   585             }
   586             if (!found) {
   587                 SDL_SetError("Cursor not associated with the current mouse");
   588                 return;
   589             }
   590         }
   591         mouse->cur_cursor = cursor;
   592     } else {
   593         if (mouse->focus) {
   594             cursor = mouse->cur_cursor;
   595         } else {
   596             cursor = mouse->def_cursor;
   597         }
   598     }
   599 
   600     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   601         if (mouse->ShowCursor) {
   602             mouse->ShowCursor(cursor);
   603         }
   604     } else {
   605         if (mouse->ShowCursor) {
   606             mouse->ShowCursor(NULL);
   607         }
   608     }
   609 }
   610 
   611 SDL_Cursor *
   612 SDL_GetCursor(void)
   613 {
   614     SDL_Mouse *mouse = SDL_GetMouse();
   615 
   616     if (!mouse) {
   617         return NULL;
   618     }
   619     return mouse->cur_cursor;
   620 }
   621 
   622 void
   623 SDL_FreeCursor(SDL_Cursor * cursor)
   624 {
   625     SDL_Mouse *mouse = SDL_GetMouse();
   626     SDL_Cursor *curr, *prev;
   627 
   628     if (!cursor) {
   629         return;
   630     }
   631 
   632     if (cursor == mouse->def_cursor) {
   633         return;
   634     }
   635     if (cursor == mouse->cur_cursor) {
   636         SDL_SetCursor(mouse->def_cursor);
   637     }
   638 
   639     for (prev = NULL, curr = mouse->cursors; curr;
   640          prev = curr, curr = curr->next) {
   641         if (curr == cursor) {
   642             if (prev) {
   643                 prev->next = curr->next;
   644             } else {
   645                 mouse->cursors = curr->next;
   646             }
   647 
   648             if (mouse->FreeCursor) {
   649                 mouse->FreeCursor(curr);
   650             }
   651             return;
   652         }
   653     }
   654 }
   655 
   656 int
   657 SDL_ShowCursor(int toggle)
   658 {
   659     SDL_Mouse *mouse = SDL_GetMouse();
   660     SDL_bool shown;
   661 
   662     if (!mouse) {
   663         return 0;
   664     }
   665 
   666     shown = mouse->cursor_shown;
   667     if (toggle >= 0) {
   668         if (toggle) {
   669             mouse->cursor_shown = SDL_TRUE;
   670         } else {
   671             mouse->cursor_shown = SDL_FALSE;
   672         }
   673         if (mouse->cursor_shown != shown) {
   674             SDL_SetCursor(NULL);
   675         }
   676     }
   677     return shown;
   678 }
   679 
   680 /* vi: set ts=4 sw=4 expandtab: */