xcb: don't rely on _NET_WORKAREA for available geometry on multi-head systems

On X11, QScreen::availableGeometry() is broken with multi-head systems,
and there doesn't seem to be a real fix for this due to limitation in
the protocol and therefore support in WMs (more details in the
patch). In Gnome this issue is more visible because on this DE the
 _NET_WORKAREA rectangle represents the intersection of the available
geometries on all monitors. This results in a big area of "dead space"
on the secondary screen, when primary screen is positioned lower in the
virtual space. If menu is opened by clicking in this dead space, the menu
is awfully misplaced (qmenu uses availableGeometry() to calculate the
position of menu).

On Ubuntu with Unity (same is true for KDE Neon+Kwin and LUbuntu+Openbox),
_NET_WORKAREA returns a bounding rectangle containing all monitors.
Which does not cause the menu misplacement as "dead space" is outside
clickable area. But this does not mean that the QScreen::availableGeometry()
reported values are correct. With the same setup as described above,
QScreen::availableGeometry() thinks that we have a tool panel on the
right screen, when in reality it is on the left screen.

AwesomeWM for example does not set _NET_WORKAREA at all, which means
QScreen::availableGeometry() == QScreen::geometry(). I am not aware that it
would cause any issues for popup/menu window positioning in Qt (Qt positions
these windows manually by bypassing WM (via Qt::BypassWindowManagerHint) and
using availableGeometry for calculations. With this patch, we would take the
same code path as if _NET_WORKAREA was not set (where we know that_NET_WORKAREA
is cleary wrong). The solution here is to recognize _NET_WORKAREA as true
available geometry only in specific cases (cases where the meaning is cleary
defined by the specification) and adjust the documentation accordingly.

Not knowing the true available geometry on X11 is mitigated by WMs. Window
manager can position windows as it wants. WMs are smart enough not to place
windows on top of reserved areas at edges (even if user has explicitly requested
this via setGeometry based on inaccurate information from availableGeometry()).

[ChangeLog][Platform Specific Changes][Linux] The _NET_WORKAREA atom is
used for calculating QScreen::availableGeometry() only on systems with one
monitor. In all other cases QScreen::availableGeometry() is equal to
QScreen::geometry(). To restore the legacy behavior with untrustworthy
values in QScreen::availableGeometry() set QT_RELY_ON_NET_WORKAREA_ATOM=1
environment variable.

Task-number: QTBUG-60513
Task-number: QTBUG-29278
Task-number: QTBUG-43768
Task-number: QTBUG-18380
Change-Id: I7e0f62f81d1444991b8a6c007c2527d8f96088c2
Reviewed-by: Uli Schlachter <psychon@znc.in>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Gatis Paeglis 2017-07-21 14:01:56 +02:00 committed by Frederik Gladhorn
parent 6c2bfbf88e
commit 5a1f1345aa
4 changed files with 31 additions and 2 deletions

View File

@ -384,6 +384,10 @@ QRect QScreen::geometry() const
The available geometry is the geometry excluding window manager reserved areas
such as task bars and system menus.
Note, on X11 this will return the true available geometry only on systems with one monitor and
if window manager has set _NET_WORKAREA atom. In all other cases this is equal to geometry().
This is a limitation in X11 window manager specification.
*/
QRect QScreen::availableGeometry() const
{

View File

@ -188,6 +188,23 @@ void QXcbVirtualDesktop::subscribeToXFixesSelectionNotify()
}
}
/*! \internal
Using _NET_WORKAREA to calculate the available desktop geometry on multi-head systems (systems
with more than one monitor) is unreliable. Different WMs have different interpretations of what
_NET_WORKAREA means with multiple attached monitors. This gets worse when monitors have
different dimensions and/or screens are not virtually aligned. In Qt we want the available
geometry per monitor (QScreen), not desktop (represented by _NET_WORKAREA). WM specification
does not have an atom for this. Thus, QScreen is limted by the lack of support from the
underlying system.
One option could be that Qt does WM's job of calculating this by subtracting geometries of
_NET_WM_STRUT_PARTIAL and windows where _NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_DOCK.
But this won't work on Gnome 3 shell as it seems that on this desktop environment the tool panel
is painted directly on the root window. Maybe there is some Gnome/GTK API that could be used
to get height of the panel, but I did not find one. Maybe other WMs have their own tricks, so
the reliability of this approach is questionable.
*/
QRect QXcbVirtualDesktop::getWorkArea() const
{
QRect r;
@ -556,6 +573,14 @@ void QXcbScreen::sendStartupMessage(const QByteArray &message) const
} while (sent < length);
}
QRect QXcbScreen::availableGeometry() const
{
static bool enforceNetWorkarea = !qEnvironmentVariableIsEmpty("QT_RELY_ON_NET_WORKAREA_ATOM");
bool isMultiHeadSystem = virtualSiblings().length() > 1;
bool useScreenGeometry = isMultiHeadSystem && !enforceNetWorkarea;
return useScreenGeometry ? m_geometry : m_availableGeometry;
}
QImage::Format QXcbScreen::format() const
{
return qt_xcb_imageFormatForVisual(connection(), screen()->root_depth, visualForId(screen()->root_visual));

View File

@ -154,7 +154,7 @@ public:
QString serialNumber() const override;
QRect geometry() const override { return m_geometry; }
QRect availableGeometry() const override {return m_availableGeometry;}
QRect availableGeometry() const override;
int depth() const override { return screen()->root_depth; }
QImage::Format format() const override;
QSizeF physicalSize() const override { return m_sizeMillimeters; }

View File

@ -152,7 +152,7 @@
on \macos, or the task bar on Windows). The default screen is used if
\a screen is -1.
\sa screenNumber(), screenGeometry()
\sa screenNumber(), screenGeometry(), QScreen::availableGeometry()
*/
/*!