0

I'm trying to make an application which will grey out all the windows except for ones that have focus. I've seen one out there but it expects that the one with focus is above all the rest, which is not the case (using focus follows mouse mode).

So, I'm thinking that this would be straight forward, and I can brush up on some of my old WinAPI skills. I create a standard Windows Desktop Application in VS2017, and modify the InitInstance() function

Creating the window:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
  HWND hWnd = CreateWindowEx(
    WS_EX_COMPOSITED | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST
    | WS_EX_TOOLWINDOW
    , szWindowClass, szTitle
    , NULL // no style
    , 0, 0, 640, 480 // initial window rect
    , NULL // parent window (desktop)
    , NULL // no menu
    , GetModuleHandle(NULL), 0);

Remove title bar, grips and such:

SetWindowLong(hWnd, GWL_STYLE, 0);

I want it to be opaque, but with alpha blended transparency:

SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);

So, now I get a white window popup. It has no titlebar or menu and sits above all my other windows and is ignored when I click on it, allowing access to the windows underneath. If I set the opacity to something lower than 255, I can see through the window.

However, I can't seem to keep it from being white. I just want to have it blank so that when I draw on it, it will show what I draw, with the appropriate alpha blended attributes.

Looking around, I found How to "Clear" a WinAPI Transparent Window, but that is for controls on a window, not the window itself. So I don't think it really applies. Also, I don't really want to do the work of iterating over all the windows and bliting the contents onto a memory DC and then bliting that onto my window DC. That seems silly. There must be some way of clearing the DC, but how?

Edit

Other things I've tried is:

  1. In the MyRegisterClass() function, I've set WNDCLASSEXW::hbrBackground to (HBRUSH)NULL_BRUSH.
  2. In the WM_PAINT message handler, I've set the PAINTSTRUCT::fErase to TRUE before calling EndPaint().

Neither of these helped.

Community
  • 1
  • 1
Adrian
  • 10,246
  • 4
  • 44
  • 110
  • Your window is white if you don't draw anything on it. Handle the `WM_PAINT` or `WM_ERASEBKGND` message, or use `UpdateLayeredWindow()` to apply a drawn bitmap to the window, or just supply a solid-color `HBRUSH` to `RegisterClass/Ex()` before calling `CreateWindowEx()`. – Remy Lebeau May 10 '19 at 21:35
  • @RemyLebeau, by default, the `WM_PAINT` is handled, and all it has is `PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); ... EndPaint(hWnd, &ps);`, where the `...` represents the code I put in. But if I paint stuff, everything that I don't paint is white. If I paint it black, it is black. I don't want anything there until I actually paint something. – Adrian May 10 '19 at 21:53
  • If you want parts of your window to have different levels of transparency you need to draw something in it and either use a color key or per-pixel alpha to tell the OS which pixels are transparent (and in the case of ppa how transparent they are). – Jonathan Potter May 10 '19 at 22:08
  • @JonathanPotter, so I _must_ use `LWA_COLORKEY` and then I could use `AlphaBlend()` to draw alpha blended colours/images on the window? That's annoying. – Adrian May 10 '19 at 22:10
  • @JonathanPotter, no, that doesn't work. – Adrian May 10 '19 at 22:59
  • @Adrian No, you don't need to use `AlphaBlend()`, if you simply use `SetLayeredWindowAttributes()` to specify which color you want to be transparent, and then draw the window normally. The OS will make the window transparent wherever the specified color is drawn, and handle alpha blending between windows for you. – Remy Lebeau May 10 '19 at 23:02
  • @RemyLebeau, no, that's not what I want. I want _"it to be opaque, but with alpha blended transparency"_. In other words, I want to make draw on the window and control how much is shown through what I draw. Some places I may want it opaque. Other places 50% transparent. Others, even more or less, depending on my mood and the colour of the sky. I want that control. – Adrian May 10 '19 at 23:05
  • If you want that level of control you need to use per-pixel alpha. – Jonathan Potter May 10 '19 at 23:16
  • 1
    @Adrian to make different areas of a window have different opacity values, you must use `UpdateLayeredWindow()` to draw your window using a 32-bit BMP, then you can specify alpha values on a per-pixel basis. See [Per-pixel Alpha Blending in Win32 Desktop Applications](http://duckmaestro.com/2010/06/06/per-pixel-alpha-blending-in-win32-desktop-applications/). – Remy Lebeau May 10 '19 at 23:17
  • @Adrian However, based on your original description, I'm not sure you actually need that much control. From your description, it may suffice to simply create a solid-color window, set a whole-window alpha using `SetLayeredWindowAttributes(LWA_ALPHA)`, and then use `SetWindowRgn()` to cut out areas that need to be transparent. In that way, you could create a solid-black window to cover the entire screen, make it 50% translucent or whatever, and then cut out an area for the window that has input focus. I've done that before to create shadow effects that cover non-active windows – Remy Lebeau May 10 '19 at 23:23
  • @RemyLebeau, ty for the info. Both are useful, but as you said in your last comment, might be overkill to have per-pixel control, unless I make some enhancements later, so still good to know. If you write that up in an answer, I'll give you the win. Your last method was what I was intending to do in the first place anyway. – Adrian May 10 '19 at 23:28
  • `SetWindowRgn()` used to be very slow, but I'm not sure if that's still the case with Win 10. `LWA_COLORKEY` might be more efficient to cut out an area for the active window. – zett42 May 11 '19 at 09:38

0 Answers0