1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #if SDL_VIDEO_DRIVER_WINDOWS
24
25 #include "SDL_windowsvideo.h"
26 #include "SDL_windowsshape.h"
27 #include "SDL_system.h"
28 #include "SDL_syswm.h"
29 #include "SDL_timer.h"
30 #include "SDL_vkeys.h"
31 #include "../../events/SDL_events_c.h"
32 #include "../../events/SDL_touch_c.h"
33 #include "../../events/scancodes_windows.h"
34 #include "SDL_assert.h"
35 #include "SDL_hints.h"
36
37 /* Dropfile support */
38 #include <shellapi.h>
39
40 /* For GET_X_LPARAM, GET_Y_LPARAM. */
41 #include <windowsx.h>
42
43 /* #define WMMSG_DEBUG */
44 #ifdef WMMSG_DEBUG
45 #include <stdio.h>
46 #include "wmmsg.h"
47 #endif
48
49 /* For processing mouse WM_*BUTTON* and WM_MOUSEMOVE message-data from GetMessageExtraInfo() */
50 #define MOUSEEVENTF_FROMTOUCH 0xFF515700
51
52 /* Masks for processing the windows KEYDOWN and KEYUP messages */
53 #define REPEATED_KEYMASK (1<<30)
54 #define EXTENDED_KEYMASK (1<<24)
55
56 #define VK_ENTER 10 /* Keypad Enter ... no VKEY defined? */
57 #ifndef VK_OEM_NEC_EQUAL
58 #define VK_OEM_NEC_EQUAL 0x92
59 #endif
60
61 /* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */
62 #ifndef WM_XBUTTONDOWN
63 #define WM_XBUTTONDOWN 0x020B
64 #endif
65 #ifndef WM_XBUTTONUP
66 #define WM_XBUTTONUP 0x020C
67 #endif
68 #ifndef GET_XBUTTON_WPARAM
69 #define GET_XBUTTON_WPARAM(w) (HIWORD(w))
70 #endif
71 #ifndef WM_INPUT
72 #define WM_INPUT 0x00ff
73 #endif
74 #ifndef WM_TOUCH
75 #define WM_TOUCH 0x0240
76 #endif
77 #ifndef WM_MOUSEHWHEEL
78 #define WM_MOUSEHWHEEL 0x020E
79 #endif
80 #ifndef WM_UNICHAR
81 #define WM_UNICHAR 0x0109
82 #endif
83
84 static SDL_Scancode
WindowsScanCodeToSDLScanCode(LPARAM lParam,WPARAM wParam)85 WindowsScanCodeToSDLScanCode(LPARAM lParam, WPARAM wParam)
86 {
87 SDL_Scancode code;
88 char bIsExtended;
89 int nScanCode = (lParam >> 16) & 0xFF;
90
91 /* 0x45 here to work around both pause and numlock sharing the same scancode, so use the VK key to tell them apart */
92 if (nScanCode == 0 || nScanCode == 0x45) {
93 switch(wParam) {
94 case VK_CLEAR: return SDL_SCANCODE_CLEAR;
95 case VK_MODECHANGE: return SDL_SCANCODE_MODE;
96 case VK_SELECT: return SDL_SCANCODE_SELECT;
97 case VK_EXECUTE: return SDL_SCANCODE_EXECUTE;
98 case VK_HELP: return SDL_SCANCODE_HELP;
99 case VK_PAUSE: return SDL_SCANCODE_PAUSE;
100 case VK_NUMLOCK: return SDL_SCANCODE_NUMLOCKCLEAR;
101
102 case VK_F13: return SDL_SCANCODE_F13;
103 case VK_F14: return SDL_SCANCODE_F14;
104 case VK_F15: return SDL_SCANCODE_F15;
105 case VK_F16: return SDL_SCANCODE_F16;
106 case VK_F17: return SDL_SCANCODE_F17;
107 case VK_F18: return SDL_SCANCODE_F18;
108 case VK_F19: return SDL_SCANCODE_F19;
109 case VK_F20: return SDL_SCANCODE_F20;
110 case VK_F21: return SDL_SCANCODE_F21;
111 case VK_F22: return SDL_SCANCODE_F22;
112 case VK_F23: return SDL_SCANCODE_F23;
113 case VK_F24: return SDL_SCANCODE_F24;
114
115 case VK_OEM_NEC_EQUAL: return SDL_SCANCODE_KP_EQUALS;
116 case VK_BROWSER_BACK: return SDL_SCANCODE_AC_BACK;
117 case VK_BROWSER_FORWARD: return SDL_SCANCODE_AC_FORWARD;
118 case VK_BROWSER_REFRESH: return SDL_SCANCODE_AC_REFRESH;
119 case VK_BROWSER_STOP: return SDL_SCANCODE_AC_STOP;
120 case VK_BROWSER_SEARCH: return SDL_SCANCODE_AC_SEARCH;
121 case VK_BROWSER_FAVORITES: return SDL_SCANCODE_AC_BOOKMARKS;
122 case VK_BROWSER_HOME: return SDL_SCANCODE_AC_HOME;
123 case VK_VOLUME_MUTE: return SDL_SCANCODE_AUDIOMUTE;
124 case VK_VOLUME_DOWN: return SDL_SCANCODE_VOLUMEDOWN;
125 case VK_VOLUME_UP: return SDL_SCANCODE_VOLUMEUP;
126
127 case VK_MEDIA_NEXT_TRACK: return SDL_SCANCODE_AUDIONEXT;
128 case VK_MEDIA_PREV_TRACK: return SDL_SCANCODE_AUDIOPREV;
129 case VK_MEDIA_STOP: return SDL_SCANCODE_AUDIOSTOP;
130 case VK_MEDIA_PLAY_PAUSE: return SDL_SCANCODE_AUDIOPLAY;
131 case VK_LAUNCH_MAIL: return SDL_SCANCODE_MAIL;
132 case VK_LAUNCH_MEDIA_SELECT: return SDL_SCANCODE_MEDIASELECT;
133
134 case VK_OEM_102: return SDL_SCANCODE_NONUSBACKSLASH;
135
136 case VK_ATTN: return SDL_SCANCODE_SYSREQ;
137 case VK_CRSEL: return SDL_SCANCODE_CRSEL;
138 case VK_EXSEL: return SDL_SCANCODE_EXSEL;
139 case VK_OEM_CLEAR: return SDL_SCANCODE_CLEAR;
140
141 case VK_LAUNCH_APP1: return SDL_SCANCODE_APP1;
142 case VK_LAUNCH_APP2: return SDL_SCANCODE_APP2;
143
144 default: return SDL_SCANCODE_UNKNOWN;
145 }
146 }
147
148 if (nScanCode > 127)
149 return SDL_SCANCODE_UNKNOWN;
150
151 code = windows_scancode_table[nScanCode];
152
153 bIsExtended = (lParam & (1 << 24)) != 0;
154 if (!bIsExtended) {
155 switch (code) {
156 case SDL_SCANCODE_HOME:
157 return SDL_SCANCODE_KP_7;
158 case SDL_SCANCODE_UP:
159 return SDL_SCANCODE_KP_8;
160 case SDL_SCANCODE_PAGEUP:
161 return SDL_SCANCODE_KP_9;
162 case SDL_SCANCODE_LEFT:
163 return SDL_SCANCODE_KP_4;
164 case SDL_SCANCODE_RIGHT:
165 return SDL_SCANCODE_KP_6;
166 case SDL_SCANCODE_END:
167 return SDL_SCANCODE_KP_1;
168 case SDL_SCANCODE_DOWN:
169 return SDL_SCANCODE_KP_2;
170 case SDL_SCANCODE_PAGEDOWN:
171 return SDL_SCANCODE_KP_3;
172 case SDL_SCANCODE_INSERT:
173 return SDL_SCANCODE_KP_0;
174 case SDL_SCANCODE_DELETE:
175 return SDL_SCANCODE_KP_PERIOD;
176 case SDL_SCANCODE_PRINTSCREEN:
177 return SDL_SCANCODE_KP_MULTIPLY;
178 default:
179 break;
180 }
181 } else {
182 switch (code) {
183 case SDL_SCANCODE_RETURN:
184 return SDL_SCANCODE_KP_ENTER;
185 case SDL_SCANCODE_LALT:
186 return SDL_SCANCODE_RALT;
187 case SDL_SCANCODE_LCTRL:
188 return SDL_SCANCODE_RCTRL;
189 case SDL_SCANCODE_SLASH:
190 return SDL_SCANCODE_KP_DIVIDE;
191 case SDL_SCANCODE_CAPSLOCK:
192 return SDL_SCANCODE_KP_PLUS;
193 default:
194 break;
195 }
196 }
197
198 return code;
199 }
200
201 static SDL_bool
WIN_ShouldIgnoreFocusClick()202 WIN_ShouldIgnoreFocusClick()
203 {
204 return !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE);
205 }
206
207 void
WIN_CheckWParamMouseButton(SDL_bool bwParamMousePressed,SDL_bool bSDLMousePressed,SDL_WindowData * data,Uint8 button,SDL_MouseID mouseID)208 WIN_CheckWParamMouseButton(SDL_bool bwParamMousePressed, SDL_bool bSDLMousePressed, SDL_WindowData *data, Uint8 button, SDL_MouseID mouseID)
209 {
210 if (data->focus_click_pending & SDL_BUTTON(button)) {
211 /* Ignore the button click for activation */
212 if (!bwParamMousePressed) {
213 data->focus_click_pending &= ~SDL_BUTTON(button);
214 if (!data->focus_click_pending) {
215 WIN_UpdateClipCursor(data->window);
216 }
217 }
218 if (WIN_ShouldIgnoreFocusClick()) {
219 return;
220 }
221 }
222
223 if (bwParamMousePressed && !bSDLMousePressed) {
224 SDL_SendMouseButton(data->window, mouseID, SDL_PRESSED, button);
225 } else if (!bwParamMousePressed && bSDLMousePressed) {
226 SDL_SendMouseButton(data->window, mouseID, SDL_RELEASED, button);
227 }
228 }
229
230 /*
231 * Some windows systems fail to send a WM_LBUTTONDOWN sometimes, but each mouse move contains the current button state also
232 * so this funciton reconciles our view of the world with the current buttons reported by windows
233 */
234 void
WIN_CheckWParamMouseButtons(WPARAM wParam,SDL_WindowData * data,SDL_MouseID mouseID)235 WIN_CheckWParamMouseButtons(WPARAM wParam, SDL_WindowData *data, SDL_MouseID mouseID)
236 {
237 if (wParam != data->mouse_button_flags) {
238 Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL);
239 WIN_CheckWParamMouseButton((wParam & MK_LBUTTON), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT, mouseID);
240 WIN_CheckWParamMouseButton((wParam & MK_MBUTTON), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE, mouseID);
241 WIN_CheckWParamMouseButton((wParam & MK_RBUTTON), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT, mouseID);
242 WIN_CheckWParamMouseButton((wParam & MK_XBUTTON1), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1, mouseID);
243 WIN_CheckWParamMouseButton((wParam & MK_XBUTTON2), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2, mouseID);
244 data->mouse_button_flags = wParam;
245 }
246 }
247
248
249 void
WIN_CheckRawMouseButtons(ULONG rawButtons,SDL_WindowData * data)250 WIN_CheckRawMouseButtons(ULONG rawButtons, SDL_WindowData *data)
251 {
252 if (rawButtons != data->mouse_button_flags) {
253 Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL);
254 if ((rawButtons & RI_MOUSE_BUTTON_1_DOWN))
255 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_1_DOWN), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT, 0);
256 if ((rawButtons & RI_MOUSE_BUTTON_1_UP))
257 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_1_UP), (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT, 0);
258 if ((rawButtons & RI_MOUSE_BUTTON_2_DOWN))
259 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_2_DOWN), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT, 0);
260 if ((rawButtons & RI_MOUSE_BUTTON_2_UP))
261 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_2_UP), (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT, 0);
262 if ((rawButtons & RI_MOUSE_BUTTON_3_DOWN))
263 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_3_DOWN), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE, 0);
264 if ((rawButtons & RI_MOUSE_BUTTON_3_UP))
265 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_3_UP), (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE, 0);
266 if ((rawButtons & RI_MOUSE_BUTTON_4_DOWN))
267 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_4_DOWN), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1, 0);
268 if ((rawButtons & RI_MOUSE_BUTTON_4_UP))
269 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_4_UP), (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1, 0);
270 if ((rawButtons & RI_MOUSE_BUTTON_5_DOWN))
271 WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_5_DOWN), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2, 0);
272 if ((rawButtons & RI_MOUSE_BUTTON_5_UP))
273 WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_5_UP), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2, 0);
274 data->mouse_button_flags = rawButtons;
275 }
276 }
277
278 void
WIN_CheckAsyncMouseRelease(SDL_WindowData * data)279 WIN_CheckAsyncMouseRelease(SDL_WindowData *data)
280 {
281 Uint32 mouseFlags;
282 SHORT keyState;
283
284 /* mouse buttons may have changed state here, we need to resync them,
285 but we will get a WM_MOUSEMOVE right away which will fix things up if in non raw mode also
286 */
287 mouseFlags = SDL_GetMouseState(NULL, NULL);
288
289 keyState = GetAsyncKeyState(VK_LBUTTON);
290 if (!(keyState & 0x8000)) {
291 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_LMASK), data, SDL_BUTTON_LEFT, 0);
292 }
293 keyState = GetAsyncKeyState(VK_RBUTTON);
294 if (!(keyState & 0x8000)) {
295 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_RMASK), data, SDL_BUTTON_RIGHT, 0);
296 }
297 keyState = GetAsyncKeyState(VK_MBUTTON);
298 if (!(keyState & 0x8000)) {
299 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_MMASK), data, SDL_BUTTON_MIDDLE, 0);
300 }
301 keyState = GetAsyncKeyState(VK_XBUTTON1);
302 if (!(keyState & 0x8000)) {
303 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_X1MASK), data, SDL_BUTTON_X1, 0);
304 }
305 keyState = GetAsyncKeyState(VK_XBUTTON2);
306 if (!(keyState & 0x8000)) {
307 WIN_CheckWParamMouseButton(SDL_FALSE, (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2, 0);
308 }
309 data->mouse_button_flags = 0;
310 }
311
312 BOOL
WIN_ConvertUTF32toUTF8(UINT32 codepoint,char * text)313 WIN_ConvertUTF32toUTF8(UINT32 codepoint, char * text)
314 {
315 if (codepoint <= 0x7F) {
316 text[0] = (char) codepoint;
317 text[1] = '\0';
318 } else if (codepoint <= 0x7FF) {
319 text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F);
320 text[1] = 0x80 | (char) (codepoint & 0x3F);
321 text[2] = '\0';
322 } else if (codepoint <= 0xFFFF) {
323 text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F);
324 text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
325 text[2] = 0x80 | (char) (codepoint & 0x3F);
326 text[3] = '\0';
327 } else if (codepoint <= 0x10FFFF) {
328 text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F);
329 text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F);
330 text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
331 text[3] = 0x80 | (char) (codepoint & 0x3F);
332 text[4] = '\0';
333 } else {
334 return SDL_FALSE;
335 }
336 return SDL_TRUE;
337 }
338
339 static SDL_bool
ShouldGenerateWindowCloseOnAltF4(void)340 ShouldGenerateWindowCloseOnAltF4(void)
341 {
342 return !SDL_GetHintBoolean(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, SDL_FALSE);
343 }
344
345 LRESULT CALLBACK
WIN_WindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)346 WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
347 {
348 SDL_WindowData *data;
349 LRESULT returnCode = -1;
350
351 /* Send a SDL_SYSWMEVENT if the application wants them */
352 if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
353 SDL_SysWMmsg wmmsg;
354
355 SDL_VERSION(&wmmsg.version);
356 wmmsg.subsystem = SDL_SYSWM_WINDOWS;
357 wmmsg.msg.win.hwnd = hwnd;
358 wmmsg.msg.win.msg = msg;
359 wmmsg.msg.win.wParam = wParam;
360 wmmsg.msg.win.lParam = lParam;
361 SDL_SendSysWMEvent(&wmmsg);
362 }
363
364 /* Get the window data for the window */
365 data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData"));
366 if (!data) {
367 return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
368 }
369
370 #ifdef WMMSG_DEBUG
371 {
372 char message[1024];
373 if (msg > MAX_WMMSG) {
374 SDL_snprintf(message, sizeof(message), "Received windows message: %p UNKNOWN (%d) -- 0x%X, 0x%X\n", hwnd, msg, wParam, lParam);
375 } else {
376 SDL_snprintf(message, sizeof(message), "Received windows message: %p %s -- 0x%X, 0x%X\n", hwnd, wmtab[msg], wParam, lParam);
377 }
378 OutputDebugStringA(message);
379 }
380 #endif /* WMMSG_DEBUG */
381
382 if (IME_HandleMessage(hwnd, msg, wParam, &lParam, data->videodata))
383 return 0;
384
385 switch (msg) {
386
387 case WM_SHOWWINDOW:
388 {
389 if (wParam) {
390 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
391 } else {
392 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
393 }
394 }
395 break;
396
397 case WM_ACTIVATE:
398 {
399 POINT cursorPos;
400 BOOL minimized;
401
402 minimized = HIWORD(wParam);
403 if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) {
404 if (LOWORD(wParam) == WA_CLICKACTIVE) {
405 if (GetAsyncKeyState(VK_LBUTTON)) {
406 data->focus_click_pending |= SDL_BUTTON_LMASK;
407 }
408 if (GetAsyncKeyState(VK_RBUTTON)) {
409 data->focus_click_pending |= SDL_BUTTON_RMASK;
410 }
411 if (GetAsyncKeyState(VK_MBUTTON)) {
412 data->focus_click_pending |= SDL_BUTTON_MMASK;
413 }
414 if (GetAsyncKeyState(VK_XBUTTON1)) {
415 data->focus_click_pending |= SDL_BUTTON_X1MASK;
416 }
417 if (GetAsyncKeyState(VK_XBUTTON2)) {
418 data->focus_click_pending |= SDL_BUTTON_X2MASK;
419 }
420 }
421
422 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
423 if (SDL_GetKeyboardFocus() != data->window) {
424 SDL_SetKeyboardFocus(data->window);
425 }
426
427 GetCursorPos(&cursorPos);
428 ScreenToClient(hwnd, &cursorPos);
429 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
430
431 WIN_CheckAsyncMouseRelease(data);
432
433 /*
434 * FIXME: Update keyboard state
435 */
436 WIN_CheckClipboardUpdate(data->videodata);
437
438 SDL_ToggleModState(KMOD_CAPS, (GetKeyState(VK_CAPITAL) & 0x0001) != 0);
439 SDL_ToggleModState(KMOD_NUM, (GetKeyState(VK_NUMLOCK) & 0x0001) != 0);
440 } else {
441 data->in_window_deactivation = SDL_TRUE;
442
443 if (SDL_GetKeyboardFocus() == data->window) {
444 SDL_SetKeyboardFocus(NULL);
445 WIN_ResetDeadKeys();
446 }
447
448 ClipCursor(NULL);
449
450 data->in_window_deactivation = SDL_FALSE;
451 }
452 }
453 returnCode = 0;
454 break;
455
456 case WM_MOUSEMOVE:
457 {
458 SDL_Mouse *mouse = SDL_GetMouse();
459 if (!mouse->relative_mode || mouse->relative_mode_warp) {
460 SDL_MouseID mouseID = (((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH) ? SDL_TOUCH_MOUSEID : 0);
461 SDL_SendMouseMotion(data->window, mouseID, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
462 }
463 }
464 /* don't break here, fall through to check the wParam like the button presses */
465 case WM_LBUTTONUP:
466 case WM_RBUTTONUP:
467 case WM_MBUTTONUP:
468 case WM_XBUTTONUP:
469 case WM_LBUTTONDOWN:
470 case WM_LBUTTONDBLCLK:
471 case WM_RBUTTONDOWN:
472 case WM_RBUTTONDBLCLK:
473 case WM_MBUTTONDOWN:
474 case WM_MBUTTONDBLCLK:
475 case WM_XBUTTONDOWN:
476 case WM_XBUTTONDBLCLK:
477 {
478 SDL_Mouse *mouse = SDL_GetMouse();
479 if (!mouse->relative_mode || mouse->relative_mode_warp) {
480 SDL_MouseID mouseID = (((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH) ? SDL_TOUCH_MOUSEID : 0);
481 WIN_CheckWParamMouseButtons(wParam, data, mouseID);
482 }
483 }
484 break;
485
486 case WM_INPUT:
487 {
488 SDL_Mouse *mouse = SDL_GetMouse();
489 HRAWINPUT hRawInput = (HRAWINPUT)lParam;
490 RAWINPUT inp;
491 UINT size = sizeof(inp);
492 const SDL_bool isRelative = mouse->relative_mode || mouse->relative_mode_warp;
493 const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0);
494
495 if (!isRelative || mouse->focus != data->window) {
496 if (!isCapture) {
497 break;
498 }
499 }
500
501 GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
502
503 /* Mouse data */
504 if (inp.header.dwType == RIM_TYPEMOUSE) {
505 if (isRelative) {
506 RAWMOUSE* rawmouse = &inp.data.mouse;
507
508 if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
509 SDL_SendMouseMotion(data->window, 0, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY);
510 } else {
511 /* synthesize relative moves from the abs position */
512 static SDL_Point initialMousePoint;
513 if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
514 initialMousePoint.x = rawmouse->lLastX;
515 initialMousePoint.y = rawmouse->lLastY;
516 }
517
518 SDL_SendMouseMotion(data->window, 0, 1, (int)(rawmouse->lLastX-initialMousePoint.x), (int)(rawmouse->lLastY-initialMousePoint.y) );
519
520 initialMousePoint.x = rawmouse->lLastX;
521 initialMousePoint.y = rawmouse->lLastY;
522 }
523 WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data);
524 } else if (isCapture) {
525 /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */
526 POINT pt;
527 GetCursorPos(&pt);
528 if (WindowFromPoint(pt) != hwnd) { /* if in the window, WM_MOUSEMOVE, etc, will cover it. */
529 ScreenToClient(hwnd, &pt);
530 SDL_SendMouseMotion(data->window, 0, 0, (int) pt.x, (int) pt.y);
531 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_LEFT);
532 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_RIGHT);
533 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE);
534 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1);
535 SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2);
536 }
537 } else {
538 SDL_assert(0 && "Shouldn't happen");
539 }
540 }
541 }
542 break;
543
544 case WM_MOUSEWHEEL:
545 {
546 static short s_AccumulatedMotion;
547
548 s_AccumulatedMotion += GET_WHEEL_DELTA_WPARAM(wParam);
549 if (s_AccumulatedMotion > 0) {
550 while (s_AccumulatedMotion >= WHEEL_DELTA) {
551 SDL_SendMouseWheel(data->window, 0, 0, 1, SDL_MOUSEWHEEL_NORMAL);
552 s_AccumulatedMotion -= WHEEL_DELTA;
553 }
554 } else {
555 while (s_AccumulatedMotion <= -WHEEL_DELTA) {
556 SDL_SendMouseWheel(data->window, 0, 0, -1, SDL_MOUSEWHEEL_NORMAL);
557 s_AccumulatedMotion += WHEEL_DELTA;
558 }
559 }
560 }
561 break;
562
563 case WM_MOUSEHWHEEL:
564 {
565 static short s_AccumulatedMotion;
566
567 s_AccumulatedMotion += GET_WHEEL_DELTA_WPARAM(wParam);
568 if (s_AccumulatedMotion > 0) {
569 while (s_AccumulatedMotion >= WHEEL_DELTA) {
570 SDL_SendMouseWheel(data->window, 0, 1, 0, SDL_MOUSEWHEEL_NORMAL);
571 s_AccumulatedMotion -= WHEEL_DELTA;
572 }
573 } else {
574 while (s_AccumulatedMotion <= -WHEEL_DELTA) {
575 SDL_SendMouseWheel(data->window, 0, -1, 0, SDL_MOUSEWHEEL_NORMAL);
576 s_AccumulatedMotion += WHEEL_DELTA;
577 }
578 }
579 }
580 break;
581
582 #ifdef WM_MOUSELEAVE
583 case WM_MOUSELEAVE:
584 if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
585 if (!IsIconic(hwnd)) {
586 POINT cursorPos;
587 GetCursorPos(&cursorPos);
588 ScreenToClient(hwnd, &cursorPos);
589 SDL_SendMouseMotion(data->window, 0, 0, cursorPos.x, cursorPos.y);
590 }
591 SDL_SetMouseFocus(NULL);
592 }
593 returnCode = 0;
594 break;
595 #endif /* WM_MOUSELEAVE */
596
597 case WM_KEYDOWN:
598 case WM_SYSKEYDOWN:
599 {
600 SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
601 const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
602
603 /* Detect relevant keyboard shortcuts */
604 if (keyboardState[SDL_SCANCODE_LALT] == SDL_PRESSED || keyboardState[SDL_SCANCODE_RALT] == SDL_PRESSED) {
605 /* ALT+F4: Close window */
606 if (code == SDL_SCANCODE_F4 && ShouldGenerateWindowCloseOnAltF4()) {
607 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
608 }
609 }
610
611 if (code != SDL_SCANCODE_UNKNOWN) {
612 SDL_SendKeyboardKey(SDL_PRESSED, code);
613 }
614 }
615
616 returnCode = 0;
617 break;
618
619 case WM_SYSKEYUP:
620 case WM_KEYUP:
621 {
622 SDL_Scancode code = WindowsScanCodeToSDLScanCode(lParam, wParam);
623 const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
624
625 if (code != SDL_SCANCODE_UNKNOWN) {
626 if (code == SDL_SCANCODE_PRINTSCREEN &&
627 keyboardState[code] == SDL_RELEASED) {
628 SDL_SendKeyboardKey(SDL_PRESSED, code);
629 }
630 SDL_SendKeyboardKey(SDL_RELEASED, code);
631 }
632 }
633 returnCode = 0;
634 break;
635
636 case WM_UNICHAR:
637 if ( wParam == UNICODE_NOCHAR ) {
638 returnCode = 1;
639 break;
640 }
641 /* otherwise fall through to below */
642 case WM_CHAR:
643 {
644 char text[5];
645 if ( WIN_ConvertUTF32toUTF8( (UINT32)wParam, text ) ) {
646 SDL_SendKeyboardText( text );
647 }
648 }
649 returnCode = 0;
650 break;
651
652 #ifdef WM_INPUTLANGCHANGE
653 case WM_INPUTLANGCHANGE:
654 {
655 WIN_UpdateKeymap();
656 SDL_SendKeymapChangedEvent();
657 }
658 returnCode = 1;
659 break;
660 #endif /* WM_INPUTLANGCHANGE */
661
662 case WM_NCLBUTTONDOWN:
663 {
664 data->in_title_click = SDL_TRUE;
665 }
666 break;
667
668 case WM_CAPTURECHANGED:
669 {
670 data->in_title_click = SDL_FALSE;
671
672 /* The mouse may have been released during a modal loop */
673 WIN_CheckAsyncMouseRelease(data);
674 }
675 break;
676
677 #ifdef WM_GETMINMAXINFO
678 case WM_GETMINMAXINFO:
679 {
680 MINMAXINFO *info;
681 RECT size;
682 int x, y;
683 int w, h;
684 int min_w, min_h;
685 int max_w, max_h;
686 int style;
687 BOOL menu;
688 BOOL constrain_max_size;
689
690 if (SDL_IsShapedWindow(data->window))
691 Win32_ResizeWindowShape(data->window);
692
693 /* If this is an expected size change, allow it */
694 if (data->expected_resize) {
695 break;
696 }
697
698 /* Get the current position of our window */
699 GetWindowRect(hwnd, &size);
700 x = size.left;
701 y = size.top;
702
703 /* Calculate current size of our window */
704 SDL_GetWindowSize(data->window, &w, &h);
705 SDL_GetWindowMinimumSize(data->window, &min_w, &min_h);
706 SDL_GetWindowMaximumSize(data->window, &max_w, &max_h);
707
708 /* Store in min_w and min_h difference between current size and minimal
709 size so we don't need to call AdjustWindowRectEx twice */
710 min_w -= w;
711 min_h -= h;
712 if (max_w && max_h) {
713 max_w -= w;
714 max_h -= h;
715 constrain_max_size = TRUE;
716 } else {
717 constrain_max_size = FALSE;
718 }
719
720 size.top = 0;
721 size.left = 0;
722 size.bottom = h;
723 size.right = w;
724
725 style = GetWindowLong(hwnd, GWL_STYLE);
726 /* DJM - according to the docs for GetMenu(), the
727 return value is undefined if hwnd is a child window.
728 Apparently it's too difficult for MS to check
729 inside their function, so I have to do it here.
730 */
731 menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
732 AdjustWindowRectEx(&size, style, menu, 0);
733 w = size.right - size.left;
734 h = size.bottom - size.top;
735
736 /* Fix our size to the current size */
737 info = (MINMAXINFO *) lParam;
738 if (SDL_GetWindowFlags(data->window) & SDL_WINDOW_RESIZABLE) {
739 info->ptMinTrackSize.x = w + min_w;
740 info->ptMinTrackSize.y = h + min_h;
741 if (constrain_max_size) {
742 info->ptMaxTrackSize.x = w + max_w;
743 info->ptMaxTrackSize.y = h + max_h;
744 }
745 } else {
746 info->ptMaxSize.x = w;
747 info->ptMaxSize.y = h;
748 info->ptMaxPosition.x = x;
749 info->ptMaxPosition.y = y;
750 info->ptMinTrackSize.x = w;
751 info->ptMinTrackSize.y = h;
752 info->ptMaxTrackSize.x = w;
753 info->ptMaxTrackSize.y = h;
754 }
755 }
756 returnCode = 0;
757 break;
758 #endif /* WM_GETMINMAXINFO */
759
760 case WM_WINDOWPOSCHANGING:
761
762 if (data->expected_resize) {
763 returnCode = 0;
764 }
765 break;
766
767 case WM_WINDOWPOSCHANGED:
768 {
769 RECT rect;
770 int x, y;
771 int w, h;
772
773 if (data->initializing || data->in_border_change) {
774 break;
775 }
776
777 if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) {
778 break;
779 }
780 ClientToScreen(hwnd, (LPPOINT) & rect);
781 ClientToScreen(hwnd, (LPPOINT) & rect + 1);
782
783 WIN_UpdateClipCursor(data->window);
784
785 x = rect.left;
786 y = rect.top;
787 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y);
788
789 w = rect.right - rect.left;
790 h = rect.bottom - rect.top;
791 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w,
792 h);
793
794 /* Forces a WM_PAINT event */
795 InvalidateRect(hwnd, NULL, FALSE);
796 }
797 break;
798
799 case WM_SIZE:
800 {
801 switch (wParam) {
802 case SIZE_MAXIMIZED:
803 SDL_SendWindowEvent(data->window,
804 SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
805 break;
806 case SIZE_MINIMIZED:
807 SDL_SendWindowEvent(data->window,
808 SDL_WINDOWEVENT_MINIMIZED, 0, 0);
809 break;
810 default:
811 SDL_SendWindowEvent(data->window,
812 SDL_WINDOWEVENT_RESTORED, 0, 0);
813 break;
814 }
815 }
816 break;
817
818 case WM_SETCURSOR:
819 {
820 Uint16 hittest;
821
822 hittest = LOWORD(lParam);
823 if (hittest == HTCLIENT) {
824 SetCursor(SDL_cursor);
825 returnCode = TRUE;
826 } else if (!g_WindowFrameUsableWhileCursorHidden && !SDL_cursor) {
827 SetCursor(NULL);
828 returnCode = TRUE;
829 }
830 }
831 break;
832
833 /* We were occluded, refresh our display */
834 case WM_PAINT:
835 {
836 RECT rect;
837 if (GetUpdateRect(hwnd, &rect, FALSE)) {
838 ValidateRect(hwnd, NULL);
839 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED,
840 0, 0);
841 }
842 }
843 returnCode = 0;
844 break;
845
846 /* We'll do our own drawing, prevent flicker */
847 case WM_ERASEBKGND:
848 {
849 }
850 return (1);
851
852 case WM_SYSCOMMAND:
853 {
854 if ((wParam & 0xFFF0) == SC_KEYMENU) {
855 return (0);
856 }
857
858 #if defined(SC_SCREENSAVE) || defined(SC_MONITORPOWER)
859 /* Don't start the screensaver or blank the monitor in fullscreen apps */
860 if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
861 (wParam & 0xFFF0) == SC_MONITORPOWER) {
862 if (SDL_GetVideoDevice()->suspend_screensaver) {
863 return (0);
864 }
865 }
866 #endif /* System has screensaver support */
867 }
868 break;
869
870 case WM_CLOSE:
871 {
872 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
873 }
874 returnCode = 0;
875 break;
876
877 case WM_TOUCH:
878 {
879 UINT i, num_inputs = LOWORD(wParam);
880 PTOUCHINPUT inputs = SDL_stack_alloc(TOUCHINPUT, num_inputs);
881 if (data->videodata->GetTouchInputInfo((HTOUCHINPUT)lParam, num_inputs, inputs, sizeof(TOUCHINPUT))) {
882 RECT rect;
883 float x, y;
884
885 if (!GetClientRect(hwnd, &rect) ||
886 (rect.right == rect.left && rect.bottom == rect.top)) {
887 if (inputs) {
888 SDL_stack_free(inputs);
889 }
890 break;
891 }
892 ClientToScreen(hwnd, (LPPOINT) & rect);
893 ClientToScreen(hwnd, (LPPOINT) & rect + 1);
894 rect.top *= 100;
895 rect.left *= 100;
896 rect.bottom *= 100;
897 rect.right *= 100;
898
899 for (i = 0; i < num_inputs; ++i) {
900 PTOUCHINPUT input = &inputs[i];
901
902 const SDL_TouchID touchId = (SDL_TouchID)((size_t)input->hSource);
903 if (SDL_AddTouch(touchId, "") < 0) {
904 continue;
905 }
906
907 /* Get the normalized coordinates for the window */
908 x = (float)(input->x - rect.left)/(rect.right - rect.left);
909 y = (float)(input->y - rect.top)/(rect.bottom - rect.top);
910
911 if (input->dwFlags & TOUCHEVENTF_DOWN) {
912 SDL_SendTouch(touchId, input->dwID, SDL_TRUE, x, y, 1.0f);
913 }
914 if (input->dwFlags & TOUCHEVENTF_MOVE) {
915 SDL_SendTouchMotion(touchId, input->dwID, x, y, 1.0f);
916 }
917 if (input->dwFlags & TOUCHEVENTF_UP) {
918 SDL_SendTouch(touchId, input->dwID, SDL_FALSE, x, y, 1.0f);
919 }
920 }
921 }
922 SDL_stack_free(inputs);
923
924 data->videodata->CloseTouchInputHandle((HTOUCHINPUT)lParam);
925 return 0;
926 }
927 break;
928
929 case WM_DROPFILES:
930 {
931 UINT i;
932 HDROP drop = (HDROP) wParam;
933 UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
934 for (i = 0; i < count; ++i) {
935 UINT size = DragQueryFile(drop, i, NULL, 0) + 1;
936 LPTSTR buffer = SDL_stack_alloc(TCHAR, size);
937 if (buffer) {
938 if (DragQueryFile(drop, i, buffer, size)) {
939 char *file = WIN_StringToUTF8(buffer);
940 SDL_SendDropFile(data->window, file);
941 SDL_free(file);
942 }
943 SDL_stack_free(buffer);
944 }
945 }
946 SDL_SendDropComplete(data->window);
947 DragFinish(drop);
948 return 0;
949 }
950 break;
951
952 case WM_NCHITTEST:
953 {
954 SDL_Window *window = data->window;
955 if (window->hit_test) {
956 POINT winpoint = { (int) LOWORD(lParam), (int) HIWORD(lParam) };
957 if (ScreenToClient(hwnd, &winpoint)) {
958 const SDL_Point point = { (int) winpoint.x, (int) winpoint.y };
959 const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
960 switch (rc) {
961 #define POST_HIT_TEST(ret) { SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); return ret; }
962 case SDL_HITTEST_DRAGGABLE: POST_HIT_TEST(HTCAPTION);
963 case SDL_HITTEST_RESIZE_TOPLEFT: POST_HIT_TEST(HTTOPLEFT);
964 case SDL_HITTEST_RESIZE_TOP: POST_HIT_TEST(HTTOP);
965 case SDL_HITTEST_RESIZE_TOPRIGHT: POST_HIT_TEST(HTTOPRIGHT);
966 case SDL_HITTEST_RESIZE_RIGHT: POST_HIT_TEST(HTRIGHT);
967 case SDL_HITTEST_RESIZE_BOTTOMRIGHT: POST_HIT_TEST(HTBOTTOMRIGHT);
968 case SDL_HITTEST_RESIZE_BOTTOM: POST_HIT_TEST(HTBOTTOM);
969 case SDL_HITTEST_RESIZE_BOTTOMLEFT: POST_HIT_TEST(HTBOTTOMLEFT);
970 case SDL_HITTEST_RESIZE_LEFT: POST_HIT_TEST(HTLEFT);
971 #undef POST_HIT_TEST
972 case SDL_HITTEST_NORMAL: return HTCLIENT;
973 }
974 }
975 /* If we didn't return, this will call DefWindowProc below. */
976 }
977 }
978 break;
979
980 }
981
982 /* If there's a window proc, assume it's going to handle messages */
983 if (data->wndproc) {
984 return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);
985 } else if (returnCode >= 0) {
986 return returnCode;
987 } else {
988 return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
989 }
990 }
991
992 /* A message hook called before TranslateMessage() */
993 static SDL_WindowsMessageHook g_WindowsMessageHook = NULL;
994 static void *g_WindowsMessageHookData = NULL;
995
SDL_SetWindowsMessageHook(SDL_WindowsMessageHook callback,void * userdata)996 void SDL_SetWindowsMessageHook(SDL_WindowsMessageHook callback, void *userdata)
997 {
998 g_WindowsMessageHook = callback;
999 g_WindowsMessageHookData = userdata;
1000 }
1001
1002 void
WIN_PumpEvents(_THIS)1003 WIN_PumpEvents(_THIS)
1004 {
1005 const Uint8 *keystate;
1006 MSG msg;
1007 DWORD start_ticks = GetTickCount();
1008
1009 if (g_WindowsEnableMessageLoop) {
1010 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1011 if (g_WindowsMessageHook) {
1012 g_WindowsMessageHook(g_WindowsMessageHookData, msg.hwnd, msg.message, msg.wParam, msg.lParam);
1013 }
1014
1015 /* Always translate the message in case it's a non-SDL window (e.g. with Qt integration) */
1016 TranslateMessage(&msg);
1017 DispatchMessage(&msg);
1018
1019 /* Make sure we don't busy loop here forever if there are lots of events coming in */
1020 if (SDL_TICKS_PASSED(msg.time, start_ticks)) {
1021 break;
1022 }
1023 }
1024 }
1025
1026 /* Windows loses a shift KEYUP event when you have both pressed at once and let go of one.
1027 You won't get a KEYUP until both are released, and that keyup will only be for the second
1028 key you released. Take heroic measures and check the keystate as of the last handled event,
1029 and if we think a key is pressed when Windows doesn't, unstick it in SDL's state. */
1030 keystate = SDL_GetKeyboardState(NULL);
1031 if ((keystate[SDL_SCANCODE_LSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_LSHIFT) & 0x8000)) {
1032 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
1033 }
1034 if ((keystate[SDL_SCANCODE_RSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_RSHIFT) & 0x8000)) {
1035 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RSHIFT);
1036 }
1037 }
1038
1039 static int app_registered = 0;
1040 LPTSTR SDL_Appname = NULL;
1041 Uint32 SDL_Appstyle = 0;
1042 HINSTANCE SDL_Instance = NULL;
1043
1044 /* Register the class for this application */
1045 int
SDL_RegisterApp(char * name,Uint32 style,void * hInst)1046 SDL_RegisterApp(char *name, Uint32 style, void *hInst)
1047 {
1048 WNDCLASSEX wcex;
1049 TCHAR path[MAX_PATH];
1050
1051 /* Only do this once... */
1052 if (app_registered) {
1053 ++app_registered;
1054 return (0);
1055 }
1056 if (!name && !SDL_Appname) {
1057 name = "SDL_app";
1058 #if defined(CS_BYTEALIGNCLIENT) || defined(CS_OWNDC)
1059 SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC);
1060 #endif
1061 SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
1062 }
1063
1064 if (name) {
1065 SDL_Appname = WIN_UTF8ToString(name);
1066 SDL_Appstyle = style;
1067 SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
1068 }
1069
1070 /* Register the application class */
1071 wcex.cbSize = sizeof(WNDCLASSEX);
1072 wcex.hCursor = NULL;
1073 wcex.hIcon = NULL;
1074 wcex.hIconSm = NULL;
1075 wcex.lpszMenuName = NULL;
1076 wcex.lpszClassName = SDL_Appname;
1077 wcex.style = SDL_Appstyle;
1078 wcex.hbrBackground = NULL;
1079 wcex.lpfnWndProc = WIN_WindowProc;
1080 wcex.hInstance = SDL_Instance;
1081 wcex.cbClsExtra = 0;
1082 wcex.cbWndExtra = 0;
1083
1084 /* Use the first icon as a default icon, like in the Explorer */
1085 GetModuleFileName(SDL_Instance, path, MAX_PATH);
1086 ExtractIconEx(path, 0, &wcex.hIcon, &wcex.hIconSm, 1);
1087
1088 if (!RegisterClassEx(&wcex)) {
1089 return SDL_SetError("Couldn't register application class");
1090 }
1091
1092 app_registered = 1;
1093 return 0;
1094 }
1095
1096 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
1097 void
SDL_UnregisterApp()1098 SDL_UnregisterApp()
1099 {
1100 WNDCLASSEX wcex;
1101
1102 /* SDL_RegisterApp might not have been called before */
1103 if (!app_registered) {
1104 return;
1105 }
1106 --app_registered;
1107 if (app_registered == 0) {
1108 /* Check for any registered window classes. */
1109 if (GetClassInfoEx(SDL_Instance, SDL_Appname, &wcex)) {
1110 UnregisterClass(SDL_Appname, SDL_Instance);
1111 if (wcex.hIcon) DestroyIcon(wcex.hIcon);
1112 if (wcex.hIconSm) DestroyIcon(wcex.hIconSm);
1113 }
1114 SDL_free(SDL_Appname);
1115 SDL_Appname = NULL;
1116 }
1117 }
1118
1119 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
1120
1121 /* vi: set ts=4 sw=4 expandtab: */
1122