diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbconnection.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 309 |
1 files changed, 218 insertions, 91 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index d2e08aecee..a20d957138 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -107,6 +107,24 @@ Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") #define XCB_GE_GENERIC 35 #endif +// 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 = (qt_xcb_ge_event_t *)event; + return e->extension == opCode; +} + #ifdef XCB_USE_XLIB static const char * const xcbConnectionErrors[] = { "No error", /* Error 0 */ @@ -305,13 +323,10 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) screen->updateRefreshRate(output.mode); // If the screen became primary, reshuffle the order in QGuiApplicationPrivate - // TODO: add a proper mechanism for updating primary screen if (!wasPrimary && screen->isPrimary()) { - QScreen *realScreen = static_cast<QPlatformScreen*>(screen)->screen(); - QGuiApplicationPrivate::screen_list.removeOne(realScreen); - QGuiApplicationPrivate::screen_list.prepend(realScreen); - m_screens.removeOne(screen); - m_screens.prepend(screen); + const int idx = m_screens.indexOf(screen); + m_screens.swap(0, idx); + QXcbIntegration::instance()->setPrimaryScreen(screen); } qCDebug(lcQpaScreen) << "output has changed" << screen; } @@ -434,6 +449,9 @@ void QXcbConnection::initializeScreens() ++xcbScreenNumber; } // for each xcb screen + foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) + virtualDesktop->subscribeToXFixesSelectionNotify(); + // If there's no randr extension, or there was some error above, or we found a // screen which doesn't have outputs for some other reason (e.g. on VNC or ssh -X), // but the dimensions are known anyway, and we don't already have any lingering @@ -461,7 +479,7 @@ void QXcbConnection::initializeScreens() // Push the screens to QApplication QXcbIntegration *integration = QXcbIntegration::instance(); foreach (QXcbScreen* screen, m_screens) { - qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; + qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ')'; integration->screenAdded(screen, screen->isPrimary()); } @@ -492,6 +510,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra , m_systemTrayTracker(0) , m_glIntegration(Q_NULLPTR) , m_xiGrab(false) + , m_qtSelectionOwner(0) { #ifdef XCB_USE_XLIB Display *dpy = XOpenDisplay(m_displayName.constData()); @@ -536,12 +555,12 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra m_netWmUserTime = XCB_CURRENT_TIME; initializeXRandr(); + initializeXFixes(); initializeScreens(); if (m_screens.isEmpty()) qFatal("QXcbConnection: no screens available"); - initializeXFixes(); initializeXRender(); m_xi2Enabled = false; #if defined(XCB_USE_XINPUT2) @@ -590,9 +609,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra qCDebug(QT_XCB_GLINTEGRATION) << "Failed to create xcb gl-integration"; sync(); - - if (qEnvironmentVariableIsEmpty("QT_IM_MODULE")) - qputenv("QT_IM_MODULE", QByteArray("compose")); } QXcbConnection::~QXcbConnection() @@ -1115,7 +1131,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) #if defined(XCB_USE_XINPUT2) case XCB_GE_GENERIC: // Here the windowEventListener is invoked from xi2HandleEvent() - if (m_xi2Enabled) + if (m_xi2Enabled && isXIEvent(event, m_xiOpCode)) xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event)); break; #endif @@ -1127,10 +1143,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) if (!handled) { if (response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) { - setTime(((xcb_xfixes_selection_notify_event_t *)event)->timestamp); + xcb_xfixes_selection_notify_event_t *notify_event = (xcb_xfixes_selection_notify_event_t *)event; + setTime(notify_event->timestamp); #ifndef QT_NO_CLIPBOARD - m_clipboard->handleXFixesSelectionRequest((xcb_xfixes_selection_notify_event_t *)event); + m_clipboard->handleXFixesSelectionRequest(notify_event); #endif + foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) + virtualDesktop->handleXFixesSelectionNotify(notify_event); + handled = true; } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) { updateScreens((xcb_randr_notify_event_t *)event); @@ -1288,10 +1308,12 @@ void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id) 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; Q_XCB_CALL(xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, - eventListener, m_screens.at(0)->root(), + eventListener, screen->root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, - m_screens.at(0)->screen()->root_visual, 0, 0)); + screen->root_visual, 0, 0)); event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; @@ -1357,6 +1379,37 @@ xcb_timestamp_t QXcbConnection::getTimestamp() return timestamp; } +xcb_window_t QXcbConnection::getSelectionOwner(xcb_atom_t atom) const +{ + xcb_connection_t *c = xcb_connection(); + xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom); + xcb_get_selection_owner_reply_t *reply; + reply = xcb_get_selection_owner_reply(c, cookie, 0); + xcb_window_t win = reply->owner; + free(reply); + return win; +} + +xcb_window_t QXcbConnection::getQtSelectionOwner() +{ + if (!m_qtSelectionOwner) { + xcb_screen_t *xcbScreen = primaryVirtualDesktop()->screen(); + int x = 0, y = 0, w = 3, h = 3; + m_qtSelectionOwner = xcb_generate_id(xcb_connection()); + Q_XCB_CALL(xcb_create_window(xcb_connection(), + XCB_COPY_FROM_PARENT, // depth -- same as root + m_qtSelectionOwner, // window id + xcbScreen->root, // parent window id + x, y, w, h, + 0, // border width + XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class + xcbScreen->root_visual, // visual + 0, // value mask + 0)); // value list + } + return m_qtSelectionOwner; +} + xcb_window_t QXcbConnection::rootWindow() { QXcbScreen *s = primaryScreen(); @@ -1437,6 +1490,105 @@ void *QXcbConnection::createVisualInfoForDefaultVisualId() const #endif +#if defined(XCB_USE_XINPUT2) +// it is safe to cast XI_* events here as long as we are only touching the first 32 bytes, +// after that position event needs memmove, see xi2PrepareXIGenericDeviceEvent +static inline bool isXIType(xcb_generic_event_t *event, int opCode, uint16_t type) +{ + if (!isXIEvent(event, opCode)) + return false; + + xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event); + return xiEvent->evtype == 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. + If event compression is not desired there are several options what developers can do: + + 1) Write responsive applications. We drop events that have been buffered in the event + queue while waiting on unresponsive GUI thread. + 2) Use QAbstractNativeEventFilter to get all events from X connection. This is not optimal + because it requires working with native event types. + 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 +{ + 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; + } +#if defined(XCB_USE_XINPUT2) + // compress XI_* events + if (responseType == XCB_GE_GENERIC) { + if (!m_xi2Enabled) + return false; + + // compress XI_Motion + if (isXIType(event, m_xiOpCode, XI_Motion)) { + 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, XI_Motion)) + return true; + } + return false; + } +#ifdef XCB_USE_XINPUT22 + // compress XI_TouchUpdate for the same touch point id + if (isXIType(event, m_xiOpCode, XI_TouchUpdate)) { + xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event); + uint32_t id = xiDeviceEvent->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, XI_TouchUpdate)) { + xXIDeviceEvent *xiDeviceNextEvent = reinterpret_cast<xXIDeviceEvent *>(next); + if (id == xiDeviceNextEvent->detail % INT_MAX) + return true; + } + } + return false; + } +#endif + 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 + && ((xcb_configure_notify_event_t *)next)->event == ((xcb_configure_notify_event_t*)event)->event) + { + return true; + } + } + return false; + } + + return false; +} + void QXcbConnection::processXcbEvents() { int connection_error = xcb_connection_has_error(xcb_connection()); @@ -1447,61 +1599,39 @@ void QXcbConnection::processXcbEvents() QXcbEventArray *eventqueue = m_reader->lock(); - for(int i = 0; i < eventqueue->size(); ++i) { + for (int i = 0; i < eventqueue->size(); ++i) { xcb_generic_event_t *event = eventqueue->at(i); if (!event) continue; QScopedPointer<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event); (*eventqueue)[i] = 0; - uint response_type = event->response_type & ~0x80; - - if (!response_type) { + if (!(event->response_type & ~0x80)) { handleXcbError((xcb_generic_error_t *)event); - } else { - if (response_type == XCB_MOTION_NOTIFY) { - // compress multiple motion notify events in a row - // to avoid swamping the event queue - xcb_generic_event_t *next = eventqueue->value(i+1, 0); - if (next && (next->response_type & ~0x80) == XCB_MOTION_NOTIFY) - continue; - } + continue; + } - if (response_type == XCB_CONFIGURE_NOTIFY) { - // compress multiple configure notify events for the same window - bool found = false; - for (int j = i; j < eventqueue->size(); ++j) { - xcb_generic_event_t *other = eventqueue->at(j); - if (other && (other->response_type & ~0x80) == XCB_CONFIGURE_NOTIFY - && ((xcb_configure_notify_event_t *)other)->event == ((xcb_configure_notify_event_t *)event)->event) - { - found = true; - break; - } - } - if (found) - continue; - } + if (compressEvent(event, i, eventqueue)) + continue; - bool accepted = false; - if (clipboard()->processIncr()) - clipboard()->incrTransactionPeeker(event, accepted); - if (accepted) - continue; + bool accepted = false; + if (clipboard()->processIncr()) + clipboard()->incrTransactionPeeker(event, accepted); + if (accepted) + continue; - QVector<PeekFunc>::iterator it = m_peekFuncs.begin(); - while (it != m_peekFuncs.end()) { - // These callbacks return true if the event is what they were - // waiting for, remove them from the list in that case. - if ((*it)(this, event)) - it = m_peekFuncs.erase(it); - else - ++it; - } - m_reader->unlock(); - handleXcbEvent(event); - m_reader->lock(); + QVector<PeekFunc>::iterator it = m_peekFuncs.begin(); + while (it != m_peekFuncs.end()) { + // These callbacks return true if the event is what they were + // waiting for, remove them from the list in that case. + if ((*it)(this, event)) + it = m_peekFuncs.erase(it); + else + ++it; } + m_reader->unlock(); + handleXcbEvent(event); + m_reader->lock(); } eventqueue->clear(); @@ -1746,7 +1876,8 @@ static const char * xcb_atomnames = { "_XSETTINGS_SETTINGS\0" "_COMPIZ_DECOR_PENDING\0" "_COMPIZ_DECOR_REQUEST\0" - "_COMPIZ_DECOR_DELETE_PIXMAP\0" // \0\0 terminates loop. + "_COMPIZ_DECOR_DELETE_PIXMAP\0" + "_COMPIZ_TOOLKIT_ACTION\0" // \0\0 terminates loop. }; QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const @@ -2043,41 +2174,21 @@ bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, doub return true; } -// 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: -// NOTE: API might change again in the next release of xcb in which case this comment will -// need to be updated to reflect the reality. -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; - -bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode) -{ - qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev; - // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from - // the xcb version 1.9.3, prior to that it was called "pad0". - if (event->extension == opCode) { - // xcb event structs contain stuff that wasn't on the wire, the full_sequence field - // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. - // Move this data back to have the same layout in memory as it was on the wire - // and allow casting, overwriting the full_sequence field. - memmove((char*) event + 32, (char*) event + 36, event->length * 4); - return true; - } - return false; +void QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event) +{ + // xcb event structs contain stuff that wasn't on the wire, the full_sequence field + // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. + // Move this data back to have the same layout in memory as it was on the wire + // and allow casting, overwriting the full_sequence field. + memmove((char*) event + 32, (char*) event + 36, event->length * 4); } #endif // defined(XCB_USE_XINPUT2) -QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() +QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const { if (!m_systemTrayTracker) { - if ( (m_systemTrayTracker = QXcbSystemTrayTracker::create(this)) ) { + QXcbConnection *self = const_cast<QXcbConnection *>(this); + if ((self->m_systemTrayTracker = QXcbSystemTrayTracker::create(self))) { connect(m_systemTrayTracker, SIGNAL(systemTrayWindowChanged(QScreen*)), QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*))); } @@ -2085,6 +2196,22 @@ QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() return m_systemTrayTracker; } +bool QXcbConnection::xEmbedSystemTrayAvailable() +{ + if (!QGuiApplicationPrivate::platformIntegration()) + return false; + QXcbConnection *connection = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration())->defaultConnection(); + return connection->systemTrayTracker(); +} + +bool QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel() +{ + if (!QGuiApplicationPrivate::platformIntegration()) + return false; + QXcbConnection *connection = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration())->defaultConnection(); + return connection->systemTrayTracker() && connection->systemTrayTracker()->visualHasAlphaChannel(); +} + bool QXcbConnection::event(QEvent *e) { if (e->type() == QEvent::User + 1) { |