src/events/SDL_mouse.c
author Michael Sartain
Mon, 19 Nov 2012 15:11:10 -0800
changeset 6675 20f3cdea0fd2
parent 6673 78f18a929123
child 6755 af77e1a6faf0
permissions -rw-r--r--
Add SDL_CreateSystemCursor for Windows and Linux.
     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 (mouse->WarpMouse) {
   384         mouse->WarpMouse(window, x, y);
   385     } else {
   386         SDL_SendMouseMotion(window, 0, x, y);
   387     }
   388 }
   389 
   390 int
   391 SDL_SetRelativeMouseMode(SDL_bool enabled)
   392 {
   393     SDL_Mouse *mouse = SDL_GetMouse();
   394 
   395     if (enabled == mouse->relative_mode) {
   396         return 0;
   397     }
   398 
   399     if (!mouse->SetRelativeMouseMode) {
   400         SDL_Unsupported();
   401         return -1;
   402     }
   403 
   404     if (mouse->SetRelativeMouseMode(enabled) < 0) {
   405         return -1;
   406     }
   407 
   408     /* Set the relative mode */
   409     mouse->relative_mode = enabled;
   410 
   411     if (enabled) {
   412         /* Save the expected mouse position */
   413         mouse->original_x = mouse->x;
   414         mouse->original_y = mouse->y;
   415     } else if (mouse->focus) {
   416         /* Restore the expected mouse position */
   417         SDL_WarpMouseInWindow(mouse->focus, mouse->original_x, mouse->original_y);
   418     }
   419 
   420     /* Flush pending mouse motion */
   421     SDL_FlushEvent(SDL_MOUSEMOTION);
   422 
   423     /* Update cursor visibility */
   424     SDL_SetCursor(NULL);
   425 
   426     return 0;
   427 }
   428 
   429 SDL_bool
   430 SDL_GetRelativeMouseMode()
   431 {
   432     SDL_Mouse *mouse = SDL_GetMouse();
   433 
   434     return mouse->relative_mode;
   435 }
   436 
   437 SDL_Cursor *
   438 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   439                  int w, int h, int hot_x, int hot_y)
   440 {
   441     SDL_Surface *surface;
   442     SDL_Cursor *cursor;
   443     int x, y;
   444     Uint32 *pixel;
   445     Uint8 datab = 0, maskb = 0;
   446     const Uint32 black = 0xFF000000;
   447     const Uint32 white = 0xFFFFFFFF;
   448     const Uint32 transparent = 0x00000000;
   449 
   450     /* Make sure the width is a multiple of 8 */
   451     w = ((w + 7) & ~7);
   452 
   453     /* Create the surface from a bitmap */
   454     surface = SDL_CreateRGBSurface(0, w, h, 32,
   455                                    0x00FF0000,
   456                                    0x0000FF00,
   457                                    0x000000FF,
   458                                    0xFF000000);
   459     if (!surface) {
   460         return NULL;
   461     }
   462     for (y = 0; y < h; ++y) {
   463         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   464         for (x = 0; x < w; ++x) {
   465             if ((x % 8) == 0) {
   466                 datab = *data++;
   467                 maskb = *mask++;
   468             }
   469             if (maskb & 0x80) {
   470                 *pixel++ = (datab & 0x80) ? black : white;
   471             } else {
   472                 *pixel++ = (datab & 0x80) ? black : transparent;
   473             }
   474             datab <<= 1;
   475             maskb <<= 1;
   476         }
   477     }
   478 
   479     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
   480 
   481     SDL_FreeSurface(surface);
   482 
   483     return cursor;
   484 }
   485 
   486 SDL_Cursor *
   487 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
   488 {
   489     SDL_Mouse *mouse = SDL_GetMouse();
   490     SDL_Surface *temp = NULL;
   491     SDL_Cursor *cursor;
   492 
   493     if (!surface) {
   494         SDL_SetError("Passed NULL cursor surface");
   495         return NULL;
   496     }
   497 
   498     if (!mouse->CreateCursor) {
   499         SDL_SetError("Cursors are not currently supported");
   500         return NULL;
   501     }
   502 
   503     /* Sanity check the hot spot */
   504     if ((hot_x < 0) || (hot_y < 0) ||
   505         (hot_x >= surface->w) || (hot_y >= surface->h)) {
   506         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   507         return NULL;
   508     }
   509 
   510     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
   511         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
   512         if (!temp) {
   513             return NULL;
   514         }
   515         surface = temp;
   516     }
   517 
   518     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   519     if (cursor) {
   520         cursor->next = mouse->cursors;
   521         mouse->cursors = cursor;
   522     }
   523 
   524     if (temp) {
   525         SDL_FreeSurface(temp);
   526     }
   527 
   528     return cursor;
   529 }
   530 
   531 SDL_Cursor *
   532 SDL_CreateSystemCursor(SDL_SystemCursor id)
   533 {
   534     SDL_Mouse *mouse = SDL_GetMouse();
   535     SDL_Cursor *cursor;
   536 
   537     if (!mouse->CreateSystemCursor) {
   538         SDL_SetError("CreateSystemCursor is not currently supported");
   539         return NULL;
   540     }
   541 
   542 	cursor = mouse->CreateSystemCursor(id);
   543     if (cursor) {
   544         cursor->next = mouse->cursors;
   545         mouse->cursors = cursor;
   546     }
   547 
   548 	return cursor;
   549 }
   550 
   551 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   552    if this is desired for any reason.  This is used when setting
   553    the video mode and when the SDL window gains the mouse focus.
   554  */
   555 void
   556 SDL_SetCursor(SDL_Cursor * cursor)
   557 {
   558     SDL_Mouse *mouse = SDL_GetMouse();
   559 
   560     /* Set the new cursor */
   561     if (cursor) {
   562         /* Make sure the cursor is still valid for this mouse */
   563         if (cursor != mouse->def_cursor) {
   564             SDL_Cursor *found;
   565             for (found = mouse->cursors; found; found = found->next) {
   566                 if (found == cursor) {
   567                     break;
   568                 }
   569             }
   570             if (!found) {
   571                 SDL_SetError("Cursor not associated with the current mouse");
   572                 return;
   573             }
   574         }
   575         mouse->cur_cursor = cursor;
   576     } else {
   577         if (mouse->focus) {
   578             cursor = mouse->cur_cursor;
   579         } else {
   580             cursor = mouse->def_cursor;
   581         }
   582     }
   583 
   584     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   585         if (mouse->ShowCursor) {
   586             mouse->ShowCursor(cursor);
   587         }
   588     } else {
   589         if (mouse->ShowCursor) {
   590             mouse->ShowCursor(NULL);
   591         }
   592     }
   593 }
   594 
   595 SDL_Cursor *
   596 SDL_GetCursor(void)
   597 {
   598     SDL_Mouse *mouse = SDL_GetMouse();
   599 
   600     if (!mouse) {
   601         return NULL;
   602     }
   603     return mouse->cur_cursor;
   604 }
   605 
   606 void
   607 SDL_FreeCursor(SDL_Cursor * cursor)
   608 {
   609     SDL_Mouse *mouse = SDL_GetMouse();
   610     SDL_Cursor *curr, *prev;
   611 
   612     if (!cursor) {
   613         return;
   614     }
   615 
   616     if (cursor == mouse->def_cursor) {
   617         return;
   618     }
   619     if (cursor == mouse->cur_cursor) {
   620         SDL_SetCursor(mouse->def_cursor);
   621     }
   622 
   623     for (prev = NULL, curr = mouse->cursors; curr;
   624          prev = curr, curr = curr->next) {
   625         if (curr == cursor) {
   626             if (prev) {
   627                 prev->next = curr->next;
   628             } else {
   629                 mouse->cursors = curr->next;
   630             }
   631 
   632             if (mouse->FreeCursor) {
   633                 mouse->FreeCursor(curr);
   634             }
   635             return;
   636         }
   637     }
   638 }
   639 
   640 int
   641 SDL_ShowCursor(int toggle)
   642 {
   643     SDL_Mouse *mouse = SDL_GetMouse();
   644     SDL_bool shown;
   645 
   646     if (!mouse) {
   647         return 0;
   648     }
   649 
   650     shown = mouse->cursor_shown;
   651     if (toggle >= 0) {
   652         if (toggle) {
   653             mouse->cursor_shown = SDL_TRUE;
   654         } else {
   655             mouse->cursor_shown = SDL_FALSE;
   656         }
   657         if (mouse->cursor_shown != shown) {
   658             SDL_SetCursor(NULL);
   659         }
   660     }
   661     return shown;
   662 }
   663 
   664 /* vi: set ts=4 sw=4 expandtab: */