src/events/SDL_mouse.c
author Sam Lantinga
Sun, 27 Feb 2011 21:36:23 -0800
changeset 5405 64fa8526e1ce
parent 5376 183ec2d4485c
child 5406 1517da4ab6b1
permissions -rw-r--r--
Added a cleaner way to set the default cursor.
Added a way to cycle through the default cursor in testcursor.c
     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     /* Flush pending mouse motion */
   312     SDL_FlushEvent(SDL_MOUSEMOTION);
   313 
   314     /* Set the relative mode */
   315     mouse->relative_mode = enabled;
   316 
   317     if (!enabled) {
   318         /* Restore the expected mouse position */
   319         SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
   320     }
   321 
   322     /* Update cursor visibility */
   323     SDL_SetCursor(NULL);
   324 
   325     return 0;
   326 }
   327 
   328 SDL_bool
   329 SDL_GetRelativeMouseMode()
   330 {
   331     SDL_Mouse *mouse = SDL_GetMouse();
   332 
   333     return mouse->relative_mode;
   334 }
   335 
   336 SDL_Cursor *
   337 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   338                  int w, int h, int hot_x, int hot_y)
   339 {
   340     SDL_Mouse *mouse = SDL_GetMouse();
   341     SDL_Surface *surface;
   342     SDL_Cursor *cursor;
   343     int x, y;
   344     Uint32 *pixel;
   345     Uint8 datab = 0, maskb = 0;
   346     const Uint32 black = 0xFF000000;
   347     const Uint32 white = 0xFFFFFFFF;
   348     const Uint32 transparent = 0x00000000;
   349 
   350     if (!mouse->CreateCursor) {
   351         SDL_SetError("Cursors are not currently supported");
   352         return NULL;
   353     }
   354 
   355     /* Sanity check the hot spot */
   356     if ((hot_x < 0) || (hot_y < 0) || (hot_x >= w) || (hot_y >= h)) {
   357         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   358         return NULL;
   359     }
   360 
   361     /* Make sure the width is a multiple of 8 */
   362     w = ((w + 7) & ~7);
   363 
   364     /* Create the surface from a bitmap */
   365     surface =
   366         SDL_CreateRGBSurface(0, w, h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF,
   367                              0xFF000000);
   368     if (!surface) {
   369         return NULL;
   370     }
   371     for (y = 0; y < h; ++y) {
   372         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   373         for (x = 0; x < w; ++x) {
   374             if ((x % 8) == 0) {
   375                 datab = *data++;
   376                 maskb = *mask++;
   377             }
   378             if (maskb & 0x80) {
   379                 *pixel++ = (datab & 0x80) ? black : white;
   380             } else {
   381                 *pixel++ = (datab & 0x80) ? black : transparent;
   382             }
   383             datab <<= 1;
   384             maskb <<= 1;
   385         }
   386     }
   387 
   388     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   389     if (cursor) {
   390         cursor->next = mouse->cursors;
   391         mouse->cursors = cursor;
   392     }
   393 
   394     SDL_FreeSurface(surface);
   395 
   396     return cursor;
   397 }
   398 
   399 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   400    if this is desired for any reason.  This is used when setting
   401    the video mode and when the SDL window gains the mouse focus.
   402  */
   403 void
   404 SDL_SetCursor(SDL_Cursor * cursor)
   405 {
   406     SDL_Mouse *mouse = SDL_GetMouse();
   407 
   408     /* Set the new cursor */
   409     if (cursor) {
   410         /* Make sure the cursor is still valid for this mouse */
   411         if (cursor != mouse->def_cursor) {
   412             SDL_Cursor *found;
   413             for (found = mouse->cursors; found; found = found->next) {
   414                 if (found == cursor) {
   415                     break;
   416                 }
   417             }
   418             if (!found) {
   419                 SDL_SetError("Cursor not associated with the current mouse");
   420                 return;
   421             }
   422         }
   423         mouse->cur_cursor = cursor;
   424     } else {
   425         cursor = mouse->cur_cursor;
   426     }
   427 
   428     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   429         if (mouse->ShowCursor) {
   430             mouse->ShowCursor(cursor);
   431         }
   432     } else {
   433         if (mouse->ShowCursor) {
   434             mouse->ShowCursor(NULL);
   435         }
   436     }
   437 }
   438 
   439 SDL_Cursor *
   440 SDL_GetCursor(void)
   441 {
   442     SDL_Mouse *mouse = SDL_GetMouse();
   443 
   444     if (!mouse) {
   445         return NULL;
   446     }
   447     return mouse->cur_cursor;
   448 }
   449 
   450 void
   451 SDL_FreeCursor(SDL_Cursor * cursor)
   452 {
   453     SDL_Mouse *mouse = SDL_GetMouse();
   454     SDL_Cursor *curr, *prev;
   455 
   456     if (!cursor) {
   457         return;
   458     }
   459 
   460     if (cursor == mouse->def_cursor) {
   461         return;
   462     }
   463     if (cursor == mouse->cur_cursor) {
   464         SDL_SetCursor(mouse->def_cursor);
   465     }
   466 
   467     for (prev = NULL, curr = mouse->cursors; curr;
   468          prev = curr, curr = curr->next) {
   469         if (curr == cursor) {
   470             if (prev) {
   471                 prev->next = curr->next;
   472             } else {
   473                 mouse->cursors = curr->next;
   474             }
   475 
   476             if (mouse->FreeCursor) {
   477                 mouse->FreeCursor(curr);
   478             }
   479             return;
   480         }
   481     }
   482 }
   483 
   484 int
   485 SDL_ShowCursor(int toggle)
   486 {
   487     SDL_Mouse *mouse = SDL_GetMouse();
   488     SDL_bool shown;
   489 
   490     if (!mouse) {
   491         return 0;
   492     }
   493 
   494     shown = mouse->cursor_shown;
   495     if (toggle >= 0) {
   496         if (toggle) {
   497             mouse->cursor_shown = SDL_TRUE;
   498         } else {
   499             mouse->cursor_shown = SDL_FALSE;
   500         }
   501         if (mouse->cursor_shown != shown) {
   502             SDL_SetCursor(NULL);
   503         }
   504     }
   505     return shown;
   506 }
   507 
   508 /* vi: set ts=4 sw=4 expandtab: */