9#include <QtCore/QString>
10#include <QtCore/QList>
12#include <qpa/qwindowsysteminterface.h>
14void QXcbConnection::xrandrSelectEvents()
16 xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(setup());
17 for (; rootIter.rem; xcb_screen_next(&rootIter)) {
18 xcb_randr_select_input(xcb_connection(),
20 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
21 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
22 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
23 XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY
28QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc)
const
30 for (QXcbScreen *screen : m_screens) {
31 if (screen->root() == rootWindow) {
32 if (screen->m_monitor) {
33 if (screen->crtcs().contains(crtc))
36 if (screen->crtc() == crtc)
45QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output)
const
47 for (QXcbScreen *screen : m_screens) {
48 if (screen->root() == rootWindow) {
49 if (screen->m_monitor) {
50 if (screen->outputs().contains(output))
53 if (screen->output() == output)
62QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow)
const
64 for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
65 if (virtualDesktop->screen()->root == rootWindow)
66 return virtualDesktop;
73
74
75void QXcbConnection::updateScreens(
const xcb_randr_notify_event_t *event)
77 if (event->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) {
78 xcb_randr_crtc_change_t crtc = event->u.cc;
79 QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(crtc.window);
84 QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc);
85 qCDebug(lcQpaScreen) <<
"QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc
86 <<
"mode" << crtc.mode <<
"relevant screen" << screen;
90 if (screen && crtc.mode) {
91 if (crtc.rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
92 crtc.rotation == XCB_RANDR_ROTATION_ROTATE_270)
93 std::swap(crtc.width, crtc.height);
94 screen->updateGeometry(QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.rotation);
95 if (screen->mode() != crtc.mode)
96 screen->updateRefreshRate(crtc.mode);
99 }
else if (event->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) {
100 xcb_randr_output_change_t output = event->u.oc;
101 QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(output.window);
106 QXcbScreen *screen = findScreenForOutput(output.window, output.output);
107 qCDebug(lcQpaScreen) <<
"QXcbConnection: XCB_RANDR_NOTIFY_OUTPUT_CHANGE:" << output.output;
109 if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) {
110 qCDebug(lcQpaScreen) <<
"screen" << screen->name() <<
"has been disconnected";
111 destroyScreen(screen);
112 }
else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
114 if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
115 auto outputInfo =
Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
116 output.output, output.config_timestamp);
118 const auto scrs = virtualDesktop->screens();
119 for (QPlatformScreen *scr : scrs) {
120 QXcbScreen *xcbScreen =
static_cast<QXcbScreen *>(scr);
121 if (xcbScreen->output() == XCB_NONE) {
128 QString nameWas = screen->name();
130 screen->setOutput(output.output, outputInfo.get());
131 updateScreen(screen, output);
132 qCDebug(lcQpaScreen) <<
"output" << screen->name()
133 <<
"is connected and enabled; was fake:" << nameWas;
135 screen = createScreen(virtualDesktop, output, outputInfo.get());
136 qCDebug(lcQpaScreen) <<
"output" << screen->name() <<
"is connected and enabled";
140 if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
142 auto outputInfo =
Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
143 output.output, output.config_timestamp);
144 if (!outputInfo || outputInfo->crtc == XCB_NONE) {
145 qCDebug(lcQpaScreen) <<
"output" << screen->name() <<
"has been disabled";
146 destroyScreen(screen);
148 qCDebug(lcQpaScreen) <<
"output" << screen->name() <<
"has been temporarily disabled for the mode switch";
151 screen->setCrtc(XCB_NONE);
154 updateScreen(screen, output);
155 qCDebug(lcQpaScreen) <<
"output has changed" << screen;
159 qCDebug(lcQpaScreen) <<
"updateScreens: primary output is" << std::as_const(m_screens).first()->name();
163bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
165 auto primary =
Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), rootWindow);
167 qWarning(
"failed to get the primary output of the screen");
169 const bool isPrimary = primary ? (primary->output == output) :
false;
174void QXcbConnection::updateScreen(QXcbScreen *screen,
const xcb_randr_output_change_t &outputChange)
176 screen->setCrtc(outputChange.crtc);
177 screen->updateGeometry(outputChange.config_timestamp);
178 if (screen->mode() != outputChange.mode)
179 screen->updateRefreshRate(outputChange.mode);
181 if (screen->screenNumber() == primaryScreenNumber()) {
182 if (!screen->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) {
183 screen->setPrimary(
true);
186 const int idx = m_screens.indexOf(screen);
188 std::as_const(m_screens).first()->setPrimary(
false);
189 m_screens.swapItemsAt(0, idx);
191 screen->virtualDesktop()->setPrimaryScreen(screen);
192 QWindowSystemInterface::handlePrimaryScreenChanged(screen);
197QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop,
198 const xcb_randr_output_change_t &outputChange,
199 xcb_randr_get_output_info_reply_t *outputInfo)
201 QXcbScreen *screen =
new QXcbScreen(
this, virtualDesktop, outputChange.output, outputInfo);
203 if (screen->screenNumber() == primaryScreenNumber())
204 screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output));
206 if (screen->isPrimary()) {
207 if (!m_screens.isEmpty())
208 std::as_const(m_screens).first()->setPrimary(
false);
210 m_screens.prepend(screen);
212 m_screens.append(screen);
214 virtualDesktop->addScreen(screen);
215 QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary());
220void QXcbConnection::destroyScreen(QXcbScreen *screen)
222 QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop();
223 if (virtualDesktop->screens().size() == 1) {
226 const QString nameWas = screen->name();
227 screen->setOutput(XCB_NONE,
nullptr);
228 qCDebug(lcQpaScreen) <<
"transformed" << nameWas <<
"to fake" << screen;
231 m_screens.removeOne(screen);
232 virtualDesktop->removeScreen(screen);
236 if (screen->isPrimary()) {
237 QXcbScreen *newPrimary =
static_cast<QXcbScreen *>(virtualDesktop->screens().at(0));
238 newPrimary->setPrimary(
true);
239 const int idx = m_screens.indexOf(newPrimary);
241 m_screens.swapItemsAt(0, idx);
242 QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary);
245 qCDebug(lcQpaScreen) <<
"destroyScreen: destroy" << screen;
246 QWindowSystemInterface::handleScreenRemoved(screen);
250QXcbVirtualDesktop *QXcbConnection::virtualDesktopForNumber(
int n)
const
252 for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
253 if (virtualDesktop->number() == n)
254 return virtualDesktop;
260QXcbScreen *QXcbConnection::findScreenForMonitorInfo(
const QList<QPlatformScreen *> &screens, xcb_randr_monitor_info_t *monitorInfo)
262 for (
int i = 0; i < screens.size(); ++i) {
263 auto s =
static_cast<QXcbScreen*>(screens[i]);
265 QByteArray ba2 = atomName(monitorInfo->name);
266 if (s->name().toLocal8Bit() == ba2)
274void QXcbConnection::initializeScreens(
bool initialized)
276 xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup());
277 int xcbScreenNumber = 0;
278 QXcbScreen *primaryScreen =
nullptr;
279 if (isAtLeastXRandR15() && initialized)
283 if (isAtLeastXRandR15())
284 initializeScreensFromMonitor(&it, xcbScreenNumber, &primaryScreen, initialized);
285 else if (isAtLeastXRandR12())
286 initializeScreensFromOutput(&it, xcbScreenNumber, &primaryScreen);
288 qWarning(
"There is no XRandR 1.2 and later version available. There will be only fake screen(s) to use.");
289 initializeScreensWithoutXRandR(&it, xcbScreenNumber, &primaryScreen);
292 xcb_screen_next(&it);
296 for (QXcbVirtualDesktop *virtualDesktop : std::as_const(m_virtualDesktops))
297 virtualDesktop->subscribeToXFixesSelectionNotify();
299 if (m_virtualDesktops.isEmpty()) {
300 qFatal(
"QXcbConnection: no screens available");
304 if (std::as_const(m_screens).first() != primaryScreen) {
305 m_screens.removeOne(primaryScreen);
306 m_screens.prepend(primaryScreen);
312 for (QXcbScreen *screen : std::as_const(m_screens)) {
313 qCDebug(lcQpaScreen) <<
"adding" << screen <<
"(Primary:" << screen->isPrimary() <<
")";
314 QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary());
318 if (!m_screens.isEmpty())
319 qCDebug(lcQpaScreen) <<
"initializeScreens: primary output is" << std::as_const(m_screens).first()->name();
323void QXcbConnection::initializeScreensWithoutXRandR(xcb_screen_iterator_t *it,
int xcbScreenNumber, QXcbScreen **primaryScreen)
326 xcb_screen_t *xcbScreen = it->data;
327 QXcbVirtualDesktop *virtualDesktop =
new QXcbVirtualDesktop(
this, xcbScreen, xcbScreenNumber);
328 m_virtualDesktops.append(virtualDesktop);
329 QList<QPlatformScreen *> siblings;
331 QXcbScreen *screen =
new QXcbScreen(
this, virtualDesktop, XCB_NONE,
nullptr);
332 qCDebug(lcQpaScreen) <<
"created fake screen" << screen;
335 if (primaryScreenNumber() == xcbScreenNumber) {
336 *primaryScreen = screen;
337 (*primaryScreen)->setPrimary(
true);
340 virtualDesktop->setScreens(std::move(siblings));
343void QXcbConnection::initializeScreensFromOutput(xcb_screen_iterator_t *it,
int xcbScreenNumber, QXcbScreen **primaryScreen)
349 xcb_screen_t *xcbScreen = it->data;
350 QXcbVirtualDesktop *virtualDesktop =
new QXcbVirtualDesktop(
this, xcbScreen, xcbScreenNumber);
351 m_virtualDesktops.append(virtualDesktop);
352 QList<QPlatformScreen *> siblings;
353 if (isAtLeastXRandR12()) {
357 auto resources_current =
Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
358 xcb_connection(), xcbScreen->root);
359 decltype(
Q_XCB_REPLY(xcb_randr_get_screen_resources,
360 xcb_connection(), xcbScreen->root)) resources;
361 if (!resources_current) {
362 qWarning(
"failed to get the current screen resources");
364 xcb_timestamp_t timestamp = 0;
365 xcb_randr_output_t *outputs =
nullptr;
366 int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get());
368 timestamp = resources_current->config_timestamp;
369 outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get());
371 resources =
Q_XCB_REPLY(xcb_randr_get_screen_resources,
372 xcb_connection(), xcbScreen->root);
374 qWarning(
"failed to get the screen resources");
376 timestamp = resources->config_timestamp;
377 outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get());
378 outputs = xcb_randr_get_screen_resources_outputs(resources.get());
383 auto primary =
Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
385 qWarning(
"failed to get the primary output of the screen");
387 for (
int i = 0; i < outputCount; i++) {
389 xcb_connection(), outputs[i], timestamp);
394 if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
395 qCDebug(lcQpaScreen,
"Output %s is not connected", qPrintable(
396 QString::fromUtf8((
const char*)xcb_randr_get_output_info_name(output.get()),
397 xcb_randr_get_output_info_name_length(output.get()))));
401 if (output->crtc == XCB_NONE) {
402 qCDebug(lcQpaScreen,
"Output %s is not enabled", qPrintable(
403 QString::fromUtf8((
const char*)xcb_randr_get_output_info_name(output.get()),
404 xcb_randr_get_output_info_name_length(output.get()))));
408 QXcbScreen *screen =
new QXcbScreen(
this, virtualDesktop, outputs[i], output.get());
416 if (primaryScreenNumber() == xcbScreenNumber) {
417 if (!(*primaryScreen) || (primary && outputs[i] == primary->output)) {
419 (*primaryScreen)->setPrimary(
false);
420 *primaryScreen = screen;
421 (*primaryScreen)->setPrimary(
true);
422 siblings.prepend(siblings.takeLast());
430 if (siblings.isEmpty()) {
433 QXcbScreen *screen =
new QXcbScreen(
this, virtualDesktop, XCB_NONE,
nullptr);
434 qCDebug(lcQpaScreen) <<
"created fake screen" << screen;
436 if (primaryScreenNumber() == xcbScreenNumber) {
437 *primaryScreen = screen;
438 (*primaryScreen)->setPrimary(
true);
442 virtualDesktop->setScreens(std::move(siblings));
445void QXcbConnection::initializeScreensFromMonitor(xcb_screen_iterator_t *it,
int xcbScreenNumber, QXcbScreen **primaryScreen,
bool initialized)
451 xcb_screen_t *xcbScreen = it->data;
452 QXcbVirtualDesktop *virtualDesktop =
nullptr;
454 virtualDesktop = virtualDesktopForNumber(xcbScreenNumber);
455 if (!virtualDesktop) {
456 virtualDesktop =
new QXcbVirtualDesktop(
this, xcbScreen, xcbScreenNumber);
457 m_virtualDesktops.append(virtualDesktop);
459 QList<QPlatformScreen *> old = virtualDesktop->m_screens;
460 QList<QXcbScreen *> newScreens;
462 QList<QPlatformScreen *> siblings;
464 xcb_randr_get_monitors_cookie_t monitors_c = xcb_randr_get_monitors(xcb_connection(), xcbScreen->root, 1);
465 xcb_randr_get_monitors_reply_t *monitors_r = xcb_randr_get_monitors_reply(xcb_connection(), monitors_c,
nullptr);
468 qWarning(
"RANDR GetMonitors failed; this should not be possible");
472 xcb_randr_monitor_info_iterator_t monitor_iter = xcb_randr_get_monitors_monitors_iterator(monitors_r);
473 while (monitor_iter.rem) {
474 xcb_randr_monitor_info_t *monitor_info = monitor_iter.data;
475 QXcbScreen *screen =
nullptr;
477 screen =
new QXcbScreen(
this, virtualDesktop, monitor_info, monitors_r->timestamp);
479 screen = findScreenForMonitorInfo(old, monitor_info);
481 screen =
new QXcbScreen(
this, virtualDesktop, monitor_info, monitors_r->timestamp);
482 newScreens.append(screen);
484 screen->setMonitor(monitor_info, monitors_r->timestamp);
485 old.removeAll(screen);
489 if (screen->isPrimary()) {
490 siblings.prepend (screen);
491 if (primaryScreenNumber() == xcbScreenNumber) {
493 (*primaryScreen)->setPrimary(
false);
494 *primaryScreen = screen;
495 (*primaryScreen)->setPrimary(
true);
497 screen->setPrimary(
false);
500 siblings.append(screen);
503 xcb_randr_monitor_info_next(&monitor_iter);
507 if (siblings.isEmpty()) {
508 QXcbScreen *screen =
nullptr;
509 if (initialized && !old.isEmpty()) {
512 screen =
static_cast<QXcbScreen *>(old.takeFirst());
513 const QString nameWas = screen->name();
514 screen->setMonitor(
nullptr, XCB_NONE);
515 qCDebug(lcQpaScreen) <<
"transformed" << nameWas <<
"to fake" << screen;
519 screen =
new QXcbScreen(
this, virtualDesktop,
nullptr);
520 qCDebug(lcQpaScreen) <<
"create a fake screen: " << screen;
526 if (primaryScreenNumber() == xcbScreenNumber) {
528 if (!*primaryScreen) {
529 (*primaryScreen) =
static_cast<QXcbScreen *>(siblings.first());
530 (*primaryScreen)->setPrimary(
true);
534 QList<QXcbScreen *> new_m_screens;
535 new_m_screens.reserve( siblings.size() + m_screens.size() );
536 for (QPlatformScreen *ps:siblings) {
537 new_m_screens.append(
static_cast<QXcbScreen *>(ps));
539 new_m_screens.append(std::move(m_screens));
540 m_screens = std::move(new_m_screens);
542 for (QPlatformScreen *ps:siblings) {
543 m_screens.append(
static_cast<QXcbScreen *>(ps));
548 if (primaryScreenNumber() == xcbScreenNumber && !newScreens.contains(*primaryScreen)) {
549 qCDebug(lcQpaScreen) <<
"assigning screen as primary: " << *primaryScreen;
550 virtualDesktop->setPrimaryScreen(*primaryScreen);
551 QWindowSystemInterface::handlePrimaryScreenChanged(*primaryScreen);
554 for (QXcbScreen *screen: newScreens) {
555 qCDebug(lcQpaScreen) <<
"adding screen: " << screen <<
"(Primary:" << screen->isPrimary() <<
")";
556 virtualDesktop->addScreen(screen);
557 QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary());
560 for (QPlatformScreen *ps : old) {
561 qCDebug(lcQpaScreen) <<
"destroy screen: " << ps;
562 QWindowSystemInterface::handleScreenRemoved(ps);
563 virtualDesktop->removeScreen(ps);
567 virtualDesktop->setScreens(std::move(siblings));
#define Q_XCB_REPLY(call,...)
#define Q_XCB_REPLY_UNCHECKED(call,...)