Sign in to follow this  
szecs

win32 not to redraw while resize (openGL)+custom window skin+others

Recommended Posts

szecs    2990
Hi all! The question is the opposite as usual: why is the window redrawing constantly when resized/moved? I know this is the behavior most of us want, but I don't (for performance reasons). And a related question: I'm not sure that my redrawing (WM_PAINT) stuff is good at all. The program should redraw, only if I want it to. (So no constant updates) The old version worked as expected: it didn't redraw, while the left button were pressed during resize/move. The updates woks as expected otherwise. Old message loop:
while( !EscapeGame )
{	
	if( PeekMessage(&msg,NULL,0,0,PM_REMOVE) )
	{
						
		if( msg.message==WM_QUIT )
			EscapeGame = true;

		else
		{	TranslateMessage(&msg);
				DispatchMessage(&msg);
		}
		continue;
	}
		
	Display();//if I comment this out, and use WM_PAINT/UpdateWindow in windProc, the problem is the same
	WaitMessage();
}





New message loop:
while( GetMessage( &msg, NULL, 0, 0 ) )
    { 
        if (msg.message==WM_QUIT)
        {
            break;
        }
        else
        {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        }
    }





WindProc:
LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	switch(uMsg)
	{
	case WM_ACTIVATE:
		if( !GameOver && GameStarted )
		{	int minimized = HIWORD(wParam);

			if ( minimized && win_state.Active )
			{		
				PauseTimeStart = timeGetTime();
				win_state.Active = false;
			}
			else if( !minimized && !win_state.Active )
			{	
				TimeStart += (timeGetTime() - PauseTimeStart);
				TimerFunc();
				
				win_state.Active = true;
			}
		}

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_CLOSE:
		
		...

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_GETMINMAXINFO:
		{
			PMINMAXINFO minmax;

			minmax = (PMINMAXINFO)lParam;

			minmax->ptMinTrackSize.x = 400;
			minmax->ptMinTrackSize.y = 300;

		}
		//InvalidateRect(win_state.hWnd, NULL, FALSE);
		//UpdateWindow(win_state.hWnd);
		return 0;

	case WM_TIMER:

		//the old version didn't redraw, even when the timer expired
		// timer values are about 0.5 seconds (quite slow)

		...

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_SIZE:
		if( wParam != SIZE_MINIMIZED )
		{
			SCRN_WDTH = LOWORD(lParam);
			SCRN_HGHT = HIWORD(lParam)-MENUBAR_HEIGHT;
			ResizeWindow(SCRN_WDTH, SCRN_HGHT);

			//InvalidateRect(win_state.hWnd, NULL, FALSE);
			//UpdateWindow(win_state.hWnd);//redraws, even when commented
		}
		return 0;

	case WM_LBUTTONDOWN:
		Lft_Btn = true;
		SetCapture(win_state.hWnd);

		if( Rgt_Btn )
			Lft_Rgt_Dwn = true;

		...

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);
		return 0;

	case WM_MBUTTONDOWN:
		Mdl_Btn = true;
		SetCapture(win_state.hWnd);

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);
		return 0;

	case WM_RBUTTONDOWN:
		Rgt_Btn = true;
		if( Lft_Btn )
			Lft_Rgt_Dwn = true;
		...

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_LBUTTONUP:
		Lft_Btn = false;
		if( !Mdl_Btn )
			ReleaseCapture();
		...

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_MBUTTONUP:	
		Mdl_Btn = false;
		if( !Lft_Btn )
			ReleaseCapture();

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_RBUTTONUP:
		Rgt_Btn = false;
		...

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_MOUSEMOVE:
		MouseX = (int)(short)LOWORD(lParam); 
		MouseY = (int)(short)HIWORD(lParam);

		MouseDX = MouseX - MousePrevX;
		MouseDY = MouseY - MousePrevY;

		MousePrevX = MouseX;
		MousePrevY = MouseY;

		...
		
		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_MOUSE_ROLL:	
		CurrentChar = wParam;

		...

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_CHAR:
		CurrentChar = wParam;
		...

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_KEYDOWN:
		CurrentChar = wParam;
		...

		InvalidateRect(win_state.hWnd, NULL, FALSE);
		UpdateWindow(win_state.hWnd);

		return 0;

	case WM_PAINT: // WM_PAINT, and UpdateWindow is only used with the new version of the message loop

		Display();
		ValidateRect(win_state.hWnd, NULL);

		return 0;
	}

	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}





Note, that this means the window is updated on almost every events, but I will make it to redraw only when I want it to. Or is it something with the validate/invalidate pairs? How can I get back to the "default" redraw behavior? Thanks for answers in advance! [Edited by - szecs on March 16, 2010 5:15:25 AM]

Share this post


Link to post
Share on other sites
szecs    2990
An other question:
Is there a way to define custom skin for a window (titlebar +borders +minimize/close buttons)?

Or I have to make a window with no title bar, and draw the stuff myself + reimplement all the functions? (that's okay, but I don't want to jump into it, if there's a much simpler solution)

Share this post


Link to post
Share on other sites
jwezorek    2663
Quote:
Original post by szecs
Is there a way to define custom skin for a window (titlebar +borders +minimize/close buttons)?

Or I have to make a window with no title bar, and draw the stuff myself + reimplement all the functions? (that's okay, but I don't want to jump into it, if there's a much simpler solution)


The only way I know to do it is to do it all yourself. Create your window barebones with the WS_POPUP style, paint the whole thing, even the stuff that constitutes the "non-client area", in WM_PAINT, and handle WM_NCHITTEST appropriately. WM_NCHITTEST is what tells Windows that the user is, for example, dragging the title bar; it's the key to whole thing.

Share this post


Link to post
Share on other sites
jwezorek    2663
Quote:

And a related question: I'm not sure that my redrawing (WM_PAINT) stuff is good at all.


I'd need to see what the Display() function in your WM_PAINT handler is doing. I'm not following the code: don't understand how Display() is not getting the window's HWND, because aren't you going to have to call BeginPaint() with a window handle?

Share this post


Link to post
Share on other sites
Pet123    122
hi there

1. Why don't you just leave windows OnPint() empty since you are using a timer?
like this :

LRESULT OnPaint(UINT, WPARAM, LPARAM, BOOL &)
{
CPaintDC dc();
return 0;
}




2. When you resize to shrink a WIN32 window, the system very likely wont issue a WM_PAINT message, since your DC is larger than current window size, so no re-paint is needed (the larger part will be clipped anyway)

3. Same as above, when you MOVE a window you should not received WM_PAINT either, since the window DC size is not changed. (Drag while showing content?)

4. InvalidateRect() is just enough and i dont think update window is necessary

5. custom window skin : google 'Layered Window' may help

just my two cents anyway.

Share this post


Link to post
Share on other sites
szecs    2990
Oh, sorry, completely forgot to mention: I use openGL for drawing. So it has its device context set up at initialization.

Thanks for your tip about WM_NCHITTEST, it seems a bit hard (MSDN doesn't explain the use of it in detail), but I have a GUI anyway, so I can implement the stuff there. It's just i don't want to update the window when WM_SIZE, because the rendering is quite slow. It's okay in the program, but can be annoying, when even moving/resizing the window is choppy.

Share this post


Link to post
Share on other sites
szecs    2990
Quote:
Original post by Pet123
hi there

1. Why don't you just leave windows OnPint() empty since you are using a timer?
like this :
*** Source Snippet Removed ***

2. When you resize to shrink a WIN32 window, the system very likely wont issue a WM_PAINT message, since your DC is larger than current window size, so no re-paint is needed (the larger part will be clipped anyway)

3. Same as above, when you MOVE a window you should not received WM_PAINT either, since the window DC size is not changed. (Drag while showing content?)

4. InvalidateRect() is just enough and i dont think update window is necessary

just my two cents anyway.
That's the problem, I don't want to update the window when moving/resizing (I don't call update in WM_SIZE), but it updates regardless of it.
Yes, InvalidateRect is enough, but the problem is the same.

Share this post


Link to post
Share on other sites
szecs    2990
An other question: for some reason MSDN isn't clear for me about this one:
Does SetTimer issue WM_TIMER messages repeatedly, or just issues one message?
My guess is that it issues messages repeatedly, but I'm not sure, maybe there's a bug somewhere in my code.

Sorry, I don't want separate threads for these small questions.

EDIT: yup, repeatedly. I should have tried before asking...

[Edited by - szecs on March 16, 2010 5:18:51 AM]

Share this post


Link to post
Share on other sites
szecs    2990
The original question is still a mystery for me. I mean how can the window be updated, when I don't call update/invalidaterect in the WM_SIZE handler?

The Layered Window stuff will be needed, so thanks for that.

Share this post


Link to post
Share on other sites
jwezorek    2663
Quote:
Original post by szecs
The original question is still a mystery for me. I mean how can the window be updated, when I don't call update/invalidaterect in the WM_SIZE handler?


Off the top of my head, I don't know. Are you concerned about the time that the redraws take or is is that they are creating a flicker? Because if it's a flicker you want to get rid of, you could double-buffer in your WM_PAINT.

Share this post


Link to post
Share on other sites
szecs    2990
Quote:
Original post by jwezorek
Quote:
Original post by szecs
The original question is still a mystery for me. I mean how can the window be updated, when I don't call update/invalidaterect in the WM_SIZE handler?


Off the top of my head, I don't know. Are you concerned about the time that the redraws take or is is that they are creating a flicker? Because if it's a flicker you want to get rid of, you could double-buffer in your WM_PAINT.
After all, it's not a big issue.
Sometimes in takes about 100 ms to render the scene, and this produces a bit choppy feel, which is okay in the application (mostly things are redrawn on events), but a bit annoying with resizing/moving the window.
And I want to understand why causes this behavior, because I want to as much control of the redrawing as possible.
I use double buffering BTW.

Share this post


Link to post
Share on other sites
szecs    2990
Other question:
I want to maximize the window, but it will cover the taskbar, and I don't want that.
This is related to the window type: I have borderless/titlebar-less window:
CreateWindowEx(WS_EX_APPWINDOW|WS_EX_WINDOWEDGE,
WIND_CLASS_NAME,
WIND_CLASS_NAME,
WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
...

I tried the followings:
if( maximize )
{
SetWindowLong( win_state.hWnd,GWL_STYLE,
WS_POPUP|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_THICKFRAME);

ShowWindow(win_state.hWnd,SW_MAXIMIZE);

SetWindowPos(win_state.hWnd,HWND_TOP,0,0,0,0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);

}
else
{
SetWindowLong( win_state.hWnd,GWL_STYLE,
WS_POPUP|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);

ShowWindow(win_state.hWnd,SW_RESTORE);

SetWindowPos(win_state.hWnd,HWND_TOP,0,0,0,0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);

}

if( maximize )
{
SetWindowLong( win_state.hWnd,GWL_STYLE,
WS_POPUP|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_THICKFRAME);

SetWindowPos(win_state.hWnd,HWND_TOP,0,0,0,0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);

ShowWindow(win_state.hWnd,SW_MAXIMIZE);

}
else
{
SetWindowLong( win_state.hWnd,GWL_STYLE,
WS_POPUP|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);

SetWindowPos(win_state.hWnd,HWND_TOP,0,0,0,0,
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);

ShowWindow(win_state.hWnd,SW_RESTORE);
}
Neither did the trick, plus the window can't be restored to its original size (without SetWindowLong/SetWindowPos the restoration is fine)

Is there a way around this, or I have to set (and keep track of) the window coordinates myself? And calculate the width of the taskbar somehow?

Thanks in advance!

Share this post


Link to post
Share on other sites
taz0010    277
You can prevent WM_PAINT from triggering if you call ValidateRect after you resize the window or do anything which would otherwise result in a WM_PAINT message being sent.

You can find out the size of various (non window) elements on the screen by using GetSystemMetrics. Also SystemParametersInfo.

Share this post


Link to post
Share on other sites
Anon Mike    1098
Quote:
Original post by szecs
The question is the opposite as usual: why is the window redrawing constantly when resized/moved?
I know this is the behavior most of us want, but I don't (for performance reasons).

To override full window drag (or size) you should detect the WM_ENTERSIZEMOVE / WM_EXITSIZEMOVE messages and set a flag that tells your WM_PAINT handler whether it should draw or not.

Share this post


Link to post
Share on other sites
szecs    2990
Thanks, I'll look into it (since I use my own rezizing function (with MoveWindow) it will be tricky).
But I realized, that it's not a big issue after all. It's a game, so the user won't want to drag it or resize it too often, if so, the choppiness is acceptable.
And I tried resizing Visual Studio, well it's choppy as hell.

Share this post


Link to post
Share on other sites
szecs    2990
The maximize/minimize thing is still not solved.
I can use systemmetrics to maximize myself, but this way the small "transition animation" would be missed (when the titlebar transitions smoothly (interestingly, I have that titlebar in the "animation", even the window doesn't have one)).
And for some reason, SM_CYFULLSCREEN returns wrong value (smaller, than the possible y resolution).
It would be the best to get the ShowWindow(win_state.hWnd,SW_MAXIMIZE/SW_RESTORE) thing working.

Thanks.

Share this post


Link to post
Share on other sites
taz0010    277
Quote:
And for some reason, SM_CYFULLSCREEN returns wrong value (smaller, than the possible y resolution).


It returns the height of the client area of a fullscreen window. Which the the screen resolution minus the taskbar size minus the window title bar and borders, etc.
Sometimes you need to take into account the menu bar size too

Share this post


Link to post
Share on other sites
taz0010    277
Someone solved this problem on another message board:

You call ShowWindow with the SW_SHOWMAXIMIZED flag, which causes the popup window to take up the entire screen. Then you catch the following message and adjust the size of the parameters:


case WM_GETMINMAXINFO:

{

RECT WorkArea; SystemParametersInfo( SPI_GETWORKAREA, 0, &WorkArea, 0 );

( ( MINMAXINFO * )lParam )->ptMaxSize.x = ( WorkArea.right - WorkArea.left );
( ( MINMAXINFO * )lParam )->ptMaxSize.y = ( WorkArea.bottom - WorkArea.top );
( ( MINMAXINFO * )lParam )->ptMaxPosition.x = WorkArea.left;
( ( MINMAXINFO * )lParam )->ptMaxPosition.y = WorkArea.top;

return 0;

}


This code just prevents the task bar from being hidden, but Windows will treat the maximized size as whatever size you specify.

Share this post


Link to post
Share on other sites
szecs    2990
Thank you very much man!

I tried to catch WM_GETMINMAXINFO before, but with

minmax->ptMaxTrackSize.x = width;
minmax->ptMaxTrackSize.y = height;

which did nothing. So many parameters in so many combinations

Thanks again!

Only one thing left: layered window, I hope it can be used with openGL context.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this