File indexing completed on 2024-05-12 13:36:46

0001 /*
0002  *  SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org>
0003  *  SPDX-FileCopyrightText: 2012-2015 Daniel Vrátil <dvratil@redhat.com>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.1-or-later
0006  */
0007 #include "xrandrconfig.h"
0008 
0009 #include "xrandr.h"
0010 #include "xrandrmode.h"
0011 #include "xrandrscreen.h"
0012 
0013 #include "../xcbwrapper.h"
0014 
0015 #include "config.h"
0016 #include "edid.h"
0017 #include "mode.h"
0018 #include "output.h"
0019 #include "screen.h"
0020 
0021 #include <QRect>
0022 #include <QScopedPointer>
0023 #include <optional>
0024 #include <utility>
0025 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0026 #include <private/qtx11extras_p.h>
0027 #else
0028 #include <QX11Info>
0029 #endif
0030 
0031 using namespace KScreen;
0032 
0033 XRandRConfig::XRandRConfig()
0034     : QObject()
0035     , m_screen(nullptr)
0036 {
0037     m_screen = new XRandRScreen(this);
0038 
0039     XCB::ScopedPointer<xcb_randr_get_screen_resources_reply_t> resources(XRandR::screenResources());
0040 
0041     xcb_randr_crtc_t *crtcs = xcb_randr_get_screen_resources_crtcs(resources.data());
0042     const int crtcsCount = xcb_randr_get_screen_resources_crtcs_length(resources.data());
0043     for (int i = 0; i < crtcsCount; ++i) {
0044         addNewCrtc(crtcs[i]);
0045     }
0046 
0047     xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(resources.data());
0048     const int outputsCount = xcb_randr_get_screen_resources_outputs_length(resources.data());
0049     for (int i = 0; i < outputsCount; ++i) {
0050         addNewOutput(outputs[i]);
0051     }
0052 }
0053 
0054 XRandRConfig::~XRandRConfig()
0055 {
0056     qDeleteAll(m_outputs);
0057     qDeleteAll(m_crtcs);
0058     delete m_screen;
0059 }
0060 
0061 XRandROutput::Map XRandRConfig::outputs() const
0062 {
0063     return m_outputs;
0064 }
0065 
0066 XRandROutput *XRandRConfig::output(xcb_randr_output_t output) const
0067 {
0068     return m_outputs[output];
0069 }
0070 
0071 XRandRCrtc::Map XRandRConfig::crtcs() const
0072 {
0073     return m_crtcs;
0074 }
0075 
0076 XRandRCrtc *XRandRConfig::crtc(xcb_randr_crtc_t crtc) const
0077 {
0078     return m_crtcs[crtc];
0079 }
0080 
0081 XRandRScreen *XRandRConfig::screen() const
0082 {
0083     return m_screen;
0084 }
0085 
0086 void XRandRConfig::addNewOutput(xcb_randr_output_t id)
0087 {
0088     XRandROutput *xOutput = new XRandROutput(id, this);
0089     m_outputs.insert(id, xOutput);
0090 }
0091 
0092 void XRandRConfig::addNewCrtc(xcb_randr_crtc_t crtc)
0093 {
0094     m_crtcs.insert(crtc, new XRandRCrtc(crtc, this));
0095 }
0096 
0097 void XRandRConfig::removeOutput(xcb_randr_output_t id)
0098 {
0099     delete m_outputs.take(id);
0100 }
0101 
0102 KScreen::ConfigPtr XRandRConfig::toKScreenConfig() const
0103 {
0104     KScreen::ConfigPtr config(new KScreen::Config);
0105 
0106     const Config::Features features = Config::Feature::Writable | Config::Feature::PrimaryDisplay | Config::Feature::OutputReplication;
0107     config->setSupportedFeatures(features);
0108 
0109     KScreen::OutputList kscreenOutputs;
0110 
0111     for (const XRandROutput *output : std::as_const(m_outputs)) {
0112         KScreen::OutputPtr kscreenOutput = output->toKScreenOutput();
0113         kscreenOutputs.insert(kscreenOutput->id(), kscreenOutput);
0114     }
0115 
0116     config->setOutputs(kscreenOutputs);
0117     config->setScreen(m_screen->toKScreenScreen());
0118 
0119     return config;
0120 }
0121 
0122 void XRandRConfig::applyKScreenConfig(const KScreen::ConfigPtr &config)
0123 {
0124     config->adjustPriorities(); // never trust input
0125 
0126     const KScreen::OutputList kscreenOutputs = config->outputs();
0127 
0128     const QSize newScreenSize = screenSize(config);
0129     const QSize currentScreenSize = m_screen->currentSize();
0130 
0131     // When the current screen configuration is bigger than the new size (like
0132     // when rotating an output), the XSetScreenSize can fail or apply the smaller
0133     // size only partially, because we apply the size (we have to) before the
0134     // output changes. To prevent all kinds of weird screen sizes from happening,
0135     // we initially set such screen size, that it can take the current as well
0136     // as the new configuration, then we apply the output changes, and finally then
0137     // (if necessary) we reduce the screen size to fix the new configuration precisely.
0138     const QSize intermediateScreenSize =
0139         QSize(qMax(newScreenSize.width(), currentScreenSize.width()), qMax(newScreenSize.height(), currentScreenSize.height()));
0140 
0141     int neededCrtcs = 0;
0142 
0143     // pairs of before/after
0144     QMap<xcb_randr_output_t, std::pair<std::optional<uint32_t>, std::optional<uint32_t>>> prioritiesChange;
0145     for (const XRandROutput *xrandrOutput : std::as_const(m_outputs)) {
0146         prioritiesChange[xrandrOutput->id()].first = std::optional(xrandrOutput->priority());
0147     }
0148     for (const KScreen::OutputPtr &kscreenOutput : kscreenOutputs) {
0149         prioritiesChange[kscreenOutput->id()].second = std::optional(kscreenOutput->priority());
0150     }
0151     const bool prioritiesDiffer = std::any_of(prioritiesChange.cbegin(), prioritiesChange.cend(), [](const auto &pair) {
0152         const auto &[before, after] = pair;
0153         return !before.has_value() || !after.has_value() || before.value() != after.value();
0154     });
0155 
0156     KScreen::OutputList toDisable, toEnable, toChange;
0157 
0158     for (const KScreen::OutputPtr &kscreenOutput : kscreenOutputs) {
0159         xcb_randr_output_t outputId = kscreenOutput->id();
0160         XRandROutput *currentOutput = output(outputId);
0161 
0162         const bool currentEnabled = currentOutput->isEnabled();
0163 
0164         if (!kscreenOutput->isEnabled() && currentEnabled) {
0165             toDisable.insert(outputId, kscreenOutput);
0166             continue;
0167         } else if (kscreenOutput->isEnabled() && !currentEnabled) {
0168             toEnable.insert(outputId, kscreenOutput);
0169             ++neededCrtcs;
0170             continue;
0171         } else if (!kscreenOutput->isEnabled() && !currentEnabled) {
0172             continue;
0173         }
0174 
0175         ++neededCrtcs;
0176 
0177         if (kscreenOutput->currentModeId() != currentOutput->currentModeId()) {
0178             if (!toChange.contains(outputId)) {
0179                 toChange.insert(outputId, kscreenOutput);
0180             }
0181         }
0182 
0183         if (kscreenOutput->pos() != currentOutput->position()) {
0184             if (!toChange.contains(outputId)) {
0185                 toChange.insert(outputId, kscreenOutput);
0186             }
0187         }
0188 
0189         if (kscreenOutput->rotation() != currentOutput->rotation()) {
0190             if (!toChange.contains(outputId)) {
0191                 toChange.insert(outputId, kscreenOutput);
0192             }
0193         }
0194 
0195         if (kscreenOutput->explicitLogicalSize() != currentOutput->logicalSize()) {
0196             if (!toChange.contains(outputId)) {
0197                 toChange.insert(outputId, kscreenOutput);
0198             }
0199         }
0200 
0201         XRandRMode *currentMode = currentOutput->modes().value(kscreenOutput->currentModeId().toInt());
0202         // For some reason, in some environments currentMode is null
0203         // which doesn't make sense because it is the *current* mode...
0204         // Since we haven't been able to figure out the reason why
0205         // this happens, we are adding this debug code to try to
0206         // figure out how this happened.
0207         if (!currentMode) {
0208             qWarning() << "Current mode is null:"
0209                        << "ModeId:" << currentOutput->currentModeId() << "Mode: " << currentOutput->currentMode() << "Output: " << currentOutput->id();
0210             printConfig(config);
0211             printInternalCond();
0212             continue;
0213         }
0214 
0215         // When the output would not fit into new screen size, we need to disable and reposition it.
0216         const QRect geom = kscreenOutput->geometry();
0217         if (geom.right() > newScreenSize.width() || geom.bottom() > newScreenSize.height()) {
0218             if (!toDisable.contains(outputId)) {
0219                 qCDebug(KSCREEN_XRANDR) << "The new output would not fit into screen - new geometry: " << geom << ", new screen size:" << newScreenSize;
0220                 toDisable.insert(outputId, kscreenOutput);
0221             }
0222         }
0223     }
0224 
0225     const KScreen::ScreenPtr kscreenScreen = config->screen();
0226     if (newScreenSize.width() > kscreenScreen->maxSize().width() || newScreenSize.height() > kscreenScreen->maxSize().height()) {
0227         qCDebug(KSCREEN_XRANDR) << "The new screen size is too big - requested: " << newScreenSize << ", maximum: " << kscreenScreen->maxSize();
0228         return;
0229     }
0230 
0231     qCDebug(KSCREEN_XRANDR) << "Needed CRTCs: " << neededCrtcs;
0232 
0233     XCB::ScopedPointer<xcb_randr_get_screen_resources_reply_t> screenResources(XRandR::screenResources());
0234 
0235     if (neededCrtcs > screenResources->num_crtcs) {
0236         qCDebug(KSCREEN_XRANDR) << "We need more CRTCs than we have available - requested: " << neededCrtcs << ", available: " << screenResources->num_crtcs;
0237         return;
0238     }
0239 
0240     qCDebug(KSCREEN_XRANDR) << "Actions to perform:\n"
0241                             << "\tPriorities:" << prioritiesDiffer;
0242     for (auto it = prioritiesChange.constBegin(); it != prioritiesChange.constEnd(); it++) {
0243         const auto &[before, after] = it.value();
0244         if (before != after) {
0245             qCDebug(KSCREEN_XRANDR) << "\tOutput" << it.key() << "\n"
0246                                     << "\t\tOld:" << (before.has_value() ? QString::number(before.value()) : QStringLiteral("none")) << "\n"
0247                                     << "\t\tNew:" << (after.has_value() ? QString::number(after.value()) : QStringLiteral("none"));
0248         }
0249     }
0250 
0251     qCDebug(KSCREEN_XRANDR) << "\tChange Screen Size:" << (newScreenSize != currentScreenSize);
0252     if (newScreenSize != currentScreenSize) {
0253         qCDebug(KSCREEN_XRANDR) << "\t\tOld:" << currentScreenSize << "\n"
0254                                 << "\t\tIntermediate:" << intermediateScreenSize << "\n"
0255                                 << "\t\tNew:" << newScreenSize;
0256     }
0257 
0258     qCDebug(KSCREEN_XRANDR) << "\tDisable outputs:" << !toDisable.isEmpty();
0259     if (!toDisable.isEmpty()) {
0260         qCDebug(KSCREEN_XRANDR) << "\t\t" << toDisable.keys();
0261     }
0262 
0263     qCDebug(KSCREEN_XRANDR) << "\tChange outputs:" << !toChange.isEmpty();
0264     if (!toChange.isEmpty()) {
0265         qCDebug(KSCREEN_XRANDR) << "\t\t" << toChange.keys();
0266     }
0267 
0268     qCDebug(KSCREEN_XRANDR) << "\tEnable outputs:" << !toEnable.isEmpty();
0269     if (!toEnable.isEmpty()) {
0270         qCDebug(KSCREEN_XRANDR) << "\t\t" << toEnable.keys();
0271     }
0272 
0273     // Grab the server so that no-one else can do changes to XRandR and to block
0274     // change notifications until we are done
0275     XCB::GrabServer grabber;
0276 
0277     // If there is nothing to do, not even bother
0278     if (!prioritiesDiffer && toDisable.isEmpty() && toEnable.isEmpty() && toChange.isEmpty()) {
0279         if (newScreenSize != currentScreenSize) {
0280             setScreenSize(newScreenSize);
0281         }
0282         return;
0283     }
0284 
0285     for (const KScreen::OutputPtr &output : toDisable) {
0286         disableOutput(output);
0287     }
0288 
0289     if (intermediateScreenSize != currentScreenSize) {
0290         setScreenSize(intermediateScreenSize);
0291     }
0292 
0293     bool forceScreenSizeUpdate = false;
0294 
0295     for (const KScreen::OutputPtr &output : toChange) {
0296         if (!changeOutput(output)) {
0297             /* If we disabled the output before changing it and XRandR failed
0298              * to re-enable it, then update screen size too */
0299             if (toDisable.contains(output->id())) {
0300                 output->setEnabled(false);
0301                 qCDebug(KSCREEN_XRANDR) << "Output failed to change: " << output->name();
0302                 forceScreenSizeUpdate = true;
0303             }
0304         }
0305     }
0306 
0307     for (const KScreen::OutputPtr &output : toEnable) {
0308         if (!enableOutput(output)) {
0309             qCDebug(KSCREEN_XRANDR) << "Output failed to be Enabled: " << output->name();
0310             forceScreenSizeUpdate = true;
0311         }
0312     }
0313 
0314     for (auto it = prioritiesChange.constBegin(); it != prioritiesChange.constEnd(); it++) {
0315         const xcb_randr_output_t outputId = it.key();
0316         const auto &[before, after] = it.value();
0317         const uint32_t priority = after.value_or(0);
0318         setOutputPriority(outputId, priority);
0319     }
0320 
0321     if (forceScreenSizeUpdate || intermediateScreenSize != newScreenSize) {
0322         QSize newSize = newScreenSize;
0323         if (forceScreenSizeUpdate) {
0324             newSize = screenSize(config);
0325             qCDebug(KSCREEN_XRANDR) << "Forced to change screen size: " << newSize;
0326         }
0327         setScreenSize(newSize);
0328     }
0329 }
0330 
0331 void XRandRConfig::printConfig(const ConfigPtr &config) const
0332 {
0333     qCDebug(KSCREEN_XRANDR) << "KScreen version:" /*<< LIBKSCREEN_VERSION*/;
0334 
0335     if (!config) {
0336         qCDebug(KSCREEN_XRANDR) << "Config is invalid";
0337         return;
0338     }
0339     if (!config->screen()) {
0340         qCDebug(KSCREEN_XRANDR) << "No screen in the configuration, broken backend";
0341         return;
0342     }
0343 
0344     qCDebug(KSCREEN_XRANDR) << "Screen:"
0345                             << "\n"
0346                             << "\tmaxSize:" << config->screen()->maxSize() << "\n"
0347                             << "\tminSize:" << config->screen()->minSize() << "\n"
0348                             << "\tcurrentSize:" << config->screen()->currentSize();
0349 
0350     const OutputList outputs = config->outputs();
0351     for (const OutputPtr &output : outputs) {
0352         qCDebug(KSCREEN_XRANDR) << "\n-----------------------------------------------------\n"
0353                                 << "\n"
0354                                 << "Id: " << output->id() << "\n"
0355                                 << "Name: " << output->name() << "\n"
0356                                 << "Type: " << output->type() << "\n"
0357                                 << "Connected: " << output->isConnected();
0358 
0359         if (!output->isConnected()) {
0360             continue;
0361         }
0362 
0363         qCDebug(KSCREEN_XRANDR) << "Enabled: " << output->isEnabled() << "\n"
0364                                 << "Priority: " << output->priority() << "\n"
0365                                 << "Rotation: " << output->rotation() << "\n"
0366                                 << "Pos: " << output->pos() << "\n"
0367                                 << "MMSize: " << output->sizeMm();
0368         if (output->currentMode()) {
0369             qCDebug(KSCREEN_XRANDR) << "Size: " << output->currentMode()->size();
0370         }
0371 
0372         qCDebug(KSCREEN_XRANDR) << "Clones: " << (output->clones().isEmpty() ? QStringLiteral("None") : QString::number(output->clones().count())) << "\n"
0373                                 << "Mode: " << output->currentModeId() << "\n"
0374                                 << "Preferred Mode: " << output->preferredModeId() << "\n"
0375                                 << "Preferred modes: " << output->preferredModes() << "\n"
0376                                 << "Modes: ";
0377 
0378         ModeList modes = output->modes();
0379         for (const ModePtr &mode : modes) {
0380             qCDebug(KSCREEN_XRANDR) << "\t" << mode->id() << "  " << mode->name() << " " << mode->size() << " " << mode->refreshRate();
0381         }
0382 
0383         Edid *edid = output->edid();
0384         qCDebug(KSCREEN_XRANDR) << "EDID Info: ";
0385         if (edid && edid->isValid()) {
0386             qCDebug(KSCREEN_XRANDR) << "\tDevice ID: " << edid->deviceId() << "\n"
0387                                     << "\tName: " << edid->name() << "\n"
0388                                     << "\tVendor: " << edid->vendor() << "\n"
0389                                     << "\tSerial: " << edid->serial() << "\n"
0390                                     << "\tEISA ID: " << edid->eisaId() << "\n"
0391                                     << "\tHash: " << edid->hash() << "\n"
0392                                     << "\tWidth: " << edid->width() << "\n"
0393                                     << "\tHeight: " << edid->height() << "\n"
0394                                     << "\tGamma: " << edid->gamma() << "\n"
0395                                     << "\tRed: " << edid->red() << "\n"
0396                                     << "\tGreen: " << edid->green() << "\n"
0397                                     << "\tBlue: " << edid->blue() << "\n"
0398                                     << "\tWhite: " << edid->white();
0399         } else {
0400             qCDebug(KSCREEN_XRANDR) << "\tUnavailable";
0401         }
0402     }
0403 }
0404 
0405 void XRandRConfig::printInternalCond() const
0406 {
0407     qCDebug(KSCREEN_XRANDR) << "Internal config in xrandr";
0408     for (const XRandROutput *output : m_outputs) {
0409         qCDebug(KSCREEN_XRANDR) << "Id: " << output->id() << "\n"
0410                                 << "Current Mode: " << output->currentMode() << "\n"
0411                                 << "Current mode id: " << output->currentModeId() << "\n"
0412                                 << "Connected: " << output->isConnected() << "\n"
0413                                 << "Enabled: " << output->isEnabled() << "\n"
0414                                 << "Priority: " << output->priority();
0415         if (!output->isEnabled()) {
0416             continue;
0417         }
0418 
0419         XRandRMode::Map modes = output->modes();
0420         for (const XRandRMode *mode : modes) {
0421             qCDebug(KSCREEN_XRANDR) << "\t" << mode->id() << "\n"
0422                                     << "\t" << mode->name() << "\n"
0423                                     << "\t" << mode->size() << mode->refreshRate();
0424         }
0425     }
0426 }
0427 
0428 QSize XRandRConfig::screenSize(const KScreen::ConfigPtr &config) const
0429 {
0430     QRect rect;
0431     for (const KScreen::OutputPtr &output : config->outputs()) {
0432         if (!output->isConnected() || !output->isEnabled()) {
0433             continue;
0434         }
0435 
0436         const ModePtr currentMode = output->currentMode();
0437         if (!currentMode) {
0438             qCDebug(KSCREEN_XRANDR) << "Output: " << output->name() << " has no current Mode!";
0439             continue;
0440         }
0441 
0442         const QRect outputGeom = output->geometry();
0443         rect = rect.united(outputGeom);
0444     }
0445 
0446     const QSize size = QSize(rect.width(), rect.height());
0447     qCDebug(KSCREEN_XRANDR) << "Requested screen size is" << size;
0448     return size;
0449 }
0450 
0451 bool XRandRConfig::setScreenSize(const QSize &size) const
0452 {
0453     const double dpi = 25.4 * XRandR::screen()->height_in_pixels / XRandR::screen()->height_in_millimeters;
0454     const int widthMM = (25.4 * size.width()) / dpi;
0455     const int heightMM = (25.4 * size.height()) / dpi;
0456 
0457     qCDebug(KSCREEN_XRANDR) << "RRSetScreenSize"
0458                             << "\n"
0459                             << "\tDPI:" << dpi << "\n"
0460                             << "\tSize:" << size << "\n"
0461                             << "\tSizeMM:" << QSize(widthMM, heightMM);
0462 
0463     xcb_randr_set_screen_size(XCB::connection(), XRandR::rootWindow(), size.width(), size.height(), widthMM, heightMM);
0464     m_screen->update(size);
0465     return true;
0466 }
0467 
0468 void XRandRConfig::setOutputPriority(xcb_randr_output_t outputId, uint32_t priority) const
0469 {
0470     qCDebug(KSCREEN_XRANDR) << "RRSetOutputPrimary"
0471                             << "\n"
0472                             << "\tNew priority:" << priority;
0473 
0474     if (m_outputs.contains(outputId)) {
0475         m_outputs[outputId]->setPriority(priority);
0476     }
0477 }
0478 
0479 bool XRandRConfig::disableOutput(const OutputPtr &kscreenOutput) const
0480 {
0481     XRandROutput *xOutput = output(kscreenOutput->id());
0482     Q_ASSERT(xOutput);
0483     Q_ASSERT(xOutput->crtc());
0484 
0485     if (!xOutput->crtc()) {
0486         qCWarning(KSCREEN_XRANDR) << "Attempting to disable output without CRTC, wth?";
0487         return false;
0488     }
0489 
0490     const xcb_randr_crtc_t crtc = xOutput->crtc()->crtc();
0491 
0492     qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (disable output)"
0493                             << "\n"
0494                             << "\tCRTC:" << crtc;
0495 
0496     xOutput->setPriority(0);
0497 
0498     auto cookie = xcb_randr_set_crtc_config(XCB::connection(), //
0499                                             crtc,
0500                                             XCB_CURRENT_TIME,
0501                                             XCB_CURRENT_TIME,
0502                                             0,
0503                                             0,
0504                                             XCB_NONE,
0505                                             XCB_RANDR_ROTATION_ROTATE_0,
0506                                             0,
0507                                             nullptr);
0508 
0509     XCB::ScopedPointer<xcb_randr_set_crtc_config_reply_t> reply(xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr));
0510 
0511     if (!reply) {
0512         qCDebug(KSCREEN_XRANDR) << "\tResult: unknown (error)";
0513         return false;
0514     }
0515     qCDebug(KSCREEN_XRANDR) << "\tResult:" << reply->status;
0516 
0517     // Update the cached output now, otherwise we get RRNotify_CrtcChange notification
0518     // for an outdated output, which can lead to a crash.
0519     if (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS) {
0520         xOutput->update(XCB_NONE, XCB_NONE, xOutput->isConnected() ? XCB_RANDR_CONNECTION_CONNECTED : XCB_RANDR_CONNECTION_DISCONNECTED);
0521         if (xOutput->crtc())
0522             xOutput->crtc()->updateTimestamp(reply->timestamp);
0523     }
0524     return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS);
0525 }
0526 
0527 bool XRandRConfig::enableOutput(const OutputPtr &kscreenOutput) const
0528 {
0529     XRandRCrtc *freeCrtc = nullptr;
0530     qCDebug(KSCREEN_XRANDR) << m_crtcs;
0531 
0532     for (XRandRCrtc *crtc : m_crtcs) {
0533         crtc->update();
0534         qCDebug(KSCREEN_XRANDR) << "Testing CRTC" << crtc->crtc() << "\n"
0535                                 << "\tFree:" << crtc->isFree() << "\n"
0536                                 << "\tMode:" << crtc->mode() << "\n"
0537                                 << "\tPossible outputs:" << crtc->possibleOutputs() << "\n"
0538                                 << "\tConnected outputs:" << crtc->outputs() << "\n"
0539                                 << "\tGeometry:" << crtc->geometry();
0540 
0541         if (crtc->isFree() && crtc->possibleOutputs().contains(kscreenOutput->id())) {
0542             freeCrtc = crtc;
0543             break;
0544         }
0545     }
0546 
0547     if (!freeCrtc) {
0548         qCWarning(KSCREEN_XRANDR) << "Failed to get free CRTC for output" << kscreenOutput->id();
0549         return false;
0550     }
0551 
0552     XRandROutput *xOutput = output(kscreenOutput->id());
0553     const int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : kscreenOutput->preferredModeId().toInt();
0554     xOutput->updateLogicalSize(kscreenOutput, freeCrtc);
0555 
0556     qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (enable output)"
0557                             << "\n"
0558                             << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() << ")"
0559                             << "\n"
0560                             << "\tNew CRTC:" << freeCrtc->crtc() << "\n"
0561                             << "\tPos:" << kscreenOutput->pos() << "\n"
0562                             << "\tMode:" << kscreenOutput->currentMode() << "Preferred:" << kscreenOutput->preferredModeId() << "\n"
0563                             << "\tRotation:" << kscreenOutput->rotation();
0564 
0565     if (!sendConfig(kscreenOutput, freeCrtc)) {
0566         return false;
0567     }
0568 
0569     xOutput->update(freeCrtc->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED);
0570     xOutput->setPriority(kscreenOutput->priority());
0571     return true;
0572 }
0573 
0574 bool XRandRConfig::changeOutput(const KScreen::OutputPtr &kscreenOutput) const
0575 {
0576     XRandROutput *xOutput = output(kscreenOutput->id());
0577     Q_ASSERT(xOutput);
0578 
0579     if (!xOutput->crtc()) {
0580         qCDebug(KSCREEN_XRANDR) << "Output" << kscreenOutput->id() << "has no CRTC, falling back to enableOutput()";
0581         return enableOutput(kscreenOutput);
0582     }
0583 
0584     int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : kscreenOutput->preferredModeId().toInt();
0585     xOutput->updateLogicalSize(kscreenOutput);
0586 
0587     qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (change output)"
0588                             << "\n"
0589                             << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() << ")"
0590                             << "\n"
0591                             << "\tCRTC:" << xOutput->crtc()->crtc() << "\n"
0592                             << "\tPos:" << kscreenOutput->pos() << "\n"
0593                             << "\tMode:" << kscreenOutput->currentMode() << "Preferred:" << kscreenOutput->preferredModeId() << "\n"
0594                             << "\tRotation:" << kscreenOutput->rotation();
0595 
0596     if (!sendConfig(kscreenOutput, xOutput->crtc())) {
0597         return false;
0598     }
0599 
0600     xOutput->update(xOutput->crtc()->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED);
0601     xOutput->setPriority(kscreenOutput->priority());
0602     return true;
0603 }
0604 
0605 bool XRandRConfig::sendConfig(const KScreen::OutputPtr &kscreenOutput, XRandRCrtc *crtc) const
0606 {
0607     xcb_randr_output_t outputs[1]{static_cast<xcb_randr_output_t>(kscreenOutput->id())};
0608     const int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : kscreenOutput->preferredModeId().toInt();
0609 
0610     auto cookie = xcb_randr_set_crtc_config(XCB::connection(),
0611                                             crtc->crtc(),
0612                                             XCB_CURRENT_TIME,
0613                                             XCB_CURRENT_TIME,
0614                                             kscreenOutput->pos().rx(),
0615                                             kscreenOutput->pos().ry(),
0616                                             modeId,
0617                                             kscreenOutput->rotation(),
0618                                             1,
0619                                             outputs);
0620 
0621     XCB::ScopedPointer<xcb_randr_set_crtc_config_reply_t> reply(xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr));
0622     if (!reply) {
0623         qCDebug(KSCREEN_XRANDR) << "\tResult: unknown (error)";
0624         return false;
0625     }
0626 
0627     crtc->updateTimestamp(reply->timestamp);
0628 
0629     qCDebug(KSCREEN_XRANDR) << "\tResult: " << reply->status << " timestamp: " << reply->timestamp;
0630     return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS);
0631 }