src/events/SDL_mouse.c
author Sam Lantinga
Mon, 21 Feb 2011 10:50:53 -0800
changeset 5371 fc3d3d580777
parent 5370 cb219a294ebf
child 5376 183ec2d4485c
permissions -rw-r--r--
Fixed the responder chain for event handling, the listener fully handles mouse events - even in fullscreen mode.
The only reason we need a custom view is to handle right mouse down.

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