I have an application with three MDI windows, all of them showing OpenGL content. On XP, everything works fine. But on Vista/Win7 the mdi child windows don't refresh properly.
After startup, all windows show their content properly. But when I change the focus from one mdi window to the next, those two windows are cleared (i.e., they only show white, no content). I have no idea why the windows get cleared, they don't receive any WM_* message when that happens, and of course don't receive a WM_PAINT message either.
When resizing those windows, I correctly get the WM_PAINT message (after WM_SIZE) and redraw the content, but then the window gets cleared too, which results in a strange flicker while resizing. After resizing stopped, the window stays cleared (white) until I manually force a refresh.
This happens independently of Aero enabled or disabled.
Any idea why this happens?
I'm surprised it works on XP. In my (limited) experience dabbling with OpenGL, WM_PAINT is not always the best place to redraw OpenGL scenes. Most likely the content is getting wiped out at the driver level. You can check for this by seeing what happens when one of your MDI windows happens to span two monitors connected to two different video cards.
Try the following:
Reinitialize your OpenGL contexts after WM_SIZE occurs.
Draw on-demand instead of in WM_PAINT. In your handler for WM_PAINT, do nothing. Use a timer or some other mechanism to periodically trigger updates of your displays.
Flicker is usually caused by interference via WM_ERASEBKGND. If you haven't already, intercept WM_ERASEBKGND and do nothing in the regions where you are displaying OpenGL content.
Use the CS_OWNDC window style on any windows hosting OpenGL content so that the HDC doesn't change per-message/per-call during the lifetimes of your MDI windows.
Other rarer causes of interference that might apply (since you are using MDI windows)
WM_NCPAINT and other related Non-Client drawing messages- you can workaround these by moving your OpenGL content to a child window with no border, inside the MDI windows.
Incorrect/Incompatible default features for OpenGL on your video card that explicitly require overlays or implicitly use them (frequent cause of issues in overlapping contexts). Unfortunately, diagnosing this out of my realm of knowledge, but some testing may shed some additional light here.
Related
I am creating a simple clock application. Whenever I make it disappear and restore it after a while I get an uncomfortable lag effect when my window is redrawing itself after some short period of time. I want to see changes immediately. It seems that window is not getting WM_PAINT messages while being not present on the screen (and this is actually the place I draw stuff). How to force Windows to redraw window just before showing up on the screen rather than afterwards?
My application is a fullscreen window rendering a specified window or the desktop.
I would like to know if it's possible to fetch the window bitmap (like i'm already doing) but without the render of my window's application ?
There is the idea : dwm.giveBitmapWithoutRendering(HWND myApplicationHandler)
Working on Windows 7/8/8.1, QTCreator C++ MINGW
You can use the PrintWindow function with your own memory DC. The success of this method will depend on how the window and its child windows have implemented the WM_PRINT message.
This doesn't use DWM but rather gets the window to repaint itself. Since it's not repainting to the screen I hope it meets your requirements.
I am wondering in which cases does a widget receive its paint event, and how does it vary with the OS.
Qt documentation for paintEvent says only
A paint event is a request to repaint all or part of a widget. It can happen for one of the following reasons:
repaint() or update() was invoked,
the widget was obscured and has now been uncovered, or
many other reasons.
So far, I've put some traces in the paintEvent,
void Widget::paintEvent(QPaintEvent *e)
{
static int count = 0;
qDebug("paintEvent, %d", count++);
}
and this is what I've found out (on Windows 7 at least):
The paintEvent is called when the widget loses/gains focus. The paint event is not called when another widget passes over our widget. I don't know if that's because of Windows 7 compositing. The paintEvent is also called when a minimized window is restored. The paintEvent is called when resizing.
So is the behaviour dependent on the OS?
Yes, in the sense that you describe, it's dependent upon the operating system.
The Desktop Window Manager (DWM), found in Windows Vista and 7, the doohickey that is responsible for desktop composition, the Aero glass effect, and all kinds of other eye candy, works a bit differently than the model used in previous versions of Windows. As you suspect, it caches the bitmaps for your windows, even when they are not visible because they're obscured by another window. That means it doesn't need you to repaint them (and thus it doesn't raise a paint event) because it can just blit them from the cached bitmap. Not only is this a potential optimization over having each application redraw itself, it also allows the DWM to implement things like Aero Flip, for which it uses its cached bitmap.
The exception to this is as it has always been for, say, the CS_SAVEBITS class style. If the bitmap that the DWM has cached has become invalidated (e.g., because your window image has changed), it will discard it and ask you to redraw the window.
Test this theory by turning off DWM composition (switching to the "Windows Classic" theme), and then obscuring your window to see if you receive a paint event. You should, just like you did in all previous versions of Windows.
But the larger point is that you should not rely on receiving paint events in any particular order. The only thing you should assume about paint events is that you'll receive one when the operating system needs you to repaint your window. Otherwise, it won't bother you. I'm sure that's why the documentation is content with being vague on this point, beyond possible technical constraints.
This is why logic should not go inside of the paint event handler. The only thing that method should be responsible for is repainting the window by its current state. That state needs to saved elsewhere. This rule is also commutative: you should not do any painting outside of the paint event handler.
Of course, you can always force a paint event to be raised by invalidating your window (I'm sure Qt has an invalidate or refresh method for this, check the documentation), but that still doesn't mean it's a good pattern to place application logic in the method that handles this event.
I have a program which is not written by me. I dont have its source and the developer of that program is developing independently. He gives me the HWND and HINSTANCE handles of that program.
I have created a child window ON his window, using win32 api.
First thing I need is to make this child window have transparency on some area and opaque on other area(like a Heads up display(HUD) for a game), so that the user may see things in both windows.
The second thing that I need is to direct all the input to the parent window. My child window needs no input.
I know that WS_EX_TRANSPARENT only makes the child draw at the end like in painters algorithm.
I cant use WS_EX_LAYERED because its a child window.
p.s.
I have looked everywhere but didn't find any solution though there were similar questions around the internet.
Actually this is a HUD like thing for that game. I can't draw directly on parent window because of the complexity with multi-threads any many other reasons.
-- EDIT ---------------------------
I am still working on it. I am trying different ways with what you all suggested. Is there a way to combine directX and SetWindowRgn() function or directx with BitBlt() function? I think that will do the trick. Currently I am testing all the stuff as a child window and a Layered window.
You can use WS_EX_LAYERED for child windows from Windows 8 and up.
To support earlier versions of windows, just create a level layered window as a popup (With no chrome) and ensure its positioned over the game window at the appropriate location. Most users don't move the windows they are working with all the time, so, while you will need to monitor for the parent window moving, and re position the HUD, this should not be a show stopper.
Not taking focus (in the case of being a child window) or activation (in the case of being a popup) is more interesting, but still quite do-able:- The operating system does not actually automatically assign either focus, or activation, to a clicked window - the Windows WindowProc always takes focus, or activation, by calling SetFocus, or some variant of SetActiveWindow or SetForegroundWindow on itself. The important thing here is, if you consume all mouse and non client mouse messages without passing them on to DefWindowProc, your HUD will never steal activation or keyboard focus from the Game window as a result of a click.
As a popup window, or a window on another thread, you might have to manually handle any mouse messages that your window proc does get, and post them to the game window. Otherwise, responding to WM_NCHITTEST with HTTRANSPARENT (a similar effect to that which WS_EX_TRANSPARENT achieves) can get the system to keep on passing the mouse message down the stack until it finds a target.
OK friends, finally I did some crazy things to make it happen. but its not very efficient, like using DirectX directly for drawing.
What I dis:
Used (WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_ TOOLWINDOW) and () on CreateWindowEx
After creating the window, removed (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE) from window styles, and also removed (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_APPWINDOW) from extended window styles.
This gives me a window with no borders and its also now shown in the taskbar. also the hittest is passed to whatever that is behind my window.
Subclassed the window procedure of the other window and got the
WM_CLOSE,WM_DESTROY, to send the WM_CLOSE or WM_DESTROY respectively to my window
WM_SIZE,WM_MOVE, to resize and move my window according to the other window
WM_LBUTTONUP,WM_RBUTTONUP,WM_MBUTTONUP, to make my window brought to the top, and still keep focus on the other window, so that my window doesn't get hidden behind the other window
Made the DirectX device have two passes:
In the first pass it draws all the elements in black on top of a white background and copy the backbuffer data to an another surface (so it give a binary image of black & white).
In the second pass it draws the things normally.
Another thread is created to keep making the window transparency by reading that black & white surface, using the SetWindowRgn() function.
This is working perfectly, the only thing is it's not very good at making things transparent.
And the other issue is giving alpha blending to the drawn objects.
But you can easily set the total alpha (transparency) using the SetLayeredWindowAttributes() function.
Thanks for all the help you guys gave, all the things you guys told me was used and they guided me, as you can see. :)
The sad thing is we decided not to use this method because of efficiency problems :(
But I learned a lot of things, and it was an awesome experience. And that's all that matters to me :)
Thank You :)
You can make a hole in the parent window using SetWindowRgn.
Also, just because it is not your window doesn't mean you can't make it a layered window.
http://msdn.microsoft.com/en-us/library/ms997507.aspx
Finally, you can take control of another window by using subclassing - essentially you substitute your Wndproc in place of theirs, to handle the messages you wish to handle, then pass the remainder to their original wndproc.
I have a VS2008 C++ application for Windows XP SP3 developed using WTL 8.1. My application contains a tab control that flickers when the application border is resized.
My window hierarchy looks like this:
CFrameWindowImpl CMainFrm
|-CSplitterWindow Splitter
|-CTabView Configuration Tabs
| |-CDialogImpl Configuration View 1
| |-CDialogImpl Configuration View 2
| |-CDialogImpl Configuration View 3
|-CDialogImpl Control View
The solution I'm trying is to make the CFrameWindowImpl derived class use the WS_EX_COMPOSITED style and all windows beneath it use the WS_EX_TRANSPARENT style. Unfortunately, this makes the tab control buttons show as an empty black bar and the controls of any Configuration View to not show at all.
If I remove the WS_EX_COMPOSITED and WS_EX_TRANSPARENT styles, the form displays properly, but the CTabView and everything beneath it flickers horribly when resized.
What do I need to change to eliminate the flicker and draw the controls properly?
Thanks,
PaulH
Edit:
Got it working. I removed all the WS_EX_TRANSPARENT styles per Mark Ransom's suggestion. I put the WS_EX_COMPOSITED style on only the CTabCtrl (contained within the CTabView). Other controls get double-buffering as needed through WTL::CDoubleBufferImpl<>.
A window flickers because it gets erased before it's drawn. To eliminate this you need to disable erasing of the window entirely and use double buffering - draw the window contents into a bitmap, then copy the bitmap to the window. Because the bitmap contains the entire contents including the background, there's no need to erase anymore.
It looks like WS_EX_COMPOSITED will handle the double buffering automatically, but you still probably need to use a NULL background brush and/or handle the WM_ERASEBKGND message.
Whats not mentioned in MSDN is that the Desktop Window Manager - the component that hooks window painting on Windows Vista and 7 to perform the desktop composition necessary to get the aero glass effect - does NOT implement WS_EX_COMPOSITED.
Which means all the work you put into getting this style to work on XP, is doomed to become irrelevent on Vista or later.
The other problem with WS_EX_COMPOSITED - and why it was an optional style and not a default on XP: The double buffering only picks up painting performed during the BeginPaint / EndPaint block of the parent window. Lots of, even standard controls, perform painting outside of their WM_PAINT handlers, and as a result the backbuffer gets only partially painted.
Sadly, the result is, the only way to "eliminate" flicker in native API apps is to try to minimize it: WS_CLIPCHILDREN and WS_CLIPSIBLINGS can help if you dont have overlapping controls - to ensure that each control's area is painted only once. And ensure that the main dialog does not perform any flood filling in WM_ERASEBKGND
It is not, in my experience, possible to use double-buffering for anything that contains child controls (unless they all fully support WM_PRINT, which most do not).