src/events/SDL_mouse.c
author Andreas Schiffler <aschiffler@ferzkopp.net>
Mon, 23 Aug 2010 23:44:28 -0700
changeset 4865 fff50e86c891
parent 4484 9322f7db8603
child 5262 b530ef003506
permissions -rw-r--r--
Update VS2010 project to add new files; update new files so code builds on Win32/Win64
     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(SDL_Window * window, 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     if (window) {
   124         SDL_SetMouseFocus(window);
   125     }
   126 
   127     /* the relative motion is calculated regarding the system cursor last position */
   128     if (relative) {
   129         xrel = x;
   130         yrel = y;
   131         x = (mouse->last_x + x);
   132         y = (mouse->last_y + y);
   133     } else {
   134         xrel = x - mouse->last_x;
   135         yrel = y - mouse->last_y;
   136     }
   137 
   138     /* Drop events that don't change state */
   139     if (!xrel && !yrel) {
   140 #if 0
   141         printf("Mouse event didn't change state - dropped!\n");
   142 #endif
   143         return 0;
   144     }
   145 
   146     /* Update internal mouse coordinates */
   147     if (mouse->relative_mode == SDL_FALSE) {
   148         mouse->x = x;
   149         mouse->y = y;
   150     } else {
   151         mouse->x += xrel;
   152         mouse->y += yrel;
   153     }
   154 
   155     SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
   156 
   157     /* make sure that the pointers find themselves inside the windows */
   158     /* only check if mouse->xmax is set ! */
   159     if (x_max && mouse->x > x_max) {
   160         mouse->x = x_max;
   161     } else if (mouse->x < 0) {
   162         mouse->x = 0;
   163     }
   164 
   165     if (y_max && mouse->y > y_max) {
   166         mouse->y = y_max;
   167     } else if (mouse->y < 0) {
   168         mouse->y = 0;
   169     }
   170 
   171     mouse->xdelta += xrel;
   172     mouse->ydelta += yrel;
   173 
   174 #if 0 /* FIXME */
   175     /* Move the mouse cursor, if needed */
   176     if (mouse->cursor_shown && !mouse->relative_mode &&
   177         mouse->MoveCursor && mouse->cur_cursor) {
   178         mouse->MoveCursor(mouse->cur_cursor);
   179     }
   180 #endif
   181 
   182     /* Post the event, if desired */
   183     posted = 0;
   184     if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) {
   185         SDL_Event event;
   186         event.motion.type = SDL_MOUSEMOTION;
   187         event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
   188         event.motion.state = mouse->buttonstate;
   189         event.motion.x = mouse->x;
   190         event.motion.y = mouse->y;
   191         event.motion.xrel = xrel;
   192         event.motion.yrel = yrel;
   193         posted = (SDL_PushEvent(&event) > 0);
   194     }
   195     mouse->last_x = mouse->x;
   196     mouse->last_y = mouse->y;
   197     return posted;
   198 }
   199 
   200 int
   201 SDL_SendMouseButton(SDL_Window * window, Uint8 state, Uint8 button)
   202 {
   203     SDL_Mouse *mouse = &SDL_mouse;
   204     int posted;
   205     Uint32 type;
   206 
   207     if (window) {
   208         SDL_SetMouseFocus(window);
   209     }
   210 
   211     /* Figure out which event to perform */
   212     switch (state) {
   213     case SDL_PRESSED:
   214         if (mouse->buttonstate & SDL_BUTTON(button)) {
   215             /* Ignore this event, no state change */
   216             return 0;
   217         }
   218         type = SDL_MOUSEBUTTONDOWN;
   219         mouse->buttonstate |= SDL_BUTTON(button);
   220         break;
   221     case SDL_RELEASED:
   222         if (!(mouse->buttonstate & SDL_BUTTON(button))) {
   223             /* Ignore this event, no state change */
   224             return 0;
   225         }
   226         type = SDL_MOUSEBUTTONUP;
   227         mouse->buttonstate &= ~SDL_BUTTON(button);
   228         break;
   229     default:
   230         /* Invalid state -- bail */
   231         return 0;
   232     }
   233 
   234     /* Post the event, if desired */
   235     posted = 0;
   236     if (SDL_GetEventState(type) == SDL_ENABLE) {
   237         SDL_Event event;
   238         event.type = type;
   239         event.button.state = state;
   240         event.button.button = button;
   241         event.button.x = mouse->x;
   242         event.button.y = mouse->y;
   243         event.button.windowID = mouse->focus ? mouse->focus->id : 0;
   244         posted = (SDL_PushEvent(&event) > 0);
   245     }
   246     return posted;
   247 }
   248 
   249 int
   250 SDL_SendMouseWheel(SDL_Window * window, int x, int y)
   251 {
   252     SDL_Mouse *mouse = &SDL_mouse;
   253     int posted;
   254 
   255     if (window) {
   256         SDL_SetMouseFocus(window);
   257     }
   258 
   259     if (!x && !y) {
   260         return 0;
   261     }
   262 
   263     /* Post the event, if desired */
   264     posted = 0;
   265     if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) {
   266         SDL_Event event;
   267         event.type = SDL_MOUSEWHEEL;
   268         event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
   269         event.wheel.x = x;
   270         event.wheel.y = y;
   271         posted = (SDL_PushEvent(&event) > 0);
   272     }
   273     return posted;
   274 }
   275 
   276 void
   277 SDL_MouseQuit(void)
   278 {
   279 }
   280 
   281 Uint8
   282 SDL_GetMouseState(int *x, int *y)
   283 {
   284     SDL_Mouse *mouse = &SDL_mouse;
   285 
   286     if (x) {
   287         *x = mouse->x;
   288     }
   289     if (y) {
   290         *y = mouse->y;
   291     }
   292     return mouse->buttonstate;
   293 }
   294 
   295 Uint8
   296 SDL_GetRelativeMouseState(int *x, int *y)
   297 {
   298     SDL_Mouse *mouse = &SDL_mouse;
   299 
   300     if (x) {
   301         *x = mouse->xdelta;
   302     }
   303     if (y) {
   304         *y = mouse->ydelta;
   305     }
   306     mouse->xdelta = 0;
   307     mouse->ydelta = 0;
   308     return mouse->buttonstate;
   309 }
   310 
   311 void
   312 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   313 {
   314     SDL_Mouse *mouse = &SDL_mouse;
   315 
   316     if (mouse->WarpMouse) {
   317         mouse->WarpMouse(mouse, window, x, y);
   318     } else {
   319         SDL_SendMouseMotion(window, 0, x, y);
   320     }
   321 }
   322 
   323 int
   324 SDL_SetRelativeMouseMode(SDL_bool enabled)
   325 {
   326     SDL_Mouse *mouse = &SDL_mouse;
   327 
   328     /* Flush pending mouse motion */
   329     SDL_FlushEvent(SDL_MOUSEMOTION);
   330 
   331     /* Set the relative mode */
   332     mouse->relative_mode = enabled;
   333 
   334     if (!enabled) {
   335         /* Restore the expected mouse position */
   336         SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
   337     }
   338 
   339     /* Update cursor visibility */
   340     SDL_SetCursor(NULL);
   341 
   342     return 0;
   343 }
   344 
   345 SDL_bool
   346 SDL_GetRelativeMouseMode()
   347 {
   348     SDL_Mouse *mouse = &SDL_mouse;
   349 
   350     return mouse->relative_mode;
   351 }
   352 
   353 SDL_Cursor *
   354 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   355                  int w, int h, int hot_x, int hot_y)
   356 {
   357     SDL_Mouse *mouse = &SDL_mouse;
   358     SDL_Surface *surface;
   359     SDL_Cursor *cursor;
   360     int x, y;
   361     Uint32 *pixel;
   362     Uint8 datab = 0, maskb = 0;
   363     const Uint32 black = 0xFF000000;
   364     const Uint32 white = 0xFFFFFFFF;
   365     const Uint32 transparent = 0x00000000;
   366 
   367     if (!mouse->CreateCursor) {
   368         SDL_SetError("Cursors are not currently supported");
   369         return NULL;
   370     }
   371 
   372     /* Sanity check the hot spot */
   373     if ((hot_x < 0) || (hot_y < 0) || (hot_x >= w) || (hot_y >= h)) {
   374         SDL_SetError("Cursor hot spot doesn't lie within cursor");
   375         return NULL;
   376     }
   377 
   378     /* Make sure the width is a multiple of 8 */
   379     w = ((w + 7) & ~7);
   380 
   381     /* Create the surface from a bitmap */
   382     surface =
   383         SDL_CreateRGBSurface(0, w, h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF,
   384                              0xFF000000);
   385     if (!surface) {
   386         return NULL;
   387     }
   388     for (y = 0; y < h; ++y) {
   389         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
   390         for (x = 0; x < w; ++x) {
   391             if ((x % 8) == 0) {
   392                 datab = *data++;
   393                 maskb = *mask++;
   394             }
   395             if (maskb & 0x80) {
   396                 *pixel++ = (datab & 0x80) ? black : white;
   397             } else {
   398                 *pixel++ = (datab & 0x80) ? black : transparent;
   399             }
   400             datab <<= 1;
   401             maskb <<= 1;
   402         }
   403     }
   404 
   405     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
   406     if (cursor) {
   407         cursor->next = mouse->cursors;
   408         mouse->cursors = cursor;
   409     }
   410 
   411     SDL_FreeSurface(surface);
   412 
   413     return cursor;
   414 }
   415 
   416 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
   417    if this is desired for any reason.  This is used when setting
   418    the video mode and when the SDL window gains the mouse focus.
   419  */
   420 void
   421 SDL_SetCursor(SDL_Cursor * cursor)
   422 {
   423     SDL_Mouse *mouse = &SDL_mouse;
   424 
   425     /* Set the new cursor */
   426     if (cursor) {
   427         /* Make sure the cursor is still valid for this mouse */
   428         SDL_Cursor *found;
   429         for (found = mouse->cursors; found; found = found->next) {
   430             if (found == cursor) {
   431                 break;
   432             }
   433         }
   434         if (!found) {
   435             SDL_SetError("Cursor not associated with the current mouse");
   436             return;
   437         }
   438         mouse->cur_cursor = cursor;
   439     } else {
   440         cursor = mouse->cur_cursor;
   441     }
   442 
   443     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
   444         if (mouse->ShowCursor) {
   445             mouse->ShowCursor(cursor);
   446         }
   447     } else {
   448         if (mouse->ShowCursor) {
   449             mouse->ShowCursor(NULL);
   450         }
   451     }
   452 }
   453 
   454 SDL_Cursor *
   455 SDL_GetCursor(void)
   456 {
   457     SDL_Mouse *mouse = &SDL_mouse;
   458 
   459     if (!mouse) {
   460         return NULL;
   461     }
   462     return mouse->cur_cursor;
   463 }
   464 
   465 void
   466 SDL_FreeCursor(SDL_Cursor * cursor)
   467 {
   468     SDL_Mouse *mouse = &SDL_mouse;
   469     SDL_Cursor *curr, *prev;
   470 
   471     if (!cursor) {
   472         return;
   473     }
   474 
   475     if (cursor == mouse->def_cursor) {
   476         return;
   477     }
   478     if (cursor == mouse->cur_cursor) {
   479         SDL_SetCursor(mouse->def_cursor);
   480     }
   481 
   482     for (prev = NULL, curr = mouse->cursors; curr;
   483          prev = curr, curr = curr->next) {
   484         if (curr == cursor) {
   485             if (prev) {
   486                 prev->next = curr->next;
   487             } else {
   488                 mouse->cursors = curr->next;
   489             }
   490 
   491             if (mouse->FreeCursor) {
   492                 mouse->FreeCursor(curr);
   493             }
   494             return;
   495         }
   496     }
   497 }
   498 
   499 int
   500 SDL_ShowCursor(int toggle)
   501 {
   502     SDL_Mouse *mouse = &SDL_mouse;
   503     SDL_bool shown;
   504 
   505     if (!mouse) {
   506         return 0;
   507     }
   508 
   509     shown = mouse->cursor_shown;
   510     if (toggle >= 0) {
   511         if (toggle) {
   512             mouse->cursor_shown = SDL_TRUE;
   513         } else {
   514             mouse->cursor_shown = SDL_FALSE;
   515         }
   516         if (mouse->cursor_shown != shown) {
   517             SDL_SetCursor(NULL);
   518         }
   519     }
   520     return shown;
   521 }
   522 
   523 /* vi: set ts=4 sw=4 expandtab: */