diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbconnection.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 1397 |
1 files changed, 127 insertions, 1270 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 4e24c970b4..9e857ea2ff 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -38,12 +38,10 @@ ****************************************************************************/ #include <QtGui/private/qguiapplication_p.h> -#include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/QDebug> #include "qxcbconnection.h" #include "qxcbkeyboard.h" -#include "qxcbscreen.h" #include "qxcbwindow.h" #include "qxcbclipboard.h" #if QT_CONFIG(draganddrop) @@ -57,39 +55,25 @@ #include "qxcbglintegration.h" #include "qxcbcursor.h" #include "qxcbbackingstore.h" +#include "qxcbeventqueue.h" -#include <QSocketNotifier> #include <QAbstractEventDispatcher> -#include <QTimer> #include <QByteArray> #include <QScopedPointer> -#include <algorithm> - #include <stdio.h> #include <errno.h> -#include <xcb/shm.h> -#include <xcb/sync.h> + #include <xcb/xfixes.h> -#include <xcb/xinerama.h> - -#if QT_CONFIG(xcb_xlib) -#define register /* C++17 deprecated register */ -#include <X11/Xlib.h> -#include <X11/Xlib-xcb.h> -#include <X11/Xlibint.h> -#include <X11/Xutil.h> -#undef register +#if QT_CONFIG(xkb) +#define explicit dont_use_cxx_explicit +#include <xcb/xkb.h> +#undef explicit #endif - #if QT_CONFIG(xcb_xinput) #include <xcb/xinput.h> #endif -#if QT_CONFIG(xcb_render) -#include <xcb/render.h> -#endif - QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input") @@ -97,7 +81,7 @@ Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices") Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events") Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events") -Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb") // for general (uncategorized) XCB logging +Q_LOGGING_CATEGORY(lcQpaEventReader, "qt.qpa.events.reader") Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker") Q_LOGGING_CATEGORY(lcQpaKeyboard, "qt.qpa.xkeyboard") Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd") @@ -108,519 +92,27 @@ Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd") #define XCB_GE_GENERIC 35 #endif -#if QT_CONFIG(xcb_xinput) -// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: -// - "pad0" became "extension" -// - "pad1" and "pad" became "pad0" -// New and old version of this struct share the following fields: -typedef struct qt_xcb_ge_event_t { - uint8_t response_type; - uint8_t extension; - uint16_t sequence; - uint32_t length; - uint16_t event_type; -} qt_xcb_ge_event_t; - -static inline bool isXIEvent(xcb_generic_event_t *event, int opCode) -{ - qt_xcb_ge_event_t *e = reinterpret_cast<qt_xcb_ge_event_t *>(event); - return e->extension == opCode; -} -#endif // QT_CONFIG(xcb_xinput) - -#if QT_CONFIG(xcb_xlib) -static const char * const xcbConnectionErrors[] = { - "No error", /* Error 0 */ - "I/O error", /* XCB_CONN_ERROR */ - "Unsupported extension used", /* XCB_CONN_CLOSED_EXT_NOTSUPPORTED */ - "Out of memory", /* XCB_CONN_CLOSED_MEM_INSUFFICIENT */ - "Maximum allowed requested length exceeded", /* XCB_CONN_CLOSED_REQ_LEN_EXCEED */ - "Failed to parse display string", /* XCB_CONN_CLOSED_PARSE_ERR */ - "No such screen on display", /* XCB_CONN_CLOSED_INVALID_SCREEN */ - "Error during FD passing" /* XCB_CONN_CLOSED_FDPASSING_FAILED */ -}; - -static int nullErrorHandler(Display *dpy, XErrorEvent *err) -{ -#ifndef Q_XCB_DEBUG - Q_UNUSED(dpy); - Q_UNUSED(err); -#else - const int buflen = 1024; - char buf[buflen]; - - XGetErrorText(dpy, err->error_code, buf, buflen); - fprintf(stderr, "X Error: serial %lu error %d %s\n", err->serial, (int) err->error_code, buf); -#endif - return 0; -} - -static int ioErrorHandler(Display *dpy) -{ - xcb_connection_t *conn = XGetXCBConnection(dpy); - if (conn != NULL) { - /* Print a message with a textual description of the error */ - int code = xcb_connection_has_error(conn); - const char *str = "Unknown error"; - int arrayLength = sizeof(xcbConnectionErrors) / sizeof(xcbConnectionErrors[0]); - if (code >= 0 && code < arrayLength) - str = xcbConnectionErrors[code]; - - qWarning("The X11 connection broke: %s (code %d)", str, code); - } - return _XDefaultIOError(dpy); -} -#endif - -QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const -{ - for (QXcbScreen *screen : m_screens) { - if (screen->root() == rootWindow && screen->crtc() == crtc) - return screen; - } - - return 0; -} - -QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const -{ - for (QXcbScreen *screen : m_screens) { - if (screen->root() == rootWindow && screen->output() == output) - return screen; - } - - return 0; -} - -QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow) const -{ - for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) { - if (virtualDesktop->screen()->root == rootWindow) - return virtualDesktop; - } - - return 0; -} - -/*! - \brief Synchronizes the screen list, adds new screens, removes deleted ones -*/ -void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) -{ - if (event->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) { - xcb_randr_crtc_change_t crtc = event->u.cc; - QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(crtc.window); - if (!virtualDesktop) - // Not for us - return; - - QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc); - qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc - << "mode" << crtc.mode << "relevant screen" << screen; - // Only update geometry when there's a valid mode on the CRTC - // CRTC with node mode could mean that output has been disabled, and we'll - // get RRNotifyOutputChange notification for that. - if (screen && crtc.mode) { - if (crtc.rotation == XCB_RANDR_ROTATION_ROTATE_90 || - crtc.rotation == XCB_RANDR_ROTATION_ROTATE_270) - std::swap(crtc.width, crtc.height); - screen->updateGeometry(QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.rotation); - if (screen->mode() != crtc.mode) - screen->updateRefreshRate(crtc.mode); - } - - } else if (event->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) { - xcb_randr_output_change_t output = event->u.oc; - QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(output.window); - if (!virtualDesktop) - // Not for us - return; - - QXcbScreen *screen = findScreenForOutput(output.window, output.output); - qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_OUTPUT_CHANGE:" << output.output; - - if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) { - qCDebug(lcQpaScreen) << "screen" << screen->name() << "has been disconnected"; - destroyScreen(screen); - } else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) { - // New XRandR output is available and it's enabled - if (output.crtc != XCB_NONE && output.mode != XCB_NONE) { - auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(), - output.output, output.config_timestamp); - // Find a fake screen - const auto scrs = virtualDesktop->screens(); - for (QPlatformScreen *scr : scrs) { - QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(scr); - if (xcbScreen->output() == XCB_NONE) { - screen = xcbScreen; - break; - } - } - - if (screen) { - QString nameWas = screen->name(); - // Transform the fake screen into a physical screen - screen->setOutput(output.output, outputInfo.get()); - updateScreen(screen, output); - qCDebug(lcQpaScreen) << "output" << screen->name() - << "is connected and enabled; was fake:" << nameWas; - } else { - screen = createScreen(virtualDesktop, output, outputInfo.get()); - qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; - } - QHighDpiScaling::updateHighDpiScaling(); - } - } else if (screen) { - if (output.crtc == XCB_NONE && output.mode == XCB_NONE) { - // Screen has been disabled - auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(), - output.output, output.config_timestamp); - if (outputInfo->crtc == XCB_NONE) { - qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled"; - destroyScreen(screen); - } else { - qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch"; - // Reset crtc to skip RRCrtcChangeNotify events, - // because they may be invalid in the middle of the mode switch - screen->setCrtc(XCB_NONE); - } - } else { - updateScreen(screen, output); - qCDebug(lcQpaScreen) << "output has changed" << screen; - } - } - - qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name(); - } -} - -bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output) -{ - auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), rootWindow); - if (!primary) - qWarning("failed to get the primary output of the screen"); - - const bool isPrimary = primary ? (primary->output == output) : false; - - return isPrimary; -} - -void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange) -{ - screen->setCrtc(outputChange.crtc); // Set the new crtc, because it can be invalid - screen->updateGeometry(outputChange.config_timestamp); - if (screen->mode() != outputChange.mode) - screen->updateRefreshRate(outputChange.mode); - // Only screen which belongs to the primary virtual desktop can be a primary screen - if (screen->screenNumber() == m_primaryScreenNumber) { - if (!screen->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) { - screen->setPrimary(true); - - // If the screen became primary, reshuffle the order in QGuiApplicationPrivate - const int idx = m_screens.indexOf(screen); - if (idx > 0) { - qAsConst(m_screens).first()->setPrimary(false); - m_screens.swap(0, idx); - } - screen->virtualDesktop()->setPrimaryScreen(screen); - QXcbIntegration::instance()->setPrimaryScreen(screen); - } - } -} - -QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop, - const xcb_randr_output_change_t &outputChange, - xcb_randr_get_output_info_reply_t *outputInfo) -{ - QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputChange.output, outputInfo); - // Only screen which belongs to the primary virtual desktop can be a primary screen - if (screen->screenNumber() == m_primaryScreenNumber) - screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output)); - - if (screen->isPrimary()) { - if (!m_screens.isEmpty()) - qAsConst(m_screens).first()->setPrimary(false); - - m_screens.prepend(screen); - } else { - m_screens.append(screen); - } - virtualDesktop->addScreen(screen); - QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); - - return screen; -} - -void QXcbConnection::destroyScreen(QXcbScreen *screen) -{ - QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop(); - if (virtualDesktop->screens().count() == 1) { - // If there are no other screens on the same virtual desktop, - // then transform the physical screen into a fake screen. - const QString nameWas = screen->name(); - screen->setOutput(XCB_NONE, nullptr); - qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen; - } else { - // There is more than one screen on the same virtual desktop, remove the screen - m_screens.removeOne(screen); - virtualDesktop->removeScreen(screen); - - // When primary screen is removed, set the new primary screen - // which belongs to the primary virtual desktop. - if (screen->isPrimary()) { - QXcbScreen *newPrimary = static_cast<QXcbScreen *>(virtualDesktop->screens().at(0)); - newPrimary->setPrimary(true); - const int idx = m_screens.indexOf(newPrimary); - if (idx > 0) - m_screens.swap(0, idx); - QXcbIntegration::instance()->setPrimaryScreen(newPrimary); - } - - QXcbIntegration::instance()->destroyScreen(screen); - } -} - -void QXcbConnection::initializeScreens() -{ - xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); - int xcbScreenNumber = 0; // screen number in the xcb sense - QXcbScreen *primaryScreen = nullptr; - while (it.rem) { - // Each "screen" in xcb terminology is a virtual desktop, - // potentially a collection of separate juxtaposed monitors. - // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.) - // which will become virtual siblings. - xcb_screen_t *xcbScreen = it.data; - QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber); - m_virtualDesktops.append(virtualDesktop); - QList<QPlatformScreen *> siblings; - if (has_randr_extension) { - // RRGetScreenResourcesCurrent is fast but it may return nothing if the - // configuration is not initialized wrt to the hardware. We should call - // RRGetScreenResources in this case. - auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current, - xcb_connection(), xcbScreen->root); - if (!resources_current) { - qWarning("failed to get the current screen resources"); - } else { - xcb_timestamp_t timestamp = 0; - xcb_randr_output_t *outputs = nullptr; - int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get()); - if (outputCount) { - timestamp = resources_current->config_timestamp; - outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get()); - } else { - auto resources = Q_XCB_REPLY(xcb_randr_get_screen_resources, - xcb_connection(), xcbScreen->root); - if (!resources) { - qWarning("failed to get the screen resources"); - } else { - timestamp = resources->config_timestamp; - outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get()); - outputs = xcb_randr_get_screen_resources_outputs(resources.get()); - } - } - - if (outputCount) { - auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root); - if (!primary) { - qWarning("failed to get the primary output of the screen"); - } else { - for (int i = 0; i < outputCount; i++) { - auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info, - xcb_connection(), outputs[i], timestamp); - // Invalid, disconnected or disabled output - if (!output) - continue; - - if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) { - qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable( - QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()), - xcb_randr_get_output_info_name_length(output.get())))); - continue; - } - - if (output->crtc == XCB_NONE) { - qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable( - QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()), - xcb_randr_get_output_info_name_length(output.get())))); - continue; - } - - QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get()); - siblings << screen; - m_screens << screen; - - // There can be multiple outputs per screen, use either - // the first or an exact match. An exact match isn't - // always available if primary->output is XCB_NONE - // or currently disconnected output. - if (m_primaryScreenNumber == xcbScreenNumber) { - if (!primaryScreen || (primary && outputs[i] == primary->output)) { - if (primaryScreen) - primaryScreen->setPrimary(false); - primaryScreen = screen; - primaryScreen->setPrimary(true); - siblings.prepend(siblings.takeLast()); - } - } - } - } - } - } - } else if (has_xinerama_extension) { - // Xinerama is available - auto screens = Q_XCB_REPLY(xcb_xinerama_query_screens, m_connection); - if (screens) { - xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens.get()); - while (it.rem) { - xcb_xinerama_screen_info_t *screen_info = it.data; - QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, - XCB_NONE, nullptr, - screen_info, it.index); - siblings << screen; - m_screens << screen; - xcb_xinerama_screen_info_next(&it); - } - } - } - if (siblings.isEmpty()) { - // If there are no XRandR outputs or XRandR extension is missing, - // then create a fake/legacy screen. - QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr); - qCDebug(lcQpaScreen) << "created fake screen" << screen; - m_screens << screen; - if (m_primaryScreenNumber == xcbScreenNumber) { - primaryScreen = screen; - primaryScreen->setPrimary(true); - } - siblings << screen; - } - virtualDesktop->setScreens(siblings); - xcb_screen_next(&it); - ++xcbScreenNumber; - } // for each xcb screen - - for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops)) - virtualDesktop->subscribeToXFixesSelectionNotify(); - - if (m_virtualDesktops.isEmpty()) { - qFatal("QXcbConnection: no screens available"); - } else { - // Ensure the primary screen is first on the list - if (primaryScreen) { - if (qAsConst(m_screens).first() != primaryScreen) { - m_screens.removeOne(primaryScreen); - m_screens.prepend(primaryScreen); - } - } - - // Push the screens to QGuiApplication - for (QXcbScreen *screen : qAsConst(m_screens)) { - qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; - QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); - } - - qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name(); - } -} - -QXcbConnection *QXcbConnection::create(QXcbNativeInterface *nativeInterface, bool canGrabServer, - xcb_visualid_t defaultVisualId, - const char *displayNameIn) -{ - const QByteArray displayName = displayNameIn ? QByteArray(displayNameIn) : qgetenv("DISPLAY"); - int primaryScreenNumber = 0; - void *xlibDisplay = nullptr; - xcb_connection_t *connection = nullptr; -#if QT_CONFIG(xcb_xlib) - Display *dpy = XOpenDisplay(displayName.constData()); - if (dpy) { - primaryScreenNumber = DefaultScreen(dpy); - connection = XGetXCBConnection(dpy); - XSetEventQueueOwner(dpy, XCBOwnsEventQueue); - XSetErrorHandler(nullErrorHandler); - XSetIOErrorHandler(ioErrorHandler); - xlibDisplay = dpy; - } -#else - connection = xcb_connect(displayName.constData(), &primaryScreenNumber); -#endif // QT_CONFIG(xcb_xlib) - if (Q_UNLIKELY(connection == nullptr)) { - qWarning("QXcbConnection: Could not connect to display \"%s\"", displayName.constData()); - return nullptr; - } - if (Q_UNLIKELY(xcb_connection_has_error(connection))) { -#if QT_CONFIG(xcb_xlib) - XCloseDisplay(static_cast<Display *>(xlibDisplay)); -#else - xcb_disconnect(connection); -#endif - qWarning("QXcbConnection: Errors occurred connecting to display \"%s\"", displayName.constData()); - return nullptr; - } - return new QXcbConnection(connection, primaryScreenNumber, nativeInterface, - canGrabServer, defaultVisualId, displayName, xlibDisplay); -} - - -QXcbConnection::QXcbConnection(xcb_connection_t *c, int primaryScreenNumber, - QXcbNativeInterface *nativeInterface, bool canGrabServer, - xcb_visualid_t defaultVisualId, const QByteArray &displayName, - void *xlibDisplay) - : m_connection(c) +QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName) + : QXcbBasicConnection(displayName) , m_canGrabServer(canGrabServer) , m_defaultVisualId(defaultVisualId) - , m_primaryScreenNumber(primaryScreenNumber) - , m_displayName(displayName) , m_nativeInterface(nativeInterface) -#if QT_CONFIG(xcb_xlib) - , m_xlib_display(xlibDisplay) -#endif { - m_reader = new QXcbEventReader(this); - m_reader->start(); - - xcb_extension_t *extensions[] = { - &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id, -#if QT_CONFIG(xkb) - &xcb_xkb_id, -#endif -#if QT_CONFIG(xcb_render) - &xcb_render_id, -#endif -#if QT_CONFIG(xcb_xinput) - &xcb_input_id, -#endif - 0 - }; - - for (xcb_extension_t **ext_it = extensions; *ext_it; ++ext_it) - xcb_prefetch_extension_data (m_connection, *ext_it); + if (!isConnected()) + return; - m_setup = xcb_get_setup(xcb_connection()); + m_eventQueue = new QXcbEventQueue(this); m_xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP").toLower(); - initializeAllAtoms(); - - initializeXSync(); - if (!qEnvironmentVariableIsSet("QT_XCB_NO_MITSHM")) - initializeShm(); - if (!qEnvironmentVariableIsSet("QT_XCB_NO_XRANDR")) - initializeXRandr(); - if (!has_randr_extension) - initializeXinerama(); - initializeXFixes(); initializeScreens(); - initializeXRender(); #if QT_CONFIG(xcb_xinput) - if (!qEnvironmentVariableIsSet("QT_XCB_NO_XI2")) - initializeXInput2(); + if (hasXInput2()) { + xi2SetupDevices(); + xi2SelectStateEvents(); + } #endif - initializeXShape(); - initializeXKB(); m_wmSupport.reset(new QXcbWMSupport(this)); m_keyboard = new QXcbKeyboard(this); @@ -646,12 +138,8 @@ QXcbConnection::~QXcbConnection() #if QT_CONFIG(draganddrop) delete m_drag; #endif - if (m_reader && m_reader->isRunning()) { - sendConnectionEvent(QXcbAtom::_QT_CLOSE_CONNECTION); - m_reader->wait(); - } - - delete m_reader; + if (m_eventQueue) + delete m_eventQueue; QXcbIntegration *integration = QXcbIntegration::instance(); // Delete screens in reverse order to avoid crash in case of multiple screens @@ -663,22 +151,9 @@ QXcbConnection::~QXcbConnection() delete m_glIntegration; - if (isConnected()) { -#if QT_CONFIG(xcb_xlib) - XCloseDisplay(static_cast<Display *>(m_xlib_display)); -#else - xcb_disconnect(xcb_connection()); -#endif - } - delete m_keyboard; } -bool QXcbConnection::isConnected() const -{ - return m_connection && !xcb_connection_has_error(m_connection); -} - QXcbScreen *QXcbConnection::primaryScreen() const { if (!m_screens.isEmpty()) { @@ -783,17 +258,17 @@ void QXcbConnection::printXcbEvent(const QLoggingCategory &log, const char *mess CASE_PRINT_AND_RETURN( XCB_GE_GENERIC ); } // XFixes - if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) + if (isXFixesType(response_type, XCB_XFIXES_SELECTION_NOTIFY)) PRINT_AND_RETURN("XCB_XFIXES_SELECTION_NOTIFY"); + // XRandR - if (has_randr_extension) { - if (response_type == xrandr_first_event + XCB_RANDR_NOTIFY) - PRINT_AND_RETURN("XCB_RANDR_NOTIFY"); - if (response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) - PRINT_AND_RETURN("XCB_RANDR_SCREEN_CHANGE_NOTIFY"); - } + if (isXRandrType(response_type, XCB_RANDR_NOTIFY)) + PRINT_AND_RETURN("XCB_RANDR_NOTIFY"); + if (isXRandrType(response_type, XCB_RANDR_SCREEN_CHANGE_NOTIFY)) + PRINT_AND_RETURN("XCB_RANDR_SCREEN_CHANGE_NOTIFY"); + // XKB - if (response_type == xkb_first_event) + if (isXkbType(response_type)) PRINT_AND_RETURN("XCB_XKB_* event"); // UNKNOWN @@ -1051,11 +526,10 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) printXcbEvent(lcQpaEvents(), "Event", event); long result = 0; // Used only by MS Windows - QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(); - bool handledByNativeEventFilter = dispatcher && dispatcher->filterNativeEvent( - m_nativeInterface->nativeEventType(), event, &result); - if (handledByNativeEventFilter) - return; + if (QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance()) { + if (dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), event, &result)) + return; + } uint response_type = event->response_type & ~0x80; @@ -1187,7 +661,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) #if QT_CONFIG(xcb_xinput) case XCB_GE_GENERIC: // Here the windowEventListener is invoked from xi2HandleEvent() - if (hasXInput2() && isXIEvent(event, m_xiOpCode)) + if (hasXInput2() && isXIEvent(event)) xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event)); break; #endif @@ -1200,7 +674,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) return; handled = true; - if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) { + if (isXFixesType(response_type, XCB_XFIXES_SELECTION_NOTIFY)) { auto notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(event); setTime(notify_event->timestamp); #ifndef QT_NO_CLIPBOARD @@ -1208,14 +682,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) #endif for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops)) virtualDesktop->handleXFixesSelectionNotify(notify_event); - } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) { + } else if (isXRandrType(response_type, XCB_RANDR_NOTIFY)) { updateScreens(reinterpret_cast<xcb_randr_notify_event_t *>(event)); - } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { + } else if (isXRandrType(response_type, XCB_RANDR_SCREEN_CHANGE_NOTIFY)) { auto change_event = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(event); if (auto virtualDesktop = virtualDesktopForRootWindow(change_event->root)) virtualDesktop->handleScreenChange(change_event); #if QT_CONFIG(xkb) - } else if (response_type == xkb_first_event) { // https://bugs.freedesktop.org/show_bug.cgi?id=51295 + } else if (isXkbType(response_type)) { auto xkb_event = reinterpret_cast<_xkb_event *>(event); if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) { switch (xkb_event->any.xkbType) { @@ -1254,151 +728,6 @@ void QXcbConnection::addPeekFunc(PeekFunc f) m_peekFuncs.append(f); } -qint32 QXcbConnection::generatePeekerId() -{ - qint32 peekerId = m_peekerIdSource++; - m_peekerToCachedIndex.insert(peekerId, 0); - return peekerId; -} - -bool QXcbConnection::removePeekerId(qint32 peekerId) -{ - if (!m_peekerToCachedIndex.contains(peekerId)) { - qCWarning(lcQpaXcb, "failed to remove unknown peeker id: %d", peekerId); - return false; - } - m_peekerToCachedIndex.remove(peekerId); - if (m_peekerToCachedIndex.isEmpty()) { - m_peekerIdSource = 0; // Once the hash becomes empty, we can start reusing IDs - m_peekerIndexCacheDirty = false; - } - return true; -} - -bool QXcbConnection::peekEventQueue(PeekerCallback peeker, void *peekerData, - PeekOptions option, qint32 peekerId) -{ - bool peekerIdProvided = peekerId != -1; - if (peekerIdProvided && !m_peekerToCachedIndex.contains(peekerId)) { - qCWarning(lcQpaXcb, "failed to find index for unknown peeker id: %d", peekerId); - return false; - } - - bool peekFromCachedIndex = option.testFlag(PeekOption::PeekFromCachedIndex); - if (peekFromCachedIndex && !peekerIdProvided) { - qCWarning(lcQpaXcb, "PeekOption::PeekFromCachedIndex requires peeker id"); - return false; - } - - if (peekerIdProvided && m_peekerIndexCacheDirty) { - // When the main event loop has flushed the buffered XCB events into the window - // system event queue, the cached indices are not valid anymore and need reset. - auto it = m_peekerToCachedIndex.begin(); - while (it != m_peekerToCachedIndex.constEnd()) { - (*it) = 0; - ++it; - } - m_peekerIndexCacheDirty = false; - } - - qint32 peekerIndex = peekFromCachedIndex ? m_peekerToCachedIndex.value(peekerId) : 0; - qint32 startingIndex = peekerIndex; - bool result = false; - m_mainEventLoopFlushedQueue = false; - - QXcbEventArray *eventqueue = m_reader->lock(); - - if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) { - qCDebug(lcQpaPeeker, "[%d] peeker index: %d | mode: %s | queue size: %d", peekerId, - peekerIndex, peekFromCachedIndex ? "cache" : "start", eventqueue->size()); - } - while (peekerIndex < eventqueue->size() && !result && !m_mainEventLoopFlushedQueue) { - xcb_generic_event_t *event = eventqueue->at(peekerIndex++); - if (!event) - continue; - if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) { - QString debug = QString((QLatin1String("[%1] peeking at index: %2"))) - .arg(peekerId).arg(peekerIndex - 1); - printXcbEvent(lcQpaPeeker(), debug.toLatin1(), event); - } - // A peeker may call QCoreApplication::processEvents(), which has two implications: - // 1) We need to make the lock available for QXcbConnection::processXcbEvents(), - // otherwise we will deadlock; - // 2) QXcbConnection::processXcbEvents() will flush the queue we are currently - // looping through; - m_reader->unlock(); - result = peeker(event, peekerData); - m_reader->lock(); - } - - m_reader->unlock(); - - if (peekerIdProvided && peekerIndex != startingIndex && !m_mainEventLoopFlushedQueue) { - auto it = m_peekerToCachedIndex.find(peekerId); - // Make sure that a peeker callback did not remove the peeker id - if (it != m_peekerToCachedIndex.constEnd()) - (*it) = peekerIndex; - } - - return result; -} - -QXcbEventReader::QXcbEventReader(QXcbConnection *connection) - : m_connection(connection) -{ -} - -void QXcbEventReader::start() -{ - connect(this, &QXcbEventReader::eventPending, m_connection, &QXcbConnection::processXcbEvents, Qt::QueuedConnection); - connect(this, &QXcbEventReader::finished, m_connection, &QXcbConnection::processXcbEvents); - QThread::start(); -} - -void QXcbEventReader::registerEventDispatcher(QAbstractEventDispatcher *dispatcher) -{ - // Flush the xcb connection before the event dispatcher is going to block. - connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, m_connection, &QXcbConnection::flush); -} - -void QXcbEventReader::run() -{ - xcb_generic_event_t *event; - while (m_connection && (event = xcb_wait_for_event(m_connection->xcb_connection()))) { - m_mutex.lock(); - addEvent(event); - while (m_connection && (event = xcb_poll_for_queued_event(m_connection->xcb_connection()))) - addEvent(event); - m_mutex.unlock(); - emit eventPending(); - } - - m_mutex.lock(); - for (int i = 0; i < m_events.size(); ++i) - free(m_events.at(i)); - m_events.clear(); - m_mutex.unlock(); -} - -void QXcbEventReader::addEvent(xcb_generic_event_t *event) -{ - if ((event->response_type & ~0x80) == XCB_CLIENT_MESSAGE - && (reinterpret_cast<xcb_client_message_event_t *>(event))->type == m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION)) - m_connection = 0; - m_events << event; -} - -QXcbEventArray *QXcbEventReader::lock() -{ - m_mutex.lock(); - return &m_events; -} - -void QXcbEventReader::unlock() -{ - m_mutex.unlock(); -} - void QXcbConnection::setFocusWindow(QWindow *w) { m_focusWindow = w ? static_cast<QXcbWindow *>(w->handle()) : nullptr; @@ -1416,38 +745,13 @@ void QXcbConnection::setMousePressWindow(QXcbWindow *w) void QXcbConnection::grabServer() { if (m_canGrabServer) - xcb_grab_server(m_connection); + xcb_grab_server(xcb_connection()); } void QXcbConnection::ungrabServer() { if (m_canGrabServer) - xcb_ungrab_server(m_connection); -} - -void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id) -{ - xcb_client_message_event_t event; - memset(&event, 0, sizeof(event)); - - const xcb_window_t eventListener = xcb_generate_id(m_connection); - xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); - xcb_screen_t *screen = it.data; - xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, - eventListener, screen->root, - 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, - screen->root_visual, 0, 0); - - event.response_type = XCB_CLIENT_MESSAGE; - event.format = 32; - event.sequence = 0; - event.window = eventListener; - event.type = atom(a); - event.data.data32[0] = id; - - xcb_send_event(xcb_connection(), false, eventListener, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event)); - xcb_destroy_window(m_connection, eventListener); - xcb_flush(xcb_connection()); + xcb_ungrab_server(xcb_connection()); } xcb_timestamp_t QXcbConnection::getTimestamp() @@ -1461,12 +765,10 @@ xcb_timestamp_t QXcbConnection::getTimestamp() connection()->flush(); xcb_generic_event_t *event = nullptr; - // lets keep this inside a loop to avoid a possible race condition, where - // reader thread has not yet had the time to acquire the mutex in order - // to add the new set of events to its event queue + while (!event) { connection()->sync(); - event = checkEvent([window, dummyAtom](xcb_generic_event_t *event, int type) { + event = eventQueue()->peek([window, dummyAtom](xcb_generic_event_t *event, int type) { if (type != XCB_PROPERTY_NOTIFY) return false; auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event); @@ -1564,21 +866,6 @@ xcb_window_t QXcbConnection::clientLeader() return m_clientLeader; } -#if QT_CONFIG(xcb_xinput) -static inline bool isXIType(xcb_generic_event_t *event, int opCode, uint16_t type) -{ - if (!isXIEvent(event, opCode)) - return false; - - auto *e = reinterpret_cast<qt_xcb_ge_event_t *>(event); - return e->event_type == type; -} -#endif -static inline bool isValid(xcb_generic_event_t *event) -{ - return event && (event->response_type & ~0x80); -} - /*! \internal Compresses events of the same type to avoid swamping the event queue. @@ -1591,22 +878,21 @@ static inline bool isValid(xcb_generic_event_t *event) 3) Or add public API to Qt for disabling event compression QTBUG-44964 */ -bool QXcbConnection::compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const +bool QXcbConnection::compressEvent(xcb_generic_event_t *event) const { + if (!QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) + return false; + uint responseType = event->response_type & ~0x80; - int nextIndex = currentIndex + 1; if (responseType == XCB_MOTION_NOTIFY) { // compress XCB_MOTION_NOTIFY notify events - for (int j = nextIndex; j < eventqueue->size(); ++j) { - xcb_generic_event_t *next = eventqueue->at(j); - if (!isValid(next)) - continue; - if (next->response_type == XCB_MOTION_NOTIFY) - return true; - } - return false; + return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch, + [](xcb_generic_event_t *, int type) { + return type == XCB_MOTION_NOTIFY; + }); } + #if QT_CONFIG(xcb_xinput) // compress XI_* events if (responseType == XCB_GE_GENERIC) { @@ -1614,58 +900,93 @@ bool QXcbConnection::compressEvent(xcb_generic_event_t *event, int currentIndex, return false; // compress XI_Motion - if (isXIType(event, m_xiOpCode, XCB_INPUT_MOTION)) { + if (isXIType(event, XCB_INPUT_MOTION)) { #if QT_CONFIG(tabletevent) - auto *xdev = reinterpret_cast<xcb_input_motion_event_t *>(event); + auto xdev = reinterpret_cast<xcb_input_motion_event_t *>(event); if (!QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents) && const_cast<QXcbConnection *>(this)->tabletDataForDevice(xdev->sourceid)) return false; #endif // QT_CONFIG(tabletevent) - for (int j = nextIndex; j < eventqueue->size(); ++j) { - xcb_generic_event_t *next = eventqueue->at(j); - if (!isValid(next)) - continue; - if (isXIType(next, m_xiOpCode, XCB_INPUT_MOTION)) - return true; - } - return false; + return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch, + [this](xcb_generic_event_t *next, int) { + return isXIType(next, XCB_INPUT_MOTION); + }); } + // compress XI_TouchUpdate for the same touch point id - if (isXIType(event, m_xiOpCode, XCB_INPUT_TOUCH_UPDATE)) { - auto *touchUpdateEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(event); + if (isXIType(event, XCB_INPUT_TOUCH_UPDATE)) { + auto touchUpdateEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(event); uint32_t id = touchUpdateEvent->detail % INT_MAX; - for (int j = nextIndex; j < eventqueue->size(); ++j) { - xcb_generic_event_t *next = eventqueue->at(j); - if (!isValid(next)) - continue; - if (isXIType(next, m_xiOpCode, XCB_INPUT_TOUCH_UPDATE)) { - auto *touchUpdateNextEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(next); - if (id == touchUpdateNextEvent->detail % INT_MAX) - return true; - } - } - return false; + + return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch, + [this, &id](xcb_generic_event_t *next, int) { + if (!isXIType(next, XCB_INPUT_TOUCH_UPDATE)) + return false; + auto touchUpdateNextEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(next); + return id == touchUpdateNextEvent->detail % INT_MAX; + }); } + return false; } #endif + if (responseType == XCB_CONFIGURE_NOTIFY) { // compress multiple configure notify events for the same window - for (int j = nextIndex; j < eventqueue->size(); ++j) { - xcb_generic_event_t *next = eventqueue->at(j); - if (isValid(next) && next->response_type == XCB_CONFIGURE_NOTIFY - && reinterpret_cast<xcb_configure_notify_event_t *>(next)->event == reinterpret_cast<xcb_configure_notify_event_t *>(event)->event) - { - return true; - } - } - return false; + return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch, + [event](xcb_generic_event_t *next, int type) { + if (type != XCB_CONFIGURE_NOTIFY) + return false; + auto currentEvent = reinterpret_cast<xcb_configure_notify_event_t *>(event); + auto nextEvent = reinterpret_cast<xcb_configure_notify_event_t *>(next); + return currentEvent->event == nextEvent->event; + }); } return false; } -void QXcbConnection::processXcbEvents() +bool QXcbConnection::isUserInputEvent(xcb_generic_event_t *event) const +{ + auto eventType = event->response_type & ~0x80; + bool isInputEvent = eventType == XCB_BUTTON_PRESS || + eventType == XCB_BUTTON_RELEASE || + eventType == XCB_KEY_PRESS || + eventType == XCB_KEY_RELEASE || + eventType == XCB_MOTION_NOTIFY || + eventType == XCB_ENTER_NOTIFY || + eventType == XCB_LEAVE_NOTIFY; + if (isInputEvent) + return true; + +#if QT_CONFIG(xcb_xinput) + if (connection()->hasXInput2()) { + isInputEvent = isXIType(event, XCB_INPUT_BUTTON_PRESS) || + isXIType(event, XCB_INPUT_BUTTON_RELEASE) || + isXIType(event, XCB_INPUT_MOTION) || + isXIType(event, XCB_INPUT_TOUCH_BEGIN) || + isXIType(event, XCB_INPUT_TOUCH_UPDATE) || + isXIType(event, XCB_INPUT_TOUCH_END) || + isXIType(event, XCB_INPUT_ENTER) || + isXIType(event, XCB_INPUT_LEAVE) || + // wacom driver's way of reporting tool proximity + isXIType(event, XCB_INPUT_PROPERTY); + } + if (isInputEvent) + return true; +#endif + + if (eventType == XCB_CLIENT_MESSAGE) { + auto clientMessage = reinterpret_cast<const xcb_client_message_event_t *>(event); + if (clientMessage->format == 32 && clientMessage->type == atom(QXcbAtom::WM_PROTOCOLS)) + if (clientMessage->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) + isInputEvent = true; + } + + return isInputEvent; +} + +void QXcbConnection::processXcbEvents(QEventLoop::ProcessEventsFlags flags) { int connection_error = xcb_connection_has_error(xcb_connection()); if (connection_error) { @@ -1673,22 +994,17 @@ void QXcbConnection::processXcbEvents() exit(1); } - QXcbEventArray *eventqueue = m_reader->lock(); + m_eventQueue->flushBufferedEvents(); - for (int i = 0; i < eventqueue->size(); ++i) { - xcb_generic_event_t *event = eventqueue->at(i); - if (!event) - continue; + while (xcb_generic_event_t *event = m_eventQueue->takeFirst(flags)) { QScopedPointer<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event); - (*eventqueue)[i] = 0; if (!(event->response_type & ~0x80)) { handleXcbError(reinterpret_cast<xcb_generic_error_t *>(event)); continue; } - if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) && - compressEvent(event, i, eventqueue)) + if (compressEvent(event)) continue; #ifndef QT_NO_CLIPBOARD @@ -1707,285 +1023,28 @@ void QXcbConnection::processXcbEvents() m_peekFuncs.erase(std::remove_if(m_peekFuncs.begin(), m_peekFuncs.end(), isWaitingFor), m_peekFuncs.end()); - m_reader->unlock(); - handleXcbEvent(event); - m_reader->lock(); - } - eventqueue->clear(); - - m_reader->unlock(); + handleXcbEvent(event); - m_peekerIndexCacheDirty = m_mainEventLoopFlushedQueue = true; + // The lock-based solution used to free the lock inside this loop, + // hence allowing for more events to arrive. ### Check if we want + // this flush here after QTBUG-70095 + m_eventQueue->flushBufferedEvents(); + } // Indicate with a null event that the event the callbacks are waiting for // is not in the queue currently. for (PeekFunc f : qAsConst(m_peekFuncs)) - f(this, 0); + f(this, nullptr); m_peekFuncs.clear(); xcb_flush(xcb_connection()); } -static const char * xcb_atomnames = { - // window-manager <-> client protocols - "WM_PROTOCOLS\0" - "WM_DELETE_WINDOW\0" - "WM_TAKE_FOCUS\0" - "_NET_WM_PING\0" - "_NET_WM_CONTEXT_HELP\0" - "_NET_WM_SYNC_REQUEST\0" - "_NET_WM_SYNC_REQUEST_COUNTER\0" - "MANAGER\0" - "_NET_SYSTEM_TRAY_OPCODE\0" - - // ICCCM window state - "WM_STATE\0" - "WM_CHANGE_STATE\0" - "WM_CLASS\0" - "WM_NAME\0" - - // Session management - "WM_CLIENT_LEADER\0" - "WM_WINDOW_ROLE\0" - "SM_CLIENT_ID\0" - "WM_CLIENT_MACHINE\0" - - // Clipboard - "CLIPBOARD\0" - "INCR\0" - "TARGETS\0" - "MULTIPLE\0" - "TIMESTAMP\0" - "SAVE_TARGETS\0" - "CLIP_TEMPORARY\0" - "_QT_SELECTION\0" - "_QT_CLIPBOARD_SENTINEL\0" - "_QT_SELECTION_SENTINEL\0" - "CLIPBOARD_MANAGER\0" - - "RESOURCE_MANAGER\0" - - "_XSETROOT_ID\0" - - "_QT_SCROLL_DONE\0" - "_QT_INPUT_ENCODING\0" - - "_QT_CLOSE_CONNECTION\0" - - "_MOTIF_WM_HINTS\0" - - "DTWM_IS_RUNNING\0" - "ENLIGHTENMENT_DESKTOP\0" - "_DT_SAVE_MODE\0" - "_SGI_DESKS_MANAGER\0" - - // EWMH (aka NETWM) - "_NET_SUPPORTED\0" - "_NET_VIRTUAL_ROOTS\0" - "_NET_WORKAREA\0" - - "_NET_MOVERESIZE_WINDOW\0" - "_NET_WM_MOVERESIZE\0" - - "_NET_WM_NAME\0" - "_NET_WM_ICON_NAME\0" - "_NET_WM_ICON\0" - - "_NET_WM_PID\0" - - "_NET_WM_WINDOW_OPACITY\0" - - "_NET_WM_STATE\0" - "_NET_WM_STATE_ABOVE\0" - "_NET_WM_STATE_BELOW\0" - "_NET_WM_STATE_FULLSCREEN\0" - "_NET_WM_STATE_MAXIMIZED_HORZ\0" - "_NET_WM_STATE_MAXIMIZED_VERT\0" - "_NET_WM_STATE_MODAL\0" - "_NET_WM_STATE_STAYS_ON_TOP\0" - "_NET_WM_STATE_DEMANDS_ATTENTION\0" - - "_NET_WM_USER_TIME\0" - "_NET_WM_USER_TIME_WINDOW\0" - "_NET_WM_FULL_PLACEMENT\0" - - "_NET_WM_WINDOW_TYPE\0" - "_NET_WM_WINDOW_TYPE_DESKTOP\0" - "_NET_WM_WINDOW_TYPE_DOCK\0" - "_NET_WM_WINDOW_TYPE_TOOLBAR\0" - "_NET_WM_WINDOW_TYPE_MENU\0" - "_NET_WM_WINDOW_TYPE_UTILITY\0" - "_NET_WM_WINDOW_TYPE_SPLASH\0" - "_NET_WM_WINDOW_TYPE_DIALOG\0" - "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0" - "_NET_WM_WINDOW_TYPE_POPUP_MENU\0" - "_NET_WM_WINDOW_TYPE_TOOLTIP\0" - "_NET_WM_WINDOW_TYPE_NOTIFICATION\0" - "_NET_WM_WINDOW_TYPE_COMBO\0" - "_NET_WM_WINDOW_TYPE_DND\0" - "_NET_WM_WINDOW_TYPE_NORMAL\0" - "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0" - - "_KDE_NET_WM_FRAME_STRUT\0" - "_NET_FRAME_EXTENTS\0" - - "_NET_STARTUP_INFO\0" - "_NET_STARTUP_INFO_BEGIN\0" - - "_NET_SUPPORTING_WM_CHECK\0" - - "_NET_WM_CM_S0\0" - - "_NET_SYSTEM_TRAY_VISUAL\0" - - "_NET_ACTIVE_WINDOW\0" - - // Property formats - "TEXT\0" - "UTF8_STRING\0" - "CARDINAL\0" - - // xdnd - "XdndEnter\0" - "XdndPosition\0" - "XdndStatus\0" - "XdndLeave\0" - "XdndDrop\0" - "XdndFinished\0" - "XdndTypeList\0" - "XdndActionList\0" - - "XdndSelection\0" - - "XdndAware\0" - "XdndProxy\0" - - "XdndActionCopy\0" - "XdndActionLink\0" - "XdndActionMove\0" - "XdndActionPrivate\0" - - // Motif DND - "_MOTIF_DRAG_AND_DROP_MESSAGE\0" - "_MOTIF_DRAG_INITIATOR_INFO\0" - "_MOTIF_DRAG_RECEIVER_INFO\0" - "_MOTIF_DRAG_WINDOW\0" - "_MOTIF_DRAG_TARGETS\0" - - "XmTRANSFER_SUCCESS\0" - "XmTRANSFER_FAILURE\0" - - // Xkb - "_XKB_RULES_NAMES\0" - - // XEMBED - "_XEMBED\0" - "_XEMBED_INFO\0" - - // XInput2 - "Button Left\0" - "Button Middle\0" - "Button Right\0" - "Button Wheel Up\0" - "Button Wheel Down\0" - "Button Horiz Wheel Left\0" - "Button Horiz Wheel Right\0" - "Abs MT Position X\0" - "Abs MT Position Y\0" - "Abs MT Touch Major\0" - "Abs MT Touch Minor\0" - "Abs MT Orientation\0" - "Abs MT Pressure\0" - "Abs MT Tracking ID\0" - "Max Contacts\0" - "Rel X\0" - "Rel Y\0" - // XInput2 tablet - "Abs X\0" - "Abs Y\0" - "Abs Pressure\0" - "Abs Tilt X\0" - "Abs Tilt Y\0" - "Abs Wheel\0" - "Abs Distance\0" - "Wacom Serial IDs\0" - "INTEGER\0" - "Rel Horiz Wheel\0" - "Rel Vert Wheel\0" - "Rel Horiz Scroll\0" - "Rel Vert Scroll\0" - "_XSETTINGS_SETTINGS\0" - "_COMPIZ_DECOR_PENDING\0" - "_COMPIZ_DECOR_REQUEST\0" - "_COMPIZ_DECOR_DELETE_PIXMAP\0" - "_COMPIZ_TOOLKIT_ACTION\0" - "_GTK_LOAD_ICONTHEMES\0" - "AT_SPI_BUS\0" - "EDID\0" - "EDID_DATA\0" - "XFree86_DDC_EDID1_RAWDATA\0" - // \0\0 terminates loop. -}; - -QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const -{ - return static_cast<QXcbAtom::Atom>(std::find(m_allAtoms, m_allAtoms + QXcbAtom::NAtoms, xatom) - m_allAtoms); -} - -void QXcbConnection::initializeAllAtoms() { - const char *names[QXcbAtom::NAtoms]; - const char *ptr = xcb_atomnames; - - int i = 0; - while (*ptr) { - names[i++] = ptr; - while (*ptr) - ++ptr; - ++ptr; - } - - Q_ASSERT(i == QXcbAtom::NAtoms); - - xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms]; - - Q_ASSERT(i == QXcbAtom::NAtoms); - for (i = 0; i < QXcbAtom::NAtoms; ++i) - cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]); - - for (i = 0; i < QXcbAtom::NAtoms; ++i) { - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0); - m_allAtoms[i] = reply->atom; - free(reply); - } -} - -xcb_atom_t QXcbConnection::internAtom(const char *name) -{ - if (!name || *name == 0) - return XCB_NONE; - - return Q_XCB_REPLY(xcb_intern_atom, xcb_connection(), false, strlen(name), name)->atom; -} - -QByteArray QXcbConnection::atomName(xcb_atom_t atom) -{ - if (!atom) - return QByteArray(); - - auto reply = Q_XCB_REPLY(xcb_get_atom_name, xcb_connection(), atom); - if (!reply) - qWarning() << "QXcbConnection::atomName: bad Atom" << atom; - else - return QByteArray(xcb_get_atom_name_name(reply.get()), xcb_get_atom_name_name_length(reply.get())); - - return QByteArray(); -} - const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const { xcb_format_iterator_t iterator = - xcb_setup_pixmap_formats_iterator(m_setup); + xcb_setup_pixmap_formats_iterator(setup()); while (iterator.rem) { xcb_format_t *format = iterator.data; @@ -2005,208 +1064,6 @@ void QXcbConnection::sync() free(xcb_get_input_focus_reply(xcb_connection(), cookie, 0)); } -void QXcbConnection::initializeShm() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_shm_id); - if (!reply || !reply->present) { - qCDebug(lcQpaXcb, "MIT-SHM extension is not present on the X server"); - return; - } - has_shm = true; - - auto shm_query = Q_XCB_REPLY(xcb_shm_query_version, m_connection); - if (shm_query) { - has_shm_fd = (shm_query->major_version == 1 && shm_query->minor_version >= 2) || - shm_query->major_version > 1; - } else { - qCWarning(lcQpaXcb, "QXcbConnection: Failed to request MIT-SHM version"); - } - - qCDebug(lcQpaXcb) << "Has MIT-SHM :" << has_shm; - qCDebug(lcQpaXcb) << "Has MIT-SHM FD :" << has_shm_fd; - - // Temporary disable warnings (unless running in debug mode). - auto logging = const_cast<QLoggingCategory*>(&lcQpaXcb()); - bool wasEnabled = logging->isEnabled(QtMsgType::QtWarningMsg); - if (!logging->isEnabled(QtMsgType::QtDebugMsg)) - logging->setEnabled(QtMsgType::QtWarningMsg, false); - if (!QXcbBackingStore::createSystemVShmSegment(this)) { - qCDebug(lcQpaXcb, "failed to create System V shared memory segment (remote " - "X11 connection?), disabling SHM"); - has_shm = has_shm_fd = false; - } - if (wasEnabled) - logging->setEnabled(QtMsgType::QtWarningMsg, true); -} - -void QXcbConnection::initializeXFixes() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xfixes_id); - if (!reply || !reply->present) - return; - - auto xfixes_query = Q_XCB_REPLY(xcb_xfixes_query_version, m_connection, - XCB_XFIXES_MAJOR_VERSION, - XCB_XFIXES_MINOR_VERSION); - if (!xfixes_query || xfixes_query->major_version < 2) { - qWarning("QXcbConnection: Failed to initialize XFixes"); - return; - } - xfixes_first_event = reply->first_event; - has_xfixes = true; -} - -void QXcbConnection::initializeXRender() -{ -#if QT_CONFIG(xcb_render) - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_render_id); - if (!reply || !reply->present) { - qCDebug(lcQpaXcb, "XRender extension not present on the X server"); - return; - } - - auto xrender_query = Q_XCB_REPLY(xcb_render_query_version, m_connection, - XCB_RENDER_MAJOR_VERSION, - XCB_RENDER_MINOR_VERSION); - if (!xrender_query) { - qCWarning(lcQpaXcb, "xcb_render_query_version failed"); - return; - } - - has_render_extension = true; - m_xrenderVersion.first = xrender_query->major_version; - m_xrenderVersion.second = xrender_query->minor_version; -#endif -} - -void QXcbConnection::initializeXRandr() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_randr_id); - if (!reply || !reply->present) - return; - - xrandr_first_event = reply->first_event; - - auto xrandr_query = Q_XCB_REPLY(xcb_randr_query_version, m_connection, - XCB_RANDR_MAJOR_VERSION, - XCB_RANDR_MINOR_VERSION); - - has_randr_extension = true; - - if (!xrandr_query || (xrandr_query->major_version < 1 || (xrandr_query->major_version == 1 && xrandr_query->minor_version < 2))) { - qWarning("QXcbConnection: Failed to initialize XRandr"); - has_randr_extension = false; - } - - xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(m_setup); - for (; rootIter.rem; xcb_screen_next(&rootIter)) { - xcb_randr_select_input(xcb_connection(), - rootIter.data->root, - XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | - XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | - XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE | - XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY - ); - } -} - -void QXcbConnection::initializeXinerama() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xinerama_id); - if (!reply || !reply->present) - return; - - auto xinerama_is_active = Q_XCB_REPLY(xcb_xinerama_is_active, m_connection); - has_xinerama_extension = xinerama_is_active && xinerama_is_active->state; -} - -void QXcbConnection::initializeXShape() -{ - const xcb_query_extension_reply_t *xshape_reply = xcb_get_extension_data(m_connection, &xcb_shape_id); - if (!xshape_reply || !xshape_reply->present) - return; - - has_shape_extension = true; - auto shape_query = Q_XCB_REPLY(xcb_shape_query_version, m_connection); - if (!shape_query) { - qWarning("QXcbConnection: Failed to initialize SHAPE extension"); - } else if (shape_query->major_version > 1 || (shape_query->major_version == 1 && shape_query->minor_version >= 1)) { - // The input shape is the only thing added in SHAPE 1.1 - has_input_shape = true; - } -} - -void QXcbConnection::initializeXKB() -{ -#if QT_CONFIG(xkb) - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xkb_id); - if (!reply || !reply->present) { - qWarning("Qt: XKEYBOARD extension not present on the X server."); - xkb_first_event = 0; - return; - } - xkb_first_event = reply->first_event; - - xcb_connection_t *c = connection()->xcb_connection(); - - auto xkb_query = Q_XCB_REPLY(xcb_xkb_use_extension, c, - XKB_X11_MIN_MAJOR_XKB_VERSION, - XKB_X11_MIN_MINOR_XKB_VERSION); - - if (!xkb_query) { - qWarning("Qt: Failed to initialize XKB extension"); - return; - } else if (!xkb_query->supported) { - qWarning("Qt: Unsupported XKB version (We want %d %d, but X server has %d %d)", - XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION, - xkb_query->serverMajor, xkb_query->serverMinor); - return; - } - - has_xkb = true; - - const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES | - XCB_XKB_MAP_PART_KEY_SYMS | - XCB_XKB_MAP_PART_MODIFIER_MAP | - XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | - XCB_XKB_MAP_PART_KEY_ACTIONS | - XCB_XKB_MAP_PART_KEY_BEHAVIORS | - XCB_XKB_MAP_PART_VIRTUAL_MODS | - XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP); - - const uint16_t required_events = (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | - XCB_XKB_EVENT_TYPE_MAP_NOTIFY | - XCB_XKB_EVENT_TYPE_STATE_NOTIFY); - - // XKB events are reported to all interested clients without regard - // to the current keyboard input focus or grab state - xcb_void_cookie_t select = xcb_xkb_select_events_checked(c, - XCB_XKB_ID_USE_CORE_KBD, - required_events, - 0, - required_events, - required_map_parts, - required_map_parts, - 0); - - xcb_generic_error_t *error = xcb_request_check(c, select); - if (error) { - free(error); - qWarning("Qt: failed to select notify events from xcb-xkb"); - return; - } -#endif -} - -void QXcbConnection::initializeXSync() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id); - if (!reply || !reply->present) - return; - - has_sync_extension = true; -} - QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const { if (!m_systemTrayTracker) { |