wm_nccalcsize, win7 aero and opengl - creating custom window frame - winapi

Is it possible to use the trick with overriding wm_nccalcsize to draw over entire window area with opengl?
I need to keep all aero features for windows (win 7 in this case), so i use (WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME) styles
It works fine for maximized mode, I can leave the border outside the screen by adjusting the Lparam.
But a regular window still has 8px borders around it, even though they are supposedly part of client rect (which I checked with GetClientRect()
Image: dark-grey border is visible
And this is all done before I init opengl context. So I dont know what's happening. Or if this even possible. Am I supposed to just create borderless and re-implement all aero features? (no way I'm doing that)
upd 2:
If I draw a rect with GDI right before I init Opengl context this is what i get:
a nice (0,0,200,200) rect, starting in the nonclient area
So it is opengl context issue. I saw in msdn docs, that opengl draws only in client area. And it still does that, ignoring that I extended the client rect to whole window. siiiigh.

The usual way to combine Aero with OpenGL is to set the window client area using the DWM API to zero. This allows you to draw to the whole window (including titlebar) using OpenGL. I have this test program to tinker with it as part of my wglarb wrapper:
https://github.com/datenwolf/wglarb/blob/master/test/layered.c
You may also be interested in my dwm_load wrapper, which allows to you call DWM functions without rigidly linking your program against DWM (which makes it incompatible with older Windows versions; you wouldn't believe how often I still get "must run on WinXP" as a feature requirement) https://github.com/datenwolf/dwm_load

Related

Drawing docking window frames on Vista and up

I need to port some old code which implements docking windows in plain C to Vista and up. When dragging docking windows around, the old code draws the docking window's outline directly to the desktop DC by doing something like this:
hdc = GetDCEx(NULL, NULL, DCX_WINDOW|DCX_CACHE|DCX_LOCKWINDOWUPDATE);
...
PatBlt(hdc, left, top, width, height, PATINVERT);
...
ReleaseDC(NULL, hdc);
This still works on Vista and up but it is very, very slow - probably, because the DWM has to copy the desktop's frame buffer into a temporary mutable pixel buffer, do the drawing, and copy it back to the now immutable frame buffer. This is very slow.
Thus, I was wondering how such code should be ported to Vista and up. AFAIK, drawing directly to the desktop DC is no longer recommended with the introduction of the DWM in Vista so I was wondering how I should draw my docking windows' outline when the user is dragging them? What's the best way to do this?
I was thinking of abusing cursor images (or drag images or whatever Win32 has on offer here) to do what I want but I'm not sure whether this is really the best way to go. Any ideas?
Thanks!

Overlay window drawing which appears in screen captures of other applications

We need to overlay a target window with a custom shape and tracks the position of the target window such that the overlay drawing always appears above it. Also, the overlay drawing should appear in screenshots taken using BitBlt or PrintClient by screen-capturing tools like Camtasia, Debut, etc. Also, moving the target window around should not leave traces of the drawing at earlier location. The target window is not made using our code.
So far we've tried several ways but each method has its problems:
1) Layered Window:
Using a layered window as the child/owned window of the target window is the easiest thing and it works. But the problem is on Windows 7 and XP, layered windows do not appear in a BitBlt done without the CAPTUREBLT flag and the screen-capturing tools may call BitBlt without the flag, thereby skipping our window from the capture.
2) Region Window:
The crude approach to support all Windows versions then is to use a region window using SetWindowRgn and make the target window its owner. However, region windows are generally very slow in rendering complex shapes and also impact the performance of other windows, sometimes to the point of freezing the application. Region window also leaves traces on dragging the application window.
3) Subclassing and Drawing on HDC:
Another approach is to sub-class the target window and draw the shape on its HDC on the OnPaint() event inside the window procedure hook. The shape can be drawn on the desktop window instead too. The problem is that applications may draw without a paint event, like when some text is selected using the cursor, and such drawing may erase a part of the custom drawing. Tracking all possible paint events is not a good way to do this.
4) Drawing continuously in a timer:
The last resort is to draw the custom shape on the target window in a timer continuously so the drawing is always above the target, even on text selection. But this may show a bit of flicker when the text is selected. Not to mention, it is very performance heavy to draw constantly.
Question
What is the best way to achieve an overlay drawing which works on all Windows versions (since XP) at the same time appearing in screen-captures. We've exhausted all approaches. Is there some method I'm missing? Can the above mentioned ways be used with some improvement? Is DirectX an option? I'm looking for methods with fast rendering capacity to achieve this. Appreciate any help!
I think the best solution to draw an overlay window, is to use a double-buffered technique GDI, just like in this example:
Overlay Drawing in CScrollView
I have myself the same task, and the only solution was that one. Hope it help you.

Skinned Window: Win32 API and DirectX

I'm trying to create a borderless window using a WS_EX_LAYERED style window. The objective is to render graphics using DirectX directly to the desktop, using alpha to blend onto the current desktop windows.
Now on my system this technique seems to work perfectly. I can set various alpha levels and achieve different levels of transparency. Unfortunately several users have reported severe performance problems and low frame rate, making this technique unusable.
The code setup is as follows:
Create a layered (WS_EX_LAYERED extended-style) window.
Initialize DirectX using the window HWND.
Create a render target using the CreateRenderTarget DirectX method.
Then during the render loop:
Render graphics to the render target using DirectX calls.
Get the HDC handle to the DirectX render target surface using GetDC method.
Update the window contents using the UpdateLayeredWindow function, specifying the DirectX surface HDC.
My question is: Am I doing something wrong? Is there a way to improve the performance of the window update. I have tried various things, like locking the render target and manually copying the bits to a DIB section to display in the window area, without success.
How big is your window? Note MSDN's documentation at http://msdn.microsoft.com/en-us/library/windows/desktop/ms633556%28v=vs.85%29.aspx says "For best drawing performance by the layered window and any underlying windows, the layered window should be as small as possible."
You may be getting a performance boost if compositing (Aero) is enabled. If Windows is already compositing, it won't have to do as much extra work to draw layered windows.
If you're not seeing any difference in performance depending on compositing, then I am probably completely off base here.

Windows Vista/7 glass completely broken?

I'm trying to get the media player glass effect for an application, but I'm facing roadblocks all around. Is it me, or is it the API?
I started by calling:
MARGINS margins = { -1, -1, -1, -1 };
HRESULT result = ::DwmExtendFrameIntoClientArea(m_hWnd, &margins);
And erasing the background to black on:
OnEraseBkgnd
Everything was fine until I added an edit box. With EditBox, places where black text appears are replaced with glass. The problem is blown out of proportion with more advanced controls like ComboBoxEx and that like. Those start to look like mess.
The bright side with this approach is that the alpha blended images look alright. And are gradually blended into the glass.
The solution for edit boxes seems to be using WS_EX_LAYERED with LWA_COLORKEY and using some awful color for glass masking, and erasing the background to that color. Unfortunately this breaks other things. The alpha blending on icons looks super ugly with shadows blended to radioactive green, and the glass itself becomes click-throughable, even though I don't have a WS_EX_TRANSPARENT style on the window.
The third suggestion is sort of custom composition with GDI+, but that seems to be a bad approach, since GDI+ is a software fallback, and I can't find a way how to render EditBox or ComboBoxEx to an image that I could use for composition later.
Am I missing something?
The sources at SO also seem to face the same problems:
link1
link2
Kenny Kerr wrote an excellent article on displaying controls on glass, using edit controls as an example:
http://weblogs.asp.net/kennykerr/archive/2007/01/23/controls-and-the-desktop-window-manager.aspx
The simple answer is, you cannot do this.
All the standard controls render themselves with normal GDI calls. Normal GDI calls are alpha unaware and mostly set the alpha channel to zero.
The more complicated answer is, you can: There are some samples on the net where standard controls are mixed with aero glass. They fall into two basic categories:
WinForms (.NET) samples. WinForms don't actually use the native controls - the WinForms controls are all rewritten versions that, amongst other features, blit using alpha aware routines. This example is depressingly simple and makes me realize how much Microsoft hate native developers.
Subclassed samples: The only way to get the regular/native controls to paint on aero glass is to sublclass the controls, catch WM_PAINT messages to create a DIBBitmap, call WM_PRINT to render the controls content onto it. Use the DWM functions to repair the alpha channel data. Paint the resulting alpha'd bitap yourself using AlphaBlend.
This article on CodeProject contains a reference implementation that subclasses most of the standard controls rather well.
Try changing your text/foreground color on controls like the edit box to something "almost black" (like #010101). Then you should be able to skip the other rendering issues except alpha transparency for images/icons.
Edit: Drawing other stuff - might be some weird approaches to handle compatibility, but then again classic apps aren't glass themed by default ... don't know. Just try to avoid drawing stuff yourself, if possible.

Win32: How to make drop shadow honor non-rectangular Layered window?

i've created a layered window by adding the the WS_EX_LAYERED extended style:
wndClass.ExStyle = wndClass.ExStyle | WS_EX_LAYERED;
Windows will use black as the chroma key color value. i'm going to leave a large border of black to make the problem obvious:
After the window is constructed, i tell it to use black as a chroma-key color:
SetLayeredWindowAttributes(hwnd, 0x00000000, 255, LWA_COLORKEY);
Now the popup layered window appears partially transparent:
The problem is the final step. i want to use CS_DROPSHADOW class style, available since Windows XP, to create a drop-shadow:
wndClass.Style = wndClass.Style | CS_DROPSHADOW;
The drop shadow appears, but the shadow surrounds the original rectangular window, and doesn't take into account the window's transparency provided by the layered window:
Does anyone know what magical option i've missed somewhere that will make the drop shadow honor the non-rectangular layered window?
Another example of where this issue appears is when you don't include the 6px padding/margin. The hint window as drawn by Windows® themes is non-rectangular. This leaves a small visible gap where the window is transparent, but the drop shadow does not appear:
Microsoft has managed to make it work, as you can see from this hint from Internet Explorer:
Looking closer at a Windows tooltips class hint window. Using SpyXX - i can get its window rect, and class styles:
SpyXX says:
Rectangle: (440, 229)-(544, 249), 104x20
Restored Rect: (440, 229)-(544, 249), 104x20
Client Rect: (0, 0)-(104, 20), 104x20
So everything points to the window itself being 104x20 pixels, with the drop shadow outside the window itself. (Which is consistent with CS_DROPSHADOW.)
Next i can look at the styles of the tooltips window class:
Windows Styles: 94000001
WS_POPUP 80000000
WS_VISIBLE 10000000
WS_CLIPSIBLINGS 4000000
TTS_ALWAYSTIP 1
Extended Styles: 00080088
WS_EX_LAYERED 80000
WS_EX_TOOLWIN 80
WS_EX_TOPMOST 8
Interestingly, it doesn't use CS_SAVEBITS (0x800); which is useful for small, short-lived, windows.
Nor does it use CS_DROPSHADOW (0x20000). So now i wonder how is it drawing outside its own window?
Note: Transparent layered windows is documented as the preferred technique over regions.
Edit: Layered Windows have been around with Windows 2000. CS_DropShadow was added with XP.
Transparent layered windows is documented as the preferred technique over regions.
However, CS_DROPSHADOW does pay attention to regions. If you crop or otherwise shape your window using a region, the drop-shadow will follow the new outline.
Fortunately, you can use regions with layered windows, and by combining the two get the effect you're looking for.
BTW: tooltips_class32 does use CS_DROPSHADOW - you won't see it in the window styles because it's a class style, not a window style.
Why don't you use LWA_ALPHA and build the shadow into the image?
Edit in reponse to your comment:
A) Doesn't stop you using an alpha channeled PNG for a shadow only. Blt the 2 images together and use as one single image.
B) Its not hard to generate a drop shadow. In the image you posted its black with 3 different alpha values.
C) But it doesn't work does it? ie Time to get creative.
D) Then don't try and get windows to do something it won't do for you.
E) Is entirely irrelevant. Layered windows handle that for you.
I strongly recommend you learn more about layered windows because they CAN help you to your goal.
Edit2: You have the bitmap. Its fairly easy to scan over the image and find which bits will be colour keyed (by identifying the black yourself) and hen modify that to have an alpha of 0 where everything else will have an alpha of 255 (Not: You may have to convert the image to a 32-bit image from a lower colour format, you can do this by creating a new DC and copying the image). This will give you the same effect with LWA_ALPHA as with LWA_COLORKEY. From there its fairly easy to identify the pixel at the edge, where the color changes to (R = 0, G = 0, B = 0, A = 0). You then change that first pixel to have a n alpha of 192, the one blow it to 128 and the one below to 64. You now have an alpha'd gradation below the image that will look like the shadow. You can adjust the alpha to get the effect you want.
CS_DROPSHADOW only works with standard rectangular windows. WS_EX_LAYERED windows don't support everything. They are more of a low-level, self-service method to draw exactly what you want.
To get a drop shadow, you'll have to generate the drop-shadow yourself from the alpha channel in the image.

Resources