diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbclipboard.cpp | 8 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 45 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 115 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.h | 16 |
4 files changed, 169 insertions, 15 deletions
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index 0a4d675606..dabdfcb6c5 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -835,6 +835,8 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb alloc_error = buf.size() != nbytes+1; } + QElapsedTimer timer; + timer.start(); for (;;) { connection()->flush(); xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY); @@ -870,9 +872,11 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb tmp_buf.resize(0); offset += length; } - } else { - break; } + + const auto elapsed = timer.elapsed(); + if (elapsed > clipboard_timeout) + break; } // timed out ... create a new requestor window, otherwise the requestor diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 9abdae6a7c..34fbc0b10b 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -221,6 +221,12 @@ void QXcbConnection::printXcbEvent(const QLoggingCategory &log, const char *mess } #define CASE_PRINT_AND_RETURN(name) case name : PRINT_AND_RETURN(#name); +#define XI_PRINT_AND_RETURN(name) { \ + qCDebug(log, "%s | XInput Event(%s) | sequence: %d", message, name, sequence); \ + return; \ +} +#define XI_CASE_PRINT_AND_RETURN(name) case name : XI_PRINT_AND_RETURN(#name); + switch (response_type) { CASE_PRINT_AND_RETURN( XCB_KEY_PRESS ); CASE_PRINT_AND_RETURN( XCB_KEY_RELEASE ); @@ -255,7 +261,44 @@ void QXcbConnection::printXcbEvent(const QLoggingCategory &log, const char *mess CASE_PRINT_AND_RETURN( XCB_COLORMAP_NOTIFY ); CASE_PRINT_AND_RETURN( XCB_CLIENT_MESSAGE ); CASE_PRINT_AND_RETURN( XCB_MAPPING_NOTIFY ); - CASE_PRINT_AND_RETURN( XCB_GE_GENERIC ); + case XCB_GE_GENERIC: { + if (hasXInput2() && isXIEvent(event)) { + auto *xiDeviceEvent = reinterpret_cast<const xcb_input_button_press_event_t*>(event); // qt_xcb_input_device_event_t + switch (xiDeviceEvent->event_type) { + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_KEY_PRESS ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_KEY_RELEASE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_BUTTON_PRESS ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_BUTTON_RELEASE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_MOTION ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_ENTER ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_LEAVE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_FOCUS_IN ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_FOCUS_OUT ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_HIERARCHY ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_PROPERTY ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_KEY_PRESS ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_KEY_RELEASE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_BUTTON_PRESS ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_BUTTON_RELEASE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_MOTION ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_TOUCH_BEGIN ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_TOUCH_UPDATE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_TOUCH_END ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_TOUCH_OWNERSHIP ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_TOUCH_BEGIN ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_TOUCH_UPDATE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_TOUCH_END ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_BARRIER_HIT ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_BARRIER_LEAVE ); + default: + qCDebug(log, "%s | XInput Event(other type) | sequence: %d", message, sequence); + return; + } + } else { + qCDebug(log, "%s | %s(%d) | sequence: %d", message, "XCB_GE_GENERIC", response_type, sequence); + return; + } + } } // XFixes if (isXFixesType(response_type, XCB_XFIXES_SELECTION_NOTIFY)) diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 050182537d..da179591e9 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -564,6 +564,11 @@ void QXcbWindow::setGeometry(const QRect &rect) { QPlatformWindow::setGeometry(rect); + if (shouldDeferTask(Task::SetGeometry)) { + m_deferredGeometry = rect; + return; + } + propagateSizeHints(); QXcbScreen *currentScreen = xcbScreen(); @@ -688,6 +693,9 @@ void QXcbWindow::setVisible(bool visible) void QXcbWindow::show() { + if (shouldDeferTask(Task::Map)) + return; + if (window()->isTopLevel()) { // update WM_NORMAL_HINTS @@ -738,6 +746,10 @@ void QXcbWindow::show() void QXcbWindow::hide() { + if (shouldDeferTask(Task::Unmap)) + return; + + m_wmStateValid = false; xcb_unmap_window(xcb_connection(), m_window); // send synthetic UnmapNotify event according to icccm 4.1.4 @@ -897,6 +909,9 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates() void QXcbWindow::setWindowFlags(Qt::WindowFlags flags) { + if (shouldDeferTask(Task::SetWindowFlags)) + return; + Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask)); if (type == Qt::ToolTip) @@ -926,6 +941,8 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags) setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput); updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus); + + m_isWmManagedWindow = !(flags & Qt::X11BypassWindowManagerHint); } void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags) @@ -1125,6 +1142,9 @@ void QXcbWindow::setWindowState(Qt::WindowStates state) if (state == m_windowState) return; + if (shouldDeferTask(Task::SetWindowState)) + return; + // unset old state if (m_windowState & Qt::WindowMinimized) xcb_map_window(xcb_connection(), m_window); @@ -1874,6 +1894,10 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event) if (event->window == m_window) { m_mapped = false; QWindowSystemInterface::handleExposeEvent(window(), QRegion()); + if (!m_isWmManagedWindow) { + m_wmStateValid = true; + handleDeferredTasks(); + } } } @@ -2188,30 +2212,98 @@ void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) handleLeaveNotifyEvent(event->root_x, event->root_y, event->mode, event->detail, event->time); } +bool QXcbWindow::shouldDeferTask(Task task) +{ + if (m_wmStateValid) + return false; + + m_deferredTasks.append(task); + return true; +} + +void QXcbWindow::handleDeferredTasks() +{ + Q_ASSERT(m_wmStateValid == true); + if (m_deferredTasks.isEmpty()) + return; + + bool map = false; + bool unmap = false; + + QVector<Task> tasks; + for (auto taskIt = m_deferredTasks.rbegin(); taskIt != m_deferredTasks.rend(); ++taskIt) { + if (!tasks.contains(*taskIt)) + tasks.prepend(*taskIt); + } + + for (Task task : tasks) { + switch (task) { + case Task::Map: + map = true; + unmap = false; + break; + case Task::Unmap: + unmap = true; + map = false; + break; + case Task::SetGeometry: + setGeometry(m_deferredGeometry); + break; + case Task::SetWindowFlags: + setWindowFlags(window()->flags()); + break; + case Task::SetWindowState: + setWindowState(window()->windowState()); + break; + } + } + m_deferredTasks.clear(); + + if (map) { + Q_ASSERT(unmap == false); + show(); + } + if (unmap) { + Q_ASSERT(map == false); + hide(); + } +} + void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) { connection()->setTime(event->time); - const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE; - - if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) { - if (propertyDeleted) + const bool wmStateChanged = event->atom == atom(QXcbAtom::WM_STATE); + const bool netWmStateChanged = event->atom == atom(QXcbAtom::_NET_WM_STATE); + if (netWmStateChanged || wmStateChanged) { + if (wmStateChanged && !m_wmStateValid && m_isWmManagedWindow) { + // ICCCM 4.1.4 + // Clients that want to re-use a client window (e.g. by mapping it again) + // after withdrawing it must wait for the withdrawal to be complete before + // proceeding. The preferred method for doing this is for clients to wait for + // a window manager to update or remove the WM_STATE property. + m_wmStateValid = true; + handleDeferredTasks(); + } + if (event->state == XCB_PROPERTY_DELETE) return; - Qt::WindowStates newState = Qt::WindowNoState; - - if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'. + if (wmStateChanged) { auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), 0, m_window, atom(QXcbAtom::WM_STATE), XCB_ATOM_ANY, 0, 1024); if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) { - const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get()); - if (reply->length != 0) - m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC - || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized)); + auto data = static_cast<const quint32 *>(xcb_get_property_value(reply.get())); + if (reply->length != 0) { + const bool changedToWithdrawn = data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN; + const bool changedToIconic = data[0] == XCB_ICCCM_WM_STATE_ICONIC; + m_minimized = changedToIconic || (changedToWithdrawn && m_minimized); + } } } + // _NET_WM_STATE handling + Qt::WindowStates newState = Qt::WindowNoState; const NetWmStates states = netWmStates(); // _NET_WM_STATE_HIDDEN should be set by the Window Manager to indicate that a window would // not be visible on the screen if its desktop/viewport were active and its coordinates were @@ -2233,7 +2325,6 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev if ((m_windowState & Qt::WindowMinimized) && connection()->mouseGrabber() == this) connection()->setMouseGrabber(nullptr); } - return; } else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) { m_dirtyFrameMargins = true; } diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 6f5c1f5ed9..55af9279b1 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -74,6 +74,14 @@ public: Q_DECLARE_FLAGS(NetWmStates, NetWmState) + enum Task { + Map, + Unmap, + SetGeometry, + SetWindowFlags, + SetWindowState + }; + QXcbWindow(QWindow *window); ~QXcbWindow(); @@ -143,6 +151,9 @@ public: QXcbWindow *toWindow() override; + bool shouldDeferTask(Task task); + void handleDeferredTasks(); + void handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global, Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source); @@ -281,6 +292,11 @@ protected: int m_swapInterval = -1; qreal m_sizeHintsScaleFactor = 1.0; + + bool m_wmStateValid = true; + QVector<Task> m_deferredTasks; + bool m_isWmManagedWindow = true; + QRect m_deferredGeometry; }; class QXcbForeignWindow : public QXcbWindow |