XCB: Compress window state change events.
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>
Tue, 10 Apr 2012 11:52:12 +0000 (13:52 +0200)
committerQt by Nokia <qt-info@nokia.com>
Wed, 11 Apr 2012 17:14:43 +0000 (19:14 +0200)
- Avoid sending Window State change events from
  WM_STATE/NET_WM_STATE changes irrelevant to Qt::WindowState.
- Introduce QFlags for the NetWmState getter and setter to
  avoid passing QVector<> around.

Change-Id: I74730928c7fffca0fa1cab3b90ded90b06304c06
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
src/plugins/platforms/xcb/qxcbwindow.cpp
src/plugins/platforms/xcb/qxcbwindow.h

index 5a8f90b..06cc5ad 100644 (file)
@@ -149,6 +149,7 @@ QXcbWindow::QXcbWindow(QWindow *window)
 #if defined(XCB_USE_EGL)
     , m_eglSurface(0)
 #endif
+    , m_lastWindowStateEvent(-1)
 {
     m_screen = static_cast<QXcbScreen *>(window->screen()->handle());
 
@@ -636,29 +637,9 @@ static void setMotifWmHints(QXcbConnection *c, xcb_window_t window, const QtMoti
     }
 }
 
-void QXcbWindow::printNetWmState(const QVector<xcb_atom_t> &state)
+QXcbWindow::NetWmStates QXcbWindow::netWmStates()
 {
-    printf("_NET_WM_STATE (%d): ", state.size());
-    for (int i = 0; i < state.size(); ++i) {
-#define CHECK_WM_STATE(state_atom) \
-        if (state.at(i) == atom(QXcbAtom::state_atom))\
-            printf(#state_atom " ");
-        CHECK_WM_STATE(_NET_WM_STATE_ABOVE)
-        CHECK_WM_STATE(_NET_WM_STATE_BELOW)
-        CHECK_WM_STATE(_NET_WM_STATE_FULLSCREEN)
-        CHECK_WM_STATE(_NET_WM_STATE_MAXIMIZED_HORZ)
-        CHECK_WM_STATE(_NET_WM_STATE_MAXIMIZED_VERT)
-        CHECK_WM_STATE(_NET_WM_STATE_MODAL)
-        CHECK_WM_STATE(_NET_WM_STATE_STAYS_ON_TOP)
-        CHECK_WM_STATE(_NET_WM_STATE_DEMANDS_ATTENTION)
-#undef CHECK_WM_STATE
-    }
-    printf("\n");
-}
-
-QVector<xcb_atom_t> QXcbWindow::getNetWmState()
-{
-    QVector<xcb_atom_t> result;
+    NetWmStates result(0);
 
     xcb_get_property_cookie_t get_cookie =
         xcb_get_property_unchecked(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
@@ -668,15 +649,24 @@ QVector<xcb_atom_t> QXcbWindow::getNetWmState()
         xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
 
     if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
-        result.resize(reply->length);
-
-        memcpy(result.data(), xcb_get_property_value(reply), reply->length * sizeof(xcb_atom_t));
-
-#ifdef NET_WM_STATE_DEBUG
-        printf("getting net wm state (%x)\n", m_window);
-        printNetWmState(result);
-#endif
-
+        const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply));
+        const xcb_atom_t *statesEnd = states + reply->length;
+        if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
+            result |= NetWmStateAbove;
+        if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_BELOW)))
+            result |= NetWmStateBelow;
+        if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)))
+            result |= NetWmStateFullScreen;
+        if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)))
+            result |= NetWmStateMaximizedHorz;
+        if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)))
+            result |= NetWmStateMaximizedVert;
+        if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MODAL)))
+            result |= NetWmStateModal;
+        if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)))
+            result |= NetWmStateStaysOnTop;
+        if (statesEnd != qFind(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
+            result |= NetWmStateDemandsAttention;
         free(reply);
     } else {
 #ifdef NET_WM_STATE_DEBUG
@@ -687,8 +677,26 @@ QVector<xcb_atom_t> QXcbWindow::getNetWmState()
     return result;
 }
 
-void QXcbWindow::setNetWmState(const QVector<xcb_atom_t> &atoms)
+void QXcbWindow::setNetWmStates(NetWmStates states)
 {
+    QVector<xcb_atom_t> atoms;
+    if (states & NetWmStateAbove)
+        atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE));
+    if (states & NetWmStateBelow)
+        atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_BELOW));
+    if (states & NetWmStateFullScreen)
+        atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
+    if (states & NetWmStateMaximizedHorz)
+        atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ));
+    if (states & NetWmStateMaximizedVert)
+        atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
+    if (states & NetWmStateModal)
+        atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MODAL));
+    if (states & NetWmStateStaysOnTop)
+        atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP));
+    if (states & NetWmStateDemandsAttention)
+        atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION));
+
     if (atoms.isEmpty()) {
         Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE)));
     } else {
@@ -699,7 +707,6 @@ void QXcbWindow::setNetWmState(const QVector<xcb_atom_t> &atoms)
     xcb_flush(xcb_connection());
 }
 
-
 Qt::WindowFlags QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
 {
     Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
@@ -966,30 +973,28 @@ void QXcbWindow::updateMotifWmHintsBeforeMap()
 
 void QXcbWindow::updateNetWmStateBeforeMap()
 {
-    QVector<xcb_atom_t> netWmState;
+    NetWmStates states(0);
 
-    Qt::WindowFlags flags = window()->windowFlags();
+    const Qt::WindowFlags flags = window()->windowFlags();
     if (flags & Qt::WindowStaysOnTopHint) {
-        netWmState.append(atom(QXcbAtom::_NET_WM_STATE_ABOVE));
-        netWmState.append(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP));
+        states |= NetWmStateAbove;
+        states |= NetWmStateStaysOnTop;
     } else if (flags & Qt::WindowStaysOnBottomHint) {
-        netWmState.append(atom(QXcbAtom::_NET_WM_STATE_BELOW));
+        states |= NetWmStateBelow;
     }
 
-    if (window()->windowState() & Qt::WindowFullScreen) {
-        netWmState.append(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
-    }
+    if (window()->windowState() & Qt::WindowFullScreen)
+        states |= NetWmStateFullScreen;
 
     if (window()->windowState() & Qt::WindowMaximized) {
-        netWmState.append(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ));
-        netWmState.append(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
+        states |= NetWmStateMaximizedHorz;
+        states |= NetWmStateMaximizedVert;
     }
 
-    if (window()->windowModality() != Qt::NonModal) {
-        netWmState.append(atom(QXcbAtom::_NET_WM_STATE_MODAL));
-    }
+    if (window()->windowModality() != Qt::NonModal)
+        states |= NetWmStateModal;
 
-    setNetWmState(netWmState);
+    setNetWmStates(states);
 }
 
 void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp)
@@ -1452,41 +1457,42 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev
 {
     connection()->setTime(event->time);
 
-    bool propertyDeleted = event->state == XCB_PROPERTY_DELETE;
+    const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE;
+    const xcb_atom_t netWmStateAtom = atom(QXcbAtom::_NET_WM_STATE);
+    const xcb_atom_t wmStateAtom = atom(QXcbAtom::WM_STATE);
 
-    if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) {
+    if (event->atom == netWmStateAtom || event->atom == wmStateAtom) {
         if (propertyDeleted)
             return;
 
-        xcb_get_property_cookie_t get_cookie =
-            xcb_get_property(xcb_connection(), 0, m_window, atom(QXcbAtom::WM_STATE),
-                             XCB_ATOM_ANY, 0, 1024);
+        Qt::WindowState newState = Qt::WindowNoState;
+        if (event->atom == wmStateAtom) { // WM_STATE: Quick check for 'Minimize'.
+            const xcb_get_property_cookie_t get_cookie =
+                xcb_get_property(xcb_connection(), 0, m_window, wmStateAtom,
+                                 XCB_ATOM_ANY, 0, 1024);
 
-        xcb_get_property_reply_t *reply =
-            xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
+            xcb_get_property_reply_t *reply =
+                xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
 
-        xcb_atom_t wm_state = XCB_WM_STATE_WITHDRAWN;
-        if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) {
-            if (reply->length != 0)
-                wm_state = ((long *)xcb_get_property_value(reply))[0];
-            free(reply);
+            if (reply && reply->format == 32 && reply->type == wmStateAtom) {
+                const long *data = (const long *)xcb_get_property_value(reply);
+                if (reply->length != 0 && XCB_WM_STATE_ICONIC == data[0])
+                    newState = Qt::WindowMinimized;
+                free(reply);
+            }
+        } // WM_STATE: Quick check for 'Minimize'.
+        if (newState != Qt::WindowMinimized) { // Something else changed, get _NET_WM_STATE.
+            const NetWmStates states = netWmStates();
+            if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert))
+                newState = Qt::WindowMaximized;
+            else if (states & NetWmStateFullScreen)
+                newState = Qt::WindowFullScreen;
+        }
+        // Send Window state, compress events in case other flags (modality, etc) are changed.
+        if (m_lastWindowStateEvent != newState) {
+            QWindowSystemInterface::handleWindowStateChanged(window(), newState);
+            m_lastWindowStateEvent = newState;
         }
-
-        QVector<xcb_atom_t> netWmState = getNetWmState();
-
-        bool maximized = netWmState.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ))
-            && netWmState.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
-        bool fullscreen = netWmState.contains(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
-
-        Qt::WindowState state = Qt::WindowNoState;
-        if (wm_state == XCB_WM_STATE_ICONIC)
-            state = Qt::WindowMinimized;
-        else if (maximized)
-            state = Qt::WindowMaximized;
-        else if (fullscreen)
-            state = Qt::WindowFullScreen;
-
-        QWindowSystemInterface::handleWindowStateChanged(window(), state);
     }
 }
 
index c212095..d4c8804 100644 (file)
@@ -59,6 +59,19 @@ class QXcbEGLSurface;
 class QXcbWindow : public QXcbObject, public QPlatformWindow
 {
 public:
+    enum NetWmState {
+        NetWmStateAbove = 0x1,
+        NetWmStateBelow = 0x2,
+        NetWmStateFullScreen = 0x4,
+        NetWmStateMaximizedHorz = 0x8,
+        NetWmStateMaximizedVert = 0x10,
+        NetWmStateModal = 0x20,
+        NetWmStateStaysOnTop = 0x40,
+        NetWmStateDemandsAttention = 0x80
+    };
+
+    Q_DECLARE_FLAGS(NetWmStates, NetWmState)
+
     QXcbWindow(QWindow *window);
     ~QXcbWindow();
 
@@ -121,9 +134,8 @@ public:
 
 private:
     void changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two = 0);
-    QVector<xcb_atom_t> getNetWmState();
-    void setNetWmState(const QVector<xcb_atom_t> &atoms);
-    void printNetWmState(const QVector<xcb_atom_t> &state);
+    NetWmStates netWmStates();
+    void setNetWmStates(NetWmStates);
 
     void setNetWmWindowFlags(Qt::WindowFlags flags);
     void setMotifWindowFlags(Qt::WindowFlags flags);
@@ -169,6 +181,7 @@ private:
     QRegion m_exposeRegion;
 
     xcb_visualid_t m_visualId;
+    int m_lastWindowStateEvent;
 };
 
 QT_END_NAMESPACE