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