From e0e6cbdacb7520a6d9906e16a517c9932702d1aa Mon Sep 17 00:00:00 2001 From: Gatis Paeglis Date: Mon, 13 Mar 2017 20:35:24 +0100 Subject: xcb: prevent dangling pointer when window focus changes With the current implementation, the QXcbConnection::m_focusWindow was holding a dangling pointer during the focus transition from a dying modal window to other modal window. 1) QXcbConnection::m_focusWindow holds a pointer to A; 2) A is closed; 3) relayFocusToModalWindow B; => m_focusWindow now points to a dead window. 4) We get a reply back from WM in a respone to relayFocusToModalWindow (_NET_ACTIVE_WINDOW) => m_focusWindow now points to a valid window. The fix is to update m_focusWindow to nullptr, when the current focus window was destroyed and the new focus window has not been set yet by WM. This patch actually solves a more general case - whenever we get a focus-out event, we should set m_focusWindow to nullptr. It is ok for none of the windows to be in-focus while focus transition is happening. The focusInPeeker will make sure to "optimize out" (when possible) this no-window-in-focus state, and GUI won't be bothered by this extra event. This is how things were working before relayFocusToModalWindow was introduced. Having a focus-relay mechanism in-between is ok, but it should not have changed the original behavior. Task-number: QTBUG-48391 Task-number: QTBUG-55197 Change-Id: I6fdda9de73f999dad84000059ce4b89c0d1a964c Reviewed-by: Laszlo Agocs --- src/plugins/platforms/xcb/qxcbconnection.cpp | 4 ++-- src/plugins/platforms/xcb/qxcbconnection.h | 2 +- src/plugins/platforms/xcb/qxcbwindow.cpp | 20 +++++++++----------- 3 files changed, 12 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 5e600c740b..5e52d4d80f 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -1382,9 +1382,9 @@ void QXcbEventReader::unlock() m_mutex.unlock(); } -void QXcbConnection::setFocusWindow(QXcbWindow *w) +void QXcbConnection::setFocusWindow(QWindow *w) { - m_focusWindow = w; + m_focusWindow = w ? static_cast(w->handle()) : nullptr; } void QXcbConnection::setMouseGrabber(QXcbWindow *w) { diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 01a97a187a..22c7d1aa8e 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -480,7 +480,7 @@ public: Qt::MouseButton translateMouseButton(xcb_button_t s); QXcbWindow *focusWindow() const { return m_focusWindow; } - void setFocusWindow(QXcbWindow *); + void setFocusWindow(QWindow *); QXcbWindow *mouseGrabber() const { return m_mouseGrabber; } void setMouseGrabber(QXcbWindow *); QXcbWindow *mousePressWindow() const { return m_mousePressWindow; } diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 15ca68c663..509bc9b038 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -941,7 +941,7 @@ void QXcbWindow::doFocusIn() if (relayFocusToModalWindow()) return; QWindow *w = static_cast(QObjectPrivate::get(window()))->eventReceiver(); - connection()->setFocusWindow(static_cast(w->handle())); + connection()->setFocusWindow(w); QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason); } @@ -949,7 +949,7 @@ static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event { if (!event) { // FocusIn event is not in the queue, proceed with FocusOut normally. - QWindowSystemInterface::handleWindowActivated(0, Qt::ActiveWindowFocusReason); + QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason); return true; } uint response_type = event->response_type & ~0x80; @@ -974,12 +974,10 @@ static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event void QXcbWindow::doFocusOut() { - if (relayFocusToModalWindow()) - return; - connection()->setFocusWindow(0); - // Do not set the active window to 0 if there is a FocusIn coming. - // There is however no equivalent for XPutBackEvent so register a - // callback for QXcbConnection instead. + connection()->setFocusWindow(nullptr); + relayFocusToModalWindow(); + // Do not set the active window to nullptr if there is a FocusIn coming. + // The FocusIn handler will update QXcbConnection::setFocusWindow() accordingly. connection()->addPeekFunc(focusInPeeker); } @@ -2834,14 +2832,14 @@ void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event) reason = Qt::OtherFocusReason; break; } - connection()->setFocusWindow(static_cast(window()->handle())); + connection()->setFocusWindow(window()); QWindowSystemInterface::handleWindowActivated(window(), reason); break; case XEMBED_FOCUS_OUT: if (window() == QGuiApplication::focusWindow() && !activeWindowChangeQueued(window())) { - connection()->setFocusWindow(0); - QWindowSystemInterface::handleWindowActivated(0); + connection()->setFocusWindow(nullptr); + QWindowSystemInterface::handleWindowActivated(nullptr); } break; } -- cgit v1.2.3