implemented SUPER key/modifier support on windows
[freeglut] / src / mswin / fg_main_mswin.c
index 3cbac50..e9d455d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * freeglut_main_mswin.c
+ * fg_main_mswin.c
  *
  * The Windows-specific mouse cursor related stuff.
  *
 #include <GL/freeglut.h>
 #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,17 +515,14 @@ 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;
-        /* 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 ) );
 
         /* 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)
@@ -150,24 +534,30 @@ static void fghUpdateWindowStatus(SFG_Window *window, GLboolean visState)
                 /* not visible, set icon title */
                 SetWindowText( window->Window.Handle, window->State.pWState.IconTitle );
         }
+
+        notify = GL_TRUE;
     }
 
-    /* Also set visibility state for children */
+    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 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 */
@@ -185,7 +575,17 @@ static int fgPlatformGetModifiers (void)
         ( ( ( GetKeyState( VK_LCONTROL ) < 0 ) ||
             ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
         ( ( ( GetKeyState( VK_LMENU    ) < 0 ) ||
-            ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
+            ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 ) |
+               ( ( ( GetKeyState( VK_LWIN     ) < 0 ) ||
+            ( GetKeyState( VK_RWIN     ) < 0 )) ? GLUT_ACTIVE_SUPER : 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)
@@ -194,7 +594,7 @@ static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean ke
                          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
@@ -203,65 +603,67 @@ 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    );
+               FG_KEY( VK_LWIN,   GLUT_KEY_SUPER_L   );
+               FG_KEY( VK_RWIN,   GLUT_KEY_SUPER_R   );
 
     /* 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: */
@@ -289,7 +691,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 )
             );
         }
@@ -317,7 +719,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,
@@ -339,7 +741,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,
@@ -349,14 +751,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 */
         {
@@ -388,8 +792,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 )
     {
@@ -447,21 +851,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)
@@ -488,61 +877,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);
             }
         }
 
@@ -551,8 +972,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 );
@@ -561,18 +981,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;
+
+    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);
 
-            /* Check if there are any open menus that need to be closed */
-            fgPlatformCheckMenuDeactivate();
+            /* 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,
@@ -592,7 +1035,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
@@ -601,14 +1044,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;
                 }
             }
         }
@@ -627,7 +1072,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;
@@ -636,26 +1081,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;
 
@@ -673,12 +1128,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 */
@@ -716,12 +1172,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       */
@@ -796,7 +1253,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 () ;
 
@@ -824,17 +1281,29 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
 
     case WM_MOUSEWHEEL:
     {
-        SFG_Window *child_window = NULL;
         int wheel_number = 0;   /* Only one scroll wheel on windows */
-        /* int GET_KEYSTATE_WPARAM( wParam ); */
-        short ticks = GET_WHEEL_DELTA_WPARAM( wParam );
+#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 ) )
@@ -844,7 +1313,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,
@@ -854,7 +1323,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, ...
@@ -873,13 +1342,13 @@ 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;
     }
@@ -928,42 +1397,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 */
@@ -1057,50 +1502,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 <stonexjr at gmail.com> for SpaceNavigator support on Windows. Dec 2014 */
+        if (fgHasSpaceball())
+        {
+            fgSpaceballHandleWinEvent(hWnd, wParam, lParam);
+        }
+        break;
 #endif
     default:
         /* Handle unhandled messages */
@@ -1110,3 +1565,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 );
+}