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 || 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
249QXcbVirtualDesktop *QXcbConnection::virtualDesktopForNumber(int n) const
250{
251 for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
252 if (virtualDesktop->number() == n)
253 return virtualDesktop;
254 }
255
256 return nullptr;
257}
258
259QXcbScreen *QXcbConnection::findScreenForMonitorInfo(const QList<QPlatformScreen *> &screens, xcb_randr_monitor_info_t *monitorInfo)
260{
261 for (int i = 0; i < screens.size(); ++i) {
262 auto s = static_cast<QXcbScreen*>(screens[i]);
263 if (monitorInfo) {
264 QByteArray ba2 = atomName(atom: monitorInfo->name);
265 if (s->name().toLocal8Bit() == ba2)
266 return s;
267 }
268 }
269
270 return nullptr;
271}
272
273void QXcbConnection::initializeScreens(bool initialized)
274{
275 xcb_screen_iterator_t it = xcb_setup_roots_iterator(R: setup());
276 int xcbScreenNumber = 0; // screen number in the xcb sense
277 QXcbScreen *primaryScreen = nullptr;
278 if (isAtLeastXRandR15() && initialized)
279 m_screens.clear();
280
281 while (it.rem) {
282 if (isAtLeastXRandR15())
283 initializeScreensFromMonitor(it: &it, screenNumber: xcbScreenNumber, primaryScreen: &primaryScreen, initialized);
284 else if (isAtLeastXRandR12())
285 initializeScreensFromOutput(it: &it, screenNumber: xcbScreenNumber, primaryScreen: &primaryScreen);
286 else {
287 qWarning(msg: "There is no XRandR 1.2 and later version available. There will be only fake screen(s) to use.");
288 initializeScreensWithoutXRandR(it: &it, screenNumber: xcbScreenNumber, primaryScreen: &primaryScreen);
289 }
290
291 xcb_screen_next(i: &it);
292 ++xcbScreenNumber;
293 }
294
295 for (QXcbVirtualDesktop *virtualDesktop : std::as_const(t&: m_virtualDesktops))
296 virtualDesktop->subscribeToXFixesSelectionNotify();
297
298 if (m_virtualDesktops.isEmpty()) {
299 qFatal(msg: "QXcbConnection: no screens available");
300 } else {
301 // Ensure the primary screen is first on the list
302 if (primaryScreen) {
303 if (std::as_const(t&: m_screens).first() != primaryScreen) {
304 m_screens.removeOne(t: primaryScreen);
305 m_screens.prepend(t: primaryScreen);
306 }
307 }
308
309 // Push the screens to QGuiApplication
310 if (!initialized) {
311 for (QXcbScreen *screen : std::as_const(t&: m_screens)) {
312 qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
313 QWindowSystemInterface::handleScreenAdded(screen, isPrimary: screen->isPrimary());
314 }
315 }
316
317 if (!m_screens.isEmpty())
318 qCDebug(lcQpaScreen) << "initializeScreens: primary output is" << std::as_const(t&: m_screens).first()->name();
319 }
320}
321
322void QXcbConnection::initializeScreensWithoutXRandR(xcb_screen_iterator_t *it, int xcbScreenNumber, QXcbScreen **primaryScreen)
323{
324 // XRandR extension is missing, then create a fake/legacy screen.
325 xcb_screen_t *xcbScreen = it->data;
326 QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
327 m_virtualDesktops.append(t: virtualDesktop);
328 QList<QPlatformScreen *> siblings;
329
330 QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
331 qCDebug(lcQpaScreen) << "created fake screen" << screen;
332 m_screens << screen;
333
334 if (primaryScreenNumber() == xcbScreenNumber) {
335 *primaryScreen = screen;
336 (*primaryScreen)->setPrimary(true);
337 }
338 siblings << screen;
339 virtualDesktop->setScreens(std::move(siblings));
340}
341
342void QXcbConnection::initializeScreensFromOutput(xcb_screen_iterator_t *it, int xcbScreenNumber, QXcbScreen **primaryScreen)
343{
344 // Each "screen" in xcb terminology is a virtual desktop,
345 // potentially a collection of separate juxtaposed monitors.
346 // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
347 // which will become virtual siblings.
348 xcb_screen_t *xcbScreen = it->data;
349 QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
350 m_virtualDesktops.append(t: virtualDesktop);
351 QList<QPlatformScreen *> siblings;
352 if (isAtLeastXRandR12()) {
353 // RRGetScreenResourcesCurrent is fast but it may return nothing if the
354 // configuration is not initialized wrt to the hardware. We should call
355 // RRGetScreenResources in this case.
356 auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
357 xcb_connection(), xcbScreen->root);
358 decltype(Q_XCB_REPLY(xcb_randr_get_screen_resources,
359 xcb_connection(), xcbScreen->root)) resources;
360 if (!resources_current) {
361 qWarning(msg: "failed to get the current screen resources");
362 } else {
363 xcb_timestamp_t timestamp = 0;
364 xcb_randr_output_t *outputs = nullptr;
365 int outputCount = xcb_randr_get_screen_resources_current_outputs_length(R: resources_current.get());
366 if (outputCount) {
367 timestamp = resources_current->config_timestamp;
368 outputs = xcb_randr_get_screen_resources_current_outputs(R: resources_current.get());
369 } else {
370 resources = Q_XCB_REPLY(xcb_randr_get_screen_resources,
371 xcb_connection(), xcbScreen->root);
372 if (!resources) {
373 qWarning(msg: "failed to get the screen resources");
374 } else {
375 timestamp = resources->config_timestamp;
376 outputCount = xcb_randr_get_screen_resources_outputs_length(R: resources.get());
377 outputs = xcb_randr_get_screen_resources_outputs(R: resources.get());
378 }
379 }
380
381 if (outputCount) {
382 auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
383 if (!primary) {
384 qWarning(msg: "failed to get the primary output of the screen");
385 } else {
386 for (int i = 0; i < outputCount; i++) {
387 auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
388 xcb_connection(), outputs[i], timestamp);
389 // Invalid, disconnected or disabled output
390 if (!output)
391 continue;
392
393 if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
394 qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
395 QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
396 xcb_randr_get_output_info_name_length(output.get()))));
397 continue;
398 }
399
400 if (output->crtc == XCB_NONE) {
401 qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
402 QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
403 xcb_randr_get_output_info_name_length(output.get()))));
404 continue;
405 }
406
407 QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get());
408 siblings << screen;
409 m_screens << screen;
410
411 // There can be multiple outputs per screen, use either
412 // the first or an exact match. An exact match isn't
413 // always available if primary->output is XCB_NONE
414 // or currently disconnected output.
415 if (primaryScreenNumber() == xcbScreenNumber) {
416 if (!(*primaryScreen) || (primary && outputs[i] == primary->output)) {
417 if (*primaryScreen)
418 (*primaryScreen)->setPrimary(false);
419 *primaryScreen = screen;
420 (*primaryScreen)->setPrimary(true);
421 siblings.prepend(t: siblings.takeLast());
422 }
423 }
424 }
425 }
426 }
427 }
428 }
429 if (siblings.isEmpty()) {
430 // If there are no XRandR outputs or XRandR extension is missing,
431 // then create a fake/legacy screen.
432 QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
433 qCDebug(lcQpaScreen) << "created fake screen" << screen;
434 m_screens << screen;
435 if (primaryScreenNumber() == xcbScreenNumber) {
436 *primaryScreen = screen;
437 (*primaryScreen)->setPrimary(true);
438 }
439 siblings << screen;
440 }
441 virtualDesktop->setScreens(std::move(siblings));
442}
443
444void QXcbConnection::initializeScreensFromMonitor(xcb_screen_iterator_t *it, int xcbScreenNumber, QXcbScreen **primaryScreen, bool initialized)
445{
446 // Each "screen" in xcb terminology is a virtual desktop,
447 // potentially a collection of separate juxtaposed monitors.
448 // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
449 // which will become virtual siblings.
450 xcb_screen_t *xcbScreen = it->data;
451 QXcbVirtualDesktop *virtualDesktop = nullptr;
452 if (initialized)
453 virtualDesktop = virtualDesktopForNumber(n: xcbScreenNumber);
454 if (!virtualDesktop) {
455 virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
456 m_virtualDesktops.append(t: virtualDesktop);
457 }
458 QList<QPlatformScreen *> old = virtualDesktop->m_screens;
459 QList<QXcbScreen *> newScreens;
460
461 QList<QPlatformScreen *> siblings;
462
463 xcb_randr_get_monitors_cookie_t monitors_c = xcb_randr_get_monitors(c: xcb_connection(), window: xcbScreen->root, get_active: 1);
464 xcb_randr_get_monitors_reply_t *monitors_r = xcb_randr_get_monitors_reply(c: xcb_connection(), cookie: monitors_c, e: nullptr);
465
466 if (!monitors_r) {
467 qWarning(msg: "RANDR GetMonitors failed; this should not be possible");
468 return;
469 }
470
471 xcb_randr_monitor_info_iterator_t monitor_iter = xcb_randr_get_monitors_monitors_iterator(R: monitors_r);
472 while (monitor_iter.rem) {
473 xcb_randr_monitor_info_t *monitor_info = monitor_iter.data;
474 QXcbScreen *screen = nullptr;
475 if (!initialized) {
476 screen = new QXcbScreen(this, virtualDesktop, monitor_info, monitors_r->timestamp);
477 } else {
478 screen = findScreenForMonitorInfo(screens: old, monitorInfo: monitor_info);
479 if (!screen) {
480 screen = new QXcbScreen(this, virtualDesktop, monitor_info, monitors_r->timestamp);
481 newScreens.append(t: screen);
482 } else {
483 screen->setMonitor(monitorInfo: monitor_info, timestamp: monitors_r->timestamp);
484 old.removeAll(t: screen);
485 }
486 }
487
488 if (screen->isPrimary()) {
489 siblings.prepend (t: screen);
490 if (primaryScreenNumber() == xcbScreenNumber) {
491 if (*primaryScreen)
492 (*primaryScreen)->setPrimary(false);
493 *primaryScreen = screen;
494 (*primaryScreen)->setPrimary(true);
495 } else { // Only screens on the main virtual desktop can be primary
496 screen->setPrimary(false);
497 }
498 } else {
499 siblings.append(t: screen);
500 }
501
502 xcb_randr_monitor_info_next(i: &monitor_iter);
503 }
504 free(ptr: monitors_r);
505
506 if (siblings.isEmpty()) {
507 QXcbScreen *screen = nullptr;
508 if (initialized && !old.isEmpty()) {
509 // If there are no other screens on the same virtual desktop,
510 // then transform the physical screen into a fake screen.
511 screen = static_cast<QXcbScreen *>(old.takeFirst());
512 const QString nameWas = screen->name();
513 screen->setMonitor(monitorInfo: nullptr, XCB_NONE);
514 qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen;
515 } else {
516 // If there are no XRandR outputs or XRandR extension is missing,
517 // then create a fake/legacy screen.
518 screen = new QXcbScreen(this, virtualDesktop, nullptr);
519 qCDebug(lcQpaScreen) << "create a fake screen: " << screen;
520 }
521
522 siblings << screen;
523 }
524
525 if (primaryScreenNumber() == xcbScreenNumber) {
526 // If no screen was reported to be primary, use the first one
527 if (!*primaryScreen) {
528 (*primaryScreen) = static_cast<QXcbScreen *>(siblings.first());
529 (*primaryScreen)->setPrimary(true);
530 }
531
532 // Prepand the siblings to the current list of screens
533 QList<QXcbScreen *> new_m_screens;
534 new_m_screens.reserve( asize: siblings.size() + m_screens.size() );
535 for (QPlatformScreen *ps:siblings) {
536 new_m_screens.append(t: static_cast<QXcbScreen *>(ps));
537 }
538 new_m_screens.append(other: std::move(m_screens));
539 m_screens = std::move(new_m_screens);
540 } else {
541 for (QPlatformScreen *ps:siblings) {
542 m_screens.append(t: static_cast<QXcbScreen *>(ps));
543 }
544 }
545
546 if (initialized) {
547 if (primaryScreenNumber() == xcbScreenNumber && !newScreens.contains(t: *primaryScreen)) {
548 qCDebug(lcQpaScreen) << "assigning screen as primary: " << *primaryScreen;
549 virtualDesktop->setPrimaryScreen(*primaryScreen);
550 QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary: *primaryScreen);
551 }
552
553 for (QXcbScreen *screen: newScreens) {
554 qCDebug(lcQpaScreen) << "adding screen: " << screen << "(Primary:" << screen->isPrimary() << ")";
555 virtualDesktop->addScreen(s: screen);
556 QWindowSystemInterface::handleScreenAdded(screen, isPrimary: screen->isPrimary());
557 }
558
559 for (QPlatformScreen *ps : old) {
560 qCDebug(lcQpaScreen) << "destroy screen: " << ps;
561 QWindowSystemInterface::handleScreenRemoved(screen: ps);
562 virtualDesktop->removeScreen(s: ps);
563 }
564 }
565
566 virtualDesktop->setScreens(std::move(siblings));
567}
568

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