Summary
In fullscreen mode the mini-toolbar's outer window can be placed on a different
host monitor than the machine window it belongs to. Because that window is
screen-sized and carries _NET_WM_STATE_FULLSCREEN, the window manager treats
the unrelated monitor as having a fullscreen window on it, and desktop panels on
that monitor stop being drawn.
With a single guest screen on a three-monitor host, this makes the desktop panel
on an unrelated monitor disappear for as long as the VM runs.
Environment
Host OS Arch Linux
Kernel 7.1.10-arch1-1
Session X11, Cinnamon 6.6.9 / Muffin 6.6.3
VirtualBox 7.2.16
qt6-base 6.11.2
Monitors 3 x 1920x1200 side by side
Guest monitorcount=1, GUI/Fullscreen=true
Host monitor layout, as enumerated by Qt:
[0] DP-2 1920x1200+1920+0
[1] DP-4 1920x1200+3840+0
[2] DP-0 1920x1200+0+0
Observed
The machine window is placed correctly on DP-2 (+1920+0). The mini-toolbar
outer window ends up on DP-4 (+3840+0):
win class="VirtualBox Machine" pos=1920,0 1920x1200
_NET_WM_STATE = FULLSCREEN
win class="VirtualBoxVM" pos=3840,0 1920x1200
_NET_WM_STATE = SKIP_PAGER, SKIP_TASKBAR, FULLSCREEN
_NET_WM_WINDOW_TYPE = UTILITY
WM_TRANSIENT_FOR = <the machine window>
VBox.log shows the toolbar being sent to a third monitor's origin entirely:
GUI: Adjust mini-toolbar for window #0
GUI: Resize mini-toolbar for window #0 to 1920x1200
GUI: Move mini-toolbar for window #0 to 0x0
Setting GUI/ShowMiniToolBar=false removes the stray window and the panel
behaves normally, which confirms the window's identity.
Cause
UIMiniToolBar::sltAdjust() resolves its host screen from the parent widget:
int iHostScreen = UIDesktopWidgetWatchdog::screenNumber(m_pParent);
UIDesktopWidgetWatchdog::screenNumber(const QWidget *) resolves through
pWidget->windowHandle()->screen(). During the fullscreen transition that
native screen association can still lag behind the geometry that
UIMachineWindowFullscreen::placeOnScreen() has already assigned to the
top-level widget, so the toolbar is placed relative to the wrong monitor.
The surrounding code already acknowledges this area is racy — the existing
WORKAROUND comment immediately below handles screenNumber() returning -1.
This is timing-sensitive rather than a Qt behaviour change: a minimal Qt program
that creates an ordinary window and compares windowHandle()->screen() against
QGuiApplication::screenAt(geometry().center()) gives identical, agreeing
results on Qt 6.11.1 and 6.11.2. But VirtualBox's own logs differ between those
two Qt versions with everything else held constant:
Qt 6.11.2 Qt 6.11.1
machine window x=1920 x=1920
mini-toolbar target x=0 x=1920
so a Qt patch-release timing change is enough to start losing the race.
Suggested fix
Resolve the screen from the geometry already assigned to the parent, rather than
from the native window association:
int iHostScreen =
UIDesktopWidgetWatchdog::screenNumber(m_pParent->geometry().center());
screenNumber(const QPoint &) already exists and uses
QGuiApplication::screenAt(). The toolbar adjustment is queued from the
parent's show event, i.e. after placeOnScreen() has moved the parent, so the
geometry is authoritative at that point.
With this change the toolbar is co-located with the machine window
(Move mini-toolbar for window #0 to 1920x0, window at 1920,0), only the
intended monitor is fullscreen, and the unrelated panel is unaffected.
I have a patch and will open a PR against this issue.
Secondary note (separate, latent)
NativeWindowSubsystem::X11SetFullScreenMonitor() writes its uScreenId
argument straight into _NET_WM_FULLSCREEN_MONITORS. Per EWMH that property is
specified in the window manager's Xinerama monitor index space, while callers
pass a Qt screen index. These orders are not the same on this host:
Qt / RandR order [0] DP-2 @1920 [1] DP-4 @3840 [2] DP-0 @0
WM order [0] DP-0 @0 [1] DP-2 @1920 [2] DP-4 @3840
I could not demonstrate a user-visible failure from this on its own — with the
fix above applied the toolbar is placed correctly despite the hint value — so I
am noting it rather than proposing a change.
Summary
In fullscreen mode the mini-toolbar's outer window can be placed on a different
host monitor than the machine window it belongs to. Because that window is
screen-sized and carries
_NET_WM_STATE_FULLSCREEN, the window manager treatsthe unrelated monitor as having a fullscreen window on it, and desktop panels on
that monitor stop being drawn.
With a single guest screen on a three-monitor host, this makes the desktop panel
on an unrelated monitor disappear for as long as the VM runs.
Environment
Host monitor layout, as enumerated by Qt:
Observed
The machine window is placed correctly on DP-2 (
+1920+0). The mini-toolbarouter window ends up on DP-4 (
+3840+0):VBox.logshows the toolbar being sent to a third monitor's origin entirely:Setting
GUI/ShowMiniToolBar=falseremoves the stray window and the panelbehaves normally, which confirms the window's identity.
Cause
UIMiniToolBar::sltAdjust()resolves its host screen from the parent widget:int iHostScreen = UIDesktopWidgetWatchdog::screenNumber(m_pParent);UIDesktopWidgetWatchdog::screenNumber(const QWidget *)resolves throughpWidget->windowHandle()->screen(). During the fullscreen transition thatnative screen association can still lag behind the geometry that
UIMachineWindowFullscreen::placeOnScreen()has already assigned to thetop-level widget, so the toolbar is placed relative to the wrong monitor.
The surrounding code already acknowledges this area is racy — the existing
WORKAROUNDcomment immediately below handlesscreenNumber()returning-1.This is timing-sensitive rather than a Qt behaviour change: a minimal Qt program
that creates an ordinary window and compares
windowHandle()->screen()againstQGuiApplication::screenAt(geometry().center())gives identical, agreeingresults on Qt 6.11.1 and 6.11.2. But VirtualBox's own logs differ between those
two Qt versions with everything else held constant:
so a Qt patch-release timing change is enough to start losing the race.
Suggested fix
Resolve the screen from the geometry already assigned to the parent, rather than
from the native window association:
screenNumber(const QPoint &)already exists and usesQGuiApplication::screenAt(). The toolbar adjustment is queued from theparent's show event, i.e. after
placeOnScreen()has moved the parent, so thegeometry is authoritative at that point.
With this change the toolbar is co-located with the machine window
(
Move mini-toolbar for window #0 to 1920x0, window at1920,0), only theintended monitor is fullscreen, and the unrelated panel is unaffected.
I have a patch and will open a PR against this issue.
Secondary note (separate, latent)
NativeWindowSubsystem::X11SetFullScreenMonitor()writes itsuScreenIdargument straight into
_NET_WM_FULLSCREEN_MONITORS. Per EWMH that property isspecified in the window manager's Xinerama monitor index space, while callers
pass a Qt screen index. These orders are not the same on this host:
I could not demonstrate a user-visible failure from this on its own — with the
fix above applied the toolbar is placed correctly despite the hint value — so I
am noting it rather than proposing a change.