src/events/SDL_mouse.c
author Sam Lantinga
Sun, 27 Sep 2009 05:18:43 +0000
branchSDL-1.2
changeset 4249 429c8dd3175d
parent 4159 a1b03ba2fcd0
child 6137 4720145f848b
permissions -rw-r--r--
Fixed bug #713

Don't clamp the mouse coordinates to the video surface size, instead clamp them to the last known window size.

This allows users to get the correct mouse coordinates even if they don't call SDL_SetVideoMode() in response to an SDL_VIDEORESIZE event (used as a hack to retain the OpenGL context on Windows and Linux after a window resize)
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 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 "../video/SDL_cursor_c.h"
    29 #include "../video/SDL_sysvideo.h"
    30 
    31 
    32 /* These are static for our mouse handling code */
    33 static Sint16 SDL_MouseX = 0;
    34 static Sint16 SDL_MouseY = 0;
    35 static Sint16 SDL_DeltaX = 0;
    36 static Sint16 SDL_DeltaY = 0;
    37 static Sint16 SDL_MouseMaxX = 0;
    38 static Sint16 SDL_MouseMaxY = 0;
    39 static Uint8  SDL_ButtonState = 0;
    40 
    41 
    42 /* Public functions */
    43 int SDL_MouseInit(void)
    44 {
    45 	/* The mouse is at (0,0) */
    46 	SDL_MouseX = 0;
    47 	SDL_MouseY = 0;
    48 	SDL_DeltaX = 0;
    49 	SDL_DeltaY = 0;
    50 	SDL_MouseMaxX = 0;
    51 	SDL_MouseMaxY = 0;
    52 	SDL_ButtonState = 0;
    53 
    54 	/* That's it! */
    55 	return(0);
    56 }
    57 void SDL_MouseQuit(void)
    58 {
    59 }
    60 
    61 /* We lost the mouse, so post button up messages for all pressed buttons */
    62 void SDL_ResetMouse(void)
    63 {
    64 	Uint8 i;
    65 	for ( i = 0; i < sizeof(SDL_ButtonState)*8; ++i ) {
    66 		if ( SDL_ButtonState & SDL_BUTTON(i) ) {
    67 			SDL_PrivateMouseButton(SDL_RELEASED, i, 0, 0);
    68 		}
    69 	}
    70 }
    71 
    72 Uint8 SDL_GetMouseState (int *x, int *y)
    73 {
    74 	if ( x ) {
    75 		*x = SDL_MouseX;
    76 	}
    77 	if ( y ) {
    78 		*y = SDL_MouseY;
    79 	}
    80 	return(SDL_ButtonState);
    81 }
    82 
    83 Uint8 SDL_GetRelativeMouseState (int *x, int *y)
    84 {
    85 	if ( x )
    86 		*x = SDL_DeltaX;
    87 	if ( y )
    88 		*y = SDL_DeltaY;
    89 	SDL_DeltaX = 0;
    90 	SDL_DeltaY = 0;
    91 	return(SDL_ButtonState);
    92 }
    93 
    94 static void ClipOffset(Sint16 *x, Sint16 *y)
    95 {
    96 	/* This clips absolute mouse coordinates when the apparent
    97 	   display surface is smaller than the real display surface.
    98 	 */
    99 	if ( SDL_VideoSurface && SDL_VideoSurface->offset ) {
   100 		*y -= SDL_VideoSurface->offset/SDL_VideoSurface->pitch;
   101 		*x -= (SDL_VideoSurface->offset%SDL_VideoSurface->pitch)/
   102 				SDL_VideoSurface->format->BytesPerPixel;
   103 	}
   104 }
   105 
   106 void SDL_SetMouseRange(int maxX, int maxY)
   107 {
   108 	SDL_MouseMaxX = (Sint16)maxX;
   109 	SDL_MouseMaxY = (Sint16)maxY;
   110 }
   111 
   112 /* These are global for SDL_eventloop.c */
   113 int SDL_PrivateMouseMotion(Uint8 buttonstate, int relative, Sint16 x, Sint16 y)
   114 {
   115 	int posted;
   116 	Uint16 X, Y;
   117 	Sint16 Xrel;
   118 	Sint16 Yrel;
   119 
   120 	/* Default buttonstate is the current one */
   121 	if ( ! buttonstate ) {
   122 		buttonstate = SDL_ButtonState;
   123 	}
   124 
   125 	Xrel = x;
   126 	Yrel = y;
   127 	if ( relative ) {
   128 		/* Push the cursor around */
   129 		x = (SDL_MouseX+x);
   130 		y = (SDL_MouseY+y);
   131 	} else {
   132 		/* Do we need to clip {x,y} ? */
   133 		ClipOffset(&x, &y);
   134 	}
   135 
   136 	/* Mouse coordinates range from 0 - width-1 and 0 - height-1 */
   137 	if ( x < 0 )
   138 		X = 0;
   139 	else
   140 	if ( x >= SDL_MouseMaxX )
   141 		X = SDL_MouseMaxX-1;
   142 	else
   143 		X = (Uint16)x;
   144 
   145 	if ( y < 0 )
   146 		Y = 0;
   147 	else
   148 	if ( y >= SDL_MouseMaxY )
   149 		Y = SDL_MouseMaxY-1;
   150 	else
   151 		Y = (Uint16)y;
   152 
   153 	/* If not relative mode, generate relative motion from clamped X/Y.
   154 	   This prevents lots of extraneous large delta relative motion when
   155 	   the screen is windowed mode and the mouse is outside the window.
   156 	*/
   157 	if ( ! relative ) {
   158 		Xrel = X-SDL_MouseX;
   159 		Yrel = Y-SDL_MouseY;
   160 	}
   161 
   162 	/* Drop events that don't change state */
   163 	if ( ! Xrel && ! Yrel ) {
   164 #if 0
   165 printf("Mouse event didn't change state - dropped!\n");
   166 #endif
   167 		return(0);
   168 	}
   169 
   170 	/* Update internal mouse state */
   171 	SDL_ButtonState = buttonstate;
   172 	SDL_MouseX = X;
   173 	SDL_MouseY = Y;
   174 	SDL_DeltaX += Xrel;
   175 	SDL_DeltaY += Yrel;
   176         SDL_MoveCursor(SDL_MouseX, SDL_MouseY);
   177 
   178 	/* Post the event, if desired */
   179 	posted = 0;
   180 	if ( SDL_ProcessEvents[SDL_MOUSEMOTION] == SDL_ENABLE ) {
   181 		SDL_Event event;
   182 		SDL_memset(&event, 0, sizeof(event));
   183 		event.type = SDL_MOUSEMOTION;
   184 		event.motion.state = buttonstate;
   185 		event.motion.x = X;
   186 		event.motion.y = Y;
   187 		event.motion.xrel = Xrel;
   188 		event.motion.yrel = Yrel;
   189 		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
   190 			posted = 1;
   191 			SDL_PushEvent(&event);
   192 		}
   193 	}
   194 	return(posted);
   195 }
   196 
   197 int SDL_PrivateMouseButton(Uint8 state, Uint8 button, Sint16 x, Sint16 y)
   198 {
   199 	SDL_Event event;
   200 	int posted;
   201 	int move_mouse;
   202 	Uint8 buttonstate;
   203 
   204 	SDL_memset(&event, 0, sizeof(event));
   205 
   206 	/* Check parameters */
   207 	if ( x || y ) {
   208 		ClipOffset(&x, &y);
   209 		move_mouse = 1;
   210 		/* Mouse coordinates range from 0 - width-1 and 0 - height-1 */
   211 		if ( x < 0 )
   212 			x = 0;
   213 		else
   214 		if ( x >= SDL_MouseMaxX )
   215 			x = SDL_MouseMaxX-1;
   216 
   217 		if ( y < 0 )
   218 			y = 0;
   219 		else
   220 		if ( y >= SDL_MouseMaxY )
   221 			y = SDL_MouseMaxY-1;
   222 	} else {
   223 		move_mouse = 0;
   224 	}
   225 	if ( ! x )
   226 		x = SDL_MouseX;
   227 	if ( ! y )
   228 		y = SDL_MouseY;
   229 
   230 	/* Figure out which event to perform */
   231 	buttonstate = SDL_ButtonState;
   232 	switch ( state ) {
   233 		case SDL_PRESSED:
   234 			event.type = SDL_MOUSEBUTTONDOWN;
   235 			buttonstate |= SDL_BUTTON(button);
   236 			break;
   237 		case SDL_RELEASED:
   238 			event.type = SDL_MOUSEBUTTONUP;
   239 			buttonstate &= ~SDL_BUTTON(button);
   240 			break;
   241 		default:
   242 			/* Invalid state -- bail */
   243 			return(0);
   244 	}
   245 
   246 	/* Update internal mouse state */
   247 	SDL_ButtonState = buttonstate;
   248 	if ( move_mouse ) {
   249 		SDL_MouseX = x;
   250 		SDL_MouseY = y;
   251 		SDL_MoveCursor(SDL_MouseX, SDL_MouseY);
   252 	}
   253 
   254 	/* Post the event, if desired */
   255 	posted = 0;
   256 	if ( SDL_ProcessEvents[event.type] == SDL_ENABLE ) {
   257 		event.button.state = state;
   258 		event.button.button = button;
   259 		event.button.x = x;
   260 		event.button.y = y;
   261 		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
   262 			posted = 1;
   263 			SDL_PushEvent(&event);
   264 		}
   265 	}
   266 	return(posted);
   267 }
   268