diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index da973b531305e0071ee1eabcc5b54cebba9dea50..81d8a9b72adb0a62ef4adcfa5d4471a482c10fcc 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 @@ -1653,6 +1775,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 a3c8c0b95ee26fce0672bc4c4dfadde11ebf5911..de454b5eae280579d02dc65cb41d65a11e149927 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> @@ -492,9 +493,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/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 08108f1e204d8a6b3735354574842f6950c9c166..383adf97349fed630940c11e2c75da91f424d4be 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 diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 53ad413541ee0bb6e70ab7e247fe55c02211d8fd..3f228465f2d20086611b8579ff792e225fbc43cb 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;