diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbwindow.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 550 |
1 files changed, 176 insertions, 374 deletions
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 59b06d543e..70aab77a51 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -70,6 +70,9 @@ #undef class #include <xcb/xfixes.h> #include <xcb/shape.h> +#if QT_CONFIG(xcb_xinput) +#include <xcb/xinput.h> +#endif // xcb-icccm 3.8 support #ifdef XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS @@ -109,19 +112,12 @@ #undef register #endif -#if QT_CONFIG(xinput2) -#include <X11/extensions/XInput2.h> -#include <X11/extensions/XI2proto.h> -#endif - #define XCOORD_MAX 16383 enum { defaultWindowWidth = 160, defaultWindowHeight = 160 }; -//#ifdef NET_WM_STATE_DEBUG - QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE); @@ -200,11 +196,6 @@ void QXcbWindow::setImageFormatForVisual(const xcb_visualtype_t *visual) } } -static inline bool positionIncludesFrame(QWindow *w) -{ - return qt_window_private(w)->positionPolicy == QWindowPrivate::WindowFrameInclusive; -} - #if QT_CONFIG(xcb_xlib) static inline XTextProperty* qstringToXTP(Display *dpy, const QString& s) { @@ -282,11 +273,7 @@ QXcbWindow::QXcbWindow(QWindow *window) setConnection(xcbScreen()->connection()); } -#ifdef Q_COMPILER_CLASS_ENUM enum : quint32 { -#else -enum { -#endif baseEventMask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE, @@ -308,6 +295,7 @@ void QXcbWindow::create() destroy(); m_windowState = Qt::WindowNoState; + m_trayIconWindow = isTrayIconWindow(window()); Qt::WindowType type = window()->type(); @@ -334,9 +322,6 @@ void QXcbWindow::create() return; } - // Parameters to XCreateWindow() are frame corner + inner size. - // This fits in case position policy is frame inclusive. There is - // currently no way to implement it for frame-exclusive geometries. QPlatformWindow::setGeometry(rect); if (platformScreen != currentScreen) @@ -368,7 +353,9 @@ void QXcbWindow::create() const xcb_visualtype_t *visual = nullptr; - if (connection()->hasDefaultVisualId()) { + if (m_trayIconWindow && connection()->systemTrayTracker()) { + visual = platformScreen->visualForId(connection()->systemTrayTracker()->visualId()); + } else if (connection()->hasDefaultVisualId()) { visual = platformScreen->visualForId(connection()->defaultVisualId()); if (!visual) qWarning() << "Failed to use requested visual id."; @@ -408,9 +395,12 @@ void QXcbWindow::create() | XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK; - static const bool haveOpenGL = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL); + static auto haveOpenGL = []() { + static const bool result = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL); + return result; + }; - if ((window()->supportsOpenGL() && haveOpenGL) || m_format.hasAlpha()) { + if ((window()->supportsOpenGL() && haveOpenGL()) || m_format.hasAlpha()) { m_cmap = xcb_generate_id(xcb_connection()); xcb_create_colormap(xcb_connection(), XCB_COLORMAP_ALLOC_NONE, @@ -448,8 +438,6 @@ void QXcbWindow::create() connection()->addWindowEventListener(m_window, this); - xcb_change_window_attributes(xcb_connection(), m_window, mask, values); - propagateSizeHints(); xcb_atom_t properties[5]; @@ -458,9 +446,7 @@ void QXcbWindow::create() properties[propertyCount++] = atom(QXcbAtom::WM_TAKE_FOCUS); properties[propertyCount++] = atom(QXcbAtom::_NET_WM_PING); - m_usingSyncProtocol = platformScreen->syncRequestSupported(); - - if (m_usingSyncProtocol) + if (connection()->hasXSync()) properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST); if (window()->flags() & Qt::WindowContextHelpButtonHint) @@ -484,7 +470,7 @@ void QXcbWindow::create() XCB_ATOM_STRING, 8, wmClass.size(), wmClass.constData()); } - if (m_usingSyncProtocol) { + if (connection()->hasXSync()) { m_syncCounter = xcb_generate_id(xcb_connection()); xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue); @@ -504,12 +490,17 @@ void QXcbWindow::create() atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32, 1, &pid); + const QByteArray clientMachine = QSysInfo::machineHostName().toLocal8Bit(); + if (!clientMachine.isEmpty()) { + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, + atom(QXcbAtom::WM_CLIENT_MACHINE), XCB_ATOM_STRING, 8, + clientMachine.size(), clientMachine.constData()); + } + + // Create WM_HINTS property on the window, so we can xcb_get_wm_hints*() + // from various setter functions for adjusting the hints. xcb_wm_hints_t hints; memset(&hints, 0, sizeof(hints)); - xcb_wm_hints_set_normal(&hints); - - xcb_wm_hints_set_input(&hints, !(window()->flags() & Qt::WindowDoesNotAcceptFocus)); - xcb_set_wm_hints(xcb_connection(), m_window, &hints); xcb_window_t leader = connection()->clientLeader(); @@ -524,7 +515,7 @@ void QXcbWindow::create() atom(QXcbAtom::_XEMBED_INFO), 32, 2, (void *)data); -#if QT_CONFIG(xinput2) +#if QT_CONFIG(xcb_xinput) if (connection()->hasXInput2()) { if (connection()->xi2MouseEventsDisabled()) connection()->xi2SelectDeviceEventsCompatibility(m_window); @@ -537,9 +528,6 @@ void QXcbWindow::create() setWindowFlags(window()->flags()); setWindowTitle(window()->title()); - if (window()->flags() & Qt::WindowTransparentForInput) - setTransparentForMouseEvents(true); - #if QT_CONFIG(xcb_xlib) // force sync to read outstanding requests - see QTBUG-29106 XSync(static_cast<Display*>(platformScreen->connection()->xlib_display()), false); @@ -562,6 +550,9 @@ void QXcbWindow::create() QByteArray wmWindowRole = window()->property(wm_window_role_property_id).toByteArray(); setWmWindowRole(wmWindowRole); } + + if (m_trayIconWindow) + m_embedded = requestSystemTrayWindowDock(); } QXcbWindow::~QXcbWindow() @@ -587,7 +578,7 @@ void QXcbWindow::destroy() if (connection()->mouseGrabber() == this) connection()->setMouseGrabber(nullptr); - if (m_syncCounter && m_usingSyncProtocol) + if (m_syncCounter && connection()->hasXSync()) xcb_sync_destroy_counter(xcb_connection(), m_syncCounter); if (m_window) { if (m_netWmUserTimeWindow) { @@ -623,25 +614,23 @@ void QXcbWindow::setGeometry(const QRect &rect) if (!newScreen) newScreen = xcbScreen(); - const QRect wmGeometry = windowToWmGeometry(rect); - if (newScreen != currentScreen) QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); if (qt_window_private(window())->positionAutomatic) { const quint32 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; const qint32 values[] = { - qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX), - qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX), + qBound<qint32>(1, rect.width(), XCOORD_MAX), + qBound<qint32>(1, rect.height(), XCOORD_MAX), }; xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)); } else { const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; const qint32 values[] = { - qBound<qint32>(-XCOORD_MAX, wmGeometry.x(), XCOORD_MAX), - qBound<qint32>(-XCOORD_MAX, wmGeometry.y(), XCOORD_MAX), - qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX), - qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX), + qBound<qint32>(-XCOORD_MAX, rect.x(), XCOORD_MAX), + qBound<qint32>(-XCOORD_MAX, rect.y(), XCOORD_MAX), + qBound<qint32>(1, rect.width(), XCOORD_MAX), + qBound<qint32>(1, rect.height(), XCOORD_MAX), }; xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)); if (window()->parent() && !window()->transientParent()) { @@ -739,34 +728,10 @@ void QXcbWindow::setVisible(bool visible) hide(); } -static inline bool testShowWithoutActivating(const QWindow *window) -{ - // QWidget-attribute Qt::WA_ShowWithoutActivating. - const QVariant showWithoutActivating = window->property("_q_showWithoutActivating"); - return showWithoutActivating.isValid() && showWithoutActivating.toBool(); -} - void QXcbWindow::show() { if (window()->isTopLevel()) { - xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window); - - xcb_wm_hints_t hints; - xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL); - - if (window()->windowStates() & Qt::WindowMinimized) - xcb_wm_hints_set_iconic(&hints); - else - xcb_wm_hints_set_normal(&hints); - - xcb_wm_hints_set_input(&hints, !(window()->flags() & Qt::WindowDoesNotAcceptFocus)); - - xcb_set_wm_hints(xcb_connection(), m_window, &hints); - - m_gravity = positionIncludesFrame(window()) ? - XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC; - // update WM_NORMAL_HINTS propagateSizeHints(); @@ -789,19 +754,18 @@ void QXcbWindow::show() if (!transientXcbParent) xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR); - // update _MOTIF_WM_HINTS - updateMotifWmHintsBeforeMap(); - // update _NET_WM_STATE updateNetWmStateBeforeMap(); } - if (testShowWithoutActivating(window())) + // QWidget-attribute Qt::WA_ShowWithoutActivating. + const auto showWithoutActivating = window()->property("_q_showWithoutActivating"); + if (showWithoutActivating.isValid() && showWithoutActivating.toBool()) updateNetWmUserTime(0); else if (connection()->time() != XCB_TIME_CURRENT_TIME) updateNetWmUserTime(connection()->time()); - if (window()->objectName() == QLatin1String("QSystemTrayIconSysWindow")) + if (m_trayIconWindow) return; // defer showing until XEMBED_EMBEDDED_NOTIFY xcb_map_window(xcb_connection(), m_window); @@ -819,7 +783,7 @@ void QXcbWindow::hide() xcb_unmap_window(xcb_connection(), m_window); // send synthetic UnmapNotify event according to icccm 4.1.4 - Q_DECLARE_XCB_EVENT(event, xcb_unmap_notify_event_t); + q_padded_xcb_event<xcb_unmap_notify_event_t> event = {}; event.response_type = XCB_UNMAP_NOTIFY; event.event = xcbScreen()->root(); event.window = m_window; @@ -940,8 +904,8 @@ void QXcbWindow::doFocusOut() struct QtMotifWmHints { quint32 flags, functions, decorations; - qint32 input_mode; - quint32 status; + qint32 input_mode; // unused + quint32 status; // unused }; enum { @@ -963,50 +927,8 @@ enum { MWM_DECOR_MENU = (1L << 4), MWM_DECOR_MINIMIZE = (1L << 5), MWM_DECOR_MAXIMIZE = (1L << 6), - - MWM_HINTS_INPUT_MODE = (1L << 2), - - MWM_INPUT_MODELESS = 0L, - MWM_INPUT_PRIMARY_APPLICATION_MODAL = 1L, - MWM_INPUT_FULL_APPLICATION_MODAL = 3L }; -static QtMotifWmHints getMotifWmHints(QXcbConnection *c, xcb_window_t window) -{ - QtMotifWmHints hints; - - auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, c->xcb_connection(), 0, window, - c->atom(QXcbAtom::_MOTIF_WM_HINTS), c->atom(QXcbAtom::_MOTIF_WM_HINTS), 0, 20); - - if (reply && reply->format == 32 && reply->type == c->atom(QXcbAtom::_MOTIF_WM_HINTS)) { - hints = *((QtMotifWmHints *)xcb_get_property_value(reply.get())); - } else { - hints.flags = 0L; - hints.functions = MWM_FUNC_ALL; - hints.decorations = MWM_DECOR_ALL; - hints.input_mode = 0L; - hints.status = 0L; - } - - return hints; -} - -static void setMotifWmHints(QXcbConnection *c, xcb_window_t window, const QtMotifWmHints &hints) -{ - if (hints.flags != 0l) { - xcb_change_property(c->xcb_connection(), - XCB_PROP_MODE_REPLACE, - window, - c->atom(QXcbAtom::_MOTIF_WM_HINTS), - c->atom(QXcbAtom::_MOTIF_WM_HINTS), - 32, - 5, - &hints); - } else { - xcb_delete_property(c->xcb_connection(), window, c->atom(QXcbAtom::_MOTIF_WM_HINTS)); - } -} - QXcbWindow::NetWmStates QXcbWindow::netWmStates() { NetWmStates result(0); @@ -1035,9 +957,7 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates() if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION))) result |= NetWmStateDemandsAttention; } else { -#ifdef NET_WM_STATE_DEBUG - printf("getting net wm state (%x), empty\n", m_window); -#endif + qCDebug(lcQpaXcb, "getting net wm state (%x), empty\n", m_window); } return result; @@ -1110,22 +1030,18 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags) setWmWindowType(wmWindowTypes, flags); setNetWmStateWindowFlags(flags); - setMotifWindowFlags(flags); + setMotifWmHints(flags); setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput); updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus); } -void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags) +void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags) { Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask)); QtMotifWmHints mwmhints; - mwmhints.flags = 0L; - mwmhints.functions = 0L; - mwmhints.decorations = 0; - mwmhints.input_mode = 0L; - mwmhints.status = 0L; + memset(&mwmhints, 0, sizeof(mwmhints)); if (type != Qt::SplashScreen) { mwmhints.flags |= MWM_HINTS_DECORATIONS; @@ -1183,7 +1099,18 @@ void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags) mwmhints.decorations = 0; } - setMotifWmHints(connection(), m_window, mwmhints); + if (mwmhints.flags) { + xcb_change_property(xcb_connection(), + XCB_PROP_MODE_REPLACE, + m_window, + atom(QXcbAtom::_MOTIF_WM_HINTS), + atom(QXcbAtom::_MOTIF_WM_HINTS), + 32, + 5, + &mwmhints); + } else { + xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_MOTIF_WM_HINTS)); + } } void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two) @@ -1242,64 +1169,18 @@ void QXcbWindow::setWindowState(Qt::WindowStates state) changeNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); } - connection()->sync(); - m_windowState = state; -} - -void QXcbWindow::updateMotifWmHintsBeforeMap() -{ - QtMotifWmHints mwmhints = getMotifWmHints(connection(), m_window); - - if (window()->modality() != Qt::NonModal) { - switch (window()->modality()) { - case Qt::WindowModal: - mwmhints.input_mode = MWM_INPUT_PRIMARY_APPLICATION_MODAL; - break; - case Qt::ApplicationModal: - default: - mwmhints.input_mode = MWM_INPUT_FULL_APPLICATION_MODAL; - break; - } - mwmhints.flags |= MWM_HINTS_INPUT_MODE; - } else { - mwmhints.input_mode = MWM_INPUT_MODELESS; - mwmhints.flags &= ~MWM_HINTS_INPUT_MODE; - } - - if (windowMinimumSize() == windowMaximumSize()) { - // fixed size, remove the resize handle (since mwm/dtwm - // isn't smart enough to do it itself) - mwmhints.flags |= MWM_HINTS_FUNCTIONS; - if (mwmhints.functions == MWM_FUNC_ALL) { - mwmhints.functions = MWM_FUNC_MOVE; - } else { - mwmhints.functions &= ~MWM_FUNC_RESIZE; - } - - if (mwmhints.decorations == MWM_DECOR_ALL) { - mwmhints.flags |= MWM_HINTS_DECORATIONS; - mwmhints.decorations = (MWM_DECOR_BORDER - | MWM_DECOR_TITLE - | MWM_DECOR_MENU); - } else { - mwmhints.decorations &= ~MWM_DECOR_RESIZEH; - } - } - - if (window()->flags() & Qt::WindowMinimizeButtonHint) { - mwmhints.flags |= MWM_HINTS_DECORATIONS; - mwmhints.decorations |= MWM_DECOR_MINIMIZE; - mwmhints.functions |= MWM_FUNC_MINIMIZE; - } - if (window()->flags() & Qt::WindowMaximizeButtonHint) { - mwmhints.flags |= MWM_HINTS_DECORATIONS; - mwmhints.decorations |= MWM_DECOR_MAXIMIZE; - mwmhints.functions |= MWM_FUNC_MAXIMIZE; + xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window); + xcb_wm_hints_t hints; + if (xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, nullptr)) { + if (state & Qt::WindowMinimized) + xcb_wm_hints_set_iconic(&hints); + else + xcb_wm_hints_set_normal(&hints); + xcb_set_wm_hints(xcb_connection(), m_window, &hints); } - if (window()->flags() & Qt::WindowCloseButtonHint) - mwmhints.functions |= MWM_FUNC_CLOSE; - setMotifWmHints(connection(), m_window, mwmhints); + connection()->sync(); + m_windowState = state; } void QXcbWindow::updateNetWmStateBeforeMap() @@ -1364,17 +1245,10 @@ void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp) xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW), XCB_ATOM_WINDOW, 32, 1, &m_netWmUserTimeWindow); xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME)); -#ifndef QT_NO_DEBUG - QByteArray ba("Qt NET_WM user time window"); - xcb_change_property(xcb_connection(), - XCB_PROP_MODE_REPLACE, - m_netWmUserTimeWindow, - atom(QXcbAtom::_NET_WM_NAME), - atom(QXcbAtom::UTF8_STRING), - 8, - ba.length(), - ba.constData()); -#endif + + QXcbWindow::setWindowTitle(connection(), m_netWmUserTimeWindow, + QStringLiteral("Qt NET_WM User Time Window")); + } else if (!isSupportedByWM) { // WM no longer supports it, then we should remove the // _NET_WM_USER_TIME_WINDOW atom. @@ -1421,9 +1295,8 @@ void QXcbWindow::updateDoesNotAcceptFocus(bool doesNotAcceptFocus) xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window); xcb_wm_hints_t hints; - if (!xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL)) { + if (!xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, nullptr)) return; - } xcb_wm_hints_set_input(&hints, !doesNotAcceptFocus); xcb_set_wm_hints(xcb_connection(), m_window, &hints); @@ -1452,24 +1325,7 @@ void QXcbWindow::setParent(const QPlatformWindow *parent) void QXcbWindow::setWindowTitle(const QString &title) { - QString fullTitle = formatWindowTitle(title, QString::fromUtf8(" \xe2\x80\x94 ")); // unicode character U+2014, EM DASH - const QByteArray ba = std::move(fullTitle).toUtf8(); - xcb_change_property(xcb_connection(), - XCB_PROP_MODE_REPLACE, - m_window, - atom(QXcbAtom::_NET_WM_NAME), - atom(QXcbAtom::UTF8_STRING), - 8, - ba.length(), - ba.constData()); - -#if QT_CONFIG(xcb_xlib) - Display *dpy = static_cast<Display *>(connection()->xlib_display()); - XTextProperty *text = qstringToXTP(dpy, title); - if (text) - XSetWMName(dpy, m_window, text); -#endif - xcb_flush(xcb_connection()); + setWindowTitle(connection(), m_window, title); } void QXcbWindow::setWindowIconText(const QString &title) @@ -1541,38 +1397,28 @@ void QXcbWindow::lower() xcb_configure_window(xcb_connection(), m_window, mask, values); } -// Adapt the geometry to match the WM expection with regards -// to gravity. -QRect QXcbWindow::windowToWmGeometry(QRect r) const -{ - if (m_dirtyFrameMargins || m_frameMargins.isNull()) - return r; - const bool frameInclusive = positionIncludesFrame(window()); - // XCB_GRAVITY_STATIC requires the inner geometry, whereas - // XCB_GRAVITY_NORTH_WEST requires the frame geometry - if (frameInclusive && m_gravity == XCB_GRAVITY_STATIC) { - r.translate(m_frameMargins.left(), m_frameMargins.top()); - } else if (!frameInclusive && m_gravity == XCB_GRAVITY_NORTH_WEST) { - r.translate(-m_frameMargins.left(), -m_frameMargins.top()); - } - return r; -} - void QXcbWindow::propagateSizeHints() { // update WM_NORMAL_HINTS xcb_size_hints_t hints; memset(&hints, 0, sizeof(hints)); - const QRect xRect = windowToWmGeometry(geometry()); + const QRect rect = geometry(); + QWindowPrivate *win = qt_window_private(window()); - QWindow *win = window(); + if (!win->positionAutomatic) + xcb_size_hints_set_position(&hints, true, rect.x(), rect.y()); + if (rect.width() < QWINDOWSIZE_MAX || rect.height() < QWINDOWSIZE_MAX) + xcb_size_hints_set_size(&hints, true, rect.width(), rect.height()); - if (!qt_window_private(win)->positionAutomatic) - xcb_size_hints_set_position(&hints, true, xRect.x(), xRect.y()); - if (xRect.width() < QWINDOWSIZE_MAX || xRect.height() < QWINDOWSIZE_MAX) - xcb_size_hints_set_size(&hints, true, xRect.width(), xRect.height()); - xcb_size_hints_set_win_gravity(&hints, m_gravity); + /* Gravity describes how to interpret x and y values the next time + window needs to be positioned on a screen. + XCB_GRAVITY_STATIC : the left top corner of the client window + XCB_GRAVITY_NORTH_WEST : the left top corner of the frame window */ + auto gravity = win->positionPolicy == QWindowPrivate::WindowFrameInclusive + ? XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC; + + xcb_size_hints_set_win_gravity(&hints, gravity); QSize minimumSize = windowMinimumSize(); QSize maximumSize = windowMaximumSize(); @@ -1836,12 +1682,6 @@ void QXcbWindow::setWmWindowRole(const QByteArray &role) role.size(), role.constData()); } -void QXcbWindow::setParentRelativeBackPixmapStatic(QWindow *window) -{ - if (window->handle()) - static_cast<QXcbWindow *>(window->handle())->setParentRelativeBackPixmap(); -} - void QXcbWindow::setParentRelativeBackPixmap() { const quint32 mask = XCB_CW_BACK_PIXMAP; @@ -1849,14 +1689,7 @@ void QXcbWindow::setParentRelativeBackPixmap() xcb_change_window_attributes(xcb_connection(), m_window, mask, values); } -bool QXcbWindow::requestSystemTrayWindowDockStatic(const QWindow *window) -{ - if (window->handle()) - return static_cast<QXcbWindow *>(window->handle())->requestSystemTrayWindowDock(); - return false; -} - -bool QXcbWindow::requestSystemTrayWindowDock() const +bool QXcbWindow::requestSystemTrayWindowDock() { if (!connection()->systemTrayTracker()) return false; @@ -1864,81 +1697,33 @@ bool QXcbWindow::requestSystemTrayWindowDock() const return true; } -QRect QXcbWindow::systemTrayWindowGlobalGeometryStatic(const QWindow *window) +bool QXcbWindow::handleNativeEvent(xcb_generic_event_t *event) { - if (window->handle()) - return static_cast<QXcbWindow *>(window->handle())->systemTrayWindowGlobalGeometry(); - return QRect(); + auto eventType = connection()->nativeInterface()->nativeEventType(); + long result = 0; // Used only by MS Windows + return QWindowSystemInterface::handleNativeEvent(window(), eventType, event, &result); } -QRect QXcbWindow::systemTrayWindowGlobalGeometry() const +void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event) { - if (!connection()->systemTrayTracker()) - return QRect(); - return connection()->systemTrayTracker()->systemTrayWindowGlobalGeometry(m_window); -} + QRect rect(event->x, event->y, event->width, event->height); + m_exposeRegion |= rect; -class ExposeCompressor -{ -public: - ExposeCompressor(xcb_window_t window, QRegion *region) - : m_window(window) - , m_region(region) - , m_pending(true) - { - } + bool pending = true; - bool checkEvent(xcb_generic_event_t *event) - { - if (!event) - return false; - if ((event->response_type & ~0x80) != XCB_EXPOSE) + connection()->eventQueue()->peek(QXcbEventQueue::PeekRemoveMatchContinue, + [this, &pending](xcb_generic_event_t *event, int type) { + if (type != XCB_EXPOSE) return false; - xcb_expose_event_t *expose = (xcb_expose_event_t *)event; + auto expose = reinterpret_cast<xcb_expose_event_t *>(event); if (expose->window != m_window) return false; if (expose->count == 0) - m_pending = false; - *m_region |= QRect(expose->x, expose->y, expose->width, expose->height); + pending = false; + m_exposeRegion |= QRect(expose->x, expose->y, expose->width, expose->height); + free(expose); return true; - } - - bool pending() const - { - return m_pending; - } - -private: - xcb_window_t m_window; - QRegion *m_region; - bool m_pending; -}; - -bool QXcbWindow::compressExposeEvent(QRegion &exposeRegion) -{ - ExposeCompressor compressor(m_window, &exposeRegion); - xcb_generic_event_t *filter = 0; - do { - filter = connection()->checkEvent(compressor); - free(filter); - } while (filter); - return compressor.pending(); -} - -bool QXcbWindow::handleGenericEvent(xcb_generic_event_t *event, long *result) -{ - return QWindowSystemInterface::handleNativeEvent(window(), - connection()->nativeInterface()->genericEventFilterType(), - event, - result); -} - -void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event) -{ - QRect rect(event->x, event->y, event->width, event->height); - - m_exposeRegion |= rect; - bool pending = compressExposeEvent(m_exposeRegion); + }); // if count is non-zero there are more expose events pending if (event->count == 0 || !pending) { @@ -1977,7 +1762,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even connection()->setTime(event->data.data32[1]); m_syncValue.lo = event->data.data32[2]; m_syncValue.hi = event->data.data32[3]; - if (m_usingSyncProtocol) + if (connection()->hasXSync()) m_syncState = SyncReceived; #ifndef QT_NO_WHATSTHIS } else if (protocolAtom == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) { @@ -2052,7 +1837,7 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * } m_oldWindowSize = actualGeometry.size(); - if (m_usingSyncProtocol && m_syncState == SyncReceived) + if (connection()->hasXSync() && m_syncState == SyncReceived) m_syncState = SyncAndConfigureReceived; m_dirtyFrameMargins = true; @@ -2137,7 +1922,7 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in updateNetWmUserTime(timestamp); - if (m_embedded) { + if (m_embedded && !m_trayIconWindow) { if (window() != QGuiApplication::focusWindow()) { const QXcbWindow *container = static_cast<const QXcbWindow *>(parent()); Q_ASSERT(container != 0); @@ -2149,7 +1934,7 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in QPoint global(root_x, root_y); if (isWheel) { -#if QT_CONFIG(xinput2) +#if QT_CONFIG(xcb_xinput) if (!connection()->isAtLeastXI21()) { #endif QPoint angleDelta; @@ -2164,7 +1949,7 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in if (modifiers & Qt::AltModifier) std::swap(angleDelta.rx(), angleDelta.ry()); QWindowSystemInterface::handleWheelEvent(window(), timestamp, local, global, QPoint(), angleDelta, modifiers); -#if QT_CONFIG(xinput2) +#if QT_CONFIG(xcb_xinput) } #endif return; @@ -2204,7 +1989,7 @@ static inline bool doCheckUnGrabAncestor(QXcbConnection *conn) if (conn) { const bool mouseButtonsPressed = (conn->buttonState() != Qt::NoButton); -#if QT_CONFIG(xinput2) +#if QT_CONFIG(xcb_xinput) return mouseButtonsPressed || (conn->hasXInput2() && !conn->xi2MouseEventsDisabled()); #else return mouseButtonsPressed; @@ -2231,24 +2016,6 @@ static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL); } -class EnterEventChecker -{ -public: - bool checkEvent(xcb_generic_event_t *event) - { - if (!event) - return false; - if ((event->response_type & ~0x80) != XCB_ENTER_NOTIFY) - return false; - - xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)event; - if (ignoreEnterEvent(enter->mode, enter->detail)) - return false; - - return true; - } -}; - void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, int root_y, quint8 mode, quint8 detail, xcb_timestamp_t timestamp) { @@ -2258,7 +2025,7 @@ void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, in if (ignoreEnterEvent(mode, detail, connection()) || connection()->mousePressWindow()) return; -#ifdef XCB_USE_XINPUT21 +#if QT_CONFIG(xcb_xinput) // Updates scroll valuators, as user might have done some scrolling outside our X client. connection()->xi2UpdateScrollingDevices(); #endif @@ -2275,9 +2042,15 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y, if (ignoreLeaveEvent(mode, detail, connection()) || connection()->mousePressWindow()) return; - EnterEventChecker checker; - xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)connection()->checkEvent(checker); - QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : 0; + // check if enter event is buffered + auto event = connection()->eventQueue()->peek([](xcb_generic_event_t *event, int type) { + if (type != XCB_ENTER_NOTIFY) + return false; + auto enter = reinterpret_cast<xcb_enter_notify_event_t *>(event); + return !ignoreEnterEvent(enter->mode, enter->detail); + }); + auto enter = reinterpret_cast<xcb_enter_notify_event_t *>(event); + QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : nullptr; if (enterWindow) { QPoint local(enter->event_x, enter->event_y); @@ -2330,16 +2103,18 @@ void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event) event->time, QEvent::MouseMove); } -#if QT_CONFIG(xinput2) -static inline int fixed1616ToInt(FP1616 val) +#if QT_CONFIG(xcb_xinput) +static inline int fixed1616ToInt(xcb_input_fp1616_t val) { return int(qreal(val) / 0x10000); } +#define qt_xcb_mask_is_set(ptr, event) (((unsigned char*)(ptr))[(event)>>3] & (1 << ((event) & 7))) + void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource source) { QXcbConnection *conn = connection(); - xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event); + auto *ev = reinterpret_cast<xcb_input_button_press_event_t *>(event); if (ev->buttons_len > 0) { unsigned char *buttonMask = (unsigned char *) &ev[1]; @@ -2347,16 +2122,16 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource // XIPointerEmulated being set: https://bugs.freedesktop.org/show_bug.cgi?id=98188 // Filter them out by other attributes: when their source device is a touch screen // and the LMB is pressed. - if (XIMaskIsSet(buttonMask, 1) && conn->isTouchScreen(ev->sourceid)) { + if (qt_xcb_mask_is_set(buttonMask, 1) && conn->isTouchScreen(ev->sourceid)) { if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) qCDebug(lcQpaXInput, "XI2 mouse event from touch device %d was ignored", ev->sourceid); return; } for (int i = 1; i <= 15; ++i) - conn->setButtonState(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i)); + conn->setButtonState(conn->translateMouseButton(i), qt_xcb_mask_is_set(buttonMask, i)); } - const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective_mods); + const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective); const int event_x = fixed1616ToInt(ev->event_x); const int event_y = fixed1616ToInt(ev->event_y); const int root_x = fixed1616ToInt(ev->root_x); @@ -2373,47 +2148,47 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource sourceName = me.valueToKey(source); } - switch (ev->evtype) { - case XI_ButtonPress: + switch (ev->event_type) { + case XCB_INPUT_BUTTON_PRESS: if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) qCDebug(lcQpaXInputEvents, "XI2 mouse press, button %d, time %d, source %s", button, ev->time, sourceName); conn->setButtonState(button, true); handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonPress, source); break; - case XI_ButtonRelease: + case XCB_INPUT_BUTTON_RELEASE: if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) qCDebug(lcQpaXInputEvents, "XI2 mouse release, button %d, time %d, source %s", button, ev->time, sourceName); conn->setButtonState(button, false); handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonRelease, source); break; - case XI_Motion: + case XCB_INPUT_MOTION: if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) qCDebug(lcQpaXInputEvents, "XI2 mouse motion %d,%d, time %d, source %s", event_x, event_y, ev->time, sourceName); handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time, QEvent::MouseMove, source); break; default: - qWarning() << "Unrecognized XI2 mouse event" << ev->evtype; + qWarning() << "Unrecognized XI2 mouse event" << ev->event_type; break; } } void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event) { - xXIEnterEvent *ev = reinterpret_cast<xXIEnterEvent *>(event); + auto *ev = reinterpret_cast<xcb_input_enter_event_t *>(event); // Compare the window with current mouse grabber to prevent deliver events to any other windows. // If leave event occurs and the window is under mouse - allow to deliver the leave event. QXcbWindow *mouseGrabber = connection()->mouseGrabber(); if (mouseGrabber && mouseGrabber != this - && (ev->evtype != XI_Leave || QGuiApplicationPrivate::currentMouseWindow != window())) { + && (ev->event_type != XCB_INPUT_LEAVE || QGuiApplicationPrivate::currentMouseWindow != window())) { return; } const int root_x = fixed1616ToInt(ev->root_x); const int root_y = fixed1616ToInt(ev->root_y); - switch (ev->evtype) { - case XI_Enter: { + switch (ev->event_type) { + case XCB_INPUT_ENTER: { const int event_x = fixed1616ToInt(ev->event_x); const int event_y = fixed1616ToInt(ev->event_y); qCDebug(lcQpaXInputEvents, "XI2 mouse enter %d,%d, mode %d, detail %d, time %d", @@ -2421,7 +2196,7 @@ void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event) handleEnterNotifyEvent(event_x, event_y, root_x, root_y, ev->mode, ev->detail, ev->time); break; } - case XI_Leave: + case XCB_INPUT_LEAVE: qCDebug(lcQpaXInputEvents, "XI2 mouse leave, mode %d, detail %d, time %d", ev->mode, ev->detail, ev->time); connection()->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group); @@ -2523,7 +2298,7 @@ void QXcbWindow::updateSyncRequestCounter() // window manager does not expect a sync event yet. return; } - if (m_usingSyncProtocol && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) { + if (connection()->hasXSync() && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) { xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue); xcb_flush(xcb_connection()); @@ -2563,7 +2338,7 @@ bool QXcbWindow::setMouseGrabEnabled(bool grab) if (grab && !connection()->canGrab()) return false; -#if QT_CONFIG(xinput2) +#if QT_CONFIG(xcb_xinput) if (connection()->hasXInput2() && !connection()->xi2MouseEventsDisabled()) { bool result = connection()->xi2SetMouseGrabEnabled(m_window, grab); if (grab && result) @@ -2591,11 +2366,11 @@ bool QXcbWindow::setMouseGrabEnabled(bool grab) return result; } -void QXcbWindow::windowEvent(QEvent *event) +bool QXcbWindow::windowEvent(QEvent *event) { switch (event->type()) { case QEvent::FocusIn: - if (m_embedded && !event->spontaneous()) { + if (m_embedded && !m_trayIconWindow && !event->spontaneous()) { QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event); switch (focusEvent->reason()) { case Qt::TabFocusReason: @@ -2617,7 +2392,7 @@ void QXcbWindow::windowEvent(QEvent *event) default: break; } - QPlatformWindow::windowEvent(event); + return QPlatformWindow::windowEvent(event); } bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) @@ -2638,7 +2413,7 @@ bool QXcbWindow::startSystemMoveResize(const QPoint &pos, int corner) return false; const QPoint globalPos = QHighDpi::toNativePixels(window()->mapToGlobal(pos), window()->screen()); -#ifdef XCB_USE_XINPUT22 +#if QT_CONFIG(xcb_xinput) // ### FIXME QTBUG-53389 bool startedByTouch = connection()->startSystemMoveResizeForTouchBegin(m_window, globalPos, corner); if (startedByTouch) { @@ -2659,6 +2434,7 @@ bool QXcbWindow::startSystemMoveResize(const QPoint &pos, int corner) return true; } + void QXcbWindow::doStartSystemMoveResize(const QPoint &globalPos, int corner) { const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE); @@ -2728,11 +2504,6 @@ void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event) case XEMBED_EMBEDDED_NOTIFY: xcb_map_window(xcb_connection(), m_window); xcbScreen()->windowShown(this); - // Without Qt::WA_TranslucentBackground, we use a ParentRelative BackPixmap. - // Clear the whole tray icon window to its background color as early as possible - // so that we can get a clean result from grabWindow() later. - xcb_clear_area(xcb_connection(), false, m_window, 0, 0, geometry().width(), geometry().height()); - xcb_flush(xcb_connection()); break; case XEMBED_FOCUS_IN: Qt::FocusReason reason; @@ -2846,6 +2617,28 @@ QXcbScreen *QXcbWindow::xcbScreen() const return static_cast<QXcbScreen *>(screen()); } +void QXcbWindow::setWindowTitle(const QXcbConnection *conn, xcb_window_t window, const QString &title) +{ + QString fullTitle = formatWindowTitle(title, QString::fromUtf8(" \xe2\x80\x94 ")); // unicode character U+2014, EM DASH + const QByteArray ba = std::move(fullTitle).toUtf8(); + xcb_change_property(conn->xcb_connection(), + XCB_PROP_MODE_REPLACE, + window, + conn->atom(QXcbAtom::_NET_WM_NAME), + conn->atom(QXcbAtom::UTF8_STRING), + 8, + ba.length(), + ba.constData()); + +#if QT_CONFIG(xcb_xlib) + Display *dpy = static_cast<Display *>(conn->xlib_display()); + XTextProperty *text = qstringToXTP(dpy, title); + if (text) + XSetWMName(dpy, window, text); +#endif + xcb_flush(conn->xcb_connection()); +} + QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window) { const xcb_atom_t utf8Atom = conn->atom(QXcbAtom::UTF8_STRING); @@ -2856,6 +2649,15 @@ QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window) const char *name = reinterpret_cast<const char *>(xcb_get_property_value(reply.get())); return QString::fromUtf8(name, xcb_get_property_value_length(reply.get())); } + + reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(), + false, window, conn->atom(QXcbAtom::WM_NAME), + XCB_ATOM_STRING, 0, 1024); + if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) { + const char *name = reinterpret_cast<const char *>(xcb_get_property_value(reply.get())); + return QString::fromLatin1(name, xcb_get_property_value_length(reply.get())); + } + return QString(); } |