terminal/src/interactivity/win32/window.hpp
Evan Koschik bc1ff0b71a
Fix restore window position when exiting fullscreen (#9737)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

This change cleans up the Fullscreen implementation for both conhost and Terminal, improving the restore position (where the window goes when exiting fullscreen).

Prior to this change the window wasn't guaranteed to restore somewhere on the window's current monitor when exiting fullscreen. With this change the window will restore always to its current monitor, at a reasonable location (and will 'double restore' (to fullscreen->maximize->restore) after monitor changes while fullscreen, which is the expected user behavior.

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? --> 
## References

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #9746
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

A fullscreen window's monitor can change.
 - Win+Shift+left/right migrates a window between monitors.
 - User could open settings, display, and move the monitor or change its DPI.
 - The monitor could be unplugged.
 - The session could be remote and be disconnected.

A fullscreen window stores a 'restore position' when entering fullscreen, used to move the window back 'where it was'. BUT, its unexpected for the window to exit fullscreen and jump to another monitor. This means its previous position must be migrated from the old monitor's work area to the new monitor's work area.

If a window is maximized, it is sized to the work area. Like with fullscreen, a maximized window has a 'restore position', though unlike with fullscreen the restore position for maximized is stored by the system itself. Migration in cases where a maximized (or fullscreen) window's monitor changes is also taken care of by the system. To restore 'safely' to maximized (after changing window styles) a window must only `SetWindowPos(SWP_FRAMECHANGED)`. While technically a maximized window that becomes fullscreen 'is still maximized' (from Win32's perspective), its prudent to also `ShowWindow(SW_MAXIMIZED)` prior to `SWP_FRAMECHANGED` (to explicitly make the window maximized).

If not restoring to maximized, the restore position is adjusted by the new/ old work area. Additionally, the new/ old window DPI is used to adjust the size of the window by the DPI change (keeping the window's logical size the same).
 - The work area origin is checked first (shifting window rect by the change in origin)
 - The DPI is checked next, changing right/ bottom (size only)
 - Each edge of the window is compared against the corresponding edge of the work area, nudging the window back on-screen if hanging offscreen. By shifting right before left, bottom before top, the top-left is guaranteed on-screen. 

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Tried it out. Seemed to work on my machine.
Jk, ran conhost/ terminal on mixed DPI system, max (or not), fullscreen, win+shift+left/ exit fullscreen/ maximize. Monitor unplug, etc.
2021-04-13 16:33:00 +00:00

176 lines
6.6 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- window.hpp
Abstract:
- This module is used for managing the main console window
Author(s):
- Michael Niksa (MiNiksa) 14-Oct-2014
- Paul Campbell (PaulCam) 14-Oct-2014
--*/
#pragma once
#include "../inc/IConsoleWindow.hpp"
namespace Microsoft::Console::Interactivity::Win32
{
class WindowUiaProvider;
class Window final : public Microsoft::Console::Types::IConsoleWindow
{
public:
[[nodiscard]] static NTSTATUS CreateInstance(_In_ Settings* const pSettings,
_In_ SCREEN_INFORMATION* const pScreen);
[[nodiscard]] NTSTATUS ActivateAndShow(const WORD wShowWindow);
~Window();
RECT GetWindowRect() const noexcept;
HWND GetWindowHandle() const;
SCREEN_INFORMATION& GetScreenInfo();
const SCREEN_INFORMATION& GetScreenInfo() const;
BYTE GetWindowOpacity() const;
void SetWindowOpacity(const BYTE bOpacity);
void ApplyWindowOpacity() const;
void ChangeWindowOpacity(const short sOpacityDelta);
bool IsInMaximized() const;
bool IsInFullscreen() const;
void SetIsFullscreen(const bool fFullscreenEnabled);
void ToggleFullscreen();
void ChangeViewport(const SMALL_RECT NewWindow);
void VerticalScroll(const WORD wScrollCommand,
const WORD wAbsoluteChange);
void HorizontalScroll(const WORD wScrollCommand,
const WORD wAbsoluteChange);
BOOL EnableBothScrollBars();
int UpdateScrollBar(bool isVertical,
bool isAltBuffer,
UINT pageSize,
int maxSize,
int viewportPosition);
void UpdateWindowSize(const COORD coordSizeInChars);
void UpdateWindowPosition(_In_ POINT const ptNewPos) const;
void UpdateWindowText();
void CaptureMouse();
BOOL ReleaseMouse();
// Dispatchers (requests from other parts of the
// console get dispatched onto the window message
// queue/thread)
BOOL SendNotifyBeep() const;
BOOL PostUpdateScrollBars() const;
BOOL PostUpdateWindowSize() const;
BOOL PostUpdateExtendedEditKeys() const;
[[nodiscard]] HRESULT SignalUia(_In_ EVENTID id);
void SetOwner();
BOOL GetCursorPosition(_Out_ LPPOINT lpPoint);
BOOL GetClientRectangle(_Out_ LPRECT lpRect);
int MapPoints(_Inout_updates_(cPoints) LPPOINT lpPoints,
_In_ UINT cPoints);
BOOL ConvertScreenToClient(_Inout_ LPPOINT lpPoint);
[[nodiscard]] HRESULT UiaSetTextAreaFocus();
protected:
// prevent accidental generation of copies
Window(Window const&) = delete;
void operator=(Window const&) = delete;
private:
Window();
// Registration/init
[[nodiscard]] static NTSTATUS s_RegisterWindowClass();
[[nodiscard]] NTSTATUS _MakeWindow(_In_ Settings* const pSettings,
_In_ SCREEN_INFORMATION* const pScreen);
void _CloseWindow() const;
static ATOM s_atomWindowClass;
Settings* _pSettings;
HWND _hWnd;
static Window* s_Instance;
[[nodiscard]] NTSTATUS _InternalSetWindowSize();
void _UpdateWindowSize(const SIZE sizeNew);
void _UpdateSystemMetrics() const;
// Wndproc
[[nodiscard]] static LRESULT CALLBACK s_ConsoleWindowProc(_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam);
[[nodiscard]] LRESULT CALLBACK ConsoleWindowProc(_In_ HWND,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam);
// Wndproc helpers
void _HandleDrop(const WPARAM wParam) const;
[[nodiscard]] HRESULT _HandlePaint() const;
void _HandleWindowPosChanged(const LPARAM lParam);
// Accessibility/UI Automation
[[nodiscard]] LRESULT _HandleGetObject(const HWND hwnd,
const WPARAM wParam,
const LPARAM lParam);
IRawElementProviderSimple* _GetUiaProvider();
WRL::ComPtr<WindowUiaProvider> _pUiaProvider;
// Dynamic Settings helpers
[[nodiscard]] static LRESULT s_RegPersistWindowPos(_In_ PCWSTR const pwszTitle,
const BOOL fAutoPos,
const Window* const pWindow);
[[nodiscard]] static LRESULT s_RegPersistWindowOpacity(_In_ PCWSTR const pwszTitle,
const Window* const pWindow);
// The size/position of the window on the most recent update.
// This is remembered so we can figure out which
// size the client was resized from.
RECT _rcClientLast;
// Full screen
void _RestoreFullscreenPosition(const RECT rcWork);
void _SetFullscreenPosition(const RECT rcMonitor, const RECT rcWork);
bool _fIsInFullscreen;
bool _fWasMaximizedBeforeFullscreen;
RECT _rcWindowBeforeFullscreen;
RECT _rcWorkBeforeFullscreen;
UINT _dpiBeforeFullscreen;
// math helpers
void _CalculateWindowRect(const COORD coordWindowInChars,
_Inout_ RECT* const prectWindow) const;
static void s_CalculateWindowRect(const COORD coordWindowInChars,
const int iDpi,
const COORD coordFontSize,
const COORD coordBufferSize,
_In_opt_ HWND const hWnd,
_Inout_ RECT* const prectWindow);
static void s_ReinitializeFontsForDPIChange();
bool _fInDPIChange = false;
static void s_ConvertWindowPosToWindowRect(const LPWINDOWPOS lpWindowPos,
_Out_ RECT* const prc);
};
}