src/events/SDL_mouse.c
author Sam Lantinga
Sun, 09 May 2010 20:47:22 -0700
changeset 4465 3e69e077cb95
parent 4429 faa9fc8e7f67
child 4484 9322f7db8603
permissions -rw-r--r--
Removed multi-mouse / multi-keyboard support in anticipation of a real multi-mouse and multi-touch API.

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