implemented SUPER key/modifier support on windows
[freeglut] / src / mswin / fg_main_mswin.c
index 97b8363..e9d455d 100644 (file)
@@ -63,7 +63,7 @@ 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
  */
@@ -474,7 +474,7 @@ fg_time_t fgPlatformSystemTime ( void )
     /* Check if we just wrapped */
     if (currTime32 < lastTime32)
         timeEpoch++;
-    
+
     lastTime32 = currTime32;
 
     return currTime32 | timeEpoch << 32;
@@ -542,7 +542,7 @@ static void fghPlatformOnWindowStatusNotify(SFG_Window *window, GLboolean visSta
     {
         SFG_Window *saved_window = fgStructure.CurrentWindow;
 
-        /* On win32 we only have two states, window displayed and window not displayed (iconified) 
+        /* 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 ) );
@@ -575,7 +575,9 @@ 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
@@ -592,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
@@ -601,7 +603,7 @@ 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( );
 
@@ -631,6 +633,8 @@ static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean ke
         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()
@@ -715,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,
@@ -756,7 +760,7 @@ SFG_Window* fghWindowUnderCursor(SFG_Window *window)
         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 */
         {
@@ -874,7 +878,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
         /* Update visibility state of the window */
         if (wParam==SIZE_MINIMIZED)
             fghPlatformOnWindowStatusNotify(window,GL_FALSE,GL_FALSE);
-        else if (wParam==SIZE_RESTORED && !window->State.Visible)
+        else if ((wParam==SIZE_RESTORED || wParam == SIZE_MAXIMIZED) && !window->State.Visible)
             fghPlatformOnWindowStatusNotify(window,GL_TRUE,GL_FALSE);
 
         /* Check window visible, we don't want do anything when we get a WM_SIZE because the user or glutIconifyWindow minimized the window */
@@ -888,7 +892,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
             width  = LOWORD(lParam);
             height = HIWORD(lParam);
 #endif /* defined(_WIN32_WCE) */
-            
+
             /* Update state and call callback, if there was a change */
             fghOnReshapeNotify(window, width, height, GL_FALSE);
         }
@@ -935,15 +939,15 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
             if (!IsIconic(window->Window.Handle))
             {
                 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 
+                 * 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
@@ -1031,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
@@ -1090,7 +1094,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
     case WM_PAINT:
     {
         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))
         {
@@ -1279,7 +1283,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
     {
         int wheel_number = 0;   /* Only one scroll wheel on windows */
 #if defined(_WIN32_WCE)
-        int modkeys = LOWORD(wParam); 
+        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)
@@ -1296,10 +1300,10 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
 
         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 ) )
@@ -1309,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,
@@ -1319,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, ...
@@ -1338,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;
     }
@@ -1498,61 +1502,61 @@ 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
-       //Added by Jinrong Xie (stonexjr@gmail.com) 12/24/2014
-       //for SpaceNavigator support on Windows.
-       /*
-       case WM_ACTIVATEAPP:
-               fgInitialiseSpaceball();
-               break;
-       */
-       case WM_INPUT:
-               fgSpaceballHandleWinEvent(hWnd, wParam, lParam);
-               break;
     default:
         /* Handle unhandled messages */
         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
@@ -1615,7 +1619,7 @@ void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
                 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
@@ -1626,7 +1630,8 @@ void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
             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)
@@ -1641,15 +1646,21 @@ void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
             window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE);
 
             /* remove decorations from style */
-            SetWindowLong(window->Window.Handle, GWL_STYLE,
-                            window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME));
+            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);
@@ -1668,7 +1679,7 @@ void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
 
     /* 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, 
+        /* 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).
@@ -1678,7 +1689,7 @@ void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
         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),
@@ -1689,7 +1700,7 @@ void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
         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
@@ -1724,7 +1735,7 @@ void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
     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,