src/events/SDL_mouse.c
author Sam Lantinga
Sun, 27 Feb 2011 22:06:46 -0800
changeset 5406 1517da4ab6b1
parent 5405 64fa8526e1ce
child 5473 5665ccbd6792
permissions -rw-r--r--
Implemented mouse relative mode on Mac OS X.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2011 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* General mouse handling code for SDL */
    25 
    26 #include "SDL_events.h"
    27 #include "SDL_events_c.h"
    28 #include "default_cursor.h"
    29 #include "../video/SDL_sysvideo.h"
    30 
    31 
    32 /* The mouse state */
    33 static SDL_Mouse SDL_mouse;
    34 
    35 
    36 /* Public functions */
    37 int
    38 SDL_MouseInit(void)
    39 {
    40     SDL_Mouse *mouse = SDL_GetMouse();
    41 
    42     mouse->cursor_shown = SDL_TRUE;
    43 
    44     return (0);
    45 }
    46 
    47 void
    48 SDL_SetDefaultCursor(SDL_Cursor * cursor)
    49 {
    50     SDL_Mouse *mouse = SDL_GetMouse();
    51 
    52     mouse->def_cursor = cursor;
    53     if (!mouse->cur_cursor) {
    54         SDL_SetCursor(cursor);
    55     }
    56 }
    57 
    58 SDL_Mouse *
    59 SDL_GetMouse(void)
    60 {
    61     return &SDL_mouse;
    62 }
    63 
    64 SDL_Window *
    65 SDL_GetMouseFocus(void)
    66 {
    67     SDL_Mouse *mouse = SDL_GetMouse();
    68 
    69     return mouse->focus;
    70 }
    71 
    72 void
    73 SDL_SetMouseFocus(SDL_Window * window)
    74 {
    75     SDL_Mouse *mouse = SDL_GetMouse();
    76 
    77     if (mouse->focus == window) {
    78         return;
    79     }
    80 
    81     /* See if the current window has lost focus */
    82     if (mouse->focus) {
    83         SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_LEAVE, 0, 0);
    84     }
    85 
    86     mouse->focus = window;
    87 
    88     if (mouse->focus) {
    89         SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_ENTER, 0, 0);
    90     }
    91 }
    92 
    93 int
    94 SDL_SendMouseMotion(SDL_Window * window, int relative, int x, int y)
    95 {
    96     SDL_Mouse *mouse = SDL_GetMouse();
    97     int posted;
    98     int xrel;
    99     int yrel;
   100     int x_max = 0, y_max = 0;
   101 
   102     if (window) {
   103         SDL_SetMouseFocus(window);
   104     }
   105 
   106     /* the relative motion is calculated regarding the system cursor last position */
   107     if (relative) {
   108         xrel = x;
   109         yrel = y;
   110         x = (mouse->last_x + x);
   111         y = (mouse->last_y + y);
   112     } else {
   113         xrel = x - mouse->last_x;
   114         yrel = y - mouse->last_y;
   115     }
   116 
   117     /* Drop events that don't change state */
   118     if (!xrel && !yrel) {
   119 #if 0
   120         printf("Mouse event didn't change state - dropped!\n");
   121 #endif
   122         return 0;
   123     }
   124 
   125     /* Update internal mouse coordinates */
   126     if (mouse->relative_mode == SDL_FALSE) {
   127         mouse->x = x;
   128         mouse->y = y;
   129     } else {
   130         mouse->x += xrel;
   131         mouse->y += yrel;
   132     }
   133 
   134     SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
   135     --x_max;
   136     --y_max;
   137 
   138     /* make sure that the pointers find themselves inside the windows */
   139     /* only check if mouse->xmax is set ! */
   140     if (mouse->x > x_max) {
   141         mouse->x = x_max;
   142     }
   143     if (mouse->x < 0) {
   144         mouse->x = 0;
   145     }
   146 
   147     if (mouse->y > y_max) {
   148         mouse->y = y_max;
   149     }
   150     if (mouse->y < 0) {
   151         mouse->y = 0;
   152     }
   153 
   154     mouse->xdelta += xrel;
   155     mouse->ydelta += yrel;
   156 
   157 #if 0 /* FIXME */
   158     /* Move the mouse cursor, if needed */
   159     if (mouse->cursor_shown && !mouse->relative_mode &&
   160         mouse->MoveCursor && mouse->cur_cursor) {
   161         mouse->MoveCursor(mouse->cur_cursor);
   162     }
   163 #endif
   164 
   165     /* Post the event, if desired */
   166     posted = 0;
   167     if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) {
   168         SDL_Event event;
   169         event.motion.type = SDL_MOUSEMOTION;
   170         event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
   171         event.motion.state = mouse->buttonstate;
   172         event.motion.x = mouse->x;
   173         event.motion.y = mouse->y;
   174         event.motion.xrel = xrel;
   175         event.motion.yrel = yrel;
   176         posted = (SDL_PushEvent(&event) > 0);
   177     }
   178     mouse->last_x = mouse->x;
   179     mouse->last_y = mouse->y;
   180     return posted;
   181 }
   182 
   183 int
   184 SDL_SendMouseButton(SDL_Window * window, Uint8 state, Uint8 button)
   185 {
   186     SDL_Mouse *mouse = SDL_GetMouse();
   187     int posted;
   188     Uint32 type;
   189 
   190     if (window) {
   191         SDL_SetMouseFocus(window);
   192     }
   193 
   194     /* Figure out which event to perform */
   195     switch (state) {
   196     case SDL_PRESSED:
   197         if (mouse->buttonstate & SDL_BUTTON(button)) {
   198             /* Ignore this event, no state change */
   199             return 0;
   200         }
   201         type = SDL_MOUSEBUTTONDOWN;
   202         mouse->buttonstate |= SDL_BUTTON(button);
   203         break;
   204     case SDL_RELEASED:
   205         if (!(mouse->buttonstate & SDL_BUTTON(button))) {
   206             /* Ignore this event, no state change */
   207             return 0;
   208         }
   209         type = SDL_MOUSEBUTTONUP;
   210         mouse->buttonstate &= ~SDL_BUTTON(button);
   211         break;
   212     default:
   213         /* Invalid state -- bail */
   214         return 0;
   215     }
   216 
   217     /* Post the event, if desired */
   218     posted = 0;
   219     if (SDL_GetEventState(type) == SDL_ENABLE) {
   220         SDL_Event event;
   221         event.type = type;
   222         event.button.state = state;
   223         event.button.button = button;
   224         event.button.x = mouse->x;
   225         event.button.y = mouse->y;
   226         event.button.windowID = mouse->focus ? mouse->focus->id : 0;
   227         posted = (SDL_PushEvent(&event) > 0);
   228     }
   229     return posted;
   230 }
   231 
   232 int
   233 SDL_SendMouseWheel(SDL_Window * window, int x, int y)
   234 {
   235     SDL_Mouse *mouse = SDL_GetMouse();
   236     int posted;
   237 
   238     if (window) {
   239         SDL_SetMouseFocus(window);
   240     }
   241 
   242     if (!x && !y) {
   243         return 0;
   244     }
   245 
   246     /* Post the event, if desired */
   247     posted = 0;
   248     if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) {
   249         SDL_Event event;
   250         event.type = SDL_MOUSEWHEEL;
   251         event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
   252         event.wheel.x = x;
   253         event.wheel.y = y;
   254         posted = (SDL_PushEvent(&event) > 0);
   255     }
   256     return posted;
   257 }
   258 
   259 void
   260 SDL_MouseQuit(void)
   261 {
   262 }
   263 
   264 Uint8
   265 SDL_GetMouseState(int *x, int *y)
   266 {
   267     SDL_Mouse *mouse = SDL_GetMouse();
   268 
   269     if (x) {
   270         *x = mouse->x;
   271     }
   272     if (y) {
   273         *y = mouse->y;
   274     }
   275     return mouse->buttonstate;
   276 }
   277 
   278 Uint8
   279 SDL_GetRelativeMouseState(int *x, int *y)
   280 {
   281     SDL_Mouse *mouse = SDL_GetMouse();
   282 
   283     if (x) {
   284         *x = mouse->xdelta;
   285     }
   286     if (y) {
   287         *y = mouse->ydelta;
   288     }
   289     mouse->xdelta = 0;
   290     mouse->ydelta = 0;
   291     return mouse->buttonstate;
   292 }
   293 
   294 void
   295 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   296 {
   297     SDL_Mouse *mouse = SDL_GetMouse();
   298 
   299     if (mouse->WarpMouse) {
   300         mouse->WarpMouse(window, x, y);
   301     } else {
   302         SDL_SendMouseMotion(window, 0, x, y);
   303     }
   304 }
   305 
   306 int
   307 SDL_SetRelativeMouseMode(SDL_bool enabled)
   308 {
   309     SDL_Mouse *mouse = SDL_GetMouse();
   310 
   311     if (enabled == mouse->relative_mode) {
   312         return 0;
   313     }
   314 
   315     if (!mouse->SetRelativeMouseMode) {
   316         SDL_Unsupported();
   317         return -1;
   318     }
   319 
   320     if (mouse->SetRelativeMouseMode(enabled) < 0) {
   321         return -1;
   322     }
   323 
   324     /* Set the relative mode */
   325     mouse->relative_mode = enabled;
   326 
   327     if (!enabled) {
   328         /* Restore the expected mouse position */
   329         SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
   330     }
   331 
   332     /* Flush pending mouse motion */
   333     SDL_FlushEvent(SDL_MOUSEMOTION);
   334 
   335     /* Update cursor visibility */
   336     SDL_SetCursor(NULL);
   337 
   338     return 0;
   339 }
   340 
   341 SDL_bool
   342 SDL_GetRelativeMouseMode()
   343 {
   344     SDL_Mouse *mouse = SDL_GetMouse();
   345 
   346     return mouse->relative_mode;
   347 }
   348 
   349 SDL_Cursor *
   350 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   351                  int w, int h, int hot_x, int hot_y)
   352 {
   353     SDL_Mouse *mouse = SDL_GetMouse();
   354     SDL_Surface *surface;
   355     SDL_Cursor *cursor;
   356     int x, y;
   357     Uint32 *pixel;
   358     Uint8 datab = 0, maskb = 0;
   359     const Uint32 black = 0xFF000000;
   360     const Uint32 white = 0xFFFFFFFF;
   361     const Uint32 transparent = 0x00000000;
   362 
   363     if (!mouse->CreateCursor) {
   364         SDL_SetError("Cursors are not currently supported");
   365         return NULL;
   366     }
   367 
   368     /* Sanity check the hot spot */
   369     if ((hot_x < 0) || (hot_y < 0) || (hot_x >= w) || (hot_y >= h)) {
   370         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   371         return NULL;
   372     }
   373 
   374     /* Make sure the width is a multiple of 8 */
   375     w = ((w + 7) & ~7);
   376 
   377     /* Create the surface from a bitmap */
   378     surface =
   379         SDL_CreateRGBSurface(0, w, h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF,
   380                              0xFF000000);
   381     if (!surface) {
   382         return NULL;
   383     }
   384     for (y = 0; y < h; ++y) {
   385         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   386         for (x = 0; x < w; ++x) {
   387             if ((x % 8) == 0) {
   388                 datab = *data++;
   389                 maskb = *mask++;
   390             }
   391             if (maskb & 0x80) {
   392                 *pixel++ = (datab & 0x80) ? black : white;
   393             } else {
   394                 *pixel++ = (datab & 0x80) ? black : transparent;
   395             }
   396             datab <<= 1;
   397             maskb <<= 1;
   398         }
   399     }
   400 
   401     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   402     if (cursor) {
   403         cursor->next = mouse->cursors;
   404         mouse->cursors = cursor;
   405     }
   406 
   407     SDL_FreeSurface(surface);
   408 
   409     return cursor;
   410 }
   411 
   412 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   413    if this is desired for any reason.  This is used when setting
   414    the video mode and when the SDL window gains the mouse focus.
   415  */
   416 void
   417 SDL_SetCursor(SDL_Cursor * cursor)
   418 {
   419     SDL_Mouse *mouse = SDL_GetMouse();
   420 
   421     /* Set the new cursor */
   422     if (cursor) {
   423         /* Make sure the cursor is still valid for this mouse */
   424         if (cursor != mouse->def_cursor) {
   425             SDL_Cursor *found;
   426             for (found = mouse->cursors; found; found = found->next) {
   427                 if (found == cursor) {
   428                     break;
   429                 }
   430             }
   431             if (!found) {
   432                 SDL_SetError("Cursor not associated with the current mouse");
   433                 return;
   434             }
   435         }
   436         mouse->cur_cursor = cursor;
   437     } else {
   438         cursor = mouse->cur_cursor;
   439     }
   440 
   441     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   442         if (mouse->ShowCursor) {
   443             mouse->ShowCursor(cursor);
   444         }
   445     } else {
   446         if (mouse->ShowCursor) {
   447             mouse->ShowCursor(NULL);
   448         }
   449     }
   450 }
   451 
   452 SDL_Cursor *
   453 SDL_GetCursor(void)
   454 {
   455     SDL_Mouse *mouse = SDL_GetMouse();
   456 
   457     if (!mouse) {
   458         return NULL;
   459     }
   460     return mouse->cur_cursor;
   461 }
   462 
   463 void
   464 SDL_FreeCursor(SDL_Cursor * cursor)
   465 {
   466     SDL_Mouse *mouse = SDL_GetMouse();
   467     SDL_Cursor *curr, *prev;
   468 
   469     if (!cursor) {
   470         return;
   471     }
   472 
   473     if (cursor == mouse->def_cursor) {
   474         return;
   475     }
   476     if (cursor == mouse->cur_cursor) {
   477         SDL_SetCursor(mouse->def_cursor);
   478     }
   479 
   480     for (prev = NULL, curr = mouse->cursors; curr;
   481          prev = curr, curr = curr->next) {
   482         if (curr == cursor) {
   483             if (prev) {
   484                 prev->next = curr->next;
   485             } else {
   486                 mouse->cursors = curr->next;
   487             }
   488 
   489             if (mouse->FreeCursor) {
   490                 mouse->FreeCursor(curr);
   491             }
   492             return;
   493         }
   494     }
   495 }
   496 
   497 int
   498 SDL_ShowCursor(int toggle)
   499 {
   500     SDL_Mouse *mouse = SDL_GetMouse();
   501     SDL_bool shown;
   502 
   503     if (!mouse) {
   504         return 0;
   505     }
   506 
   507     shown = mouse->cursor_shown;
   508     if (toggle >= 0) {
   509         if (toggle) {
   510             mouse->cursor_shown = SDL_TRUE;
   511         } else {
   512             mouse->cursor_shown = SDL_FALSE;
   513         }
   514         if (mouse->cursor_shown != shown) {
   515             SDL_SetCursor(NULL);
   516         }
   517     }
   518     return shown;
   519 }
   520 
   521 /* vi: set ts=4 sw=4 expandtab: */