Win32: How to make popup window match monitor DPI - winapi

I'm working on a Win32 app with multiple windows, some of which use WS_POPUPWINDOW style (no caption) and some of which use WS_OVERLAPPEDWINDOW style. The app has System DPI awareness.
When I move the overlapped windows between monitors, they scale to match the current monitor DPI as expected. But the popup windows only do this if there are no other open overlapped windows. Otherwise, they always try to match the scaling of the last active overlapped window in the same process, even if it's on another monitor with a different scale factor.
I guess this makes sense for some use cases, since you'd always want eg. a popup menu to match the scale of the current window. But I want my popups to behave as independent windows with their own scale factor.
How do I make popup windows always match the DPI of the monitor that they're positioned on?

As a quick fix before I eventually convert the app to per-monitor DPI awareness, I created an invisible overlapped owner window for the popup that tracks its position / size. This ensures that whenever the popup is active, it updates to use its owner's scaling, which always matches the current monitor.

Related

Windows API - Maximize a window across all monitors

We have a custom RDP client that we've built using the RDP ActiveX control and MFC:
https://learn.microsoft.com/en-us/windows/win32/termserv/using-remote-desktop-web-connection
In order to support multiple monitors, we use the put_UseMultimon function:
https://learn.microsoft.com/en-us/windows/win32/termserv/imsrdpclientnonscriptable5-usemultimon
This kind of works, but we have to manually stretch the window across the monitors. Maximizing the window will maximize it on one of the monitors. The experience we get is not so good, we can't really use the entire area, and the window title bar remains (when maximizing the window on one screen it disappears).
We would like to get an experience similar to mstsc, where maximizing the window will change it's style to maximized and make the window span on the entire area. Is there a way to maximize a window and make it span across all monitors?
#JonathanPotter is right.
WM_GETMINMAXINFO sent to a window when the size or position of the
window is about to change. An application can use this message to
override the window's default maximized size and position.
The following is an example to achieve this purpose. (Note: Extend from main display to the second display does work. otherwise doesn't work.)
case WM_GETMINMAXINFO:
MINMAXINFO* maxInfo = (MINMAXINFO*)lParam;
maxInfo->ptMaxPosition.x = GetSystemMetrics(SM_XVIRTUALSCREEN);
maxInfo->ptMaxPosition.y = GetSystemMetrics(SM_YVIRTUALSCREEN);
maxInfo->ptMaxSize.x = GetSystemMetrics(SM_CXVIRTUALSCREEN);
maxInfo->ptMaxSize.y = GetSystemMetrics(SM_CYVIRTUALSCREEN);
return 0;
More reference: "Multiple Monitor System Metrics" "GetSystemMetrics function".

How to check the position of taskbar in windows in win32 program?

I'm writing a small application in Windows using win32(vc++) and I want to create a different view based on the position of the taskbar. How can I check the position of taskbar in code like any api?
You can use the SHAppBarMessage() function with the ABM_GETTASKBARPOS message.
The taskbar may be displayed on multiple displays so in general it's not possible to retrieve a single rect that defines it's position. It may occupy discontiguous space.
Use EnumDisplayMonitors to enumerate the attached displays. Then for each display use GetMonitorInfo to obtain bounding and work area rectangles for the display. If there's a difference, then that difference is (usually) due to the taskbar. Remember that the taskbar can appear on multiple displays.
I say usually because you might have applications with app bars that reduce the working area. But I'm guessing that what you really want to do is display your program so that it does not overlap with the taskbar, and if you want to avoid overlapping the taskbar then you'll likely want to avoid overlapping app bars too.

OpenGL on a secondary display

I have a Windows 7 system, a regular monitor as the primary display (serving as a desktop, etc.), and an additional screen attached to the same graphics card.
I want to write a program that takes control of the secondary display and uses it for fullscreen OpenGL rendering. I tried to enumerate displays with EnumDisplaySettings, pick the secondary display, create a device context associated with the display, set the pixel format on the DC, and create a WGL context associated with it. I can get this far without errors, but then the call to wglMakeCurrent fails for no apparent reason (return value is 0, GetLastError() is 0, and OpenGL does not function.)
The only way I could get it to work is to extend the desktop onto the secondary display (manually, from Windows display settings), create a window and move it onto the secondary display. Which is tolerable but undesirable (I don't want the secondary display to interfere with the desktop. For example, in this setup, I can move the mouse cursor from the desktop into the secondary display.) Is there a way to avoid this?
More generally, in order to get OpenGL to work on a display, do I need (1) to have the display attached to the desktop (or "a" desktop?), and/or (2) to have a window of my own on that display?
P.S. It seems that I might be able to get this to work with a third-party library such as glfw3, but I don't want extra baggage (I don't need 90% of functionality of glfw3) and I'd prefer to get this done directly through native API calls if possible.
Unfortunately the Windows graphics driver model does not allow to use displays independently. You will have to extend the desktop to the second display and create a fullscreen window on it. When it comes to constraining the mouse, the usual way is to hook into the system mouse events and whenever the mouse pointer is moved into the secondary screen remove it back to the primary screen.

Can a window be resized past the screen size/offscreen?

My purpose is to size a window to a width/height greater than the size of my physical screen programmatically under Win32. How can I do this?
On my systems it seems the maximum size of a given window is bound by the size of my screen whether programmatically or whether sizing manually by dragging the sizing cursor.
I have tried programmatically with SetWindowPos() and MoveWindow() and both cap the size of the target window. Oddly I know some people do not have this 'cap' so I wonder whether this is perhaps due to some OS setting (registry). Does anyone know something about this? Or perhaps some way to workaround it?
// Edit: new developments
I am testing on Windows XP and Windows 7. The graphics cards I'm using are a NVIDIA Quadro NVS 290 (256MB) and a Geforce 9800GT (1GB). After further investigation it looks like Windows is intercepting the message and fiddling with the parameters. For example, if you call SetWindowPos to make a target 2000x2000 it will only receive a WM_SIZE for the capped x/y.
Implement a message handler for WM_GETMINMAXINFO to stop Windows from applying the sane default behavior:
case WM_GETMINMAXINFO: {
DefWindowProc(hWnd, message, wParam, lParam);
MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
pmmi->ptMaxTrackSize.x = 2000;
pmmi->ptMaxTrackSize.y = 2000;
return 0;
}
Windows with a thick frame (to allow user resize) are restricted from growing larger than the desktop.
Try SetWindowLong() clearing the THICKFRAME (0x40000) flag.
The following should allow programatic sizing, but the user will lose the ability to resize. If you add the Thickframe back after sizing, the user can resize, but when he does so the window will immediately shrink back to the desktop limited size.
The following is from some csharp code that also removes all borders, caption, etc:
WS style = (WS)GetWindowLong(ptr, GWL_STYLE);
style = style & ~WS.BORDER & ~WS.ThickFrame & ~WS.SYSMENU & ~WS.CAPTION | WS.POPUP;
SetWindowLong(ptr, GWL_STYLE, (int)style);
A good tool to play with window settings is uuSpy.
It's like Microsoft Spy++, but allows you to modify settings like THICKFRAME.
Yes, windows can be larger than the screen (or even the sum of all your monitors). Windows can also be positioned off-screen (which some applications do as a hack to hide while remaining active).
Perhaps the Windows 7 desktop manager is kicking in and trying to "dock" those windows to the edges of your screen for you.
You might try using the slightly lower-level API SetWindowPos, which gives you control over notifications, z-order, and other stuff.
You can get a window to be larger in resolution (and even way way larger) than your screen, using the 'Infinte Screen" software:
http://ynea.futureware.at/cgi-bin/infinite_screen.pl
Here's how to use it:
Download it, run it.
In the Oversize tab, choose the Windows you want to enlarge.
Give it the Width and Height you want. Done!
Just in case you need a large screenshot (that's how I ended up here):
If you want to get a screenshot of the window, you've got a screenshot option in the same Oversize tab. (Because screenshots are normally no bigger than the screen size, even if the window is larger). Another (and better) way to screenshot the window is using Greenshot, as you can save them in .tiff and directly watching the window.

Auto-Hide taskbar not appearing when my application is maximized

My application draws all its own window borders and decorations. It works fine with Windows taskbars that are set to auto-hide, except when my application window is maximized. The taskbar won't "roll up". It will behave normally if I have the application not maximized, even when sized all the way to the bottom of the screen. It even works normally if I just resize the window to take up the entire display (as though it was maximized).
I found the problem. My application was handling the WM_GETMINMAXINFO message, and was overriding the values in the parameter MINMAXINFO record. The values that were in the record were inflated by 7 (border width) the screen pixel resolution. That makes sense in that when maximized, it pushes the borders of the window beyond the visible part of the screen. It also set the ptMaxPosition (point that the window origin is set to when maximized) to -7, -7. My application was setting that to 0,0, and the max height and width to exactly the screen resolution size (not inflated). Not sure why this was done; it was written by a predecessor. If I comment out that code and don't modify the MINMAXINFO structure, the Auto-hide works.
As to why, I'm not entirely sure. It's possible that the detection for popping up an "autohidden" taskbar is hooked into the mechanism for handling WM_MOUSEMOVE messages, and not for WM_NCMOUSEMOVE. With my application causing the maximize to park my border right on the bottom of the screen, I would have been generating WM_NCMOUSEMOVE events; with the MINMAXINFO left alone, I would have been generating WM_MOUSEMOVE.
This is dependant on whether 'Keep the taskbar on top of other windows' is checked on the taskbar properties. If it's checked then the taskbar will appear.
But don't be tempted to programmatically alter this setting on an end users machine just to suit your needs, it's considered rude and bad practice. Your app should fit whatever environment it gets deployed to.

Resources