strdup instead of malloc/strcpy
[freeglut] / src / mswin / fg_window_mswin.c
index 0698349..4365b8c 100644 (file)
@@ -84,13 +84,7 @@ typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShar
 typedef BOOL (WINAPI *pRegisterTouchWindow)(HWND,ULONG);
 static pRegisterTouchWindow fghRegisterTouchWindow = (pRegisterTouchWindow)0xDEADBEEF;
 #endif
-
-/* 
- * Helper functions for getting client area from the window rect
- * and the window rect from the client area given the style of the window
- * (or a valid window pointer from which the style can be queried).
- */
-extern void fghGetBorderWidth(const DWORD windowStyle, int* xBorderWidth, int* yBorderWidth);
+extern void fghNotifyWindowStatus(SFG_Window *window);
 
 
 /*
@@ -378,6 +372,39 @@ void fgPlatformSetWindow ( SFG_Window *window )
 }
 
 
+void fghGetDefaultWindowStyle(DWORD *flags)
+{
+    if ( fgState.DisplayMode & GLUT_BORDERLESS )
+    {
+        /* no window decorations needed, no-op */
+    }
+    else if ( fgState.DisplayMode & GLUT_CAPTIONLESS )
+        /* only window decoration is a border, no title bar or buttons */
+        (*flags) |= WS_DLGFRAME;
+    else
+        /* window decoration are a border, title bar and buttons. */
+        (*flags) |= WS_OVERLAPPEDWINDOW;
+}
+
+/* Get window style and extended window style of a FreeGLUT window
+ * If the window pointer or the window handle is NULL, a fully
+ * decorated window (caption and border) is assumed.
+ */
+void fghGetStyleFromWindow( const SFG_Window *window, DWORD *windowStyle, DWORD *windowExStyle )
+{
+    if (window && window->Window.Handle)
+    {
+        *windowStyle   = GetWindowLong(window->Window.Handle, GWL_STYLE);
+        *windowExStyle = GetWindowLong(window->Window.Handle, GWL_EXSTYLE);
+    }
+    else
+    {
+        *windowStyle   = 0;
+        fghGetDefaultWindowStyle(windowStyle);
+        /* WindowExStyle==0 is fine/default, exStyle is currently only used for menu windows */
+        *windowExStyle = 0;
+    }
+}
 
 /* Computes position of corners of window Rect (outer position including
  * decorations) based on the provided client rect and based on the style
@@ -388,33 +415,25 @@ void fgPlatformSetWindow ( SFG_Window *window )
  * (rect.right-rect.left,rect.bottom-rect.top) is the size of the drawable
  * area.
  */
-void fghComputeWindowRectFromClientArea_UseStyle( const DWORD windowStyle, RECT *clientRect, BOOL posIsOutside )
+void fghComputeWindowRectFromClientArea_UseStyle( RECT *clientRect, const DWORD windowStyle, const DWORD windowExStyle, BOOL posIsOutside )
 {
-    int xBorderWidth = 0, yBorderWidth = 0;
+    RECT windowRect   = {0,0,0,0};
+    CopyRect(&windowRect,clientRect);
 
-    /* If window has title bar, correct rect for it */
-    if (windowStyle & WS_MAXIMIZEBOX) /* Need to query for WS_MAXIMIZEBOX to see if we have a title bar, the WS_CAPTION query is also true for a WS_DLGFRAME only... */
-        if (posIsOutside)
-            clientRect->bottom += GetSystemMetrics( SM_CYCAPTION );
-        else
-            clientRect->top -= GetSystemMetrics( SM_CYCAPTION );
+    /* Get rect including non-client area */
+    AdjustWindowRectEx(&windowRect,windowStyle,FALSE,windowExStyle);
 
-    /* get width of window's borders (frame), correct rect for it.
-     * Note, borders can be of zero width if style does not specify borders
-     */
-    fghGetBorderWidth(windowStyle, &xBorderWidth, &yBorderWidth);
+    /* Move window right and down by non-client area extent on left and top, if wanted */
     if (posIsOutside)
     {
-        clientRect->right  += xBorderWidth * 2;
-        clientRect->bottom += yBorderWidth * 2;
-    }
-    else
-    {
-        clientRect->left   -= xBorderWidth;
-        clientRect->right  += xBorderWidth;
-        clientRect->top    -= yBorderWidth;
-        clientRect->bottom += yBorderWidth;
+        windowRect.right   += clientRect->left-windowRect.left;
+        windowRect.bottom  += clientRect->top -windowRect.top;
+        windowRect.left     = clientRect->left;
+        windowRect.top      = clientRect->top;
     }
+    
+    /* done, copy windowRect to output */
+    CopyRect(clientRect,&windowRect);
 }
 
 /* Computes position of corners of window Rect (outer position including
@@ -427,111 +446,33 @@ void fghComputeWindowRectFromClientArea_UseStyle( const DWORD windowStyle, RECT
  * (rect.right-rect.left,rect.bottom-rect.top) is the size of the drawable
  * area.
 */
-void fghComputeWindowRectFromClientArea_QueryWindow( const SFG_Window *window, RECT *clientRect, BOOL posIsOutside )
-{
-    DWORD windowStyle = 0;
-
-    if (window && window->Window.Handle)
-        windowStyle = GetWindowLong(window->Window.Handle, GWL_STYLE);
-    else
-        windowStyle = WS_OVERLAPPEDWINDOW;
-
-    fghComputeWindowRectFromClientArea_UseStyle(windowStyle, clientRect, posIsOutside);
-}
-
-/* Computes position of corners of client area (drawable area) of a window
- * based on the provided window Rect (outer position including decorations)
- * and based on the style of the window in question. If the window pointer
- * or the window handle is NULL, a fully decorated window (caption and
- * border) is assumed.
- * Furthermore, if wantPosOutside is set to true, the output client Rect
- * will follow freeGLUT's window specification convention in which the
- * top-left corner is at the outside of the window, the size
- * (rect.right-rect.left,rect.bottom-rect.top) is the size of the drawable
- * area.
- */
-void fghComputeClientAreaFromWindowRect( const SFG_Window *window, RECT *windowRect, BOOL wantPosOutside )
+void fghComputeWindowRectFromClientArea_QueryWindow( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside )
 {
-    DWORD windowStyle = 0;
-    int xBorderWidth = 0, yBorderWidth = 0;
-
-    if (window && window->Window.Handle)
-        windowStyle = GetWindowLong(window->Window.Handle, GWL_STYLE);
-    else
-        windowStyle = WS_OVERLAPPEDWINDOW;
-
-    /* If window has title bar, correct rect for it */
-    if (windowStyle & WS_MAXIMIZEBOX) /* Need to query for WS_MAXIMIZEBOX to see if we have a title bar, the WS_CAPTION query is also true for a WS_DLGFRAME only... */
-        if (wantPosOutside)
-            windowRect->bottom -= GetSystemMetrics( SM_CYCAPTION );
-        else
-            windowRect->top    += GetSystemMetrics( SM_CYCAPTION );
+    DWORD windowStyle = 0, windowExStyle = 0;
+    fghGetStyleFromWindow(window,&windowStyle,&windowExStyle);
 
-    /* get width of window's borders (frame), correct rect for it.
-     * Note, borders can be of zero width if style does not specify borders
-     */
-    fghGetBorderWidth(windowStyle, &xBorderWidth, &yBorderWidth);
-    if (wantPosOutside)
-    {
-        windowRect->right  -= xBorderWidth * 2;
-        windowRect->bottom -= yBorderWidth * 2;
-    }
-    else
-    {
-        windowRect->left   += xBorderWidth;
-        windowRect->right  -= xBorderWidth;
-        windowRect->top    += yBorderWidth;
-        windowRect->bottom -= yBorderWidth;
-    }
+    fghComputeWindowRectFromClientArea_UseStyle(clientRect, windowStyle, windowExStyle, posIsOutside);
 }
 
 /* Gets the rect describing the client area (drawable area) of the
- * specified window.
- * Returns an empty rect if window pointer or window handle is NULL.
- * If wantPosOutside is set to true, the output client Rect
- * will follow freeGLUT's window specification convention in which the
- * top-left corner is at the outside of the window, while the size
- * (rect.right-rect.left,rect.bottom-rect.top) is the size of the drawable
- * area.
+ * specified window. Output is position of corners of client area (drawable area) on the screen.
+ * Does not touch clientRect if window pointer or window handle is NULL.
+ * (rect.right-rect.left,rect.bottom-rect.top) is the size of the drawable area.
  */
-RECT fghGetClientArea( const SFG_Window *window, BOOL wantPosOutside )
+void fghGetClientArea( RECT *clientRect, const SFG_Window *window )
 {
-    RECT windowRect = {0,0,0,0};
+    POINT topLeftClient = {0,0};
 
-    freeglut_return_val_if_fail((window && window->Window.Handle),windowRect);
+    freeglut_return_if_fail((window && window->Window.Handle));
     
-    /*
-     * call GetWindowRect()
-     * (this returns the pixel coordinates of the outside of the window)
-     */
-    GetWindowRect( window->Window.Handle, &windowRect );
-
-    /* Then correct the results */
-    fghComputeClientAreaFromWindowRect(window, &windowRect, wantPosOutside);
-
-    return windowRect;
+    /* Get size of client rect */
+    GetClientRect(window->Window.Handle, clientRect);
+    /* Get position of top-left of client area on the screen */
+    ClientToScreen(window->Window.Handle,&topLeftClient);
+    /* Add top-left offset */
+    OffsetRect(clientRect,topLeftClient.x,topLeftClient.y);
 }
 
-/* Returns the width of the window borders based on the window's style.
- */
-void fghGetBorderWidth(const DWORD windowStyle, int* xBorderWidth, int* yBorderWidth)
-{
-    if (windowStyle & WS_THICKFRAME)
-    {
-        *xBorderWidth = GetSystemMetrics(SM_CXSIZEFRAME);
-        *yBorderWidth = GetSystemMetrics(SM_CYSIZEFRAME);
-    }
-    else if (windowStyle & WS_DLGFRAME)
-    {
-        *xBorderWidth = GetSystemMetrics(SM_CXFIXEDFRAME);
-        *yBorderWidth = GetSystemMetrics(SM_CYFIXEDFRAME);
-    }
-    else
-    {
-        *xBorderWidth = 0;
-        *yBorderWidth = 0;
-    }
-}
 
 #if(WINVER >= 0x500)
 typedef struct
@@ -651,23 +592,9 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title,
 #if defined(_WIN32_WCE)
         /* no decorations for windows CE */
 #else
-        /* if this is not a subwindow (child), set its style based on the requested display mode */
+        /* if this is not a subwindow (child), set its style based on the requested window decorations */
         else if( window->Parent == NULL )
-            if ( fgState.DisplayMode & GLUT_BORDERLESS )
-            {
-                /* no window decorations needed */
-            }
-            else if ( fgState.DisplayMode & GLUT_CAPTIONLESS )
-                /* only window decoration is a border, no title bar or buttons */
-                flags |= WS_DLGFRAME;
-            else
-                /* window decoration are a border, title bar and buttons.
-                 * NB: we later query whether the window has a title bar or
-                 * not by testing for the maximize button, as the test for
-                 * WS_CAPTION can be true without the window having a title
-                 * bar. This style WS_OVERLAPPEDWINDOW gives you a maximize
-                 * button. */
-                flags |= WS_OVERLAPPEDWINDOW;
+            fghGetDefaultWindowStyle(&flags);
 #endif
         else
             /* subwindows always have no decoration, but are marked as a child window to the OS */
@@ -724,8 +651,9 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title,
         windowRect.right    = x+w;
         windowRect.bottom   = y+h;
 
-        fghComputeWindowRectFromClientArea_UseStyle(flags,&windowRect,TRUE);
+        fghComputeWindowRectFromClientArea_UseStyle(&windowRect,flags,exFlags,TRUE);
 
+        /* NB: w and h are now width and height of window including non-client area! */
         w = windowRect.right - windowRect.left;
         h = windowRect.bottom- windowRect.top;
     }
@@ -772,6 +700,12 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title,
     if( !( window->Window.Handle ) )
         fgError( "Failed to create a window (%s)!", title );
 
+    /* Store title */
+    {
+        window->State.pWState.WindowTitle = malloc (strlen(title) + 1);
+        strcpy(window->State.pWState.WindowTitle, title);
+    }
+
 #if !defined(_WIN32_WCE)
     /* Need to set requested style again, apparently Windows doesn't listen when requesting windows without title bar or borders */
     SetWindowLong(window->Window.Handle, GWL_STYLE, flags);
@@ -799,12 +733,103 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title,
     ShowWindow( window->Window.Handle, SW_SHOW );
 #else
     ShowWindow( window->Window.Handle,
-                fgState.ForceIconic ? SW_SHOWMINIMIZED : SW_SHOW );
+                fgState.ForceIconic ? SW_SHOWMINIMIZED : SW_SHOWNORMAL );
 #endif /* defined(_WIN32_WCE) */
 
-    UpdateWindow( window->Window.Handle );
-    ShowCursor( TRUE );  /* XXX Old comments say "hide cursor"! */
+    ShowCursor( TRUE );
+}
+
+
+void fgPlatformDisplayWindow ( SFG_Window *window )
+{
+    /* This immediately generates a WM_PAINT message upon which we call the display callbacks to redraw the window */
+    RedrawWindow(
+        window->Window.Handle, NULL, NULL,
+        RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW
+        );
+}
+
+
+void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height )
+{
+    RECT windowRect;
+
+    /*
+     * HACK HACK HACK:
+     * Before we do anything else, check if this is a newly created window
+     * that did not have its windowStatus/visibility func called yet
+     * we do that on first paint, but because I want to keep the paint
+     * operation as lean as possible, we do it here. The first paint
+     * goes together with a first resize call before the display callback
+     * is called, so this is just in time. Shitty place to do it, but this
+     * is the only reliable way I can think of to call the callback upon
+     * first draw of the window.
+     * More broadly speaking, I know this is an ugly hack, but I'm not sure
+     * what else to do about it.  Depending on WM_ACTIVATE would not work
+     * as not all windows get this when you are opening multiple before the
+     * mainloop starts. WM_SHOWWINDOW looked like an interesting candidate,
+     * but it is generated and processed before glutCreate(Sub)Window
+     * returns, so no callback can yet be set on the window.
+     */
+    /* Check windowStatus/visibility func has been notified that window is visible (deferred from creation time to give user opportunity to register callbacks) */
+    if (!window->State.pWState.WindowFuncCalled)
+    {
+        fghNotifyWindowStatus(window);
+        window->State.pWState.WindowFuncCalled = GL_TRUE;
+    }
+
+    /*
+     * For windowed mode, get the current position of the
+     * window and resize taking the size of the frame
+     * decorations into account.
+     *
+     * 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.
+     */
+
+    /* "GetWindowRect" returns the pixel coordinates of the outside of the window */
+    GetWindowRect( window->Window.Handle, &windowRect );
+
+    /* Create rect in FreeGLUT format, (X,Y) topleft outside window, WxH of client area */
+    windowRect.right    = windowRect.left+width;
+    windowRect.bottom   = windowRect.top+height;
+
+    if (window->Parent == NULL)
+        /* get the window rect from this to feed to SetWindowPos, correct for window decorations */
+        fghComputeWindowRectFromClientArea_QueryWindow(&windowRect,window,TRUE);
+    else
+    {
+        /* correct rect for position client area of parent window
+         * (SetWindowPos input for child windows is in coordinates
+         * relative to the parent's client area).
+         * Child windows don't have decoration, so no need to correct
+         * for them.
+         */
+        RECT parentRect;
+        fghGetClientArea( &parentRect, window->Parent );
+        OffsetRect(&windowRect,-parentRect.left,-parentRect.top);
+    }
+    
+    /* Do the actual resizing */
+    SetWindowPos( window->Window.Handle,
+                  HWND_TOP,
+                  windowRect.left, windowRect.top,
+                  windowRect.right - windowRect.left,
+                  windowRect.bottom- windowRect.top,
+                  SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING |
+                  SWP_NOZORDER
+    );
 
+    /* Set new width and height so we can test for that in WM_SIZE message handler and don't do anything if not needed */
+    window->State.Width  = width;
+    window->State.Height = height;
 }
 
 
@@ -839,6 +864,12 @@ void fgPlatformCloseWindow( SFG_Window* window )
     }
 
     DestroyWindow( window->Window.Handle );
+
+    /* clean up copied title text(s) */
+    if (window->State.pWState.WindowTitle)
+        free(window->State.pWState.WindowTitle);
+    if (window->State.pWState.IconTitle)
+        free(window->State.pWState.IconTitle);
 }
 
 
@@ -864,7 +895,14 @@ void fgPlatformGlutHideWindow( void )
  */
 void fgPlatformGlutIconifyWindow( void )
 {
-    ShowWindow( fgStructure.CurrentWindow->Window.Handle, SW_MINIMIZE );
+    SFG_Window *win = fgStructure.CurrentWindow;
+
+    /* Call on parent window */
+    while (win->Parent)
+        win = win->Parent;
+
+    /* Visibility status of window gets updated in the WM_SHOWWINDOW handler */
+    ShowWindow(win->Window.Handle, SW_MINIMIZE);
 }
 
 /*
@@ -881,6 +919,11 @@ void fgPlatformGlutSetWindowTitle( const char* title )
 #else
     SetWindowText( fgStructure.CurrentWindow->Window.Handle, title );
 #endif
+
+    /* Make copy of string to refer to later */
+    if (fgStructure.CurrentWindow->State.pWState.WindowTitle)
+        free(fgStructure.CurrentWindow->State.pWState.WindowTitle);
+    fgStructure.CurrentWindow->State.pWState.WindowTitle = strdup(title);
 }
 
 /*
@@ -888,15 +931,10 @@ void fgPlatformGlutSetWindowTitle( const char* title )
  */
 void fgPlatformGlutSetIconTitle( const char* title )
 {
-#ifdef _WIN32_WCE
-    {
-        wchar_t* wstr = fghWstrFromStr(title);
-        SetWindowText( fgStructure.CurrentWindow->Window.Handle, wstr );
-        free(wstr);
-    }
-#else
-    SetWindowText( fgStructure.CurrentWindow->Window.Handle, title );
-#endif
+    /* Make copy of string to refer to later */
+    if (fgStructure.CurrentWindow->State.pWState.IconTitle)
+        free(fgStructure.CurrentWindow->State.pWState.IconTitle);
+    fgStructure.CurrentWindow->State.pWState.IconTitle = strdup(title);
 }
 
 /*
@@ -959,7 +997,6 @@ void fgPlatformGlutFullScreen( SFG_Window *win )
 
     {
 #if(WINVER >= 0x0500) /* Windows 2000 or later */
-        DWORD s;
         RECT rect;
         HMONITOR hMonitor;
         MONITORINFO mi;
@@ -971,17 +1008,25 @@ void fgPlatformGlutFullScreen( SFG_Window *win )
          */
 
         
-        /* store current window rect */
-        GetWindowRect( win->Window.Handle, &win->State.pWState.OldRect );
+        /* save current window rect, style, exstyle and maximized state */
+        win->State.pWState.OldMaximized = !!IsZoomed(win->Window.Handle);
+        if (win->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(win->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
 
-        /* store current window style */
-        win->State.pWState.OldStyle = s = GetWindowLong(win->Window.Handle, GWL_STYLE);
+        GetWindowRect( win->Window.Handle, &win->State.pWState.OldRect );
+        win->State.pWState.OldStyle   = GetWindowLong(win->Window.Handle, GWL_STYLE);
+        win->State.pWState.OldStyleEx = GetWindowLong(win->Window.Handle, GWL_EXSTYLE);
 
-        /* remove decorations from style and add popup style*/
-        s &= ~WS_OVERLAPPEDWINDOW;
-        s |= WS_POPUP;
-        SetWindowLong(win->Window.Handle, GWL_STYLE, s);
-        SetWindowPos(win->Window.Handle, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+        /* remove decorations from style */
+        SetWindowLong(win->Window.Handle, GWL_STYLE,
+                      win->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME));
+        SetWindowLong(win->Window.Handle, GWL_EXSTYLE,
+                      win->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.
@@ -1043,7 +1088,7 @@ void fgPlatformGlutLeaveFullScreen( SFG_Window *win )
 
     /* restore style of window before making it fullscreen */
     SetWindowLong(win->Window.Handle, GWL_STYLE, win->State.pWState.OldStyle);
-    SetWindowPos(win->Window.Handle, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+    SetWindowLong(win->Window.Handle, GWL_EXSTYLE, win->State.pWState.OldStyleEx);
 
     /* Then resize */
     SetWindowPos(win->Window.Handle,
@@ -1056,6 +1101,9 @@ void fgPlatformGlutLeaveFullScreen( SFG_Window *win )
         SWP_NOZORDER
         );
 
+    if (win->State.pWState.OldMaximized)
+        SendMessage(win->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
+
     win->State.IsFullscreen = GL_FALSE;
 #endif
 }