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 }