1 /**
2  * SDL mouse+keyboard test
3 
4  * Copyright (C) 2007  Sylvain Beucler
5 
6  * This file is part of GNU FreeDink
7 
8  * GNU FreeDink is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 3 of the
11  * License, or (at your option) any later version.
12 
13  * GNU FreeDink is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17 
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see
20  * <http://www.gnu.org/licenses/>.
21  */
22 
23 /* Moves the cursor using either the mouse or the keyboard - the
24    program tries to keep the mouse under control (otherwise the mouse
25    may move out of the window and lose focus), and no jumps when
26    switching from mouse to keyboard or vice-versa. */
27 
28 #include <stdio.h>
29 #include "SDL.h"
30 
31 #define IMIN(a,b) ((a < b) ? a : b)
32 #define IMAX(a,b) ((a > b) ? a : b)
33 
34 static int ignore_mouse_event = 0;
35 
mouse_event_filter(const SDL_Event * event)36 int mouse_event_filter(const SDL_Event *event)
37 {
38   if (ignore_mouse_event && event->type == SDL_MOUSEMOTION)
39     {
40       printf("Filtered mouse event\n");
41       return 0;
42     }
43   return 1;
44 }
45 
main(int argc,char * argv[])46 int main(int argc, char *argv[])
47 {
48   SDL_Surface *screen, *pic;
49   int quit = 0;
50   double px = 320, py = 240;
51   int mouse_dx = 0, mouse_dy = 0;
52   int keyboard_dx = 0, keyboard_dy = 0;
53   SDL_Rect dst = {320, 240};
54   Uint32 last_update = 0;
55   int mouse_speed = 200; /* pixels per second */
56 
57   if (SDL_Init(SDL_INIT_VIDEO) < 0)
58     {
59       fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
60       exit(1);
61     }
62 
63   putenv("SDL_VIDEO_CENTERED=1");
64   screen = SDL_SetVideoMode(640, 480, 0, SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF | SDL_ANYFORMAT);
65   if (screen == NULL)
66     {
67       fprintf(stderr, "Couldn't set video mode: %s\n", SDL_GetError());
68       exit(1);
69     }
70   SDL_WM_SetCaption("MouseTest", NULL);
71   SDL_ShowCursor(SDL_DISABLE);
72 
73   pic = SDL_LoadBMP("S08.bmp");
74   if (pic == NULL)
75     {
76       fprintf(stderr, "Failed to load image: %s\n", SDL_GetError());
77       exit(1);
78     }
79   SDL_SetColorKey(pic, SDL_SRCCOLORKEY, SDL_MapRGB(pic->format, 0, 0, 0));
80 
81   /* Fill screen in white */
82   SDL_FillRect(screen, NULL,
83 	       SDL_MapRGB(screen->format, 0, 0, 255));
84   SDL_Flip(screen);
85 
86 
87   /* SDL_SetEventFilter(mouse_event_filter); */
88 
89   /* Synchronize physical and game mouse */
90   {
91     int tmp_dx, tmp_dy;
92 
93     ignore_mouse_event = 1;
94     SDL_WarpMouse(320, 240);
95     SDL_PumpEvents();
96     ignore_mouse_event = 0;
97     SDL_GetRelativeMouseState(&tmp_dx, &tmp_dy);
98   }
99 
100   /* SDL_MouseMotionEvent: If the cursor is hidden (SDL_ShowCursor(0))
101      and the input is grabbed (SDL_WM_GrabInput(SDL_GRAB_ON)), then
102      the mouse will give relative motion events even when the cursor
103      reaches the edge of the screen. This is currently only
104      implemented on Windows and Linux/Unix-alikes. */
105   /* So it's not portable and it blocks Alt+Tab, so let's try
106      something else */
107   /* SDL_WM_GrabInput(SDL_GRAB_ON); */
108 
109   last_update = SDL_GetTicks();
110 
111   /* Main game loop */
112   while(!quit) {
113     SDL_Event event;
114     while (SDL_PollEvent(&event))
115       {
116 	switch(event.type)
117 	  {
118 	  case SDL_KEYDOWN:
119 	    switch (event.key.keysym.sym)
120 	      {
121 	      case 'q':
122 	      case SDLK_ESCAPE:
123 		quit = 1;
124 		break;
125 	      case 'f':
126 		SDL_WM_ToggleFullScreen(screen);
127 		break;
128 	      case SDLK_LEFT:
129 		keyboard_dx = -1;
130 		break;
131 	      case SDLK_RIGHT:
132 		keyboard_dx = 1;
133 		break;
134 	      case SDLK_UP:
135 		keyboard_dy = -1;
136 		break;
137 	      case SDLK_DOWN:
138 		keyboard_dy = 1;
139 		break;
140 	      default:
141 		break;
142 	      }
143 	    break;
144 
145 	  case SDL_KEYUP:
146 	    switch (event.key.keysym.sym)
147 	      {
148 	      case SDLK_LEFT:
149 	      case SDLK_RIGHT:
150 		keyboard_dx = 0;
151 		break;
152 	      case SDLK_UP:
153 	      case SDLK_DOWN:
154 		keyboard_dy = 0;
155 		break;
156 	      default:
157 		break;
158 	      }
159 	    break;
160 
161 	  case SDL_ACTIVEEVENT:
162 	    {
163 	      SDL_ActiveEvent *active_event = (SDL_ActiveEvent*) &event;
164 	      if (active_event->state & SDL_APPMOUSEFOCUS)
165 		{
166 		  /* mouse_focus = active_event->gain; */
167 		  printf("mouse event: %d\n", active_event->gain);
168 		}
169 	    }
170 	    break;
171 
172 	  case SDL_QUIT:
173 	    quit = 1;
174 
175 	    break;
176 	  }
177       }
178 
179     /* Update position - mouse event*/
180     {
181       /* SDL_GetMouseState(&mouse_x, &mouse_y); */
182       SDL_GetRelativeMouseState(&mouse_dx, &mouse_dy);
183       if (mouse_dx != 0 || mouse_dy != 0)
184 	{
185 	  px += mouse_dx;
186 	  py += mouse_dy;
187 
188 	  /* Clip */
189 	  if (px < 0) px = 0;
190 	  if (py < 0) py = 0;
191 	  if (px > 640 - 1) px = 640 - 1;
192 	  if (py > 480 - 1) py = 480 - 1;
193 
194 	  /* Try to get the mouse (and the focus) within the window,
195 	     not 100% safe but good enough */
196 	  SDL_WarpMouse(320, 240);
197 	  /* Ignore the mouse event generated by SDL_WarpMouse: */
198 	  SDL_PumpEvents();
199 	  SDL_GetRelativeMouseState(NULL, NULL);
200 	}
201     }
202 
203     {
204       SDL_Rect erase, update;
205       int prev_x, prev_y;
206       double dx, dy;
207       int update_p1_x, update_p1_y;
208       int update_p2_x, update_p2_y;
209       Uint32 dt;
210 
211       /* Check elapsed time since last frame */
212       dt = SDL_GetTicks() - last_update;
213       last_update = SDL_GetTicks();
214 
215       /* Erase last position */
216       prev_x = dst.x;
217       prev_y = dst.y;
218       erase.x = prev_x;
219       erase.y = prev_y;
220       erase.w = pic->w;
221       erase.h = pic->h;
222       SDL_FillRect(screen, &erase,
223 		   SDL_MapRGB(screen->format, 0, 0, 255));
224 
225 
226       /* Update position - keyboard event */
227       dx = 1.0 * keyboard_dx * mouse_speed * dt / 1000;
228       dy = 1.0 * keyboard_dy * mouse_speed * dt / 1000;
229       px += dx;
230       py += dy;
231       dst.x = px;
232       dst.y = py;
233 
234       /* Display new position */
235       SDL_BlitSurface(pic, NULL, screen, &dst);
236 
237 
238       /* What to refresh */
239       update_p1_x = IMIN(prev_x, dst.x);
240       update_p1_y = IMIN(prev_y, dst.y);
241       update_p2_x = IMAX(prev_x, dst.x) + pic->w;
242       update_p2_y = IMAX(prev_y, dst.y) + pic->h;
243 
244       /* Clipping */
245       update_p1_x = IMIN(IMAX(update_p1_x, 0), screen->w);
246       update_p1_y = IMIN(IMAX(update_p1_y, 0), screen->h);
247       update_p2_x = IMIN(IMAX(update_p2_x, 0), screen->w);
248       update_p2_y = IMIN(IMAX(update_p2_y, 0), screen->h);
249 
250       /* Refresh */
251       update.x = update_p1_x;
252       update.y = update_p1_y;
253       update.w = update_p2_x - update_p1_x;
254       update.h = update_p2_y - update_p1_y;
255       SDL_UpdateRects(screen, 1, &update);
256       /* printf("pos %f,%f\n", px, py); */
257     }
258   }
259   SDL_Quit();
260 
261   return 0;
262 }
263