File indexing completed on 2024-05-12 09:36:14

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