Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qxcbscreen.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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// Qt-Security score:significant reason:default
4
5#include "qxcbscreen.h"
6#include "qxcbwindow.h"
7#include "qxcbcursor.h"
8#include "qxcbimage.h"
9#include "qnamespace.h"
10#include "qxcbxsettings.h"
11
12#include <stdio.h>
13
14#include <QDebug>
15#include <QtAlgorithms>
16
17#include <qpa/qwindowsysteminterface.h>
18#include <private/qmath_p.h>
19#include <QtGui/private/qhighdpiscaling_p.h>
20
21QT_BEGIN_NAMESPACE
22
23QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t *screen, int number)
24 : QXcbObject(connection)
25 , m_screen(screen)
26 , m_number(number)
27 , m_xSettings(new QXcbXSettings(this))
28{
29 const QByteArray cmAtomName = "_NET_WM_CM_S" + QByteArray::number(m_number);
30 m_net_wm_cm_atom = connection->internAtom(cmAtomName.constData());
31 m_compositingActive = connection->selectionOwner(m_net_wm_cm_atom);
32
33 m_workArea = getWorkArea();
34
35 readXResources();
36
37 auto rootAttribs = Q_XCB_REPLY_UNCHECKED(xcb_get_window_attributes, xcb_connection(),
38 screen->root);
39 const quint32 existingEventMask = !rootAttribs ? 0 : rootAttribs->your_event_mask;
40
41 const quint32 mask = XCB_CW_EVENT_MASK;
42 const quint32 values[] = {
43 // XCB_CW_EVENT_MASK
44 XCB_EVENT_MASK_ENTER_WINDOW
45 | XCB_EVENT_MASK_LEAVE_WINDOW
46 | XCB_EVENT_MASK_PROPERTY_CHANGE
47 | XCB_EVENT_MASK_STRUCTURE_NOTIFY // for the "MANAGER" atom (system tray notification).
48 | existingEventMask // don't overwrite the event mask on the root window
49 };
50
51 xcb_change_window_attributes(xcb_connection(), screen->root, mask, values);
52
53 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
54 false, screen->root,
55 atom(QXcbAtom::Atom_NET_SUPPORTING_WM_CHECK),
56 XCB_ATOM_WINDOW, 0, 1024);
57 if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
58 xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply.get()));
59
60 if (windowManager != XCB_WINDOW_NONE)
61 m_windowManagerName = QXcbWindow::windowTitle(connection, windowManager);
62 }
63
64 xcb_depth_iterator_t depth_iterator =
65 xcb_screen_allowed_depths_iterator(screen);
66
67 while (depth_iterator.rem) {
68 xcb_depth_t *depth = depth_iterator.data;
69 xcb_visualtype_iterator_t visualtype_iterator =
70 xcb_depth_visuals_iterator(depth);
71
72 while (visualtype_iterator.rem) {
73 xcb_visualtype_t *visualtype = visualtype_iterator.data;
74 m_visuals.insert(visualtype->visual_id, *visualtype);
75 m_visualDepths.insert(visualtype->visual_id, depth->depth);
76 xcb_visualtype_next(&visualtype_iterator);
77 }
78
79 xcb_depth_next(&depth_iterator);
80 }
81
82 auto dpiChangedCallback = [](QXcbVirtualDesktop *desktop, const QByteArray &, const QVariant &property, void *) {
83 if (!desktop->setDpiFromXSettings(property))
84 return;
85 const auto dpi = desktop->forcedDpi();
86 for (QXcbScreen *screen : desktop->connection()->screens())
87 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->QPlatformScreen::screen(), dpi, dpi);
88 };
89 setDpiFromXSettings(xSettings()->setting("Xft/DPI"));
90 xSettings()->registerCallbackForProperty("Xft/DPI", dpiChangedCallback, nullptr);
91}
92
94{
95 delete m_xSettings;
96
97 for (auto cmap : std::as_const(m_visualColormaps))
98 xcb_free_colormap(xcb_connection(), cmap);
99}
100
102{
103 const QSize virtualSize = size();
104 const QSize virtualSizeMillimeters = physicalSize();
105
106 return QDpi(Q_MM_PER_INCH * virtualSize.width() / virtualSizeMillimeters.width(),
107 Q_MM_PER_INCH * virtualSize.height() / virtualSizeMillimeters.height());
108}
109
110QXcbScreen *QXcbVirtualDesktop::screenAt(const QPoint &pos) const
111{
112 const auto screens = connection()->screens();
113 for (QXcbScreen *screen : screens) {
114 if (screen->virtualDesktop() == this && screen->geometry().contains(pos))
115 return screen;
116 }
117 return nullptr;
118}
119
120void QXcbVirtualDesktop::addScreen(QPlatformScreen *s)
121{
122 ((QXcbScreen *) s)->isPrimary() ? m_screens.prepend(s) : m_screens.append(s);
123}
124
125void QXcbVirtualDesktop::setPrimaryScreen(QPlatformScreen *s)
126{
127 const int idx = m_screens.indexOf(s);
128 Q_ASSERT(idx > -1);
129 m_screens.swapItemsAt(0, idx);
130}
131
133{
134 return m_xSettings;
135}
136
138{
139 if (connection()->hasXFixes())
140 return m_compositingActive;
141 else
142 return connection()->selectionOwner(m_net_wm_cm_atom);
143}
144
145void QXcbVirtualDesktop::handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event)
146{
147 if (notify_event->selection == m_net_wm_cm_atom)
148 m_compositingActive = notify_event->owner;
149}
150
152{
153 if (connection()->hasXFixes()) {
154 const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
155 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
156 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
157 xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->qtSelectionOwner(), m_net_wm_cm_atom, mask);
158 }
159}
160
161/*!
162 \brief handle the XCB screen change event and update properties
163
164 On a mobile device, the ideal use case is that the accelerometer would
165 drive the orientation. This could be achieved by using QSensors to read the
166 accelerometer and adjusting the rotation in QML, or by reading the
167 orientation from the QScreen object and doing the same, or in many other
168 ways. However, on X we have the XRandR extension, which makes it possible
169 to have the whole screen rotated, so that individual apps DO NOT have to
170 rotate themselves. Apps could optionally use the
171 QScreen::primaryOrientation property to optimize layout though.
172 Furthermore, there is no support in X for accelerometer events anyway. So
173 it makes more sense on a Linux system running X to just run a daemon which
174 monitors the accelerometer and runs xrandr automatically to do the rotation,
175 then apps do not have to be aware of it (but probably the window manager
176 would resize them accordingly). updateGeometry() is written with this
177 design in mind. Therefore the physical geometry, available geometry,
178 virtual geometry, orientation and primaryOrientation should all change at
179 the same time. On a system which cannot rotate the whole screen, it would
180 be correct for only the orientation (not the primary orientation) to
181 change.
182*/
183void QXcbVirtualDesktop::handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event)
184{
185 // No need to do anything when screen rotation did not change - if any
186 // xcb output geometry has changed, we will get RRCrtcChangeNotify and
187 // RROutputChangeNotify events next
188 if (change_event->rotation == m_rotation)
189 return;
190
191 m_rotation = change_event->rotation;
192 switch (m_rotation) {
193 case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal
194 m_screen->width_in_pixels = change_event->width;
195 m_screen->height_in_pixels = change_event->height;
196 m_screen->width_in_millimeters = change_event->mwidth;
197 m_screen->height_in_millimeters = change_event->mheight;
198 break;
199 case XCB_RANDR_ROTATION_ROTATE_90: // xrandr --rotate left
200 m_screen->width_in_pixels = change_event->height;
201 m_screen->height_in_pixels = change_event->width;
202 m_screen->width_in_millimeters = change_event->mheight;
203 m_screen->height_in_millimeters = change_event->mwidth;
204 break;
205 case XCB_RANDR_ROTATION_ROTATE_180: // xrandr --rotate inverted
206 m_screen->width_in_pixels = change_event->width;
207 m_screen->height_in_pixels = change_event->height;
208 m_screen->width_in_millimeters = change_event->mwidth;
209 m_screen->height_in_millimeters = change_event->mheight;
210 break;
211 case XCB_RANDR_ROTATION_ROTATE_270: // xrandr --rotate right
212 m_screen->width_in_pixels = change_event->height;
213 m_screen->height_in_pixels = change_event->width;
214 m_screen->width_in_millimeters = change_event->mheight;
215 m_screen->height_in_millimeters = change_event->mwidth;
216 break;
217 // We don't need to do anything with these, since QScreen doesn't store reflection state,
218 // and Qt-based applications probably don't need to care about it anyway.
219 case XCB_RANDR_ROTATION_REFLECT_X: break;
220 case XCB_RANDR_ROTATION_REFLECT_Y: break;
221 }
222
223 for (QPlatformScreen *platformScreen : std::as_const(m_screens)) {
224 QDpi ldpi = platformScreen->logicalDpi();
225 QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(platformScreen->screen(), ldpi.first, ldpi.second);
226 }
227}
228
229/*! \internal
230
231 Using _NET_WORKAREA to calculate the available desktop geometry on multi-head systems (systems
232 with more than one monitor) is unreliable. Different WMs have different interpretations of what
233 _NET_WORKAREA means with multiple attached monitors. This gets worse when monitors have
234 different dimensions and/or screens are not virtually aligned. In Qt we want the available
235 geometry per monitor (QScreen), not desktop (represented by _NET_WORKAREA). WM specification
236 does not have an atom for this. Thus, QScreen is limited by the lack of support from the
237 underlying system.
238
239 One option could be that Qt does WM's job of calculating this by subtracting geometries of
240 _NET_WM_STRUT_PARTIAL and windows where _NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_DOCK.
241 But this won't work on Gnome 3 shell as it seems that on this desktop environment the tool panel
242 is painted directly on the root window. Maybe there is some Gnome/GTK API that could be used
243 to get height of the panel, but I did not find one. Maybe other WMs have their own tricks, so
244 the reliability of this approach is questionable.
245 */
246QRect QXcbVirtualDesktop::getWorkArea() const
247{
248 QRect r;
249 auto workArea = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), false, screen()->root,
250 atom(QXcbAtom::Atom_NET_WORKAREA),
251 XCB_ATOM_CARDINAL, 0, 1024);
252 if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) {
253 // If workArea->value_len > 4, the remaining ones seem to be for WM's virtual desktops
254 // (don't mess with QXcbVirtualDesktop which represents an X screen).
255 // But QScreen doesn't know about that concept. In reality there could be a
256 // "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop.
257 // But for now just assume the first 4 values give us the geometry of the
258 // "work area", AKA "available geometry"
259 uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea.get());
260 r = QRect(geom[0], geom[1], geom[2], geom[3]);
261 } else {
262 r.setWidth(-1);
263 }
264 return r;
265}
266
268{
269 QRect workArea = getWorkArea();
270 if (m_workArea != workArea) {
271 m_workArea = workArea;
272 for (QPlatformScreen *screen : std::as_const(m_screens))
273 ((QXcbScreen *)screen)->updateAvailableGeometry();
274 }
275}
276
277QRect QXcbVirtualDesktop::availableGeometry(const QRect &screenGeometry) const
278{
279 return m_workArea.width() >= 0 ? screenGeometry & m_workArea : screenGeometry;
280}
281
282static inline QSizeF sizeInMillimeters(const QSize &size, const QDpi &dpi)
283{
284 return QSizeF(Q_MM_PER_INCH * size.width() / dpi.first,
285 Q_MM_PER_INCH * size.height() / dpi.second);
286}
287
288bool QXcbVirtualDesktop::xResource(const QByteArray &identifier,
289 const QByteArray &expectedIdentifier,
290 QByteArray& stringValue)
291{
292 if (identifier.startsWith(expectedIdentifier)) {
293 stringValue = identifier.mid(expectedIdentifier.size());
294 return true;
295 }
296 return false;
297}
298
299static bool parseXftInt(const QByteArray& stringValue, int *value)
300{
301 Q_ASSERT(value);
302 bool ok;
303 *value = stringValue.toInt(&ok);
304 return ok;
305}
306
307static bool parseXftDpi(const QByteArray& stringValue, int *value)
308{
309 Q_ASSERT(value);
310 bool ok = parseXftInt(stringValue, value);
311 // Support GNOME 3 bug that wrote DPI with fraction:
312 if (!ok)
313 *value = qRound(stringValue.toDouble(&ok));
314 return ok;
315}
316
317static QFontEngine::HintStyle parseXftHintStyle(const QByteArray& stringValue)
318{
319 if (stringValue == "hintfull")
320 return QFontEngine::HintFull;
321 else if (stringValue == "hintnone")
322 return QFontEngine::HintNone;
323 else if (stringValue == "hintmedium")
324 return QFontEngine::HintMedium;
325 else if (stringValue == "hintslight")
326 return QFontEngine::HintLight;
327
328 return QFontEngine::HintStyle(-1);
329}
330
331static QFontEngine::SubpixelAntialiasingType parseXftRgba(const QByteArray& stringValue)
332{
333 if (stringValue == "none")
334 return QFontEngine::Subpixel_None;
335 else if (stringValue == "rgb")
336 return QFontEngine::Subpixel_RGB;
337 else if (stringValue == "bgr")
338 return QFontEngine::Subpixel_BGR;
339 else if (stringValue == "vrgb")
340 return QFontEngine::Subpixel_VRGB;
341 else if (stringValue == "vbgr")
342 return QFontEngine::Subpixel_VBGR;
343
344 return QFontEngine::SubpixelAntialiasingType(-1);
345}
346
347void QXcbVirtualDesktop::readXResources()
348{
349 int offset = 0;
350 QByteArray resources;
351 while (true) {
352 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
353 false, screen()->root,
354 XCB_ATOM_RESOURCE_MANAGER,
355 XCB_ATOM_STRING, offset/4, 8192);
356 bool more = false;
357 if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
358 resources += QByteArray((const char *)xcb_get_property_value(reply.get()), xcb_get_property_value_length(reply.get()));
359 offset += xcb_get_property_value_length(reply.get());
360 more = reply->bytes_after != 0;
361 }
362
363 if (!more)
364 break;
365 }
366
367 QList<QByteArray> split = resources.split('\n');
368 for (int i = 0; i < split.size(); ++i) {
369 const QByteArray &r = split.at(i);
370 int value;
371 QByteArray stringValue;
372 if (xResource(r, "Xft.dpi:\t", stringValue)) {
373 if (parseXftDpi(stringValue, &value))
374 m_forcedDpi = value;
375 } else if (xResource(r, "Xft.hintstyle:\t", stringValue)) {
376 m_hintStyle = parseXftHintStyle(stringValue);
377 } else if (xResource(r, "Xft.antialias:\t", stringValue)) {
378 if (parseXftInt(stringValue, &value))
379 m_antialiasingEnabled = value;
380 } else if (xResource(r, "Xft.rgba:\t", stringValue)) {
381 m_subpixelType = parseXftRgba(stringValue);
382 }
383 }
384}
385
386bool QXcbVirtualDesktop::setDpiFromXSettings(const QVariant &property)
387{
388 bool ok;
389 int dpiTimes1k = property.toInt(&ok);
390 if (!ok)
391 return false;
392 int dpi = dpiTimes1k / 1024;
393 if (m_forcedDpi == dpi)
394 return false;
395 m_forcedDpi = dpi;
396 return true;
397}
398
399QSurfaceFormat QXcbVirtualDesktop::surfaceFormatFor(const QSurfaceFormat &format) const
400{
401 const xcb_visualid_t xcb_visualid = connection()->hasDefaultVisualId() ? connection()->defaultVisualId()
402 : screen()->root_visual;
403 const xcb_visualtype_t *xcb_visualtype = visualForId(xcb_visualid);
404
405 const int redSize = qPopulationCount(xcb_visualtype->red_mask);
406 const int greenSize = qPopulationCount(xcb_visualtype->green_mask);
407 const int blueSize = qPopulationCount(xcb_visualtype->blue_mask);
408
409 QSurfaceFormat result = format;
410
411 if (result.redBufferSize() < 0)
412 result.setRedBufferSize(redSize);
413
414 if (result.greenBufferSize() < 0)
415 result.setGreenBufferSize(greenSize);
416
417 if (result.blueBufferSize() < 0)
418 result.setBlueBufferSize(blueSize);
419
420 return result;
421}
422
423const xcb_visualtype_t *QXcbVirtualDesktop::visualForFormat(const QSurfaceFormat &format) const
424{
425 const xcb_visualtype_t *candidate = nullptr;
426
427 for (const xcb_visualtype_t &xcb_visualtype : m_visuals) {
428
429 const int redSize = qPopulationCount(xcb_visualtype.red_mask);
430 const int greenSize = qPopulationCount(xcb_visualtype.green_mask);
431 const int blueSize = qPopulationCount(xcb_visualtype.blue_mask);
432 const int alphaSize = depthOfVisual(xcb_visualtype.visual_id) - redSize - greenSize - blueSize;
433
434 if (format.redBufferSize() != -1 && redSize != format.redBufferSize())
435 continue;
436
437 if (format.greenBufferSize() != -1 && greenSize != format.greenBufferSize())
438 continue;
439
440 if (format.blueBufferSize() != -1 && blueSize != format.blueBufferSize())
441 continue;
442
443 if (format.alphaBufferSize() != -1 && alphaSize != format.alphaBufferSize())
444 continue;
445
446 // Try to find a RGB visual rather than e.g. BGR or GBR
447 if (qCountTrailingZeroBits(xcb_visualtype.blue_mask) == 0)
448 return &xcb_visualtype;
449
450 // In case we do not find anything we like, just remember the first one
451 // and hope for the best:
452 if (!candidate)
453 candidate = &xcb_visualtype;
454 }
455
456 return candidate;
457}
458
459const xcb_visualtype_t *QXcbVirtualDesktop::visualForId(xcb_visualid_t visualid) const
460{
461 QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);
462 if (it == m_visuals.constEnd())
463 return nullptr;
464 return &*it;
465}
466
467quint8 QXcbVirtualDesktop::depthOfVisual(xcb_visualid_t visualid) const
468{
469 QMap<xcb_visualid_t, quint8>::const_iterator it = m_visualDepths.find(visualid);
470 if (it == m_visualDepths.constEnd())
471 return 0;
472 return *it;
473}
474
475xcb_colormap_t QXcbVirtualDesktop::colormapForVisual(xcb_visualid_t visualid) const
476{
477 auto it = m_visualColormaps.constFind(visualid);
478 if (it != m_visualColormaps.constEnd())
479 return *it;
480
481 auto cmap = xcb_generate_id(xcb_connection());
482 xcb_create_colormap(xcb_connection(),
483 XCB_COLORMAP_ALLOC_NONE,
484 cmap,
485 screen()->root,
486 visualid);
487 m_visualColormaps.insert(visualid, cmap);
488 return cmap;
489}
490
491QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
492 xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output)
493 : QXcbObject(connection)
494 , m_virtualDesktop(virtualDesktop)
495 , m_monitor(nullptr)
496 , m_output(outputId)
497 , m_crtc(output ? output->crtc : XCB_NONE)
498 , m_outputName(getOutputName(output))
499 , m_outputSizeMillimeters(output ? QSize(output->mm_width, output->mm_height) : QSize())
500 , m_cursor(std::make_unique<QXcbCursor>(connection, this))
501{
502 if (connection->isAtLeastXRandR12()) {
503 auto crtc = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_crtc_info, xcb_connection(),
504 m_crtc, output ? output->timestamp : 0);
505 if (crtc) {
506 updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation);
507 updateRefreshRate(crtc->mode);
508 }
509 }
510
511 if (m_geometry.isEmpty())
512 m_geometry = QRect(QPoint(), virtualDesktop->size());
513
514 if (m_availableGeometry.isEmpty())
515 m_availableGeometry = m_virtualDesktop->availableGeometry(m_geometry);
516
517 if (m_sizeMillimeters.isEmpty())
518 m_sizeMillimeters = virtualDesktop->physicalSize();
519
520 updateColorSpaceAndEdid();
521}
522
523void QXcbScreen::updateColorSpaceAndEdid()
524{
525 {
526 // Read colord ICC data (from GNOME settings)
527 auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
528 false, screen()->root,
529 connection()->atom(QXcbAtom::Atom_ICC_PROFILE),
530 XCB_ATOM_CARDINAL, 0, 8192);
531 if (reply->format == 8 && reply->type == XCB_ATOM_CARDINAL) {
532 QByteArray data(reinterpret_cast<const char *>(xcb_get_property_value(reply.get())), reply->value_len);
533 m_colorSpace = QColorSpace::fromIccProfile(data);
534 }
535 }
536 if (connection()->isAtLeastXRandR12()) { // Parse EDID
537 QByteArray edid = getEdid();
538 if (m_edid.parse(edid)) {
539 qCDebug(lcQpaScreen, "EDID data for output \"%s\": identifier '%s', manufacturer '%s',"
540 "model '%s', serial '%s', physical size: %.2fx%.2f",
541 name().toLatin1().constData(),
542 m_edid.identifier.toLatin1().constData(),
543 m_edid.manufacturer.toLatin1().constData(),
544 m_edid.model.toLatin1().constData(),
545 m_edid.serialNumber.toLatin1().constData(),
546 m_edid.physicalSize.width(), m_edid.physicalSize.height());
547 if (!m_colorSpace.isValid()) {
548 if (m_edid.sRgb)
549 m_colorSpace = QColorSpace::SRgb;
550 else {
551 if (!m_edid.useTables) {
552 m_colorSpace = QColorSpace(m_edid.whiteChromaticity, m_edid.redChromaticity,
553 m_edid.greenChromaticity, m_edid.blueChromaticity,
554 QColorSpace::TransferFunction::Gamma, m_edid.gamma);
555 } else {
556 if (m_edid.tables.size() == 1) {
557 m_colorSpace = QColorSpace(m_edid.whiteChromaticity, m_edid.redChromaticity,
558 m_edid.greenChromaticity, m_edid.blueChromaticity,
559 m_edid.tables[0]);
560 } else if (m_edid.tables.size() == 3) {
561 m_colorSpace = QColorSpace(m_edid.whiteChromaticity, m_edid.redChromaticity,
562 m_edid.greenChromaticity, m_edid.blueChromaticity,
563 m_edid.tables[0], m_edid.tables[1], m_edid.tables[2]);
564 }
565 }
566 }
567 }
568 } else {
569 // This property is defined by the xrandr spec. Parsing failure indicates a valid error,
570 // but keep this as debug, for details see 4f515815efc318ddc909a0399b71b8a684962f38.
571 qCDebug(lcQpaScreen) << "Failed to parse EDID data for output" << name() <<
572 "edid data: " << edid;
573 }
574 }
575 if (!m_colorSpace.isValid())
576 m_colorSpace = QColorSpace::SRgb;
577}
578
579QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
580 xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp)
581 : QXcbObject(connection)
582 , m_virtualDesktop(virtualDesktop)
583 , m_monitor(monitorInfo)
584 , m_cursor(std::make_unique<QXcbCursor>(connection, this))
585{
586 setMonitor(monitorInfo, timestamp);
587}
588
589void QXcbScreen::setMonitor(xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp)
590{
591 if (!connection()->isAtLeastXRandR15())
592 return;
593
594 m_outputs.clear();
595 m_crtcs.clear();
596 m_output = XCB_NONE;
597 m_crtc = XCB_NONE;
598 m_singlescreen = false;
599
600 if (!monitorInfo) {
601 m_monitor = nullptr;
602 m_mode = XCB_NONE;
603 m_outputName = defaultName();
604 // TODO: Send an event to the QScreen instance that the screen changed its name
605 return;
606 }
607
608 m_monitor = monitorInfo;
609 qCDebug(lcQpaScreen) << "xcb_randr_monitor_info_t: primary=" << m_monitor->primary << ", x=" << m_monitor->x << ", y=" << m_monitor->y
610 << ", width=" << m_monitor->width << ", height=" << m_monitor->height
611 << ", width_in_millimeters=" << m_monitor->width_in_millimeters << ", height_in_millimeters=" << m_monitor->height_in_millimeters;
612 QRect monitorGeometry = QRect(m_monitor->x, m_monitor->y,
613 m_monitor->width, m_monitor->height);
614 m_sizeMillimeters = QSize(m_monitor->width_in_millimeters, m_monitor->height_in_millimeters);
615
616 int outputCount = xcb_randr_monitor_info_outputs_length(m_monitor);
617 xcb_randr_output_t *outputs = nullptr;
618 if (outputCount) {
619 outputs = xcb_randr_monitor_info_outputs(m_monitor);
620 for (int i = 0; i < outputCount; i++) {
621 auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
622 xcb_connection(), outputs[i], timestamp);
623 // Invalid, disconnected or disabled output
624 if (!output)
625 continue;
626
627 if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
628 qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
629 QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
630 xcb_randr_get_output_info_name_length(output.get()))));
631 continue;
632 }
633
634 if (output->crtc == XCB_NONE) {
635 qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
636 QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
637 xcb_randr_get_output_info_name_length(output.get()))));
638 continue;
639 }
640
641 m_outputs << outputs[i];
642 if (m_output == XCB_NONE) {
643 m_output = outputs[i];
644 m_outputSizeMillimeters = QSize(output->mm_width, output->mm_height);
645 }
646 m_crtcs << output->crtc;
647 if (m_crtc == XCB_NONE)
648 m_crtc = output->crtc;
649 }
650 }
651
652 if (m_crtcs.size() == 1) {
653 auto crtc = Q_XCB_REPLY(xcb_randr_get_crtc_info,
654 xcb_connection(), m_crtcs[0], timestamp);
655 if (crtc == XCB_NONE) {
656 qCDebug(lcQpaScreen, "Didn't get crtc info when m_crtcs.size() == 1");
657 } else {
658 m_singlescreen = (monitorGeometry == (QRect(crtc->x, crtc->y, crtc->width, crtc->height)));
659 if (m_singlescreen) {
660 if (crtc->mode) {
661 updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation);
662 if (mode() != crtc->mode)
663 updateRefreshRate(crtc->mode);
664 }
665 }
666 }
667 }
668
669 if (!m_singlescreen)
670 m_geometry = monitorGeometry;
671 m_availableGeometry = m_virtualDesktop->availableGeometry(m_geometry);
672 if (m_geometry.isEmpty())
673 m_geometry = QRect(QPoint(), virtualDesktop()->size());
674 if (m_availableGeometry.isEmpty())
675 m_availableGeometry = m_virtualDesktop->availableGeometry(m_geometry);
676
677 if (m_sizeMillimeters.isEmpty())
678 m_sizeMillimeters = virtualDesktop()->physicalSize();
679
680 m_outputName = getName(monitorInfo);
681 m_primary = false;
682 if (connection()->primaryScreenNumber() == virtualDesktop()->number()) {
683 if (monitorInfo->primary || isPrimaryInXScreen())
684 m_primary = true;
685 }
686
687 updateColorSpaceAndEdid();
688}
689
690QString QXcbScreen::defaultName()
691{
692 QString name;
693 QByteArray displayName = connection()->displayName();
694 int dotPos = displayName.lastIndexOf('.');
695 if (dotPos != -1)
696 displayName.truncate(dotPos);
697 name = QString::fromLocal8Bit(displayName) + u'.'
698 + QString::number(m_virtualDesktop->number());
699 return name;
700}
701
702bool QXcbScreen::isPrimaryInXScreen()
703{
704 auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, connection()->xcb_connection(), root());
705 if (!primary)
706 qWarning("failed to get the primary output of the screen");
707
708 const bool isPrimary = primary ? (m_monitor ? m_outputs.contains(primary->output) : m_output == primary->output) : false;
709
710 return isPrimary;
711}
712
713QXcbScreen::~QXcbScreen()
714{
715}
716
717QString QXcbScreen::getOutputName(xcb_randr_get_output_info_reply_t *outputInfo)
718{
719 QString name;
720 if (outputInfo) {
721 name = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(outputInfo),
722 xcb_randr_get_output_info_name_length(outputInfo));
723 } else {
724 name = defaultName();
725 }
726 return name;
727}
728
729QString QXcbScreen::getName(xcb_randr_monitor_info_t *monitorInfo)
730{
731 QString name;
732 QByteArray ba = connection()->atomName(monitorInfo->name);
733 if (!ba.isEmpty()) {
734 name = QString::fromLatin1(ba.constData());
735 } else {
736 QByteArray displayName = connection()->displayName();
737 int dotPos = displayName.lastIndexOf('.');
738 if (dotPos != -1)
739 displayName.truncate(dotPos);
740 name = QString::fromLocal8Bit(displayName) + u'.'
741 + QString::number(m_virtualDesktop->number());
742 }
743 return name;
744}
745
746QString QXcbScreen::manufacturer() const
747{
748 return m_edid.manufacturer;
749}
750
751QString QXcbScreen::model() const
752{
753 return m_edid.model;
754}
755
756QString QXcbScreen::serialNumber() const
757{
758 return m_edid.serialNumber;
759}
760
761QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
762{
763 xcb_window_t root = screen()->root;
764
765 int x = p.x();
766 int y = p.y();
767
768 xcb_window_t parent = root;
769 xcb_window_t child = root;
770
771 do {
772 auto translate_reply = Q_XCB_REPLY_UNCHECKED(xcb_translate_coordinates, xcb_connection(), parent, child, x, y);
773 if (!translate_reply) {
774 return nullptr;
775 }
776
777 parent = child;
778 child = translate_reply->child;
779 x = translate_reply->dst_x;
780 y = translate_reply->dst_y;
781
782 if (!child || child == root)
783 return nullptr;
784
785 QPlatformWindow *platformWindow = connection()->platformWindowFromId(child);
786 if (platformWindow)
787 return platformWindow->window();
788 } while (parent != child);
789
790 return nullptr;
791}
792
793void QXcbScreen::windowShown(QXcbWindow *window)
794{
795 // Freedesktop.org Startup Notification
796 if (!connection()->startupId().isEmpty() && window->window()->isTopLevel()) {
797 sendStartupMessage(QByteArrayLiteral("remove: ID=") + connection()->startupId());
798 connection()->setStartupId({});
799 }
800}
801
802QSurfaceFormat QXcbScreen::surfaceFormatFor(const QSurfaceFormat &format) const
803{
804 return m_virtualDesktop->surfaceFormatFor(format);
805}
806
807const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
808{
809 return m_virtualDesktop->visualForId(visualid);
810}
811
812void QXcbScreen::sendStartupMessage(const QByteArray &message) const
813{
814 xcb_window_t rootWindow = root();
815
816 xcb_client_message_event_t ev;
817 ev.response_type = XCB_CLIENT_MESSAGE;
818 ev.format = 8;
819 ev.type = connection()->atom(QXcbAtom::Atom_NET_STARTUP_INFO_BEGIN);
820 ev.sequence = 0;
821 ev.window = rootWindow;
822 int sent = 0;
823 int length = message.size() + 1; // include NUL byte
824 const char *data = message.constData();
825 do {
826 if (sent == 20)
827 ev.type = connection()->atom(QXcbAtom::Atom_NET_STARTUP_INFO);
828
829 const int start = sent;
830 const int numBytes = qMin(length - start, 20);
831 memcpy(ev.data.data8, data + start, numBytes);
832 xcb_send_event(connection()->xcb_connection(), false, rootWindow, XCB_EVENT_MASK_PROPERTY_CHANGE, (const char *) &ev);
833
834 sent += numBytes;
835 } while (sent < length);
836}
837
838QRect QXcbScreen::availableGeometry() const
839{
840 static bool enforceNetWorkarea = !qEnvironmentVariableIsEmpty("QT_RELY_ON_NET_WORKAREA_ATOM");
841 bool isMultiHeadSystem = virtualSiblings().size() > 1;
842 bool useScreenGeometry = isMultiHeadSystem && !enforceNetWorkarea;
843 return useScreenGeometry ? m_geometry : m_availableGeometry;
844}
845
846QImage::Format QXcbScreen::format() const
847{
848 QImage::Format format;
849 bool needsRgbSwap;
850 qt_xcb_imageFormatForVisual(connection(), screen()->root_depth, visualForId(screen()->root_visual), &format, &needsRgbSwap);
851 // We are ignoring needsRgbSwap here and just assumes the backing-store will handle it.
852 if (format != QImage::Format_Invalid)
853 return format;
854 return QImage::Format_RGB32;
855}
856
857int QXcbScreen::forcedDpi() const
858{
859 const int forcedDpi = m_virtualDesktop->forcedDpi();
860 if (forcedDpi > 0)
861 return forcedDpi;
862 return 0;
863}
864
865QDpi QXcbScreen::logicalDpi() const
866{
867 const int forcedDpi = this->forcedDpi();
868 if (forcedDpi > 0)
869 return QDpi(forcedDpi, forcedDpi);
870
871 // Fall back to 96 DPI in case no logical DPI is set. We don't want to
872 // return physical DPI here, since that is a different type of DPI: Logical
873 // DPI typically accounts for user preference and viewing distance, and is
874 // quantized into DPI classes (96, 144, 192, etc); pysical DPI is an exact
875 // physical measure.
876 return QDpi(96, 96);
877}
878
879QPlatformCursor *QXcbScreen::cursor() const
880{
881 return m_cursor.get();
882}
883
884void QXcbScreen::setOutput(xcb_randr_output_t outputId,
885 xcb_randr_get_output_info_reply_t *outputInfo)
886{
887 m_monitor = nullptr;
888 m_output = outputId;
889 m_crtc = outputInfo ? outputInfo->crtc : XCB_NONE;
890 m_mode = XCB_NONE;
891 m_outputName = getOutputName(outputInfo);
892 // TODO: Send an event to the QScreen instance that the screen changed its name
893}
894
895void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
896{
897 if (!connection()->isAtLeastXRandR12())
898 return;
899
900 auto crtc = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_crtc_info, xcb_connection(),
901 m_crtc, timestamp);
902 if (crtc)
903 updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation);
904}
905
906void QXcbScreen::updateGeometry(const QRect &geometry, uint8_t rotation)
907{
908 const Qt::ScreenOrientation oldOrientation = m_orientation;
909
910 switch (rotation) {
911 case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal
912 m_orientation = Qt::LandscapeOrientation;
913 if (!m_monitor)
914 m_sizeMillimeters = m_outputSizeMillimeters;
915 break;
916 case XCB_RANDR_ROTATION_ROTATE_90: // xrandr --rotate left
917 m_orientation = Qt::PortraitOrientation;
918 if (!m_monitor)
919 m_sizeMillimeters = m_outputSizeMillimeters.transposed();
920 break;
921 case XCB_RANDR_ROTATION_ROTATE_180: // xrandr --rotate inverted
922 m_orientation = Qt::InvertedLandscapeOrientation;
923 if (!m_monitor)
924 m_sizeMillimeters = m_outputSizeMillimeters;
925 break;
926 case XCB_RANDR_ROTATION_ROTATE_270: // xrandr --rotate right
927 m_orientation = Qt::InvertedPortraitOrientation;
928 if (!m_monitor)
929 m_sizeMillimeters = m_outputSizeMillimeters.transposed();
930 break;
931 }
932
933 // It can be that physical size is unknown while virtual size
934 // is known (probably back-calculated from DPI and resolution),
935 // e.g. on VNC or with some hardware.
936 if (m_sizeMillimeters.isEmpty())
937 m_sizeMillimeters = sizeInMillimeters(geometry.size(), m_virtualDesktop->dpi());
938
939 m_geometry = geometry;
940 m_availableGeometry = m_virtualDesktop->availableGeometry(m_geometry);
941 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry);
942 if (m_orientation != oldOrientation)
943 QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), m_orientation);
944}
945
946void QXcbScreen::updateAvailableGeometry()
947{
948 QRect availableGeometry = m_virtualDesktop->availableGeometry(m_geometry);
949 if (m_availableGeometry != availableGeometry) {
950 m_availableGeometry = availableGeometry;
951 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry);
952 }
953}
954
955void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode)
956{
957 if (!connection()->isAtLeastXRandR12())
958 return;
959
960 if (m_mode == mode)
961 return;
962
963 // we can safely use get_screen_resources_current here, because in order to
964 // get here, we must have called get_screen_resources before
965 auto resources = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_screen_resources_current,
966 xcb_connection(), screen()->root);
967 if (resources) {
968 xcb_randr_mode_info_iterator_t modesIter =
969 xcb_randr_get_screen_resources_current_modes_iterator(resources.get());
970 for (; modesIter.rem; xcb_randr_mode_info_next(&modesIter)) {
971 xcb_randr_mode_info_t *modeInfo = modesIter.data;
972 if (modeInfo->id == mode) {
973 const uint32_t dotCount = modeInfo->htotal * modeInfo->vtotal;
974 m_refreshRate = (dotCount != 0) ? modeInfo->dot_clock / qreal(dotCount) : 0;
975 m_mode = mode;
976 break;
977 }
978 }
979
980 QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), m_refreshRate);
981 }
982}
983
984static inline bool translate(xcb_connection_t *connection, xcb_window_t child, xcb_window_t parent,
985 int *x, int *y)
986{
987 auto translate_reply = Q_XCB_REPLY_UNCHECKED(xcb_translate_coordinates,
988 connection, child, parent, *x, *y);
989 if (!translate_reply)
990 return false;
991 *x = translate_reply->dst_x;
992 *y = translate_reply->dst_y;
993 return true;
994}
995
996QPixmap QXcbScreen::grabWindow(WId window, int xIn, int yIn, int width, int height) const
997{
998 if (width == 0 || height == 0)
999 return QPixmap();
1000
1001 int x = xIn;
1002 int y = yIn;
1003 QXcbScreen *screen = const_cast<QXcbScreen *>(this);
1004 xcb_window_t root = screen->root();
1005
1006 auto rootReply = Q_XCB_REPLY_UNCHECKED(xcb_get_geometry, xcb_connection(), root);
1007 if (!rootReply)
1008 return QPixmap();
1009
1010 const quint8 rootDepth = rootReply->depth;
1011
1012 QSize windowSize;
1013 quint8 effectiveDepth = 0;
1014 if (window) {
1015 auto windowReply = Q_XCB_REPLY_UNCHECKED(xcb_get_geometry, xcb_connection(), window);
1016 if (!windowReply)
1017 return QPixmap();
1018 windowSize = QSize(windowReply->width, windowReply->height);
1019 effectiveDepth = windowReply->depth;
1020 if (effectiveDepth == rootDepth) {
1021 // if the depth of the specified window and the root window are the
1022 // same, grab pixels from the root window (so that we get the any
1023 // overlapping windows and window manager frames)
1024
1025 // map x and y to the root window
1026 if (!translate(xcb_connection(), window, root, &x, &y))
1027 return QPixmap();
1028
1029 window = root;
1030 }
1031 } else {
1032 window = root;
1033 effectiveDepth = rootDepth;
1034 windowSize = m_geometry.size();
1035 x += m_geometry.x();
1036 y += m_geometry.y();
1037 }
1038
1039 if (width < 0)
1040 width = windowSize.width() - xIn;
1041 if (height < 0)
1042 height = windowSize.height() - yIn;
1043
1044 auto attributes_reply = Q_XCB_REPLY_UNCHECKED(xcb_get_window_attributes, xcb_connection(), window);
1045
1046 if (!attributes_reply)
1047 return QPixmap();
1048
1049 const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual);
1050
1051 xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection());
1052 xcb_create_pixmap(xcb_connection(), effectiveDepth, pixmap, window, width, height);
1053
1054 uint32_t gc_value_mask = XCB_GC_SUBWINDOW_MODE;
1055 uint32_t gc_value_list[] = { XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS };
1056
1057 xcb_gcontext_t gc = xcb_generate_id(xcb_connection());
1058 xcb_create_gc(xcb_connection(), gc, pixmap, gc_value_mask, gc_value_list);
1059
1060 xcb_copy_area(xcb_connection(), window, pixmap, gc, x, y, 0, 0, width, height);
1061
1062 QPixmap result = qt_xcb_pixmapFromXPixmap(connection(), pixmap, width, height, effectiveDepth, visual);
1063 xcb_free_gc(xcb_connection(), gc);
1064 xcb_free_pixmap(xcb_connection(), pixmap);
1065
1066 return result;
1067}
1068
1069QXcbXSettings *QXcbScreen::xSettings() const
1070{
1071 return m_virtualDesktop->xSettings();
1072}
1073
1074QByteArray QXcbScreen::getOutputProperty(xcb_atom_t atom) const
1075{
1076 QByteArray result;
1077
1078 auto reply = Q_XCB_REPLY(xcb_randr_get_output_property, xcb_connection(),
1079 m_output, atom, XCB_ATOM_ANY, 0, 100, false, false);
1080 if (reply && reply->type == XCB_ATOM_INTEGER && reply->format == 8) {
1081 quint8 *data = new quint8[reply->num_items];
1082 memcpy(data, xcb_randr_get_output_property_data(reply.get()), reply->num_items);
1083 result = QByteArray(reinterpret_cast<const char *>(data), reply->num_items);
1084 delete[] data;
1085 }
1086
1087 return result;
1088}
1089
1090QByteArray QXcbScreen::getEdid() const
1091{
1092 QByteArray result;
1093 if (!connection()->isAtLeastXRandR12())
1094 return result;
1095
1096 // Try a bunch of atoms
1097 result = getOutputProperty(atom(QXcbAtom::AtomEDID));
1098 if (result.isEmpty())
1099 result = getOutputProperty(atom(QXcbAtom::AtomEDID_DATA));
1100 if (result.isEmpty())
1101 result = getOutputProperty(atom(QXcbAtom::AtomXFree86_DDC_EDID1_RAWDATA));
1102
1103 return result;
1104}
1105
1106static inline void formatRect(QDebug &debug, const QRect r)
1107{
1108 debug << r.width() << 'x' << r.height()
1109 << Qt::forcesign << r.x() << r.y() << Qt::noforcesign;
1110}
1111
1112static inline void formatSizeF(QDebug &debug, const QSizeF s)
1113{
1114 debug << s.width() << 'x' << s.height() << "mm";
1115}
1116
1117QDebug operator<<(QDebug debug, const QXcbScreen *screen)
1118{
1119 const QDebugStateSaver saver(debug);
1120 debug.nospace();
1121 debug << "QXcbScreen(" << (const void *)screen;
1122 if (screen) {
1123 debug << Qt::fixed << qSetRealNumberPrecision(1);
1124 debug << ", name=" << screen->name();
1125 debug << ", geometry=";
1126 formatRect(debug, screen->geometry());
1127 debug << ", availableGeometry=";
1128 formatRect(debug, screen->availableGeometry());
1129 debug << ", devicePixelRatio=" << screen->devicePixelRatio();
1130 debug << ", logicalDpi=" << screen->logicalDpi();
1131 debug << ", physicalSize=";
1132 formatSizeF(debug, screen->physicalSize());
1133 // TODO 5.6 if (debug.verbosity() > 2) {
1134 debug << ", screenNumber=" << screen->screenNumber();
1135 const QSize virtualSize = screen->virtualDesktop()->size();
1136 debug << ", virtualSize=" << virtualSize.width() << 'x' << virtualSize.height() << " (";
1137 formatSizeF(debug, virtualSize);
1138 debug << "), orientation=" << screen->orientation();
1139 debug << ", depth=" << screen->depth();
1140 debug << ", refreshRate=" << screen->refreshRate();
1141 debug << ", root=" << Qt::hex << screen->root();
1142 debug << ", windowManagerName=" << screen->windowManagerName();
1143 }
1144 debug << ')';
1145 return debug;
1146}
1147
1148QT_END_NAMESPACE
QXcbConnection * connection() const
Definition qxcbobject.h:17
xcb_connection_t * xcb_connection() const
Definition qxcbobject.h:20
xcb_colormap_t colormapForVisual(xcb_visualid_t) const
bool compositingActive() const
void addScreen(QPlatformScreen *s)
const xcb_visualtype_t * visualForId(xcb_visualid_t) const
void handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event)
handle the XCB screen change event and update properties
const xcb_visualtype_t * visualForFormat(const QSurfaceFormat &format) const
quint8 depthOfVisual(xcb_visualid_t) const
QXcbXSettings * xSettings() const
void subscribeToXFixesSelectionNotify()
void setPrimaryScreen(QPlatformScreen *s)
QRect availableGeometry(const QRect &screenGeometry) const
void handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event)
QXcbScreen * screenAt(const QPoint &pos) const
QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &format) const
xcb_screen_t * screen() const
Definition qxcbscreen.h:38
#define Q_XCB_REPLY(call,...)
#define Q_XCB_REPLY_UNCHECKED(call,...)
static QSizeF sizeInMillimeters(const QSize &size, const QDpi &dpi)
static bool parseXftInt(const QByteArray &stringValue, int *value)
static void formatSizeF(QDebug &debug, const QSizeF s)
static QFontEngine::SubpixelAntialiasingType parseXftRgba(const QByteArray &stringValue)
static bool parseXftDpi(const QByteArray &stringValue, int *value)
static void formatRect(QDebug &debug, const QRect r)
static QFontEngine::HintStyle parseXftHintStyle(const QByteArray &stringValue)
static bool translate(xcb_connection_t *connection, xcb_window_t child, xcb_window_t parent, int *x, int *y)