0
|
1 /*
|
|
2 SDL - Simple DirectMedia Layer
|
|
3 Copyright (C) 1997, 1998, 1999 Sam Lantinga
|
|
4
|
|
5 This library is free software; you can redistribute it and/or
|
|
6 modify it under the terms of the GNU Library General Public
|
|
7 License as published by the Free Software Foundation; either
|
|
8 version 2 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 Library General Public License for more details.
|
|
14
|
|
15 You should have received a copy of the GNU Library General Public
|
|
16 License along with this library; if not, write to the Free
|
|
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
18
|
|
19 Sam Lantinga
|
|
20 slouken@devolution.com
|
|
21 */
|
|
22
|
|
23 #ifdef SAVE_RCSID
|
|
24 static char rcsid =
|
|
25 "@(#) $Id$";
|
|
26 #endif
|
|
27
|
|
28 #include <stdlib.h>
|
|
29 #include <stdio.h>
|
|
30 #include <windows.h>
|
|
31
|
|
32 #include "SDL_getenv.h"
|
|
33 #include "SDL_events.h"
|
|
34 #include "SDL_video.h"
|
|
35 #include "SDL_error.h"
|
|
36 #include "SDL_syswm.h"
|
|
37 #include "SDL_sysevents.h"
|
|
38 #include "SDL_events_c.h"
|
|
39 #include "SDL_sysvideo.h"
|
|
40 #include "SDL_lowvideo.h"
|
|
41 #include "SDL_syswm_c.h"
|
|
42 #include "SDL_main.h"
|
|
43
|
|
44 #ifdef WMMSG_DEBUG
|
|
45 #include "wmmsg.h"
|
|
46 #endif
|
|
47
|
|
48 #ifdef _WIN32_WCE
|
|
49 #define NO_GETKEYBOARDSTATE
|
|
50 #endif
|
|
51
|
|
52 /* The window we use for everything... */
|
|
53 const char *SDL_Appname = NULL;
|
|
54 HINSTANCE SDL_Instance = NULL;
|
|
55 HWND SDL_Window = NULL;
|
|
56 RECT SDL_bounds = {0, 0, 0, 0};
|
|
57 int SDL_resizing = 0;
|
|
58 int mouse_relative = 0;
|
|
59 int posted = 0;
|
|
60
|
|
61
|
|
62 /* Functions called by the message processing function */
|
|
63 LONG
|
|
64 (*HandleMessage)(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)=NULL;
|
|
65 void (*WIN_RealizePalette)(_THIS);
|
|
66 void (*WIN_PaletteChanged)(_THIS, HWND window);
|
|
67 void (*WIN_SwapGamma)(_THIS);
|
|
68 void (*WIN_WinPAINT)(_THIS, HDC hdc);
|
|
69
|
|
70 #ifdef WM_MOUSELEAVE
|
|
71 /*
|
|
72 Special code to handle mouse leave events - this sucks...
|
|
73 http://support.microsoft.com/support/kb/articles/q183/1/07.asp
|
|
74
|
|
75 TrackMouseEvent() is only available on Win98 and WinNT.
|
|
76 _TrackMouseEvent() is available on Win95, but isn't yet in the mingw32
|
|
77 development environment, and only works on systems that have had IE 3.0
|
|
78 or newer installed on them (which is not the case with the base Win95).
|
|
79 Therefore, we implement our own version of _TrackMouseEvent() which
|
|
80 uses our own implementation if TrackMouseEvent() is not available.
|
|
81 */
|
|
82 static BOOL (WINAPI *_TrackMouseEvent)(TRACKMOUSEEVENT *ptme) = NULL;
|
|
83
|
|
84 static VOID CALLBACK
|
|
85 TrackMouseTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
|
|
86 {
|
|
87 RECT rect;
|
|
88 POINT pt;
|
|
89
|
|
90 GetClientRect(hWnd, &rect);
|
|
91 MapWindowPoints(hWnd, NULL, (LPPOINT)&rect, 2);
|
|
92 GetCursorPos(&pt);
|
|
93 if ( !PtInRect(&rect, pt) || (WindowFromPoint(pt) != hWnd) ) {
|
|
94 if ( !KillTimer(hWnd, idEvent) ) {
|
|
95 /* Error killing the timer! */
|
|
96 }
|
|
97 PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
|
|
98 }
|
|
99 }
|
|
100 static BOOL WINAPI WIN_TrackMouseEvent(TRACKMOUSEEVENT *ptme)
|
|
101 {
|
|
102 if ( ptme->dwFlags == TME_LEAVE ) {
|
|
103 return SetTimer(ptme->hwndTrack, ptme->dwFlags, 100,
|
|
104 (TIMERPROC)TrackMouseTimerProc);
|
|
105 }
|
|
106 return FALSE;
|
|
107 }
|
|
108 #endif /* WM_MOUSELEAVE */
|
|
109
|
|
110 /* Function to retrieve the current keyboard modifiers */
|
|
111 static void WIN_GetKeyboardState(void)
|
|
112 {
|
|
113 #ifndef NO_GETKEYBOARDSTATE
|
|
114 SDLMod state;
|
|
115 BYTE keyboard[256];
|
|
116
|
|
117 state = KMOD_NONE;
|
|
118 if ( GetKeyboardState(keyboard) ) {
|
|
119 if ( keyboard[VK_LSHIFT] & 0x80) {
|
|
120 state |= KMOD_LSHIFT;
|
|
121 }
|
|
122 if ( keyboard[VK_RSHIFT] & 0x80) {
|
|
123 state |= KMOD_RSHIFT;
|
|
124 }
|
|
125 if ( keyboard[VK_LCONTROL] & 0x80) {
|
|
126 state |= KMOD_LCTRL;
|
|
127 }
|
|
128 if ( keyboard[VK_RCONTROL] & 0x80) {
|
|
129 state |= KMOD_RCTRL;
|
|
130 }
|
|
131 if ( keyboard[VK_LMENU] & 0x80) {
|
|
132 state |= KMOD_LALT;
|
|
133 }
|
|
134 if ( keyboard[VK_RMENU] & 0x80) {
|
|
135 state |= KMOD_RALT;
|
|
136 }
|
|
137 if ( keyboard[VK_NUMLOCK] & 0x80) {
|
|
138 state |= KMOD_NUM;
|
|
139 }
|
|
140 if ( keyboard[VK_CAPITAL] & 0x80) {
|
|
141 state |= KMOD_CAPS;
|
|
142 }
|
|
143 }
|
|
144 SDL_SetModState(state);
|
|
145 #endif /* !NO_GETKEYBOARDSTATE */
|
|
146 }
|
|
147
|
|
148 /* The main Win32 event handler */
|
|
149 static LONG CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
150 {
|
|
151 SDL_VideoDevice *this = current_video;
|
|
152 static int mouse_pressed = 0;
|
|
153 static int in_window = 0;
|
|
154 #ifdef WMMSG_DEBUG
|
|
155 fprintf(stderr, "Received windows message: ");
|
|
156 if ( msg > MAX_WMMSG ) {
|
|
157 fprintf(stderr, "%d", msg);
|
|
158 } else {
|
|
159 fprintf(stderr, "%s", wmtab[msg]);
|
|
160 }
|
|
161 fprintf(stderr, " -- 0x%X, 0x%X\n", wParam, lParam);
|
|
162 #endif
|
|
163 switch (msg) {
|
|
164
|
|
165 case WM_ACTIVATE: {
|
|
166 SDL_VideoDevice *this = current_video;
|
|
167 BOOL minimized;
|
|
168 Uint8 appstate;
|
|
169
|
|
170 minimized = HIWORD(wParam);
|
|
171 if ( !minimized && (LOWORD(wParam) != WA_INACTIVE) ) {
|
|
172 /* Gain the following states */
|
|
173 appstate = SDL_APPACTIVE|SDL_APPINPUTFOCUS;
|
|
174 if ( this->input_grab != SDL_GRAB_OFF ) {
|
|
175 WIN_GrabInput(this, SDL_GRAB_ON);
|
|
176 }
|
|
177 if ( !(SDL_GetAppState()&SDL_APPINPUTFOCUS) ) {
|
|
178 WIN_SwapGamma(this);
|
|
179 }
|
|
180 posted = SDL_PrivateAppActive(1, appstate);
|
|
181 WIN_GetKeyboardState();
|
|
182 } else {
|
|
183 /* Lose the following states */
|
|
184 appstate = SDL_APPINPUTFOCUS;
|
|
185 if ( minimized ) {
|
|
186 appstate |= SDL_APPACTIVE;
|
|
187 }
|
|
188 if ( this->input_grab != SDL_GRAB_OFF ) {
|
|
189 WIN_GrabInput(this, SDL_GRAB_OFF);
|
|
190 }
|
|
191 if ( SDL_GetAppState() & SDL_APPINPUTFOCUS ) {
|
|
192 WIN_SwapGamma(this);
|
|
193 }
|
|
194 posted = SDL_PrivateAppActive(0, appstate);
|
|
195 }
|
|
196 return(0);
|
|
197 }
|
|
198 break;
|
|
199
|
|
200 case WM_MOUSEMOVE: {
|
|
201
|
|
202 /* Mouse is handled by DirectInput when fullscreen */
|
|
203 if ( SDL_VideoSurface && ! DIRECTX_FULLSCREEN() ) {
|
|
204 Sint16 x, y;
|
|
205
|
|
206 /* mouse has entered the window */
|
|
207 if ( ! in_window ) {
|
|
208 #ifdef WM_MOUSELEAVE
|
|
209 TRACKMOUSEEVENT tme;
|
|
210
|
|
211 tme.cbSize = sizeof(tme);
|
|
212 tme.dwFlags = TME_LEAVE;
|
|
213 tme.hwndTrack = SDL_Window;
|
|
214 _TrackMouseEvent(&tme);
|
|
215 #endif /* WM_MOUSELEAVE */
|
|
216 in_window = TRUE;
|
|
217
|
|
218 posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
|
|
219 }
|
|
220
|
|
221 /* mouse has moved within the window */
|
|
222 x = LOWORD(lParam);
|
|
223 y = HIWORD(lParam);
|
|
224 if ( mouse_relative ) {
|
|
225 POINT center;
|
|
226 center.x = (SDL_VideoSurface->w/2);
|
|
227 center.y = (SDL_VideoSurface->h/2);
|
|
228 x -= (Sint16)center.x;
|
|
229 y -= (Sint16)center.y;
|
|
230 if ( x || y ) {
|
|
231 ClientToScreen(SDL_Window, ¢er);
|
|
232 SetCursorPos(center.x, center.y);
|
|
233 posted = SDL_PrivateMouseMotion(0, 1, x, y);
|
|
234 }
|
|
235 } else {
|
|
236 posted = SDL_PrivateMouseMotion(0, 0, x, y);
|
|
237 }
|
|
238 }
|
|
239 }
|
|
240 return(0);
|
|
241
|
|
242 #ifdef WM_MOUSELEAVE
|
|
243 case WM_MOUSELEAVE: {
|
|
244
|
|
245 /* Mouse is handled by DirectInput when fullscreen */
|
|
246 if ( SDL_VideoSurface && ! DIRECTX_FULLSCREEN() ) {
|
|
247 /* mouse has left the window */
|
|
248 /* or */
|
|
249 /* Elvis has left the building! */
|
|
250 in_window = FALSE;
|
|
251 posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
|
|
252 }
|
|
253 }
|
|
254 return(0);
|
|
255 #endif /* WM_MOUSELEAVE */
|
|
256
|
|
257 case WM_LBUTTONDOWN:
|
|
258 case WM_LBUTTONUP:
|
|
259 case WM_MBUTTONDOWN:
|
|
260 case WM_MBUTTONUP:
|
|
261 case WM_RBUTTONDOWN:
|
|
262 case WM_RBUTTONUP: {
|
|
263 /* Mouse is handled by DirectInput when fullscreen */
|
|
264 if ( SDL_VideoSurface && ! DIRECTX_FULLSCREEN() ) {
|
|
265 Sint16 x, y;
|
|
266 Uint8 button, state;
|
|
267
|
|
268 /* Figure out which button to use */
|
|
269 switch (msg) {
|
|
270 case WM_LBUTTONDOWN:
|
|
271 button = 1;
|
|
272 state = SDL_PRESSED;
|
|
273 break;
|
|
274 case WM_LBUTTONUP:
|
|
275 button = 1;
|
|
276 state = SDL_RELEASED;
|
|
277 break;
|
|
278 case WM_MBUTTONDOWN:
|
|
279 button = 2;
|
|
280 state = SDL_PRESSED;
|
|
281 break;
|
|
282 case WM_MBUTTONUP:
|
|
283 button = 2;
|
|
284 state = SDL_RELEASED;
|
|
285 break;
|
|
286 case WM_RBUTTONDOWN:
|
|
287 button = 3;
|
|
288 state = SDL_PRESSED;
|
|
289 break;
|
|
290 case WM_RBUTTONUP:
|
|
291 button = 3;
|
|
292 state = SDL_RELEASED;
|
|
293 break;
|
|
294 default:
|
|
295 /* Eh? Unknown button? */
|
|
296 return(0);
|
|
297 }
|
|
298 if ( state == SDL_PRESSED ) {
|
|
299 /* Grab mouse so we get up events */
|
|
300 if ( ++mouse_pressed > 0 ) {
|
|
301 SetCapture(hwnd);
|
|
302 }
|
|
303 } else {
|
|
304 /* Release mouse after all up events */
|
|
305 if ( --mouse_pressed <= 0 ) {
|
|
306 ReleaseCapture();
|
|
307 mouse_pressed = 0;
|
|
308 }
|
|
309 }
|
|
310 if ( mouse_relative ) {
|
|
311 /* RJR: March 28, 2000
|
|
312 report internal mouse position if in relative mode */
|
|
313 x = 0; y = 0;
|
|
314 } else {
|
|
315 x = (Sint16)LOWORD(lParam);
|
|
316 y = (Sint16)HIWORD(lParam);
|
|
317 }
|
|
318 posted = SDL_PrivateMouseButton(
|
|
319 state, button, x, y);
|
|
320 }
|
|
321 }
|
|
322 return(0);
|
|
323
|
|
324 #ifdef WM_GETMINMAXINFO
|
|
325 /* This message is sent as a way for us to "check" the values
|
|
326 * of a position change. If we don't like it, we can adjust
|
|
327 * the values before they are changed.
|
|
328 */
|
|
329 case WM_GETMINMAXINFO: {
|
|
330 MINMAXINFO *info;
|
|
331 RECT size;
|
|
332 int x, y;
|
|
333 int width;
|
|
334 int height;
|
|
335
|
|
336 /* We don't want to clobber an internal resize */
|
|
337 if ( SDL_resizing )
|
|
338 return(0);
|
|
339
|
|
340 /* We allow resizing with the SDL_RESIZABLE flag */
|
|
341 if ( SDL_PublicSurface &&
|
|
342 (SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
|
|
343 return(0);
|
|
344 }
|
|
345
|
|
346 /* Get the current position of our window */
|
|
347 GetWindowRect(SDL_Window, &size);
|
|
348 x = size.left;
|
|
349 y = size.top;
|
|
350
|
|
351 /* Calculate current width and height of our window */
|
|
352 size.top = 0;
|
|
353 size.left = 0;
|
|
354 if ( SDL_PublicSurface != NULL ) {
|
|
355 size.bottom = SDL_PublicSurface->h;
|
|
356 size.right = SDL_PublicSurface->w;
|
|
357 } else {
|
|
358 size.bottom = 0;
|
|
359 size.right = 0;
|
|
360 }
|
|
361 AdjustWindowRect(&size, GetWindowLong(hwnd, GWL_STYLE),
|
|
362 FALSE);
|
|
363 width = size.right - size.left;
|
|
364 height = size.bottom - size.top;
|
|
365
|
|
366 /* Fix our size to the current size */
|
|
367 info = (MINMAXINFO *)lParam;
|
|
368 info->ptMaxSize.x = width;
|
|
369 info->ptMaxSize.y = height;
|
|
370 info->ptMaxPosition.x = x;
|
|
371 info->ptMaxPosition.y = y;
|
|
372 info->ptMinTrackSize.x = width;
|
|
373 info->ptMinTrackSize.y = height;
|
|
374 info->ptMaxTrackSize.x = width;
|
|
375 info->ptMaxTrackSize.y = height;
|
|
376 }
|
|
377 return(0);
|
|
378 #endif /* WM_GETMINMAXINFO */
|
|
379
|
|
380 case WM_MOVE: {
|
|
381 SDL_VideoDevice *this = current_video;
|
|
382
|
|
383 GetClientRect(SDL_Window, &SDL_bounds);
|
|
384 ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds);
|
|
385 ClientToScreen(SDL_Window, (LPPOINT)&SDL_bounds+1);
|
|
386 if ( this->input_grab != SDL_GRAB_OFF ) {
|
|
387 ClipCursor(&SDL_bounds);
|
|
388 }
|
|
389 }
|
|
390 break;
|
|
391
|
|
392 case WM_SIZE: {
|
|
393 if ( SDL_PublicSurface &&
|
|
394 (SDL_PublicSurface->flags & SDL_RESIZABLE) ) {
|
|
395 SDL_PrivateResize(LOWORD(lParam), HIWORD(lParam));
|
|
396 }
|
|
397 return(0);
|
|
398 }
|
|
399 break;
|
|
400
|
|
401 /* We need to set the cursor */
|
|
402 case WM_SETCURSOR: {
|
|
403 Uint16 hittest;
|
|
404
|
|
405 hittest = LOWORD(lParam);
|
|
406 if ( hittest == HTCLIENT ) {
|
|
407 SetCursor(SDL_hcursor);
|
|
408 return(TRUE);
|
|
409 }
|
|
410 }
|
|
411 break;
|
|
412
|
|
413 /* We are about to get palette focus! */
|
|
414 case WM_QUERYNEWPALETTE: {
|
|
415 WIN_RealizePalette(current_video);
|
|
416 return(TRUE);
|
|
417 }
|
|
418 break;
|
|
419
|
|
420 /* Another application changed the palette */
|
|
421 case WM_PALETTECHANGED: {
|
|
422 WIN_PaletteChanged(current_video, (HWND)wParam);
|
|
423 }
|
|
424 break;
|
|
425
|
|
426 /* We were occluded, refresh our display */
|
|
427 case WM_PAINT: {
|
|
428 HDC hdc;
|
|
429 PAINTSTRUCT ps;
|
|
430
|
|
431 hdc = BeginPaint(SDL_Window, &ps);
|
|
432 if ( current_video->screen &&
|
|
433 !(current_video->screen->flags & SDL_OPENGL) ) {
|
|
434 WIN_WinPAINT(current_video, hdc);
|
|
435 }
|
|
436 EndPaint(SDL_Window, &ps);
|
|
437 }
|
|
438 return(0);
|
|
439
|
|
440 case WM_ERASEBKGND: {
|
|
441 /* Just do nothing */ ;
|
|
442 }
|
|
443 return(1);
|
|
444
|
|
445 case WM_CLOSE: {
|
|
446 if ( (posted = SDL_PrivateQuit()) )
|
|
447 PostQuitMessage(0);
|
|
448 }
|
|
449 return(0);
|
|
450
|
|
451 case WM_DESTROY: {
|
|
452 PostQuitMessage(0);
|
|
453 }
|
|
454 return(0);
|
|
455
|
|
456 default: {
|
|
457 /* Special handling by the video driver */
|
|
458 if (HandleMessage) {
|
|
459 return(HandleMessage(current_video,
|
|
460 hwnd, msg, wParam, lParam));
|
|
461 }
|
|
462 }
|
|
463 break;
|
|
464 }
|
|
465 return(DefWindowProc(hwnd, msg, wParam, lParam));
|
|
466 }
|
|
467
|
|
468 /* This allows the SDL_WINDOWID hack */
|
|
469 const char *SDL_windowid = NULL;
|
|
470
|
|
471 /* Register the class for this application -- exported for winmain.c */
|
|
472 int SDL_RegisterApp(char *name, Uint32 style, void *hInst)
|
|
473 {
|
|
474 static int initialized = 0;
|
|
475 WNDCLASS class;
|
|
476 #ifdef WM_MOUSELEAVE
|
|
477 HMODULE handle;
|
|
478 #endif
|
|
479
|
|
480 /* Only do this once... */
|
|
481 if ( initialized ) {
|
|
482 return(0);
|
|
483 }
|
|
484
|
|
485 /* This function needs to be passed the correct process handle
|
|
486 by the application. The following call just returns a handle
|
|
487 to the SDL DLL, which is useless for our purposes and causes
|
|
488 DirectInput to fail to initialize.
|
|
489 */
|
|
490 if ( ! hInst ) {
|
|
491 hInst = GetModuleHandle(NULL);
|
|
492 }
|
|
493
|
|
494 /* Register the application class */
|
|
495 class.hCursor = NULL;
|
|
496 #ifdef _WIN32_WCE
|
|
497 {
|
|
498 /* WinCE uses the UNICODE version */
|
|
499 int nLen = strlen(name);
|
|
500 LPWSTR lpszW = alloca((nLen+1)*2);
|
|
501 MultiByteToWideChar(CP_ACP, 0, name, -1, lpszW, nLen);
|
|
502 class.hIcon = LoadImage(hInst, lpszW, IMAGE_ICON,
|
|
503 0, 0, LR_DEFAULTCOLOR);
|
|
504 class.lpszMenuName = lpszW;
|
|
505 class.lpszClassName = lpszW;
|
|
506 }
|
|
507 #else
|
|
508 class.hIcon = LoadImage(hInst, name, IMAGE_ICON,
|
|
509 0, 0, LR_DEFAULTCOLOR);
|
|
510 class.lpszMenuName = "(none)";
|
|
511 class.lpszClassName = name;
|
|
512 #endif /* _WIN32_WCE */
|
|
513 class.hbrBackground = NULL;
|
|
514 class.hInstance = hInst ? hInst : GetModuleHandle(0);
|
|
515 class.style = style;
|
|
516 #ifdef HAVE_OPENGL
|
|
517 class.style |= CS_OWNDC;
|
|
518 #endif
|
|
519 class.lpfnWndProc = WinMessage;
|
|
520 class.cbWndExtra = 0;
|
|
521 class.cbClsExtra = 0;
|
|
522 if ( ! RegisterClass(&class) ) {
|
|
523 SDL_SetError("Couldn't register application class");
|
|
524 return(-1);
|
|
525 }
|
|
526 SDL_Appname = name;
|
|
527 SDL_Instance = hInst;
|
|
528
|
|
529 #ifdef WM_MOUSELEAVE
|
|
530 /* Get the version of TrackMouseEvent() we use */
|
|
531 _TrackMouseEvent = NULL;
|
|
532 handle = GetModuleHandle("USER32.DLL");
|
|
533 if ( handle ) {
|
|
534 _TrackMouseEvent = (BOOL (WINAPI *)(TRACKMOUSEEVENT *))GetProcAddress(handle, "TrackMouseEvent");
|
|
535 }
|
|
536 if ( _TrackMouseEvent == NULL ) {
|
|
537 _TrackMouseEvent = WIN_TrackMouseEvent;
|
|
538 }
|
|
539 #endif /* WM_MOUSELEAVE */
|
|
540
|
|
541 /* Check for SDL_WINDOWID hack */
|
|
542 SDL_windowid = getenv("SDL_WINDOWID");
|
|
543
|
|
544 initialized = 1;
|
|
545 return(0);
|
|
546 }
|
|
547
|