From 2047fe9253775347cced87bca5a6b9adf364c0bb Mon Sep 17 00:00:00 2001
From: Alexander Volkov <a.volkov@rusbitech.ru>
Date: Wed, 19 Aug 2015 19:13:07 +0300
Subject: [PATCH] xcb: Process _NET_WORKAREA and screen geometry changes
 separately
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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>
---
 src/plugins/platforms/xcb/qxcbconnection.cpp | 11 ++++-
 src/plugins/platforms/xcb/qxcbscreen.cpp     | 68 +++++++++++++++++++---------
 src/plugins/platforms/xcb/qxcbscreen.h       |  8 ++++
 src/plugins/platforms/xcb/qxcbwindow.cpp     |  2 -
 4 files changed, 64 insertions(+), 25 deletions(-)

diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index e4eaeef..901764b 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -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()
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 9b24884..2a53b18 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -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())
diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h
index 7b5d3cb..c68c290 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.h
+++ b/src/plugins/platforms/xcb/qxcbscreen.h
@@ -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();
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index b691c75..bcc571c 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -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);
     }
 }
 
-- 
2.6.2

