QXcbWindow: protect access to m_mapped with a QMutex

QXcbWindow::requestActivateWindow() is called from the main thread,
while handleMapNotifyEvent() is called from the XCB event thread.
In a data race, handleMapNotifyEvent sets m_mapped to true, before
requestActivateWindow() checks if it is false and sets
m_deferredActivation to true.

If that happens, a window activation is skipped until the next window
systems event is processed.

This is harmless in a normal application environment, where each mouse
move causes window systems events to be processed. In an autotest
environment, it causes flakiness, because an expected window activation
may or may not happen.

As a workaround, QApplicationPrivate::setActiveWindow() is frequently
called after QWidget::activateWindow() or QWindow::requestActivate(),
to set the active window on application level. In essence, this is
setting expected outcome of a skipped deferred activation by force.

This patch protects access to m_mapped and m_deferredActivation with a
QMutex in both methods. That prevents the data race and ensures all
deferred activations processed.

Calling QApplicationPrivate::setActiveWindow() after activation on
window/widget level becomes redundant.

Task-number: QTBUG-119287
Pick-to: 6.5
Change-Id: I2eee69292afe0ef6381880a10d4e8483c2c7cbfa
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
(cherry picked from commit 2f6fe3a26843ff68c5d3f9af0a2fc3cce6caac22)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Axel Spoerl 2023-11-27 16:33:12 +01:00 committed by Qt Cherry-pick Bot
parent 36fadb4c13
commit 4ec05ee2ad
2 changed files with 14 additions and 5 deletions

View File

@ -1461,11 +1461,14 @@ void QXcbWindow::requestActivateWindow()
return;
}
if (!m_mapped) {
m_deferredActivation = true;
return;
{
QMutexLocker locker(&m_mappedMutex);
if (!m_mapped) {
m_deferredActivation = true;
return;
}
m_deferredActivation = false;
}
m_deferredActivation = false;
updateNetWmUserTime(connection()->time());
QWindow *focusWindow = QGuiApplication::focusWindow();
@ -1874,8 +1877,11 @@ QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const
void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event)
{
if (event->window == m_window) {
m_mappedMutex.lock();
m_mapped = true;
if (m_deferredActivation)
const bool deferredActivation = m_deferredActivation;
m_mappedMutex.unlock();
if (deferredActivation)
requestActivateWindow();
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
@ -1885,7 +1891,9 @@ void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event)
void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
{
if (event->window == m_window) {
m_mappedMutex.lock();
m_mapped = false;
m_mappedMutex.unlock();
QWindowSystemInterface::handleExposeEvent(window(), QRegion());
}
}

View File

@ -222,6 +222,7 @@ protected:
Qt::WindowStates m_windowState = Qt::WindowNoState;
QMutex m_mappedMutex;
bool m_mapped = false;
bool m_transparent = false;
bool m_deferredActivation = false;