X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Fmswin%2Ffg_main_mswin.c;h=6d286e75142f2225d7e5c0178647791f0f5cdd56;hb=226cd5ccd0f86cc1f2741e686fcf8e4846acf867;hp=85492f7e70412f665a5ec73ea80b972b5e0300df;hpb=a7362e5d688d2d2873d6bab49b86302aa588f1bf;p=freeglut diff --git a/src/mswin/fg_main_mswin.c b/src/mswin/fg_main_mswin.c index 85492f7..6d286e7 100644 --- a/src/mswin/fg_main_mswin.c +++ b/src/mswin/fg_main_mswin.c @@ -1,5 +1,5 @@ /* - * freeglut_main_mswin.c + * fg_main_mswin.c * * The Windows-specific mouse cursor related stuff. * @@ -28,14 +28,18 @@ #include #include "../fg_internal.h" - extern void fghRedrawWindow ( SFG_Window *window ); +extern void fghRedrawWindowAndChildren ( SFG_Window *window ); +extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify); +extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify); +extern void fghComputeWindowRectFromClientArea_QueryWindow( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside ); +extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside ); extern void fgNewWGLCreateContext( SFG_Window* window ); extern GLboolean fgSetupPixelFormat( SFG_Window* window, GLboolean checkOnly, unsigned char layer_type ); -extern void fgPlatformCheckMenuDeactivate(); +extern void fgPlatformCheckMenuDeactivate(HWND newFocusWnd); #ifdef WM_TOUCH typedef BOOL (WINAPI *pGetTouchInputInfo)(HTOUCHINPUT,UINT,PTOUCHINPUT,int); @@ -58,6 +62,389 @@ GXOPENINPUT GXOpenInput_ = NULL; struct GXKeyList gxKeyList; #endif /* _WIN32_WCE */ +#ifdef _DEBUG +/* + * WM_ message to string, for debugging + * This is taken from the 8.0 SDK, so Windows 8 API and everything earlier is included + */ +struct WM_MESSAGE_MAP +{ + UINT nMsg; + LPCSTR lpszMsg; +}; +#define DEFINE_MESSAGE(wm){ wm, #wm } +static struct WM_MESSAGE_MAP allMessages[] = +{ + DEFINE_MESSAGE(WM_NULL), + DEFINE_MESSAGE(WM_CREATE), + DEFINE_MESSAGE(WM_DESTROY), + DEFINE_MESSAGE(WM_MOVE), + DEFINE_MESSAGE(WM_SIZE), + + DEFINE_MESSAGE(WM_ACTIVATE), + DEFINE_MESSAGE(WM_SETFOCUS), + DEFINE_MESSAGE(WM_KILLFOCUS), + DEFINE_MESSAGE(WM_ENABLE), + DEFINE_MESSAGE(WM_SETREDRAW), + DEFINE_MESSAGE(WM_SETTEXT), + DEFINE_MESSAGE(WM_GETTEXT), + DEFINE_MESSAGE(WM_GETTEXTLENGTH), + DEFINE_MESSAGE(WM_PAINT), + DEFINE_MESSAGE(WM_CLOSE), +# ifndef _WIN32_WCE + DEFINE_MESSAGE(WM_QUERYENDSESSION), + DEFINE_MESSAGE(WM_QUERYOPEN), + DEFINE_MESSAGE(WM_ENDSESSION), +# endif + DEFINE_MESSAGE(WM_QUIT), + DEFINE_MESSAGE(WM_ERASEBKGND), + DEFINE_MESSAGE(WM_SYSCOLORCHANGE), + DEFINE_MESSAGE(WM_SHOWWINDOW), + DEFINE_MESSAGE(WM_WININICHANGE), + + DEFINE_MESSAGE(WM_DEVMODECHANGE), + DEFINE_MESSAGE(WM_ACTIVATEAPP), + DEFINE_MESSAGE(WM_FONTCHANGE), + DEFINE_MESSAGE(WM_TIMECHANGE), + DEFINE_MESSAGE(WM_CANCELMODE), + DEFINE_MESSAGE(WM_SETCURSOR), + DEFINE_MESSAGE(WM_MOUSEACTIVATE), + DEFINE_MESSAGE(WM_CHILDACTIVATE), + DEFINE_MESSAGE(WM_QUEUESYNC), + + DEFINE_MESSAGE(WM_GETMINMAXINFO), + + DEFINE_MESSAGE(WM_PAINTICON), + DEFINE_MESSAGE(WM_ICONERASEBKGND), + DEFINE_MESSAGE(WM_NEXTDLGCTL), + DEFINE_MESSAGE(WM_SPOOLERSTATUS), + DEFINE_MESSAGE(WM_DRAWITEM), + DEFINE_MESSAGE(WM_MEASUREITEM), + DEFINE_MESSAGE(WM_DELETEITEM), + DEFINE_MESSAGE(WM_VKEYTOITEM), + DEFINE_MESSAGE(WM_CHARTOITEM), + DEFINE_MESSAGE(WM_SETFONT), + DEFINE_MESSAGE(WM_GETFONT), + DEFINE_MESSAGE(WM_SETHOTKEY), + DEFINE_MESSAGE(WM_GETHOTKEY), + DEFINE_MESSAGE(WM_QUERYDRAGICON), + DEFINE_MESSAGE(WM_COMPAREITEM), +# if(WINVER >= 0x0500) +# ifndef _WIN32_WCE + DEFINE_MESSAGE(WM_GETOBJECT), + # endif +# endif /* WINVER >= 0x0500 */ + DEFINE_MESSAGE(WM_COMPACTING), + DEFINE_MESSAGE(WM_COMMNOTIFY), + DEFINE_MESSAGE(WM_WINDOWPOSCHANGING), + DEFINE_MESSAGE(WM_WINDOWPOSCHANGED), + + DEFINE_MESSAGE(WM_POWER), + + DEFINE_MESSAGE(WM_COPYDATA), + DEFINE_MESSAGE(WM_CANCELJOURNAL), + +# if(WINVER >= 0x0400) + DEFINE_MESSAGE(WM_NOTIFY), + DEFINE_MESSAGE(WM_INPUTLANGCHANGEREQUEST), + DEFINE_MESSAGE(WM_INPUTLANGCHANGE), + DEFINE_MESSAGE(WM_TCARD), + DEFINE_MESSAGE(WM_HELP), + DEFINE_MESSAGE(WM_USERCHANGED), + DEFINE_MESSAGE(WM_NOTIFYFORMAT), + + DEFINE_MESSAGE(WM_CONTEXTMENU), + DEFINE_MESSAGE(WM_STYLECHANGING), + DEFINE_MESSAGE(WM_STYLECHANGED), + DEFINE_MESSAGE(WM_DISPLAYCHANGE), + DEFINE_MESSAGE(WM_GETICON), + DEFINE_MESSAGE(WM_SETICON), +# endif /* WINVER >= 0x0400 */ + + DEFINE_MESSAGE(WM_NCCREATE), + DEFINE_MESSAGE(WM_NCDESTROY), + DEFINE_MESSAGE(WM_NCCALCSIZE), + DEFINE_MESSAGE(WM_NCHITTEST), + DEFINE_MESSAGE(WM_NCPAINT), + DEFINE_MESSAGE(WM_NCACTIVATE), + DEFINE_MESSAGE(WM_GETDLGCODE), +# ifndef _WIN32_WCE + DEFINE_MESSAGE(WM_SYNCPAINT), +# endif + DEFINE_MESSAGE(WM_NCMOUSEMOVE), + DEFINE_MESSAGE(WM_NCLBUTTONDOWN), + DEFINE_MESSAGE(WM_NCLBUTTONUP), + DEFINE_MESSAGE(WM_NCLBUTTONDBLCLK), + DEFINE_MESSAGE(WM_NCRBUTTONDOWN), + DEFINE_MESSAGE(WM_NCRBUTTONUP), + DEFINE_MESSAGE(WM_NCRBUTTONDBLCLK), + DEFINE_MESSAGE(WM_NCMBUTTONDOWN), + DEFINE_MESSAGE(WM_NCMBUTTONUP), + DEFINE_MESSAGE(WM_NCMBUTTONDBLCLK), + + + +# if(_WIN32_WINNT >= 0x0500) && defined(WM_NCXBUTTONDOWN) + DEFINE_MESSAGE(WM_NCXBUTTONDOWN), + DEFINE_MESSAGE(WM_NCXBUTTONUP), + DEFINE_MESSAGE(WM_NCXBUTTONDBLCLK), +# endif /* _WIN32_WINNT >= 0x0500 */ + + +# if(_WIN32_WINNT >= 0x0501) + DEFINE_MESSAGE(WM_INPUT_DEVICE_CHANGE), +# endif /* _WIN32_WINNT >= 0x0501 */ + +# if(_WIN32_WINNT >= 0x0501) + DEFINE_MESSAGE(WM_INPUT), +# endif /* _WIN32_WINNT >= 0x0501 */ + + DEFINE_MESSAGE(WM_KEYDOWN), + DEFINE_MESSAGE(WM_KEYUP), + DEFINE_MESSAGE(WM_CHAR), + DEFINE_MESSAGE(WM_DEADCHAR), + DEFINE_MESSAGE(WM_SYSKEYDOWN), + DEFINE_MESSAGE(WM_SYSKEYUP), + DEFINE_MESSAGE(WM_SYSCHAR), + DEFINE_MESSAGE(WM_SYSDEADCHAR), +# if(_WIN32_WINNT >= 0x0501) + DEFINE_MESSAGE(WM_UNICHAR), +# endif /* _WIN32_WINNT >= 0x0501 */ + +# if(WINVER >= 0x0400) + DEFINE_MESSAGE(WM_IME_STARTCOMPOSITION), + DEFINE_MESSAGE(WM_IME_ENDCOMPOSITION), + DEFINE_MESSAGE(WM_IME_COMPOSITION), + DEFINE_MESSAGE(WM_IME_KEYLAST), +# endif /* WINVER >= 0x0400 */ + + DEFINE_MESSAGE(WM_INITDIALOG), + DEFINE_MESSAGE(WM_COMMAND), + DEFINE_MESSAGE(WM_SYSCOMMAND), + DEFINE_MESSAGE(WM_TIMER), + DEFINE_MESSAGE(WM_HSCROLL), + DEFINE_MESSAGE(WM_VSCROLL), + DEFINE_MESSAGE(WM_INITMENU), + DEFINE_MESSAGE(WM_INITMENUPOPUP), +# if(WINVER >= 0x0601) + DEFINE_MESSAGE(WM_GESTURE), + DEFINE_MESSAGE(WM_GESTURENOTIFY), +# endif /* WINVER >= 0x0601 */ + DEFINE_MESSAGE(WM_MENUSELECT), + DEFINE_MESSAGE(WM_MENUCHAR), + DEFINE_MESSAGE(WM_ENTERIDLE), +# if(WINVER >= 0x0500) +# ifndef _WIN32_WCE + DEFINE_MESSAGE(WM_MENURBUTTONUP), + DEFINE_MESSAGE(WM_MENUDRAG), + DEFINE_MESSAGE(WM_MENUGETOBJECT), + DEFINE_MESSAGE(WM_UNINITMENUPOPUP), + DEFINE_MESSAGE(WM_MENUCOMMAND), + +# if(_WIN32_WINNT >= 0x0500) && defined(WM_CHANGEUISTATE) + DEFINE_MESSAGE(WM_CHANGEUISTATE), + DEFINE_MESSAGE(WM_UPDATEUISTATE), + DEFINE_MESSAGE(WM_QUERYUISTATE), +# endif /* _WIN32_WINNT >= 0x0500 */ + +# endif +# endif /* WINVER >= 0x0500 */ + + DEFINE_MESSAGE(WM_CTLCOLORMSGBOX), + DEFINE_MESSAGE(WM_CTLCOLOREDIT), + DEFINE_MESSAGE(WM_CTLCOLORLISTBOX), + DEFINE_MESSAGE(WM_CTLCOLORBTN), + DEFINE_MESSAGE(WM_CTLCOLORDLG), + DEFINE_MESSAGE(WM_CTLCOLORSCROLLBAR), + DEFINE_MESSAGE(WM_CTLCOLORSTATIC), +# define MN_GETHMENU 0x01E1 + + DEFINE_MESSAGE(WM_MOUSEMOVE), + DEFINE_MESSAGE(WM_LBUTTONDOWN), + DEFINE_MESSAGE(WM_LBUTTONUP), + DEFINE_MESSAGE(WM_LBUTTONDBLCLK), + DEFINE_MESSAGE(WM_RBUTTONDOWN), + DEFINE_MESSAGE(WM_RBUTTONUP), + DEFINE_MESSAGE(WM_RBUTTONDBLCLK), + DEFINE_MESSAGE(WM_MBUTTONDOWN), + DEFINE_MESSAGE(WM_MBUTTONUP), + DEFINE_MESSAGE(WM_MBUTTONDBLCLK), +# if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) + DEFINE_MESSAGE(WM_MOUSEWHEEL), +# endif +# if (_WIN32_WINNT >= 0x0500) && defined(WM_XBUTTONDOWN) + DEFINE_MESSAGE(WM_XBUTTONDOWN), + DEFINE_MESSAGE(WM_XBUTTONUP), + DEFINE_MESSAGE(WM_XBUTTONDBLCLK), +# endif +# if (_WIN32_WINNT >= 0x0600) + DEFINE_MESSAGE(WM_MOUSEHWHEEL), +# endif + + + + DEFINE_MESSAGE(WM_PARENTNOTIFY), + DEFINE_MESSAGE(WM_ENTERMENULOOP), + DEFINE_MESSAGE(WM_EXITMENULOOP), + +# if(WINVER >= 0x0400) + DEFINE_MESSAGE(WM_NEXTMENU), + DEFINE_MESSAGE(WM_SIZING), + DEFINE_MESSAGE(WM_CAPTURECHANGED), + DEFINE_MESSAGE(WM_MOVING), +# endif /* WINVER >= 0x0400 */ + +# if(WINVER >= 0x0400) + DEFINE_MESSAGE(WM_POWERBROADCAST), + DEFINE_MESSAGE(WM_DEVICECHANGE), +# endif /* WINVER >= 0x0400 */ + + DEFINE_MESSAGE(WM_MDICREATE), + DEFINE_MESSAGE(WM_MDIDESTROY), + DEFINE_MESSAGE(WM_MDIACTIVATE), + DEFINE_MESSAGE(WM_MDIRESTORE), + DEFINE_MESSAGE(WM_MDINEXT), + DEFINE_MESSAGE(WM_MDIMAXIMIZE), + DEFINE_MESSAGE(WM_MDITILE), + DEFINE_MESSAGE(WM_MDICASCADE), + DEFINE_MESSAGE(WM_MDIICONARRANGE), + DEFINE_MESSAGE(WM_MDIGETACTIVE), + + + DEFINE_MESSAGE(WM_MDISETMENU), + DEFINE_MESSAGE(WM_ENTERSIZEMOVE), + DEFINE_MESSAGE(WM_EXITSIZEMOVE), + DEFINE_MESSAGE(WM_DROPFILES), + DEFINE_MESSAGE(WM_MDIREFRESHMENU), + +# if(WINVER >= 0x0602) + DEFINE_MESSAGE(WM_POINTERDEVICECHANGE), + DEFINE_MESSAGE(WM_POINTERDEVICEINRANGE), + DEFINE_MESSAGE(WM_POINTERDEVICEOUTOFRANGE), +# endif /* WINVER >= 0x0602 */ + +# if(WINVER >= 0x0601) + DEFINE_MESSAGE(WM_TOUCH), +# endif /* WINVER >= 0x0601 */ + +# if(WINVER >= 0x0602) + DEFINE_MESSAGE(WM_NCPOINTERUPDATE), + DEFINE_MESSAGE(WM_NCPOINTERDOWN), + DEFINE_MESSAGE(WM_NCPOINTERUP), + DEFINE_MESSAGE(WM_POINTERUPDATE), + DEFINE_MESSAGE(WM_POINTERDOWN), + DEFINE_MESSAGE(WM_POINTERUP), + DEFINE_MESSAGE(WM_POINTERENTER), + DEFINE_MESSAGE(WM_POINTERLEAVE), + DEFINE_MESSAGE(WM_POINTERACTIVATE), + DEFINE_MESSAGE(WM_POINTERCAPTURECHANGED), + DEFINE_MESSAGE(WM_TOUCHHITTESTING), + DEFINE_MESSAGE(WM_POINTERWHEEL), + DEFINE_MESSAGE(WM_POINTERHWHEEL), +# endif /* WINVER >= 0x0602 */ + + +# if(WINVER >= 0x0400) + DEFINE_MESSAGE(WM_IME_SETCONTEXT), + DEFINE_MESSAGE(WM_IME_NOTIFY), + DEFINE_MESSAGE(WM_IME_CONTROL), + DEFINE_MESSAGE(WM_IME_COMPOSITIONFULL), + DEFINE_MESSAGE(WM_IME_SELECT), + DEFINE_MESSAGE(WM_IME_CHAR), +# endif /* WINVER >= 0x0400 */ +# if(WINVER >= 0x0500) + DEFINE_MESSAGE(WM_IME_REQUEST), +# endif /* WINVER >= 0x0500 */ +# if(WINVER >= 0x0400) + DEFINE_MESSAGE(WM_IME_KEYDOWN), + DEFINE_MESSAGE(WM_IME_KEYUP), +# endif /* WINVER >= 0x0400 */ + +# if((_WIN32_WINNT >= 0x0400) || (WINVER >= 0x0500)) + DEFINE_MESSAGE(WM_MOUSEHOVER), + DEFINE_MESSAGE(WM_MOUSELEAVE), +# endif +# if(WINVER >= 0x0500) && defined(WM_NCMOUSEHOVER) + DEFINE_MESSAGE(WM_NCMOUSEHOVER), + DEFINE_MESSAGE(WM_NCMOUSELEAVE), +# endif /* WINVER >= 0x0500 */ + +# if(_WIN32_WINNT >= 0x0501) + DEFINE_MESSAGE(WM_WTSSESSION_CHANGE), +# endif /* _WIN32_WINNT >= 0x0501 */ + + DEFINE_MESSAGE(WM_CUT), + DEFINE_MESSAGE(WM_COPY), + DEFINE_MESSAGE(WM_PASTE), + DEFINE_MESSAGE(WM_CLEAR), + DEFINE_MESSAGE(WM_UNDO), + DEFINE_MESSAGE(WM_RENDERFORMAT), + DEFINE_MESSAGE(WM_RENDERALLFORMATS), + DEFINE_MESSAGE(WM_DESTROYCLIPBOARD), + DEFINE_MESSAGE(WM_DRAWCLIPBOARD), + DEFINE_MESSAGE(WM_PAINTCLIPBOARD), + DEFINE_MESSAGE(WM_VSCROLLCLIPBOARD), + DEFINE_MESSAGE(WM_SIZECLIPBOARD), + DEFINE_MESSAGE(WM_ASKCBFORMATNAME), + DEFINE_MESSAGE(WM_CHANGECBCHAIN), + DEFINE_MESSAGE(WM_HSCROLLCLIPBOARD), + DEFINE_MESSAGE(WM_QUERYNEWPALETTE), + DEFINE_MESSAGE(WM_PALETTEISCHANGING), + DEFINE_MESSAGE(WM_PALETTECHANGED), + DEFINE_MESSAGE(WM_HOTKEY), + +# if(WINVER >= 0x0400) + DEFINE_MESSAGE(WM_PRINT), + DEFINE_MESSAGE(WM_PRINTCLIENT), +# endif /* WINVER >= 0x0400 */ + +# if(_WIN32_WINNT >= 0x0500) && defined(WM_APPCOMMAND) + DEFINE_MESSAGE(WM_APPCOMMAND), +# endif /* _WIN32_WINNT >= 0x0500 */ + +# if(_WIN32_WINNT >= 0x0501) + DEFINE_MESSAGE(WM_THEMECHANGED), +# endif /* _WIN32_WINNT >= 0x0501 */ + + +# if(_WIN32_WINNT >= 0x0501) + DEFINE_MESSAGE(WM_CLIPBOARDUPDATE), +# endif /* _WIN32_WINNT >= 0x0501 */ + +# if(_WIN32_WINNT >= 0x0600) + DEFINE_MESSAGE(WM_DWMCOMPOSITIONCHANGED), + DEFINE_MESSAGE(WM_DWMNCRENDERINGCHANGED), + DEFINE_MESSAGE(WM_DWMCOLORIZATIONCOLORCHANGED), + DEFINE_MESSAGE(WM_DWMWINDOWMAXIMIZEDCHANGE), +# endif /* _WIN32_WINNT >= 0x0600 */ + +# if(_WIN32_WINNT >= 0x0601) + DEFINE_MESSAGE(WM_DWMSENDICONICTHUMBNAIL), + DEFINE_MESSAGE(WM_DWMSENDICONICLIVEPREVIEWBITMAP), +# endif /* _WIN32_WINNT >= 0x0601 */ + + +# if(WINVER >= 0x0600) + DEFINE_MESSAGE(WM_GETTITLEBARINFOEX), +# endif /* WINVER >= 0x0600 */ + { 0, NULL, } /* end of message list */ +}; +#undef DEFINE_MESSAGE + +char* WMMsg2Str(DWORD dwMessage) +{ + struct WM_MESSAGE_MAP* pMapMsg = allMessages; + for (/*null*/; pMapMsg->lpszMsg != NULL; pMapMsg++) + { + if (pMapMsg->nMsg == dwMessage ) + { + return (char *)pMapMsg->lpszMsg; + } + } + return ""; +} +#endif /* _DEBUG */ + /* Get system time, taking special precautions against 32bit timer wrap. We use timeGetTime and not GetTickCount because of its better stability, @@ -87,7 +474,7 @@ fg_time_t fgPlatformSystemTime ( void ) /* Check if we just wrapped */ if (currTime32 < lastTime32) timeEpoch++; - + lastTime32 = currTime32; return currTime32 | timeEpoch << 32; @@ -128,32 +515,49 @@ void fgPlatformProcessSingleEvent ( void ) -static void fghUpdateWindowStatus(SFG_Window *window, GLboolean visState) +static void fghPlatformOnWindowStatusNotify(SFG_Window *window, GLboolean visState, GLboolean forceNotify) { + GLboolean notify = GL_FALSE; SFG_Window* child; if (window->State.Visible != visState) { window->State.Visible = visState; + + /* If top level window (not a subwindow/child), and icon title text available, switch titles based on visibility state */ + if (!window->Parent && window->State.pWState.IconTitle) + { + if (visState) + /* visible, set window title */ + SetWindowText( window->Window.Handle, window->State.pWState.WindowTitle ); + else + /* not visible, set icon title */ + SetWindowText( window->Window.Handle, window->State.pWState.IconTitle ); + } + + notify = GL_TRUE; + } + + if (notify || forceNotify) + { + SFG_Window *saved_window = fgStructure.CurrentWindow; + + /* On win32 we only have two states, window displayed and window not displayed (iconified) + * We map these to GLUT_FULLY_RETAINED and GLUT_HIDDEN respectively. + */ INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) ); + fgSetWindow( saved_window ); } - /* Also set visibility state for children */ + /* Also set windowStatus/visibility state for children */ for( child = ( SFG_Window * )window->Children.First; child; child = ( SFG_Window * )child->Node.Next ) { - fghUpdateWindowStatus(child, visState); + fghPlatformOnWindowStatusNotify(child, visState, GL_FALSE); /* No need to propagate forceNotify. Childs get this from their own INIT_WORK */ } } -void fghNotifyWindowStatus(SFG_Window *window) -{ - INVOKE_WCB( *window, WindowStatus, ( window->State.Visible?GLUT_FULLY_RETAINED:GLUT_HIDDEN ) ); - - /* Don't notify children, they get their own just before first time they're drawn */ -} - void fgPlatformMainLoopPreliminaryWork ( void ) { /* no-op */ @@ -174,13 +578,21 @@ static int fgPlatformGetModifiers (void) ( GetKeyState( VK_RMENU ) < 0 )) ? GLUT_ACTIVE_ALT : 0 ); } +/* Check whether a button (VK_*BUTTON) is currently depressed. Returns + * non-zero (not necessarily 1) if yes. */ +static SHORT fgGetKeyState(int vKey) +{ + /* MSDN says: "If the high-order bit is 1, the key is down; otherwise, it is up". */ + return GetKeyState(vKey) & 0xFF00; +} + static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean keydown, WPARAM wParam, LPARAM lParam) { static unsigned char lControl = 0, lShift = 0, lAlt = 0, rControl = 0, rShift = 0, rAlt = 0; int keypress = -1; - + /* if keydown, check for repeat */ /* If repeat is globally switched off, it cannot be switched back on per window. * But if it is globally switched on, it can be switched off per window. This matches @@ -189,65 +601,65 @@ static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean ke */ if( keydown && ( fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE ) && (HIWORD(lParam) & KF_REPEAT) ) return 1; - + /* Remember the current modifiers state so user can query it from their callback */ fgState.Modifiers = fgPlatformGetModifiers( ); /* Convert the Win32 keystroke codes to GLUTtish way */ -# define KEY(a,b) case a: keypress = b; break; +# define FG_KEY(a,b) case a: keypress = b; break; switch( wParam ) { - KEY( VK_F1, GLUT_KEY_F1 ); - KEY( VK_F2, GLUT_KEY_F2 ); - KEY( VK_F3, GLUT_KEY_F3 ); - KEY( VK_F4, GLUT_KEY_F4 ); - KEY( VK_F5, GLUT_KEY_F5 ); - KEY( VK_F6, GLUT_KEY_F6 ); - KEY( VK_F7, GLUT_KEY_F7 ); - KEY( VK_F8, GLUT_KEY_F8 ); - KEY( VK_F9, GLUT_KEY_F9 ); - KEY( VK_F10, GLUT_KEY_F10 ); - KEY( VK_F11, GLUT_KEY_F11 ); - KEY( VK_F12, GLUT_KEY_F12 ); - KEY( VK_PRIOR, GLUT_KEY_PAGE_UP ); - KEY( VK_NEXT, GLUT_KEY_PAGE_DOWN ); - KEY( VK_HOME, GLUT_KEY_HOME ); - KEY( VK_END, GLUT_KEY_END ); - KEY( VK_LEFT, GLUT_KEY_LEFT ); - KEY( VK_UP, GLUT_KEY_UP ); - KEY( VK_RIGHT, GLUT_KEY_RIGHT ); - KEY( VK_DOWN, GLUT_KEY_DOWN ); - KEY( VK_INSERT, GLUT_KEY_INSERT ); + FG_KEY( VK_F1, GLUT_KEY_F1 ); + FG_KEY( VK_F2, GLUT_KEY_F2 ); + FG_KEY( VK_F3, GLUT_KEY_F3 ); + FG_KEY( VK_F4, GLUT_KEY_F4 ); + FG_KEY( VK_F5, GLUT_KEY_F5 ); + FG_KEY( VK_F6, GLUT_KEY_F6 ); + FG_KEY( VK_F7, GLUT_KEY_F7 ); + FG_KEY( VK_F8, GLUT_KEY_F8 ); + FG_KEY( VK_F9, GLUT_KEY_F9 ); + FG_KEY( VK_F10, GLUT_KEY_F10 ); + FG_KEY( VK_F11, GLUT_KEY_F11 ); + FG_KEY( VK_F12, GLUT_KEY_F12 ); + FG_KEY( VK_PRIOR, GLUT_KEY_PAGE_UP ); + FG_KEY( VK_NEXT, GLUT_KEY_PAGE_DOWN ); + FG_KEY( VK_HOME, GLUT_KEY_HOME ); + FG_KEY( VK_END, GLUT_KEY_END ); + FG_KEY( VK_LEFT, GLUT_KEY_LEFT ); + FG_KEY( VK_UP, GLUT_KEY_UP ); + FG_KEY( VK_RIGHT, GLUT_KEY_RIGHT ); + FG_KEY( VK_DOWN, GLUT_KEY_DOWN ); + FG_KEY( VK_INSERT, GLUT_KEY_INSERT ); /* handle control, alt and shift. For GLUT, we want to distinguish between left and right presses. * The VK_L* & VK_R* left and right Alt, Ctrl and Shift virtual keys are however only used as parameters to GetAsyncKeyState() and GetKeyState() * so when we get an alt, shift or control keypress here, we manually check whether it was the left or the right */ -#define ASYNC_KEY_EVENT(winKey,glutKey,keyStateVar)\ - if (!keyStateVar && GetAsyncKeyState ( winKey ))\ +#define FG_KEY_EVENT(winKey,glutKey,keyStateVar)\ + if (!keyStateVar && fgGetKeyState ( winKey ))\ {\ keypress = glutKey;\ keyStateVar = 1;\ }\ - else if (keyStateVar && !GetAsyncKeyState ( winKey ))\ + else if (keyStateVar && !fgGetKeyState ( winKey ))\ {\ keypress = glutKey;\ keyStateVar = 0;\ } case VK_CONTROL: - ASYNC_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl); - ASYNC_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl); + FG_KEY_EVENT(VK_LCONTROL,GLUT_KEY_CTRL_L,lControl); + FG_KEY_EVENT(VK_RCONTROL,GLUT_KEY_CTRL_R,rControl); break; case VK_SHIFT: - ASYNC_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift); - ASYNC_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift); + FG_KEY_EVENT(VK_LSHIFT,GLUT_KEY_SHIFT_L,lShift); + FG_KEY_EVENT(VK_RSHIFT,GLUT_KEY_SHIFT_R,rShift); break; case VK_MENU: - ASYNC_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt); - ASYNC_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt); + FG_KEY_EVENT(VK_LMENU,GLUT_KEY_ALT_L,lAlt); + FG_KEY_EVENT(VK_RMENU,GLUT_KEY_ALT_R,rAlt); break; -#undef ASYNC_KEY_EVENT +#undef KEY_EVENT case VK_DELETE: /* The delete key should be treated as an ASCII keypress: */ @@ -275,7 +687,7 @@ static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean ke wParam=code[ 0 ]; INVOKE_WCB( *window, KeyboardUp, - ( (char)wParam, + ( (char)(wParam & 0xFF), /* and with 0xFF to indicate to runtime that we want to strip out higher bits - otherwise we get a runtime error when "Smaller Type Checks" is enabled */ window->State.MouseX, window->State.MouseY ) ); } @@ -303,7 +715,7 @@ static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean ke keypress = GLUT_KEY_F4; } #endif - + if( keypress != -1 ) if (keydown) INVOKE_WCB( *window, Special, @@ -325,7 +737,7 @@ static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean ke return 1; } -static SFG_Window* fghWindowUnderCursor(SFG_Window *window) +SFG_Window* fghWindowUnderCursor(SFG_Window *window) { /* Check if the current window that the mouse is over is a child window * of the window the message was sent to. Some events only sent to main window, @@ -335,14 +747,16 @@ static SFG_Window* fghWindowUnderCursor(SFG_Window *window) */ if (window && window->Children.First) /* This window has childs */ { - SFG_WindowHandleType hwnd; + HWND hwnd; SFG_Window* child_window; /* Get mouse position at time of message */ DWORD mouse_pos_dw = GetMessagePos(); - POINT mouse_pos = {GET_X_LPARAM(mouse_pos_dw), GET_Y_LPARAM(mouse_pos_dw)}; + POINT mouse_pos; + mouse_pos.x = GET_X_LPARAM(mouse_pos_dw); + mouse_pos.y = GET_Y_LPARAM(mouse_pos_dw); ScreenToClient( window->Window.Handle, &mouse_pos ); - + hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos); if (hwnd && hwnd!=window->Window.Handle) /* can be NULL if mouse outside parent by the time we get here, or can be same as parent if we didn't find a child */ { @@ -374,8 +788,8 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR if ( ( window == NULL ) && ( uMsg != WM_CREATE ) ) return DefWindowProc( hWnd, uMsg, wParam, lParam ); - /* printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0, - uMsg, wParam, lParam ); */ + /* printf ( "Window %3d message %s (<%04x>) %12d %12d\n", window?window->ID:0, + WMMsg2Str(uMsg), uMsg, wParam, lParam ); */ switch( uMsg ) { @@ -433,21 +847,6 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR #endif } - window->State.NeedToResize = GL_TRUE; - /* if we used CW_USEDEFAULT (thats a negative value) for the size - * of the window, query the window now for the size at which it - * was created. - */ - if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) ) - { - SFG_Window *current_window = fgStructure.CurrentWindow; - - fgSetWindow( window ); - window->State.Width = glutGet( GLUT_WINDOW_WIDTH ); - window->State.Height = glutGet( GLUT_WINDOW_HEIGHT ); - fgSetWindow( current_window ); - } - ReleaseDC( window->Window.Handle, window->Window.pContext.Device ); #if defined(_WIN32_WCE) @@ -474,61 +873,93 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR /* Update visibility state of the window */ if (wParam==SIZE_MINIMIZED) - fghUpdateWindowStatus(window,GL_FALSE); - else if (wParam==SIZE_RESTORED && !window->State.Visible) - fghUpdateWindowStatus(window,GL_TRUE); + fghPlatformOnWindowStatusNotify(window,GL_FALSE,GL_FALSE); + else if ((wParam==SIZE_RESTORED || wParam == SIZE_MAXIMIZED) && !window->State.Visible) + fghPlatformOnWindowStatusNotify(window,GL_TRUE,GL_FALSE); - /* Check window visible, we don't want to resize when the user or glutIconifyWindow minimized the window */ + /* Check window visible, we don't want do anything when we get a WM_SIZE because the user or glutIconifyWindow minimized the window */ if( window->State.Visible ) { - /* get old values first to compare to below */ - int width = window->State.Width, height=window->State.Height; + int width, height; #if defined(_WIN32_WCE) - window->State.Width = HIWORD(lParam); - window->State.Height = LOWORD(lParam); + width = HIWORD(lParam); + height = LOWORD(lParam); #else - window->State.Width = LOWORD(lParam); - window->State.Height = HIWORD(lParam); + width = LOWORD(lParam); + height = HIWORD(lParam); #endif /* defined(_WIN32_WCE) */ - - if (width!=window->State.Width || height!=window->State.Height) - /* Something changed, need to resize */ - window->State.NeedToResize = GL_TRUE; + + /* Update state and call callback, if there was a change */ + fghOnReshapeNotify(window, width, height, GL_FALSE); } /* according to docs, should return 0 */ lRet = 0; break; - case WM_MOVE: + case WM_SIZING: { - SFG_Window* saved_window = fgStructure.CurrentWindow; - RECT windowRect; + /* User resize-dragging the window, call reshape callback and + * force redisplay so display keeps running during dragging. + * Screen still wont update when not moving the cursor though... + */ + RECT rect; + /* PRECT prect = (PRECT) lParam; + printf("WM_SIZING: nc-area: %i,%i\n",prect->right-prect->left,prect->bottom-prect->top); */ + /* Get client area, the rect in lParam is including non-client area. */ + fghGetClientArea(&rect,window,FALSE); + + /* We'll get a WM_SIZE as well, but as state has + * already been updated here, the fghOnReshapeNotify + * in the handler for that message doesn't do anything. + */ + fghOnReshapeNotify(window, rect.right-rect.left, rect.bottom-rect.top, GL_FALSE); + + /* Now directly call the drawing function to update + * window and window's childs. + * This mimics the WM_PAINT messages that are received during + * resizing. Note that we don't have a WM_MOVING handler + * as move-dragging doesn't generate WM_MOVE or WM_PAINT + * messages until the mouse is released. + */ + fghRedrawWindowAndChildren(window); + } + /* according to docs, should return TRUE */ + lRet = TRUE; + break; + + case WM_MOVE: + { /* Check window is minimized, we don't want to call the position callback when the user or glutIconifyWindow minimized the window */ if (!IsIconic(window->Window.Handle)) { - /* Get top-left of non-client area of window, matching coordinates of - * glutInitPosition and glutPositionWindow, but not those of + RECT windowRect; + + /* lParam contains coordinates of top-left of client area. + * Get top-left of non-client area of window, matching coordinates of + * glutInitPosition and glutPositionWindow, but not those of * glutGet(GLUT_WINDOW_X) and glutGet(GLUT_WINDOW_Y), which return * top-left of client area. */ GetWindowRect( window->Window.Handle, &windowRect ); - + if (window->Parent) { /* For child window, we should return relative to upper-left * of parent's client area. */ - POINT topleft = {windowRect.left,windowRect.top}; + POINT topleft; + topleft.x = windowRect.left; + topleft.y = windowRect.top; ScreenToClient(window->Parent->Window.Handle,&topleft); windowRect.left = topleft.x; windowRect.top = topleft.y; } - INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) ); - fgSetWindow(saved_window); + /* Update state and call callback, if there was a change */ + fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_FALSE); } } @@ -537,8 +968,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR break; case WM_SETFOCUS: -/* printf("WM_SETFOCUS: %p\n", window ); */ - + /*printf("WM_SETFOCUS: %p\n", window );*/ lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); SetActiveWindow( window->Window.Handle ); @@ -547,18 +977,41 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR break; case WM_KILLFOCUS: - { -/* printf("WM_KILLFOCUS: %p\n", window ); */ + /*printf("WM_KILLFOCUS: %p\n", window ); */ + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); + + /* Check if there are any open menus that need to be closed */ + fgPlatformCheckMenuDeactivate((HWND)wParam); + break; + + case WM_MOUSEACTIVATE: + /* Clicks should not activate the menu. + * Especially important when clicking on a menu's submenu item which has no effect. + */ + /*printf("WM_MOUSEACTIVATE\n");*/ + if (window->IsMenu) + lRet = MA_NOACTIVATEANDEAT; + else lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); + break; - /* Check if there are any open menus that need to be closed */ - fgPlatformCheckMenuDeactivate(); + case WM_NCLBUTTONDOWN: + case WM_NCMBUTTONDOWN: + case WM_NCRBUTTONDOWN: + { + SFG_Menu *menu; + if (fgState.ActiveMenus && (menu = fgGetActiveMenu())) + /* user clicked non-client area of window while a menu is open. Close menu */ + fgDeactivateMenu(menu->ParentWindow); + + /* and always pass to DefWindowProc */ + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); } break; #if 0 case WM_ACTIVATE: - //printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam)); + /* printf("WM_ACTIVATE: %x (ID: %i) %d %d\n",lParam, window->ID, HIWORD(wParam), LOWORD(wParam)); */ if (LOWORD(wParam) != WA_INACTIVE) { /* printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window, @@ -578,7 +1031,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR { TRACKMOUSEEVENT tme; - /* Cursor just entered window, set cursor look */ + /* Cursor just entered window, set cursor look */ fgSetCursor ( window, window->State.Cursor ) ; /* If an EntryFunc callback is specified by the user, also @@ -587,14 +1040,16 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR */ if (FETCH_WCB( *window, Entry )) { + SFG_Window* saved_window = fgStructure.CurrentWindow; INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) ); + fgSetWindow(saved_window); tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; tme.hwndTrack = window->Window.Handle; TrackMouseEvent(&tme); - window->State.pWState.MouseTracking = GL_TRUE; + window->State.pWState.MouseTracking = TRUE; } } } @@ -613,7 +1068,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) ); fgSetWindow(saved_window); - window->State.pWState.MouseTracking = GL_FALSE; + window->State.pWState.MouseTracking = FALSE; lRet = 0; /* As per docs, must return zero */ } break; @@ -622,26 +1077,36 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */ if (wParam) { - fghUpdateWindowStatus(window, GL_TRUE); - window->State.Redisplay = GL_TRUE; + fghPlatformOnWindowStatusNotify(window, GL_TRUE, GL_FALSE); + window->State.WorkMask |= GLUT_DISPLAY_WORK; } else { - fghUpdateWindowStatus(window, GL_FALSE); - window->State.Redisplay = GL_FALSE; + fghPlatformOnWindowStatusNotify(window, GL_FALSE, GL_FALSE); + window->State.WorkMask &= ~GLUT_DISPLAY_WORK; } break; case WM_PAINT: { - PAINTSTRUCT ps; - /* Turn on the visibility in case it was turned off somehow */ - window->State.Visible = GL_TRUE; - - InvalidateRect( hWnd, NULL, GL_FALSE ); /* Make sure whole window is repainted. Bit of a hack, but a safe one from what google turns up... */ - BeginPaint( hWnd, &ps ); - fghRedrawWindow( window ); - EndPaint( hWnd, &ps ); + RECT rect; + + /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */ + if (GetUpdateRect(hWnd,&rect,FALSE)) + { + /* Dummy begin/end paint to validate rect that needs + * redrawing, then signal that a redisplay is needed. + * This allows us full control about when we do any + * redrawing, and is the same as what original GLUT + * does. + */ + PAINTSTRUCT ps; + BeginPaint( hWnd, &ps ); + EndPaint( hWnd, &ps ); + + window->State.WorkMask |= GLUT_DISPLAY_WORK; + } + lRet = 0; /* As per docs, should return 0 */ } break; @@ -659,12 +1124,13 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR case WM_MOUSEMOVE: { + /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */ #if defined(_WIN32_WCE) - window->State.MouseX = 320-HIWORD( lParam ); + window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */ window->State.MouseY = LOWORD( lParam ); #else - window->State.MouseX = LOWORD( lParam ); - window->State.MouseY = HIWORD( lParam ); + window->State.MouseX = GET_X_LPARAM( lParam ); + window->State.MouseY = GET_Y_LPARAM( lParam ); #endif /* defined(_WIN32_WCE) */ /* Restrict to [-32768, 32767] to match X11 behaviour */ /* See comment in "freeglut_developer" mailing list 10/4/04 */ @@ -702,12 +1168,13 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR GLboolean pressed = GL_TRUE; int button; + /* Per docs, use LOWORD/HIWORD for WinCE and GET_X_LPARAM/GET_Y_LPARAM for desktop windows */ #if defined(_WIN32_WCE) - window->State.MouseX = 320-HIWORD( lParam ); + window->State.MouseX = 320-HIWORD( lParam ); /* XXX: Docs say x should be loword and y hiword? */ window->State.MouseY = LOWORD( lParam ); #else - window->State.MouseX = LOWORD( lParam ); - window->State.MouseY = HIWORD( lParam ); + window->State.MouseX = GET_X_LPARAM( lParam ); + window->State.MouseY = GET_Y_LPARAM( lParam ); #endif /* defined(_WIN32_WCE) */ /* Restrict to [-32768, 32767] to match X11 behaviour */ @@ -782,7 +1249,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR SetCapture ( window->Window.Handle ) ; setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */ } - else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON)) + else if (!fgGetKeyState(VK_LBUTTON) && !fgGetKeyState(VK_MBUTTON) && !fgGetKeyState(VK_RBUTTON)) /* Make sure all mouse buttons are released before releasing capture */ ReleaseCapture () ; @@ -810,16 +1277,29 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR case WM_MOUSEWHEEL: { - SFG_Window *child_window = NULL; - int wheel_number = LOWORD( wParam ); - short ticks = ( short )HIWORD( wParam ); + int wheel_number = 0; /* Only one scroll wheel on windows */ +#if defined(_WIN32_WCE) + int modkeys = LOWORD(wParam); + short ticks = (short)HIWORD(wParam); + /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first: + xPos = LOWORD(lParam); -- straight from docs, not consistent with mouse nutton and mouse motion above (which i think is wrong) + yPos = HIWORD(lParam); + */ +#else + /* int modkeys = GET_KEYSTATE_WPARAM( wParam ); */ + short ticks = HIWORD( wParam ); + /* commented out as should not be needed here, mouse motion is processed in WM_MOUSEMOVE first: + window->State.MouseX = GET_X_LPARAM( lParam ); + window->State.MouseY = GET_Y_LPARAM( lParam ); + */ +#endif /* defined(_WIN32_WCE) */ window = fghWindowUnderCursor(window); - fgState.MouseWheelTicks += ticks; + fgState.MouseWheelTicks += ticks; if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA ) - { - int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1; + { + int direction = ( fgState.MouseWheelTicks > 0 ) ? 1 : -1; if( ! FETCH_WCB( *window, MouseWheel ) && ! FETCH_WCB( *window, Mouse ) ) @@ -829,7 +1309,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR fgState.Modifiers = fgPlatformGetModifiers( ); while( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA ) - { + { if( FETCH_WCB( *window, MouseWheel ) ) INVOKE_WCB( *window, MouseWheel, ( wheel_number, @@ -839,7 +1319,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR ) ); else /* No mouse wheel, call the mouse button callback twice */ - { + { /* * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4 * " " one +1 to 5, -1 to 6, ... @@ -858,13 +1338,15 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR ( button, GLUT_UP, window->State.MouseX, window->State.MouseY ) ); - } + } - fgState.MouseWheelTicks -= WHEEL_DELTA * direction; - } + fgState.MouseWheelTicks -= WHEEL_DELTA * direction; + } fgState.Modifiers = INVALID_MODIFIERS; - } + } + /* Per docs, should return zero */ + lRet = 0; } break ; @@ -911,42 +1393,18 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR lRet = 0; /* Per docs, should return zero */ break; - /* Other messages that I have seen and which are not handled already */ - case WM_SETTEXT: /* 0x000c */ - lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); - /* Pass it on to "DefWindowProc" to set the window text */ - break; - - case WM_GETTEXT: /* 0x000d */ - /* Ideally we would copy the title of the window into "lParam" */ - /* strncpy ( (char *)lParam, "Window Title", wParam ); - lRet = ( wParam > 12 ) ? 12 : wParam; */ - /* the number of characters copied */ - lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); - break; - - case WM_GETTEXTLENGTH: /* 0x000e */ - /* Ideally we would get the length of the title of the window */ - lRet = 12; - /* the number of characters in "Window Title\0" (see above) */ - break; - - case WM_ERASEBKGND: /* 0x0014 */ - lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); - break; - #if !defined(_WIN32_WCE) case WM_SYNCPAINT: /* 0x0088 */ /* Another window has moved, need to update this one */ - window->State.Redisplay = GL_TRUE; + window->State.WorkMask |= GLUT_DISPLAY_WORK; lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); /* Help screen says this message must be passed to "DefWindowProc" */ break; - case WM_NCPAINT: /* 0x0085 */ - /* Need to update the border of this window */ - lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); - /* Pass it on to "DefWindowProc" to repaint a standard border */ + case WM_DISPLAYCHANGE: /* 0x007E */ + /* The system display resolution/depth has changed */ + fgDisplay.ScreenWidth = LOWORD(lParam); + fgDisplay.ScreenHeight = HIWORD(lParam); break; case WM_SYSCOMMAND : /* 0x0112 */ @@ -1040,50 +1498,60 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR break; #ifdef WM_TOUCH - /* handle multi-touch messages */ - case WM_TOUCH: - { - unsigned int numInputs = (unsigned int)wParam; - unsigned int i = 0; - TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs); - - if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) { - fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo"); - fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle"); - } - - if (!fghGetTouchInputInfo) { - free( (void*)ti ); - break; - } - - if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) { - /* Handle each contact point */ - for (i = 0; i < numInputs; ++i ) { - - POINT tp; - tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x); - tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y); - ScreenToClient( hWnd, &tp ); - - ti[i].dwID = ti[i].dwID * 2; - - if (ti[i].dwFlags & TOUCHEVENTF_DOWN) { - INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_ENTERED ) ); - INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) ); - } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) { - INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) ); - } else if (ti[i].dwFlags & TOUCHEVENTF_UP) { - INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) ); - INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_LEFT ) ); - } - } - } - fghCloseTouchInputHandle((HTOUCHINPUT)lParam); - free( (void*)ti ); - lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/ - break; - } + /* handle multi-touch messages */ + case WM_TOUCH: + { + unsigned int numInputs = (unsigned int)wParam; + unsigned int i = 0; + TOUCHINPUT* ti = (TOUCHINPUT*)malloc( sizeof(TOUCHINPUT)*numInputs); + + if (fghGetTouchInputInfo == (pGetTouchInputInfo)0xDEADBEEF) { + fghGetTouchInputInfo = (pGetTouchInputInfo)GetProcAddress(GetModuleHandle("user32"),"GetTouchInputInfo"); + fghCloseTouchInputHandle = (pCloseTouchInputHandle)GetProcAddress(GetModuleHandle("user32"),"CloseTouchInputHandle"); + } + + if (!fghGetTouchInputInfo) { + free( (void*)ti ); + break; + } + + if (fghGetTouchInputInfo( (HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT) )) { + /* Handle each contact point */ + for (i = 0; i < numInputs; ++i ) { + + POINT tp; + tp.x = TOUCH_COORD_TO_PIXEL(ti[i].x); + tp.y = TOUCH_COORD_TO_PIXEL(ti[i].y); + ScreenToClient( hWnd, &tp ); + + ti[i].dwID = ti[i].dwID * 2; + + if (ti[i].dwFlags & TOUCHEVENTF_DOWN) { + INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_ENTERED ) ); + INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_DOWN ) ); + } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) { + INVOKE_WCB( *window, MultiMotion, ( ti[i].dwID, tp.x, tp.y ) ); + } else if (ti[i].dwFlags & TOUCHEVENTF_UP) { + INVOKE_WCB( *window, MultiButton, ( ti[i].dwID, tp.x, tp.y, 0, GLUT_UP ) ); + INVOKE_WCB( *window, MultiEntry, ( ti[i].dwID, GLUT_LEFT ) ); + } + } + } + fghCloseTouchInputHandle((HTOUCHINPUT)lParam); + free( (void*)ti ); + lRet = 0; /*DefWindowProc( hWnd, uMsg, wParam, lParam );*/ + break; + } +#endif + +#ifdef WM_INPUT + case WM_INPUT: + /* Added by Jinrong Xie for SpaceNavigator support on Windows. Dec 2014 */ + if (fgHasSpaceball()) + { + fgSpaceballHandleWinEvent(hWnd, wParam, lParam); + } + break; #endif default: /* Handle unhandled messages */ @@ -1093,3 +1561,226 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR return lRet; } + + +/* deal with work list items */ +void fgPlatformInitWork(SFG_Window* window) +{ + RECT windowRect; + + /* Notify windowStatus/visibility */ + fghPlatformOnWindowStatusNotify(window, window->State.Visible, GL_TRUE); + + /* get and notify window's position */ + GetWindowRect(window->Window.Handle,&windowRect); + fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_TRUE); + + /* get and notify window's size */ + GetClientRect(window->Window.Handle,&windowRect); + fghOnReshapeNotify(window, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, GL_TRUE); +} + +/* On windows we can position, resize and change z order at the same time */ +void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask) +{ + UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER; + HWND insertAfter = HWND_TOP; + RECT clientRect; + +#if !defined(_WIN32_WCE) /* FIXME: what about WinCE */ + if (workMask & GLUT_FULL_SCREEN_WORK) + { + /* This asks us to toggle fullscreen mode */ + flags |= SWP_FRAMECHANGED; + + if (window->State.IsFullscreen) + { + /* If we are fullscreen, resize the current window back to its original size */ + /* printf("OldRect %i,%i to %i,%i\n",window->State.pWState.OldRect.left,window->State.pWState.OldRect.top,window->State.pWState.OldRect.right,window->State.pWState.OldRect.bottom); */ + + /* restore style of window before making it fullscreen */ + SetWindowLong(window->Window.Handle, GWL_STYLE, window->State.pWState.OldStyle); + SetWindowLong(window->Window.Handle, GWL_EXSTYLE, window->State.pWState.OldStyleEx); + + /* Then set up resize/reposition, unless user already queued up reshape/position work */ + if (!(workMask & GLUT_POSITION_WORK)) + { + workMask |= GLUT_POSITION_WORK; + window->State.DesiredXpos = window->State.pWState.OldRect.left; + window->State.DesiredYpos = window->State.pWState.OldRect.top; + } + if (!(workMask & GLUT_SIZE_WORK)) + { + workMask |= GLUT_SIZE_WORK; + window->State.DesiredWidth = window->State.pWState.OldRect.right - window->State.pWState.OldRect.left; + window->State.DesiredHeight = window->State.pWState.OldRect.bottom - window->State.pWState.OldRect.top; + } + + /* We'll finish off the fullscreen operation below after the other GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK */ + } + else + { + /* we are currently not fullscreen, go to fullscreen: + * remove window decoration and then maximize + */ + RECT rect; + HMONITOR hMonitor; + MONITORINFO mi; + DWORD newStyle; + + /* save current window rect, style, exstyle and maximized state */ + window->State.pWState.OldMaximized = !!IsZoomed(window->Window.Handle); + if (window->State.pWState.OldMaximized) + /* We force the window into restored mode before going + * fullscreen because Windows doesn't seem to hide the + * taskbar if the window is in the maximized state. + */ + SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + fghGetClientArea( &window->State.pWState.OldRect, window, GL_TRUE ); + window->State.pWState.OldStyle = GetWindowLong(window->Window.Handle, GWL_STYLE); + window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE); + + /* remove decorations from style */ + newStyle = window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME); + if (fgState.DisplayMode & GLUT_STEREO) + { + /* stereo mode does not engage on nVidia stereo buffers. This kills child + windows, but we make the guess that those are rare for stereo windows. */ + newStyle |= WS_POPUP; + } + SetWindowLong(window->Window.Handle, GWL_STYLE, newStyle); + SetWindowLong(window->Window.Handle, GWL_EXSTYLE, + window->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME | + WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); + + /* For fullscreen mode, find the monitor that is covered the most + * by the window and get its rect as the resize target. + */ + hMonitor= MonitorFromWindow(window->Window.Handle, MONITOR_DEFAULTTONEAREST); + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + rect = mi.rcMonitor; + + /* then setup window resize, overwriting other work queued on the window */ + window->State.WorkMask |= GLUT_POSITION_WORK | GLUT_SIZE_WORK; + window->State.WorkMask &= ~GLUT_ZORDER_WORK; + window->State.DesiredXpos = rect.left; + window->State.DesiredYpos = rect.top; + window->State.DesiredWidth = rect.right - rect.left; + window->State.DesiredHeight = rect.bottom - rect.top; + } + } +#endif /*!defined(_WIN32_WCE) */ + + /* Now deal with normal position, reshape and z order requests (some might have been set when handling GLUT_FULLSCREEN_WORK above */ + { + /* get rect describing window's current position and size, + * in screen coordinates and in FreeGLUT format + * (size (right-left, bottom-top) is client area size, top and left + * are outside of window including decorations). + */ + fghGetClientArea( &clientRect, window, TRUE ); + + if (workMask & GLUT_POSITION_WORK) + { + flags &= ~SWP_NOMOVE; + + /* Move rect so that top-left is at requested position */ + /* This also automatically makes sure that child window requested coordinates are relative + * to top-left of parent's client area (needed input for SetWindowPos on child windows), + * so no need to further correct rect for child windows below (childs don't have decorations either). + */ + OffsetRect(&clientRect,window->State.DesiredXpos-clientRect.left,window->State.DesiredYpos-clientRect.top); + } + if (workMask & GLUT_SIZE_WORK) + { + flags &= ~SWP_NOSIZE; + + /* Note on maximizing behavior of Windows: the resize borders are off + * the screen such that the client area extends all the way from the + * leftmost corner to the rightmost corner to maximize screen real + * estate. A caption is still shown however to allow interaction with + * the window controls. This is default behavior of Windows that + * FreeGLUT sticks with. To alter, one would have to check if + * WS_MAXIMIZE style is set when a resize event is triggered, and + * then manually correct the windowRect to put the borders back on + * screen. + */ + + /* Set new size of window, WxH specify client area */ + clientRect.right = clientRect.left + window->State.DesiredWidth; + clientRect.bottom = clientRect.top + window->State.DesiredHeight; + } + if (workMask & GLUT_ZORDER_WORK) + { + flags &= ~SWP_NOZORDER; + + /* Could change this to push it down or up one window at a time with some + * more code using GetWindow with GW_HWNDPREV and GW_HWNDNEXT. + * What would be consistent with X11? Win32 GLUT does what we do here... + */ + if (window->State.DesiredZOrder < 0) + insertAfter = HWND_BOTTOM; + } + } + + /* Adjust for window decorations + * Child windows don't have decoration, so no need to correct + */ + if (!window->Parent) + /* get the window rect from this to feed to SetWindowPos, correct for window decorations */ + fghComputeWindowRectFromClientArea_QueryWindow(&clientRect,window,TRUE); + + /* Do the requested positioning, moving, and z order push/pop. */ + SetWindowPos( window->Window.Handle, + insertAfter, + clientRect.left, clientRect.top, + clientRect.right - clientRect.left, + clientRect.bottom- clientRect.top, + flags + ); + + /* Finish off the fullscreen operation we were doing, if any */ + if (workMask & GLUT_FULL_SCREEN_WORK) + { + if (window->State.IsFullscreen) + { + /* leaving fullscreen, restore maximized state, if any */ + if (window->State.pWState.OldMaximized) + SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + + window->State.IsFullscreen = GL_FALSE; + } + else + window->State.IsFullscreen = GL_TRUE; + } +} + + +void fgPlatformVisibilityWork(SFG_Window* window) +{ + /* Visibility status of window gets updated in the WM_SHOWWINDOW and WM_SIZE handlers */ + int cmdShow = 0; + SFG_Window *win = window; + switch (window->State.DesiredVisibility) + { + case DesireHiddenState: + cmdShow = SW_HIDE; + break; + case DesireIconicState: + cmdShow = SW_MINIMIZE; + /* Call on top-level window */ + while (win->Parent) + win = win->Parent; + break; + case DesireNormalState: + if (win->IsMenu && !fgStructure.GameModeWindow) + cmdShow = SW_SHOWNA; /* Just show, don't activate window if its a menu. Only exception is when there is a gamemode window as the menu would pop under it when we do this... */ + else + cmdShow = SW_SHOW; + break; + } + + ShowWindow( win->Window.Handle, cmdShow ); +}