src/video/win32/SDL_win32events.c
author Edgar Simo <bobbens@gmail.com>
Sun, 06 Jul 2008 17:06:37 +0000
branchgsoc2008_force_feedback
changeset 2498 ab567bd667bf
parent 2324 3202e4826c57
child 2710 44e49d3fa6cf
child 3767 abc8acb8e3d7
permissions -rw-r--r--
Fixed various mistakes in the doxygen.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include "SDL_win32video.h"
    25 #include "SDL_syswm.h"
    26 #include "SDL_vkeys.h"
    27 #include "../../events/SDL_events_c.h"
    28 
    29 /*#define WMMSG_DEBUG*/
    30 #ifdef WMMSG_DEBUG
    31 #include <stdio.h>
    32 #include "wmmsg.h"
    33 #endif
    34 
    35 /* Masks for processing the windows KEYDOWN and KEYUP messages */
    36 #define REPEATED_KEYMASK    (1<<30)
    37 #define EXTENDED_KEYMASK    (1<<24)
    38 
    39 #define VK_ENTER    10          /* Keypad Enter ... no VKEY defined? */
    40 
    41 /* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */
    42 #ifndef WM_XBUTTONDOWN
    43 #define WM_XBUTTONDOWN 0x020B
    44 #endif
    45 #ifndef WM_XBUTTONUP
    46 #define WM_XBUTTONUP 0x020C
    47 #endif
    48 #ifndef GET_XBUTTON_WPARAM
    49 #define GET_XBUTTON_WPARAM(w) (HIWORD(w))
    50 #endif
    51 
    52 static WPARAM
    53 RemapVKEY(WPARAM wParam, LPARAM lParam)
    54 {
    55     int i;
    56     BYTE scancode = (BYTE) ((lParam >> 16) & 0xFF);
    57 
    58     /* Windows remaps alphabetic keys based on current layout.
    59        We try to provide USB scancodes, so undo this mapping.
    60      */
    61     if (wParam >= 'A' && wParam <= 'Z') {
    62         if (scancode != alpha_scancodes[wParam - 'A']) {
    63             for (i = 0; i < SDL_arraysize(alpha_scancodes); ++i) {
    64                 if (scancode == alpha_scancodes[i]) {
    65                     wParam = 'A' + i;
    66                     break;
    67                 }
    68             }
    69         }
    70     }
    71 
    72     /* Keypad keys are a little trickier, we always scan for them. */
    73     for (i = 0; i < SDL_arraysize(keypad_scancodes); ++i) {
    74         if (scancode == keypad_scancodes[i]) {
    75             wParam = VK_NUMPAD0 + i;
    76             break;
    77         }
    78     }
    79 
    80     return wParam;
    81 }
    82 
    83 LRESULT CALLBACK
    84 WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    85 {
    86     SDL_WindowData *data;
    87 
    88     /* Send a SDL_SYSWMEVENT if the application wants them */
    89     if (SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE) {
    90         SDL_SysWMmsg wmmsg;
    91 
    92         SDL_VERSION(&wmmsg.version);
    93         wmmsg.hwnd = hwnd;
    94         wmmsg.msg = msg;
    95         wmmsg.wParam = wParam;
    96         wmmsg.lParam = lParam;
    97         SDL_SendSysWMEvent(&wmmsg);
    98     }
    99 
   100     /* Get the window data for the window */
   101     data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData"));
   102     if (!data) {
   103         return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
   104     }
   105 #ifdef WMMSG_DEBUG
   106     {
   107         FILE *log = fopen("wmmsg.txt", "a");
   108         fprintf(log, "Received windows message: %p ", hwnd);
   109         if (msg > MAX_WMMSG) {
   110             fprintf(log, "%d", msg);
   111         } else {
   112             fprintf(log, "%s", wmtab[msg]);
   113         }
   114         fprintf(log, " -- 0x%X, 0x%X\n", wParam, lParam);
   115         fclose(log);
   116     }
   117 #endif
   118 
   119     switch (msg) {
   120 
   121     case WM_SHOWWINDOW:
   122         {
   123             if (wParam) {
   124                 SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_SHOWN, 0,
   125                                     0);
   126             } else {
   127                 SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_HIDDEN, 0,
   128                                     0);
   129             }
   130         }
   131         break;
   132 
   133     case WM_ACTIVATE:
   134         {
   135             int index;
   136             SDL_Keyboard *keyboard;
   137             BOOL minimized;
   138 
   139             minimized = HIWORD(wParam);
   140             index = data->videodata->keyboard;
   141             keyboard = SDL_GetKeyboard(index);
   142             if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) {
   143                 SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_SHOWN,
   144                                     0, 0);
   145                 SDL_SendWindowEvent(data->windowID,
   146                                     SDL_WINDOWEVENT_RESTORED, 0, 0);
   147                 if (IsZoomed(hwnd)) {
   148                     SDL_SendWindowEvent(data->windowID,
   149                                         SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   150                 }
   151                 if (keyboard && keyboard->focus != data->windowID) {
   152                     SDL_SetKeyboardFocus(index, data->windowID);
   153                 }
   154                 /* FIXME: Update keyboard state */
   155             } else {
   156                 if (keyboard && keyboard->focus == data->windowID) {
   157                     SDL_SetKeyboardFocus(index, 0);
   158                 }
   159                 if (minimized) {
   160                     SDL_SendWindowEvent(data->windowID,
   161                                         SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   162                 }
   163             }
   164             return (0);
   165         }
   166         break;
   167 
   168     case WM_MOUSEMOVE:
   169         {
   170             int index;
   171             SDL_Mouse *mouse;
   172             int x, y;
   173 
   174             index = data->videodata->mouse;
   175             mouse = SDL_GetMouse(index);
   176 
   177             if (mouse->focus != data->windowID) {
   178                 TRACKMOUSEEVENT tme;
   179 
   180                 tme.cbSize = sizeof(tme);
   181                 tme.dwFlags = TME_LEAVE;
   182                 tme.hwndTrack = hwnd;
   183                 TrackMouseEvent(&tme);
   184 
   185                 SDL_SetMouseFocus(index, data->windowID);
   186             }
   187 
   188             /* mouse has moved within the window */
   189             x = LOWORD(lParam);
   190             y = HIWORD(lParam);
   191             if (mouse->relative_mode) {
   192                 int w, h;
   193                 POINT center;
   194                 SDL_GetWindowSize(data->windowID, &w, &h);
   195                 center.x = (w / 2);
   196                 center.y = (h / 2);
   197                 x -= center.x;
   198                 y -= center.y;
   199                 if (x || y) {
   200                     ClientToScreen(hwnd, &center);
   201                     SetCursorPos(center.x, center.y);
   202                     SDL_SendMouseMotion(index, 1, x, y);
   203                 }
   204             } else {
   205                 SDL_SendMouseMotion(index, 0, x, y);
   206             }
   207         }
   208         return (0);
   209 
   210     case WM_MOUSELEAVE:
   211         {
   212             int index;
   213             SDL_Mouse *mouse;
   214 
   215             index = data->videodata->mouse;
   216             mouse = SDL_GetMouse(index);
   217 
   218             if (mouse->focus == data->windowID) {
   219                 SDL_SetMouseFocus(index, 0);
   220             }
   221         }
   222         return (0);
   223 
   224     case WM_LBUTTONDOWN:
   225     case WM_LBUTTONUP:
   226     case WM_MBUTTONDOWN:
   227     case WM_MBUTTONUP:
   228     case WM_RBUTTONDOWN:
   229     case WM_RBUTTONUP:
   230     case WM_XBUTTONDOWN:
   231     case WM_XBUTTONUP:
   232         {
   233             int xbuttonval = 0;
   234             int index;
   235             SDL_Mouse *mouse;
   236             Uint8 button, state;
   237 
   238             /* DJM:
   239                We want the SDL window to take focus so that
   240                it acts like a normal windows "component"
   241                (e.g. gains keyboard focus on a mouse click).
   242              */
   243             SetFocus(hwnd);
   244 
   245             index = data->videodata->mouse;
   246             mouse = SDL_GetMouse(index);
   247 
   248             /* Figure out which button to use */
   249             switch (msg) {
   250             case WM_LBUTTONDOWN:
   251                 button = SDL_BUTTON_LEFT;
   252                 state = SDL_PRESSED;
   253                 break;
   254             case WM_LBUTTONUP:
   255                 button = SDL_BUTTON_LEFT;
   256                 state = SDL_RELEASED;
   257                 break;
   258             case WM_MBUTTONDOWN:
   259                 button = SDL_BUTTON_MIDDLE;
   260                 state = SDL_PRESSED;
   261                 break;
   262             case WM_MBUTTONUP:
   263                 button = SDL_BUTTON_MIDDLE;
   264                 state = SDL_RELEASED;
   265                 break;
   266             case WM_RBUTTONDOWN:
   267                 button = SDL_BUTTON_RIGHT;
   268                 state = SDL_PRESSED;
   269                 break;
   270             case WM_RBUTTONUP:
   271                 button = SDL_BUTTON_RIGHT;
   272                 state = SDL_RELEASED;
   273                 break;
   274             case WM_XBUTTONDOWN:
   275                 xbuttonval = GET_XBUTTON_WPARAM(wParam);
   276                 button = SDL_BUTTON_X1 + xbuttonval - 1;
   277                 state = SDL_PRESSED;
   278                 break;
   279             case WM_XBUTTONUP:
   280                 xbuttonval = GET_XBUTTON_WPARAM(wParam);
   281                 button = SDL_BUTTON_X1 + xbuttonval - 1;
   282                 state = SDL_RELEASED;
   283                 break;
   284             default:
   285                 /* Eh? Unknown button? */
   286                 return (0);
   287             }
   288             if (state == SDL_PRESSED) {
   289                 /* Grab mouse so we get up events */
   290                 if (++data->mouse_pressed > 0) {
   291                     SetCapture(hwnd);
   292                 }
   293             } else {
   294                 /* Release mouse after all up events */
   295                 if (--data->mouse_pressed <= 0) {
   296                     ReleaseCapture();
   297                     data->mouse_pressed = 0;
   298                 }
   299             }
   300 
   301             if (!mouse->relative_mode) {
   302                 int x, y;
   303                 x = LOWORD(lParam);
   304                 y = HIWORD(lParam);
   305                 SDL_SendMouseMotion(index, 0, x, y);
   306             }
   307             SDL_SendMouseButton(index, state, button);
   308 
   309             /*
   310              * MSDN says:
   311              *  "Unlike the WM_LBUTTONUP, WM_MBUTTONUP, and WM_RBUTTONUP
   312              *   messages, an application should return TRUE from [an
   313              *   XBUTTON message] if it processes it. Doing so will allow
   314              *   software that simulates this message on Microsoft Windows
   315              *   systems earlier than Windows 2000 to determine whether
   316              *   the window procedure processed the message or called
   317              *   DefWindowProc to process it.
   318              */
   319             if (xbuttonval > 0) {
   320                 return (TRUE);
   321             }
   322         }
   323         return (0);
   324 
   325     case WM_MOUSEWHEEL:
   326         {
   327             int index;
   328             int motion = (short) HIWORD(wParam);
   329 
   330             index = data->videodata->mouse;
   331             SDL_SendMouseWheel(index, 0, motion);
   332         }
   333         return (0);
   334 
   335     case WM_SYSKEYDOWN:
   336     case WM_KEYDOWN:
   337         {
   338             int index;
   339 
   340             /* Ignore repeated keys */
   341             if (lParam & REPEATED_KEYMASK) {
   342                 return (0);
   343             }
   344 
   345             index = data->videodata->keyboard;
   346             wParam = RemapVKEY(wParam, lParam);
   347             switch (wParam) {
   348             case VK_CONTROL:
   349                 if (lParam & EXTENDED_KEYMASK)
   350                     wParam = VK_RCONTROL;
   351                 else
   352                     wParam = VK_LCONTROL;
   353                 break;
   354             case VK_SHIFT:
   355                 /* EXTENDED trick doesn't work here */
   356                 {
   357                     Uint8 *state = SDL_GetKeyboardState(NULL);
   358                     if (state[SDL_SCANCODE_LSHIFT] == SDL_RELEASED
   359                         && (GetKeyState(VK_LSHIFT) & 0x8000)) {
   360                         wParam = VK_LSHIFT;
   361                     } else if (state[SDL_SCANCODE_RSHIFT] == SDL_RELEASED
   362                                && (GetKeyState(VK_RSHIFT) & 0x8000)) {
   363                         wParam = VK_RSHIFT;
   364                     } else {
   365                         /* Probably a key repeat */
   366                         return (0);
   367                     }
   368                 }
   369                 break;
   370             case VK_MENU:
   371                 if (lParam & EXTENDED_KEYMASK)
   372                     wParam = VK_RMENU;
   373                 else
   374                     wParam = VK_LMENU;
   375                 break;
   376             case VK_RETURN:
   377                 if (lParam & EXTENDED_KEYMASK)
   378                     wParam = VK_ENTER;
   379                 break;
   380             }
   381             if (wParam < 256) {
   382                 SDL_SendKeyboardKey(index, SDL_PRESSED,
   383                                     data->videodata->key_layout[wParam]);
   384             }
   385         }
   386         return (0);
   387 
   388     case WM_SYSKEYUP:
   389     case WM_KEYUP:
   390         {
   391             int index;
   392 
   393             index = data->videodata->keyboard;
   394             wParam = RemapVKEY(wParam, lParam);
   395             switch (wParam) {
   396             case VK_CONTROL:
   397                 if (lParam & EXTENDED_KEYMASK)
   398                     wParam = VK_RCONTROL;
   399                 else
   400                     wParam = VK_LCONTROL;
   401                 break;
   402             case VK_SHIFT:
   403                 /* EXTENDED trick doesn't work here */
   404                 {
   405                     Uint8 *state = SDL_GetKeyboardState(NULL);
   406                     if (state[SDL_SCANCODE_LSHIFT] == SDL_PRESSED
   407                         && !(GetKeyState(VK_LSHIFT) & 0x8000)) {
   408                         wParam = VK_LSHIFT;
   409                     } else if (state[SDL_SCANCODE_RSHIFT] == SDL_PRESSED
   410                                && !(GetKeyState(VK_RSHIFT) & 0x8000)) {
   411                         wParam = VK_RSHIFT;
   412                     } else {
   413                         /* Probably a key repeat */
   414                         return (0);
   415                     }
   416                 }
   417                 break;
   418             case VK_MENU:
   419                 if (lParam & EXTENDED_KEYMASK)
   420                     wParam = VK_RMENU;
   421                 else
   422                     wParam = VK_LMENU;
   423                 break;
   424             case VK_RETURN:
   425                 if (lParam & EXTENDED_KEYMASK)
   426                     wParam = VK_ENTER;
   427                 break;
   428             }
   429             /* Windows only reports keyup for print screen */
   430             if (wParam == VK_SNAPSHOT
   431                 && SDL_GetKeyboardState(NULL)[SDL_SCANCODE_PRINTSCREEN] ==
   432                 SDL_RELEASED) {
   433                 SDL_SendKeyboardKey(index, SDL_PRESSED,
   434                                     data->videodata->key_layout[wParam]);
   435             }
   436             if (wParam < 256) {
   437                 SDL_SendKeyboardKey(index, SDL_RELEASED,
   438                                     data->videodata->key_layout[wParam]);
   439             }
   440         }
   441         return (0);
   442 
   443     case WM_CHAR:
   444         {
   445             char text[4];
   446 
   447             /* Convert to UTF-8 and send it on... */
   448             if (wParam <= 0x7F) {
   449                 text[0] = (char) wParam;
   450                 text[1] = '\0';
   451             } else if (wParam <= 0x7FF) {
   452                 text[0] = 0xC0 | (char) ((wParam >> 6) & 0x1F);
   453                 text[1] = 0x80 | (char) (wParam & 0x3F);
   454                 text[2] = '\0';
   455             } else {
   456                 text[0] = 0xE0 | (char) ((wParam >> 12) & 0x0F);
   457                 text[1] = 0x80 | (char) ((wParam >> 6) & 0x3F);
   458                 text[2] = 0x80 | (char) (wParam & 0x3F);
   459                 text[3] = '\0';
   460             }
   461             SDL_SendKeyboardText(data->videodata->keyboard, text);
   462         }
   463         return (0);
   464 
   465     case WM_INPUTLANGCHANGE:
   466         {
   467             WIN_UpdateKeymap(data->videodata->keyboard);
   468         }
   469         return (1);
   470 
   471     case WM_GETMINMAXINFO:
   472         {
   473             MINMAXINFO *info;
   474             RECT size;
   475             int x, y;
   476             int w, h;
   477             int style;
   478 
   479             /* If we allow resizing, let the resize happen naturally */
   480             if (SDL_GetWindowFlags(data->windowID) & SDL_WINDOW_RESIZABLE) {
   481                 return (0);
   482             }
   483 
   484             /* Get the current position of our window */
   485             GetWindowRect(hwnd, &size);
   486             x = size.left;
   487             y = size.top;
   488 
   489             /* Calculate current size of our window */
   490             SDL_GetWindowSize(data->windowID, &w, &h);
   491             size.top = 0;
   492             size.left = 0;
   493             size.bottom = h;
   494             size.right = w;
   495 
   496             /* DJM - according to the docs for GetMenu(), the
   497                return value is undefined if hwnd is a child window.
   498                Aparently it's too difficult for MS to check
   499                inside their function, so I have to do it here.
   500              */
   501             style = GetWindowLong(hwnd, GWL_STYLE);
   502             AdjustWindowRect(&size,
   503                              style,
   504                              style & WS_CHILDWINDOW ? FALSE : GetMenu(hwnd) !=
   505                              NULL);
   506 
   507             w = size.right - size.left;
   508             h = size.bottom - size.top;
   509 
   510             /* Fix our size to the current size */
   511             info = (MINMAXINFO *) lParam;
   512             info->ptMaxSize.x = w;
   513             info->ptMaxSize.y = h;
   514             info->ptMaxPosition.x = x;
   515             info->ptMaxPosition.y = y;
   516             info->ptMinTrackSize.x = w;
   517             info->ptMinTrackSize.y = h;
   518             info->ptMaxTrackSize.x = w;
   519             info->ptMaxTrackSize.y = h;
   520         }
   521         return (0);
   522 
   523     case WM_WINDOWPOSCHANGED:
   524         {
   525             RECT rect;
   526             int x, y;
   527             int w, h;
   528             Uint32 window_flags;
   529 
   530             GetClientRect(hwnd, &rect);
   531             ClientToScreen(hwnd, (LPPOINT) & rect);
   532             ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   533 
   534             window_flags = SDL_GetWindowFlags(data->windowID);
   535             if ((window_flags & SDL_WINDOW_INPUT_GRABBED) &&
   536                 (window_flags & SDL_WINDOW_INPUT_FOCUS)) {
   537                 ClipCursor(&rect);
   538             }
   539 
   540             x = rect.left;
   541             y = rect.top;
   542             SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_MOVED, x, y);
   543 
   544             w = rect.right - rect.left;
   545             h = rect.bottom - rect.top;
   546             SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_RESIZED, w,
   547                                 h);
   548         }
   549         break;
   550 
   551     case WM_SETCURSOR:
   552         {
   553             /*
   554                Uint16 hittest;
   555 
   556                hittest = LOWORD(lParam);
   557                if (hittest == HTCLIENT) {
   558                SetCursor(SDL_hcursor);
   559                return (TRUE);
   560                }
   561              */
   562         }
   563         break;
   564 
   565         /* We are about to get palette focus! */
   566     case WM_QUERYNEWPALETTE:
   567         {
   568             /*
   569                WIN_RealizePalette(current_video);
   570                return (TRUE);
   571              */
   572         }
   573         break;
   574 
   575         /* Another application changed the palette */
   576     case WM_PALETTECHANGED:
   577         {
   578             /*
   579                WIN_PaletteChanged(current_video, (HWND) wParam);
   580              */
   581         }
   582         break;
   583 
   584         /* We were occluded, refresh our display */
   585     case WM_PAINT:
   586         {
   587             RECT rect;
   588             if (GetUpdateRect(hwnd, &rect, FALSE)) {
   589                 ValidateRect(hwnd, &rect);
   590                 SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_EXPOSED,
   591                                     0, 0);
   592             }
   593         }
   594         return (0);
   595 
   596         /* We'll do our own drawing, prevent flicker */
   597     case WM_ERASEBKGND:
   598         {
   599         }
   600         return (1);
   601 
   602     case WM_SYSCOMMAND:
   603         {
   604             /* Don't start the screensaver or blank the monitor in fullscreen apps */
   605             if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
   606                 (wParam & 0xFFF0) == SC_MONITORPOWER) {
   607                 if (SDL_GetWindowFlags(data->windowID) &
   608                     SDL_WINDOW_FULLSCREEN) {
   609                     return (0);
   610                 }
   611             }
   612         }
   613         break;
   614 
   615     case WM_CLOSE:
   616         {
   617             SDL_SendWindowEvent(data->windowID, SDL_WINDOWEVENT_CLOSE, 0, 0);
   618         }
   619         return (0);
   620     }
   621     return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);
   622 }
   623 
   624 void
   625 WIN_PumpEvents(_THIS)
   626 {
   627     MSG msg;
   628     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
   629         TranslateMessage(&msg);
   630         DispatchMessage(&msg);
   631     }
   632 }
   633 
   634 static int app_registered = 0;
   635 LPTSTR SDL_Appname = NULL;
   636 Uint32 SDL_Appstyle = 0;
   637 HINSTANCE SDL_Instance = NULL;
   638 
   639 /* Register the class for this application */
   640 int
   641 SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   642 {
   643     WNDCLASS class;
   644 
   645     /* Only do this once... */
   646     if (app_registered) {
   647         ++app_registered;
   648         return (0);
   649     }
   650     if (!name && !SDL_Appname) {
   651         name = "SDL_app";
   652         SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC);
   653         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   654     }
   655 
   656     if (name) {
   657         SDL_Appname = WIN_UTF8ToString(name);
   658         SDL_Appstyle = style;
   659         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   660     }
   661 
   662     /* Register the application class */
   663     class.hCursor = NULL;
   664     class.hIcon = LoadImage(SDL_Instance, SDL_Appname,
   665                             IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
   666     class.lpszMenuName = NULL;
   667     class.lpszClassName = SDL_Appname;
   668     class.hbrBackground = NULL;
   669     class.hInstance = SDL_Instance;
   670     class.style = SDL_Appstyle;
   671     class.lpfnWndProc = DefWindowProc;
   672     class.cbWndExtra = 0;
   673     class.cbClsExtra = 0;
   674     if (!RegisterClass(&class)) {
   675         SDL_SetError("Couldn't register application class");
   676         return (-1);
   677     }
   678 
   679     app_registered = 1;
   680     return (0);
   681 }
   682 
   683 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
   684 void
   685 SDL_UnregisterApp()
   686 {
   687     WNDCLASS class;
   688 
   689     /* SDL_RegisterApp might not have been called before */
   690     if (!app_registered) {
   691         return;
   692     }
   693     --app_registered;
   694     if (app_registered == 0) {
   695         /* Check for any registered window classes. */
   696         if (GetClassInfo(SDL_Instance, SDL_Appname, &class)) {
   697             UnregisterClass(SDL_Appname, SDL_Instance);
   698         }
   699         SDL_free(SDL_Appname);
   700         SDL_Appname = NULL;
   701     }
   702 }
   703 
   704 /* Sets an error message based on GetLastError() */
   705 void
   706 WIN_SetError(const char *prefix)
   707 {
   708     TCHAR buffer[1024];
   709     char *message;
   710 
   711     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
   712                   NULL,
   713                   GetLastError(), 0, buffer, SDL_arraysize(buffer), NULL);
   714 
   715     message = WIN_StringToUTF8(buffer);
   716     SDL_SetError("%s%s%s", prefix ? prefix : "", prefix ? ":" : "", message);
   717     SDL_free(message);
   718 }
   719 
   720 /* vi: set ts=4 sw=4 expandtab: */