X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Fmswin%2Ffg_window_mswin.c;h=834250343c5f606a3da60b551724f7670df70bf4;hb=bac05204a224d9cabe6b7d52c00d2e0601825cb4;hp=6515063e6c32e00bc3e9ed61ecf5f9da58a2daca;hpb=5b3d339481bac6dbaeb599bffc1325f716585bfe;p=freeglut diff --git a/src/mswin/fg_window_mswin.c b/src/mswin/fg_window_mswin.c index 6515063..8342503 100644 --- a/src/mswin/fg_window_mswin.c +++ b/src/mswin/fg_window_mswin.c @@ -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); /* @@ -113,10 +107,8 @@ static wchar_t* fghWstrFromStr(const char* str) static void fghFillContextAttributes( int *attributes ) { int where = 0, contextFlags, contextProfile; - if ( !fghIsLegacyContextVersionRequested() ) { - ATTRIB_VAL( WGL_CONTEXT_MAJOR_VERSION_ARB, fgState.MajorVersion ); - ATTRIB_VAL( WGL_CONTEXT_MINOR_VERSION_ARB, fgState.MinorVersion ); - } + ATTRIB_VAL( WGL_CONTEXT_MAJOR_VERSION_ARB, fgState.MajorVersion ); + ATTRIB_VAL( WGL_CONTEXT_MINOR_VERSION_ARB, fgState.MinorVersion ); contextFlags = fghMapBit( fgState.ContextFlags, GLUT_DEBUG, WGL_CONTEXT_DEBUG_BIT_ARB ) | @@ -163,6 +155,9 @@ void fgNewWGLCreateContext( SFG_Window* window ) if ( !fghIsExtensionSupported( window->Window.pContext.Device, "WGL_ARB_create_context" ) ) { + /* wglCreateContextAttribsARB not found, yet the user has requested the new context creation */ + fgWarning( "OpenGL >2.1 context requested but wglCreateContextAttribsARB is not available! Falling back to legacy context creation" ); + /* Legacy context already created at this point in WM_CREATE path of fgPlatformWindowProc, just return */ return; } @@ -172,7 +167,10 @@ void fgNewWGLCreateContext( SFG_Window* window ) wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress( "wglCreateContextAttribsARB" ); if ( wglCreateContextAttribsARB == NULL ) { - fgError( "wglCreateContextAttribsARB not found" ); + /* wglCreateContextAttribsARB not found, yet the user has requested the new context creation */ + fgWarning( "OpenGL >2.1 context requested but wglCreateContextAttribsARB is not available! Falling back to legacy context creation" ); + /* Legacy context already created at this point in WM_CREATE path of fgPlatformWindowProc, just return */ + return; } context = wglCreateContextAttribsARB( window->Window.pContext.Device, 0, attributes ); @@ -374,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 @@ -384,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 @@ -423,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 ) +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); -} - -/* 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; - } + 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 @@ -647,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 */ @@ -720,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; } @@ -768,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); @@ -795,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; } @@ -835,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); } @@ -860,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); } /* @@ -877,6 +919,12 @@ 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 = malloc (strlen(title) + 1); + strcpy(fgStructure.CurrentWindow->State.pWState.WindowTitle, title); } /* @@ -884,15 +932,11 @@ 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 = malloc (strlen(title) + 1); + strcpy(fgStructure.CurrentWindow->State.pWState.IconTitle, title); } /* @@ -955,7 +999,6 @@ void fgPlatformGlutFullScreen( SFG_Window *win ) { #if(WINVER >= 0x0500) /* Windows 2000 or later */ - DWORD s; RECT rect; HMONITOR hMonitor; MONITORINFO mi; @@ -967,17 +1010,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. @@ -1039,7 +1090,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, @@ -1052,6 +1103,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 }