GCC Code Coverage Report


Directory: ./
File: backends/xrandr/xrandrconfig.cpp
Date: 2023-04-20 22:59:23
Exec Total Coverage
Lines: 0 375 0.0%
Branches: 0 326 0.0%

Line Branch Exec Source
1 /*************************************************************************************
2 * Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org> *
3 * Copyright (C) 2012 - 2015 by Daniel Vrátil <dvratil@redhat.com> *
4 * *
5 * This library is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU Lesser General Public *
7 * License as published by the Free Software Foundation; either *
8 * version 2.1 of the License, or (at your option) any later version. *
9 * *
10 * This library is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
13 * Lesser General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU Lesser General Public *
16 * License along with this library; if not, write to the Free Software *
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18 *************************************************************************************/
19 #include "xrandrconfig.h"
20 #include "xcbwrapper.h"
21 #include "xrandr.h"
22 #include "xrandrcrtc.h"
23 #include "xrandrmode.h"
24 #include "xrandroutput.h"
25 #include "xrandrscreen.h"
26
27 #include "xrandr_logging.h"
28
29 #include "config.h"
30 #include "output.h"
31
32 #include <QRect>
33
34 using namespace Disman;
35
36 XRandRConfig::XRandRConfig()
37 : QObject()
38 , m_screen(nullptr)
39 {
40 m_screen = new XRandRScreen(this);
41
42 XCB::ScopedPointer<xcb_randr_get_screen_resources_reply_t> resources(XRandR::screenResources());
43
44 xcb_randr_crtc_t* crtcs = xcb_randr_get_screen_resources_crtcs(resources.data());
45 const int crtcsCount = xcb_randr_get_screen_resources_crtcs_length(resources.data());
46 for (int i = 0; i < crtcsCount; ++i) {
47 addNewCrtc(crtcs[i]);
48 }
49
50 xcb_randr_output_t* outputs = xcb_randr_get_screen_resources_outputs(resources.data());
51 const int outputsCount = xcb_randr_get_screen_resources_outputs_length(resources.data());
52 for (int i = 0; i < outputsCount; ++i) {
53 addNewOutput(outputs[i]);
54 }
55 }
56
57 XRandRConfig::~XRandRConfig()
58 {
59 for (auto& [key, output] : m_outputs) {
60 delete output;
61 }
62 for (auto& [key, crtc] : m_crtcs) {
63 delete crtc;
64 }
65 delete m_screen;
66 }
67
68 XRandROutput::Map XRandRConfig::outputs() const
69 {
70 return m_outputs;
71 }
72
73 XRandROutput* XRandRConfig::output(xcb_randr_output_t output) const
74 {
75 if (auto it = m_outputs.find(output); it != m_outputs.end()) {
76 return it->second;
77 }
78 return nullptr;
79 }
80
81 XRandRCrtc::Map XRandRConfig::crtcs() const
82 {
83 return m_crtcs;
84 }
85
86 XRandRCrtc* XRandRConfig::crtc(xcb_randr_crtc_t crtc) const
87 {
88 if (auto it = m_crtcs.find(crtc); it != m_crtcs.end()) {
89 return it->second;
90 }
91 return nullptr;
92 }
93
94 XRandRScreen* XRandRConfig::screen() const
95 {
96 return m_screen;
97 }
98
99 void XRandRConfig::addNewOutput(xcb_randr_output_t id)
100 {
101 XRandROutput* xOutput = new XRandROutput(id, this);
102 m_outputs.insert({id, xOutput});
103 }
104
105 void XRandRConfig::addNewCrtc(xcb_randr_crtc_t crtc)
106 {
107 m_crtcs.insert({crtc, new XRandRCrtc(crtc, this)});
108 }
109
110 void XRandRConfig::removeOutput(xcb_randr_output_t id)
111 {
112 auto it = m_outputs.find(id);
113 if (it != m_outputs.end()) {
114 delete it->second;
115 m_outputs.erase(it);
116 }
117 }
118
119 Disman::ConfigPtr XRandRConfig::update_config(Disman::ConfigPtr& config) const
120 {
121 const Config::Features features = Config::Feature::Writable | Config::Feature::PrimaryDisplay
122 | Config::Feature::OutputReplication;
123 config->set_supported_features(features);
124
125 Disman::OutputMap dismanOutputs;
126
127 for (auto const& [key, output] : m_outputs) {
128 if (!output->isConnected()) {
129 continue;
130 }
131
132 Disman::OutputPtr dismanOutput;
133 if (auto existing_output = config->output(output->id())) {
134 dismanOutput = existing_output;
135 } else {
136 dismanOutput.reset(new Disman::Output);
137 }
138
139 output->updateDismanOutput(dismanOutput);
140 dismanOutputs.insert({dismanOutput->id(), dismanOutput});
141 }
142
143 config->set_outputs(dismanOutputs);
144 config->setScreen(m_screen->toDismanScreen());
145
146 return config;
147 }
148
149 bool XRandRConfig::applyDismanConfig(const Disman::ConfigPtr& config)
150 {
151 auto const dismanOutputs = config->outputs();
152
153 const QSize newScreenSize = screenSize(config);
154 const QSize currentScreenSize = m_screen->currentSize();
155
156 // When the current screen configuration is bigger than the new size (like
157 // when rotating an output), the XSetScreenSize can fail or apply the smaller
158 // size only partially, because we apply the size (we have to) before the
159 // output changes. To prevent all kinds of weird screen sizes from happening,
160 // we initially set such screen size, that it can take the current as well
161 // as the new configuration, then we apply the output changes, and finally then
162 // (if necessary) we reduce the screen size to fix the new configuration precisely.
163 const QSize intermediateScreenSize
164 = QSize(qMax(newScreenSize.width(), currentScreenSize.width()),
165 qMax(newScreenSize.height(), currentScreenSize.height()));
166
167 int neededCrtcs = 0;
168 xcb_randr_output_t primaryOutput = 0;
169 xcb_randr_output_t oldPrimaryOutput = 0;
170
171 for (auto const& [key, xrandrOutput] : m_outputs) {
172 if (xrandrOutput->isPrimary()) {
173 oldPrimaryOutput = xrandrOutput->id();
174 break;
175 }
176 }
177
178 Disman::OutputMap toDisable, toEnable, toChange;
179 auto primary = config->primary_output();
180
181 // Only set the output as primary if it is enabled.
182 if (primary && primary->enabled()) {
183 primaryOutput = primary->id();
184 }
185
186 // If there's only one output, set the output as primary.
187 if (dismanOutputs.size() == 1) {
188 auto output = dismanOutputs.begin()->second;
189 if (output->enabled()) {
190 primaryOutput = output->id();
191 }
192 }
193
194 for (auto const& [key, dismanOutput] : dismanOutputs) {
195 xcb_randr_output_t outputId = dismanOutput->id();
196 XRandROutput* currentOutput = output(outputId);
197
198 const bool currentEnabled = currentOutput->enabled();
199
200 if (!dismanOutput->enabled() && currentEnabled) {
201 toDisable.insert({outputId, dismanOutput});
202 continue;
203 } else if (dismanOutput->enabled() && !currentEnabled) {
204 toEnable.insert({outputId, dismanOutput});
205 ++neededCrtcs;
206 continue;
207 } else if (!dismanOutput->enabled() && !currentEnabled) {
208 continue;
209 }
210
211 ++neededCrtcs;
212
213 if (dismanOutput->auto_mode()->id() != currentOutput->currentModeId()) {
214 if (toChange.find(outputId) == toChange.end()) {
215 toChange.insert({outputId, dismanOutput});
216 }
217 }
218
219 if (dismanOutput->position() != currentOutput->position()) {
220 if (toChange.find(outputId) == toChange.end()) {
221 toChange.insert({outputId, dismanOutput});
222 }
223 }
224
225 if (dismanOutput->rotation() != currentOutput->rotation()) {
226 if (toChange.find(outputId) == toChange.end()) {
227 toChange.insert({outputId, dismanOutput});
228 }
229 }
230
231 if (dismanOutput->geometry().size() != currentOutput->logicalSize()) {
232 if (toChange.find(outputId) == toChange.end()) {
233 toChange.insert({outputId, dismanOutput});
234 }
235 }
236
237 auto currentMode = currentOutput->modes().at(std::stoi(dismanOutput->auto_mode()->id()));
238 // For some reason, in some environments currentMode is null
239 // which doesn't make sense because it is the *current* mode...
240 // Since we haven't been able to figure out the reason why
241 // this happens, we are adding this debug code to try to
242 // figure out how this happened.
243 if (!currentMode) {
244 qWarning() << "Current mode is null:"
245 << "ModeId:" << currentOutput->currentModeId().c_str()
246 << "Mode: " << currentOutput->currentMode()
247 << "Output: " << currentOutput->id();
248 printConfig(config);
249 printInternalCond();
250 continue;
251 }
252
253 // When the output would not fit into new screen size, we need to disable and reposition it.
254 const QRectF geom = dismanOutput->geometry();
255 if (geom.right() > newScreenSize.width() || geom.bottom() > newScreenSize.height()) {
256 if (toDisable.find(outputId) == toDisable.end()) {
257 qCDebug(DISMAN_XRANDR)
258 << "The new output would not fit into screen - new geometry: " << geom
259 << ", new screen size:" << newScreenSize;
260 toDisable.insert({outputId, dismanOutput});
261 }
262 }
263 }
264
265 const Disman::ScreenPtr dismanScreen = config->screen();
266 if (newScreenSize.width() > dismanScreen->max_size().width()
267 || newScreenSize.height() > dismanScreen->max_size().height()) {
268 qCDebug(DISMAN_XRANDR) << "The new screen size is too big - requested: " << newScreenSize
269 << ", maximum: " << dismanScreen->max_size();
270 return false;
271 }
272
273 qCDebug(DISMAN_XRANDR) << "Needed CRTCs: " << neededCrtcs;
274
275 XCB::ScopedPointer<xcb_randr_get_screen_resources_reply_t> screenResources(
276 XRandR::screenResources());
277
278 if (neededCrtcs > screenResources->num_crtcs) {
279 qCDebug(DISMAN_XRANDR) << "We need more CRTCs than we have available - requested: "
280 << neededCrtcs << ", available: " << screenResources->num_crtcs;
281 return false;
282 }
283
284 qCDebug(DISMAN_XRANDR) << "Actions to perform:";
285 qCDebug(DISMAN_XRANDR) << "\tPrimary Output:" << (primaryOutput != oldPrimaryOutput);
286 if (primaryOutput != oldPrimaryOutput) {
287 qCDebug(DISMAN_XRANDR) << "\t\tOld:" << oldPrimaryOutput << "\n"
288 << "\t\tNew:" << primaryOutput;
289 }
290
291 qCDebug(DISMAN_XRANDR) << "\tChange Screen Size:" << (newScreenSize != currentScreenSize);
292 if (newScreenSize != currentScreenSize) {
293 qCDebug(DISMAN_XRANDR) << "\t\tOld:" << currentScreenSize << "\n"
294 << "\t\tIntermediate:" << intermediateScreenSize << "\n"
295 << "\t\tNew:" << newScreenSize;
296 }
297
298 auto print_keys = [](auto const& outputs) {
299 std::string line = "\t\t";
300 for (auto const& [key, output] : outputs) {
301 line = line + " " + std::to_string(key);
302 }
303 qCDebug(DISMAN_XRANDR) << line.c_str();
304 };
305
306 qCDebug(DISMAN_XRANDR) << "\tDisable outputs:" << !toDisable.empty();
307 if (!toDisable.empty()) {
308 print_keys(toDisable);
309 }
310
311 qCDebug(DISMAN_XRANDR) << "\tChange outputs:" << !toChange.empty();
312 if (!toChange.empty()) {
313 print_keys(toChange);
314 }
315
316 qCDebug(DISMAN_XRANDR) << "\tEnable outputs:" << !toEnable.empty();
317 if (!toEnable.empty()) {
318 print_keys(toEnable);
319 }
320
321 // Grab the server so that no-one else can do changes to XRandR and to block
322 // change notifications until we are done
323 XCB::GrabServer grabber;
324
325 // If there is nothing to do, not even bother
326 if (oldPrimaryOutput == primaryOutput && toDisable.empty() && toEnable.empty()
327 && toChange.empty()) {
328 if (newScreenSize != currentScreenSize) {
329 setScreenSize(newScreenSize);
330 }
331 return false;
332 }
333
334 for (auto const& [key, output] : toDisable) {
335 disableOutput(output);
336 }
337
338 if (intermediateScreenSize != currentScreenSize) {
339 setScreenSize(intermediateScreenSize);
340 }
341
342 bool forceScreenSizeUpdate = false;
343
344 for (auto const& [key, output] : toChange) {
345 if (!changeOutput(output, output->id() == static_cast<int>(primaryOutput))) {
346 /* If we disabled the output before changing it and XRandR failed
347 * to re-enable it, then update screen size too */
348 if (toDisable.find(output->id()) != toDisable.end()) {
349 output->set_enabled(false);
350 qCDebug(DISMAN_XRANDR) << "Output failed to change: " << output->name().c_str();
351 forceScreenSizeUpdate = true;
352 }
353 }
354 }
355
356 for (auto const& [key, output] : toEnable) {
357 if (!enableOutput(output, output->id() == static_cast<int>(primaryOutput))) {
358 qCDebug(DISMAN_XRANDR) << "Output failed to be Enabled: " << output->name().c_str();
359 forceScreenSizeUpdate = true;
360 }
361 }
362
363 if (oldPrimaryOutput != primaryOutput) {
364 setPrimaryOutput(primaryOutput);
365 }
366
367 if (forceScreenSizeUpdate || intermediateScreenSize != newScreenSize) {
368 QSize newSize = newScreenSize;
369 if (forceScreenSizeUpdate) {
370 newSize = screenSize(config);
371 qCDebug(DISMAN_XRANDR) << "Forced to change screen size: " << newSize;
372 }
373 setScreenSize(newSize);
374 }
375
376 return true;
377 }
378
379 void XRandRConfig::printConfig(const ConfigPtr& config) const
380 {
381 qCDebug(DISMAN_XRANDR) << "Disman version:" /*<< DISMAN_VERSION*/;
382
383 if (!config) {
384 qCDebug(DISMAN_XRANDR) << "Config is invalid";
385 return;
386 }
387 if (!config->screen()) {
388 qCDebug(DISMAN_XRANDR) << "No screen in the configuration, broken backend";
389 return;
390 }
391
392 qCDebug(DISMAN_XRANDR) << "Screen:"
393 << "\n"
394 << "\tmax_size:" << config->screen()->max_size() << "\n"
395 << "\tmin_size:" << config->screen()->min_size() << "\n"
396 << "\tcurrent_size:" << config->screen()->current_size();
397
398 qCDebug(DISMAN_XRANDR) << "Primary output:"
399 << (config->primary_output()
400 ? std::to_string(config->primary_output()->id()).c_str()
401 : "none");
402
403 auto const outputs = config->outputs();
404 for (auto const& [key, output] : outputs) {
405 qCDebug(DISMAN_XRANDR) << "\n-----------------------------------------------------\n"
406 << "\n"
407 << "Id: " << output->id() << "\n"
408 << "Connector name: " << output->name().c_str() << "\n"
409 << "Description: " << output->description().c_str() << "\n"
410 << "Type: " << output->type();
411
412 qCDebug(DISMAN_XRANDR) << "Enabled: " << output->enabled() << "\n"
413 << "Rotation: " << output->rotation() << "\n"
414 << "Pos: " << output->position() << "\n"
415 << "Phyiscal size: " << output->physical_size();
416 if (output->auto_mode()) {
417 qCDebug(DISMAN_XRANDR) << "Size: " << output->auto_mode()->size();
418 }
419
420 qCDebug(DISMAN_XRANDR) << "Mode: " << output->auto_mode()->id().c_str() << "\n"
421 << "Preferred Mode: " << output->preferred_mode()->id().c_str()
422 << "\n"
423 << "Preferred modes:";
424 for (auto const& mode_string : output->preferred_modes()) {
425 qCDebug(DISMAN_XRANDR) << "\t" << mode_string.c_str();
426 }
427
428 qCDebug(DISMAN_XRANDR) << "Modes: ";
429 for (auto const& [key, mode] : output->modes()) {
430 qCDebug(DISMAN_XRANDR) << "\t" << mode->id().c_str() << " " << mode->name().c_str()
431 << " " << mode->size() << " " << mode->refresh();
432 }
433 }
434 }
435
436 void XRandRConfig::printInternalCond() const
437 {
438 qCDebug(DISMAN_XRANDR) << "Internal config in xrandr";
439 for (auto const& [key, output] : m_outputs) {
440 qCDebug(DISMAN_XRANDR) << "Id: " << output->id() << "\n"
441 << "Current Mode: " << output->currentMode() << "\n"
442 << "Current mode id: " << output->currentModeId().c_str() << "\n"
443 << "Connected: " << output->isConnected() << "\n"
444 << "Enabled: " << output->enabled() << "\n"
445 << "Primary: " << output->isPrimary();
446 if (!output->enabled()) {
447 continue;
448 }
449
450 XRandRMode::Map modes = output->modes();
451 for (auto const& [mode_key, mode] : modes) {
452 qCDebug(DISMAN_XRANDR) << "\t" << mode->id() << "\n"
453 << "\t" << mode->name() << "\n"
454 << "\t" << mode->size() << mode->refreshRate();
455 }
456 }
457 }
458
459 QSize XRandRConfig::screenSize(const Disman::ConfigPtr& config) const
460 {
461 QRect rect;
462 for (auto const& [key, output] : config->outputs()) {
463 if (!output->enabled()) {
464 continue;
465 }
466 if (output->replication_source()) {
467 continue;
468 }
469
470 const ModePtr currentMode = output->auto_mode();
471 if (!currentMode) {
472 qCDebug(DISMAN_XRANDR)
473 << "Output: " << output->name().c_str() << " has no current Mode!";
474 continue;
475 }
476
477 const QRect outputGeom = output->geometry().toRect();
478 rect = rect.united(outputGeom);
479 }
480
481 const QSize size = QSize(rect.width(), rect.height());
482 qCDebug(DISMAN_XRANDR) << "Requested screen size is" << size;
483 return size;
484 }
485
486 bool XRandRConfig::setScreenSize(const QSize& size) const
487 {
488 const double dpi
489 = 25.4 * XRandR::screen()->height_in_pixels / XRandR::screen()->height_in_millimeters;
490 const int widthMM = (25.4 * size.width()) / dpi;
491 const int heightMM = (25.4 * size.height()) / dpi;
492
493 qCDebug(DISMAN_XRANDR) << "RRSetScreenSize"
494 << "\n"
495 << "\tDPI:" << dpi << "\n"
496 << "\tSize:" << size << "\n"
497 << "\tPhysical size:" << QSize(widthMM, heightMM);
498
499 xcb_randr_set_screen_size(
500 XCB::connection(), XRandR::rootWindow(), size.width(), size.height(), widthMM, heightMM);
501 m_screen->update(size);
502 return true;
503 }
504
505 void XRandRConfig::setPrimaryOutput(xcb_randr_output_t outputId) const
506 {
507 qCDebug(DISMAN_XRANDR) << "RRSetOutputPrimary"
508 << "\n"
509 << "\tNew primary:" << outputId;
510 xcb_randr_set_output_primary(XCB::connection(), XRandR::rootWindow(), outputId);
511
512 for (auto const& [key, output] : m_outputs) {
513 output->setIsPrimary(output->id() == outputId);
514 }
515 }
516
517 bool XRandRConfig::disableOutput(const OutputPtr& dismanOutput) const
518 {
519 XRandROutput* xOutput = output(dismanOutput->id());
520 Q_ASSERT(xOutput);
521 Q_ASSERT(xOutput->crtc());
522
523 if (!xOutput->crtc()) {
524 qCWarning(DISMAN_XRANDR) << "Attempting to disable output without CRTC, wth?";
525 return false;
526 }
527
528 const xcb_randr_crtc_t crtc = xOutput->crtc()->crtc();
529
530 qCDebug(DISMAN_XRANDR) << "RRSetCrtcConfig (disable output)"
531 << "\n"
532 << "\tCRTC:" << crtc;
533
534 auto cookie = xcb_randr_set_crtc_config(XCB::connection(),
535 crtc,
536 XCB_CURRENT_TIME,
537 XCB_CURRENT_TIME,
538 0,
539 0,
540 XCB_NONE,
541 XCB_RANDR_ROTATION_ROTATE_0,
542 0,
543 nullptr);
544
545 XCB::ScopedPointer<xcb_randr_set_crtc_config_reply_t> reply(
546 xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr));
547
548 if (!reply) {
549 qCDebug(DISMAN_XRANDR) << "\tResult: unknown (error)";
550 return false;
551 }
552 qCDebug(DISMAN_XRANDR) << "\tResult:" << reply->status;
553
554 // Update the cached output now, otherwise we get RRNotify_CrtcChange notification
555 // for an outdated output, which can lead to a crash.
556 if (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS) {
557 xOutput->update(XCB_NONE,
558 XCB_NONE,
559 xOutput->isConnected() ? XCB_RANDR_CONNECTION_CONNECTED
560 : XCB_RANDR_CONNECTION_DISCONNECTED,
561 false);
562 }
563 return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS);
564 }
565
566 bool XRandRConfig::enableOutput(const OutputPtr& dismanOutput, bool primary) const
567 {
568 XRandRCrtc* freeCrtc = nullptr;
569 qCDebug(DISMAN_XRANDR) << m_crtcs;
570
571 for (auto const& [key, crtc] : m_crtcs) {
572 crtc->update();
573 qCDebug(DISMAN_XRANDR) << "Testing CRTC" << crtc->crtc() << "\n"
574 << "\tFree:" << crtc->isFree() << "\n"
575 << "\tMode:" << crtc->mode() << "\n"
576 << "\tPossible outputs:" << crtc->possibleOutputs() << "\n"
577 << "\tConnected outputs:" << crtc->outputs() << "\n"
578 << "\tGeometry:" << crtc->geometry();
579
580 if (crtc->isFree() && crtc->possibleOutputs().contains(dismanOutput->id())) {
581 freeCrtc = crtc;
582 break;
583 }
584 }
585
586 if (!freeCrtc) {
587 qCWarning(DISMAN_XRANDR) << "Failed to get free CRTC for output" << dismanOutput->id();
588 return false;
589 }
590
591 XRandROutput* xOutput = output(dismanOutput->id());
592 auto const modeId = std::stoi(dismanOutput->auto_mode() ? dismanOutput->auto_mode()->id()
593 : dismanOutput->preferred_mode()->id());
594 xOutput->updateLogicalSize(dismanOutput, freeCrtc);
595
596 qCDebug(DISMAN_XRANDR) << "RRSetCrtcConfig (enable output)"
597 << "\n"
598 << "\tOutput:" << dismanOutput->id() << "("
599 << dismanOutput->name().c_str() << ")"
600 << "\n"
601 << "\tNew CRTC:" << freeCrtc->crtc() << "\n"
602 << "\tPos:" << dismanOutput->position() << "\n"
603 << "\tMode:" << dismanOutput->auto_mode()
604 << "Preferred:" << dismanOutput->preferred_mode()->id().c_str() << "\n"
605 << "\tRotation:" << dismanOutput->rotation();
606
607 if (!sendConfig(dismanOutput, freeCrtc)) {
608 return false;
609 }
610
611 xOutput->update(freeCrtc->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED, primary);
612 return true;
613 }
614
615 bool XRandRConfig::changeOutput(const Disman::OutputPtr& dismanOutput, bool primary) const
616 {
617 XRandROutput* xOutput = output(dismanOutput->id());
618 Q_ASSERT(xOutput);
619
620 if (!xOutput->crtc()) {
621 qCDebug(DISMAN_XRANDR) << "Output" << dismanOutput->id()
622 << "has no CRTC, falling back to enableOutput()";
623 return enableOutput(dismanOutput, primary);
624 }
625
626 auto const modeId = std::stoi(dismanOutput->auto_mode() ? dismanOutput->auto_mode()->id()
627 : dismanOutput->preferred_mode()->id());
628 xOutput->updateLogicalSize(dismanOutput);
629
630 qCDebug(DISMAN_XRANDR) << "RRSetCrtcConfig (change output)"
631 << "\n"
632 << "\tOutput:" << dismanOutput->id() << "("
633 << dismanOutput->name().c_str() << ")"
634 << "\n"
635 << "\tCRTC:" << xOutput->crtc()->crtc() << "\n"
636 << "\tPos:" << dismanOutput->position() << "\n"
637 << "\tMode:" << modeId << dismanOutput->auto_mode() << "\n"
638 << "\tRotation:" << dismanOutput->rotation();
639
640 if (!sendConfig(dismanOutput, xOutput->crtc())) {
641 return false;
642 }
643
644 xOutput->update(xOutput->crtc()->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED, primary);
645 return true;
646 }
647
648 bool XRandRConfig::sendConfig(const Disman::OutputPtr& dismanOutput, XRandRCrtc* crtc) const
649 {
650 xcb_randr_output_t outputs[1]{static_cast<xcb_randr_output_t>(dismanOutput->id())};
651 auto const modeId = std::stoi(dismanOutput->auto_mode() ? dismanOutput->auto_mode()->id()
652 : dismanOutput->preferred_mode()->id());
653
654 auto cookie = xcb_randr_set_crtc_config(XCB::connection(),
655 crtc->crtc(),
656 XCB_CURRENT_TIME,
657 XCB_CURRENT_TIME,
658 dismanOutput->position().rx(),
659 dismanOutput->position().ry(),
660 modeId,
661 dismanOutput->rotation(),
662 1,
663 outputs);
664
665 XCB::ScopedPointer<xcb_randr_set_crtc_config_reply_t> reply(
666 xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr));
667 if (!reply) {
668 qCDebug(DISMAN_XRANDR) << "\tResult: unknown (error)";
669 return false;
670 }
671 qCDebug(DISMAN_XRANDR) << "\tResult: " << reply->status;
672 return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS);
673 }
674