xcb: Process _NET_WORKAREA and screen geometry changes separately

This commit allows to avoid superfluous calls to the X server.
We don't request _NET_WORKAREA values when the screen geometry changes,
and we avoid RandR calls on each _NET_WORKAREA change.
Besides, update the available geometry of all screens with the
same root window, rather than only that one which corresponds
to Qt::Desktop window.

Change-Id: I5ec624717f5f261c986cd9aaf2425f22985e11c0
Reviewed-by: Błażej Szczygieł <spaz16@wp.pl>
Reviewed-by: Uli Schlachter <psychon@znc.in>
Reviewed-by: Shawn Rutledge <shawn.rutledge@theqtcompany.com>
This commit is contained in:
Alexander Volkov 2015-08-19 19:13:07 +03:00
parent 8beec99899
commit 2047fe9253
4 changed files with 64 additions and 25 deletions

View File

@ -1121,8 +1121,17 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
handled = false;
break;
case XCB_PROPERTY_NOTIFY:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent);
{
xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event;
if (pn->atom == atom(QXcbAtom::_NET_WORKAREA)) {
QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(pn->window);
if (virtualDesktop)
virtualDesktop->updateWorkArea();
} else {
HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent);
}
break;
}
#if defined(XCB_USE_XINPUT2)
case XCB_GE_GENERIC:
// Here the windowEventListener is invoked from xi2HandleEvent()

View File

@ -58,6 +58,8 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t
cmAtomName += QByteArray::number(m_number);
m_net_wm_cm_atom = connection->internAtom(cmAtomName.constData());
m_compositingActive = connection->getSelectionOwner(m_net_wm_cm_atom);
m_workArea = getWorkArea();
}
QXcbVirtualDesktop::~QXcbVirtualDesktop()
@ -112,6 +114,40 @@ void QXcbVirtualDesktop::subscribeToXFixesSelectionNotify()
}
}
QRect QXcbVirtualDesktop::getWorkArea() const
{
QRect r;
xcb_get_property_reply_t * workArea =
xcb_get_property_reply(xcb_connection(),
xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
atom(QXcbAtom::_NET_WORKAREA),
XCB_ATOM_CARDINAL, 0, 1024), NULL);
if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) {
// If workArea->value_len > 4, the remaining ones seem to be for WM's virtual desktops
// (don't mess with QXcbVirtualDesktop which represents an X screen).
// But QScreen doesn't know about that concept. In reality there could be a
// "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop.
// But for now just assume the first 4 values give us the geometry of the
// "work area", AKA "available geometry"
uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea);
r = QRect(geom[0], geom[1], geom[2], geom[3]);
} else {
r = QRect(QPoint(), size());
}
free(workArea);
return r;
}
void QXcbVirtualDesktop::updateWorkArea()
{
QRect workArea = getWorkArea();
if (m_workArea != workArea) {
m_workArea = workArea;
foreach (QPlatformScreen *screen, m_screens)
((QXcbScreen *)screen)->updateAvailableGeometry();
}
}
QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output,
QString outputName)
@ -457,7 +493,6 @@ void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation)
{
QRect xGeometry = geom;
QRect xAvailableGeometry = xGeometry;
switch (rotation) {
case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal
m_orientation = Qt::LandscapeOrientation;
@ -486,34 +521,23 @@ void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation)
Q_MM_PER_INCH * xGeometry.width() / dpi.second);
}
xcb_get_property_reply_t * workArea =
xcb_get_property_reply(xcb_connection(),
xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
atom(QXcbAtom::_NET_WORKAREA),
XCB_ATOM_CARDINAL, 0, 1024), NULL);
if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) {
// If workArea->value_len > 4, the remaining ones seem to be for virtual desktops.
// But QScreen doesn't know about that concept. In reality there could be a
// "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop.
// But for now just assume the first 4 values give us the geometry of the
// "work area", AKA "available geometry"
uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea);
QRect virtualAvailableGeometry(geom[0], geom[1], geom[2], geom[3]);
// Take the intersection of the desktop's available geometry with this screen's geometry
// to get the part of the available geometry which belongs to this screen.
xAvailableGeometry = xGeometry & virtualAvailableGeometry;
}
free(workArea);
qreal dpi = xGeometry.width() / physicalSize().width() * qreal(25.4);
m_pixelDensity = qRound(dpi/96);
m_geometry = QRect(xGeometry.topLeft(), xGeometry.size());
m_nativeGeometry = QRect(xGeometry.topLeft(), xGeometry.size());
m_availableGeometry = QRect(xAvailableGeometry.topLeft(), xAvailableGeometry.size());
m_availableGeometry = xGeometry & m_virtualDesktop->workArea();
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry);
}
void QXcbScreen::updateAvailableGeometry()
{
QRect availableGeometry = m_geometry & m_virtualDesktop->workArea();
if (m_availableGeometry != availableGeometry) {
m_availableGeometry = availableGeometry;
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry);
}
}
void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode)
{
if (!connection()->hasXRandr())

View File

@ -77,10 +77,15 @@ public:
bool compositingActive() const;
QRect workArea() const { return m_workArea; }
void updateWorkArea();
void handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event);
void subscribeToXFixesSelectionNotify();
private:
QRect getWorkArea() const;
xcb_screen_t *m_screen;
int m_number;
QList<QPlatformScreen *> m_screens;
@ -88,6 +93,8 @@ private:
QXcbXSettings *m_xSettings;
xcb_atom_t m_net_wm_cm_atom;
bool m_compositingActive;
QRect m_workArea;
};
class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen
@ -142,6 +149,7 @@ public:
void handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event);
void updateGeometry(const QRect &geom, uint8_t rotation);
void updateGeometry(xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME);
void updateAvailableGeometry();
void updateRefreshRate(xcb_randr_mode_t mode);
void readXResources();

View File

@ -2378,8 +2378,6 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev
return;
} else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) {
m_dirtyFrameMargins = true;
} else if (event->atom == atom(QXcbAtom::_NET_WORKAREA) && xcbScreen() && event->window == xcbScreen()->root()) {
xcbScreen()->updateGeometry(event->time);
}
}