How to clear an HWND so that it will show everything underneath? - winapi

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:
In the MyRegisterClass() function, I've set WNDCLASSEXW::hbrBackground to (HBRUSH)NULL_BRUSH.
In the WM_PAINT message handler, I've set the PAINTSTRUCT::fErase to TRUE before calling EndPaint().
Neither of these helped.

Related

Setting a window to full screen programmatically

I am trying to resize a console window to full screen in a non-.NET C++ application that runs under Windows 10.
I am able to get rid of the window frame and resize, using one of SetWindowPos or MoveWindow from the WinAPI.
But the window origin (top-left corner) does not move to the top-left corner of the screen and stays in its initial position, which is random. In fact, the X, Y arguments of these functions seem to be just ignored.
Any suggestion ?
That did it:
HWND hWnd = GetConsoleWindow();
DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
SetWindowLong(hWnd, GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW);
SendMessage(hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
Adjust the buffer and window sizes of the console to avoid scrollbars.
Credit to
500 - Internal Server Error
.

Change style of maximized window

I want a window which behaves identically to normal Overlapped window, except it does not have capton bar when maximized (to make more room for the client area).
I can remove WS_CAPTION|WS_SYSMENU from the window style.
However, I cannot find way to get window position and size right:
A normal window is maximized by expanding work area rectangle with border width. This makes border to "hang" outside. When I remove WS_CAPTION, the border is different (3 vs 4 pixels in my case) so I have to reposition the window somehow.
What I have tried:
First change style, then maximize: this does not maximize to work area, but to full screen instead. Looks like this is a feature of window manager that relies on WS_CAPTION style. Other than that, the border is "hanging" correctly.
First maximize, then change style, position and size:
I can't find API to get the maximized size and position. However, work area of closest monitor looks good to me.
I can't find API to expand window rect with appropriate "hanging border". AdjustWindowRectEx is almost there, however non-client area is not the same as border (obviously it also includes caption and menu). I also tried to do the math myself using GetSystemMetrics values, but it seems too unpredictable. The border could be SM_CXSIZEFRAME, or SM_CXSIZEFRAME+SM_CXPADDEDBORDER, or SM_CXFIXEDFRAME, and maybe this depends on OS version, theme and whatever.
Is it possible to do this in a robust, "official" way?
I put my own answer below but it is too hacked.
You are already removing the caption. If you don't want to show the borders, then remove the borders as well. Get ready to restore the border later. Find the desktop rectangle, and position the window in to that rectangle. The full screen window can have WS_OVERLAPPED or WS_POPUP flag with no caption and no borders. Example:
void switch_view()
{
RECT rc;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0);
DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE);
if(style & WS_CAPTION)
{
SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPED);
SetWindowLongPtr(hwnd, GWL_EXSTYLE, 0);
SetWindowPos(hwnd, NULL, 0, 0, rc.right, rc.bottom,
SWP_SHOWWINDOW | SWP_FRAMECHANGED);
}
else
{
SetWindowLongPtr(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
}
}
This is what I get after many attempts.
The border problem is solved. As suggested by others, I remove border together with caption. Therefore no need to calculate it. There is also no need to calculate border for captioned window with border, because this is special-cased by window manager.
To obtain and maintain the right size, I use MonitorFromWindow and GetMonitorInfo. The position and size are applied together with style change (both ways) and also in WM_SIZE handler. Watching WM_SIZE allows to recover from external minimize-restore events, as well as TaskBar change etc.
For unknown reason, moving with Win+Arrow keys does not work. This issue only exists when the window is in maximized state and without caption. Using this workaround:
in WM_WINDOWPOSCHANGING set WS_CAPTION style. This allows window to move correctly. Afterwards, the WM_SIZE handler applies the right style/position/size again.
Approach that failed: use restored window rather than maximized. In this case there is too much going wrong in restore-move-maximize-restore lifecycle (either restore or maximize goes to wrong monitor).

Fullscreen vs Borderless window

My goal is to create an OpenGL application. I've seen many games that let the user decide if the "game window" will be "fullscreen" or "borderless". What's the difference? How do I implement each method?
I heard that fullscreen windows are just windows with WS_POPUP style that are set to be the width and height of the screen. Is this true for only one of the approaches I mentioned above?
What's the difference between "Borderless" and "Fullscreen" here? (screenshot taken from LoL)
You just need WS_POPUP and full screen width & height to hide the task bar. Here is example of changing window style after window is already shown:
if (msg == WM_LBUTTONDOWN)
{
if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_POPUP)
{
SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, NULL, 0, 0, 600, 400, SWP_FRAMECHANGED);
}
else
{//show full-screen
int w = GetSystemMetrics(SM_CXSCREEN);
int h = GetSystemMetrics(SM_CYSCREEN);
SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
SetWindowPos(hwnd, HWND_TOP, 0, 0, w, h, SWP_FRAMECHANGED);
}
}
Or to show with initial full-screen size:
int w = GetSystemMetrics(SM_CXSCREEN);
int h = GetSystemMetrics(SM_CYSCREEN);
HWND hmain = CreateWindow(L"className", L"title", WS_POPUP, 0, 0, w, h, 0, 0, hInst, 0);
ShowWindow(hmain, SW_SHOW);
Windows are either WS_OVERLAPPED, WS_POPUP, or WS_CHILD. These three flags can't be combined with each other, but they can be combined with other WS_XXXX flags.
Top windows are either WS_OVERLAPPED or WS_POPUP Different styles for main window include:
Normal window: WS_OVERLAPPED, shown with ShowWindow(hwnd, SW_SHOW)
Maximized window: WS_OVERLAPPED, shown with ShowWindow(hwnd, SW_MAXMIZE) covers the whole screen, not including the taskbar
Fullscreen: WS_POPUP flag, with width & height set to SM_CXSCREEN/SM_CYSCREEN, covers the whole screen, it goes over the task bar
All these windows can have WS_BORDER or not. Or they may have WS_THICKFRAME for resizing border. In fullscreen mode, the window usually has no border. In maximized mode, the borders fall outside the view area.
More details: Window Styles
'Fullscreen' mode is a special mode where the game takes over the entire display and doesn't attempt to cooperate with other windows in any way. It's how all games used to work, is more flexible (your game resolution doesn't have to match your desktop resolution) and in theory faster. In practice it can cause a lot of problems when you alt-tab and suchlike. You can in some situations end up with the game window still showing when you should be using another application.
'Borderless' makes a normal window but with no title bar or border that takes up the whole screen. Visually this should be the same as Fullscreen, but avoids the interoperating problems. You can have other things on top of it (eg. taskmanager), you can alt-tab quite happily. The downside is that it may be slower because Windows gets involved in the drawing process, and you're stuck with the user's desktop resolution.
'Windowed' is a normal Windows window which always has a title bar, and may have resizing handles, minimise button and so on. This might also take up the whole screen when maximised, but the actual usable area is smaller because you lose some space to the title bar/close buttons/border.
Screen modes are generally Windows level things, rather than openGL.
To get Windowed mode you use CreateWindowEx() and pass the WS_OVERLAPPED style.
To get Borderless you use the same function with WS_POPUP style. In theory, anyway. I've seen video drivers give you Fullscreen anyway.
To get Fullscreen you generally apply ChangeDisplaySettings(blah,CDS_FULLSCREEN) to switch to your desired video mode.

Windows 7 edit control, vertical scroll does not work

I have an application that compiles ok (mingw) and runs ok, however the edit control that is created has scroll bars but the vertical scroll bar doesn't do anything. The same executable works fine on WinXP. Also, the edit control has both vertical and horizontal scroll bars, the horizontal works just fine, but vertical does not. The vertical scroll bar is visible and clickable, but nothing happens...
Following code is used when creating the edit control:
HWND hwndEdit = CreateWindow(TEXT("EDIT"), TEXT("Edit control"),
WS_OVERLAPPED | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_MULTILINE |
ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_READONLY | WS_SIZEBOX,
1000, 480, 400, 500, NULL, 0, GetModuleHandle(NULL), NULL);
SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM) TEXT(""));
SendMessage(hwndEdit, EM_LIMITTEXT, 0, 0);
Following code is used repeatedly to add text to edit control:
char test[] = "test";
SendMessage(hWndDbg, EM_SETSEL, 0x7FFFFFFF, 0x7FFFFFFF); // Undo any selection and move to end.
SendMessage(hWndDbg, EM_REPLACESEL, 0, (LPARAM) temp);
Finally found the fix. It seems in WinXP the edit control does not have to have a parent window defined, in Win7 it is required for the vertical scroll bar to work, horizontal scroll bar works in Win7 even without parent window defined.
Note that vertical scroll bars even make sense on single-line edit controls. In this case, WM_VSCROLL must be set, and ES_AUTOVSCROLL not. (EM_AUTOVSCROLL means EM_AutoHIDEVScroll.) The alternative UpDown control makes more programming efford than capturing VM_VSROLL messages, and then changing the (typically numerical) value inside the edit control.
Unluckily, the MS Visual Studio built-in Resource Editor won't let you add WM_VSROLL to single-line edits! You must do this by adding this bit by editing the .RC file in text mode.
Similar to the fact that this Resource Editor won't let you add a default text to the control, which is still supported by the resource loader.
Use GetDesktopWindow() as hWndParent parameter. It works on Win7.

Not drawing a thickframe on a CWnd

I've got an application window in which I'm adding the WS_THICKFRAME style and I have removed the WS_CAPTION style. When the window maximizes, I want to hide the WS_THICKFRAME, but retain the Aero-Snap feature, so I have altered my handler for WS_NCCALCSIZE to return an inflated rect with respect to the size of the window borders.
That is, the important part of the WS_NCCLIENTSIZE handler code looks like this:
...
CRect rc( lpncsp->rgrc[0] );
if (IsZoomed())
{
int borderSize = GetSystemMetrics(SM_CYSIZEFRAME);
rc.InflateRect(borderSize,topOff+borderSize,borderSize,borderSize);
}
else
rc.InflateRect(0,topOff+0,0,0);
lpncsp->rgrc[0] = rc;
...
That code effectively makes the WS_THICKFRAME hidden.
Only problem is that when the window loses focus or regains focus (while maximized) the WS_THICKFRAME gets drawn within the boundary. Is there a message in which I can return the Inflated rect back or at least re-adjust the window size to hide the WS_THICKFRAME again when the window focus is set/unset?
Yeah, that won't work. Implement a message handler for WM_GETMINMAXINFO to allow the borders of the window to fall off the screen. Beware that if you didn't set the linker's /SUBSYSTEM option to say that your program is made for Vista or Win7 (version 6,0) then Aero will lie to you when you use GetWindowRect(). The value you get back is based on thin (legacy) borders.

Resources