diff options
author | Simon Hausmann <simon.hausmann@theqtcompany.com> | 2015-03-18 15:23:56 +0000 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2015-03-18 22:27:24 +0000 |
commit | dcf0ececbf167502ac9943c6435bb9ddeeb29d5e (patch) | |
tree | 19416d427b84f0a90e0d3028588404a544003734 /src/plugins/platforms/xcb | |
parent | 7aad6b9fd426e05fdfb3892d60432a45e4079887 (diff) | |
parent | 198606f6dbca95ba3a170fff387327d8271018cd (diff) |
Merge "Merge remote-tracking branch 'origin/5.5' into dev" into refs/staging/dev
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbclipboard.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 296 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.h | 13 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbnativeinterface.cpp | 56 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbnativeinterface.h | 1 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbscreen.cpp | 131 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbscreen.h | 21 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbsessionmanager.cpp | 2 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 2 |
9 files changed, 373 insertions, 153 deletions
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index d4f1c6ae48..2c0a7a11cc 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -97,7 +97,7 @@ protected: that->format_atoms = m_clipboard->getDataInFormat(modeAtom, m_clipboard->atom(QXcbAtom::TARGETS)); if (format_atoms.size() > 0) { - xcb_atom_t *targets = (xcb_atom_t *) format_atoms.data(); + const xcb_atom_t *targets = (const xcb_atom_t *) format_atoms.data(); int size = format_atoms.size() / sizeof(xcb_atom_t); for (int i = 0; i < size; ++i) { @@ -128,7 +128,7 @@ protected: (void)formats(); // trigger update of format list QVector<xcb_atom_t> atoms; - xcb_atom_t *targets = (xcb_atom_t *) format_atoms.data(); + const xcb_atom_t *targets = (const xcb_atom_t *) format_atoms.data(); int size = format_atoms.size() / sizeof(xcb_atom_t); atoms.reserve(size); for (int i = 0; i < size; ++i) diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 7ec5851737..11ff313144 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -51,6 +51,7 @@ #include <QAbstractEventDispatcher> #include <QTimer> #include <QByteArray> +#include <QScopedPointer> #include <algorithm> @@ -116,8 +117,29 @@ static int ioErrorHandler(Display *dpy) } #endif -QXcbScreen* QXcbConnection::findOrCreateScreen(QList<QXcbScreen *>& newScreens, - int screenNumber, xcb_screen_t* xcbScreen, xcb_randr_get_output_info_reply_t *output) +QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) +{ + foreach (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) +{ + foreach (QXcbScreen *screen, m_screens) { + if (screen->root() == rootWindow && screen->output() == output) + return screen; + } + + return 0; +} + +QXcbScreen* QXcbConnection::createScreen(int screenNumber, xcb_screen_t* xcbScreen, + xcb_randr_output_t outputId, + xcb_randr_get_output_info_reply_t *output) { QString name; if (output) @@ -130,23 +152,147 @@ QXcbScreen* QXcbConnection::findOrCreateScreen(QList<QXcbScreen *>& newScreens, displayName.truncate(dotPos); name = QString::fromLocal8Bit(displayName) + QLatin1Char('.') + QString::number(screenNumber); } - foreach (QXcbScreen* scr, m_screens) - if (scr->name() == name && scr->root() == xcbScreen->root) - return scr; - QXcbScreen *ret = new QXcbScreen(this, xcbScreen, output, name, screenNumber); - newScreens << ret; - return ret; + + return new QXcbScreen(this, xcbScreen, outputId, output, name, screenNumber); +} + +bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output) +{ + xcb_generic_error_t *error = 0; + xcb_randr_get_output_primary_cookie_t primaryCookie = + xcb_randr_get_output_primary(xcb_connection(), rootWindow); + QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary ( + xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error)); + if (!primary || error) { + qWarning("failed to get the primary output of the screen"); + free(error); + error = NULL; + } + const bool isPrimary = primary ? (primary->output == output) : false; + + return isPrimary; +} + +xcb_screen_t* QXcbConnection::xcbScreenForRootWindow(xcb_window_t rootWindow, int *xcbScreenNumber) +{ + xcb_screen_iterator_t xcbScreenIter = xcb_setup_roots_iterator(m_setup); + for (; xcbScreenIter.rem; xcb_screen_next(&xcbScreenIter)) { + if (xcbScreenIter.data->root == rootWindow) { + if (xcbScreenNumber) + *xcbScreenNumber = xcb_setup_roots_length(m_setup) - xcbScreenIter.rem; + return xcbScreenIter.data; + } + } + + return 0; } /*! \brief Synchronizes the screen list, adds new screens, removes deleted ones */ -void QXcbConnection::updateScreens() +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; + xcb_screen_t *xcbScreen = xcbScreenForRootWindow(crtc.window); + if (!xcbScreen) + // Not for us + return; + + qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc; + QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc); + // 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) { + 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; + int xcbScreenNumber = 0; + xcb_screen_t *xcbScreen = xcbScreenForRootWindow(output.window, &xcbScreenNumber); + if (!xcbScreen) + // 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"; + + // Known screen removed -> delete it + m_screens.removeOne(screen); + foreach (QXcbScreen *otherScreen, m_screens) + otherScreen->removeVirtualSibling((QPlatformScreen *) screen); + + QXcbIntegration::instance()->destroyScreen(screen); + + // QTBUG-40174, QTBUG-42985: If there are no outputs, then there must be + // no QScreen instances; a Qt application can survive this situation, and + // start rendering again later when there is a screen again. + + } 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) { + xcb_randr_get_output_info_cookie_t outputInfoCookie = + xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp); + QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo( + xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL)); + + screen = createScreen(xcbScreenNumber, xcbScreen, output.output, outputInfo.data()); + qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; + + screen->setPrimary(checkOutputIsPrimary(output.window, output.output)); + foreach (QXcbScreen *otherScreen, m_screens) + if (otherScreen->root() == output.window) + otherScreen->addVirtualSibling(screen); + m_screens << screen; + QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + } + // else ignore disabled screens + } else if (screen) { + // Screen has been disabled -> remove + if (output.crtc == XCB_NONE && output.mode == XCB_NONE) { + qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled"; + m_screens.removeOne(screen); + foreach (QXcbScreen *otherScreen, m_screens) + otherScreen->removeVirtualSibling((QPlatformScreen *) screen); + QXcbIntegration::instance()->destroyScreen(screen); + } else { + // Just update existing screen + screen->updateGeometry(output.config_timestamp); + const bool wasPrimary = screen->isPrimary(); + screen->setPrimary(checkOutputIsPrimary(output.window, output.output)); + if (screen->mode() != output.mode) + 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); + } + qCDebug(lcQpaScreen) << "output has changed" << screen; + } + } + if (!m_screens.isEmpty()) + qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); + else + qCDebug(lcQpaScreen) << "no outputs"; + } +} + +void QXcbConnection::initializeScreens() { xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); int xcbScreenNumber = 0; // screen number in the xcb sense - QList<QXcbScreen *> activeScreens; - QList<QXcbScreen *> newScreens; QXcbScreen* primaryScreen = NULL; while (it.rem) { // Each "screen" in xcb terminology is a virtual desktop, @@ -161,59 +307,73 @@ void QXcbConnection::updateScreens() xcb_generic_error_t *error = NULL; xcb_randr_get_output_primary_cookie_t primaryCookie = xcb_randr_get_output_primary(xcb_connection(), xcbScreen->root); + // TODO: RRGetScreenResources has to be called on each X display at least once before + // RRGetScreenResourcesCurrent can be used - we can't know if we are the first application + // to do so or not, so we always call the slower version here. Ideally we should share some + // global flag (an atom on root window maybe) that at least other Qt apps would understand + // and could call RRGetScreenResourcesCurrent here, speeding up start. xcb_randr_get_screen_resources_cookie_t resourcesCookie = xcb_randr_get_screen_resources(xcb_connection(), xcbScreen->root); - xcb_randr_get_output_primary_reply_t *primary = - xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error); + QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary( + xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error)); if (!primary || error) { - qWarning("QXcbConnection: Failed to get the primary output of the screen"); + qWarning("failed to get the primary output of the screen"); free(error); } else { - xcb_randr_get_screen_resources_reply_t *resources = - xcb_randr_get_screen_resources_reply(xcb_connection(), resourcesCookie, &error); + QScopedPointer<xcb_randr_get_screen_resources_reply_t, QScopedPointerPodDeleter> resources( + xcb_randr_get_screen_resources_reply(xcb_connection(), resourcesCookie, &error)); if (!resources || error) { - qWarning("QXcbConnection: Failed to get the screen resources"); + qWarning("failed to get the screen resources"); free(error); } else { xcb_timestamp_t timestamp = resources->config_timestamp; - outputCount = xcb_randr_get_screen_resources_outputs_length(resources); - xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(resources); + outputCount = xcb_randr_get_screen_resources_outputs_length(resources.data()); + xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(resources.data()); for (int i = 0; i < outputCount; i++) { - xcb_randr_get_output_info_reply_t *output = + QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> output( xcb_randr_get_output_info_reply(xcb_connection(), - xcb_randr_get_output_info_unchecked(xcb_connection(), outputs[i], timestamp), NULL); + xcb_randr_get_output_info_unchecked(xcb_connection(), outputs[i], timestamp), NULL)); + + // Invalid, disconnected or disabled output if (output == NULL) 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.data()), + xcb_randr_get_output_info_name_length(output.data())))); + continue; + } if (output->crtc == XCB_NONE) { - qCDebug(lcQpaScreen, "output %s is not connected", qPrintable( - QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output), - xcb_randr_get_output_info_name_length(output)))); + qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable( + QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.data()), + xcb_randr_get_output_info_name_length(output.data())))); continue; } - QXcbScreen *screen = findOrCreateScreen(newScreens, xcbScreenNumber, xcbScreen, output); + QXcbScreen *screen = createScreen(xcbScreenNumber, xcbScreen, outputs[i], output.data()); siblings << screen; - activeScreens << screen; ++connectedOutputCount; + 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()); } } - free(output); } } - free(resources); } - free(primary); } foreach (QPlatformScreen* s, siblings) ((QXcbScreen*)s)->setVirtualSiblings(siblings); @@ -221,47 +381,7 @@ void QXcbConnection::updateScreens() ++xcbScreenNumber; } // for each xcb screen - QXcbIntegration *integration = QXcbIntegration::instance(); - - // Rebuild screen list, ensuring primary screen is always in front, - // both in the QXcbConnection::m_screens list as well as in the - // QGuiApplicationPrivate::screen_list list, which gets updated via - // - screen added: integration->screenAdded() - // - screen removed: integration->destroyScreen - - // Gather screens to delete - QList<QXcbScreen*> screensToDelete; - for (int i = m_screens.count() - 1; i >= 0; --i) { - if (!activeScreens.contains(m_screens[i])) { - screensToDelete.append(m_screens.takeAt(i)); - } - } - - // If there is a new primary screen, add that one first - if (newScreens.contains(primaryScreen)) { - newScreens.removeOne(primaryScreen); - m_screens.prepend(primaryScreen); - qCDebug(lcQpaScreen) << "adding as primary" << primaryScreen; - integration->screenAdded(primaryScreen, true); - } - - // Add the remaining new screens - foreach (QXcbScreen* screen, newScreens) { - m_screens.append(screen); - qCDebug(lcQpaScreen) << "adding" << screen; - integration->screenAdded(screen); - } - - // Delete the old screens, now that the new ones were added - // and we are sure that there is at least one screen available - foreach (QXcbScreen* screen, screensToDelete) { - qCDebug(lcQpaScreen) << "removing" << screen; - integration->destroyScreen(screen); - } - - // Ensure that the primary screen is first in m_screens too - // (in case the assignment of primary was the only change, - // without adding or removing screens) + // Ensure the primary screen is first in the list if (primaryScreen) { Q_ASSERT(!m_screens.isEmpty()); if (m_screens.first() != primaryScreen) { @@ -270,13 +390,15 @@ void QXcbConnection::updateScreens() } } + // Push the screens to QApplication + QXcbIntegration *integration = QXcbIntegration::instance(); + foreach (QXcbScreen* screen, m_screens) { + qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; + integration->screenAdded(screen, screen->isPrimary()); + } + if (!m_screens.isEmpty()) qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); - else - // QTBUG-40174, QTBUG-42985: If there are no outputs, then there must be - // no QScreen instances; a Qt application can survive this situation, and - // start rendering again later when there is a screen again. - qCDebug(lcQpaScreen) << "xcb connection has no outputs"; } QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, const char *displayName) @@ -343,7 +465,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra m_netWmUserTime = XCB_CURRENT_TIME; initializeXRandr(); - updateScreens(); + initializeScreens(); if (m_screens.isEmpty()) qFatal("QXcbConnection: no screens available"); @@ -943,14 +1065,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) m_clipboard->handleXFixesSelectionRequest((xcb_xfixes_selection_notify_event_t *)event); #endif handled = true; + } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) { + updateScreens((xcb_randr_notify_event_t *)event); + handled = true; } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { - updateScreens(); xcb_randr_screen_change_notify_event_t *change_event = (xcb_randr_screen_change_notify_event_t *)event; foreach (QXcbScreen *s, m_screens) { - if (s->root() == change_event->root ) { + if (s->root() == change_event->root ) s->handleScreenChange(change_event); - s->updateRefreshRate(); - } } handled = true; #ifndef QT_NO_XKB @@ -1178,7 +1300,8 @@ xcb_timestamp_t QXcbConnection::getTimestamp() xcb_window_t QXcbConnection::rootWindow() { - return primaryScreen()->root(); + QXcbScreen *s = primaryScreen(); + return s ? s->root() : 0; } #ifdef XCB_USE_XLIB @@ -1653,6 +1776,17 @@ void QXcbConnection::initializeXRandr() has_randr_extension = false; } free(xrandr_query); + + 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::initializeXShape() diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 97c2590708..4c014bc6e2 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -35,6 +35,7 @@ #define QXCBCONNECTION_H #include <xcb/xcb.h> +#include <xcb/randr.h> #include "qxcbexport.h" #include <QHash> @@ -495,9 +496,15 @@ private: void initializeXShape(); void initializeXKB(); void handleClientMessageEvent(const xcb_client_message_event_t *event); - QXcbScreen* findOrCreateScreen(QList<QXcbScreen *>& newScreens, int screenNumber, - xcb_screen_t* xcbScreen, xcb_randr_get_output_info_reply_t *output = NULL); - void updateScreens(); + QXcbScreen* createScreen(int screenNumber, xcb_screen_t* xcbScreen, + xcb_randr_output_t outputId = XCB_NONE, + xcb_randr_get_output_info_reply_t *output = 0); + QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc); + QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output); + xcb_screen_t* xcbScreenForRootWindow(xcb_window_t rootWindow, int *xcbScreenNumber = 0); + bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output); + void initializeScreens(); + void updateScreens(const xcb_randr_notify_event_t *event); void handleButtonPress(xcb_generic_event_t *event); void handleButtonRelease(xcb_generic_event_t *event); void handleMotionNotify(xcb_generic_event_t *event); diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index a375128288..75ffaa37bb 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -93,13 +93,21 @@ QXcbNativeInterface::QXcbNativeInterface() : void QXcbNativeInterface::beep() // For QApplication::beep() { - QPlatformScreen *screen = QGuiApplication::primaryScreen()->handle(); + QScreen *priScreen = QGuiApplication::primaryScreen(); + if (!priScreen) + return; + QPlatformScreen *screen = priScreen->handle(); + if (!screen) + return; xcb_connection_t *connection = static_cast<QXcbScreen *>(screen)->xcb_connection(); xcb_bell(connection, 0); } static inline QXcbSystemTrayTracker *systemTrayTracker(const QScreen *s) { + if (!s) + return Q_NULLPTR; + return static_cast<const QXcbScreen *>(s->handle())->connection()->systemTrayTracker(); } @@ -179,6 +187,9 @@ void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resour case AtspiBus: result = atspiBus(); break; + case Connection: + result = connection(); + break; default: break; } @@ -355,16 +366,25 @@ QFunctionPointer QXcbNativeInterface::platformFunction(const QByteArray &functio void *QXcbNativeInterface::appTime(const QXcbScreen *screen) { + if (!screen) + return Q_NULLPTR; + return reinterpret_cast<void *>(quintptr(screen->connection()->time())); } void *QXcbNativeInterface::appUserTime(const QXcbScreen *screen) { + if (!screen) + return Q_NULLPTR; + return reinterpret_cast<void *>(quintptr(screen->connection()->netWmUserTime())); } void *QXcbNativeInterface::getTimestamp(const QXcbScreen *screen) { + if (!screen) + return Q_NULLPTR; + return reinterpret_cast<void *>(quintptr(screen->connection()->getTimestamp())); } @@ -400,10 +420,16 @@ void *QXcbNativeInterface::display() #ifdef XCB_USE_XLIB QXcbIntegration *integration = QXcbIntegration::instance(); QXcbConnection *defaultConnection = integration->defaultConnection(); - return defaultConnection->xlib_display(); -#else - return 0; + if (defaultConnection) + return defaultConnection->xlib_display(); #endif + return Q_NULLPTR; +} + +void *QXcbNativeInterface::connection() +{ + QXcbIntegration *integration = QXcbIntegration::instance(); + return integration->defaultConnection()->xcb_connection(); } void *QXcbNativeInterface::atspiBus() @@ -429,12 +455,16 @@ void *QXcbNativeInterface::atspiBus() void QXcbNativeInterface::setAppTime(QScreen* screen, xcb_timestamp_t time) { - static_cast<QXcbScreen *>(screen->handle())->connection()->setTime(time); + if (screen) { + static_cast<QXcbScreen *>(screen->handle())->connection()->setTime(time); + } } void QXcbNativeInterface::setAppUserTime(QScreen* screen, xcb_timestamp_t time) { - static_cast<QXcbScreen *>(screen->handle())->connection()->setNetWmUserTime(time); + if (screen) { + static_cast<QXcbScreen *>(screen->handle())->connection()->setNetWmUserTime(time); + } } void QXcbNativeInterface::setStartupId(const char *data) @@ -450,9 +480,11 @@ QXcbScreen *QXcbNativeInterface::qPlatformScreenForWindow(QWindow *window) { QXcbScreen *screen; if (window) { - screen = static_cast<QXcbScreen *>(window->screen()->handle()); + QScreen *qs = window->screen(); + screen = static_cast<QXcbScreen *>(qs ? qs->handle() : Q_NULLPTR); } else { - screen = static_cast<QXcbScreen *>(QGuiApplication::primaryScreen()->handle()); + QScreen *qs = QGuiApplication::primaryScreen(); + screen = static_cast<QXcbScreen *>(qs ? qs->handle() : Q_NULLPTR); } return screen; } @@ -461,23 +493,23 @@ void *QXcbNativeInterface::displayForWindow(QWindow *window) { #if defined(XCB_USE_XLIB) QXcbScreen *screen = qPlatformScreenForWindow(window); - return screen->connection()->xlib_display(); + return screen ? screen->connection()->xlib_display() : Q_NULLPTR; #else Q_UNUSED(window); - return 0; + return Q_NULLPTR; #endif } void *QXcbNativeInterface::connectionForWindow(QWindow *window) { QXcbScreen *screen = qPlatformScreenForWindow(window); - return screen->xcb_connection(); + return screen ? screen->xcb_connection() : Q_NULLPTR; } void *QXcbNativeInterface::screenForWindow(QWindow *window) { QXcbScreen *screen = qPlatformScreenForWindow(window); - return screen->screen(); + return screen ? screen->screen() : Q_NULLPTR; } void QXcbNativeInterface::addHandler(QXcbNativeInterfaceHandler *handler) diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h index 054c0ec2b5..f88b710864 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.h +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h @@ -100,6 +100,7 @@ public: void *rootWindow(); void *display(); void *atspiBus(); + void *connection(); static void setStartupId(const char *); static void setAppTime(QScreen *screen, xcb_timestamp_t time); static void setAppUserTime(QScreen *screen, xcb_timestamp_t time); diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 78ced43af8..383adf9734 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -48,10 +48,15 @@ QT_BEGIN_NAMESPACE QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, - xcb_randr_get_output_info_reply_t *output, const QString &outputName, int number) + xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output, + QString outputName, int number) : QXcbObject(connection) , m_screen(scr) + , m_output(outputId) , m_crtc(output ? output->crtc : 0) + , m_mode(XCB_NONE) + , m_primary(false) + , m_rotation(XCB_RANDR_ROTATION_ROTATE_0) , m_outputName(outputName) , m_outputSizeMillimeters(output ? QSize(output->mm_width, output->mm_height) : QSize()) , m_virtualSize(scr->width_in_pixels, scr->height_in_pixels) @@ -67,11 +72,20 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, , m_antialiasingEnabled(-1) , m_xSettings(0) { - if (connection->hasXRandr()) + if (connection->hasXRandr()) { xcb_randr_select_input(xcb_connection(), screen()->root, true); - - updateGeometry(output ? output->timestamp : 0); - updateRefreshRate(); + xcb_randr_get_crtc_info_cookie_t crtcCookie = + xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, output ? output->timestamp : 0); + xcb_randr_get_crtc_info_reply_t *crtc = + xcb_randr_get_crtc_info_reply(xcb_connection(), crtcCookie, NULL); + if (crtc) { + updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation); + updateRefreshRate(crtc->mode); + free(crtc); + } + } else { + updateGeometry(output ? output->timestamp : 0); + } const int dpr = int(devicePixelRatio()); // On VNC, it can be that physical size is unknown while @@ -352,9 +366,15 @@ QPlatformCursor *QXcbScreen::cursor() const */ void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event) { - updateGeometry(change_event->config_timestamp); + // No need to do anything when screen rotation did not change - if any + // xcb output geometry has changed, we will get RRCrtcChangeNotify and + // RROutputChangeNotify events next + if (change_event->rotation == m_rotation) + return; - switch (change_event->rotation) { + m_rotation = change_event->rotation; + updateGeometry(change_event->timestamp); + switch (m_rotation) { case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal m_orientation = Qt::LandscapeOrientation; m_virtualSize.setWidth(change_event->width); @@ -406,35 +426,37 @@ void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *chan void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) { - QRect xGeometry; - QRect xAvailableGeometry; + xcb_randr_get_crtc_info_cookie_t crtcCookie = + xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, timestamp); + xcb_randr_get_crtc_info_reply_t *crtc = + xcb_randr_get_crtc_info_reply(xcb_connection(), crtcCookie, NULL); + if (crtc) { + updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation); + free(crtc); + } +} - if (connection()->hasXRandr()) { - xcb_randr_get_crtc_info_reply_t *crtc = xcb_randr_get_crtc_info_reply(xcb_connection(), - xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, timestamp), NULL); - if (crtc) { - xGeometry = QRect(crtc->x, crtc->y, crtc->width, crtc->height); - xAvailableGeometry = xGeometry; - switch (crtc->rotation) { - case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal - m_orientation = Qt::LandscapeOrientation; - m_sizeMillimeters = m_outputSizeMillimeters; - break; - case XCB_RANDR_ROTATION_ROTATE_90: // xrandr --rotate left - m_orientation = Qt::PortraitOrientation; - m_sizeMillimeters = m_outputSizeMillimeters.transposed(); - break; - case XCB_RANDR_ROTATION_ROTATE_180: // xrandr --rotate inverted - m_orientation = Qt::InvertedLandscapeOrientation; - m_sizeMillimeters = m_outputSizeMillimeters; - break; - case XCB_RANDR_ROTATION_ROTATE_270: // xrandr --rotate right - m_orientation = Qt::InvertedPortraitOrientation; - m_sizeMillimeters = m_outputSizeMillimeters.transposed(); - break; - } - free(crtc); - } +void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation) +{ + QRect xGeometry = geom; + QRect xAvailableGeometry = xGeometry; + switch (rotation) { + case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal + m_orientation = Qt::LandscapeOrientation; + m_sizeMillimeters = m_outputSizeMillimeters; + break; + case XCB_RANDR_ROTATION_ROTATE_90: // xrandr --rotate left + m_orientation = Qt::PortraitOrientation; + m_sizeMillimeters = m_outputSizeMillimeters.transposed(); + break; + case XCB_RANDR_ROTATION_ROTATE_180: // xrandr --rotate inverted + m_orientation = Qt::InvertedLandscapeOrientation; + m_sizeMillimeters = m_outputSizeMillimeters; + break; + case XCB_RANDR_ROTATION_ROTATE_270: // xrandr --rotate right + m_orientation = Qt::InvertedPortraitOrientation; + m_sizeMillimeters = m_outputSizeMillimeters.transposed(); + break; } xcb_get_property_reply_t * workArea = @@ -463,31 +485,38 @@ void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) m_geometry = QRect(xGeometry.topLeft()/dpr, xGeometry.size()/dpr); m_nativeGeometry = QRect(xGeometry.topLeft(), xGeometry.size()); m_availableGeometry = QRect(xAvailableGeometry.topLeft()/dpr, xAvailableGeometry.size()/dpr); - QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry); } -void QXcbScreen::updateRefreshRate() +void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode) { if (!connection()->hasXRandr()) return; - int rate = m_refreshRate; - - xcb_randr_get_screen_info_reply_t *screenInfoReply = - xcb_randr_get_screen_info_reply(xcb_connection(), xcb_randr_get_screen_info_unchecked(xcb_connection(), m_screen->root), 0); - - if (screenInfoReply) { - rate = screenInfoReply->rate; - free(screenInfoReply); - } - - if (rate == m_refreshRate) + if (m_mode == mode) return; - m_refreshRate = rate; + // we can safely use get_screen_resources_current here, because in order to + // get here, we must have called get_screen_resources before + xcb_randr_get_screen_resources_current_cookie_t resourcesCookie = + xcb_randr_get_screen_resources_current_unchecked(xcb_connection(), m_screen->root); + xcb_randr_get_screen_resources_current_reply_t *resources = + xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, NULL); + if (resources) { + xcb_randr_mode_info_iterator_t modesIter = + xcb_randr_get_screen_resources_current_modes_iterator(resources); + for (; modesIter.rem; xcb_randr_mode_info_next(&modesIter)) { + xcb_randr_mode_info_t *modeInfo = modesIter.data; + if (modeInfo->id == mode) { + m_refreshRate = modeInfo->dot_clock / (modeInfo->htotal * modeInfo->vtotal); + m_mode = mode; + break; + } + } - QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), rate); + free(resources); + QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), m_refreshRate); + } } QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) const @@ -703,7 +732,7 @@ Q_XCB_EXPORT QDebug operator<<(QDebug debug, const QXcbScreen *screen) { const QDebugStateSaver saver(debug); debug.nospace(); - debug << "QXcbScreen(" << (void *)screen; + debug << "QXcbScreen(" << (const void *)screen; if (screen) { debug << fixed << qSetRealNumberPrecision(1); debug << ", name=" << screen->name(); diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 53ad413541..3f228465f2 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -58,7 +58,8 @@ class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen { public: QXcbScreen(QXcbConnection *connection, xcb_screen_t *screen, - xcb_randr_get_output_info_reply_t *output, const QString &outputName, int number); + xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output, + QString outputName, int number); ~QXcbScreen(); QPixmap grabWindow(WId window, int x, int y, int width, int height) const Q_DECL_OVERRIDE; @@ -80,11 +81,19 @@ public: Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE { return m_orientation; } QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE { return m_siblings; } void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; } + void removeVirtualSibling(QPlatformScreen *s) { m_siblings.removeOne(s); } + void addVirtualSibling(QPlatformScreen *s) { ((QXcbScreen *) s)->isPrimary() ? m_siblings.prepend(s) : m_siblings.append(s); } + + void setPrimary(bool primary) { m_primary = primary; } + bool isPrimary() const { return m_primary; } int screenNumber() const { return m_number; } xcb_screen_t *screen() const { return m_screen; } xcb_window_t root() const { return m_screen->root; } + xcb_randr_output_t output() const { return m_output; } + xcb_randr_crtc_t crtc() const { return m_crtc; } + xcb_randr_mode_t mode() const { return m_mode; } xcb_window_t clientLeader() const { return m_clientLeader; } @@ -98,8 +107,9 @@ public: QString name() const Q_DECL_OVERRIDE { return m_outputName; } void handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event); - void updateGeometry(xcb_timestamp_t timestamp); - void updateRefreshRate(); + void updateGeometry(const QRect &geom, uint8_t rotation); + void updateGeometry(xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME); + void updateRefreshRate(xcb_randr_mode_t mode); void readXResources(); @@ -117,7 +127,12 @@ private: void sendStartupMessage(const QByteArray &message) const; xcb_screen_t *m_screen; + xcb_randr_output_t m_output; xcb_randr_crtc_t m_crtc; + xcb_randr_mode_t m_mode; + bool m_primary; + uint8_t m_rotation; + QString m_outputName; QSizeF m_outputSizeMillimeters; QSizeF m_sizeMillimeters; diff --git a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp index 33e67a14b1..328b72234a 100644 --- a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp +++ b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp @@ -125,7 +125,7 @@ static void sm_setProperty(const QString &name, const QString &value) QByteArray v = value.toUtf8(); SmPropValue prop; prop.length = v.length(); - prop.value = (SmPointer) v.constData(); + prop.value = (SmPointer) const_cast<char *>(v.constData()); sm_setProperty(name.toLatin1().data(), SmARRAY8, 1, &prop); } diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 6d2f5276b3..0dde679a7a 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -1372,6 +1372,8 @@ void QXcbWindow::setParent(const QPlatformWindow *parent) xcb_parent_id = qXcbParent->xcb_window(); m_embedded = qXcbParent->window()->type() == Qt::ForeignWindow; } else { + if (!xcbScreen()) + return; xcb_parent_id = xcbScreen()->root(); m_embedded = false; } |