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