Setting a window to full screen programmatically - windows

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
.

Related

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

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.

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.

Flicker when moving/resizing window

I have developed an application to display jpeg images. It can display 4 images, one in each quadrant of the screen. It uses 4 windows for that. The windows have no border (frame) nor titlebar.
When a new image is loaded, the window size is adjusted for the new image and then the image is displayed.
Especially when the window is made larger, there is often a flicker. With my eyes to slits, it seems that the old contents is moved when resizing before the new contents is displayed.
I consulted many resources and used all tricks:
the window has only style CS_DBLCLKS (no CS_HREDRAW or CS_VREDRAW);
the background brush is NULL;
WM_ERASEBKGND returns 1;
WM_NCPAINT returns 0;
WM_NCCALCSIZE tells to align to the side not moved (can you tell it to discard the client area?);
WM_WINDOWPOSCHANGING returns 0;
SetWindowPos has flags SWP_NOCOPYBITS | SWP_DEFERERASE | SWP_NOREDRAW | SWP_NOSENDCHANGING.
Still, the flicker (or the contents move) occurrs when resizing the window. What I want is to:
SetWindowPos to new size and position;
InvalidateRect (hWnd, NULL, FALSE);
UpdateWindow(hWnd);
without any painting, background erasing or content moving until WM_PAINT.
WM_PAINT does:
hDC= BeginPaint (hWnd, &ps);
hMemDC= CreateCompatibleDC (hDC);
hOldBitmap = SelectObject (hMemDC, hNewBitmap);
BitBlt (hDC,...,hMemDC,0,0,SRCCOPY);
SelectObject (hMemDC, hOldBitmap);
DeleteDC (hMemDC);
EndPaint (hWnd, &ps);
Can anyone tell me if/where I make a mistake that causes the old content of the window to be moved?
Hardware etc: Windows 7 on HP Elitebook Core7 64 bits with NVIDIA Quadro K1000m driver 9.18.13.3265 (updated to 341.44).
UPDATE (Jul '17)
I have seen the behavior of the pogram also on another Windows computer (Windows 8/10). It does not seem to be the NVIDIA display driver.
The behavior is the most visible when resizing a window tiled to the centre of the screen (right bottom = w/2, h/2) to the left or left upper corner (0, 0).
I may have problems with the calculations for the WM_NCCALCSIZE message to tell Windows not to do anything. Could anyone give an example calculation for my purpose? See also How do I force windows NOT to redraw anything in my dialog when the user is resizing my dialog?
You have an impressive list of anti-flickering tricks :-)
I don't know if this is of importance (since it depends on how your tool windows are created and espacially if they are child windows to a common parent):
Try setting window style WS_CLIPCHILDREN in the parent window of the tool windows (if there is one).
If not set the parent window will erase it's (entire) background and then forward the paint messages to the child windows which will cause flickering. If WS_CLIPCHILDREN is set the parent window does nothing to the client area occupied by child windows. As a result the area of child windows isn't drawn twice and there is no chance for flickering.
This is a theory more than an answer:
By default, in modern Windows, your window is just a texture on the video card, and the desktop window manager is mapping that to a rectangle on the screen. You've seem to have done everything necessary to make sure that texture gets updated in one fell swoop.
But when you resize the window, perhaps the desktop compositor immediately updates its geometry, causing the (still unchanged) texture to be appear in the new position on the screen. It's only later, when you do the paint, that the texture is updated.
You can test this theory by temporarily turning off desktop compositing. On Windows 7, you navigate to System Properties, choose the Advanced tab, under Performance choose Settings..., on the Visual Effects tab, deselect the "Enable desktop composition" setting. Then try to reproduce the problem. If it goes away, then that supports (but doesn't absolutely prove) my theory.
(Remember to re-enable compositing, since that's how most of your users will be running most of the time.)
If the theory is true, it seems the goal is to get to the paint as soon possible after the window resize. If the window of time is small enough, then both could happen within a monitor refresh cycle and there would be no flicker.
Ironically, your efforts to eliminate flicker may be working against you here, since you've intentionally suppressed the invalidation and redraw that would normally result from SetWindowPos until you do it manually at a later step.
A debugging tip: Try introducing delays (e.g., Sleep(1000);) at key points in the process so you can see whether the resize and redraw are actually rendering on screen as two distinct steps.
You have indeed got a nice set of tricks.
First I can suggest a few variants on the existing tricks that might help especially on XP/Vista/7, and second I want to mention where the persistent flicker you see on Win8/10 is likely coming from and some tricks to reduce that.
First, despite advice to the contrary in other OP posts, you may find that if you set the CS_HREDRAW|CS_VREDRAW window styles that you avoided before, it actually eliminates the BitBlt that is done inside the internal SetWindowPos Windows does during window resizing (but---and this is the confusing part---on Windows 8/10 you will still see flicker from another source...more on that below).
If you don't want to include CS_HREDRAW|CS_VREDRAW, you can also intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing.
Or, you could add to your WM_NCCALCSIZE trick by having it return a set of values that will tell Windows to just BitBlt 1 pixel on top of itself so that even if the BitBlt does happen, it doesn't do anything visibly:
case WM_NCCALCSIZE:
{
RECT ocr; // old client rect
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new window rect
// np->rgrc[1] is old window rect
// np->rgrc[2] is old client rect
ocr = np->rgrc[2];
}
else
{
RECT *r = (RECT *)lParam;
// *r is window rect
}
// first give Windoze a crack at it
lRet = DefWindowProc(hWnd, uMsg, wParam, lParam);
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new client rect computed
// np->rgrc[1] is going to be dst blit rect
// np->rgrc[2] is going to be src blit rect
//
ncr = np->rgrc[0];
RECT &dst = np->rgrc[1];
RECT &src = np->rgrc[2];
// FYI DefWindowProc gives us new client rect that
// - in y
// - shares bottom edge if user dragging top border
// - shares top edge if user dragging bottom border
// - in x
// - shares left edge if user dragging right border
// - shares right edge if user dragging left border
//
src = ocr;
dst = ncr;
// - so src and dst may have different size
// - ms dox are totally unclear about what this means
// https://learn.microsoft.com/en-us/windows/desktop/
// winmsg/wm-nccalcsize
// https://learn.microsoft.com/en-us/windows/desktop/
// api/winuser/ns-winuser-tagnccalcsize_params
// - they just say "src is clipped by dst"
// - they don't say how src and dst align for blt
// - resolve ambiguity
// essentially disable BitBlt by making src/dst same
// make it 1 px to avoid waste in case Windoze still does it
dst.right = dst.left + 1;
dst.bottom = dst.top + 1;
src.right = dst.left + 1;
src.bottom = dst.top + 1;
lRet = WVR_VALIDRECTS;
}
else // wParam == 0: Windoze wants us to map a single rect w->c
{
RECT *r = (RECT *)lParam;
// *r is client rect
}
return lRet;
So that's all very nice, but why does Windows 8/10 look so bad?
Apps under Windows 8/10 Aero don't draw directly to the screen, but rather draw to offscreen buffers that are then composited by the evil DWM.exe window manager. DWM actually adds another layer of BitBlt-type behavior on top of the existing legacy XP/Vista/7 BitBlt behavior.
And the DWM blit behavior is even more crazy because they don't just copy the client area, but they actually replicate pixels at the edges of your old client area to make the new one. Unfortunately, making DWM not do its blit is much harder than just passing some extra flags.
I don't have a 100% solution, but I hope the above info will help, and please see this Q&A for a timing trick you can use to reduce the chances of the DWM-layer blit happening to your app:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
Just in addition to your list of tricks. I have the same problem with flicker on my Dell XPS notebook while resizing window using left/top edge. I've tried all the tricks you mentioned. As far as I understand window border is drawn in GPU and the window content is prepared in GDI subsystem and transferred to video memory for window composition (DWM introduced in Windows 8.1). I tried to remove GDI rendering completely (setting WS_EX_NOREDIRECTIONBITMAP style) which makes window without any content, and then create content surface directly using DXGI subsystem (using CreateSwapChainForComposition, there are few examples on how to do this). But the problem remains. There is still a lag between rendering a window frame and resizing/displaying content surface.
However it may solve your problem, as you don't have the border and you will not notice this lag. But you will have full control over window repaint and it will be made on GPU side.

Windows 8 Tray notification bug

I'm trying to create simple Powershell script on Windows 8, that will notify me via system tray notification balloon. Code is very simple:
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$objNotifyIcon.Icon = "D:\1.ico"
$objNotifyIcon.BalloonTipIcon = "Info"
$objNotifyIcon.BalloonTipText = "I'm there"
$objNotifyIcon.BalloonTipTitle = "Hello!"
$objNotifyIcon.Visible = $True
$objNotifyIcon.ShowBalloonTip(10000)
1.ico is custom icon really existing on disc.
It works as it should except one small thing. I prefer to have taskbar on top of my window and it seems to make troubles for balloon: it is painted under taskbar (screen: https://dl.dropbox.com/u/1138313/systraybug.PNG).
I made test application in C# with notifyIcon and got same result. But another applications like Dropbox or Skydrive haven't such problem and my script with taskbar on bottom works perfect too.
I didn't find any style options in docs for NotifyIcon. Is it annoying bug or I can fix it?
Regards.
UPS: It seems, that however Dropbox app has same problem (shame on me, didn't see at first time). So this is system bug, I guess.
This is known bug in Windows. The only way you may be able to override the behavior of the taskbar is to find the handle of the balloon and then use SetWindowPos to make it topmost:
BOOL WINAPI SetWindowPos(
_In_ HWND hWnd,
_In_opt_ HWND hWndInsertAfter,
_In_ int X,
_In_ int Y,
_In_ int cx,
_In_ int cy,
_In_ UINT uFlags
);
MSDN: "A window can be made a topmost window either by setting the hWndInsertAfter parameter to HWND_TOPMOST and ensuring that the SWP_NOZORDER flag is not set, or by setting a window's position in the Z order so that it is above any existing topmost windows. When a non-topmost window is made topmost, its owned windows are also made topmost. Its owners, however, are not changed." See SetWindowPos for more info.
Another strategy is to demote the taskbar. Use this code to find the topmost window:
HWND FindMyTopMostWindow()
{
DWORD dwProcID = GetCurrentProcessId();
HWND hWnd = GetTopWindow(GetDesktopWindow());
while(hWnd)
{
DWORD dwWndProcID = 0;
GetWindowThreadProcessId(hWnd, &dwWndProcID);
if(dwWndProcID == dwProcID)
return hWnd;
hWnd = GetNextWindow(hWnd, GW_HWNDNEXT);
}
return NULL;
}
Then demote the window or set the zorder of your window higher.
I had the same problem and discovered that the shape of the balloon depend on the size of the message body.
Namely, if your message body has up to 60 chars, round-shaped balloon will be displayed, and for longer messages, new and standard square-shaped balloon will be used.
I am not using the PowerShell to interact with system tray, but WPF NiotifyIcon library for tray icon display within WPF apps.
HTH

Resources