9#include <QtGui/private/qhighdpiscaling_p.h>
10#include <QtCore/QString>
11#include <QtCore/QList>
13#include <qpa/qwindowsysteminterface.h>
15void QXcbConnection::xrandrSelectEvents()
17 xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(setup());
18 for (; rootIter.rem; xcb_screen_next(&rootIter)) {
19 xcb_randr_select_input(xcb_connection(),
21 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
22 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
23 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
24 XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY
29QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc)
const
31 for (QXcbScreen *screen : m_screens) {
32 if (screen->root() == rootWindow) {
33 if (screen->m_monitor) {
34 if (screen->crtcs().contains(crtc))
37 if (screen->crtc() == crtc)
46QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output)
const
48 for (QXcbScreen *screen : m_screens) {
49 if (screen->root() == rootWindow) {
50 if (screen->m_monitor) {
51 if (screen->outputs().contains(output))
54 if (screen->output() == output)
63QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow)
const
65 for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
66 if (virtualDesktop->screen()->root == rootWindow)
67 return virtualDesktop;
74
75
76void QXcbConnection::updateScreens(
const xcb_randr_notify_event_t *event)
78 if (event->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) {
79 xcb_randr_crtc_change_t crtc = event->u.cc;
80 QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(crtc.window);
85 QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc);
86 qCDebug(lcQpaScreen) <<
"QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc
87 <<
"mode" << crtc.mode <<
"relevant screen" << screen;
91 if (screen && crtc.mode) {
92 if (crtc.rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
93 crtc.rotation == XCB_RANDR_ROTATION_ROTATE_270)
94 std::swap(crtc.width, crtc.height);
95 screen->updateGeometry(QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.rotation);
96 if (screen->mode() != crtc.mode)
97 screen->updateRefreshRate(crtc.mode);
100 }
else if (event->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) {
101 xcb_randr_output_change_t output = event->u.oc;
102 QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(output.window);
107 QXcbScreen *screen = findScreenForOutput(output.window, output.output);
108 qCDebug(lcQpaScreen) <<
"QXcbConnection: XCB_RANDR_NOTIFY_OUTPUT_CHANGE:" << output.output;
110 if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) {
111 qCDebug(lcQpaScreen) <<
"screen" << screen->name() <<
"has been disconnected";
112 destroyScreen(screen);
113 }
else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
115 if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
116 auto outputInfo =
Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
117 output.output, output.config_timestamp);
119 const auto scrs = virtualDesktop->screens();
120 for (QPlatformScreen *scr : scrs) {
121 QXcbScreen *xcbScreen =
static_cast<QXcbScreen *>(scr);
122 if (xcbScreen->output() == XCB_NONE) {
129 QString nameWas = screen->name();
131 screen->setOutput(output.output, outputInfo.get());
132 updateScreen(screen, output);
133 qCDebug(lcQpaScreen) <<
"output" << screen->name()
134 <<
"is connected and enabled; was fake:" << nameWas;
136 screen = createScreen(virtualDesktop, output, outputInfo.get());
137 qCDebug(lcQpaScreen) <<
"output" << screen->name() <<
"is connected and enabled";
141 if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
143 auto outputInfo =
Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
144 output.output, output.config_timestamp);
145 if (!outputInfo || outputInfo->crtc == XCB_NONE) {
146 qCDebug(lcQpaScreen) <<
"output" << screen->name() <<
"has been disabled";
147 destroyScreen(screen);
149 qCDebug(lcQpaScreen) <<
"output" << screen->name() <<
"has been temporarily disabled for the mode switch";
152 screen->setCrtc(XCB_NONE);
155 updateScreen(screen, output);
156 qCDebug(lcQpaScreen) <<
"output has changed" << screen;
160 qCDebug(lcQpaScreen) <<
"updateScreens: primary output is" << std::as_const(m_screens).first()->name();
164bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
166 auto primary =
Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), rootWindow);
168 qWarning(
"failed to get the primary output of the screen");
170 const bool isPrimary = primary ? (primary->output == output) :
false;
175void QXcbConnection::updateScreen(QXcbScreen *screen,
const xcb_randr_output_change_t &outputChange)
177 screen->setCrtc(outputChange.crtc);
178 screen->updateGeometry(outputChange.config_timestamp);
179 if (screen->mode() != outputChange.mode)
180 screen->updateRefreshRate(outputChange.mode);
182 if (screen->screenNumber() == primaryScreenNumber()) {
183 if (!screen->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) {
184 screen->setPrimary(
true);
187 const int idx = m_screens.indexOf(screen);
189 std::as_const(m_screens).first()->setPrimary(
false);
190 m_screens.swapItemsAt(0, idx);
192 screen->virtualDesktop()->setPrimaryScreen(screen);
193 QWindowSystemInterface::handlePrimaryScreenChanged(screen);
198QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop,
199 const xcb_randr_output_change_t &outputChange,
200 xcb_randr_get_output_info_reply_t *outputInfo)
202 QXcbScreen *screen =
new QXcbScreen(
this, virtualDesktop, outputChange.output, outputInfo);
204 if (screen->screenNumber() == primaryScreenNumber())
205 screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output));
207 if (screen->isPrimary()) {
208 if (!m_screens.isEmpty())
209 std::as_const(m_screens).first()->setPrimary(
false);
211 m_screens.prepend(screen);
213 m_screens.append(screen);
215 virtualDesktop->addScreen(screen);
216 QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary());
221void QXcbConnection::destroyScreen(QXcbScreen *screen)
223 QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop();
224 if (virtualDesktop->screens().size() == 1) {
227 const QString nameWas = screen->name();
228 screen->setOutput(XCB_NONE,
nullptr);
229 qCDebug(lcQpaScreen) <<
"transformed" << nameWas <<
"to fake" << screen;
232 m_screens.removeOne(screen);
233 virtualDesktop->removeScreen(screen);
237 if (screen->isPrimary()) {
238 QXcbScreen *newPrimary =
static_cast<QXcbScreen *>(virtualDesktop->screens().at(0));
239 newPrimary->setPrimary(
true);
240 const int idx = m_screens.indexOf(newPrimary);
242 m_screens.swapItemsAt(0, idx);
243 QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary);
246 qCDebug(lcQpaScreen) <<
"destroyScreen: destroy" << screen;
247 QWindowSystemInterface::handleScreenRemoved(screen);
251QXcbVirtualDesktop *QXcbConnection::virtualDesktopForNumber(
int n)
const
253 for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
254 if (virtualDesktop->number() == n)
255 return virtualDesktop;
261QXcbScreen *QXcbConnection::findScreenForMonitorInfo(
const QList<QPlatformScreen *> &screens, xcb_randr_monitor_info_t *monitorInfo)
263 for (
int i = 0; i < screens.size(); ++i) {
264 auto s =
static_cast<QXcbScreen*>(screens[i]);
266 QByteArray ba2 = atomName(monitorInfo->name);
267 if (s->name().toLocal8Bit() == ba2)
275void QXcbConnection::initializeScreens(
bool initialized)
277 xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup());
278 int xcbScreenNumber = 0;
279 QXcbScreen *primaryScreen =
nullptr;
280 if (isAtLeastXRandR15() && initialized)
284 if (isAtLeastXRandR15())
285 initializeScreensFromMonitor(&it, xcbScreenNumber, &primaryScreen, initialized);
286 else if (isAtLeastXRandR12())
287 initializeScreensFromOutput(&it, xcbScreenNumber, &primaryScreen);
289 qWarning(
"There is no XRandR 1.2 and later version available. There will be only fake screen(s) to use.");
290 initializeScreensWithoutXRandR(&it, xcbScreenNumber, &primaryScreen);
293 xcb_screen_next(&it);
297 for (QXcbVirtualDesktop *virtualDesktop : std::as_const(m_virtualDesktops))
298 virtualDesktop->subscribeToXFixesSelectionNotify();
300 if (m_virtualDesktops.isEmpty()) {
301 qFatal(
"QXcbConnection: no screens available");
305 if (std::as_const(m_screens).first() != primaryScreen) {
306 m_screens.removeOne(primaryScreen);
307 m_screens.prepend(primaryScreen);
313 for (QXcbScreen *screen : std::as_const(m_screens)) {
314 qCDebug(lcQpaScreen) <<
"adding" << screen <<
"(Primary:" << screen->isPrimary() <<
")";
315 QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary());
319 if (!m_screens.isEmpty())
320 qCDebug(lcQpaScreen) <<
"initializeScreens: primary output is" << std::as_const(m_screens).first()->name();
324void QXcbConnection::initializeScreensWithoutXRandR(xcb_screen_iterator_t *it,
int xcbScreenNumber, QXcbScreen **primaryScreen)
327 xcb_screen_t *xcbScreen = it->data;
328 QXcbVirtualDesktop *virtualDesktop =
new QXcbVirtualDesktop(
this, xcbScreen, xcbScreenNumber);
329 m_virtualDesktops.append(virtualDesktop);
330 QList<QPlatformScreen *> siblings;
332 QXcbScreen *screen =
new QXcbScreen(
this, virtualDesktop, XCB_NONE,
nullptr);
333 qCDebug(lcQpaScreen) <<
"created fake screen" << screen;
336 if (primaryScreenNumber() == xcbScreenNumber) {
337 *primaryScreen = screen;
338 (*primaryScreen)->setPrimary(
true);
341 virtualDesktop->setScreens(std::move(siblings));
344void QXcbConnection::initializeScreensFromOutput(xcb_screen_iterator_t *it,
int xcbScreenNumber, QXcbScreen **primaryScreen)
350 xcb_screen_t *xcbScreen = it->data;
351 QXcbVirtualDesktop *virtualDesktop =
new QXcbVirtualDesktop(
this, xcbScreen, xcbScreenNumber);
352 m_virtualDesktops.append(virtualDesktop);
353 QList<QPlatformScreen *> siblings;
354 if (isAtLeastXRandR12()) {
358 auto resources_current =
Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
359 xcb_connection(), xcbScreen->root);
360 decltype(
Q_XCB_REPLY(xcb_randr_get_screen_resources,
361 xcb_connection(), xcbScreen->root)) resources;
362 if (!resources_current) {
363 qWarning(
"failed to get the current screen resources");
365 xcb_timestamp_t timestamp = 0;
366 xcb_randr_output_t *outputs =
nullptr;
367 int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get());
369 timestamp = resources_current->config_timestamp;
370 outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get());
372 resources =
Q_XCB_REPLY(xcb_randr_get_screen_resources,
373 xcb_connection(), xcbScreen->root);
375 qWarning(
"failed to get the screen resources");
377 timestamp = resources->config_timestamp;
378 outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get());
379 outputs = xcb_randr_get_screen_resources_outputs(resources.get());
384 auto primary =
Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
386 qWarning(
"failed to get the primary output of the screen");
388 for (
int i = 0; i < outputCount; i++) {
390 xcb_connection(), outputs[i], timestamp);
395 if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
396 qCDebug(lcQpaScreen,
"Output %s is not connected", qPrintable(
397 QString::fromUtf8((
const char*)xcb_randr_get_output_info_name(output.get()),
398 xcb_randr_get_output_info_name_length(output.get()))));
402 if (output->crtc == XCB_NONE) {
403 qCDebug(lcQpaScreen,
"Output %s is not enabled", qPrintable(
404 QString::fromUtf8((
const char*)xcb_randr_get_output_info_name(output.get()),
405 xcb_randr_get_output_info_name_length(output.get()))));
409 QXcbScreen *screen =
new QXcbScreen(
this, virtualDesktop, outputs[i], output.get());
417 if (primaryScreenNumber() == xcbScreenNumber) {
418 if (!(*primaryScreen) || (primary && outputs[i] == primary->output)) {
420 (*primaryScreen)->setPrimary(
false);
421 *primaryScreen = screen;
422 (*primaryScreen)->setPrimary(
true);
423 siblings.prepend(siblings.takeLast());
431 if (siblings.isEmpty()) {
434 QXcbScreen *screen =
new QXcbScreen(
this, virtualDesktop, XCB_NONE,
nullptr);
435 qCDebug(lcQpaScreen) <<
"created fake screen" << screen;
437 if (primaryScreenNumber() == xcbScreenNumber) {
438 *primaryScreen = screen;
439 (*primaryScreen)->setPrimary(
true);
443 virtualDesktop->setScreens(std::move(siblings));
446void QXcbConnection::initializeScreensFromMonitor(xcb_screen_iterator_t *it,
int xcbScreenNumber, QXcbScreen **primaryScreen,
bool initialized)
452 xcb_screen_t *xcbScreen = it->data;
453 QXcbVirtualDesktop *virtualDesktop =
nullptr;
455 virtualDesktop = virtualDesktopForNumber(xcbScreenNumber);
456 if (!virtualDesktop) {
457 virtualDesktop =
new QXcbVirtualDesktop(
this, xcbScreen, xcbScreenNumber);
458 m_virtualDesktops.append(virtualDesktop);
460 QList<QPlatformScreen *> old = virtualDesktop->m_screens;
461 QList<QXcbScreen *> newScreens;
463 QList<QPlatformScreen *> siblings;
465 xcb_randr_get_monitors_cookie_t monitors_c = xcb_randr_get_monitors(xcb_connection(), xcbScreen->root, 1);
466 xcb_randr_get_monitors_reply_t *monitors_r = xcb_randr_get_monitors_reply(xcb_connection(), monitors_c,
nullptr);
469 qWarning(
"RANDR GetMonitors failed; this should not be possible");
473 xcb_randr_monitor_info_iterator_t monitor_iter = xcb_randr_get_monitors_monitors_iterator(monitors_r);
474 while (monitor_iter.rem) {
475 xcb_randr_monitor_info_t *monitor_info = monitor_iter.data;
476 QXcbScreen *screen =
nullptr;
478 screen =
new QXcbScreen(
this, virtualDesktop, monitor_info, monitors_r->timestamp);
480 screen = findScreenForMonitorInfo(old, monitor_info);
482 screen =
new QXcbScreen(
this, virtualDesktop, monitor_info, monitors_r->timestamp);
483 newScreens.append(screen);
485 screen->setMonitor(monitor_info, monitors_r->timestamp);
486 old.removeAll(screen);
490 if (screen->isPrimary()) {
491 siblings.prepend (screen);
492 if (primaryScreenNumber() == xcbScreenNumber) {
494 (*primaryScreen)->setPrimary(
false);
495 *primaryScreen = screen;
496 (*primaryScreen)->setPrimary(
true);
498 screen->setPrimary(
false);
501 siblings.append(screen);
504 xcb_randr_monitor_info_next(&monitor_iter);
508 if (siblings.isEmpty()) {
509 QXcbScreen *screen =
nullptr;
510 if (initialized && !old.isEmpty()) {
513 screen =
static_cast<QXcbScreen *>(old.takeFirst());
514 const QString nameWas = screen->name();
515 screen->setMonitor(
nullptr, XCB_NONE);
516 qCDebug(lcQpaScreen) <<
"transformed" << nameWas <<
"to fake" << screen;
520 screen =
new QXcbScreen(
this, virtualDesktop,
nullptr);
521 qCDebug(lcQpaScreen) <<
"create a fake screen: " << screen;
527 if (primaryScreenNumber() == xcbScreenNumber) {
529 if (!*primaryScreen) {
530 (*primaryScreen) =
static_cast<QXcbScreen *>(siblings.first());
531 (*primaryScreen)->setPrimary(
true);
535 QList<QXcbScreen *> new_m_screens;
536 new_m_screens.reserve( siblings.size() + m_screens.size() );
537 for (QPlatformScreen *ps:siblings) {
538 new_m_screens.append(
static_cast<QXcbScreen *>(ps));
540 new_m_screens.append(std::move(m_screens));
541 m_screens = std::move(new_m_screens);
543 for (QPlatformScreen *ps:siblings) {
544 m_screens.append(
static_cast<QXcbScreen *>(ps));
549 if (primaryScreenNumber() == xcbScreenNumber && !newScreens.contains(*primaryScreen)) {
550 qCDebug(lcQpaScreen) <<
"assigning screen as primary: " << *primaryScreen;
551 virtualDesktop->setPrimaryScreen(*primaryScreen);
552 QWindowSystemInterface::handlePrimaryScreenChanged(*primaryScreen);
555 for (QXcbScreen *screen: newScreens) {
556 qCDebug(lcQpaScreen) <<
"adding screen: " << screen <<
"(Primary:" << screen->isPrimary() <<
")";
557 virtualDesktop->addScreen(screen);
558 QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary());
561 for (QPlatformScreen *ps : old) {
562 qCDebug(lcQpaScreen) <<
"destroy screen: " << ps;
563 QWindowSystemInterface::handleScreenRemoved(ps);
564 virtualDesktop->removeScreen(ps);
568 virtualDesktop->setScreens(std::move(siblings));
#define Q_XCB_REPLY(call,...)
#define Q_XCB_REPLY_UNCHECKED(call,...)