File indexing completed on 2024-05-12 05:33:52
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"