Skip to content

Windows: optionally open the main window on the monitor it was last shown on - #1920

Open
amail80 wants to merge 5 commits into
veracrypt:masterfrom
amail80:fix/remember-monitor
Open

amail80 wants to merge 5 commits into
veracrypt:masterfrom
amail80:fix/remember-monitor

Conversation

@amail80

@amail80 amail80 commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #1858. On a multi-monitor setup the main window opens where DS_CENTER places it, on a monitor chosen by the system, which is often not the one the user keeps VeraCrypt on. This adds an opt-in setting, "Remember which monitor the main window was shown on" (Preferences, General tab), that opens the main window on the monitor it was last shown on.

Behaviour, when the setting is on

  • Placement. At the end of WM_INITDIALOG, if the remembered monitor is attached and the window hasn't been shown yet, the window is centered on that monitor's work area, using MainDlgNormalSize as RepairMainWindowPlacement does. Prompts displayed during startup (command-line mounts, auto-mount) are shown before that and keep their usual placement. Whether the window is then shown normally or minimized, or stays in the notification area, it restores to that position.
  • Recording. The monitor is recorded when the user moves the window to another monitor (WM_ENTERSIZEMOVE / WM_EXITSIZEMOVE, so a click on the caption records nothing), when the setting is turned on, and when the settings are saved. Saving does not replace the remembered monitor with one the system chose because the remembered monitor was missing at startup or was detached during the run, as on a laptop used without its external monitor; it does pick up a move made with Win+Shift+Arrow. Nothing is recorded while the window is hidden, minimized or outside every monitor.
  • Repair. RepairMainWindowPlacement brings a window whose restore position lies outside every monitor back on the remembered monitor rather than on the primary one, converting with that monitor's work-area offset. A restore position that lies on a monitor is repaired on that monitor, as before.
  • Both placements share one centering helper, which keeps the caption on the work area if the window is larger than it.

With the main window shown, the wait dialog and the Secure Desktop prompt follow it (#1865, #1912). While it is hidden in the notification area or minimized, they keep their current placement: GetWindowMonitorWorkAreaCenter skips such a parent, and this PR doesn't change that.

Settings

  • RememberWindowMonitor (default 0) and LastMainWindowMonitor, the display device name of the monitor, e.g. \\.\DISPLAY2. A monitor handle can't be kept between runs, and EnumDisplayMonitors defines no order an index could rely on.
  • With the setting on, the settings file is rewritten when the remembered monitor changes, and only then. Turning the setting off clears the stored monitor.

Translations

The English text of the new string is appended to all language files, as the XML validation requires.

Testing

Windows 11, Debug x64 build of this branch. Two displays at different scale factors: the primary one external, 1920x1080 at (0,0), and the laptop panel below it at (-302,1080), 1260x840 in logical coordinates; "secondary" below is the laptop panel. The test script described in #1858 reads the placement, checks whether the window is centered on a work area, and produces the off-screen states and the appbar.

case result
setting off, window moved to the secondary, restart opens on the primary, as before
setting turned on with the window on the secondary, restart centered on the secondary's work area
moved to the primary by dragging, restart opens on the primary
moved to the secondary with Win+Shift+Arrow, restart opens on the secondary
secondary remembered, started with only the primary attached (Win+P), exit; both attached again, restart opens on the secondary
secondary remembered and detached during the run, window restored on the primary from the taskbar, secondary attached again with the window left on the primary, exit, restart opens on the secondary
reported state of #1858 (not minimized, at (-32000,-32000), 160x28), taskbar click normal size, centered on the secondary, not on the primary
minimized with the parking rectangle as its restore position, taskbar click restored on the secondary
reported state with an 80 px appbar at the top of the secondary, taskbar click centered on the secondary's work area, below the appbar
started with /q background, then the notification area icon clicked opens on the secondary
setting turned off RememberWindowMonitor 0 and LastMainWindowMonitor empty in the settings file; the next start opens on the primary

In Preferences, with the Russian translation (which shows the English text of the new string), the new checkbox fits inside the Windows group box.

For the detach case, Windows 11's "Remember window locations based on monitor connection" was turned off. With it on, which is the default, Windows moves the window back to the secondary when the display is attached again, so the case doesn't arise. For /q background, a volume was mounted, as a Debug build ends a hidden background task when no volume is mounted.

…hown on

Add an opt-in setting, "Remember which monitor the main window was shown
on", to the General tab of Preferences. Without it, the main window opens
where DS_CENTER places it, on a monitor chosen by the system, which on a
multi-monitor setup is often not the monitor the user keeps VeraCrypt on.

The monitor is stored by display device name (for example \\.\DISPLAY2)
as LastMainWindowMonitor: a monitor handle cannot be kept from one run to
the next, and EnumDisplayMonitors defines no order an index could rely
on. When the setting is enabled:

  - At the end of WM_INITDIALOG, the window is centered on the work area
    of the remembered monitor if that monitor is attached and the window
    has not been shown yet. Prompts displayed during startup, such as for
    mounting from the command line, keep their usual placement. Whether
    the window is then shown normally or minimized, or stays in the
    notification area, it restores to that position.
  - The monitor is recorded when the user moves the window to another
    monitor (a click on the caption, which also enters the move loop,
    records nothing), when the setting is turned on, and when the
    settings are saved. Saving records it only if no monitor was
    remembered yet, or if the remembered monitor is attached and the
    window was placed on it, or a monitor was recorded, during this run
    without the remembered monitor being detached since. This picks up a
    move made with Win+Shift+Arrow, but not the monitor the system chose
    because the remembered one was missing, as on a laptop used without
    its external monitor. Nothing is recorded while the window is hidden,
    minimized or outside every monitor.
  - RepairMainWindowPlacement brings a window whose restore position lies
    outside every monitor back on the remembered monitor instead of the
    primary one, and converts the new position with that monitor's work
    area offset. A restore position that lies on a monitor is repaired on
    that monitor, as before.

Both placements now use the same centering, which keeps the caption on
the work area when the window is larger than it.

Turning the setting off clears the remembered monitor. While the setting
is on, the settings file is rewritten when the remembered monitor
changes, and only then. The English text of the new string is appended
to all language files.
@amail80

amail80 commented Oct 17, 2026

Copy link
Copy Markdown
Contributor Author

With 1.26.30 getting close, a gentle ping on this one: it's ready for review and has no conflicts with master. It builds on #1865, #1858 and #1912. If it's too late for this release, the next one is fine too.

@idrassi idrassi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The option makes sense, especially for people who keep VeraCrypt on a secondary display. See inline for the issues I see.

Comment thread src/Mount/Mount.c
Comment on lines +1044 to +1045
if (!bRememberWindowMonitor || !MainDlg || !IsWindowVisible (MainDlg) || IsIconic (MainDlg))
return;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A move made with Win+Shift+Arrow isn't recorded until SaveSettings runs. That leaves a gap if the user hides the window first.

For example, open VeraCrypt on remembered monitor A and move it to B with the keyboard shortcut. Hide it through the hotkey or tray menu, then exit from the tray. ChangeMainWindowVisibility doesn't record the monitor before hiding the window. When settings are finally saved, this check prevents B from being recorded, so the next start returns to A. Minimizing before exiting has the same problem.

Dragging to B works because WM_EXITSIZEMOVE records it earlier. Could we preserve the keyboard choice while the window is still visible, before it gets hidden or minimized? We still need the protection against recording Windows parked position.

Comment thread src/Mount/Mount.c
Comment on lines +8962 to +8963
if (LastMainWindowMonitorDevice[0] && !FindMonitorByDevice (LastMainWindowMonitorDevice))
MainWindowMonitorChosen = FALSE;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once MainWindowMonitorChosen becomes FALSE here, reconnecting the monitor doesn't restore the ability to record keyboard moves.

Consider two displays. Start with the window on remembered monitor A, then disconnect A. After reconnecting it, let Windows return the window to A. Now move it to B with Win+Shift+Arrow and save while the window is visible. RememberMainWindowMonitor still rejects the update because this flag remains FALSE, leaving A remembered.

Please make sure a later keyboard move can establish a new choice without requiring a drag or toggling the option. Simply resetting the flag on reconnection would need care, since that could allow Windows temporary placement to replace the user choice.

Comment thread src/Mount/Mount.rc
"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,158,20,150,16

GROUPBOX "Windows",IDT_WINDOWS_RELATED_SETTING,7,53,304,135
GROUPBOX "Windows",IDT_WINDOWS_RELATED_SETTING,7,53,304,148

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The larger group box extends below its tab page.

I checked this: 100% scaling, the page's client area ends at y=319, while the group box ends at y=327. Its bottom border is clipped by eight pixels. The checkbox ends at y=300, so the checkbox itself fits.

PreferencesDlgProc resizes the page using the result from TabCtrl_AdjustRect. We can't rely on the height declared in the page template when choosing the group height. Could we adjust this to fit the space actually assigned to the page? The original group height fits at this scale.

Comment thread src/Mount/Mount.c
Comment on lines +1003 to +1009
memset (&mi, 0, sizeof (mi));
mi.cbSize = sizeof (mi);
if (GetMonitorInfoW (hMonitor, (LPMONITORINFO) &mi) && wcscmp (mi.szDevice, search->device) == 0)
{
search->hMonitor = hMonitor;
return FALSE;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What guarantees do we have that this name still identifies the same monitor after a restart or a change in how the displays are connected?

\\.\DISPLAY2 identifies a GDI display source. Microsoft documents the source name separately from the monitor target information. If the saved name later belongs to another display, this comparison would accept that display and also treat the remembered monitor as still attached.

I would like us to settle whether we're remembering a Windows display source or the same physical monitor before relying on this comparison to preserve the choice across reconnections.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants