1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#include "qxcbconnection.h"
4#include "qxcbscreen.h"
5#include "qxcbintegration.h"
6
7#include <QtGui/private/qhighdpiscaling_p.h>
8#include <QtCore/QString>
9#include <QtCore/QList>
10
11#include <qpa/qwindowsysteminterface.h>
12
13void QXcbConnection::xrandrSelectEvents()
14{
15 xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(R: setup());
16 for (; rootIter.rem; xcb_screen_next(i: &rootIter)) {
17 xcb_randr_select_input(c: xcb_connection(),
18 window: rootIter.data->root,
19 enable: XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
20 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
21 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
22 XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY
23 );
24 }
25}
26
27QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const
28{
29 for (QXcbScreen *screen : m_screens) {
30 if (screen->root() == rootWindow) {
31 if (screen->m_monitor) {
32 if (screen->crtcs().contains(t: crtc))
33 return screen;
34 } else {
35 if (screen->crtc() == crtc)
36 return screen;
37 }
38 }
39 }
40
41 return nullptr;
42}
43
44QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const
45{
46 for (QXcbScreen *screen : m_screens) {
47 if (screen->root() == rootWindow) {
48 if (screen->m_monitor) {
49 if (screen->outputs().contains(t: output))
50 return screen;
51 } else {
52 if (screen->output() == output)
53 return screen;
54 }
55 }
56 }
57
58 return nullptr;
59}
60
61QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow) const
62{
63 for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
64 if (virtualDesktop->screen()->root == rootWindow)
65 return virtualDesktop;
66 }
67
68 return nullptr;
69}
70
71/*!
72 \brief Synchronizes the screen list, adds new screens, removes deleted ones
73*/
74void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
75{
76 if (event->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) {
77 xcb_randr_crtc_change_t crtc = event->u.cc;
78 QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(rootWindow: crtc.window);
79 if (!virtualDesktop)
80 // Not for us
81 return;
82
83 QXcbScreen *screen = findScreenForCrtc(rootWindow: crtc.window, crtc: crtc.crtc);
84 qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc
85 << "mode" << crtc.mode << "relevant screen" << screen;
86 // Only update geometry when there's a valid mode on the CRTC
87 // CRTC with node mode could mean that output has been disabled, and we'll
88 // get RRNotifyOutputChange notification for that.
89 if (screen && crtc.mode) {
90 if (crtc.rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
91 crtc.rotation == XCB_RANDR_ROTATION_ROTATE_270)
92 std::swap(a&: crtc.width, b&: crtc.height);
93 screen->updateGeometry(geometry: QRect(crtc.x, crtc.y, crtc.width, crtc.height), rotation: crtc.rotation);
94 if (screen->mode() != crtc.mode)
95 screen->updateRefreshRate(mode: crtc.mode);
96 }
97
98 } else if (event->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) {
99 xcb_randr_output_change_t output = event->u.oc;
100 QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(rootWindow: output.window);
101 if (!virtualDesktop)
102 // Not for us
103 return;
104
105 QXcbScreen *screen = findScreenForOutput(rootWindow: output.window, output: output.output);
106 qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_OUTPUT_CHANGE:" << output.output;
107
108 if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) {
109 qCDebug(lcQpaScreen) << "screen" << screen->name() << "has been disconnected";
110 destroyScreen(screen);
111 } else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
112 // New XRandR output is available and it's enabled
113 if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
114 auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
115 output.output, output.config_timestamp);
116 // Find a fake screen
117 const auto scrs = virtualDesktop->screens();
118 for (QPlatformScreen *scr : scrs) {
119 QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(scr);
120 if (xcbScreen->output() == XCB_NONE) {
121 screen = xcbScreen;
122 break;
123 }
124 }
125
126 if (screen) {
127 QString nameWas = screen->name();
128 // Transform the fake screen into a physical screen
129 screen->setOutput(outputId: output.output, outputInfo: outputInfo.get());
130 updateScreen(screen, outputChange: output);
131 qCDebug(lcQpaScreen) << "output" << screen->name()
132 << "is connected and enabled; was fake:" << nameWas;
133 } else {
134 screen = createScreen(virtualDesktop, outputChange: output, outputInfo: outputInfo.get());
135 qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled";
136 }
137 }
138 } else if (screen) {
139 if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
140 // Screen has been disabled
141 auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
142 output.output, output.config_timestamp);
143 if (outputInfo->crtc == XCB_NONE) {
144 qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled";
145 destroyScreen(screen);
146 } else {
147 qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch";
148 // Reset crtc to skip RRCrtcChangeNotify events,
149 // because they may be invalid in the middle of the mode switch
150 screen->setCrtc(XCB_NONE);
151 }
152 } else {
153 updateScreen(screen, outputChange: output);
154 qCDebug(lcQpaScreen) << "output has changed" << screen;
155 }
156 }
157
158 qCDebug(lcQpaScreen) << "updateScreens: primary output is" << std::as_const(t&: m_screens).first()->name();
159 }
160}
161
162bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
163{
164 auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), rootWindow);
165 if (!primary)
166 qWarning(msg: "failed to get the primary output of the screen");
167
168 const bool isPrimary = primary ? (primary->output == output) : false;
169
170 return isPrimary;
171}
172
173void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange)
174{
175 screen->setCrtc(outputChange.crtc); // Set the new crtc, because it can be invalid
176 screen->updateGeometry(timestamp: outputChange.config_timestamp);
177 if (screen->mode() != outputChange.mode)
178 screen->updateRefreshRate(mode: outputChange.mode);
179 // Only screen which belongs to the primary virtual desktop can be a primary screen
180 if (screen->screenNumber() == primaryScreenNumber()) {
181 if (!screen->isPrimary() && checkOutputIsPrimary(rootWindow: outputChange.window, output: outputChange.output)) {
182 screen->setPrimary(true);
183
184 // If the screen became primary, reshuffle the order in QGuiApplicationPrivate
185 const int idx = m_screens.indexOf(t: screen);
186 if (idx > 0) {
187 std::as_const(t&: m_screens).first()->setPrimary(false);
188 m_screens.swapItemsAt(i: 0, j: idx);
189 }
190 screen->virtualDesktop()->setPrimaryScreen(screen);
191 QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary: screen);
192 }
193 }
194}
195
196QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop,
197 const xcb_randr_output_change_t &outputChange,
198 xcb_randr_get_output_info_reply_t *outputInfo)
199{
200 QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputChange.output, outputInfo);
201 // Only screen which belongs to the primary virtual desktop can be a primary screen
202 if (screen->screenNumber() == primaryScreenNumber())
203 screen->setPrimary(checkOutputIsPrimary(rootWindow: outputChange.window, output: outputChange.output));
204
205 if (screen->isPrimary()) {
206 if (!m_screens.isEmpty())
207 std::as_const(t&: m_screens).first()->setPrimary(false);
208
209 m_screens.prepend(t: screen);
210 } else {
211 m_screens.append(t: screen);
212 }
213 virtualDesktop->addScreen(s: screen);
214 QWindowSystemInterface::handleScreenAdded(screen, isPrimary: screen->isPrimary());
215
216 return screen;
217}
218
219void QXcbConnection::destroyScreen(QXcbScreen *screen)
220{
221 QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop();
222 if (virtualDesktop->screens().size() == 1) {
223 // If there are no other screens on the same virtual desktop,
224 // then transform the physical screen into a fake screen.
225 const QString nameWas = screen->name();
226 screen->setOutput(XCB_NONE, outputInfo: nullptr);
227 qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen;
228 } else {
229 // There is more than one screen on the same virtual desktop, remove the screen
230 m_screens.removeOne(t: screen);
231 virtualDesktop->removeScreen(s: screen);
232
233 // When primary screen is removed, set the new primary screen
234 // which belongs to the primary virtual desktop.
235 if (screen->isPrimary()) {
236 QXcbScreen *newPrimary = static_cast<QXcbScreen *>(virtualDesktop->screens().at(i: 0));
237 newPrimary->setPrimary(true);
238 const int idx = m_screens.indexOf(t: newPrimary);
239 if (idx > 0)
240 m_screens.swapItemsAt(i: 0, j: idx);
241 QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary);
242 }
243
244 qCDebug(lcQpaScreen) << "destroyScreen: destroy" << screen;
245 QWindowSystemInterface::handleScreenRemoved(screen);
246 }
247}
248
249void QXcbConnection::updateScreen_monitor(QXcbScreen *screen, xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp)
250{
251 screen->setMonitor(monitorInfo, timestamp);
252
253 if (screen->isPrimary()) {
254 const int idx = m_screens.indexOf(t: screen);
255 if (idx > 0) {
256 std::as_const(t&: m_screens).first()->setPrimary(false);
257 m_screens.swapItemsAt(i: 0, j: idx);
258 }
259 screen->virtualDesktop()->setPrimaryScreen(screen);
260 QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary: screen);
261 }
262 qCDebug(lcQpaScreen) << "updateScreen_monitor: update" << screen << "(Primary:" << screen->isPrimary() << ")";
263}
264
265QXcbScreen *QXcbConnection::createScreen_monitor(QXcbVirtualDesktop *virtualDesktop, xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp)
266{
267 QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, monitorInfo, timestamp);
268
269 if (screen->isPrimary()) {
270 if (!m_screens.isEmpty())
271 std::as_const(t&: m_screens).first()->setPrimary(false);
272
273 m_screens.prepend(t: screen);
274 } else {
275 m_screens.append(t: screen);
276 }
277 qCDebug(lcQpaScreen) << "createScreen_monitor: adding" << screen << "(Primary:" << screen->isPrimary() << ")";
278 virtualDesktop->addScreen(s: screen);
279 QWindowSystemInterface::handleScreenAdded(screen, isPrimary: screen->isPrimary());
280 return screen;
281}
282
283QXcbVirtualDesktop *QXcbConnection::virtualDesktopForNumber(int n) const
284{
285 for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
286 if (virtualDesktop->number() == n)
287 return virtualDesktop;
288 }
289
290 return nullptr;
291}
292
293QXcbScreen *QXcbConnection::findScreenForMonitorInfo(const QList<QPlatformScreen *> &screens, xcb_randr_monitor_info_t *monitorInfo)
294{
295 for (int i = 0; i < screens.size(); ++i) {
296 auto s = static_cast<QXcbScreen*>(screens[i]);
297 if (monitorInfo) {
298 QByteArray ba2 = atomName(atom: monitorInfo->name);
299 if (s->name().toLocal8Bit() == ba2)
300 return s;
301 }
302 }
303
304 return nullptr;
305}
306
307void QXcbConnection::initializeScreens(bool initialized)
308{
309 xcb_screen_iterator_t it = xcb_setup_roots_iterator(R: setup());
310 int xcbScreenNumber = 0; // screen number in the xcb sense
311 QXcbScreen *primaryScreen = nullptr;
312 if (isAtLeastXRandR15() && initialized)
313 m_screens.clear();
314
315 while (it.rem) {
316 if (isAtLeastXRandR15())
317 initializeScreensFromMonitor(it: &it, screenNumber: xcbScreenNumber, primaryScreen: &primaryScreen, initialized);
318 else if (isAtLeastXRandR12())
319 initializeScreensFromOutput(it: &it, screenNumber: xcbScreenNumber, primaryScreen: &primaryScreen);
320 else {
321 qWarning(msg: "There is no XRandR 1.2 and later version available. There will be only fake screen(s) to use.");
322 initializeScreensWithoutXRandR(it: &it, screenNumber: xcbScreenNumber, primaryScreen: &primaryScreen);
323 }
324
325 xcb_screen_next(i: &it);
326 ++xcbScreenNumber;
327 }
328
329 for (QXcbVirtualDesktop *virtualDesktop : std::as_const(t&: m_virtualDesktops))
330 virtualDesktop->subscribeToXFixesSelectionNotify();
331
332 if (m_virtualDesktops.isEmpty()) {
333 qFatal(msg: "QXcbConnection: no screens available");
334 } else {
335 // Ensure the primary screen is first on the list
336 if (primaryScreen) {
337 if (std::as_const(t&: m_screens).first() != primaryScreen) {
338 m_screens.removeOne(t: primaryScreen);
339 m_screens.prepend(t: primaryScreen);
340 }
341 }
342
343 // Push the screens to QGuiApplication
344 if (!initialized) {
345 for (QXcbScreen *screen : std::as_const(t&: m_screens)) {
346 qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
347 QWindowSystemInterface::handleScreenAdded(screen, isPrimary: screen->isPrimary());
348 }
349 }
350
351 if (!m_screens.isEmpty())
352 qCDebug(lcQpaScreen) << "initializeScreens: primary output is" << std::as_const(t&: m_screens).first()->name();
353 }
354}
355
356void QXcbConnection::initializeScreensWithoutXRandR(xcb_screen_iterator_t *it, int xcbScreenNumber, QXcbScreen **primaryScreen)
357{
358 // XRandR extension is missing, then create a fake/legacy screen.
359 xcb_screen_t *xcbScreen = it->data;
360 QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
361 m_virtualDesktops.append(t: virtualDesktop);
362 QList<QPlatformScreen *> siblings;
363
364 QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
365 qCDebug(lcQpaScreen) << "created fake screen" << screen;
366 m_screens << screen;
367
368 if (primaryScreenNumber() == xcbScreenNumber) {
369 *primaryScreen = screen;
370 (*primaryScreen)->setPrimary(true);
371 }
372 siblings << screen;
373 virtualDesktop->setScreens(std::move(siblings));
374}
375
376void QXcbConnection::initializeScreensFromOutput(xcb_screen_iterator_t *it, int xcbScreenNumber, QXcbScreen **primaryScreen)
377{
378 // Each "screen" in xcb terminology is a virtual desktop,
379 // potentially a collection of separate juxtaposed monitors.
380 // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
381 // which will become virtual siblings.
382 xcb_screen_t *xcbScreen = it->data;
383 QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
384 m_virtualDesktops.append(t: virtualDesktop);
385 QList<QPlatformScreen *> siblings;
386 if (isAtLeastXRandR12()) {
387 // RRGetScreenResourcesCurrent is fast but it may return nothing if the
388 // configuration is not initialized wrt to the hardware. We should call
389 // RRGetScreenResources in this case.
390 auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
391 xcb_connection(), xcbScreen->root);
392 decltype(Q_XCB_REPLY(xcb_randr_get_screen_resources,
393 xcb_connection(), xcbScreen->root)) resources;
394 if (!resources_current) {
395 qWarning(msg: "failed to get the current screen resources");
396 } else {
397 xcb_timestamp_t timestamp = 0;
398 xcb_randr_output_t *outputs = nullptr;
399 int outputCount = xcb_randr_get_screen_resources_current_outputs_length(R: resources_current.get());
400 if (outputCount) {
401 timestamp = resources_current->config_timestamp;
402 outputs = xcb_randr_get_screen_resources_current_outputs(R: resources_current.get());
403 } else {
404 resources = Q_XCB_REPLY(xcb_randr_get_screen_resources,
405 xcb_connection(), xcbScreen->root);
406 if (!resources) {
407 qWarning(msg: "failed to get the screen resources");
408 } else {
409 timestamp = resources->config_timestamp;
410 outputCount = xcb_randr_get_screen_resources_outputs_length(R: resources.get());
411 outputs = xcb_randr_get_screen_resources_outputs(R: resources.get());
412 }
413 }
414
415 if (outputCount) {
416 auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
417 if (!primary) {
418 qWarning(msg: "failed to get the primary output of the screen");
419 } else {
420 for (int i = 0; i < outputCount; i++) {
421 auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
422 xcb_connection(), outputs[i], timestamp);
423 // Invalid, disconnected or disabled output
424 if (!output)
425 continue;
426
427 if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
428 qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
429 QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
430 xcb_randr_get_output_info_name_length(output.get()))));
431 continue;
432 }
433
434 if (output->crtc == XCB_NONE) {
435 qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
436 QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
437 xcb_randr_get_output_info_name_length(output.get()))));
438 continue;
439 }
440
441 QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get());
442 siblings << screen;
443 m_screens << screen;
444
445 // There can be multiple outputs per screen, use either
446 // the first or an exact match. An exact match isn't
447 // always available if primary->output is XCB_NONE
448 // or currently disconnected output.
449 if (primaryScreenNumber() == xcbScreenNumber) {
450 if (!(*primaryScreen) || (primary && outputs[i] == primary->output)) {
451 if (*primaryScreen)
452 (*primaryScreen)->setPrimary(false);
453 *primaryScreen = screen;
454 (*primaryScreen)->setPrimary(true);
455 siblings.prepend(t: siblings.takeLast());
456 }
457 }
458 }
459 }
460 }
461 }
462 }
463 if (siblings.isEmpty()) {
464 // If there are no XRandR outputs or XRandR extension is missing,
465 // then create a fake/legacy screen.
466 QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
467 qCDebug(lcQpaScreen) << "created fake screen" << screen;
468 m_screens << screen;
469 if (primaryScreenNumber() == xcbScreenNumber) {
470 *primaryScreen = screen;
471 (*primaryScreen)->setPrimary(true);
472 }
473 siblings << screen;
474 }
475 virtualDesktop->setScreens(std::move(siblings));
476}
477
478void QXcbConnection::initializeScreensFromMonitor(xcb_screen_iterator_t *it, int xcbScreenNumber, QXcbScreen **primaryScreen, bool initialized)
479{
480 // Each "screen" in xcb terminology is a virtual desktop,
481 // potentially a collection of separate juxtaposed monitors.
482 // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
483 // which will become virtual siblings.
484 xcb_screen_t *xcbScreen = it->data;
485 QXcbVirtualDesktop *virtualDesktop = nullptr;
486 if (initialized)
487 virtualDesktop = virtualDesktopForNumber(n: xcbScreenNumber);
488 if (!virtualDesktop) {
489 virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
490 m_virtualDesktops.append(t: virtualDesktop);
491 }
492
493 if (xcbScreenNumber != primaryScreenNumber())
494 return;
495
496 QList<QPlatformScreen*> old = virtualDesktop->m_screens;
497
498 QList<QPlatformScreen *> siblings;
499
500 xcb_randr_get_monitors_cookie_t monitors_c = xcb_randr_get_monitors(c: xcb_connection(), window: xcbScreen->root, get_active: 1);
501 xcb_randr_get_monitors_reply_t *monitors_r = xcb_randr_get_monitors_reply(c: xcb_connection(), cookie: monitors_c, e: nullptr);
502
503 if (!monitors_r) {
504 qWarning(msg: "RANDR GetMonitors failed; this should not be possible");
505 return;
506 }
507
508 xcb_randr_monitor_info_iterator_t monitor_iter = xcb_randr_get_monitors_monitors_iterator(R: monitors_r);
509 while (monitor_iter.rem) {
510 xcb_randr_monitor_info_t *monitor_info = monitor_iter.data;
511 QXcbScreen *screen = nullptr;
512 if (!initialized) {
513 screen = new QXcbScreen(this, virtualDesktop, monitor_info, monitors_r->timestamp);
514 } else {
515 screen = findScreenForMonitorInfo(screens: old, monitorInfo: monitor_info);
516 if (!screen) {
517 screen = createScreen_monitor(virtualDesktop, monitorInfo: monitor_info, timestamp: monitors_r->timestamp);
518 } else {
519 updateScreen_monitor(screen, monitorInfo: monitor_info, timestamp: monitors_r->timestamp);
520 old.removeAll(t: screen);
521 }
522 }
523 if (!m_screens.contains(t: screen))
524 m_screens << screen;
525 siblings << screen;
526
527 // similar logic with QXcbConnection::initializeScreensFromOutput()
528 if (!(*primaryScreen) || monitor_info->primary) {
529 if (*primaryScreen)
530 (*primaryScreen)->setPrimary(false);
531 *primaryScreen = screen;
532 (*primaryScreen)->setPrimary(true);
533 siblings.prepend(t: siblings.takeLast());
534 }
535
536 xcb_randr_monitor_info_next(i: &monitor_iter);
537 }
538 free(ptr: monitors_r);
539
540 if (siblings.isEmpty()) {
541 QXcbScreen *screen = nullptr;
542 if (initialized && !old.isEmpty()) {
543 // If there are no other screens on the same virtual desktop,
544 // then transform the physical screen into a fake screen.
545 screen = static_cast<QXcbScreen *>(old.takeFirst());
546 const QString nameWas = screen->name();
547 screen->setMonitor(monitorInfo: nullptr, XCB_NONE);
548 qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen;
549 } else {
550 // If there are no XRandR outputs or XRandR extension is missing,
551 // then create a fake/legacy screen.
552 screen = new QXcbScreen(this, virtualDesktop, nullptr);
553 qCDebug(lcQpaScreen) << "create a fake screen: " << screen;
554 }
555
556 *primaryScreen = screen;
557 (*primaryScreen)->setPrimary(true);
558
559 siblings << screen;
560 m_screens << screen;
561 }
562
563 if (initialized) {
564 for (QPlatformScreen *ps : old) {
565 virtualDesktop->removeScreen(s: ps);
566 qCDebug(lcQpaScreen) << "destroy screen: " << ps;
567 QWindowSystemInterface::handleScreenRemoved(screen: ps);
568 }
569 }
570
571 virtualDesktop->setScreens(std::move(siblings));
572}
573

source code of qtbase/src/plugins/platforms/xcb/qxcbconnection_screens.cpp