how to redraw the content of a window

Community Forums/General Help/how to redraw the content of a window

RemiD(Posted 2015) [#1]
Hello,

I want to have a program in windowed mode with 3d render and 2d render (with Blitz3d), with the possibility to detect if the program is the active window or not, and if it is, redraw the content of the window so that all is displayed properly.

As i understand it, the foreground window is the window the most over the others, whereas the active window is the window with which the mouse/keyboard is interacting with. It is confusing to make this difference imo, in my gui system i put the active window the most over the others ...

Anyway, i can already detect if the window is the active window or not.
If it is the active window, i want to put this window over all the others window and i want to redraw the content of the window so that all is displayed properly.
If i set the window to be topmost (with SetWindowPos()), then the window is always over all the others windows regardless if it is the active window or not. I don't want that.
But if i set the window to be top (with SetWindowPos()), then the window is over all the others only if it is the active window but the content of the window is sometimes partially redrawn, see :

the window is the active window (after having been ran) :


the window is not the active window (after another window was selected) :


the window is the active window (after having been selected, but it is partially redrawn...)


the window is the active window (after something happens, it is totally redrawn)

The "something happens" apparently is when the mouse cursor goes over an interactable element (textfield, button) of another window.

Any idea how to force the redrawing of the window if i know it is the active window ?
(before i do something like this https://www.youtube.com/watch?v=2H7NZ0GNIIE :D)

Thanks,


Chalky(Posted 2015) [#2]
Something like this?

Loop
  Get window active status
  If yes and previously no Then
    Call SetWindowPos() with HWND_TOPMOST
    Redraw window
    Call SetWindowPos() with HWND_NOTOPMOST
  Endif
Endloop



RemiD(Posted 2015) [#3]
@Chalky>>If i set the window to TOPMOST, the window is always drawn over the others windows even if it is not the active window.
That's why i set the window to TOP, but in this case some part of the window is not redrawn if there are others windows under it.
The window is redrawn entirely only when the mouse cursor goes over an interactable element of another window.

My mainloop looks like this :
While()
 Check if the active window is this window
 if yes (active)
  GetInput
  UpdateThings
  Render3d
  Draw2d
  Flip
 else if no (if not active)
  Pause
  Draw2d
  Flip
 endif
 
Wend



Chalky(Posted 2015) [#4]
I notice that you're not including Render3d in your "no" logic. Does the following work?

While()
 Check if the active window is this window
 if yes (active)
  GetInput
  UpdateThings
 endif
 Render3d
 Draw2d
 if no
  Pause <-- I'm assuming this simply draws "paused" [or whatever] and returns to the main loop?
 endif
 Flip
 Delay <-- to allow O/S processing to occur
Wend



RemiD(Posted 2015) [#5]
@Chalky>>Yes because i copy the Render3d to an image and this allows me to render3d only when necessary (i don't render3d when the menu/GUI/pause screen (image) is activated)
But this is not the cause of the problem since when the window is active, render3d happens, and when the window is not active, the game is paused and a menu/gui/pause screen (image) is drawn over the last render3d screen (image).


Chalky(Posted 2015) [#6]
Hmm... I'm guessing that the Render3d is particularly CPU/GPU intensive which is why you don't want to call it if the window is not active?


RemiD(Posted 2015) [#7]
@Chalky>>Yes i don't see the point to render3d when the menu/gui/pause screen (image) is displayed...
But that's not the cause of the problem...

There must be a way to force the redraw of the active window entirely...

The TOPMOST helps to redraw the active window entirely, but then it is always over the others windows even if it is not the active one...

This is as if Windows (OS) was considering to redraw only the shared area of the previously interactedwith/active window (which is not active anymore) and of the blitz3d window (which is active).


Chalky(Posted 2015) [#8]
Are you saying that even if you use HWND_NOTOPMOST after drawing the window (as in my initial post above), the window still always remains on top?

Strictly speaking, Windows does not actually redraw window contents. Instead it issues a WM_PAINT message to applications whose windows have become corrupted, and each application then needs to execute its own redraw routine. In BlitzMax this can be handled via a system hook - not sure how blitz3d handles this?

Anyway - it's very difficult to know what's going on when we're talking in pseudo code. Can you post the blitz3d code invoked when the window is not active so we can have a look at it?


RemiD(Posted 2015) [#9]
Yes here is a simplified example :


Try to run the code, then to select this program window, then to move with the zqsd keys, then to open notepad, then to select notepad window, then to write something in notepad, then to select this program window, then to move with the zqsd keys, and you will see that only the area which is above the notepad window is updated, then move the cursor over the notepad window and the program window will be redrawn.
(it also happens with others programs like browsers, image editors, where there are interactable elements (textfields, buttons, ...))


RemiD(Posted 2015) [#10]
What is strange is that it seems that the program mainloop still runs because you can see an increase of the seconds counter.
However somehow a part of the screen is not redrawn.


Chalky(Posted 2015) [#11]
To save me time, could you please post the required decls file?


RemiD(Posted 2015) [#12]
Oh ! yes, sorry...




RemiD(Posted 2015) [#13]
I have just noticed the "bRepaint" parameter in api_MoveWindow()... Maybe it is the solution ?


Chalky(Posted 2015) [#14]
Thanks.

Well - you probably don't want to hear this, but - I've just run the code you posted and it works fine. The entire window is updated regardless of what's been covering it. To clarify: I ran your code, moved around with the zqsd keys, ran notepad, wrote something, selected your program window, moved with the zqsd keys, and the whole window updated correctly, selected the blitz3d ide, typed, went back to your program, moved around - and everything still updated properly...

Re: api_MoveWindow() - since you're not calling it (in the code posted above) it won't be the issue


RemiD(Posted 2015) [#15]
No it does not seem to help...


RemiD(Posted 2015) [#16]
To see the problem, there must be another window with interactable elements (like notepad textfield) with which you have interacted just before (clicked, typed text), behind the program window. Have you done that ? See what i mean on the 3rd screenshot.


Chalky(Posted 2015) [#17]
Yes - I do understand the issue. I ran your program and moved using zqsd. Then I ran notepad, moved its window so it half-covered your program window, typed some gobbledegook, selected your program (which pushed Notepad behind yours) and moved around again using zqsd. Everything worked fine.


RemiD(Posted 2015) [#18]
Ok...
This is strange. I will do some tests on others computers and see if this happens.

Thanks for the test. :)


Chalky(Posted 2015) [#19]
No problem! :0)


RemiD(Posted 2015) [#20]
good news, bad news :

I have tested this same test program on 5 computers, on 3 of them there is no problem, on 2 of them there is the problem mentioned above.

It can happen with notepad or with explorer or with an image editor, or with a webbrowser in the background...

That's not good, i have to find a solution.

There must be a solution because when the program window is active and when the mouse cursor goes over an element of another window (which is behind) which can be interacted with, or if the mouse cursor goes over the taskbar, the program window is updated and redrawn entirely.
Also i have seen a similar problem with my demo "SnowFall and SnowAccumulation", sometimes a part of the window or all of the window freezes and i had to drag the window a little to have the window updated and redrawn entirely.


RemiD(Posted 2015) [#21]
I think that i have found a fix ! Testing...


RemiD(Posted 2015) [#22]
Yes it works well on all my computers now.

So the fix is simply to add :
;before the mainloop :
Const SWP_FRAMECHANGED = $0020

;in the mainloop :
api_SetWindowPos(WH,HWND_TOP,DPWidth/2-WPWidth/2,DPHeight/2-WPHeight/2,WPWidth,WPHeight,SWP_FRAMECHANGED)

Done :)


Chalky(Posted 2015) [#23]
Excellent - well done for finding the fix, and thank you for posting it here (I didn't know about that flag setting).

Any clues as to why it didn't work on 2 of them (O/S, graphics card type etc.)?


RemiD(Posted 2015) [#24]

Any clues as to why it didn't work on 2 of them (O/S, graphics card type etc.)?


Not sure, maybe because of the os or because of the graphics card software...
The 2 computers with this problem have Windows 7 but the graphics card are from different manufacturers (at least the brand name, i don't know how they are made...).
The only way to know would be to test this program (without the fix and with the fix) on others computers...

Here are the tests that i have done on each computer :

run the test program in windowed mode
turn move the camera (the entire window is usually redrawn)
run an other program
set the other program in windowed mode
interact with the other program (click, hold, type)
select the test program (the other program window must be visible but behind the test program window)
turn move the camera (only the area shared by the 2 windows is redrawn)

press win+d
select the test program in the taskbar
turn move the camera (the entire window is usually redrawn)

press alt+tab to select the other program
interact with the other program (click, hold, type)
press alt+tab to select the test program (the other program window must be visible but behind the test program window)
turn move the camera (only the area shared by the 2 windows is redrawn)

press win
interact with the start menu
click on the test program window
turn move the camera (the entire window is usually redrawn)

So with the fix, it should work whatever the player/user does.
I find this fix useful because sometimes i like to listen to interviews/presentations/debates/audiobooks while i play a game, and some players/users probably do the same ;)


Chalky(Posted 2015) [#25]
Done some more checking by running your original code (without SWP_FRAMECHANGED) on my 3 systems: WinXP (SP3), Win7 (Pro) & Win8.1. Both WinXP and Win8.1 were fine (the image updated correctly no matter what was behind your window), but the Win7 box had the problem described in your OP. Since the Win8.1 rig has an AMD graphics card and the WinXP and Win7 both have nVidia - it looks like this is most probably a Win7 issue. I know you've already found a fix, but hopefully this info might be useful to somebody... :D


RemiD(Posted 2015) [#26]
Thanks for the tests and reports.

There may be the same problem with Windows Vista, see :
http://www.blitzbasic.com/Community/posts.php?topic=88786

I will be able to test this program (without/with the fix) on a computer with Windows Vista, but not today.


RemiD(Posted 2015) [#27]
I confirm that there is the same problem on a computer with Windows Vista... But with this fix, it works well (all the tests mentionned in post24)


Danny(Posted 2015) [#28]
Hi RemiD,

I haven't read this entire thread, but it sounds like a nightmare I had a long time back... I found two rough hacks, that seem to force a window into repainting it.

NOTE: This is NOT just a Vista problem! It can happen in any windows version - depending on ... many things. Hope these hacks help:
Call them when e.g. you regain focus to your app.

Make sure to place your RenderWorld within these - BEFORE the wait statement (the wait gives win32 time to actually perform the repaint events).

Function WB3D_WindowRepaint(win)
	; ensure window hasn't been closed in the meanwhile (X button)
	If Not WB3D_GadgetExists(win) Then Return 
	If Not WB3D_WindowMaximized(win)
		x = WB3D_GadgetX(win)
		y = WB3D_GadgetY(win)
		w = WB3D_GadgetWidth(win)
		h = WB3D_GadgetHeight(win)
		If y >= 0 Then WB3D_SetGadgetShape(win,x,y,w,h)
	EndIf
End Function


This one is 'more robust' but also produces a visible flicker (!) so I only use this one when e.g. after the window is resized, returning from hibernation, alt-tab, etc.

Const RDW_INVALIDATE = $0001
Const RDW_INTERNALPAINT = $0002
Const RDW_ERASE = $0004
Const RDW_VALIDATE = $0008
Const RDW_NOINTERNALPAINT = $0010
Const RDW_NOERASE = $0020
Const RDW_NOCHILDREN = $0040
Const RDW_ALLCHILDREN = $0080
Const RDW_UPDATENOW = $0100
Const RDW_ERASENOW = $0200
Const RDW_FRAME = $0400
Const RDW_NOFRAME = $0800

Function WB3D_WindowEraseAndRepaint(win)
	; msdn:	 http://msdn.microsoft.com/en-us/library/windows/desktop/dd162911%28v=vs.85%29.aspx
	; forum: http://www.blitzbasic.com/Community/posts.php?topic=32334
	; will cause a flicker! so don't use often! only on e.g. resized windows!
	;#>> Might Mav on Vista ?? check MSDN links above!
	
	; ensure window hasn't been closed in the meanwhile (X button)
	If Not WB3D_GadgetExists(win) Then Return 
	
	api_InvalidateRect(win, 0, 0)
	api_RedrawWindow(win, 0,0, RDW_INVALIDATE +RDW_ALLCHILDREN +RDW_UPDATENOW)
	api_UpdateWindow(win)
	;;--> RenderWorld here !!
	;;RenderFrame()
	Delay(1)
End Function


I really hope this helps,

Danny


Danny(Posted 2015) [#29]
TIP: ALWAYS call e.g. WB3D_WindowRepaint() AFTER the user closes a dialogue box or ANY other (temp) pop-up you might have had - since that could leave a "hole" in your viewport (ie. Windows not repainting the rect where the pop-up just was)..