xcb: Optimize QXcb{Screen,VirtualDesktop} for multi-monitor setup

Move non-monitor-specific members of QXcbScreen to QXcbVirtualDesktop:
they relate to an X screen rather than to a monitor.
It allows to save some memory and X server callings that were
previously made for each monitor.

Change-Id: I079cdec5f27ca93a2aade00c3040936a0e50b494
Reviewed-by: Gatis Paeglis <gatis.paeglis@qt.io>
This commit is contained in:
Alexander Volkov 2017-02-23 22:38:11 +03:00
parent 71264bae08
commit 4428763001
2 changed files with 267 additions and 241 deletions

View File

@ -65,6 +65,67 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t
m_compositingActive = connection->getSelectionOwner(m_net_wm_cm_atom);
m_workArea = getWorkArea();
readXResources();
auto rootAttribs = Q_XCB_REPLY_UNCHECKED(xcb_get_window_attributes, xcb_connection(),
screen->root);
const quint32 existingEventMask = !rootAttribs ? 0 : rootAttribs->your_event_mask;
const quint32 mask = XCB_CW_EVENT_MASK;
const quint32 values[] = {
// XCB_CW_EVENT_MASK
XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_LEAVE_WINDOW
| XCB_EVENT_MASK_PROPERTY_CHANGE
| XCB_EVENT_MASK_STRUCTURE_NOTIFY // for the "MANAGER" atom (system tray notification).
| existingEventMask // don't overwrite the event mask on the root window
};
xcb_change_window_attributes(xcb_connection(), screen->root, mask, values);
auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
false, screen->root,
atom(QXcbAtom::_NET_SUPPORTING_WM_CHECK),
XCB_ATOM_WINDOW, 0, 1024);
if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply.get()));
if (windowManager != XCB_WINDOW_NONE) {
auto windowManagerReply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
false, windowManager,
atom(QXcbAtom::_NET_WM_NAME),
atom(QXcbAtom::UTF8_STRING), 0, 1024);
if (windowManagerReply && windowManagerReply->format == 8 && windowManagerReply->type == atom(QXcbAtom::UTF8_STRING)) {
m_windowManagerName = QString::fromUtf8((const char *)xcb_get_property_value(windowManagerReply.get()),
xcb_get_property_value_length(windowManagerReply.get()));
}
}
}
const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id);
if (!sync_reply || !sync_reply->present)
m_syncRequestSupported = false;
else
m_syncRequestSupported = true;
xcb_depth_iterator_t depth_iterator =
xcb_screen_allowed_depths_iterator(screen);
while (depth_iterator.rem) {
xcb_depth_t *depth = depth_iterator.data;
xcb_visualtype_iterator_t visualtype_iterator =
xcb_depth_visuals_iterator(depth);
while (visualtype_iterator.rem) {
xcb_visualtype_t *visualtype = visualtype_iterator.data;
m_visuals.insert(visualtype->visual_id, *visualtype);
m_visualDepths.insert(visualtype->visual_id, depth->depth);
xcb_visualtype_next(&visualtype_iterator);
}
xcb_depth_next(&depth_iterator);
}
}
QXcbVirtualDesktop::~QXcbVirtualDesktop()
@ -164,6 +225,170 @@ static inline QSizeF sizeInMillimeters(const QSize &size, const QDpi &dpi)
Q_MM_PER_INCH * size.height() / dpi.second);
}
bool QXcbVirtualDesktop::xResource(const QByteArray &identifier,
const QByteArray &expectedIdentifier,
QByteArray& stringValue)
{
if (identifier.startsWith(expectedIdentifier)) {
stringValue = identifier.mid(expectedIdentifier.size());
return true;
}
return false;
}
static bool parseXftInt(const QByteArray& stringValue, int *value)
{
Q_ASSERT(value != 0);
bool ok;
*value = stringValue.toInt(&ok);
return ok;
}
static QFontEngine::HintStyle parseXftHintStyle(const QByteArray& stringValue)
{
if (stringValue == "hintfull")
return QFontEngine::HintFull;
else if (stringValue == "hintnone")
return QFontEngine::HintNone;
else if (stringValue == "hintmedium")
return QFontEngine::HintMedium;
else if (stringValue == "hintslight")
return QFontEngine::HintLight;
return QFontEngine::HintStyle(-1);
}
static QFontEngine::SubpixelAntialiasingType parseXftRgba(const QByteArray& stringValue)
{
if (stringValue == "none")
return QFontEngine::Subpixel_None;
else if (stringValue == "rgb")
return QFontEngine::Subpixel_RGB;
else if (stringValue == "bgr")
return QFontEngine::Subpixel_BGR;
else if (stringValue == "vrgb")
return QFontEngine::Subpixel_VRGB;
else if (stringValue == "vbgr")
return QFontEngine::Subpixel_VBGR;
return QFontEngine::SubpixelAntialiasingType(-1);
}
void QXcbVirtualDesktop::readXResources()
{
int offset = 0;
QByteArray resources;
while (true) {
auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
false, screen()->root,
XCB_ATOM_RESOURCE_MANAGER,
XCB_ATOM_STRING, offset/4, 8192);
bool more = false;
if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
resources += QByteArray((const char *)xcb_get_property_value(reply.get()), xcb_get_property_value_length(reply.get()));
offset += xcb_get_property_value_length(reply.get());
more = reply->bytes_after != 0;
}
if (!more)
break;
}
QList<QByteArray> split = resources.split('\n');
for (int i = 0; i < split.size(); ++i) {
const QByteArray &r = split.at(i);
int value;
QByteArray stringValue;
if (xResource(r, "Xft.dpi:\t", stringValue)) {
if (parseXftInt(stringValue, &value))
m_forcedDpi = value;
} else if (xResource(r, "Xft.hintstyle:\t", stringValue)) {
m_hintStyle = parseXftHintStyle(stringValue);
} else if (xResource(r, "Xft.antialias:\t", stringValue)) {
if (parseXftInt(stringValue, &value))
m_antialiasingEnabled = value;
} else if (xResource(r, "Xft.rgba:\t", stringValue)) {
m_subpixelType = parseXftRgba(stringValue);
}
}
}
QSurfaceFormat QXcbVirtualDesktop::surfaceFormatFor(const QSurfaceFormat &format) const
{
const xcb_visualid_t xcb_visualid = connection()->hasDefaultVisualId() ? connection()->defaultVisualId()
: screen()->root_visual;
const xcb_visualtype_t *xcb_visualtype = visualForId(xcb_visualid);
const int redSize = qPopulationCount(xcb_visualtype->red_mask);
const int greenSize = qPopulationCount(xcb_visualtype->green_mask);
const int blueSize = qPopulationCount(xcb_visualtype->blue_mask);
QSurfaceFormat result = format;
if (result.redBufferSize() < 0)
result.setRedBufferSize(redSize);
if (result.greenBufferSize() < 0)
result.setGreenBufferSize(greenSize);
if (result.blueBufferSize() < 0)
result.setBlueBufferSize(blueSize);
return result;
}
const xcb_visualtype_t *QXcbVirtualDesktop::visualForFormat(const QSurfaceFormat &format) const
{
const xcb_visualtype_t *candidate = nullptr;
for (const xcb_visualtype_t &xcb_visualtype : m_visuals) {
const int redSize = qPopulationCount(xcb_visualtype.red_mask);
const int greenSize = qPopulationCount(xcb_visualtype.green_mask);
const int blueSize = qPopulationCount(xcb_visualtype.blue_mask);
const int alphaSize = depthOfVisual(xcb_visualtype.visual_id) - redSize - greenSize - blueSize;
if (format.redBufferSize() != -1 && redSize != format.redBufferSize())
continue;
if (format.greenBufferSize() != -1 && greenSize != format.greenBufferSize())
continue;
if (format.blueBufferSize() != -1 && blueSize != format.blueBufferSize())
continue;
if (format.alphaBufferSize() != -1 && alphaSize != format.alphaBufferSize())
continue;
// Try to find a RGB visual rather than e.g. BGR or GBR
if (qCountTrailingZeroBits(xcb_visualtype.blue_mask) == 0)
return &xcb_visualtype;
// In case we do not find anything we like, just remember the first one
// and hope for the best:
if (!candidate)
candidate = &xcb_visualtype;
}
return candidate;
}
const xcb_visualtype_t *QXcbVirtualDesktop::visualForId(xcb_visualid_t visualid) const
{
QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);
if (it == m_visuals.constEnd())
return 0;
return &*it;
}
quint8 QXcbVirtualDesktop::depthOfVisual(xcb_visualid_t visualid) const
{
QMap<xcb_visualid_t, quint8>::const_iterator it = m_visualDepths.find(visualid);
if (it == m_visualDepths.constEnd())
return 0;
return *it;
}
QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output,
const xcb_xinerama_screen_info_t *xineramaScreenInfo, int xineramaScreenIdx)
@ -202,67 +427,6 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe
if (m_sizeMillimeters.isEmpty())
m_sizeMillimeters = m_virtualSizeMillimeters;
readXResources();
auto rootAttribs = Q_XCB_REPLY_UNCHECKED(xcb_get_window_attributes, xcb_connection(),
screen()->root);
const quint32 existingEventMask = !rootAttribs ? 0 : rootAttribs->your_event_mask;
const quint32 mask = XCB_CW_EVENT_MASK;
const quint32 values[] = {
// XCB_CW_EVENT_MASK
XCB_EVENT_MASK_ENTER_WINDOW
| XCB_EVENT_MASK_LEAVE_WINDOW
| XCB_EVENT_MASK_PROPERTY_CHANGE
| XCB_EVENT_MASK_STRUCTURE_NOTIFY // for the "MANAGER" atom (system tray notification).
| existingEventMask // don't overwrite the event mask on the root window
};
xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values);
auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
false, screen()->root,
atom(QXcbAtom::_NET_SUPPORTING_WM_CHECK),
XCB_ATOM_WINDOW, 0, 1024);
if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply.get()));
if (windowManager != XCB_WINDOW_NONE) {
auto windowManagerReply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
false, windowManager,
atom(QXcbAtom::_NET_WM_NAME),
atom(QXcbAtom::UTF8_STRING), 0, 1024);
if (windowManagerReply && windowManagerReply->format == 8 && windowManagerReply->type == atom(QXcbAtom::UTF8_STRING)) {
m_windowManagerName = QString::fromUtf8((const char *)xcb_get_property_value(windowManagerReply.get()),
xcb_get_property_value_length(windowManagerReply.get()));
}
}
}
const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id);
if (!sync_reply || !sync_reply->present)
m_syncRequestSupported = false;
else
m_syncRequestSupported = true;
xcb_depth_iterator_t depth_iterator =
xcb_screen_allowed_depths_iterator(screen());
while (depth_iterator.rem) {
xcb_depth_t *depth = depth_iterator.data;
xcb_visualtype_iterator_t visualtype_iterator =
xcb_depth_visuals_iterator(depth);
while (visualtype_iterator.rem) {
xcb_visualtype_t *visualtype = visualtype_iterator.data;
m_visuals.insert(visualtype->visual_id, *visualtype);
m_visualDepths.insert(visualtype->visual_id, depth->depth);
xcb_visualtype_next(&visualtype_iterator);
}
xcb_depth_next(&depth_iterator);
}
m_cursor = new QXcbCursor(connection, this);
}
@ -331,62 +495,12 @@ void QXcbScreen::windowShown(QXcbWindow *window)
QSurfaceFormat QXcbScreen::surfaceFormatFor(const QSurfaceFormat &format) const
{
const xcb_visualid_t xcb_visualid = connection()->hasDefaultVisualId() ? connection()->defaultVisualId()
: screen()->root_visual;
const xcb_visualtype_t *xcb_visualtype = visualForId(xcb_visualid);
const int redSize = qPopulationCount(xcb_visualtype->red_mask);
const int greenSize = qPopulationCount(xcb_visualtype->green_mask);
const int blueSize = qPopulationCount(xcb_visualtype->blue_mask);
QSurfaceFormat result = format;
if (result.redBufferSize() < 0)
result.setRedBufferSize(redSize);
if (result.greenBufferSize() < 0)
result.setGreenBufferSize(greenSize);
if (result.blueBufferSize() < 0)
result.setBlueBufferSize(blueSize);
return result;
return m_virtualDesktop->surfaceFormatFor(format);
}
const xcb_visualtype_t *QXcbScreen::visualForFormat(const QSurfaceFormat &format) const
const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
{
const xcb_visualtype_t *candidate = nullptr;
for (const xcb_visualtype_t &xcb_visualtype : m_visuals) {
const int redSize = qPopulationCount(xcb_visualtype.red_mask);
const int greenSize = qPopulationCount(xcb_visualtype.green_mask);
const int blueSize = qPopulationCount(xcb_visualtype.blue_mask);
const int alphaSize = depthOfVisual(xcb_visualtype.visual_id) - redSize - greenSize - blueSize;
if (format.redBufferSize() != -1 && redSize != format.redBufferSize())
continue;
if (format.greenBufferSize() != -1 && greenSize != format.greenBufferSize())
continue;
if (format.blueBufferSize() != -1 && blueSize != format.blueBufferSize())
continue;
if (format.alphaBufferSize() != -1 && alphaSize != format.alphaBufferSize())
continue;
// Try to find a RGB visual rather than e.g. BGR or GBR
if (qCountTrailingZeroBits(xcb_visualtype.blue_mask) == 0)
return &xcb_visualtype;
// In case we do not find anything we like, just remember the first one
// and hope for the best:
if (!candidate)
candidate = &xcb_visualtype;
}
return candidate;
return m_virtualDesktop->visualForId(visualid);
}
void QXcbScreen::sendStartupMessage(const QByteArray &message) const
@ -415,22 +529,6 @@ void QXcbScreen::sendStartupMessage(const QByteArray &message) const
} while (sent < length);
}
const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
{
QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);
if (it == m_visuals.constEnd())
return 0;
return &*it;
}
quint8 QXcbScreen::depthOfVisual(xcb_visualid_t visualid) const
{
QMap<xcb_visualid_t, quint8>::const_iterator it = m_visualDepths.find(visualid);
if (it == m_visualDepths.constEnd())
return 0;
return *it;
}
QImage::Format QXcbScreen::format() const
{
return qt_xcb_imageFormatForVisual(connection(), screen()->root_depth, visualForId(screen()->root_visual));
@ -449,8 +547,9 @@ QDpi QXcbScreen::logicalDpi() const
if (overrideDpi)
return QDpi(overrideDpi, overrideDpi);
if (m_forcedDpi > 0) {
return QDpi(m_forcedDpi, m_forcedDpi);
const int forcedDpi = m_virtualDesktop->forcedDpi();
if (forcedDpi > 0) {
return QDpi(forcedDpi, forcedDpi);
}
return virtualDpi();
}
@ -726,94 +825,6 @@ QPixmap QXcbScreen::grabWindow(WId window, int xIn, int yIn, int width, int heig
return result;
}
static bool parseXftInt(const QByteArray& stringValue, int *value)
{
Q_ASSERT(value != 0);
bool ok;
*value = stringValue.toInt(&ok);
return ok;
}
static QFontEngine::HintStyle parseXftHintStyle(const QByteArray& stringValue)
{
if (stringValue == "hintfull")
return QFontEngine::HintFull;
else if (stringValue == "hintnone")
return QFontEngine::HintNone;
else if (stringValue == "hintmedium")
return QFontEngine::HintMedium;
else if (stringValue == "hintslight")
return QFontEngine::HintLight;
return QFontEngine::HintStyle(-1);
}
static QFontEngine::SubpixelAntialiasingType parseXftRgba(const QByteArray& stringValue)
{
if (stringValue == "none")
return QFontEngine::Subpixel_None;
else if (stringValue == "rgb")
return QFontEngine::Subpixel_RGB;
else if (stringValue == "bgr")
return QFontEngine::Subpixel_BGR;
else if (stringValue == "vrgb")
return QFontEngine::Subpixel_VRGB;
else if (stringValue == "vbgr")
return QFontEngine::Subpixel_VBGR;
return QFontEngine::SubpixelAntialiasingType(-1);
}
bool QXcbScreen::xResource(const QByteArray &identifier,
const QByteArray &expectedIdentifier,
QByteArray& stringValue)
{
if (identifier.startsWith(expectedIdentifier)) {
stringValue = identifier.mid(expectedIdentifier.size());
return true;
}
return false;
}
void QXcbScreen::readXResources()
{
int offset = 0;
QByteArray resources;
while(1) {
auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
false, screen()->root,
XCB_ATOM_RESOURCE_MANAGER,
XCB_ATOM_STRING, offset/4, 8192);
bool more = false;
if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
resources += QByteArray((const char *)xcb_get_property_value(reply.get()), xcb_get_property_value_length(reply.get()));
offset += xcb_get_property_value_length(reply.get());
more = reply->bytes_after != 0;
}
if (!more)
break;
}
QList<QByteArray> split = resources.split('\n');
for (int i = 0; i < split.size(); ++i) {
const QByteArray &r = split.at(i);
int value;
QByteArray stringValue;
if (xResource(r, "Xft.dpi:\t", stringValue)) {
if (parseXftInt(stringValue, &value))
m_forcedDpi = value;
} else if (xResource(r, "Xft.hintstyle:\t", stringValue)) {
m_hintStyle = parseXftHintStyle(stringValue);
} else if (xResource(r, "Xft.antialias:\t", stringValue)) {
if (parseXftInt(stringValue, &value))
m_antialiasingEnabled = value;
} else if (xResource(r, "Xft.rgba:\t", stringValue)) {
m_subpixelType = parseXftRgba(stringValue);
}
}
}
QXcbXSettings *QXcbScreen::xSettings() const
{
return m_virtualDesktop->xSettings();

View File

@ -91,9 +91,28 @@ public:
void handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event);
void subscribeToXFixesSelectionNotify();
int forcedDpi() const { return m_forcedDpi; }
QFontEngine::HintStyle hintStyle() const { return m_hintStyle; }
QFontEngine::SubpixelAntialiasingType subpixelType() const { return m_subpixelType; }
int antialiasingEnabled() const { return m_antialiasingEnabled; }
QString windowManagerName() const { return m_windowManagerName; }
bool syncRequestSupported() const { return m_syncRequestSupported; }
QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &format) const;
const xcb_visualtype_t *visualForFormat(const QSurfaceFormat &format) const;
const xcb_visualtype_t *visualForId(xcb_visualid_t) const;
quint8 depthOfVisual(xcb_visualid_t) const;
private:
QRect getWorkArea() const;
static bool xResource(const QByteArray &identifier,
const QByteArray &expectedIdentifier,
QByteArray &stringValue);
void readXResources();
xcb_screen_t *m_screen;
const int m_number;
QList<QPlatformScreen *> m_screens;
@ -103,6 +122,15 @@ private:
bool m_compositingActive = false;
QRect m_workArea;
int m_forcedDpi = -1;
QFontEngine::HintStyle m_hintStyle = QFontEngine::HintStyle(-1);
QFontEngine::SubpixelAntialiasingType m_subpixelType = QFontEngine::SubpixelAntialiasingType(-1);
int m_antialiasingEnabled = -1;
QString m_windowManagerName;
bool m_syncRequestSupported = false;
QMap<xcb_visualid_t, xcb_visualtype_t> m_visuals;
QMap<xcb_visualid_t, quint8> m_visualDepths;
};
class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen
@ -152,14 +180,14 @@ public:
void setCrtc(xcb_randr_crtc_t crtc) { m_crtc = crtc; }
void windowShown(QXcbWindow *window);
QString windowManagerName() const { return m_windowManagerName; }
bool syncRequestSupported() const { return m_syncRequestSupported; }
QString windowManagerName() const { return m_virtualDesktop->windowManagerName(); }
bool syncRequestSupported() const { return m_virtualDesktop->syncRequestSupported(); }
QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &format) const;
const xcb_visualtype_t *visualForFormat(const QSurfaceFormat &format) const;
const xcb_visualtype_t *visualForId(xcb_visualid_t) const;
quint8 depthOfVisual(xcb_visualid_t) const;
const xcb_visualtype_t *visualForFormat(const QSurfaceFormat &format) const { return m_virtualDesktop->visualForFormat(format); }
const xcb_visualtype_t *visualForId(xcb_visualid_t visualid) const;
quint8 depthOfVisual(xcb_visualid_t visualid) const { return m_virtualDesktop->depthOfVisual(visualid); }
QString name() const override { return m_outputName; }
@ -169,18 +197,13 @@ public:
void updateAvailableGeometry();
void updateRefreshRate(xcb_randr_mode_t mode);
void readXResources();
QFontEngine::HintStyle hintStyle() const { return m_hintStyle; }
QFontEngine::SubpixelAntialiasingType subpixelType() const { return m_subpixelType; }
int antialiasingEnabled() const { return m_antialiasingEnabled; }
QFontEngine::HintStyle hintStyle() const { return m_virtualDesktop->hintStyle(); }
QFontEngine::SubpixelAntialiasingType subpixelType() const { return m_virtualDesktop->subpixelType(); }
int antialiasingEnabled() const { return m_virtualDesktop->antialiasingEnabled(); }
QXcbXSettings *xSettings() const;
private:
static bool xResource(const QByteArray &identifier,
const QByteArray &expectedIdentifier,
QByteArray &stringValue);
void sendStartupMessage(const QByteArray &message) const;
QXcbVirtualDesktop *m_virtualDesktop;
@ -198,17 +221,9 @@ private:
QSize m_virtualSize;
QSizeF m_virtualSizeMillimeters;
Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation;
QString m_windowManagerName;
bool m_syncRequestSupported = false;
QMap<xcb_visualid_t, xcb_visualtype_t> m_visuals;
QMap<xcb_visualid_t, quint8> m_visualDepths;
QXcbCursor *m_cursor;
int m_refreshRate = 60;
int m_forcedDpi = -1;
int m_pixelDensity = 1;
QFontEngine::HintStyle m_hintStyle = QFontEngine::HintStyle(-1);
QFontEngine::SubpixelAntialiasingType m_subpixelType = QFontEngine::SubpixelAntialiasingType(-1);
int m_antialiasingEnabled = -1;
};
#ifndef QT_NO_DEBUG_STREAM