src/video/win32/SDL_win32events.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 29 Sep 2010 21:13:52 -0700
changeset 4902 50d0bff24d81
parent 4900 69d9db65f248
child 4919 716b2cbf4c9e
permissions -rw-r--r--
Make the union nameless to reduce the complexity of the API.
(Are there any compilers still in use that don't support this?)
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 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 
    23 #include "SDL_config.h"
    24 
    25 #include "SDL_win32video.h"
    26 #include "SDL_win32shape.h"
    27 #include "SDL_syswm.h"
    28 #include "SDL_vkeys.h"
    29 #include "../../events/SDL_events_c.h"
    30 
    31 
    32 
    33 /*#define WMMSG_DEBUG*/
    34 #ifdef WMMSG_DEBUG
    35 #include <stdio.h>	
    36 #include "wmmsg.h"
    37 #endif
    38 
    39 /* Masks for processing the windows KEYDOWN and KEYUP messages */
    40 #define REPEATED_KEYMASK    (1<<30)
    41 #define EXTENDED_KEYMASK    (1<<24)
    42 
    43 #define VK_ENTER    10          /* Keypad Enter ... no VKEY defined? */
    44 
    45 /* Make sure XBUTTON stuff is defined that isn't in older Platform SDKs... */
    46 #ifndef WM_XBUTTONDOWN
    47 #define WM_XBUTTONDOWN 0x020B
    48 #endif
    49 #ifndef WM_XBUTTONUP
    50 #define WM_XBUTTONUP 0x020C
    51 #endif
    52 #ifndef GET_XBUTTON_WPARAM
    53 #define GET_XBUTTON_WPARAM(w) (HIWORD(w))
    54 #endif
    55 #ifndef WM_INPUT
    56 #define WM_INPUT 0x00ff
    57 #endif
    58 #ifndef WM_GESTURE
    59 #define WM_GESTURE 0x0119
    60 #endif
    61 #ifndef WM_TOUCH
    62 #define WM_TOUCH 0x0240
    63 #endif
    64 
    65 static WPARAM
    66 RemapVKEY(WPARAM wParam, LPARAM lParam)
    67 {
    68     int i;
    69     BYTE scancode = (BYTE) ((lParam >> 16) & 0xFF);
    70 
    71     /* Windows remaps alphabetic keys based on current layout.
    72        We try to provide USB scancodes, so undo this mapping.
    73      */
    74     if (wParam >= 'A' && wParam <= 'Z') {
    75         if (scancode != alpha_scancodes[wParam - 'A']) {
    76             for (i = 0; i < SDL_arraysize(alpha_scancodes); ++i) {
    77                 if (scancode == alpha_scancodes[i]) {
    78                     wParam = 'A' + i;
    79                     break;
    80                 }
    81             }
    82         }
    83     }
    84 
    85     /* Keypad keys are a little trickier, we always scan for them.
    86        Keypad arrow keys have the same scancode as normal arrow keys,
    87        except they don't have the extended bit (0x1000000) set.
    88      */
    89     if (!(lParam & 0x1000000)) {
    90         if (wParam == VK_DELETE) {
    91             wParam = VK_DECIMAL;
    92         } else {
    93             for (i = 0; i < SDL_arraysize(keypad_scancodes); ++i) {
    94                 if (scancode == keypad_scancodes[i]) {
    95                     wParam = VK_NUMPAD0 + i;
    96                     break;
    97                 }
    98             }
    99         }
   100     }
   101 
   102     return wParam;
   103 }
   104 
   105 LRESULT CALLBACK
   106 WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   107 {
   108     SDL_WindowData *data;
   109     LRESULT returnCode = -1;
   110 
   111     /* Send a SDL_SYSWMEVENT if the application wants them */
   112     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   113         SDL_SysWMmsg wmmsg;
   114 
   115         SDL_VERSION(&wmmsg.version);
   116         wmmsg.subsystem = SDL_SYSWM_WINDOWS;
   117         wmmsg.win.hwnd = hwnd;
   118         wmmsg.win.msg = msg;
   119         wmmsg.win.wParam = wParam;
   120         wmmsg.win.lParam = lParam;
   121         SDL_SendSysWMEvent(&wmmsg);
   122     }
   123 
   124     /* Get the window data for the window */
   125     data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData"));
   126     if (!data) {
   127         return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
   128     }
   129 
   130 #ifdef WMMSG_DEBUG
   131     {		
   132         FILE *log = fopen("wmmsg.txt", "a");		
   133         fprintf(log, "Received windows message: %p ", hwnd);
   134         if (msg > MAX_WMMSG) {
   135             fprintf(log, "%d", msg);
   136         } else {
   137             fprintf(log, "%s", wmtab[msg]);
   138         }
   139         fprintf(log, " -- 0x%X, 0x%X\n", wParam, lParam);
   140         fclose(log);
   141     }
   142 #endif
   143 
   144     if (IME_HandleMessage(hwnd, msg, wParam, &lParam, data->videodata))
   145         return 0;
   146 
   147     switch (msg) {
   148 
   149     case WM_SHOWWINDOW:
   150         {
   151             if (wParam) {
   152                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   153             } else {
   154                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   155             }
   156         }
   157         break;
   158 
   159     case WM_ACTIVATE:
   160         {
   161             BOOL minimized;
   162 
   163             minimized = HIWORD(wParam);
   164             if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) {
   165                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   166                 SDL_SendWindowEvent(data->window,
   167                                     SDL_WINDOWEVENT_RESTORED, 0, 0);
   168 #ifndef _WIN32_WCE              /* WinCE misses IsZoomed() */
   169                 if (IsZoomed(hwnd)) {
   170                     SDL_SendWindowEvent(data->window,
   171                                         SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   172                 }
   173 #endif
   174                 if (SDL_GetKeyboardFocus() != data->window) {
   175                     SDL_SetKeyboardFocus(data->window);
   176                 }
   177                 /*
   178                  * FIXME: Update keyboard state
   179                  */
   180                 WIN_CheckClipboardUpdate(data->videodata);
   181             } else {
   182                 if (SDL_GetKeyboardFocus() == data->window) {
   183                     SDL_SetKeyboardFocus(NULL);
   184                 }
   185                 if (minimized) {
   186                     SDL_SendWindowEvent(data->window,
   187                                         SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   188                 }
   189             }
   190         }
   191         returnCode = 0;
   192         break;
   193 
   194 	case WM_MOUSEMOVE:
   195 #ifdef _WIN32_WCE
   196 	/* transform coords for VGA, WVGA... */
   197 	{
   198 	    SDL_VideoData *videodata = data->videodata;
   199 	    if(videodata->CoordTransform &&
   200 		(videodata->render == RENDER_GAPI || videodata->render == RENDER_RAW))
   201 	    {
   202 		POINT pt;
   203 		pt.x = LOWORD(lParam);
   204 		pt.y = HIWORD(lParam);
   205 		videodata->CoordTransform(data->window, &pt);
   206     		SDL_SendMouseMotion(data->window, 0, pt.x, pt.y);
   207 		break;
   208 	    }
   209 	}
   210 #endif
   211         SDL_SendMouseMotion(data->window, 0, LOWORD(lParam), HIWORD(lParam));
   212         break;
   213 
   214     case WM_LBUTTONDOWN:
   215         SDL_SendMouseButton(data->window, SDL_PRESSED, SDL_BUTTON_LEFT);
   216         break;
   217 
   218     case WM_LBUTTONUP:
   219         SDL_SendMouseButton(data->window, SDL_RELEASED, SDL_BUTTON_LEFT);
   220         break;
   221 
   222     case WM_MOUSELEAVE:
   223         if (SDL_GetMouseFocus() == data->window) {
   224             SDL_SetMouseFocus(NULL);
   225         }
   226         returnCode = 0;
   227         break;
   228 
   229     case WM_SYSKEYDOWN:
   230     case WM_KEYDOWN:
   231         {
   232             wParam = RemapVKEY(wParam, lParam);
   233             switch (wParam) {
   234             case VK_CONTROL:
   235                 if (lParam & EXTENDED_KEYMASK)
   236                     wParam = VK_RCONTROL;
   237                 else
   238                     wParam = VK_LCONTROL;
   239                 break;
   240             case VK_SHIFT:
   241                 /* EXTENDED trick doesn't work here */
   242                 {
   243                     Uint8 *state = SDL_GetKeyboardState(NULL);
   244                     if (state[SDL_SCANCODE_LSHIFT] == SDL_RELEASED
   245                         && (GetKeyState(VK_LSHIFT) & 0x8000)) {
   246                         wParam = VK_LSHIFT;
   247                     } else if (state[SDL_SCANCODE_RSHIFT] == SDL_RELEASED
   248                                && (GetKeyState(VK_RSHIFT) & 0x8000)) {
   249                         wParam = VK_RSHIFT;
   250                     } else {
   251                         /* Probably a key repeat */
   252                         wParam = 256;
   253                     }
   254                 }
   255                 break;
   256             case VK_MENU:
   257                 if (lParam & EXTENDED_KEYMASK)
   258                     wParam = VK_RMENU;
   259                 else
   260                     wParam = VK_LMENU;
   261                 break;
   262             case VK_RETURN:
   263                 if (lParam & EXTENDED_KEYMASK)
   264                     wParam = VK_ENTER;
   265                 break;
   266             }
   267             if (wParam < 256) {
   268                 SDL_SendKeyboardKey(SDL_PRESSED,
   269                                     data->videodata->key_layout[wParam]);
   270             }
   271         }
   272         returnCode = 0;
   273         break;
   274 
   275     case WM_SYSKEYUP:
   276     case WM_KEYUP:
   277         {
   278             wParam = RemapVKEY(wParam, lParam);
   279             switch (wParam) {
   280             case VK_CONTROL:
   281                 if (lParam & EXTENDED_KEYMASK)
   282                     wParam = VK_RCONTROL;
   283                 else
   284                     wParam = VK_LCONTROL;
   285                 break;
   286             case VK_SHIFT:
   287                 /* EXTENDED trick doesn't work here */
   288                 {
   289                     Uint8 *state = SDL_GetKeyboardState(NULL);
   290                     if (state[SDL_SCANCODE_LSHIFT] == SDL_PRESSED
   291                         && !(GetKeyState(VK_LSHIFT) & 0x8000)) {
   292                         wParam = VK_LSHIFT;
   293                     } else if (state[SDL_SCANCODE_RSHIFT] == SDL_PRESSED
   294                                && !(GetKeyState(VK_RSHIFT) & 0x8000)) {
   295                         wParam = VK_RSHIFT;
   296                     } else {
   297                         /* Probably a key repeat */
   298                         wParam = 256;
   299                     }
   300                 }
   301                 break;
   302             case VK_MENU:
   303                 if (lParam & EXTENDED_KEYMASK)
   304                     wParam = VK_RMENU;
   305                 else
   306                     wParam = VK_LMENU;
   307                 break;
   308             case VK_RETURN:
   309                 if (lParam & EXTENDED_KEYMASK)
   310                     wParam = VK_ENTER;
   311                 break;
   312             }
   313 
   314             /* Windows only reports keyup for print screen */
   315             if (wParam == VK_SNAPSHOT
   316                 && SDL_GetKeyboardState(NULL)[SDL_SCANCODE_PRINTSCREEN] ==
   317                 SDL_RELEASED) {
   318                 SDL_SendKeyboardKey(SDL_PRESSED,
   319                                     data->videodata->key_layout[wParam]);
   320             }
   321             if (wParam < 256) {
   322                 SDL_SendKeyboardKey(SDL_RELEASED,
   323                                     data->videodata->key_layout[wParam]);
   324             }
   325         }
   326         returnCode = 0;
   327         break;
   328 
   329     case WM_CHAR:
   330         {
   331             char text[4];
   332 
   333             /* Convert to UTF-8 and send it on... */
   334             if (wParam <= 0x7F) {
   335                 text[0] = (char) wParam;
   336                 text[1] = '\0';
   337             } else if (wParam <= 0x7FF) {
   338                 text[0] = 0xC0 | (char) ((wParam >> 6) & 0x1F);
   339                 text[1] = 0x80 | (char) (wParam & 0x3F);
   340                 text[2] = '\0';
   341             } else {
   342                 text[0] = 0xE0 | (char) ((wParam >> 12) & 0x0F);
   343                 text[1] = 0x80 | (char) ((wParam >> 6) & 0x3F);
   344                 text[2] = 0x80 | (char) (wParam & 0x3F);
   345                 text[3] = '\0';
   346             }
   347             SDL_SendKeyboardText(text);
   348         }
   349         returnCode = 0;
   350         break;
   351 
   352     case WM_INPUTLANGCHANGE:
   353         {
   354             WIN_UpdateKeymap();
   355         }
   356         returnCode = 1;
   357         break;
   358 
   359     case WM_GETMINMAXINFO:
   360         {
   361             MINMAXINFO *info;
   362             RECT size;
   363             int x, y;
   364             int w, h;
   365             int style;
   366             BOOL menu;
   367 
   368             /* If we allow resizing, let the resize happen naturally */
   369             if(SDL_IsShapedWindow(data->window))
   370                 Win32_ResizeWindowShape(data->window);
   371             if (SDL_GetWindowFlags(data->window) & SDL_WINDOW_RESIZABLE) {
   372                 returnCode = 0;
   373                 break;
   374             }
   375 
   376             /* Get the current position of our window */
   377             GetWindowRect(hwnd, &size);
   378             x = size.left;
   379             y = size.top;
   380 
   381             /* Calculate current size of our window */
   382             SDL_GetWindowSize(data->window, &w, &h);
   383             size.top = 0;
   384             size.left = 0;
   385             size.bottom = h;
   386             size.right = w;
   387 
   388 
   389             style = GetWindowLong(hwnd, GWL_STYLE);
   390 #ifdef _WIN32_WCE
   391             menu = FALSE;
   392 #else
   393             /* DJM - according to the docs for GetMenu(), the
   394                return value is undefined if hwnd is a child window.
   395                Aparently it's too difficult for MS to check
   396                inside their function, so I have to do it here.
   397              */
   398             menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
   399 #endif
   400             AdjustWindowRectEx(&size, style, menu, 0);
   401             w = size.right - size.left;
   402             h = size.bottom - size.top;
   403 
   404             /* Fix our size to the current size */
   405             info = (MINMAXINFO *) lParam;
   406             info->ptMaxSize.x = w;
   407             info->ptMaxSize.y = h;
   408             info->ptMaxPosition.x = x;
   409             info->ptMaxPosition.y = y;
   410             info->ptMinTrackSize.x = w;
   411             info->ptMinTrackSize.y = h;
   412             info->ptMaxTrackSize.x = w;
   413             info->ptMaxTrackSize.y = h;
   414         }
   415         returnCode = 0;
   416         break;
   417 
   418     case WM_WINDOWPOSCHANGED:
   419         {
   420             RECT rect;
   421             int x, y;
   422             int w, h;
   423             Uint32 window_flags;
   424 
   425             if (!GetClientRect(hwnd, &rect) ||
   426                 (rect.right == rect.left && rect.bottom == rect.top)) {
   427                 break;
   428             }
   429             ClientToScreen(hwnd, (LPPOINT) & rect);
   430             ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   431 
   432             window_flags = SDL_GetWindowFlags(data->window);
   433             if ((window_flags & SDL_WINDOW_INPUT_GRABBED) &&
   434                 (window_flags & SDL_WINDOW_INPUT_FOCUS)) {
   435                 ClipCursor(&rect);
   436             }
   437 
   438             x = rect.left;
   439             y = rect.top;
   440             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y);
   441 
   442             w = rect.right - rect.left;
   443             h = rect.bottom - rect.top;
   444             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w,
   445                                 h);
   446         }
   447         break;
   448 
   449     case WM_SETCURSOR:
   450         {
   451             Uint16 hittest;
   452 
   453             hittest = LOWORD(lParam);
   454             if (hittest == HTCLIENT) {
   455                 /* FIXME: Implement the cursor API */
   456                 static HCURSOR cursor;
   457                 if (!cursor) {
   458                     cursor = LoadCursor(NULL, IDC_ARROW);
   459                 }
   460                 SetCursor(cursor);
   461                 returnCode = TRUE;
   462             }
   463         }
   464         break;
   465 
   466         /* We are about to get palette focus! */
   467     case WM_QUERYNEWPALETTE:
   468         {
   469             /*
   470                 WIN_RealizePalette(current_video);
   471                 returnCode = TRUE;
   472              */
   473         }
   474         break;
   475 
   476         /* Another application changed the palette */
   477     case WM_PALETTECHANGED:
   478         {
   479             /*
   480                WIN_PaletteChanged(current_video, (HWND) wParam);
   481              */
   482         }
   483         break;
   484 
   485         /* We were occluded, refresh our display */
   486     case WM_PAINT:
   487         {
   488             RECT rect;
   489             if (GetUpdateRect(hwnd, &rect, FALSE)) {
   490                 ValidateRect(hwnd, &rect);
   491                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED,
   492                                     0, 0);
   493             }
   494         }
   495         returnCode = 0;
   496         break;
   497 
   498         /* We'll do our own drawing, prevent flicker */
   499     case WM_ERASEBKGND:
   500         {
   501         }
   502         return (1);
   503 
   504     case WM_SYSCOMMAND:
   505         {
   506             /* Don't start the screensaver or blank the monitor in fullscreen apps */
   507             if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
   508                 (wParam & 0xFFF0) == SC_MONITORPOWER) {
   509                 if (SDL_GetVideoDevice()->suspend_screensaver) {
   510                     return (0);
   511                 }
   512             }
   513         }
   514         break;
   515 
   516     case WM_CLOSE:
   517         {
   518             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   519         }
   520         returnCode = 0;
   521         break;
   522 	case WM_TOUCH:
   523 		{
   524 			//printf("Got Touch Event!\n");
   525     
   526 #ifdef WMMSG_DEBUG
   527 			FILE *log = fopen("wmmsg.txt", "a");
   528 			fprintf(log, "Received Touch Message: %p ", hwnd);
   529 			if (msg > MAX_WMMSG) {
   530 				fprintf(log, "%d", msg);
   531 			} else {
   532 				fprintf(log, "%s", wmtab[msg]);
   533 			}
   534 			fprintf(log, "WM_TOUCH = %d -- 0x%X, 0x%X\n",msg, wParam, lParam);
   535 			fclose(log);
   536 #endif
   537     
   538 		}
   539 		break;
   540 	case WM_GESTURE:
   541 		{
   542 			//printf("Got Touch Event!\n");
   543     
   544 #ifdef WMMSG_DEBUG
   545 			FILE *log = fopen("wmmsg.txt", "a");
   546 			fprintf(log, "Received Gesture Message: %p ", hwnd);
   547 			if (msg > MAX_WMMSG) {
   548 				fprintf(log, "%d", msg);
   549 			} else {
   550 				fprintf(log, "%s", wmtab[msg]);
   551 			}
   552 			fprintf(log, "WM_GESTURE = %d -- 0x%X, 0x%X\n",msg, wParam, lParam);
   553 			fclose(log);
   554 #endif
   555 		}
   556 		break;		
   557 	}
   558 
   559     /* If there's a window proc, assume it's going to handle messages */
   560     if (data->wndproc) {
   561         return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);
   562     } else if (returnCode >= 0) {
   563         return returnCode;
   564     } else {
   565         return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
   566     }
   567 }
   568 
   569 void
   570 WIN_PumpEvents(_THIS)
   571 {
   572     MSG msg;
   573     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
   574         TranslateMessage(&msg);
   575         DispatchMessage(&msg);
   576     }
   577 }
   578 
   579 static int app_registered = 0;
   580 LPTSTR SDL_Appname = NULL;
   581 Uint32 SDL_Appstyle = 0;
   582 HINSTANCE SDL_Instance = NULL;
   583 
   584 /* Register the class for this application */
   585 int
   586 SDL_RegisterApp(char *name, Uint32 style, void *hInst)
   587 {
   588     WNDCLASS class;
   589 
   590     /* Only do this once... */
   591     if (app_registered) {
   592         ++app_registered;
   593         return (0);
   594     }
   595     if (!name && !SDL_Appname) {
   596         name = "SDL_app";
   597         SDL_Appstyle = (CS_BYTEALIGNCLIENT | CS_OWNDC);
   598         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   599     }
   600 
   601     if (name) {
   602         SDL_Appname = WIN_UTF8ToString(name);
   603         SDL_Appstyle = style;
   604         SDL_Instance = hInst ? hInst : GetModuleHandle(NULL);
   605     }
   606 
   607     /* Register the application class */
   608     class.hCursor = NULL;
   609     class.hIcon =
   610         LoadImage(SDL_Instance, SDL_Appname, IMAGE_ICON, 0, 0,
   611                   LR_DEFAULTCOLOR);
   612     class.lpszMenuName = NULL;
   613     class.lpszClassName = SDL_Appname;
   614     class.hbrBackground = NULL;
   615     class.hInstance = SDL_Instance;
   616     class.style = SDL_Appstyle;
   617     class.lpfnWndProc = WIN_WindowProc;
   618     class.cbWndExtra = 0;
   619     class.cbClsExtra = 0;
   620     if (!RegisterClass(&class)) {
   621         SDL_SetError("Couldn't register application class");
   622         return (-1);
   623     }
   624 
   625     app_registered = 1;
   626     return (0);
   627 }
   628 
   629 /* Unregisters the windowclass registered in SDL_RegisterApp above. */
   630 void
   631 SDL_UnregisterApp()
   632 {
   633     WNDCLASS class;
   634 
   635     /* SDL_RegisterApp might not have been called before */
   636     if (!app_registered) {
   637         return;
   638     }
   639     --app_registered;
   640     if (app_registered == 0) {
   641         /* Check for any registered window classes. */
   642         if (GetClassInfo(SDL_Instance, SDL_Appname, &class)) {
   643             UnregisterClass(SDL_Appname, SDL_Instance);
   644         }
   645         SDL_free(SDL_Appname);
   646         SDL_Appname = NULL;
   647     }
   648 }
   649 
   650 /* vi: set ts=4 sw=4 expandtab: */