From 50b8506eaccc3c9bc3d717b241193bc8be9708b0 Mon Sep 17 00:00:00 2001 From: Jorgen Lind Date: Thu, 3 Apr 2014 13:11:59 +0200 Subject: XCB: fix that modal dialogs can go behind other process windows Task-number: QTBUG-35302 Change-Id: I1ad7a66e530710d5338a15057254360dae676451 Reviewed-by: Friedemann Kleint Reviewed-by: Laszlo Agocs --- src/plugins/platforms/windows/qwindowscontext.cpp | 8 +- src/plugins/platforms/windows/qwindowswindow.cpp | 22 ++--- src/plugins/platforms/xcb/qxcbwindow.cpp | 104 +++++++++++++++------- src/plugins/platforms/xcb/qxcbwindow.h | 4 + 4 files changed, 93 insertions(+), 45 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 6462cb8d3e..f66859b72a 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -49,11 +49,11 @@ #include "qwindowsmime.h" #include "qwindowsinputcontext.h" #include "qwindowstabletsupport.h" +#include #ifndef QT_NO_ACCESSIBILITY # include "accessible/qwindowsaccessibility.h" #endif #if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) -# include # include # include "qwindowssessionmanager.h" #endif @@ -1025,6 +1025,12 @@ void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et, { QWindow *nextActiveWindow = 0; if (et == QtWindows::FocusInEvent) { + QWindow *topWindow = QWindowsWindow::topLevelOf(platformWindow->window()); + QWindow *modalWindow = 0; + if (QGuiApplicationPrivate::instance()->isWindowBlocked(topWindow, &modalWindow) && topWindow != modalWindow) { + modalWindow->requestActivate(); + return; + } nextActiveWindow = platformWindow->window(); } else { // Focus out: Is the next window known and different diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index ee9bf9936c..db06525508 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1016,17 +1016,17 @@ QWindow *QWindowsWindow::topLevelOf(QWindow *w) while (QWindow *parent = w->parent()) w = parent; - const QWindowsWindow *ww = static_cast(w->handle()); - - // In case the topmost parent is embedded, find next ancestor using native methods - if (ww->isEmbedded(0)) { - HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT); - const HWND desktopHwnd = GetDesktopWindow(); - const QWindowsContext *ctx = QWindowsContext::instance(); - while (parentHWND && parentHWND != desktopHwnd) { - if (QWindowsWindow *ancestor = ctx->findPlatformWindow(parentHWND)) - return topLevelOf(ancestor->window()); - parentHWND = GetAncestor(parentHWND, GA_PARENT); + if (const QPlatformWindow *handle = w->handle()) { + const QWindowsWindow *ww = static_cast(handle); + if (ww->isEmbedded(0)) { + HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT); + const HWND desktopHwnd = GetDesktopWindow(); + const QWindowsContext *ctx = QWindowsContext::instance(); + while (parentHWND && parentHWND != desktopHwnd) { + if (QWindowsWindow *ancestor = ctx->findPlatformWindow(parentHWND)) + return topLevelOf(ancestor->window()); + parentHWND = GetAncestor(parentHWND, GA_PARENT); + } } } return w; diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 193e75c1d6..dbffd577c1 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -485,7 +485,7 @@ QXcbWindow::~QXcbWindow() void QXcbWindow::destroy() { if (connection()->focusWindow() == this) - connection()->setFocusWindow(0); + doFocusOut(); if (m_syncCounter && m_usingSyncProtocol) Q_XCB_CALL(xcb_sync_destroy_counter(xcb_connection(), m_syncCounter)); @@ -671,6 +671,9 @@ void QXcbWindow::show() Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window)); + if (QGuiApplication::modalWindow() == window()) + requestActivateWindow(); + m_screen->windowShown(this); connection()->sync(); @@ -694,6 +697,68 @@ void QXcbWindow::hide() m_mapped = false; } +static QWindow *tlWindow(QWindow *window) +{ + if (window && window->parent()) + return tlWindow(window->parent()); + return window; +} + +bool QXcbWindow::relayFocusToModalWindow() const +{ + QWindow *w = tlWindow(static_cast(QObjectPrivate::get(window()))->eventReceiver()); + QWindow *modal_window = 0; + if (QGuiApplicationPrivate::instance()->isWindowBlocked(w,&modal_window) && modal_window != w) { + modal_window->requestActivate(); + connection()->flush(); + return true; + } + + return false; +} + +void QXcbWindow::doFocusIn() +{ + if (relayFocusToModalWindow()) + return; + QWindow *w = static_cast(QObjectPrivate::get(window()))->eventReceiver(); + connection()->setFocusWindow(static_cast(w->handle())); + QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason); +} + +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); + return true; + } + uint response_type = event->response_type & ~0x80; + if (response_type == XCB_FOCUS_IN) + return true; + + /* We are also interested in XEMBED_FOCUS_IN events */ + if (response_type == XCB_CLIENT_MESSAGE) { + xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event; + if (cme->type == connection->atom(QXcbAtom::_XEMBED) + && cme->data.data32[1] == XEMBED_FOCUS_IN) + return true; + } + + return false; +} + +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()->addPeekFunc(focusInPeeker); +} + struct QtMotifWmHints { quint32 flags, functions, decorations; qint32 input_mode; @@ -1514,6 +1579,8 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even QWindowSystemInterface::handleCloseEvent(window()); } else if (event->data.data32[0] == atom(QXcbAtom::WM_TAKE_FOCUS)) { connection()->setTime(event->data.data32[1]); + relayFocusToModalWindow(); + return; } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) { if (event->window == m_screen->root()) return; @@ -1549,8 +1616,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even } else if (event->type == atom(QXcbAtom::_XEMBED)) { handleXEmbedMessage(event); } else if (event->type == atom(QXcbAtom::_NET_ACTIVE_WINDOW)) { - connection()->setFocusWindow(this); - QWindowSystemInterface::handleWindowActivated(window(), Qt::ActiveWindowFocusReason); + doFocusIn(); } else if (event->type == atom(QXcbAtom::MANAGER) || event->type == atom(QXcbAtom::_NET_WM_STATE) || event->type == atom(QXcbAtom::WM_CHANGE_STATE)) { @@ -1865,41 +1931,13 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *) { - QWindow *w = window(); - w = static_cast(QObjectPrivate::get(w))->eventReceiver(); - connection()->setFocusWindow(static_cast(w->handle())); - QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason); + doFocusIn(); } -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); - return true; - } - uint response_type = event->response_type & ~0x80; - if (response_type == XCB_FOCUS_IN) - return true; - - /* We are also interested in XEMBED_FOCUS_IN events */ - if (response_type == XCB_CLIENT_MESSAGE) { - xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event; - if (cme->type == connection->atom(QXcbAtom::_XEMBED) - && cme->data.data32[1] == XEMBED_FOCUS_IN) - return true; - } - - return false; -} void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *) { - 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()->addPeekFunc(focusInPeeker); + doFocusOut(); } void QXcbWindow::updateSyncRequestCounter() diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index a90ad7b5ed..12d17023fb 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -176,6 +176,10 @@ private: void show(); void hide(); + bool relayFocusToModalWindow() const; + void doFocusIn(); + void doFocusOut(); + QXcbScreen *m_screen; xcb_window_t m_window; -- cgit v1.2.3