From 637e1260edb9068591665330c32407549ca36535 Mon Sep 17 00:00:00 2001 From: Diederick Niehorster Date: Fri, 16 Nov 2012 11:02:06 +0000 Subject: [PATCH] reimplemented the client-area/window-area conversion code. It should now be robust across Windows and Visual Studio versions. Bugs in window size with VS2012 fixed! git-svn-id: svn+ssh://svn.code.sf.net/p/freeglut/code/trunk/freeglut/freeglut@1394 7f0cb862-5218-0410-a997-914c9d46530a --- progs/demos/Resizer/Resizer.cpp | 14 +++- src/mswin/fg_main_mswin.c | 13 ++-- src/mswin/fg_menu_mswin.c | 11 +-- src/mswin/fg_state_mswin.c | 47 ++++++++---- src/mswin/fg_window_mswin.c | 159 ++++++++++++++------------------------- 5 files changed, 110 insertions(+), 134 deletions(-) diff --git a/progs/demos/Resizer/Resizer.cpp b/progs/demos/Resizer/Resizer.cpp index 76d55f7..bd19ffe 100644 --- a/progs/demos/Resizer/Resizer.cpp +++ b/progs/demos/Resizer/Resizer.cpp @@ -220,13 +220,19 @@ void Redisplay(void) int main(int argc, char* argv[]) { + int border, caption; glutInit( &argc, argv ); glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE /*| GLUT_BORDERLESS*/); // do try as well with GLUT_BORDERLESS and GLUT_CAPTIONLESS glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE,GLUT_ACTION_GLUTMAINLOOP_RETURNS); - - /* The window position you request is the outer top-left of the window, - * the client area is at a different position if the window has borders - * and/or a title bar. + + /* Get border and caption size of default window style */ + border = glutGet(GLUT_WINDOW_BORDER_WIDTH); + caption = glutGet(GLUT_WINDOW_HEADER_HEIGHT); + printf("default window style border: %dpx, caption: %dpx\n",border,caption); + + /* NB: The window position you request is the outer top-left of the + * window, the client area is at a different position if the window has + * borders and/or a title bar. */ glutInitWindowPosition(150,250); glutInitWindowSize(200,200); diff --git a/src/mswin/fg_main_mswin.c b/src/mswin/fg_main_mswin.c index d778d67..a9f8552 100644 --- a/src/mswin/fg_main_mswin.c +++ b/src/mswin/fg_main_mswin.c @@ -63,8 +63,8 @@ struct GXKeyList gxKeyList; * 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 fghComputeWindowRectFromClientArea_QueryWindow( const SFG_Window *window, RECT *clientRect, BOOL posIsOutside ); -extern RECT fghGetClientArea ( const SFG_Window *window, BOOL wantPosOutside ); +extern void fghComputeWindowRectFromClientArea_QueryWindow( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside ); +extern void fghGetClientArea ( RECT *clientRect, const SFG_Window *window, BOOL wantPosOutside ); void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ) @@ -86,7 +86,7 @@ void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ) if (window->Parent == NULL) /* get the window rect from this to feed to SetWindowPos, correct for window decorations */ - fghComputeWindowRectFromClientArea_QueryWindow(window,&windowRect,TRUE); + fghComputeWindowRectFromClientArea_QueryWindow(&windowRect,window,TRUE); else { /* correct rect for position client area of parent window @@ -96,11 +96,8 @@ void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ) * for them. */ RECT parentRect; - parentRect = fghGetClientArea( window->Parent, FALSE ); - windowRect.left -= parentRect.left; - windowRect.right -= parentRect.left; - windowRect.top -= parentRect.top; - windowRect.bottom -= parentRect.top; + fghGetClientArea( &parentRect, window->Parent, FALSE ); + OffsetRect(&windowRect,-parentRect.left,-parentRect.top); } /* Do the actual resizing */ diff --git a/src/mswin/fg_menu_mswin.c b/src/mswin/fg_menu_mswin.c index 0d6a599..3d83693 100644 --- a/src/mswin/fg_menu_mswin.c +++ b/src/mswin/fg_menu_mswin.c @@ -29,7 +29,7 @@ #include #include "../fg_internal.h" -extern RECT fghGetClientArea( const SFG_Window *window, BOOL wantPosOutside ); +extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window, BOOL wantPosOutside ); GLvoid fgPlatformGetGameModeVMaxExtent( SFG_Window* window, int* x, int* y ) @@ -69,11 +69,12 @@ void fgPlatformCheckMenuDeactivate() else { /* Check if focus lost because non-client area of - * window was pressed (pressing on client area is - * handled in fgCheckActiveMenu) - */ + * window was pressed (pressing on client area is + * handled in fgCheckActiveMenu) + */ POINT mouse_pos; - RECT clientArea = fghGetClientArea(menu->ParentWindow, GL_FALSE); + RECT clientArea; + fghGetClientArea(&clientArea,menu->ParentWindow, GL_FALSE); GetCursorPos(&mouse_pos); if ( !PtInRect( &clientArea, mouse_pos ) ) fgDeactivateMenu(menu->ParentWindow); diff --git a/src/mswin/fg_state_mswin.c b/src/mswin/fg_state_mswin.c index 91256a3..659ceca 100644 --- a/src/mswin/fg_state_mswin.c +++ b/src/mswin/fg_state_mswin.c @@ -37,8 +37,9 @@ extern GLboolean fgSetupPixelFormat( SFG_Window* window, GLboolean checkOnly, * 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 RECT fghGetClientArea( const SFG_Window *window, BOOL wantPosOutside ); -extern void fghGetBorderWidth(const DWORD windowStyle, int* xBorderWidth, int* yBorderWidth); +extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window, BOOL wantPosOutside ); +extern void fghGetStyleFromWindow( const SFG_Window *window, DWORD *windowStyle, DWORD *windowExStyle ); +extern void fghComputeWindowRectFromClientArea_UseStyle( RECT *clientRect, const DWORD windowStyle, const DWORD windowExStyle, BOOL posIsOutside ); /* The following include file is available from SGI but is not standard: @@ -165,7 +166,7 @@ int fgPlatformGlutGet ( GLenum eWhat ) * behaviour, both under Windows and under UNIX/X11: * - When you create a window with position (x,y) and size * (w,h), the upper left hand corner of the outside of the - * window is at (x,y) and the size of the drawable area is + * window is at (x,y) and the size of the drawable area is * (w,h). * - When you query the size and position of the window--as * is happening here for Windows--"freeglut" will return @@ -182,7 +183,7 @@ int fgPlatformGlutGet ( GLenum eWhat ) #if defined(_WIN32_WCE) GetWindowRect( fgStructure.CurrentWindow->Window.Handle, &winRect ); #else - winRect = fghGetClientArea(fgStructure.CurrentWindow, FALSE); + fghGetClientArea(&winRect,fgStructure.CurrentWindow, FALSE); if (fgStructure.CurrentWindow->Parent && (eWhat==GLUT_WINDOW_X || eWhat==GLUT_WINDOW_Y)) { /* For child window, we should return relative to upper-left @@ -214,25 +215,45 @@ int fgPlatformGlutGet ( GLenum eWhat ) return 0; #else { - DWORD windowStyle; - + /* We can't get the border width or header height in the simple way + * with some calls to GetSystemMetrics. We'd then have to assume which + * elements are present for a given decoration, and such calculations + * wouldn't be valid for every version of Windows. The below should be + * robust. */ + int borderWidth, captionHeight; + DWORD windowStyle, windowExStyle; + RECT clientRect, winRect; + + /* Get style of window, or default style */ + fghGetStyleFromWindow( fgStructure.CurrentWindow, &windowStyle, &windowExStyle ); + /* Get client area if any window */ if (fgStructure.CurrentWindow && fgStructure.CurrentWindow->Window.Handle) - windowStyle = GetWindowLong(fgStructure.CurrentWindow->Window.Handle, GWL_STYLE); + fghGetClientArea(&clientRect,fgStructure.CurrentWindow,FALSE); else - /* If no window, return sizes for a default window with title bar and border */ - windowStyle = WS_OVERLAPPEDWINDOW; + SetRect(&clientRect,0,0,200,200); + + /* Compute window rect (including non-client area) */ + CopyRect(&winRect,&clientRect); + fghComputeWindowRectFromClientArea_UseStyle(&winRect,windowStyle,windowExStyle,FALSE); + + /* Calculate border width by taking width of whole window minus width of client area and divide by two + * NB: we assume horizontal and vertical borders have the same size, which should always be the case + * unless the user bypassed FreeGLUT and messed with the windowstyle himself. + * Once borderwidth is known, account for it when comparing height of window to height of client area. + * all other extra pixels are assumed to be atop the window, forming the caption. + */ + borderWidth = ((winRect.right-winRect.left)-(clientRect.right-clientRect.left))/2; + captionHeight = (winRect.bottom-winRect.top)-(clientRect.bottom-clientRect.top)-borderWidth*2; switch( eWhat ) { case GLUT_WINDOW_BORDER_WIDTH: { - int xBorderWidth, yBorderWidth; - fghGetBorderWidth(windowStyle, &xBorderWidth, &yBorderWidth); - return xBorderWidth; + return borderWidth; } case GLUT_WINDOW_HEADER_HEIGHT: /* 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... */ - return (windowStyle & WS_MAXIMIZEBOX)? GetSystemMetrics( SM_CYCAPTION ) : 0; + return captionHeight; } } #endif /* defined(_WIN32_WCE) */ diff --git a/src/mswin/fg_window_mswin.c b/src/mswin/fg_window_mswin.c index 7aef7a7..7720929 100644 --- a/src/mswin/fg_window_mswin.c +++ b/src/mswin/fg_window_mswin.c @@ -371,29 +371,26 @@ void fgPlatformSetWindow ( SFG_Window *window ) } -/* Returns the width of the window borders based on the window's style. -*/ -void fghGetBorderWidth(const DWORD windowStyle, int* xBorderWidth, int* yBorderWidth) +/* 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 (windowStyle & WS_THICKFRAME) - { - *xBorderWidth = GetSystemMetrics(SM_CXSIZEFRAME); - *yBorderWidth = GetSystemMetrics(SM_CYSIZEFRAME); - } - else if (windowStyle & WS_DLGFRAME) + if (window && window->Window.Handle) { - *xBorderWidth = GetSystemMetrics(SM_CXFIXEDFRAME); - *yBorderWidth = GetSystemMetrics(SM_CYFIXEDFRAME); + *windowStyle = GetWindowLong(window->Window.Handle, GWL_STYLE); + *windowExStyle = GetWindowLong(window->Window.Handle, GWL_EXSTYLE); } else { - *xBorderWidth = 0; - *yBorderWidth = 0; + /* WindowExStyle==0 is fine/default, exStyle is currently only used for menu windows */ + *windowStyle = WS_OVERLAPPEDWINDOW; + *windowExStyle = 0; } } - /* Computes position of corners of window Rect (outer position including * decorations) based on the provided client rect and based on the style * of the window in question. @@ -403,35 +400,25 @@ void fghGetBorderWidth(const DWORD windowStyle, int* xBorderWidth, int* yBorderW * (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 @@ -444,68 +431,17 @@ 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 ) +void fghComputeWindowRectFromClientArea_QueryWindow( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside ) { - DWORD windowStyle = 0; - - if (window && window->Window.Handle) - windowStyle = GetWindowLong(window->Window.Handle, GWL_STYLE); - else - windowStyle = WS_OVERLAPPEDWINDOW; + DWORD windowStyle = 0, windowExStyle = 0; + fghGetStyleFromWindow(window,&windowStyle,&windowExStyle); - fghComputeWindowRectFromClientArea_UseStyle(windowStyle, clientRect, posIsOutside); + fghComputeWindowRectFromClientArea_UseStyle(clientRect, windowStyle, windowExStyle, 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 ) -{ - 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 ); - } - - /* 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; - } -} /* Gets the rect describing the client area (drawable area) of the - * specified window. + * specified window. Output is position of corners of client area (drawable area) on the screen. * 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 @@ -513,24 +449,38 @@ void fghComputeClientAreaFromWindowRect( const SFG_Window *window, RECT *windowR * (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, BOOL wantPosOutside ) { - RECT windowRect = {0,0,0,0}; + POINT topLeftClient = {0,0}; + POINT topLeftWindow = {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) - * cannot use GetClientRect as it seems to return a rect relative to + * cannot use GetClientRect as it returns a rect relative to * the top-left point of the client area (.top and .left are thus always 0) + * and is thus only useful for querying the size of the client area, not + * its position. */ - GetWindowRect( window->Window.Handle, &windowRect ); - - /* Then correct the results */ - fghComputeClientAreaFromWindowRect(window, &windowRect, wantPosOutside); - - return windowRect; + GetWindowRect( window->Window.Handle, clientRect ); + topLeftWindow.x = clientRect->top; + topLeftWindow.y = clientRect->left; + + /* 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); + + /* replace top and left with top and left of window, if wanted */ + if (wantPosOutside) + { + clientRect->left = topLeftWindow.x; + clientRect->top = topLeftWindow.y; + } } #if(WINVER >= 0x500) @@ -724,8 +674,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; } -- 1.7.10.4