summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h17
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp1
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp30
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h1
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp13
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp18
-rw-r--r--src/plugins/platforms/xcb/nativepainting/nativepainting.pri22
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp203
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h70
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp644
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h75
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp2837
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h118
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp2108
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h160
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qt_x11_p.h193
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qtessellator.cpp1494
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qtessellator_p.h83
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp315
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h96
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.cpp106
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp86
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp662
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h177
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp1012
-rw-r--r--src/plugins/platforms/xcb/qxcbcursor.cpp12
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp131
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.h3
-rw-r--r--src/plugins/platforms/xcb/qxcbimage.cpp32
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp63
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h7
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp98
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp81
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.h15
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp603
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.h71
-rw-r--r--src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp38
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkaninstance.cpp156
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkaninstance.h79
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkanwindow.cpp88
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkanwindow.h65
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp665
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h10
-rw-r--r--src/plugins/platforms/xcb/qxcbwmsupport.cpp21
-rw-r--r--src/plugins/platforms/xcb/qxcbxsettings.cpp43
-rw-r--r--src/plugins/platforms/xcb/xcb_qpa_lib.pro16
48 files changed, 10822 insertions, 2020 deletions
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h
index 48e774bbb2..fe50afa62a 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h
@@ -47,38 +47,28 @@
QT_BEGIN_NAMESPACE
-//####todo remove the noops (looks like their where there in the initial commit)
class QXcbEglContext : public QEGLPlatformContext
{
public:
QXcbEglContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share,
- EGLDisplay display, QXcbConnection *c, const QVariant &nativeHandle)
+ EGLDisplay display, const QVariant &nativeHandle)
: QEGLPlatformContext(glFormat, share, display, 0, nativeHandle)
- , m_connection(c)
{
- Q_XCB_NOOP(m_connection);
}
void swapBuffers(QPlatformSurface *surface)
{
- Q_XCB_NOOP(m_connection);
QEGLPlatformContext::swapBuffers(surface);
- Q_XCB_NOOP(m_connection);
}
bool makeCurrent(QPlatformSurface *surface)
{
- Q_XCB_NOOP(m_connection);
- bool ret = QEGLPlatformContext::makeCurrent(surface);
- Q_XCB_NOOP(m_connection);
- return ret;
+ return QEGLPlatformContext::makeCurrent(surface);
}
void doneCurrent()
{
- Q_XCB_NOOP(m_connection);
QEGLPlatformContext::doneCurrent();
- Q_XCB_NOOP(m_connection);
}
EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface)
@@ -92,9 +82,6 @@ public:
QVariant nativeHandle() const {
return QVariant::fromValue<QEGLNativeContext>(QEGLNativeContext(eglContext(), eglDisplay()));
}
-
-private:
- QXcbConnection *m_connection;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
index 9c52733120..7aa1d631df 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
@@ -102,7 +102,6 @@ QPlatformOpenGLContext *QXcbEglIntegration::createPlatformOpenGLContext(QOpenGLC
QXcbEglContext *platformContext = new QXcbEglContext(screen->surfaceFormatFor(context->format()),
context->shareHandle(),
eglDisplay(),
- screen->connection(),
context->nativeHandle());
context->setNativeHandle(platformContext->nativeHandle());
return platformContext;
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
index e2e573f0e1..3bc8590d36 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
@@ -167,7 +167,7 @@ static void updateFormatFromContext(QSurfaceFormat &format)
QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share,
const QVariant &nativeHandle)
: QPlatformOpenGLContext()
- , m_display(DISPLAY_FROM_XCB(screen))
+ , m_display(static_cast<Display *>(screen->connection()->xlib_display()))
, m_config(0)
, m_context(0)
, m_shareContext(0)
@@ -196,7 +196,7 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
if (share)
m_shareContext = static_cast<const QGLXContext*>(share)->glxContext();
- GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(screen),screen->screenNumber(),m_format);
+ GLXFBConfig config = qglx_findConfig(m_display, screen->screenNumber(), m_format);
m_config = config;
XVisualInfo *visualInfo = 0;
Window window = 0; // Temporary window used to query OpenGL context
@@ -304,10 +304,10 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
// Get the basic surface format details
if (m_context)
- qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(screen), config);
+ qglx_surfaceFormatFromGLXFBConfig(&m_format, m_display, config);
// Create a temporary window so that we can make the new context current
- window = createDummyWindow(DISPLAY_FROM_XCB(screen), config, screen->screenNumber(), screen->root());
+ window = createDummyWindow(m_display, config, screen->screenNumber(), screen->root());
} else {
// requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out
if (m_format.renderableType() == QSurfaceFormat::OpenGLES)
@@ -325,7 +325,7 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
}
// Create a temporary window so that we can make the new context current
- window = createDummyWindow(DISPLAY_FROM_XCB(screen), visualInfo, screen->screenNumber(), screen->root());
+ window = createDummyWindow(m_display, visualInfo, screen->screenNumber(), screen->root());
XFree(visualInfo);
}
@@ -360,7 +360,7 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share, const
// Use the provided Display, if available. If not, use our own. It may still work.
Display *dpy = handle.display();
if (!dpy)
- dpy = DISPLAY_FROM_XCB(screen);
+ dpy = m_display;
// Legacy contexts created using glXCreateContext are created using a visual
// and the FBConfig cannot be queried. The only way to adapt these contexts
@@ -665,8 +665,10 @@ void QGLXContext::queryDummyContext()
Display *display = glXGetCurrentDisplay();
if (!display) {
// FIXME: Since Qt 5.6 we don't need to check whether primary screen is NULL
- if (QScreen *screen = QGuiApplication::primaryScreen())
- display = DISPLAY_FROM_XCB(static_cast<QXcbScreen *>(screen->handle()));
+ if (QScreen *screen = QGuiApplication::primaryScreen()) {
+ QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(screen->handle());
+ display = static_cast<Display *>(xcbScreen->connection()->xlib_display());
+ }
}
const char *glxvendor = glXGetClientString(display, GLX_VENDOR);
if (glxvendor && !strcmp(glxvendor, "ATI")) {
@@ -729,8 +731,7 @@ void QGLXContext::queryDummyContext()
bool QGLXContext::supportsThreading()
{
- if (!m_queriedDummyContext)
- queryDummyContext();
+ queryDummyContext();
return m_supportsThreading;
}
@@ -738,9 +739,10 @@ QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface)
: QPlatformOffscreenSurface(offscreenSurface)
, m_screen(static_cast<QXcbScreen *>(offscreenSurface->screen()->handle()))
, m_format(m_screen->surfaceFormatFor(offscreenSurface->requestedFormat()))
+ , m_display(static_cast<Display *>(m_screen->connection()->xlib_display()))
, m_pbuffer(0)
{
- GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber(), m_format);
+ GLXFBConfig config = qglx_findConfig(m_display, m_screen->screenNumber(), m_format);
if (config) {
const int attributes[] = {
@@ -751,17 +753,17 @@ QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface)
None
};
- m_pbuffer = glXCreatePbuffer(DISPLAY_FROM_XCB(m_screen), config, attributes);
+ m_pbuffer = glXCreatePbuffer(m_display, config, attributes);
if (m_pbuffer)
- qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(m_screen), config);
+ qglx_surfaceFormatFromGLXFBConfig(&m_format, m_display, config);
}
}
QGLXPbuffer::~QGLXPbuffer()
{
if (m_pbuffer)
- glXDestroyPbuffer(DISPLAY_FROM_XCB(m_screen), m_pbuffer);
+ glXDestroyPbuffer(m_display, m_pbuffer);
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h
index 3dfe0ac618..f6372582db 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h
@@ -108,6 +108,7 @@ public:
private:
QXcbScreen *m_screen;
QSurfaceFormat m_format;
+ Display *m_display;
GLXPbuffer m_pbuffer;
};
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
index fea365cabc..377066df61 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
@@ -108,18 +108,13 @@ bool QXcbGlxIntegration::initialize(QXcbConnection *connection)
m_glx_first_event = reply->first_event;
- xcb_generic_error_t *error = 0;
- xcb_glx_query_version_cookie_t xglx_query_cookie = xcb_glx_query_version(m_connection->xcb_connection(),
- XCB_GLX_MAJOR_VERSION,
- XCB_GLX_MINOR_VERSION);
- xcb_glx_query_version_reply_t *xglx_query = xcb_glx_query_version_reply(m_connection->xcb_connection(),
- xglx_query_cookie, &error);
- if (!xglx_query || error) {
+ auto xglx_query = Q_XCB_REPLY(xcb_glx_query_version, m_connection->xcb_connection(),
+ XCB_GLX_MAJOR_VERSION,
+ XCB_GLX_MINOR_VERSION);
+ if (!xglx_query) {
qCWarning(lcQpaGl) << "QXcbConnection: Failed to initialize GLX";
- free(error);
return false;
}
- free(xglx_query);
#endif
m_native_interface_handler.reset(new QXcbGlxNativeInterfaceHandler(connection->nativeInterface()));
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
index 8df8b28f72..145a11a5e3 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
@@ -41,6 +41,7 @@
#include "qxcbscreen.h"
#include <QtGlxSupport/private/qglxconvenience_p.h>
+#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -58,13 +59,28 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual()
QXcbScreen *scr = xcbScreen();
if (!scr)
return Q_NULLPTR;
- XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(scr), scr->screenNumber(), &m_format);
+
+ qDebug(lcQpaGl) << "Requested format before FBConfig/Visual selection:" << m_format;
+
+ Display *dpy = static_cast<Display *>(scr->connection()->xlib_display());
+ const char *glxExts = glXQueryExtensionsString(dpy, scr->screenNumber());
+ int flags = 0;
+ if (glxExts) {
+ qCDebug(lcQpaGl, "Available GLX extensions: %s", glxExts);
+ if (strstr(glxExts, "GLX_EXT_framebuffer_sRGB") || strstr(glxExts, "GLX_ARB_framebuffer_sRGB"))
+ flags |= QGLX_SUPPORTS_SRGB;
+ }
+
+ XVisualInfo *visualInfo = qglx_findVisualInfo(dpy, scr->screenNumber(), &m_format, GLX_WINDOW_BIT, flags);
if (!visualInfo) {
qWarning() << "No XVisualInfo for format" << m_format;
return Q_NULLPTR;
}
const xcb_visualtype_t *xcb_visualtype = scr->visualForId(visualInfo->visualid);
XFree(visualInfo);
+
+ qDebug(lcQpaGl) << "Got format:" << m_format;
+
return xcb_visualtype;
}
diff --git a/src/plugins/platforms/xcb/nativepainting/nativepainting.pri b/src/plugins/platforms/xcb/nativepainting/nativepainting.pri
new file mode 100644
index 0000000000..78ed00843f
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/nativepainting.pri
@@ -0,0 +1,22 @@
+qtConfig(xcb-native-painting) {
+ qtConfig(xrender): QMAKE_USE += xrender
+ qtConfig(fontconfig): QMAKE_USE_PRIVATE += freetype
+
+ INCLUDEPATH += $$PWD
+ HEADERS += \
+ $$PWD/qtessellator_p.h \
+ $$PWD/qpixmap_x11_p.h \
+ $$PWD/qpaintengine_x11_p.h \
+ $$PWD/qt_x11_p.h \
+ $$PWD/qcolormap_x11_p.h \
+ $$PWD/qbackingstore_x11_p.h \
+ $$PWD/qxcbnativepainting.h
+
+ SOURCES += \
+ $$PWD/qtessellator.cpp \
+ $$PWD/qpixmap_x11.cpp \
+ $$PWD/qpaintengine_x11.cpp \
+ $$PWD/qcolormap_x11.cpp \
+ $$PWD/qbackingstore_x11.cpp \
+ $$PWD/qxcbnativepainting.cpp
+}
diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
new file mode 100644
index 0000000000..2dd2cdd9e3
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbackingstore_x11_p.h"
+#include "qxcbwindow.h"
+#include "qpixmap_x11_p.h"
+
+#include <private/qhighdpiscaling_p.h>
+#include <QPainter>
+
+#if QT_CONFIG(xrender)
+# include <X11/extensions/Xrender.h>
+#endif
+
+#include <X11/Xlib.h>
+
+#ifndef None
+#define None 0L
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QXcbNativeBackingStore::QXcbNativeBackingStore(QWindow *window)
+ : QPlatformBackingStore(window)
+ , m_translucentBackground(false)
+{
+ if (QXcbWindow *w = static_cast<QXcbWindow *>(window->handle()))
+ m_translucentBackground = w->connection()->hasXRender() && QImage::toPixelFormat(w->imageFormat()).alphaSize() > 0;
+}
+
+QXcbNativeBackingStore::~QXcbNativeBackingStore()
+{}
+
+QPaintDevice *QXcbNativeBackingStore::paintDevice()
+{
+ return &m_pixmap;
+}
+
+void QXcbNativeBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
+{
+ if (m_pixmap.isNull())
+ return;
+
+ QSize pixmapSize = m_pixmap.size();
+
+ QRegion clipped = region;
+ clipped &= QRect(QPoint(), QHighDpi::toNativePixels(window->size(), window));
+ clipped &= QRect(0, 0, pixmapSize.width(), pixmapSize.height()).translated(-offset);
+
+ QRect br = clipped.boundingRect();
+ if (br.isNull())
+ return;
+
+ QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
+ if (!platformWindow) {
+ qWarning("QXcbBackingStore::flush: QWindow has no platform window (QTBUG-32681)");
+ return;
+ }
+
+ Window wid = platformWindow->xcb_window();
+ Pixmap pid = qt_x11PixmapHandle(m_pixmap);
+
+ QVector<XRectangle> clipRects = qt_region_to_xrectangles(clipped);
+
+#if QT_CONFIG(xrender)
+ if (m_translucentBackground)
+ {
+ XWindowAttributes attrib;
+ XGetWindowAttributes(display(), wid, &attrib);
+ XRenderPictFormat *format = XRenderFindVisualFormat(display(), attrib.visual);
+
+ Picture srcPic = qt_x11PictureHandle(m_pixmap);
+ Picture dstPic = XRenderCreatePicture(display(), wid, format, 0, 0);
+
+ XRenderSetPictureClipRectangles(display(), dstPic, 0, 0, clipRects.constData(), clipRects.size());
+
+ XRenderComposite(display(), PictOpSrc, srcPic, None, dstPic,
+ br.x() + offset.x(), br.y() + offset.y(),
+ 0, 0,
+ br.x(), br.y(),
+ br.width(), br.height());
+
+ XRenderFreePicture(display(), dstPic);
+ }
+ else
+#endif
+ {
+ GC gc = XCreateGC(display(), wid, 0, Q_NULLPTR);
+
+ if (clipRects.size() != 1)
+ XSetClipRectangles(display(), gc, 0, 0, clipRects.data(), clipRects.size(), YXBanded);
+
+ XCopyArea(display(), pid, wid, gc, br.x() + offset.x(), br.y() + offset.y(), br.width(), br.height(), br.x(), br.y());
+ XFreeGC(display(), gc);
+ }
+
+
+ if (platformWindow->needsSync()) {
+ platformWindow->updateSyncRequestCounter();
+ } else {
+ XFlush(display());
+ }
+}
+
+QImage QXcbNativeBackingStore::toImage() const
+{
+ return m_pixmap.toImage();
+}
+
+void QXcbNativeBackingStore::resize(const QSize &size, const QRegion &staticContents)
+{
+ if (size == m_pixmap.size())
+ return;
+
+ QPixmap newPixmap(size);
+
+#if QT_CONFIG(xrender)
+ if (m_translucentBackground && newPixmap.depth() != 32)
+ qt_x11Pixmap(newPixmap)->convertToARGB32();
+#endif
+
+ if (!m_pixmap.isNull()) {
+ Pixmap from = qt_x11PixmapHandle(m_pixmap);
+ Pixmap to = qt_x11PixmapHandle(newPixmap);
+ QRect br = staticContents.boundingRect().intersected(QRect(QPoint(0, 0), size));
+
+ if (!br.isEmpty()) {
+ GC gc = XCreateGC(display(), to, 0, Q_NULLPTR);
+ XCopyArea(display(), from, to, gc, br.x(), br.y(), br.width(), br.height(), br.x(), br.y());
+ XFreeGC(display(), gc);
+ }
+ }
+
+ m_pixmap = newPixmap;
+}
+
+bool QXcbNativeBackingStore::scroll(const QRegion &area, int dx, int dy)
+{
+ if (m_pixmap.isNull())
+ return false;
+
+ QRect rect = area.boundingRect();
+ Pixmap pix = qt_x11PixmapHandle(m_pixmap);
+
+ GC gc = XCreateGC(display(), pix, 0, Q_NULLPTR);
+ XCopyArea(display(), pix, pix, gc,
+ rect.x(), rect.y(), rect.width(), rect.height(),
+ rect.x()+dx, rect.y()+dy);
+ XFreeGC(display(), gc);
+ return true;
+}
+
+void QXcbNativeBackingStore::beginPaint(const QRegion &region)
+{
+#if QT_CONFIG(xrender)
+ if (m_translucentBackground) {
+ const QVector<XRectangle> xrects = qt_region_to_xrectangles(region);
+ const XRenderColor color = { 0, 0, 0, 0 };
+ XRenderFillRectangles(display(), PictOpSrc,
+ qt_x11PictureHandle(m_pixmap), &color,
+ xrects.constData(), xrects.size());
+ }
+#else
+ Q_UNUSED(region);
+#endif
+}
+
+Display *QXcbNativeBackingStore::display() const
+{
+ return static_cast<Display *>(static_cast<QXcbWindow *>(window()->handle())->connection()->xlib_display());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h
new file mode 100644
index 0000000000..5f4c24ec11
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBACKINGSTORE_X11_H
+#define QBACKINGSTORE_X11_H
+
+#include <qpa/qplatformbackingstore.h>
+
+typedef struct _XDisplay Display;
+
+QT_BEGIN_NAMESPACE
+
+class QXcbWindow;
+
+class QXcbNativeBackingStore : public QPlatformBackingStore
+{
+public:
+ QXcbNativeBackingStore(QWindow *window);
+ ~QXcbNativeBackingStore();
+
+ QPaintDevice *paintDevice() override;
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
+
+ QImage toImage() const override;
+
+ void resize(const QSize &size, const QRegion &staticContents) override;
+ bool scroll(const QRegion &area, int dx, int dy) override;
+
+ void beginPaint(const QRegion &region) override;
+
+private:
+ Display *display() const;
+
+ QPixmap m_pixmap;
+ bool m_translucentBackground;
+};
+
+QT_END_NAMESPACE
+
+#endif // QBACKINGSTORE_X11_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp
new file mode 100644
index 0000000000..8554c5445d
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp
@@ -0,0 +1,644 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QVarLengthArray>
+
+#include <private/qguiapplication_p.h>
+
+#include "qcolormap_x11_p.h"
+#include "qxcbnativepainting.h"
+#include "qt_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QXcbColormapPrivate
+{
+public:
+ QXcbColormapPrivate()
+ : ref(1), mode(QXcbColormap::Direct), depth(0),
+ colormap(0), defaultColormap(true),
+ visual(0), defaultVisual(true),
+ r_max(0), g_max(0), b_max(0),
+ r_shift(0), g_shift(0), b_shift(0)
+ {}
+
+ QAtomicInt ref;
+
+ QXcbColormap::Mode mode;
+ int depth;
+
+ Colormap colormap;
+ bool defaultColormap;
+
+ Visual *visual;
+ bool defaultVisual;
+
+ int r_max;
+ int g_max;
+ int b_max;
+
+ uint r_shift;
+ uint g_shift;
+ uint b_shift;
+
+ QVector<QColor> colors;
+ QVector<int> pixels;
+};
+
+static uint right_align(uint v)
+{
+ while (!(v & 0x1))
+ v >>= 1;
+ return v;
+}
+
+static int cube_root(int v)
+{
+ if (v == 1)
+ return 1;
+ // brute force algorithm
+ int i = 1;
+ for (;;) {
+ const int b = i * i * i;
+ if (b <= v) {
+ ++i;
+ } else {
+ --i;
+ break;
+ }
+ }
+ return i;
+}
+
+static Visual *find_visual(Display *display,
+ int screen,
+ int visual_class,
+ int visual_id,
+ int *depth,
+ bool *defaultVisual)
+{
+ XVisualInfo *vi, rvi;
+ int count;
+
+ uint mask = VisualScreenMask;
+ rvi.screen = screen;
+
+ if (visual_class != -1) {
+ rvi.c_class = visual_class;
+ mask |= VisualClassMask;
+ }
+ if (visual_id != -1) {
+ rvi.visualid = visual_id;
+ mask |= VisualIDMask;
+ }
+
+ Visual *visual = DefaultVisual(display, screen);
+ *defaultVisual = true;
+ *depth = DefaultDepth(display, screen);
+
+ vi = XGetVisualInfo(display, mask, &rvi, &count);
+ if (vi) {
+ int best = 0;
+ for (int x = 0; x < count; ++x) {
+ if (vi[x].depth > vi[best].depth)
+ best = x;
+ }
+ if (best >= 0 && best <= count && vi[best].visualid != XVisualIDFromVisual(visual)) {
+ visual = vi[best].visual;
+ *defaultVisual = (visual == DefaultVisual(display, screen));
+ *depth = vi[best].depth;
+ }
+ }
+ if (vi)
+ XFree((char *)vi);
+ return visual;
+}
+
+static void query_colormap(QXcbColormapPrivate *d, int screen)
+{
+ Display *display = X11->display;
+
+ // query existing colormap
+ int q_colors = (((1u << d->depth) > 256u) ? 256u : (1u << d->depth));
+ XColor queried[256];
+ memset(queried, 0, sizeof(queried));
+ for (int x = 0; x < q_colors; ++x)
+ queried[x].pixel = x;
+ XQueryColors(display, d->colormap, queried, q_colors);
+
+ d->colors.resize(q_colors);
+ for (int x = 0; x < q_colors; ++x) {
+ if (queried[x].red == 0
+ && queried[x].green == 0
+ && queried[x].blue == 0
+ && queried[x].pixel != BlackPixel(display, screen)) {
+ // unallocated color cell, skip it
+ continue;
+ }
+
+ d->colors[x] = QColor::fromRgbF(queried[x].red / float(USHRT_MAX),
+ queried[x].green / float(USHRT_MAX),
+ queried[x].blue / float(USHRT_MAX));
+ }
+
+ // for missing colors, find the closest color in the existing colormap
+ Q_ASSERT(d->pixels.size());
+ for (int x = 0; x < d->pixels.size(); ++x) {
+ if (d->pixels.at(x) != -1)
+ continue;
+
+ QRgb rgb;
+ if (d->mode == QXcbColormap::Indexed) {
+ const int r = (x / (d->g_max * d->b_max)) % d->r_max;
+ const int g = (x / d->b_max) % d->g_max;
+ const int b = x % d->b_max;
+ rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+ } else {
+ rgb = qRgb(x, x, x);
+ }
+
+ // find closest color
+ int mindist = INT_MAX, best = -1;
+ for (int y = 0; y < q_colors; ++y) {
+ int r = qRed(rgb) - (queried[y].red >> 8);
+ int g = qGreen(rgb) - (queried[y].green >> 8);
+ int b = qBlue(rgb) - (queried[y].blue >> 8);
+ int dist = (r * r) + (g * g) + (b * b);
+ if (dist < mindist) {
+ mindist = dist;
+ best = y;
+ }
+ }
+
+ Q_ASSERT(best >= 0 && best < q_colors);
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = queried[best].red;
+ xcolor.green = queried[best].green;
+ xcolor.blue = queried[best].blue;
+ xcolor.pixel = queried[best].pixel;
+
+ if (XAllocColor(display, d->colormap, &xcolor)) {
+ d->pixels[x] = xcolor.pixel;
+ } else {
+ // some weird stuff is going on...
+ d->pixels[x] = (qGray(rgb) < 127
+ ? BlackPixel(display, screen)
+ : WhitePixel(display, screen));
+ }
+ } else {
+ d->pixels[x] = best;
+ }
+ }
+}
+
+static void init_gray(QXcbColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max);
+
+ for (int g = 0; g < d->g_max; ++g) {
+ const int gray = (g * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1);
+ const QRgb rgb = qRgb(gray, gray, gray);
+
+ d->pixels[g] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(X11->display, d->colormap, &xcolor))
+ d->pixels[g] = xcolor.pixel;
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_indexed(QXcbColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max * d->g_max * d->b_max);
+
+ // create color cube
+ for (int x = 0, r = 0; r < d->r_max; ++r) {
+ for (int g = 0; g < d->g_max; ++g) {
+ for (int b = 0; b < d->b_max; ++b, ++x) {
+ const QRgb rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+
+ d->pixels[x] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(X11->display, d->colormap, &xcolor))
+ d->pixels[x] = xcolor.pixel;
+ }
+ }
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_direct(QXcbColormapPrivate *d, bool ownColormap)
+{
+ if (d->visual->c_class != DirectColor || !ownColormap)
+ return;
+
+ // preallocate 768 on the stack, so that we don't have to malloc
+ // for the common case (<= 24 bpp)
+ QVarLengthArray<XColor, 768> colorTable(d->r_max + d->g_max + d->b_max);
+ int i = 0;
+
+ for (int r = 0; r < d->r_max; ++r) {
+ colorTable[i].red = r << 8 | r;
+ colorTable[i].pixel = r << d->r_shift;
+ colorTable[i].flags = DoRed;
+ ++i;
+ }
+
+ for (int g = 0; g < d->g_max; ++g) {
+ colorTable[i].green = g << 8 | g;
+ colorTable[i].pixel = g << d->g_shift;
+ colorTable[i].flags = DoGreen;
+ ++i;
+ }
+
+ for (int b = 0; b < d->b_max; ++b) {
+ colorTable[i].blue = (b << 8 | b);
+ colorTable[i].pixel = b << d->b_shift;
+ colorTable[i].flags = DoBlue;
+ ++i;
+ }
+
+ XStoreColors(X11->display, d->colormap, colorTable.data(), colorTable.count());
+}
+
+static QXcbColormap **cmaps = 0;
+
+void QXcbColormap::initialize()
+{
+ Display *display = X11->display;
+ const int screens = ScreenCount(display);
+
+ cmaps = new QXcbColormap*[screens];
+
+ for (int i = 0; i < screens; ++i) {
+ cmaps[i] = new QXcbColormap;
+ QXcbColormapPrivate * const d = cmaps[i]->d;
+
+ bool use_stdcmap = false;
+ int color_count = X11->color_count;
+
+ // defaults
+ d->depth = DefaultDepth(display, i);
+ d->colormap = DefaultColormap(display, i);
+ d->defaultColormap = true;
+ d->visual = DefaultVisual(display, i);
+ d->defaultVisual = true;
+
+ Visual *argbVisual = 0;
+
+ if (X11->visual && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->visual = find_visual(display, i, X11->visual->c_class,
+ XVisualIDFromVisual(X11->visual),
+ &d->depth, &d->defaultVisual);
+ } else if ((X11->visual_class != -1 && X11->visual_class >= 0 && X11->visual_class < 6)
+ || (X11->visual_id != -1)) {
+ // look for a specific visual or type of visual
+ d->visual = find_visual(display, i, X11->visual_class, X11->visual_id,
+ &d->depth, &d->defaultVisual);
+ } else if (!X11->custom_cmap) {
+ XStandardColormap *stdcmap = 0;
+ int ncmaps = 0;
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ int nvi;
+ XVisualInfo templ;
+ templ.screen = i;
+ templ.depth = 32;
+ templ.c_class = TrueColor;
+ XVisualInfo *xvi = XGetVisualInfo(X11->display, VisualScreenMask |
+ VisualDepthMask |
+ VisualClassMask, &templ, &nvi);
+ for (int idx = 0; idx < nvi; ++idx) {
+ XRenderPictFormat *format = XRenderFindVisualFormat(X11->display,
+ xvi[idx].visual);
+ if (format->type == PictTypeDirect && format->direct.alphaMask) {
+ argbVisual = xvi[idx].visual;
+ break;
+ }
+ }
+ XFree(xvi);
+ }
+#endif
+ if (XGetRGBColormaps(display, RootWindow(display, i),
+ &stdcmap, &ncmaps, XA_RGB_DEFAULT_MAP)) {
+ if (stdcmap) {
+ for (int c = 0; c < ncmaps; ++c) {
+ if (!stdcmap[c].red_max ||
+ !stdcmap[c].green_max ||
+ !stdcmap[c].blue_max ||
+ !stdcmap[c].red_mult ||
+ !stdcmap[c].green_mult ||
+ !stdcmap[c].blue_mult)
+ continue; // invalid stdcmap
+
+ XVisualInfo proto;
+ proto.visualid = stdcmap[c].visualid;
+ proto.screen = i;
+
+ int nvisuals = 0;
+ XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask | VisualScreenMask,
+ &proto, &nvisuals);
+ if (vi) {
+ if (nvisuals > 0) {
+ use_stdcmap = true;
+
+ d->mode = ((vi[0].visual->c_class < StaticColor)
+ ? Gray
+ : ((vi[0].visual->c_class < TrueColor)
+ ? Indexed
+ : Direct));
+
+ d->depth = vi[0].depth;
+ d->colormap = stdcmap[c].colormap;
+ d->defaultColormap = true;
+ d->visual = vi[0].visual;
+ d->defaultVisual = (d->visual == DefaultVisual(display, i));
+
+ d->r_max = stdcmap[c].red_max + 1;
+ d->g_max = stdcmap[c].green_max + 1;
+ d->b_max = stdcmap[c].blue_max + 1;
+
+ if (d->mode == Direct) {
+ // calculate offsets
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ } else {
+ d->r_shift = 0;
+ d->g_shift = 0;
+ d->b_shift = 0;
+ }
+ }
+ XFree(vi);
+ }
+ break;
+ }
+ XFree(stdcmap);
+ }
+ }
+ }
+ if (!use_stdcmap) {
+ switch (d->visual->c_class) {
+ case StaticGray:
+ d->mode = Gray;
+
+ d->r_max = d->g_max = d->b_max = d->visual->map_entries;
+ break;
+
+ case XGrayScale:
+ d->mode = Gray;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = color_count;
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 4096;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 512;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = 12;
+ else
+ d->r_max = d->g_max = d->b_max = 4;
+ break;
+
+ case StaticColor:
+ d->mode = Indexed;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+ break;
+
+ case PseudoColor:
+ d->mode = Indexed;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = cube_root(color_count);
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 27;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 12;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries - 125);
+ else
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries);
+ break;
+
+ case TrueColor:
+ case DirectColor:
+ d->mode = Direct;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ break;
+ }
+ }
+
+ bool ownColormap = false;
+ if (X11->colormap && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->colormap = X11->colormap;
+ d->defaultColormap = (d->colormap == DefaultColormap(display, i));
+ } else if ((!use_stdcmap
+ && (((d->visual->c_class & 1) && X11->custom_cmap)
+ || d->visual != DefaultVisual(display, i)))
+ || d->visual->c_class == DirectColor) {
+ // allocate custom colormap (we always do this when using DirectColor visuals)
+ d->colormap =
+ XCreateColormap(display, RootWindow(display, i), d->visual,
+ d->visual->c_class == DirectColor ? AllocAll : AllocNone);
+ d->defaultColormap = false;
+ ownColormap = true;
+ }
+
+ switch (d->mode) {
+ case Gray:
+ init_gray(d, i);
+ break;
+ case Indexed:
+ init_indexed(d, i);
+ break;
+ case Direct:
+ init_direct(d, ownColormap);
+ break;
+ }
+
+ QX11InfoData *screen = X11->screens + i;
+ screen->depth = d->depth;
+ screen->visual = d->visual;
+ screen->defaultVisual = d->defaultVisual;
+ screen->colormap = d->colormap;
+ screen->defaultColormap = d->defaultColormap;
+ screen->cells = screen->visual->map_entries;
+
+ if (argbVisual) {
+ X11->argbVisuals[i] = argbVisual;
+ X11->argbColormaps[i] = XCreateColormap(display, RootWindow(display, i), argbVisual, AllocNone);
+ }
+
+ // ###
+ // We assume that 8bpp == pseudocolor, but this is not
+ // always the case (according to the X server), so we need
+ // to make sure that our internal data is setup in a way
+ // that is compatible with our assumptions
+ if (screen->visual->c_class == TrueColor && screen->depth == 8 && screen->cells == 8)
+ screen->cells = 256;
+ }
+}
+
+void QXcbColormap::cleanup()
+{
+ Display *display = X11->display;
+ const int screens = ScreenCount(display);
+
+ for (int i = 0; i < screens; ++i)
+ delete cmaps[i];
+
+ delete [] cmaps;
+ cmaps = 0;
+}
+
+
+QXcbColormap QXcbColormap::instance(int screen)
+{
+ if (screen == -1)
+ screen = QXcbX11Info::appScreen();
+ return *cmaps[screen];
+}
+
+/*! \internal
+ Constructs a new colormap.
+*/
+QXcbColormap::QXcbColormap()
+ : d(new QXcbColormapPrivate)
+{}
+
+QXcbColormap::QXcbColormap(const QXcbColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QXcbColormap::~QXcbColormap()
+{
+ if (!d->ref.deref()) {
+ if (!d->defaultColormap)
+ XFreeColormap(X11->display, d->colormap);
+ delete d;
+ }
+}
+
+QXcbColormap::Mode QXcbColormap::mode() const
+{ return d->mode; }
+
+int QXcbColormap::depth() const
+{ return d->depth; }
+
+int QXcbColormap::size() const
+{
+ return (d->mode == Gray
+ ? d->r_max
+ : (d->mode == Indexed
+ ? d->r_max * d->g_max * d->b_max
+ : -1));
+}
+
+uint QXcbColormap::pixel(const QColor &color) const
+{
+ const QRgba64 rgba64 = color.rgba64();
+ // XXX We emulate the raster engine here by getting the
+ // 8-bit values, but we could instead use the 16-bit
+ // values for slightly better color accuracy
+ const uint r = (rgba64.red8() * d->r_max) >> 8;
+ const uint g = (rgba64.green8() * d->g_max) >> 8;
+ const uint b = (rgba64.blue8() * d->b_max) >> 8;
+ if (d->mode != Direct) {
+ if (d->mode == Gray)
+ return d->pixels.at((r * 30 + g * 59 + b * 11) / 100);
+ return d->pixels.at(r * d->g_max * d->b_max + g * d->b_max + b);
+ }
+ return (r << d->r_shift) + (g << d->g_shift) + (b << d->b_shift);
+}
+
+const QColor QXcbColormap::colorAt(uint pixel) const
+{
+ if (d->mode != Direct) {
+ Q_ASSERT(pixel <= (uint)d->colors.size());
+ return d->colors.at(pixel);
+ }
+
+ const int r = (((pixel & d->visual->red_mask) >> d->r_shift) << 8) / d->r_max;
+ const int g = (((pixel & d->visual->green_mask) >> d->g_shift) << 8) / d->g_max;
+ const int b = (((pixel & d->visual->blue_mask) >> d->b_shift) << 8) / d->b_max;
+ return QColor(r, g, b);
+}
+
+const QVector<QColor> QXcbColormap::colormap() const
+{ return d->colors; }
+
+QXcbColormap &QXcbColormap::operator=(const QXcbColormap &colormap)
+{
+ qAtomicAssign(d, colormap.d);
+ return *this;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h
new file mode 100644
index 0000000000..530e3113e4
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORMAP_X11_H
+#define QCOLORMAP_X11_H
+
+#include <QColor>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+class QXcbColormapPrivate;
+class QXcbColormap
+{
+public:
+ enum Mode { Direct, Indexed, Gray };
+
+ static void initialize();
+ static void cleanup();
+
+ static QXcbColormap instance(int screen = -1);
+
+ QXcbColormap(const QXcbColormap &colormap);
+ ~QXcbColormap();
+
+ QXcbColormap &operator=(const QXcbColormap &colormap);
+
+ Mode mode() const;
+
+ int depth() const;
+ int size() const;
+
+ uint pixel(const QColor &color) const;
+ const QColor colorAt(uint pixel) const;
+
+ const QVector<QColor> colormap() const;
+
+private:
+ QXcbColormap();
+ QXcbColormapPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOLORMAP_X11_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
new file mode 100644
index 0000000000..3364b07c08
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
@@ -0,0 +1,2837 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qpixmapcache_p.h>
+#include <private/qpaintengine_p.h>
+#include <private/qpolygonclipper_p.h>
+#include <private/qpainterpath_p.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qfontengineglyphcache_p.h>
+
+#if QT_CONFIG(fontconfig)
+#include <private/qfontengine_ft_p.h>
+#endif
+
+#include "qpaintengine_x11_p.h"
+#include "qtessellator_p.h"
+#include "qpixmap_x11_p.h"
+#include "qcolormap_x11_p.h"
+#include "qt_x11_p.h"
+#include "qxcbexport.h"
+#include "qxcbnativepainting.h"
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(xrender)
+
+class QXRenderTessellator : public QTessellator
+{
+public:
+ QXRenderTessellator() : traps(0), allocated(0), size(0) {}
+ ~QXRenderTessellator() { free(traps); }
+ XTrapezoid *traps;
+ int allocated;
+ int size;
+ void addTrap(const Trapezoid &trap);
+ QRect tessellate(const QPointF *points, int nPoints, bool winding) {
+ size = 0;
+ setWinding(winding);
+ return QTessellator::tessellate(points, nPoints).toRect();
+ }
+ void done() {
+ if (allocated > 64) {
+ free(traps);
+ traps = 0;
+ allocated = 0;
+ }
+ }
+};
+
+void QXRenderTessellator::addTrap(const Trapezoid &trap)
+{
+ if (size == allocated) {
+ allocated = qMax(2*allocated, 64);
+ traps = q_check_ptr((XTrapezoid *)realloc(traps, allocated * sizeof(XTrapezoid)));
+ }
+ traps[size].top = Q27Dot5ToXFixed(trap.top);
+ traps[size].bottom = Q27Dot5ToXFixed(trap.bottom);
+ traps[size].left.p1.x = Q27Dot5ToXFixed(trap.topLeft->x);
+ traps[size].left.p1.y = Q27Dot5ToXFixed(trap.topLeft->y);
+ traps[size].left.p2.x = Q27Dot5ToXFixed(trap.bottomLeft->x);
+ traps[size].left.p2.y = Q27Dot5ToXFixed(trap.bottomLeft->y);
+ traps[size].right.p1.x = Q27Dot5ToXFixed(trap.topRight->x);
+ traps[size].right.p1.y = Q27Dot5ToXFixed(trap.topRight->y);
+ traps[size].right.p2.x = Q27Dot5ToXFixed(trap.bottomRight->x);
+ traps[size].right.p2.y = Q27Dot5ToXFixed(trap.bottomRight->y);
+ ++size;
+}
+
+#endif // QT_CONFIG(xrender)
+
+class QX11PaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QX11PaintEngine)
+public:
+ QX11PaintEnginePrivate()
+ {
+ opacity = 1.0;
+ scrn = -1;
+ hd = 0;
+ picture = 0;
+ gc = gc_brush = 0;
+ dpy = 0;
+ xinfo = 0;
+ txop = QTransform::TxNone;
+ has_clipping = false;
+ render_hints = 0;
+ xform_scale = 1;
+#if QT_CONFIG(xrender)
+ tessellator = 0;
+#endif
+ }
+ enum GCMode {
+ PenGC,
+ BrushGC
+ };
+
+ void init();
+ void fillPolygon_translated(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPolygon_dev(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPath(const QPainterPath &path, GCMode gcmode, bool transform);
+ void strokePolygon_dev(const QPointF *points, int pointCount, bool close);
+ void strokePolygon_translated(const QPointF *points, int pointCount, bool close);
+ void setupAdaptedOrigin(const QPoint &p);
+ void resetAdaptedOrigin();
+ void decidePathFallback() {
+ use_path_fallback = has_alpha_brush
+ || has_alpha_pen
+ || has_custom_pen
+ || has_complex_xform
+ || (render_hints & QPainter::Antialiasing);
+ }
+ void decideCoordAdjust() {
+ adjust_coords = !(render_hints & QPainter::Antialiasing)
+ && (render_hints & QPainter::Qt4CompatiblePainting)
+ && (has_alpha_pen
+ || (has_alpha_brush && has_pen && !has_alpha_pen)
+ || (cpen.style() > Qt::SolidLine));
+ }
+ void clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly);
+ void systemStateChanged() override;
+ inline bool isCosmeticPen() const {
+ if ((render_hints & QPainter::Qt4CompatiblePainting) && cpen == QPen())
+ return true;
+ return cpen.isCosmetic();
+ }
+
+ Display *dpy;
+ int scrn;
+ int pdev_depth;
+ unsigned long hd;
+ QPixmap brush_pm;
+#if QT_CONFIG(xrender)
+ unsigned long picture;
+ unsigned long current_brush;
+ QPixmap bitmap_texture;
+ int composition_mode;
+#else
+ unsigned long picture;
+#endif
+ GC gc;
+ GC gc_brush;
+
+ QPen cpen;
+ QBrush cbrush;
+ QRegion crgn;
+ QTransform matrix;
+ qreal opacity;
+
+ uint has_complex_xform : 1;
+ uint has_scaling_xform : 1;
+ uint has_non_scaling_xform : 1;
+ uint has_custom_pen : 1;
+ uint use_path_fallback : 1;
+ uint adjust_coords : 1;
+ uint has_clipping : 1;
+ uint adapted_brush_origin : 1;
+ uint adapted_pen_origin : 1;
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_texture : 1;
+ uint has_alpha_texture : 1;
+ uint has_pattern : 1;
+ uint has_alpha_pen : 1;
+ uint has_alpha_brush : 1;
+ uint render_hints;
+
+ const QXcbX11Info *xinfo;
+ QPointF bg_origin;
+ QTransform::TransformationType txop;
+ qreal xform_scale;
+
+ struct qt_float_point
+ {
+ qreal x, y;
+ };
+ QPolygonClipper<qt_float_point, qt_float_point, float> polygonClipper;
+
+ int xlibMaxLinePoints;
+#if QT_CONFIG(xrender)
+ QXRenderTessellator *tessellator;
+#endif
+};
+
+#if QT_CONFIG(xrender)
+class QXRenderGlyphCache : public QFontEngineGlyphCache
+{
+public:
+ QXRenderGlyphCache(QXcbX11Info x, QFontEngine::GlyphFormat format, const QTransform &matrix);
+ ~QXRenderGlyphCache();
+
+ bool addGlyphs(const QTextItemInt &ti, QVarLengthArray<glyph_t> glyphs, QVarLengthArray<QFixedPoint> positions);
+ bool draw(Drawable src, Drawable dst, const QTransform &matrix, const QTextItemInt &ti);
+
+ inline GlyphSet glyphSet();
+ inline int glyphBufferSize(const QFontEngineFT::Glyph &glyph) const;
+
+ inline QImage::Format imageFormat() const;
+ inline const XRenderPictFormat *renderPictFormat() const;
+
+ static inline QFontEngine::GlyphFormat glyphFormatForDepth(QFontEngine *fontEngine, int depth);
+ static inline Glyph glyphId(glyph_t glyph, QFixed subPixelPosition);
+
+ static inline bool isValidCoordinate(const QFixedPoint &fp);
+
+private:
+ QXcbX11Info xinfo;
+ GlyphSet gset;
+ QSet<Glyph> cachedGlyphs;
+};
+#endif // QT_CONFIG(xrender)
+
+extern QPixmap qt_pixmapForBrush(int brushStyle, bool invert); //in qbrush.cpp
+extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap);
+
+extern "C" {
+Q_XCB_EXPORT Drawable qt_x11Handle(const QPaintDevice *pd)
+{
+ if (!pd)
+ return 0;
+
+// if (pd->devType() == QInternal::Widget)
+// return static_cast<const QWidget *>(pd)->handle();
+
+ if (pd->devType() == QInternal::Pixmap)
+ return qt_x11PixmapHandle(*static_cast<const QPixmap *>(pd));
+
+ return 0;
+}
+}
+
+static const QXcbX11Info *qt_x11Info(const QPaintDevice *pd)
+{
+ if (!pd)
+ return 0;
+
+// if (pd->devType() == QInternal::Widget)
+// return &static_cast<const QWidget *>(pd)->x11Info();
+
+ if (pd->devType() == QInternal::Pixmap)
+ return &qt_x11Info(*static_cast<const QPixmap *>(pd));
+
+ return 0;
+}
+
+// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+#undef X11 // defined in qt_x11_p.h
+extern "C" {
+/*!
+ Returns the X11 specific pen GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+GC Q_XCB_EXPORT qt_x11_get_pen_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc;
+ }
+ return 0;
+}
+
+/*!
+ Returns the X11 specific brush GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+GC Q_XCB_EXPORT qt_x11_get_brush_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc_brush;
+ }
+ return 0;
+}
+}
+#define X11 qt_x11Data
+
+// internal helper. Converts an integer value to an unique string token
+template <typename T>
+ struct HexString
+{
+ inline HexString(const T t)
+ : val(t)
+ {}
+
+ inline void write(QChar *&dest) const
+ {
+ const ushort hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ const char *c = reinterpret_cast<const char *>(&val);
+ for (uint i = 0; i < sizeof(T); ++i) {
+ *dest++ = hexChars[*c & 0xf];
+ *dest++ = hexChars[(*c & 0xf0) >> 4];
+ ++c;
+ }
+ }
+ const T val;
+};
+
+// specialization to enable fast concatenating of our string tokens to a string
+template <typename T>
+ struct QConcatenable<HexString<T> >
+{
+ typedef HexString<T> type;
+ enum { ExactSize = true };
+ static int size(const HexString<T> &) { return sizeof(T) * 2; }
+ static inline void appendTo(const HexString<T> &str, QChar *&out) { str.write(out); }
+ typedef QString ConvertTo;
+};
+
+#if QT_CONFIG(xrender)
+static const int compositionModeToRenderOp[QPainter::CompositionMode_Xor + 1] = {
+ PictOpOver, //CompositionMode_SourceOver,
+ PictOpOverReverse, //CompositionMode_DestinationOver,
+ PictOpClear, //CompositionMode_Clear,
+ PictOpSrc, //CompositionMode_Source,
+ PictOpDst, //CompositionMode_Destination,
+ PictOpIn, //CompositionMode_SourceIn,
+ PictOpInReverse, //CompositionMode_DestinationIn,
+ PictOpOut, //CompositionMode_SourceOut,
+ PictOpOutReverse, //CompositionMode_DestinationOut,
+ PictOpAtop, //CompositionMode_SourceAtop,
+ PictOpAtopReverse, //CompositionMode_DestinationAtop,
+ PictOpXor //CompositionMode_Xor
+};
+
+static inline int qpainterOpToXrender(QPainter::CompositionMode mode)
+{
+ Q_ASSERT(mode <= QPainter::CompositionMode_Xor);
+ return compositionModeToRenderOp[mode];
+}
+
+static inline bool complexPictOp(int op)
+{
+ return op != PictOpOver && op != PictOpSrc;
+}
+#endif
+
+static inline void x11SetClipRegion(Display *dpy, GC gc, GC gc2,
+#if QT_CONFIG(xrender)
+ Picture picture,
+#else
+ Qt::HANDLE picture,
+#endif
+ const QRegion &r)
+{
+// int num;
+// XRectangle *rects = (XRectangle *)qt_getClipRects(r, num);
+ QVector<XRectangle> rects = qt_region_to_xrectangles(r);
+ int num = rects.size();
+
+ if (gc)
+ XSetClipRectangles( dpy, gc, 0, 0, rects.data(), num, Unsorted );
+ if (gc2)
+ XSetClipRectangles( dpy, gc2, 0, 0, rects.data(), num, Unsorted );
+
+#if QT_CONFIG(xrender)
+ if (picture)
+ XRenderSetPictureClipRectangles(dpy, picture, 0, 0, rects.data(), num);
+#else
+ Q_UNUSED(picture);
+#endif // QT_CONFIG(xrender)
+}
+
+
+static inline void x11ClearClipRegion(Display *dpy, GC gc, GC gc2,
+#if QT_CONFIG(xrender)
+ Picture picture
+#else
+ Qt::HANDLE picture
+#endif
+ )
+{
+ if (gc)
+ XSetClipMask(dpy, gc, XNone);
+ if (gc2)
+ XSetClipMask(dpy, gc2, XNone);
+
+#if QT_CONFIG(xrender)
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture (dpy, picture, CPClipMask, &attrs);
+ }
+#else
+ Q_UNUSED(picture);
+#endif // QT_CONFIG(xrender)
+}
+
+
+#define DITHER_SIZE 16
+static const uchar base_dither_matrix[DITHER_SIZE][DITHER_SIZE] = {
+ { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
+ { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
+ { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
+ { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
+ { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
+ { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
+ { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
+ { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
+ { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
+ { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
+ { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
+ { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
+ { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
+ { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
+ { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
+ { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
+};
+
+static QPixmap qt_patternForAlpha(uchar alpha, int screen)
+{
+ QPixmap pm;
+ QString key = QLatin1Literal("$qt-alpha-brush$")
+ % HexString<uchar>(alpha)
+ % HexString<int>(screen);
+
+ if (!QPixmapCache::find(key, pm)) {
+ // #### why not use a mono image here????
+ QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32);
+ pattern.fill(0xffffffff);
+ for (int y = 0; y < DITHER_SIZE; ++y) {
+ for (int x = 0; x < DITHER_SIZE; ++x) {
+ if (base_dither_matrix[x][y] <= alpha)
+ pattern.setPixel(x, y, 0x00000000);
+ }
+ }
+ pm = QBitmap::fromImage(pattern);
+ qt_x11SetScreen(pm, screen);
+ //pm.x11SetScreen(screen);
+ QPixmapCache::insert(key, pm);
+ }
+ return pm;
+}
+
+
+#if QT_CONFIG(xrender)
+static Picture getPatternFill(int screen, const QBrush &b)
+{
+ if (!X11->use_xrender)
+ return XNone;
+
+ XRenderColor color = X11->preMultiply(b.color());
+ XRenderColor bg_color;
+
+ bg_color = X11->preMultiply(QColor(0, 0, 0, 0));
+
+ for (int i = 0; i < X11->pattern_fill_count; ++i) {
+ if (X11->pattern_fills[i].screen == screen
+ && X11->pattern_fills[i].opaque == false
+ && X11->pattern_fills[i].style == b.style()
+ && X11->pattern_fills[i].color.alpha == color.alpha
+ && X11->pattern_fills[i].color.red == color.red
+ && X11->pattern_fills[i].color.green == color.green
+ && X11->pattern_fills[i].color.blue == color.blue
+ && X11->pattern_fills[i].bg_color.alpha == bg_color.alpha
+ && X11->pattern_fills[i].bg_color.red == bg_color.red
+ && X11->pattern_fills[i].bg_color.green == bg_color.green
+ && X11->pattern_fills[i].bg_color.blue == bg_color.blue)
+ return X11->pattern_fills[i].picture;
+ }
+ // none found, replace one
+ int i = qrand() % 16;
+
+ if (X11->pattern_fills[i].screen != screen && X11->pattern_fills[i].picture) {
+ XRenderFreePicture (QXcbX11Info::display(), X11->pattern_fills[i].picture);
+ X11->pattern_fills[i].picture = 0;
+ }
+
+ if (!X11->pattern_fills[i].picture) {
+ Pixmap pixmap = XCreatePixmap (QXcbX11Info::display(), RootWindow (QXcbX11Info::display(), screen), 8, 8, 32);
+ XRenderPictureAttributes attrs;
+ attrs.repeat = True;
+ X11->pattern_fills[i].picture = XRenderCreatePicture (QXcbX11Info::display(), pixmap,
+ XRenderFindStandardFormat(QXcbX11Info::display(), PictStandardARGB32),
+ CPRepeat, &attrs);
+ XFreePixmap (QXcbX11Info::display(), pixmap);
+ }
+
+ X11->pattern_fills[i].screen = screen;
+ X11->pattern_fills[i].color = color;
+ X11->pattern_fills[i].bg_color = bg_color;
+ X11->pattern_fills[i].opaque = false;
+ X11->pattern_fills[i].style = b.style();
+
+ XRenderFillRectangle(QXcbX11Info::display(), PictOpSrc, X11->pattern_fills[i].picture, &bg_color, 0, 0, 8, 8);
+
+ QPixmap pattern(qt_pixmapForBrush(b.style(), true));
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(QXcbX11Info::display(), qt_x11PictureHandle(pattern), CPRepeat, &attrs);
+
+ Picture fill_fg = X11->getSolidFill(screen, b.color());
+ XRenderComposite(QXcbX11Info::display(), PictOpOver, fill_fg, qt_x11PictureHandle(pattern),
+ X11->pattern_fills[i].picture,
+ 0, 0, 0, 0, 0, 0, 8, 8);
+
+ return X11->pattern_fills[i].picture;
+}
+
+static void qt_render_bitmap(Display *dpy, int scrn, Picture src, Picture dst,
+ int sx, int sy, int x, int y, int sw, int sh,
+ const QPen &pen)
+{
+ Picture fill_fg = X11->getSolidFill(scrn, pen.color());
+ XRenderComposite(dpy, PictOpOver,
+ fill_fg, src, dst, sx, sy, sx, sy, x, y, sw, sh);
+}
+#endif
+
+void QX11PaintEnginePrivate::init()
+{
+ dpy = 0;
+ scrn = 0;
+ hd = 0;
+ picture = 0;
+ xinfo = 0;
+#if QT_CONFIG(xrender)
+ current_brush = 0;
+ composition_mode = PictOpOver;
+ tessellator = new QXRenderTessellator;
+#endif
+}
+
+void QX11PaintEnginePrivate::setupAdaptedOrigin(const QPoint &p)
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, p.x(), p.y());
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, p.x(), p.y());
+}
+
+void QX11PaintEnginePrivate::resetAdaptedOrigin()
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, 0, 0);
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, 0, 0);
+}
+
+void QX11PaintEnginePrivate::clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly)
+{
+ int clipped_count = 0;
+ qt_float_point *clipped_points = 0;
+ polygonClipper.clipPolygon((qt_float_point *) poly.data(), poly.size(),
+ &clipped_points, &clipped_count);
+ clipped_poly->resize(clipped_count);
+ for (int i=0; i<clipped_count; ++i)
+ (*clipped_poly)[i] = *((QPointF *)(&clipped_points[i]));
+}
+
+void QX11PaintEnginePrivate::systemStateChanged()
+{
+ Q_Q(QX11PaintEngine);
+ QPainter *painter = q->state ? q->state->painter() : nullptr;
+ if (painter && painter->hasClipping()) {
+ if (q->testDirty(QPaintEngine::DirtyTransform))
+ q->updateMatrix(q->state->transform());
+ QPolygonF clip_poly_dev(matrix.map(painter->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ q->updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ q->updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+}
+
+static QPaintEngine::PaintEngineFeatures qt_decide_features()
+{
+ QPaintEngine::PaintEngineFeatures features =
+ QPaintEngine::PrimitiveTransform
+ | QPaintEngine::PatternBrush
+ | QPaintEngine::AlphaBlend
+ | QPaintEngine::PainterPaths
+ | QPaintEngine::RasterOpModes;
+
+ if (X11->use_xrender) {
+ features |= QPaintEngine::Antialiasing;
+ features |= QPaintEngine::PorterDuff;
+ features |= QPaintEngine::MaskedBrush;
+#if 0
+ if (X11->xrender_version > 10) {
+ features |= QPaintEngine::LinearGradientFill;
+ // ###
+ }
+#endif
+ }
+
+ return features;
+}
+
+/*
+ * QX11PaintEngine members
+ */
+
+QX11PaintEngine::QX11PaintEngine()
+ : QPaintEngine(*(new QX11PaintEnginePrivate), qt_decide_features())
+{
+ Q_D(QX11PaintEngine);
+ d->init();
+}
+
+QX11PaintEngine::QX11PaintEngine(QX11PaintEnginePrivate &dptr)
+ : QPaintEngine(dptr, qt_decide_features())
+{
+ Q_D(QX11PaintEngine);
+ d->init();
+}
+
+QX11PaintEngine::~QX11PaintEngine()
+{
+#if QT_CONFIG(xrender)
+ Q_D(QX11PaintEngine);
+ delete d->tessellator;
+#endif
+}
+
+bool QX11PaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QX11PaintEngine);
+ d->xinfo = qt_x11Info(pdev);
+#if QT_CONFIG(xrender)
+ if (pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap *>(pdev);
+ QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(pm->handle());
+ if (X11->use_xrender && data->depth() != 32 && data->x11_mask)
+ data->convertToARGB32();
+ d->picture = qt_x11PictureHandle(*static_cast<const QPixmap *>(pdev));
+ }
+#else
+ d->picture = 0;
+#endif
+ d->hd = qt_x11Handle(pdev);
+
+ Q_ASSERT(d->xinfo != 0);
+ d->dpy = d->xinfo->display(); // get display variable
+ d->scrn = d->xinfo->screen(); // get screen variable
+
+ d->crgn = QRegion();
+ d->gc = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->gc_brush = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->has_alpha_brush = false;
+ d->has_alpha_pen = false;
+ d->has_clipping = false;
+ d->has_complex_xform = false;
+ d->has_scaling_xform = false;
+ d->has_non_scaling_xform = true;
+ d->xform_scale = 1;
+ d->has_custom_pen = false;
+ d->matrix = QTransform();
+ d->pdev_depth = d->pdev->depth();
+ d->render_hints = 0;
+ d->txop = QTransform::TxNone;
+ d->use_path_fallback = false;
+#if QT_CONFIG(xrender)
+ d->composition_mode = PictOpOver;
+#endif
+ d->xlibMaxLinePoints = 32762; // a safe number used to avoid, call to XMaxRequestSize(d->dpy) - 3;
+ d->opacity = 1;
+
+ // Set up the polygon clipper. Note: This will only work in
+ // polyline mode as long as we have a buffer zone, since a
+ // polyline may be clipped into several non-connected polylines.
+ const int BUFFERZONE = 1000;
+ QRect devClipRect(-BUFFERZONE, -BUFFERZONE,
+ pdev->width() + 2*BUFFERZONE, pdev->height() + 2*BUFFERZONE);
+ d->polygonClipper.setBoundingRect(devClipRect);
+
+ setSystemClip(systemClip());
+ d->systemStateChanged();
+
+ qt_x11SetDefaultScreen(d->xinfo->screen());
+
+ updatePen(QPen(Qt::black));
+ updateBrush(QBrush(Qt::white), QPoint());
+
+ setDirty(QPaintEngine::DirtyClipRegion);
+ setDirty(QPaintEngine::DirtyPen);
+ setDirty(QPaintEngine::DirtyBrush);
+ setDirty(QPaintEngine::DirtyBackground);
+
+ setActive(true);
+ return true;
+}
+
+bool QX11PaintEngine::end()
+{
+ Q_D(QX11PaintEngine);
+
+#if QT_CONFIG(xrender)
+ if (d->picture) {
+ // reset clipping/subwindow mode on our render picture
+ XRenderPictureAttributes attrs;
+ attrs.subwindow_mode = ClipByChildren;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture(d->dpy, d->picture, CPClipMask|CPSubwindowMode, &attrs);
+ }
+#endif
+
+ if (d->gc_brush && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc_brush);
+ d->gc_brush = 0;
+ }
+
+ if (d->gc && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc);
+ d->gc = 0;
+ }
+
+ // Restore system clip for alien widgets painting outside the paint event.
+// if (d->pdev->devType() == QInternal::Widget && !static_cast<QWidget *>(d->pdev)->internalWinId())
+ d->currentClipDevice = nullptr;
+ setSystemClip(QRegion());
+
+ setActive(false);
+ return true;
+}
+
+static bool clipLine(QLineF *line, const QRect &rect)
+{
+ qreal x1 = line->x1();
+ qreal x2 = line->x2();
+ qreal y1 = line->y1();
+ qreal y2 = line->y2();
+
+ qreal left = rect.x();
+ qreal right = rect.x() + rect.width() - 1;
+ qreal top = rect.y();
+ qreal bottom = rect.y() + rect.height() - 1;
+
+ enum { Left, Right, Top, Bottom };
+ // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
+ int p1 = ((x1 < left) << Left)
+ | ((x1 > right) << Right)
+ | ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ int p2 = ((x2 < left) << Left)
+ | ((x2 > right) << Right)
+ | ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+
+ if (p1 & p2)
+ // completely outside
+ return false;
+
+ if (p1 | p2) {
+ qreal dx = x2 - x1;
+ qreal dy = y2 - y1;
+
+ // clip x coordinates
+ if (x1 < left) {
+ y1 += dy/dx * (left - x1);
+ x1 = left;
+ } else if (x1 > right) {
+ y1 -= dy/dx * (x1 - right);
+ x1 = right;
+ }
+ if (x2 < left) {
+ y2 += dy/dx * (left - x2);
+ x2 = left;
+ } else if (x2 > right) {
+ y2 -= dy/dx * (x2 - right);
+ x2 = right;
+ }
+ p1 = ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ p2 = ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+ if (p1 & p2)
+ return false;
+ // clip y coordinates
+ if (y1 < top) {
+ x1 += dx/dy * (top - y1);
+ y1 = top;
+ } else if (y1 > bottom) {
+ x1 -= dx/dy * (y1 - bottom);
+ y1 = bottom;
+ }
+ if (y2 < top) {
+ x2 += dx/dy * (top - y2);
+ y2 = top;
+ } else if (y2 > bottom) {
+ x2 -= dx/dy * (y2 - bottom);
+ y2 = bottom;
+ }
+ *line = QLineF(QPointF(x1, y1), QPointF(x2, y2));
+ }
+ return true;
+}
+
+void QX11PaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef;
+ if (d->txop == QTransform::TxNone) {
+ linef = lines[i];
+ } else {
+ linef = d->matrix.map(QLineF(lines[i]));
+ }
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+void QX11PaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef = d->matrix.map(lines[i]);
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+static inline QLine clipStraightLine(const QRect &clip, const QLine &l)
+{
+ if (l.p1().x() == l.p2().x()) {
+ int x = qBound(clip.left(), l.p1().x(), clip.right());
+ int y1 = qBound(clip.top(), l.p1().y(), clip.bottom());
+ int y2 = qBound(clip.top(), l.p2().y(), clip.bottom());
+
+ return QLine(x, y1, x, y2);
+ } else {
+ Q_ASSERT(l.p1().y() == l.p2().y());
+
+ int x1 = qBound(clip.left(), l.p1().x(), clip.right());
+ int x2 = qBound(clip.left(), l.p2().x(), clip.right());
+ int y = qBound(clip.top(), l.p1().y(), clip.bottom());
+
+ return QLine(x1, y, x2, y);
+ }
+}
+
+void QX11PaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (rectCount != 1
+ || d->has_pen
+ || d->has_alpha_brush
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || d->cbrush.style() != Qt::SolidPattern
+#if QT_CONFIG(xrender)
+ || complexPictOp(d->composition_mode)
+#endif
+ )
+ {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+
+ QPoint alignedOffset;
+ if (d->txop == QTransform::TxTranslate) {
+ QPointF offset(d->matrix.dx(), d->matrix.dy());
+ alignedOffset = offset.toPoint();
+ if (offset != QPointF(alignedOffset)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ }
+
+ const QRectF& r = rects[0];
+ QRect alignedRect = r.toAlignedRect();
+ if (r != QRectF(alignedRect)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ alignedRect.translate(alignedOffset);
+
+ QRect clip(d->polygonClipper.boundingRect());
+ alignedRect = alignedRect.intersected(clip);
+ if (alignedRect.isEmpty())
+ return;
+
+ // simple-case:
+ // the rectangle is pixel-aligned
+ // the fill brush is just a solid non-alpha color
+ // the painter transform is only integer translation
+ // ignore: antialiasing and just XFillRectangles directly
+ XRectangle xrect;
+ xrect.x = short(alignedRect.x());
+ xrect.y = short(alignedRect.y());
+ xrect.width = ushort(alignedRect.width());
+ xrect.height = ushort(alignedRect.height());
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, &xrect, 1);
+}
+
+void QX11PaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (d->has_alpha_pen
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || (d->render_hints & QPainter::Antialiasing))
+ {
+ for (int i = 0; i < rectCount; ++i) {
+ QPainterPath path;
+ path.addRect(rects[i]);
+ drawPath(path);
+ }
+ return;
+ }
+
+ QRect clip(d->polygonClipper.boundingRect());
+ QPoint offset(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
+#if QT_CONFIG(xrender)
+ ::Picture pict = d->picture;
+
+ if (X11->use_xrender && pict && d->has_brush && d->pdev_depth != 1
+ && (d->has_texture || d->has_alpha_brush || complexPictOp(d->composition_mode)))
+ {
+ XRenderColor xc;
+ if (!d->has_texture && !d->has_pattern)
+ xc = X11->preMultiply(d->cbrush.color());
+
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ if (d->has_texture || d->has_pattern) {
+ XRenderComposite(d->dpy, d->composition_mode, d->current_brush, 0, pict,
+ qRound(r.x() - d->bg_origin.x()), qRound(r.y() - d->bg_origin.y()),
+ 0, 0, r.x(), r.y(), r.width(), r.height());
+ } else {
+ XRenderFillRectangle(d->dpy, d->composition_mode, pict, &xc, r.x(), r.y(), r.width(), r.height());
+ }
+ if (d->has_pen)
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ } else
+#endif // QT_CONFIG(xrender)
+ {
+ if (d->has_brush && d->has_pen) {
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ d->setupAdaptedOrigin(r.topLeft());
+ XFillRectangle(d->dpy, d->hd, d->gc_brush, r.x(), r.y(), r.width(), r.height());
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ d->resetAdaptedOrigin();
+ } else {
+ QVarLengthArray<XRectangle> xrects(rectCount);
+ int numClipped = rectCount;
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ --numClipped;
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty()) {
+ --numClipped;
+ continue;
+ }
+ xrects[i].x = short(r.x());
+ xrects[i].y = short(r.y());
+ xrects[i].width = ushort(r.width());
+ xrects[i].height = ushort(r.height());
+ }
+ if (numClipped) {
+ d->setupAdaptedOrigin(rects[0].topLeft());
+ if (d->has_brush)
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, xrects.data(), numClipped);
+ else if (d->has_pen)
+ XDrawRectangles(d->dpy, d->hd, d->gc, xrects.data(), numClipped);
+ d->resetAdaptedOrigin();
+ }
+ }
+ }
+}
+
+static inline void setCapStyle(int cap_style, GC gc)
+{
+ ulong mask = GCCapStyle;
+ XGCValues vals;
+ vals.cap_style = cap_style;
+ XChangeGC(QXcbX11Info::display(), gc, mask, &vals);
+}
+
+void QX11PaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+ const QPoint *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x()+.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPoint &xformed = d->matrix.map(points[i]);
+ int x = xformed.x();
+ int y = xformed.y();
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+void QX11PaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+
+ const QPointF *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x() + 0.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPointF &xformed = d->matrix.map(points[i]);
+ int x = qFloor(xformed.x());
+ int y = qFloor(xformed.y());
+
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+QPainter::RenderHints QX11PaintEngine::supportedRenderHints() const
+{
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender)
+ return QPainter::Antialiasing;
+#endif
+ return QFlag(0);
+}
+
+void QX11PaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QX11PaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+
+ if (flags & DirtyOpacity) {
+ d->opacity = state.opacity();
+ // Force update pen/brush as to get proper alpha colors propagated
+ flags |= DirtyPen;
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyTransform) updateMatrix(state.transform());
+ if (flags & DirtyPen) updatePen(state.pen());
+ if (flags & (DirtyBrush | DirtyBrushOrigin)) updateBrush(state.brush(), state.brushOrigin());
+ if (flags & DirtyFont) updateFont(state.font());
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled()) {
+ QPolygonF clip_poly_dev(d->matrix.map(painter()->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+ }
+
+ if (flags & DirtyClipPath) {
+ QPolygonF clip_poly_dev(d->matrix.map(state.clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon(), state.clipPath().fillRule()),
+ state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ extern QPainterPath qt_regionToPath(const QRegion &region);
+ QPainterPath clip_path = qt_regionToPath(state.clipRegion());
+ QPolygonF clip_poly_dev(d->matrix.map(clip_path.toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), state.clipOperation());
+ }
+ if (flags & DirtyHints) updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode) {
+ int function = GXcopy;
+ if (state.compositionMode() >= QPainter::RasterOp_SourceOrDestination) {
+ switch (state.compositionMode()) {
+ case QPainter::RasterOp_SourceOrDestination:
+ function = GXor;
+ break;
+ case QPainter::RasterOp_SourceAndDestination:
+ function = GXand;
+ break;
+ case QPainter::RasterOp_SourceXorDestination:
+ function = GXxor;
+ break;
+ case QPainter::RasterOp_NotSourceAndNotDestination:
+ function = GXnor;
+ break;
+ case QPainter::RasterOp_NotSourceOrNotDestination:
+ function = GXnand;
+ break;
+ case QPainter::RasterOp_NotSourceXorDestination:
+ function = GXequiv;
+ break;
+ case QPainter::RasterOp_NotSource:
+ function = GXcopyInverted;
+ break;
+ case QPainter::RasterOp_SourceAndNotDestination:
+ function = GXandReverse;
+ break;
+ case QPainter::RasterOp_NotSourceAndDestination:
+ function = GXandInverted;
+ break;
+ default:
+ function = GXcopy;
+ }
+ }
+#if QT_CONFIG(xrender)
+ else {
+ d->composition_mode =
+ qpainterOpToXrender(state.compositionMode());
+ }
+#endif
+ XSetFunction(X11->display, d->gc, function);
+ XSetFunction(X11->display, d->gc_brush, function);
+ }
+ d->decidePathFallback();
+ d->decideCoordAdjust();
+}
+
+void QX11PaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QX11PaintEngine);
+ d->render_hints = hints;
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender && d->picture) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = (hints & QPainter::Antialiasing) ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(d->dpy, d->picture, CPPolyEdge, &attrs);
+ }
+#endif
+}
+
+void QX11PaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QX11PaintEngine);
+ d->cpen = pen;
+ int cp = CapButt;
+ int jn = JoinMiter;
+ int ps = pen.style();
+
+ if (d->opacity < 1.0) {
+ QColor c = d->cpen.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cpen.setColor(c);
+ }
+
+ d->has_pen = (ps != Qt::NoPen);
+ d->has_alpha_pen = (pen.color().alpha() != 255);
+
+ switch (pen.capStyle()) {
+ case Qt::SquareCap:
+ cp = CapProjecting;
+ break;
+ case Qt::RoundCap:
+ cp = CapRound;
+ break;
+ case Qt::FlatCap:
+ default:
+ cp = CapButt;
+ break;
+ }
+ switch (pen.joinStyle()) {
+ case Qt::BevelJoin:
+ jn = JoinBevel;
+ break;
+ case Qt::RoundJoin:
+ jn = JoinRound;
+ break;
+ case Qt::MiterJoin:
+ default:
+ jn = JoinMiter;
+ break;
+ }
+
+ d->adapted_pen_origin = false;
+
+ char dashes[10]; // custom pen dashes
+ int dash_len = 0; // length of dash list
+ int xStyle = LineSolid;
+
+ /*
+ We are emulating Windows here. Windows treats cpen.width() == 1
+ (or 0) as a very special case. The fudge variable unifies this
+ case with the general case.
+ */
+ qreal pen_width = pen.widthF();
+ int scale = qRound(pen_width < 1 ? 1 : pen_width);
+ int space = (pen_width < 1 && pen_width > 0 ? 1 : (2 * scale));
+ int dot = 1 * scale;
+ int dash = 4 * scale;
+
+ d->has_custom_pen = false;
+
+ switch (ps) {
+ case Qt::NoPen:
+ case Qt::SolidLine:
+ xStyle = LineSolid;
+ break;
+ case Qt::DashLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DotLine:
+ dashes[0] = dot;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dash_len = 4;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dashes[4] = dot;
+ dashes[5] = space;
+ dash_len = 6;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::CustomDashLine:
+ d->has_custom_pen = true;
+ break;
+ }
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures | GCLineWidth
+ | GCCapStyle | GCJoinStyle | GCLineStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(pen.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (d->pdev->devType() == QInternal::Pixmap && d->pdev_depth == 32
+ && X11->use_xrender) {
+ vals.foreground = pen.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QXcbColormap cmap = QXcbColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(pen.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+ }
+
+
+ vals.line_width = qRound(pen.widthF());
+ vals.cap_style = cp;
+ vals.join_style = jn;
+ vals.line_style = xStyle;
+
+ XChangeGC(d->dpy, d->gc, mask, &vals);
+
+ if (dash_len) { // make dash list
+ XSetDashes(d->dpy, d->gc, 0, dashes, dash_len);
+ }
+
+ if (!d->has_clipping) { // if clipping is set the paintevent clip region is merged with the clip region
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
+{
+ Q_D(QX11PaintEngine);
+ d->cbrush = brush;
+ d->bg_origin = origin;
+ d->adapted_brush_origin = false;
+#if QT_CONFIG(xrender)
+ d->current_brush = 0;
+#endif
+ if (d->opacity < 1.0) {
+ QColor c = d->cbrush.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cbrush.setColor(c);
+ }
+
+ int s = FillSolid;
+ int bs = d->cbrush.style();
+ d->has_brush = (bs != Qt::NoBrush);
+ d->has_pattern = bs >= Qt::Dense1Pattern && bs <= Qt::DiagCrossPattern;
+ d->has_texture = bs == Qt::TexturePattern;
+ d->has_alpha_brush = brush.color().alpha() != 255;
+ d->has_alpha_texture = d->has_texture && d->cbrush.texture().hasAlphaChannel();
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures
+ | GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(d->cbrush.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (X11->use_xrender && d->pdev->devType() == QInternal::Pixmap
+ && d->pdev_depth == 32) {
+ vals.foreground = d->cbrush.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QXcbColormap cmap = QXcbColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(d->cbrush.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+
+ if (!X11->use_xrender && d->has_brush && !d->has_pattern && !brush.isOpaque()) {
+ QPixmap pattern = qt_patternForAlpha(brush.color().alpha(), d->scrn);
+ mask |= GCStipple;
+ vals.stipple = qt_x11PixmapHandle(pattern);
+ s = FillStippled;
+ d->adapted_brush_origin = true;
+ }
+ }
+ vals.cap_style = CapButt;
+ vals.join_style = JoinMiter;
+ vals.line_style = LineSolid;
+
+ if (d->has_pattern || d->has_texture) {
+ if (bs == Qt::TexturePattern) {
+ d->brush_pm = qt_toX11Pixmap(d->cbrush.texture());
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, qt_x11PictureHandle(d->brush_pm), CPRepeat, &attrs);
+ QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(d->brush_pm.handle());
+ if (data->mask_picture)
+ XRenderChangePicture(d->dpy, data->mask_picture, CPRepeat, &attrs);
+ }
+#endif
+ } else {
+ d->brush_pm = qt_toX11Pixmap(qt_pixmapForBrush(bs, true));
+ }
+ qt_x11SetScreen(d->brush_pm, d->scrn);
+ if (d->brush_pm.depth() == 1) {
+ mask |= GCStipple;
+ vals.stipple = qt_x11PixmapHandle(d->brush_pm);
+ s = FillStippled;
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ d->bitmap_texture = QPixmap(d->brush_pm.size());
+ d->bitmap_texture.fill(Qt::transparent);
+ d->bitmap_texture = qt_toX11Pixmap(d->bitmap_texture);
+ qt_x11SetScreen(d->bitmap_texture, d->scrn);
+
+ ::Picture src = X11->getSolidFill(d->scrn, d->cbrush.color());
+ XRenderComposite(d->dpy, PictOpSrc, src, qt_x11PictureHandle(d->brush_pm),
+ qt_x11PictureHandle(d->bitmap_texture),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height(),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height());
+
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, qt_x11PictureHandle(d->bitmap_texture), CPRepeat, &attrs);
+
+ d->current_brush = qt_x11PictureHandle(d->bitmap_texture);
+ }
+#endif
+ } else {
+ mask |= GCTile;
+#if QT_CONFIG(xrender)
+ if (d->pdev_depth == 32 && d->brush_pm.depth() != 32) {
+ d->brush_pm.detach();
+ QX11PlatformPixmap *brushData = static_cast<QX11PlatformPixmap*>(d->brush_pm.handle());
+ brushData->convertToARGB32();
+ }
+#endif
+ vals.tile = (d->brush_pm.depth() == d->pdev_depth
+ ? qt_x11PixmapHandle(d->brush_pm)
+ : static_cast<QX11PlatformPixmap*>(d->brush_pm.handle())->x11ConvertToDefaultDepth());
+ s = FillTiled;
+#if QT_CONFIG(xrender)
+ d->current_brush = qt_x11PictureHandle(d->cbrush.texture());
+#endif
+ }
+
+ mask |= GCTileStipXOrigin | GCTileStipYOrigin;
+ vals.ts_x_origin = qRound(origin.x());
+ vals.ts_y_origin = qRound(origin.y());
+ }
+#if QT_CONFIG(xrender)
+ else if (d->has_alpha_brush) {
+ d->current_brush = X11->getSolidFill(d->scrn, d->cbrush.color());
+ }
+#endif
+
+ vals.fill_style = s;
+ XChangeGC(d->dpy, d->gc_brush, mask, &vals);
+ if (!d->has_clipping) {
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc_brush, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc_brush, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::drawEllipse(const QRectF &rect)
+{
+ QRect aligned = rect.toAlignedRect();
+ if (aligned == rect)
+ drawEllipse(aligned);
+ else
+ QPaintEngine::drawEllipse(rect);
+}
+
+void QX11PaintEngine::drawEllipse(const QRect &rect)
+{
+ if (rect.isEmpty()) {
+ drawRects(&rect, 1);
+ return;
+ }
+
+ Q_D(QX11PaintEngine);
+ QRect devclip(SHRT_MIN, SHRT_MIN, SHRT_MAX*2 - 1, SHRT_MAX*2 - 1);
+ QRect r(rect);
+ if (d->txop < QTransform::TxRotate) {
+ r = d->matrix.mapRect(rect);
+ } else if (d->txop == QTransform::TxRotate && rect.width() == rect.height()) {
+ QPainterPath path;
+ path.addEllipse(rect);
+ r = d->matrix.map(path).boundingRect().toRect();
+ }
+
+ if (d->has_alpha_brush || d->has_alpha_pen || d->has_custom_pen || (d->render_hints & QPainter::Antialiasing)
+ || d->has_alpha_texture || devclip.intersected(r) != r
+ || (d->has_complex_xform
+ && !(d->has_non_scaling_xform && rect.width() == rect.height())))
+ {
+ QPainterPath path;
+ path.addEllipse(rect);
+ drawPath(path);
+ return;
+ }
+
+ int x = r.x();
+ int y = r.y();
+ int w = r.width();
+ int h = r.height();
+ if (w < 1 || h < 1)
+ return;
+ if (w == 1 && h == 1) {
+ XDrawPoint(d->dpy, d->hd, d->has_pen ? d->gc : d->gc_brush, x, y);
+ return;
+ }
+ d->setupAdaptedOrigin(rect.topLeft());
+ if (d->has_brush) { // draw filled ellipse
+ XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w, h, 0, 360*64);
+ if (!d->has_pen) // make smoother outline
+ XDrawArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 0, 360*64);
+ }
+ if (d->has_pen) // draw outline
+ XDrawArc(d->dpy, d->hd, d->gc, x, y, w, h, 0, 360*64);
+ d->resetAdaptedOrigin();
+}
+
+
+
+void QX11PaintEnginePrivate::fillPolygon_translated(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing))
+ offset += QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
+
+ for (int i = 0; i < pointCount; ++i) {
+ translated_points[i] = polygonPoints[i] + offset;
+
+ translated_points[i].rx() = qRound(translated_points[i].x()) + offs;
+ translated_points[i].ry() = qRound(translated_points[i].y()) + offs;
+ }
+
+ fillPolygon_dev(translated_points.data(), pointCount, gcMode, mode);
+}
+
+#if QT_CONFIG(xrender)
+static void qt_XRenderCompositeTrapezoids(Display *dpy,
+ int op,
+ Picture src,
+ Picture dst,
+ _Xconst XRenderPictFormat *maskFormat,
+ int xSrc,
+ int ySrc,
+ const XTrapezoid *traps, int size)
+{
+ const int MAX_TRAPS = 50000;
+ while (size) {
+ int to_draw = size;
+ if (to_draw > MAX_TRAPS)
+ to_draw = MAX_TRAPS;
+ XRenderCompositeTrapezoids(dpy, op, src, dst,
+ maskFormat,
+ xSrc, ySrc,
+ traps, to_draw);
+ size -= to_draw;
+ traps += to_draw;
+ }
+}
+#endif
+
+void QX11PaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+ Q_Q(QX11PaintEngine);
+
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+
+#if QT_CONFIG(xrender)
+ //can change if we switch to pen if gcMode != BrushGC
+ bool has_fill_texture = has_texture;
+ bool has_fill_pattern = has_pattern;
+ ::Picture src;
+#endif
+ QBrush fill;
+ GC fill_gc;
+ if (gcMode == BrushGC) {
+ fill = cbrush;
+ fill_gc = gc_brush;
+#if QT_CONFIG(xrender)
+ if (current_brush)
+ src = current_brush;
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ } else {
+ fill = QBrush(cpen.brush());
+ fill_gc = gc;
+#if QT_CONFIG(xrender)
+ //we use the pens brush
+ has_fill_texture = (fill.style() == Qt::TexturePattern);
+ has_fill_pattern = (fill.style() >= Qt::Dense1Pattern && fill.style() <= Qt::DiagCrossPattern);
+ if (has_fill_texture)
+ src = qt_x11PictureHandle(fill.texture());
+ else if (has_fill_pattern)
+ src = getPatternFill(scrn, fill);
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ }
+
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount);
+
+#if QT_CONFIG(xrender)
+ bool solid_fill = fill.color().alpha() == 255;
+ if (has_fill_texture && fill.texture().depth() == 1 && solid_fill) {
+ has_fill_texture = false;
+ has_fill_pattern = true;
+ }
+
+ bool antialias = render_hints & QPainter::Antialiasing;
+
+ if (X11->use_xrender
+ && picture
+ && !has_fill_pattern
+ && (clippedCount > 0)
+ && (fill.style() != Qt::NoBrush)
+ && ((has_fill_texture && fill.texture().hasAlpha()) || antialias || !solid_fill || has_alpha_pen != has_alpha_brush))
+ {
+ tessellator->tessellate((QPointF *)clippedPoints, clippedCount,
+ mode == QPaintEngine::WindingMode);
+ if (tessellator->size > 0) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = antialias ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(dpy, picture, CPPolyEdge, &attrs);
+ int x_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.x) - bg_origin.x());
+ int y_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.y) - bg_origin.y());
+ qt_XRenderCompositeTrapezoids(dpy, composition_mode, src, picture,
+ antialias
+ ? XRenderFindStandardFormat(dpy, PictStandardA8)
+ : XRenderFindStandardFormat(dpy, PictStandardA1),
+ x_offset, y_offset,
+ tessellator->traps, tessellator->size);
+ tessellator->done();
+ }
+ } else
+#endif
+ if (fill.style() != Qt::NoBrush) {
+ if (clippedCount > 200000) {
+ QPolygon poly;
+ for (int i = 0; i < clippedCount; ++i)
+ poly << QPoint(qFloor(clippedPoints[i].x), qFloor(clippedPoints[i].y));
+
+ const QRect bounds = poly.boundingRect();
+ const QRect aligned = bounds
+ & QRect(QPoint(), QSize(pdev->width(), pdev->height()));
+
+ QImage img(aligned.size(), QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+
+ QPainter painter(&img);
+ painter.translate(-aligned.x(), -aligned.y());
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(fill);
+ if (gcMode == BrushGC)
+ painter.setBrushOrigin(q->painter()->brushOrigin());
+ painter.drawPolygon(poly);
+ painter.end();
+
+ q->drawImage(aligned, img, img.rect(), Qt::AutoColor);
+ } else if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qFloor(clippedPoints[i].x);
+ xpoints[i].y = qFloor(clippedPoints[i].y);
+ }
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, WindingRule);
+ setupAdaptedOrigin(QPoint(xpoints[0].x, xpoints[0].y));
+ XFillPolygon(dpy, hd, fill_gc,
+ xpoints.data(), clippedCount,
+ mode == QPaintEngine::ConvexMode ? Convex : Complex, CoordModeOrigin);
+ resetAdaptedOrigin();
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, EvenOddRule);
+ }
+ }
+}
+
+void QX11PaintEnginePrivate::strokePolygon_translated(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+ for (int i = 0; i < pointCount; ++i)
+ translated_points[i] = polygonPoints[i] + offset;
+ strokePolygon_dev(translated_points.data(), pointCount, close);
+}
+
+void QX11PaintEnginePrivate::strokePolygon_dev(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount, close);
+
+ if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qRound(clippedPoints[i].x + aliasedCoordinateDelta);
+ xpoints[i].y = qRound(clippedPoints[i].y + aliasedCoordinateDelta);
+ }
+ uint numberPoints = qMin(clippedCount, xlibMaxLinePoints);
+ XPoint *pts = xpoints.data();
+ XDrawLines(dpy, hd, gc, pts, numberPoints, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ while (clippedCount) {
+ XDrawLines(dpy, hd, gc, pts-1, numberPoints+1, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ }
+ }
+}
+
+void QX11PaintEngine::drawPolygon(const QPointF *polygonPoints, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QX11PaintEngine);
+
+ if (d->use_path_fallback) {
+ QPainterPath path(polygonPoints[0]);
+ for (int i = 1; i < pointCount; ++i)
+ path.lineTo(polygonPoints[i]);
+ if (mode == PolylineMode) {
+ QBrush oldBrush = d->cbrush;
+ d->cbrush = QBrush(Qt::NoBrush);
+ path.setFillRule(Qt::WindingFill);
+ drawPath(path);
+ d->cbrush = oldBrush;
+ } else {
+ path.setFillRule(mode == OddEvenMode ? Qt::OddEvenFill : Qt::WindingFill);
+ path.closeSubpath();
+ drawPath(path);
+ }
+ return;
+ }
+ if (mode != PolylineMode && d->has_brush)
+ d->fillPolygon_translated(polygonPoints, pointCount, QX11PaintEnginePrivate::BrushGC, mode);
+
+ if (d->has_pen)
+ d->strokePolygon_translated(polygonPoints, pointCount, mode != PolylineMode);
+}
+
+
+void QX11PaintEnginePrivate::fillPath(const QPainterPath &path, QX11PaintEnginePrivate::GCMode gc_mode, bool transform)
+{
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+
+ QPainterPath clippedPath;
+ QPainterPath clipPath;
+ clipPath.addRect(polygonClipper.boundingRect());
+
+ if (transform)
+ clippedPath = (path*matrix).intersected(clipPath);
+ else
+ clippedPath = path.intersected(clipPath);
+
+ QList<QPolygonF> polys = clippedPath.toFillPolygons();
+ for (int i = 0; i < polys.size(); ++i) {
+ QVarLengthArray<QPointF> translated_points(polys.at(i).size());
+
+ for (int j = 0; j < polys.at(i).size(); ++j) {
+ translated_points[j] = polys.at(i).at(j);
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing)) {
+ translated_points[j].rx() = qRound(translated_points[j].rx() + aliasedCoordinateDelta) + offs;
+ translated_points[j].ry() = qRound(translated_points[j].ry() + aliasedCoordinateDelta) + offs;
+ }
+ }
+
+ fillPolygon_dev(translated_points.data(), polys.at(i).size(), gc_mode,
+ path.fillRule() == Qt::OddEvenFill ? QPaintEngine::OddEvenMode : QPaintEngine::WindingMode);
+ }
+}
+
+void QX11PaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QX11PaintEngine);
+ if (path.isEmpty())
+ return;
+
+ if (d->has_brush)
+ d->fillPath(path, QX11PaintEnginePrivate::BrushGC, true);
+ if (d->has_pen
+ && ((X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate
+ && !d->has_non_scaling_xform)
+ || (d->cpen.style() == Qt::CustomDashLine))) {
+ QPainterPathStroker stroker;
+ if (d->cpen.style() == Qt::CustomDashLine) {
+ stroker.setDashPattern(d->cpen.dashPattern());
+ stroker.setDashOffset(d->cpen.dashOffset());
+ } else {
+ stroker.setDashPattern(d->cpen.style());
+ }
+ stroker.setCapStyle(d->cpen.capStyle());
+ stroker.setJoinStyle(d->cpen.joinStyle());
+ QPainterPath stroke;
+ qreal width = d->cpen.widthF();
+ QPolygonF poly;
+ QRectF deviceRect(0, 0, d->pdev->width(), d->pdev->height());
+ // necessary to get aliased alphablended primitives to be drawn correctly
+ if (d->isCosmeticPen() || d->has_scaling_xform) {
+ if (d->isCosmeticPen())
+ stroker.setWidth(width == 0 ? 1 : width);
+ else
+ stroker.setWidth(width * d->xform_scale);
+ stroker.d_ptr->stroker.setClipRect(deviceRect);
+ stroke = stroker.createStroke(path * d->matrix);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, false);
+ } else {
+ stroker.setWidth(width);
+ stroker.d_ptr->stroker.setClipRect(d->matrix.inverted().mapRect(deviceRect));
+ stroke = stroker.createStroke(path);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, true);
+ }
+ } else if (d->has_pen) {
+ // if we have a cosmetic pen - use XDrawLine() for speed
+ QList<QPolygonF> polys = path.toSubpathPolygons(d->matrix);
+ for (int i = 0; i < polys.size(); ++i)
+ d->strokePolygon_dev(polys.at(i).data(), polys.at(i).size(), false);
+ }
+}
+
+Q_GUI_EXPORT void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image,
+ Drawable hd, GC gc, Display *dpy, Visual *visual, int depth)
+{
+ Q_ASSERT(image.format() == QImage::Format_RGB32);
+ Q_ASSERT(image.depth() == 32);
+
+ XImage *xi;
+ // Note: this code assumes either RGB or BGR, 8 bpc server layouts
+ const uint red_mask = (uint) visual->red_mask;
+ bool bgr_layout = (red_mask == 0xff);
+
+ const int w = rect.width();
+ const int h = rect.height();
+
+ QImage im;
+ int image_byte_order = ImageByteOrder(QXcbX11Info::display());
+ if ((QSysInfo::ByteOrder == QSysInfo::BigEndian && ((image_byte_order == LSBFirst) || bgr_layout))
+ || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ || (image_byte_order == LSBFirst && bgr_layout))
+ {
+ im = image.copy(rect);
+ const int iw = im.bytesPerLine() / 4;
+ uint *data = (uint *)im.bits();
+ for (int i=0; i < h; i++) {
+ uint *p = data;
+ uint *end = p + w;
+ if (bgr_layout && image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ while (p < end) {
+ *p = ((*p << 8) & 0xffffff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if ((image_byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if ((image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (image_byte_order == LSBFirst && bgr_layout))
+ {
+ while (p < end) {
+ *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff)
+ | ((*p ) & 0xff00ff00);
+ p++;
+ }
+ }
+ data += iw;
+ }
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) im.bits(), w, h, 32, im.bytesPerLine());
+ } else {
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) image.scanLine(rect.y())+rect.x()*sizeof(uint), w, h, 32, image.bytesPerLine());
+ }
+ XPutImage(dpy, hd, gc, xi, 0, 0, pos.x(), pos.y(), w, h);
+ xi->data = 0; // QImage owns these bits
+ XDestroyImage(xi);
+}
+
+void QX11PaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ Q_D(QX11PaintEngine);
+
+ if (image.format() == QImage::Format_RGB32
+ && d->pdev_depth >= 24 && image.depth() == 32
+ && r.size() == sr.size())
+ {
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+
+ qt_x11_drawImage(QRect(sx, sy, w, h), QPoint(x, y), image, d->hd, d->gc, d->dpy,
+ (Visual *)d->xinfo->visual(), d->pdev_depth);
+ } else {
+ QPaintEngine::drawImage(r, image, sr, flags);
+ }
+}
+
+void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRectF &_sr)
+{
+ Q_D(QX11PaintEngine);
+ QRectF sr = _sr;
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int sw = qRound(sr.width());
+ int sh = qRound(sr.height());
+
+ QPixmap pixmap = qt_toX11Pixmap(px);
+ if (pixmap.isNull())
+ return;
+
+ if ((d->xinfo && d->xinfo->screen() != qt_x11Info(pixmap).screen())
+ || (qt_x11Info(pixmap).screen() != DefaultScreen(QXcbX11Info::display()))) {
+ qt_x11SetScreen(pixmap, d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
+ }
+
+ qt_x11SetDefaultScreen(qt_x11Info(pixmap).screen());
+
+#if QT_CONFIG(xrender)
+ ::Picture src_pict = qt_x11PictureHandle(pixmap);
+ if (src_pict && d->picture) {
+ const int pDepth = pixmap.depth();
+ if (pDepth == 1 && (d->has_alpha_pen)) {
+ qt_render_bitmap(d->dpy, d->scrn, src_pict, d->picture,
+ sx, sy, x, y, sw, sh, d->cpen);
+ return;
+ } else if (pDepth != 1 && (pDepth == 32 || pDepth != d->pdev_depth)) {
+ XRenderComposite(d->dpy, d->composition_mode,
+ src_pict, 0, d->picture, sx, sy, 0, 0, x, y, sw, sh);
+ return;
+ }
+ }
+#endif
+
+ bool mono_src = pixmap.depth() == 1;
+ bool mono_dst = d->pdev_depth == 1;
+ bool restore_clip = false;
+
+ if (static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask) { // pixmap has a mask
+ QBitmap comb(sw, sh);
+ GC cgc = XCreateGC(d->dpy, qt_x11PixmapHandle(comb), 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, qt_x11PixmapHandle(comb), cgc, 0, 0, sw, sh);
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ if (!d->crgn.isEmpty()) {
+ QVector<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects.data(), rects.size(), Unsorted);
+ } else if (d->has_clipping) {
+ XSetClipRectangles(d->dpy, cgc, 0, 0, 0, 0, Unsorted);
+ }
+ XSetFillStyle(d->dpy, cgc, FillOpaqueStippled);
+ XSetTSOrigin(d->dpy, cgc, -sx, -sy);
+ XSetStipple(d->dpy, cgc,
+ static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask);
+ XFillRectangle(d->dpy, qt_x11PixmapHandle(comb), cgc, 0, 0, sw, sh);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XSetClipMask(d->dpy, d->gc, qt_x11PixmapHandle(comb));
+ restore_clip = true;
+ }
+
+ if (mono_src) {
+ if (!d->crgn.isEmpty()) {
+ Pixmap comb = XCreatePixmap(d->dpy, d->hd, sw, sh, 1);
+ GC cgc = XCreateGC(d->dpy, comb, 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, comb, cgc, 0, 0, sw, sh);
+ QVector<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects.data(), rects.size(), Unsorted);
+ XCopyArea(d->dpy, qt_x11PixmapHandle(pixmap), comb, cgc, sx, sy, sw, sh, 0, 0);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipMask(d->dpy, d->gc, comb);
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XFreePixmap(d->dpy, comb);
+ } else {
+ XSetClipMask(d->dpy, d->gc, qt_x11PixmapHandle(pixmap));
+ XSetClipOrigin(d->dpy, d->gc, x - sx, y - sy);
+ }
+
+ if (mono_dst) {
+ XSetForeground(d->dpy, d->gc, qGray(d->cpen.color().rgb()) > 127 ? 0 : 1);
+ } else {
+ QXcbColormap cmap = QXcbColormap::instance(d->scrn);
+ XSetForeground(d->dpy, d->gc, cmap.pixel(d->cpen.color()));
+ }
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, sw, sh);
+ restore_clip = true;
+ } else if (mono_dst && !mono_src) {
+ QBitmap bitmap(pixmap);
+ XCopyArea(d->dpy, qt_x11PixmapHandle(bitmap), d->hd, d->gc, sx, sy, sw, sh, x, y);
+ } else {
+ XCopyArea(d->dpy, qt_x11PixmapHandle(pixmap), d->hd, d->gc, sx, sy, sw, sh, x, y);
+ }
+
+ if (d->pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *px = static_cast<const QPixmap*>(d->pdev);
+ Pixmap src_mask = static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask;
+ Pixmap dst_mask = static_cast<QX11PlatformPixmap*>(px->handle())->x11_mask;
+ if (dst_mask) {
+ GC cgc = XCreateGC(d->dpy, dst_mask, 0, 0);
+ XSetClipOrigin(d->dpy, cgc, x, y);
+ XSetClipMask(d->dpy, cgc, src_mask);
+ if (src_mask) { // copy src mask into dst mask
+ XCopyArea(d->dpy, src_mask, dst_mask, cgc, sx, sy, sw, sh, x, y);
+ } else { // no src mask, but make sure the area copied is opaque in dest
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ XFillRectangle(d->dpy, dst_mask, cgc, x, y, sw, sh);
+ }
+ XFreeGC(d->dpy, cgc);
+ }
+ }
+
+ if (restore_clip) {
+ XSetClipOrigin(d->dpy, d->gc, 0, 0);
+ QVector<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
+ if (rects.isEmpty())
+ XSetClipMask(d->dpy, d->gc, XNone);
+ else
+ XSetClipRectangles(d->dpy, d->gc, 0, 0, rects.data(), rects.size(), Unsorted);
+ }
+}
+
+void QX11PaintEngine::updateMatrix(const QTransform &mtx)
+{
+ Q_D(QX11PaintEngine);
+ d->txop = mtx.type();
+ d->matrix = mtx;
+
+ d->has_complex_xform = (d->txop > QTransform::TxTranslate);
+
+ extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+ bool scaling = qt_scaleForTransform(d->matrix, &d->xform_scale);
+ d->has_scaling_xform = scaling && d->xform_scale != 1.0;
+ d->has_non_scaling_xform = scaling && d->xform_scale == 1.0;
+}
+
+/*
+ NB! the clip region is expected to be in dev coordinates
+*/
+void QX11PaintEngine::updateClipRegion_dev(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QX11PaintEngine);
+ QRegion sysClip = systemClip();
+ if (op == Qt::NoClip) {
+ d->has_clipping = false;
+ d->crgn = sysClip;
+ if (!sysClip.isEmpty()) {
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, sysClip);
+ } else {
+ x11ClearClipRegion(d->dpy, d->gc, d->gc_brush, d->picture);
+ }
+ return;
+ }
+
+ switch (op) {
+ case Qt::IntersectClip:
+ if (d->has_clipping) {
+ d->crgn &= clipRegion;
+ break;
+ }
+ // fall through
+ case Qt::ReplaceClip:
+ if (!sysClip.isEmpty())
+ d->crgn = clipRegion.intersected(sysClip);
+ else
+ d->crgn = clipRegion;
+ break;
+// case Qt::UniteClip:
+// d->crgn |= clipRegion;
+// if (!sysClip.isEmpty())
+// d->crgn = d->crgn.intersected(sysClip);
+// break;
+ default:
+ break;
+ }
+ d->has_clipping = true;
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, d->crgn);
+}
+
+void QX11PaintEngine::updateFont(const QFont &)
+{
+}
+
+Drawable QX11PaintEngine::handle() const
+{
+ Q_D(const QX11PaintEngine);
+ Q_ASSERT(isActive());
+ Q_ASSERT(d->hd);
+ return d->hd;
+}
+
+extern void qt_draw_tile(QPaintEngine *, qreal, qreal, qreal, qreal, const QPixmap &,
+ qreal, qreal);
+
+void QX11PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
+{
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+ int sx = qRound(p.x());
+ int sy = qRound(p.y());
+
+ bool mono_src = pixmap.depth() == 1;
+ Q_D(QX11PaintEngine);
+
+ if ((d->xinfo && d->xinfo->screen() != qt_x11Info(pixmap).screen())
+ || (qt_x11Info(pixmap).screen() != DefaultScreen(QXcbX11Info::display()))) {
+ QPixmap* p = const_cast<QPixmap *>(&pixmap);
+ qt_x11SetScreen(*p, d->xinfo ? d->xinfo->screen() : DefaultScreen(QXcbX11Info::display()));
+ }
+
+ qt_x11SetDefaultScreen(qt_x11Info(pixmap).screen());
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender && d->picture && qt_x11PictureHandle(pixmap)) {
+#if 0
+ // ### Qt 5: enable this
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, pixmap.x11PictureHandle(), CPRepeat, &attrs);
+
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, pixmap.x11PictureHandle(), d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ pixmap.x11PictureHandle(), XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#else
+ const int numTiles = (w / pixmap.width()) * (h / pixmap.height());
+ if (numTiles < 100) {
+ // this is essentially qt_draw_tile(), inlined for
+ // the XRenderComposite call
+ int yPos, xPos, drawH, drawW, yOff, xOff;
+ yPos = y;
+ yOff = sy;
+ while (yPos < y + h) {
+ drawH = pixmap.height() - yOff; // Cropping first row
+ if (yPos + drawH > y + h) // Cropping last row
+ drawH = y + h - yPos;
+ xPos = x;
+ xOff = sx;
+ while (xPos < x + w) {
+ drawW = pixmap.width() - xOff; // Cropping first column
+ if (xPos + drawW > x + w) // Cropping last column
+ drawW = x + w - xPos;
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, qt_x11PictureHandle(pixmap), d->picture,
+ xOff, yOff, xPos, yPos, drawW, drawH, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ qt_x11PictureHandle(pixmap), XNone, d->picture,
+ xOff, yOff, 0, 0, xPos, yPos, drawW, drawH);
+ }
+ xPos += drawW;
+ xOff = 0;
+ }
+ yPos += drawH;
+ yOff = 0;
+ }
+ } else {
+ w = qMin(w, d->pdev->width() - x);
+ h = qMin(h, d->pdev->height() - y);
+ if (w <= 0 || h <= 0)
+ return;
+
+ const int pw = w + sx;
+ const int ph = h + sy;
+ QPixmap pm(pw, ph);
+ if (pixmap.hasAlpha() || mono_src)
+ pm.fill(Qt::transparent);
+
+ const int mode = pixmap.hasAlpha() ? PictOpOver : PictOpSrc;
+ const ::Picture pmPicture = qt_x11PictureHandle(pm);
+
+ // first tile
+ XRenderComposite(d->dpy, mode,
+ qt_x11PictureHandle(pixmap), XNone, pmPicture,
+ 0, 0, 0, 0, 0, 0, qMin(pw, pixmap.width()), qMin(ph, pixmap.height()));
+
+ // first row of tiles
+ int xPos = pixmap.width();
+ const int sh = qMin(ph, pixmap.height());
+ while (xPos < pw) {
+ const int sw = qMin(xPos, pw - xPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, xPos, 0, sw, sh);
+ xPos *= 2;
+ }
+
+ // remaining rows
+ int yPos = pixmap.height();
+ const int sw = pw;
+ while (yPos < ph) {
+ const int sh = qMin(yPos, ph - yPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, 0, yPos, sw, sh);
+ yPos *= 2;
+ }
+
+ // composite
+ if (mono_src)
+ qt_render_bitmap(d->dpy, d->scrn, pmPicture, d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ else
+ XRenderComposite(d->dpy, d->composition_mode,
+ pmPicture, XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#endif
+ } else
+#endif // QT_CONFIG(xrender)
+ if (pixmap.depth() > 1 && !static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask) {
+ XSetTile(d->dpy, d->gc, qt_x11PixmapHandle(pixmap));
+ XSetFillStyle(d->dpy, d->gc, FillTiled);
+ XSetTSOrigin(d->dpy, d->gc, x-sx, y-sy);
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, w, h);
+ XSetTSOrigin(d->dpy, d->gc, 0, 0);
+ XSetFillStyle(d->dpy, d->gc, FillSolid);
+ } else {
+ qt_draw_tile(this, x, y, w, h, pixmap, sx, sy);
+ }
+}
+
+bool QX11PaintEngine::drawCachedGlyphs(const QTransform &transform, const QTextItemInt &ti)
+{
+#if QT_CONFIG(xrender)
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
+
+ if (!X11->use_xrender)
+ return false;
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
+ QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform);
+
+ if (!set || set->outline_drawing)
+ return false;
+
+ QFontEngine::GlyphFormat glyphFormat = QXRenderGlyphCache::glyphFormatForDepth(ft, d->pdev_depth);
+
+ QXRenderGlyphCache *cache = static_cast<QXRenderGlyphCache *>(ft->glyphCache(set, glyphFormat, transform));
+ if (!cache) {
+ cache = new QXRenderGlyphCache(QXcbX11Info(), glyphFormat, transform);
+ ft->setGlyphCache(set, cache);
+ }
+
+ return cache->draw(X11->getSolidFill(d->scrn, d->cpen.color()), d->picture, transform, ti);
+#else // !QT_CONFIG(xrender)
+ return false;
+#endif // QT_CONFIG(xrender)
+}
+
+void QX11PaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QX11PaintEngine);
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ switch (ti.fontEngine->type()) {
+ case QFontEngine::TestFontEngine:
+ case QFontEngine::Box:
+ d->drawBoxTextItem(p, ti);
+ break;
+#if QT_CONFIG(fontconfig)
+ case QFontEngine::Freetype:
+ drawFreetype(p, ti);
+ break;
+#endif
+ default:
+ Q_ASSERT(false);
+ }
+}
+
+#if QT_CONFIG(fontconfig)
+static bool path_for_glyphs(QPainterPath *path,
+ const QVarLengthArray<glyph_t> &glyphs,
+ const QVarLengthArray<QFixedPoint> &positions,
+ const QFontEngineFT *ft)
+{
+ bool result = true;
+ *path = QPainterPath();
+ path->setFillRule(Qt::WindingFill);
+ ft->lockFace();
+ int i = 0;
+ while (i < glyphs.size()) {
+ QFontEngineFT::Glyph *glyph = ft->loadGlyph(glyphs[i], 0, QFontEngineFT::Format_Mono);
+ // #### fix case where we don't get a glyph
+ if (!glyph || glyph->format != QFontEngineFT::Format_Mono) {
+ result = false;
+ break;
+ }
+
+ int n = 0;
+ int h = glyph->height;
+ int xp = qRound(positions[i].x);
+ int yp = qRound(positions[i].y);
+
+ xp += glyph->x;
+ yp += -glyph->y + glyph->height;
+ int pitch = ((glyph->width + 31) & ~31) >> 3;
+
+ uchar *src = glyph->data;
+ while (h--) {
+ for (int x = 0; x < glyph->width; ++x) {
+ bool set = src[x >> 3] & (0x80 >> (x & 7));
+ if (set) {
+ QRect r(xp + x, yp - h, 1, 1);
+ while (x+1 < glyph->width && src[(x+1) >> 3] & (0x80 >> ((x+1) & 7))) {
+ ++x;
+ r.setRight(r.right()+1);
+ }
+
+ path->addRect(r);
+ ++n;
+ }
+ }
+ src += pitch;
+ }
+ ++i;
+ }
+ ft->unlockFace();
+ return result;
+}
+
+void QX11PaintEngine::drawFreetype(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QX11PaintEngine);
+
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ if (!d->cpen.isSolid()) {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ const bool xrenderPath = (X11->use_xrender
+ && !(d->pdev->devType() == QInternal::Pixmap
+ && static_cast<const QPixmap *>(d->pdev)->handle()->pixelType() == QPlatformPixmap::BitmapType));
+
+ if (xrenderPath) {
+ QTransform transform = d->matrix;
+ transform.translate(p.x(), p.y());
+
+ if (drawCachedGlyphs(transform, ti))
+ return;
+ }
+
+ QTransform transform;
+ transform.translate(p.x(), p.y());
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ ti.fontEngine->getGlyphPositions(ti.glyphs, transform, ti.flags, glyphs, positions);
+
+ if (glyphs.count() == 0)
+ return;
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
+ QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform);
+ QPainterPath path;
+
+ if (!set || set->outline_drawing || !path_for_glyphs(&path, glyphs, positions, ft)) {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ if (path.elementCount() <= 1)
+ return;
+
+ Q_ASSERT((path.elementCount() % 5) == 0);
+ if (d->txop >= QTransform::TxScale) {
+ painter()->save();
+ painter()->setBrush(d->cpen.brush());
+ painter()->setPen(Qt::NoPen);
+ painter()->drawPath(path);
+ painter()->restore();
+ return;
+ }
+
+ const int rectcount = 256;
+ XRectangle rects[rectcount];
+ int num_rects = 0;
+
+ QPoint delta(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
+ QRect clip(d->polygonClipper.boundingRect());
+ for (int i=0; i < path.elementCount(); i+=5) {
+ int x = qRound(path.elementAt(i).x);
+ int y = qRound(path.elementAt(i).y);
+ int w = qRound(path.elementAt(i+1).x) - x;
+ int h = qRound(path.elementAt(i+2).y) - y;
+
+ QRect rect = QRect(x + delta.x(), y + delta.y(), w, h);
+ rect = rect.intersected(clip);
+ if (rect.isEmpty())
+ continue;
+
+ rects[num_rects].x = short(rect.x());
+ rects[num_rects].y = short(rect.y());
+ rects[num_rects].width = ushort(rect.width());
+ rects[num_rects].height = ushort(rect.height());
+ ++num_rects;
+ if (num_rects == rectcount) {
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+ num_rects = 0;
+ }
+ }
+ if (num_rects > 0)
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+}
+#endif // QT_CONFIG(fontconfig)
+
+#if QT_CONFIG(xrender)
+QXRenderGlyphCache::QXRenderGlyphCache(QXcbX11Info x, QFontEngine::GlyphFormat format, const QTransform &matrix)
+ : QFontEngineGlyphCache(format, matrix)
+ , xinfo(x)
+ , gset(XNone)
+{}
+
+QXRenderGlyphCache::~QXRenderGlyphCache()
+{
+ if (gset != XNone)
+ XRenderFreeGlyphSet(xinfo.display(), gset);
+}
+
+bool QXRenderGlyphCache::addGlyphs(const QTextItemInt &ti, QVarLengthArray<glyph_t> glyphs, QVarLengthArray<QFixedPoint> positions)
+{
+ Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
+ QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform());
+
+ XGlyphInfo xglyphinfo;
+
+ for (int i = 0; i < glyphs.size(); ++i) {
+ const QFixed spp = ft->subPixelPositionForX(positions[i].x);
+ QFontEngineFT::Glyph *glyph = set->getGlyph(glyphs[i], spp);
+ Glyph xglyphid = qHash(QFontEngineFT::GlyphAndSubPixelPosition(glyphs[i], spp));
+
+ if (glyph && glyph->format == glyphFormat()) {
+ if (cachedGlyphs.contains(xglyphid)) {
+ continue;
+ } else {
+ set->setGlyph(glyphs[i], spp, nullptr);
+ delete glyph;
+ glyph = 0;
+ }
+ }
+
+ glyph = ft->loadGlyphFor(glyphs[i], spp, glyphFormat(), transform());
+
+ if (glyph == 0 || glyph->format != glyphFormat())
+ return false;
+
+ set->setGlyph(glyphs[i], spp, glyph);
+ Q_ASSERT(glyph->data || glyph->width == 0 || glyph->height == 0);
+
+ xglyphinfo.width = glyph->width;
+ xglyphinfo.height = glyph->height;
+ xglyphinfo.x = -glyph->x;
+ xglyphinfo.y = glyph->y;
+ xglyphinfo.xOff = glyph->advance;
+ xglyphinfo.yOff = 0;
+
+ XRenderAddGlyphs(xinfo.display(), glyphSet(), &xglyphid, &xglyphinfo, 1, (const char *) glyph->data, glyphBufferSize(*glyph));
+ cachedGlyphs.insert(xglyphid);
+ }
+
+ return true;
+}
+
+bool QXRenderGlyphCache::draw(Drawable src, Drawable dst, const QTransform &matrix, const QTextItemInt &ti)
+{
+ Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
+
+ if (ti.glyphs.numGlyphs == 0)
+ return true;
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
+ QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(matrix);
+
+ QVarLengthArray<glyph_t> glyphs;
+ QVarLengthArray<QFixedPoint> positions;
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ if (glyphs.isEmpty())
+ return true;
+
+ if (!addGlyphs(ti, glyphs, positions))
+ return false;
+
+ QVarLengthArray<unsigned int> chars(glyphs.size());
+
+ for (int i = 0; i < glyphs.size(); ++i)
+ chars[i] = glyphId(glyphs[i], ft->subPixelPositionForX(positions[i].x));
+
+ int i = 0;
+ while (i < glyphs.size() && !isValidCoordinate(positions[i]))
+ ++i;
+
+ if (i >= glyphs.size())
+ return true;
+
+ QFixed xp = positions[i].x;
+ QFixed yp = positions[i].y;
+ QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+
+ XGlyphElt32 elt;
+ elt.glyphset = gset;
+ elt.chars = &chars[i];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+
+ ++i;
+
+ for (; i < glyphs.size(); ++i) {
+ if (!isValidCoordinate(positions[i]))
+ break;
+
+ const QFixed spp = ft->subPixelPositionForX(positions[i].x);
+ QFontEngineFT::Glyph *g = set->getGlyph(glyphs[i], spp);
+
+ if (g
+ && positions[i].x == xp + g->advance
+ && positions[i].y == yp
+ && elt.nchars < 253 // don't draw more than 253 characters as some X servers
+ // hang with it
+ ) {
+ elt.nchars++;
+ xp += g->advance;
+ } else {
+ xp = positions[i].x;
+ yp = positions[i].y;
+
+ XRenderCompositeText32(xinfo.display(), PictOpOver, src, dst,
+ renderPictFormat(), 0, 0, 0, 0,
+ &elt, 1);
+ elt.chars = &chars[i];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+ }
+ }
+
+ XRenderCompositeText32(xinfo.display(), PictOpOver, src, dst,
+ renderPictFormat(), 0, 0, 0, 0, &elt, 1);
+
+ return true;
+}
+
+GlyphSet QXRenderGlyphCache::glyphSet()
+{
+ if (gset == XNone)
+ gset = XRenderCreateGlyphSet(xinfo.display(), renderPictFormat());
+
+ Q_ASSERT(gset != XNone);
+ return gset;
+}
+
+int QXRenderGlyphCache::glyphBufferSize(const QFontEngineFT::Glyph &glyph) const
+{
+ int pitch = 0;
+
+ switch (glyphFormat()) {
+ case QFontEngine::Format_Mono:
+ pitch = ((glyph.width + 31) & ~31) >> 3;
+ break;
+ case QFontEngine::Format_A8:
+ pitch = (glyph.width + 3) & ~3;
+ break;
+ default:
+ pitch = glyph.width * 4;
+ break;
+ }
+
+ return pitch * glyph.height;
+}
+
+QImage::Format QXRenderGlyphCache::imageFormat() const
+{
+ switch (glyphFormat()) {
+ case QFontEngine::Format_None:
+ Q_UNREACHABLE();
+ break;
+ case QFontEngine::Format_Mono:
+ return QImage::Format_Mono;
+ break;
+ case QFontEngine::Format_A8:
+ return QImage::Format_Alpha8;
+ break;
+ case QFontEngine::Format_A32:
+ case QFontEngine::Format_ARGB:
+ return QImage::Format_ARGB32_Premultiplied;
+ break;
+ }
+
+ Q_UNREACHABLE();
+}
+
+const XRenderPictFormat *QXRenderGlyphCache::renderPictFormat() const
+{
+ switch (glyphFormat()) {
+ case QFontEngine::Format_None:
+ Q_UNREACHABLE();
+ break;
+ case QFontEngine::Format_Mono:
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardA1);
+ break;
+ case QFontEngine::Format_A8:
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardA8);
+ break;
+ case QFontEngine::Format_A32:
+ case QFontEngine::Format_ARGB:
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32);
+ break;
+ }
+
+ Q_UNREACHABLE();
+}
+
+QFontEngine::GlyphFormat QXRenderGlyphCache::glyphFormatForDepth(QFontEngine *fontEngine, int depth)
+{
+ QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat;
+
+ if (glyphFormat == QFontEngine::Format_None) {
+ switch (depth) {
+ case 32:
+ glyphFormat = QFontEngine::Format_ARGB;
+ break;
+ case 24:
+ glyphFormat = QFontEngine::Format_A32;
+ break;
+ case 1:
+ glyphFormat = QFontEngine::Format_Mono;
+ break;
+ default:
+ glyphFormat = QFontEngine::Format_A8;
+ break;
+ }
+ }
+
+ return glyphFormat;
+}
+
+Glyph QXRenderGlyphCache::glyphId(glyph_t glyph, QFixed subPixelPosition)
+{
+ return qHash(QFontEngineFT::GlyphAndSubPixelPosition(glyph, subPixelPosition));
+}
+
+bool QXRenderGlyphCache::isValidCoordinate(const QFixedPoint &fp)
+{
+ enum { t_min = SHRT_MIN, t_max = SHRT_MAX };
+ return (fp.x < t_min || fp.x > t_max || fp.y < t_min || fp.y > t_max) ? false : true;
+}
+#endif // QT_CONFIG(xrender)
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
new file mode 100644
index 0000000000..34b5d929d5
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_X11_H
+#define QPAINTENGINE_X11_H
+
+#include <QtGui/QPaintEngine>
+
+typedef unsigned long XID;
+typedef XID Drawable;
+typedef struct _XGC *GC;
+
+QT_BEGIN_NAMESPACE
+
+extern "C" {
+Drawable qt_x11Handle(const QPaintDevice *pd);
+GC qt_x11_get_pen_gc(QPainter *);
+GC qt_x11_get_brush_gc(QPainter *);
+}
+
+class QX11PaintEnginePrivate;
+class QX11PaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QX11PaintEngine)
+public:
+ QX11PaintEngine();
+ ~QX11PaintEngine();
+
+ bool begin(QPaintDevice *pdev) Q_DECL_OVERRIDE;
+ bool end() Q_DECL_OVERRIDE;
+
+ void updateState(const QPaintEngineState &state) Q_DECL_OVERRIDE;
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateRenderHints(QPainter::RenderHints hints);
+ void updateFont(const QFont &font);
+ void updateMatrix(const QTransform &matrix);
+ void updateClipRegion_dev(const QRegion &region, Qt::ClipOperation op);
+
+ void drawLines(const QLine *lines, int lineCount) Q_DECL_OVERRIDE;
+ void drawLines(const QLineF *lines, int lineCount) Q_DECL_OVERRIDE;
+
+ void drawRects(const QRect *rects, int rectCount) Q_DECL_OVERRIDE;
+ void drawRects(const QRectF *rects, int rectCount) Q_DECL_OVERRIDE;
+
+ void drawPoints(const QPoint *points, int pointCount) Q_DECL_OVERRIDE;
+ void drawPoints(const QPointF *points, int pointCount) Q_DECL_OVERRIDE;
+
+ void drawEllipse(const QRect &r) Q_DECL_OVERRIDE;
+ void drawEllipse(const QRectF &r) Q_DECL_OVERRIDE;
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) Q_DECL_OVERRIDE;
+ inline void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) Q_DECL_OVERRIDE
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) Q_DECL_OVERRIDE;
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) Q_DECL_OVERRIDE;
+ void drawPath(const QPainterPath &path) Q_DECL_OVERRIDE;
+ void drawTextItem(const QPointF &p, const QTextItem &textItem) Q_DECL_OVERRIDE;
+ void drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor) Q_DECL_OVERRIDE;
+
+ virtual Drawable handle() const;
+ inline Type type() const Q_DECL_OVERRIDE { return QPaintEngine::X11; }
+
+ QPainter::RenderHints supportedRenderHints() const;
+
+protected:
+ QX11PaintEngine(QX11PaintEnginePrivate &dptr);
+
+#if QT_CONFIG(fontconfig)
+ void drawFreetype(const QPointF &p, const QTextItemInt &ti);
+ bool drawCachedGlyphs(const QTransform &transform, const QTextItemInt &ti);
+#endif // QT_CONFIG(fontconfig)
+
+ friend class QPixmap;
+ friend class QFontEngineBox;
+ friend GC qt_x11_get_pen_gc(QPainter *);
+ friend GC qt_x11_get_brush_gc(QPainter *);
+
+private:
+ Q_DISABLE_COPY(QX11PaintEngine)
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_X11_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp
new file mode 100644
index 0000000000..f791c90346
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp
@@ -0,0 +1,2108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+
+#include <private/qdrawhelper_p.h>
+#include <private/qimage_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
+
+#include "qxcbnativepainting.h"
+#include "qpixmap_x11_p.h"
+#include "qcolormap_x11_p.h"
+#include "qpaintengine_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if QT_POINTER_SIZE == 8 // 64-bit versions
+
+Q_ALWAYS_INLINE uint PREMUL(uint x) {
+ uint a = x >> 24;
+ quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8;
+ t &= 0x000000ff00ff00ff;
+ return (uint(t)) | (uint(t >> 24)) | (a << 24);
+}
+
+#else // 32-bit versions
+
+Q_ALWAYS_INLINE uint PREMUL(uint x) {
+ uint a = x >> 24;
+ uint t = (x & 0xff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff) * a;
+ x = (x + ((x >> 8) & 0xff) + 0x80);
+ x &= 0xff00;
+ x |= t | (a << 24);
+ return x;
+}
+#endif
+
+
+
+struct QXImageWrapper
+{
+ XImage *xi;
+};
+
+QPixmap qt_toX11Pixmap(const QImage &image)
+{
+ QPlatformPixmap *data =
+ new QX11PlatformPixmap(image.depth() == 1
+ ? QPlatformPixmap::BitmapType
+ : QPlatformPixmap::PixmapType);
+
+ data->fromImage(image, Qt::AutoColor);
+
+ return QPixmap(data);
+}
+
+QPixmap qt_toX11Pixmap(const QPixmap &pixmap)
+{
+ if (pixmap.isNull())
+ return QPixmap();
+
+ if (QPixmap(pixmap).data_ptr()->classId() == QPlatformPixmap::X11Class)
+ return pixmap;
+
+ return qt_toX11Pixmap(pixmap.toImage());
+}
+
+// For thread-safety:
+// image->data does not belong to X11, so we must free it ourselves.
+
+inline static void qSafeXDestroyImage(XImage *x)
+{
+ if (x->data) {
+ free(x->data);
+ x->data = 0;
+ }
+ XDestroyImage(x);
+}
+
+QBitmap QX11PlatformPixmap::mask_to_bitmap(int screen) const
+{
+ if (!x11_mask)
+ return QBitmap();
+ qt_x11SetDefaultScreen(screen);
+ QBitmap bm(w, h);
+ QX11PlatformPixmap *that = qt_x11Pixmap(bm);
+ const QXcbX11Info *x = that->x11_info();
+ GC gc = XCreateGC(x->display(), that->handle(), 0, 0);
+ XCopyArea(x->display(), x11_mask, that->handle(), gc, 0, 0,
+ that->width(), that->height(), 0, 0);
+ XFreeGC(x->display(), gc);
+ return bm;
+}
+
+void QX11PlatformPixmap::bitmapFromImage(const QImage &image)
+{
+ w = image.width();
+ h = image.height();
+ d = 1;
+ is_null = (w <= 0 || h <= 0);
+ hd = createBitmapFromImage(image);
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender)
+ picture = XRenderCreatePicture(xinfo.display(), hd,
+ XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0);
+#endif // QT_CONFIG(xrender)
+}
+
+bool QX11PlatformPixmap::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ if (xi->format != ZPixmap)
+ return false;
+
+ // ARGB32_Premultiplied
+ if (picture && depth() == 32)
+ return true;
+
+ // RGB32
+ if (depth() == 24 && xi->bits_per_pixel == 32 && xi->red_mask == 0xff0000
+ && xi->green_mask == 0xff00 && xi->blue_mask == 0xff)
+ return true;
+
+ // RGB16
+ if (depth() == 16 && xi->bits_per_pixel == 16 && xi->red_mask == 0xf800
+ && xi->green_mask == 0x7e0 && xi->blue_mask == 0x1f)
+ return true;
+
+ return false;
+}
+
+QImage QX11PlatformPixmap::takeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ QImage::Format format = QImage::Format_ARGB32_Premultiplied;
+ if (depth() == 24)
+ format = QImage::Format_RGB32;
+ else if (depth() == 16)
+ format = QImage::Format_RGB16;
+
+ QImage image((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format);
+ image.setDevicePixelRatio(devicePixelRatio());
+ // take ownership
+ image.data_ptr()->own_data = true;
+ xi->data = 0;
+
+ // we may have to swap the byte order
+ if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst)
+ || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst))
+ {
+ for (int i=0; i < image.height(); i++) {
+ if (depth() == 16) {
+ ushort *p = (ushort*)image.scanLine(i);
+ ushort *end = p + image.width();
+ while (p < end) {
+ *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff);
+ p++;
+ }
+ } else {
+ uint *p = (uint*)image.scanLine(i);
+ uint *end = p + image.width();
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ }
+ }
+ }
+
+ // fix-up alpha channel
+ if (format == QImage::Format_RGB32) {
+ QRgb *p = (QRgb *)image.bits();
+ for (int y = 0; y < xi->height; ++y) {
+ for (int x = 0; x < xi->width; ++x)
+ p[x] |= 0xff000000;
+ p += xi->bytes_per_line / 4;
+ }
+ }
+
+ XDestroyImage(xi);
+ return image;
+}
+
+XID QX11PlatformPixmap::bitmap_to_mask(const QBitmap &bitmap, int screen)
+{
+ if (bitmap.isNull())
+ return 0;
+ QBitmap bm = bitmap;
+ qt_x11SetScreen(bm, screen);
+
+ QX11PlatformPixmap *that = qt_x11Pixmap(bm);
+ const QXcbX11Info *x = that->x11_info();
+ Pixmap mask = XCreatePixmap(x->display(), RootWindow(x->display(), screen),
+ that->width(), that->height(), 1);
+ GC gc = XCreateGC(x->display(), mask, 0, 0);
+ XCopyArea(x->display(), that->handle(), mask, gc, 0, 0,
+ that->width(), that->height(), 0, 0);
+ XFreeGC(x->display(), gc);
+ return mask;
+}
+
+Drawable qt_x11Handle(const QPixmap &pixmap)
+{
+ if (pixmap.isNull())
+ return XNone;
+
+ if (pixmap.handle()->classId() != QPlatformPixmap::X11Class)
+ return XNone;
+
+ return static_cast<const QX11PlatformPixmap *>(pixmap.handle())->handle();
+}
+
+
+/*****************************************************************************
+ Internal functions
+ *****************************************************************************/
+
+//extern const uchar *qt_get_bitflip_array(); // defined in qimage.cpp
+
+// Returns position of highest bit set or -1 if none
+static int highest_bit(uint v)
+{
+ int i;
+ uint b = (uint)1 << 31;
+ for (i=31; ((b & v) == 0) && i>=0; i--)
+ b >>= 1;
+ return i;
+}
+
+// Counts the number of bits set in 'v'
+static uint n_bits(uint v)
+{
+ int i = 0;
+ while (v) {
+ v = v & (v - 1);
+ i++;
+ }
+ return i;
+}
+
+static uint *red_scale_table = 0;
+static uint *green_scale_table = 0;
+static uint *blue_scale_table = 0;
+
+static void cleanup_scale_tables()
+{
+ delete[] red_scale_table;
+ delete[] green_scale_table;
+ delete[] blue_scale_table;
+}
+
+/*
+ Could do smart bitshifting, but the "obvious" algorithm only works for
+ nBits >= 4. This is more robust.
+*/
+static void build_scale_table(uint **table, uint nBits)
+{
+ if (nBits > 7) {
+ qWarning("build_scale_table: internal error, nBits = %i", nBits);
+ return;
+ }
+ if (!*table) {
+ static bool firstTable = true;
+ if (firstTable) {
+ qAddPostRoutine(cleanup_scale_tables);
+ firstTable = false;
+ }
+ *table = new uint[256];
+ }
+ int maxVal = (1 << nBits) - 1;
+ int valShift = 8 - nBits;
+ int i;
+ for (i = 0 ; i < maxVal + 1 ; i++)
+ (*table)[i << valShift] = i*255/maxVal;
+}
+
+static int defaultScreen = -1;
+
+int qt_x11SetDefaultScreen(int screen)
+{
+ int old = defaultScreen;
+ defaultScreen = screen;
+ return old;
+}
+
+void qt_x11SetScreen(QPixmap &pixmap, int screen)
+{
+ if (pixmap.paintingActive()) {
+ qWarning("qt_x11SetScreen(): Cannot change screens during painting");
+ return;
+ }
+
+ if (pixmap.isNull())
+ return;
+
+ if (pixmap.handle()->classId() != QPlatformPixmap::X11Class)
+ return;
+
+ if (screen < 0)
+ screen = QXcbX11Info::appScreen();
+
+ QX11PlatformPixmap *pm = static_cast<QX11PlatformPixmap *>(pixmap.handle());
+ if (screen == pm->xinfo.screen())
+ return; // nothing to do
+
+ if (pixmap.isNull()) {
+ pm->xinfo = QXcbX11Info::fromScreen(screen);
+ return;
+ }
+
+#if 0
+ qDebug("qt_x11SetScreen for %p from %d to %d. Size is %d/%d", pm, pm->xinfo.screen(), screen, pm->width(), pm->height());
+#endif
+
+ qt_x11SetDefaultScreen(screen);
+ pixmap = qt_toX11Pixmap(pixmap.toImage());
+}
+
+/*****************************************************************************
+ QPixmap member functions
+ *****************************************************************************/
+
+QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0);
+int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0;
+
+QX11PlatformPixmap::QX11PlatformPixmap(PixelType pixelType)
+ : QPlatformPixmap(pixelType, X11Class), hd(0),
+ flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0),
+ dpr(1.0), pengine(0)
+{}
+
+QX11PlatformPixmap::~QX11PlatformPixmap()
+{
+ // Cleanup hooks have to be called before the handles are freed
+ if (is_cached) {
+ QImagePixmapCleanupHooks::executePlatformPixmapDestructionHooks(this);
+ is_cached = false;
+ }
+
+ release();
+}
+
+QPlatformPixmap *QX11PlatformPixmap::createCompatiblePlatformPixmap() const
+{
+ QX11PlatformPixmap *p = new QX11PlatformPixmap(pixelType());
+ p->setDevicePixelRatio(devicePixelRatio());
+ return p;
+}
+
+void QX11PlatformPixmap::resize(int width, int height)
+{
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ xinfo = QXcbX11Info::fromScreen(defaultScreen);
+ }
+
+ int dd = xinfo.depth();
+
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ bool make_null = w <= 0 || h <= 0; // create null pixmap
+ d = (pixelType() == BitmapType ? 1 : dd);
+ if (make_null || d == 0) {
+ w = 0;
+ h = 0;
+ is_null = true;
+ hd = 0;
+ picture = 0;
+ d = 0;
+ if (!make_null)
+ qWarning("QPixmap: Invalid pixmap parameters");
+ return;
+ }
+ hd = XCreatePixmap(xinfo.display(),
+ RootWindow(xinfo.display(), xinfo.screen()),
+ w, h, d);
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(xinfo.display(), PictStandardA1)
+ : XRenderFindVisualFormat(xinfo.display(), (Visual *) xinfo.visual());
+ picture = XRenderCreatePicture(xinfo.display(), hd, format, 0, 0);
+ }
+#endif // QT_CONFIG(xrender)
+}
+
+struct QX11AlphaDetector
+{
+ bool hasAlpha() const {
+ if (checked)
+ return has;
+ // Will implicitly also check format and return quickly for opaque types...
+ checked = true;
+ has = image->isNull() ? false : const_cast<QImage *>(image)->data_ptr()->checkForAlphaPixels();
+ return has;
+ }
+
+ bool hasXRenderAndAlpha() const {
+ if (!X11->use_xrender)
+ return false;
+ return hasAlpha();
+ }
+
+ QX11AlphaDetector(const QImage *i, Qt::ImageConversionFlags flags)
+ : image(i), checked(false), has(false)
+ {
+ if (flags & Qt::NoOpaqueDetection) {
+ checked = true;
+ has = image->hasAlphaChannel();
+ }
+ }
+
+ const QImage *image;
+ mutable bool checked;
+ mutable bool has;
+};
+
+void QX11PlatformPixmap::fromImage(const QImage &img, Qt::ImageConversionFlags flags)
+{
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ w = img.width();
+ h = img.height();
+ d = img.depth();
+ is_null = (w <= 0 || h <= 0);
+ setDevicePixelRatio(img.devicePixelRatio());
+
+ if (is_null) {
+ w = h = 0;
+ return;
+ }
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ xinfo = QXcbX11Info::fromScreen(defaultScreen);
+ }
+
+ if (pixelType() == BitmapType) {
+ bitmapFromImage(img);
+ return;
+ }
+
+ if (uint(w) >= 32768 || uint(h) >= 32768) {
+ w = h = 0;
+ is_null = true;
+ return;
+ }
+
+ QX11AlphaDetector alphaCheck(&img, flags);
+ int dd = alphaCheck.hasXRenderAndAlpha() ? 32 : xinfo.depth();
+
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ QImage image = img;
+
+ // must be monochrome
+ if (dd == 1 || (flags & Qt::ColorMode_Mask) == Qt::MonoOnly) {
+ if (d != 1) {
+ // dither
+ image = image.convertToFormat(QImage::Format_MonoLSB, flags);
+ d = 1;
+ }
+ } else { // can be both
+ bool conv8 = false;
+ if (d > 8 && dd <= 8) { // convert to 8 bit
+ if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
+ flags = (flags & ~Qt::DitherMode_Mask)
+ | Qt::PreferDither;
+ conv8 = true;
+ } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
+ conv8 = (d == 1); // native depth wanted
+ } else if (d == 1) {
+ if (image.colorCount() == 2) {
+ QRgb c0 = image.color(0); // Auto: convert to best
+ QRgb c1 = image.color(1);
+ conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
+ } else {
+ // eg. 1-color monochrome images (they do exist).
+ conv8 = true;
+ }
+ }
+ if (conv8) {
+ image = image.convertToFormat(QImage::Format_Indexed8, flags);
+ d = 8;
+ }
+ }
+
+ if (d == 1 || image.format() > QImage::Format_ARGB32_Premultiplied) {
+ QImage::Format fmt = QImage::Format_RGB32;
+ if (alphaCheck.hasXRenderAndAlpha() && d > 1)
+ fmt = QImage::Format_ARGB32_Premultiplied;
+ image = image.convertToFormat(fmt, flags);
+ fromImage(image, Qt::AutoColor);
+ return;
+ }
+
+ Display *dpy = xinfo.display();
+ Visual *visual = (Visual *)xinfo.visual();
+ XImage *xi = 0;
+ bool trucol = (visual->c_class >= TrueColor);
+ size_t nbytes = image.sizeInBytes();
+ uchar *newbits= 0;
+
+#if QT_CONFIG(xrender)
+ if (alphaCheck.hasXRenderAndAlpha()) {
+ const QImage &cimage = image;
+
+ d = 32;
+
+ if (QXcbX11Info::appDepth() != d) {
+ xinfo.setDepth(d);
+ }
+
+ hd = XCreatePixmap(dpy, RootWindow(dpy, xinfo.screen()), w, h, d);
+ picture = XRenderCreatePicture(dpy, hd,
+ XRenderFindStandardFormat(dpy, PictStandardARGB32), 0, 0);
+
+ xi = XCreateImage(dpy, visual, d, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ xi->data = (char *)newbits;
+
+ switch (cimage.format()) {
+ case QImage::Format_Indexed8: {
+ QVector<QRgb> colorTable = cimage.colorTable();
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const uchar *p = cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = colorTable[p[x]];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+ }
+ break;
+ case QImage::Format_RGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x)
+ *xidata++ = p[x] | 0xff000000;
+ }
+ }
+ break;
+ case QImage::Format_ARGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = p[x];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+
+ }
+ break;
+ case QImage::Format_ARGB32_Premultiplied: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ memcpy(xidata, p, w*sizeof(QRgb));
+ xidata += w;
+ }
+ }
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ if ((xi->byte_order == MSBFirst) != (QSysInfo::ByteOrder == QSysInfo::BigEndian)) {
+ uint *xidata = (uint *)xi->data;
+ uint *xiend = xidata + w*h;
+ while (xidata < xiend) {
+ *xidata = (*xidata >> 24)
+ | ((*xidata >> 8) & 0xff00)
+ | ((*xidata << 8) & 0xff0000)
+ | (*xidata << 24);
+ ++xidata;
+ }
+ }
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+
+ return;
+ }
+#endif // QT_CONFIG(xrender)
+
+ if (trucol) { // truecolor display
+ if (image.format() == QImage::Format_ARGB32_Premultiplied)
+ image = image.convertToFormat(QImage::Format_ARGB32);
+
+ const QImage &cimage = image;
+ QRgb pix[256]; // pixel translation table
+ const bool d8 = (d == 8);
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+ const uint rbits = highest_bit(red_mask) - lowest_bit(red_mask) + 1;
+ const uint gbits = highest_bit(green_mask) - lowest_bit(green_mask) + 1;
+ const uint bbits = highest_bit(blue_mask) - lowest_bit(blue_mask) + 1;
+
+ if (d8) { // setup pixel translation
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i=0; i < cimage.colorCount(); i++) {
+ int r = qRed (ctable[i]);
+ int g = qGreen(ctable[i]);
+ int b = qBlue (ctable[i]);
+ r = red_shift > 0 ? r << red_shift : r >> -red_shift;
+ g = green_shift > 0 ? g << green_shift : g >> -green_shift;
+ b = blue_shift > 0 ? b << blue_shift : b >> -blue_shift;
+ pix[i] = (b & blue_mask) | (g & green_mask) | (r & red_mask)
+ | ~(blue_mask | green_mask | red_mask);
+ }
+ }
+
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ int bppc = xi->bits_per_pixel;
+
+ bool contig_bits = n_bits(red_mask) == rbits &&
+ n_bits(green_mask) == gbits &&
+ n_bits(blue_mask) == bbits;
+ bool dither_tc =
+ // Want it?
+ (flags & Qt::Dither_Mask) != Qt::ThresholdDither &&
+ (flags & Qt::DitherMode_Mask) != Qt::AvoidDither &&
+ // Need it?
+ bppc < 24 && !d8 &&
+ // Can do it? (Contiguous bits?)
+ contig_bits;
+
+ static bool init=false;
+ static int D[16][16];
+ if (dither_tc && !init) {
+ // I also contributed this code to XV - WWA.
+ /*
+ The dither matrix, D, is obtained with this formula:
+
+ D2 = [0 2]
+ [3 1]
+
+
+ D2*n = [4*Dn 4*Dn+2*Un]
+ [4*Dn+3*Un 4*Dn+1*Un]
+ */
+ int n,i,j;
+ init=1;
+
+ /* Set D2 */
+ D[0][0]=0;
+ D[1][0]=2;
+ D[0][1]=3;
+ D[1][1]=1;
+
+ /* Expand using recursive definition given above */
+ for (n=2; n<16; n*=2) {
+ for (i=0; i<n; i++) {
+ for (j=0; j<n; j++) {
+ D[i][j]*=4;
+ D[i+n][j]=D[i][j]+2;
+ D[i][j+n]=D[i][j]+3;
+ D[i+n][j+n]=D[i][j]+1;
+ }
+ }
+ }
+ init=true;
+ }
+
+ enum { BPP8,
+ BPP16_565, BPP16_555,
+ BPP16_MSB, BPP16_LSB,
+ BPP24_888,
+ BPP24_MSB, BPP24_LSB,
+ BPP32_8888,
+ BPP32_MSB, BPP32_LSB
+ } mode = BPP8;
+
+ bool same_msb_lsb = (xi->byte_order == MSBFirst) == (QSysInfo::ByteOrder == QSysInfo::BigEndian);
+
+ if (bppc == 8) // 8 bit
+ mode = BPP8;
+ else if (bppc == 16) { // 16 bit MSB/LSB
+ if (red_shift == 8 && green_shift == 3 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_565;
+ else if (red_shift == 7 && green_shift == 2 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_555;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP16_LSB : BPP16_MSB;
+ } else if (bppc == 24) { // 24 bit MSB/LSB
+ if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP24_888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP24_LSB : BPP24_MSB;
+ } else if (bppc == 32) { // 32 bit MSB/LSB
+ if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP32_8888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP32_LSB : BPP32_MSB;
+ } else
+ qFatal("Logic error 3");
+
+#define GET_PIXEL \
+ uint pixel; \
+ if (d8) pixel = pix[*src++]; \
+ else { \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask) \
+ | ~(blue_mask | green_mask | red_mask); \
+ }
+
+#define GET_PIXEL_DITHER_TC \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ const int thres = D[x%16][y%16]; \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ uint pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask);
+
+// again, optimized case
+// can't be optimized that much :(
+#define GET_PIXEL_DITHER_TC_OPT(red_shift,green_shift,blue_shift,red_mask,green_mask,blue_mask, \
+ rbits,gbits,bbits) \
+ const int thres = D[x%16][y%16]; \
+ int r = qRed (*p); \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ int g = qGreen(*p); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ int b = qBlue (*p++); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ uint pixel = ((r red_shift) & red_mask) \
+ | ((g green_shift) & green_mask) \
+ | ((b blue_shift) & blue_mask);
+
+#define CYCLE(body) \
+ for (int y=0; y<h; y++) { \
+ const uchar* src = cimage.scanLine(y); \
+ uchar* dst = newbits + xi->bytes_per_line*y; \
+ const QRgb* p = (const QRgb *)src; \
+ body \
+ }
+
+ if (dither_tc) {
+ switch (mode) {
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<8,<<3,>>3,0xf800,0x7e0,0x1f,5,6,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<7,<<2,>>3,0x7c00,0x3e0,0x1f,5,5,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error");
+ }
+ } else {
+ switch (mode) {
+ case BPP8: // 8 bit
+ CYCLE(
+ Q_UNUSED(p);
+ for (int x=0; x<w; x++)
+ *dst++ = pix[*src++];
+ )
+ break;
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x = 0; x < w; x++) {
+ *dst16++ = ((*p >> 8) & 0xf800)
+ | ((*p >> 5) & 0x7e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ *dst16++ = ((*p >> 9) & 0x7c00)
+ | ((*p >> 6) & 0x3e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ case BPP24_888:
+ CYCLE(
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ for (int x=0; x<w; x++) {
+ *dst++ = qRed (*p);
+ *dst++ = qGreen(*p);
+ *dst++ = qBlue (*p++);
+ }
+ } else {
+ for (int x=0; x<w; x++) {
+ *dst++ = qBlue (*p);
+ *dst++ = qGreen(*p);
+ *dst++ = qRed (*p++);
+ }
+ }
+ )
+ break;
+ case BPP24_MSB: // 24 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP24_LSB: // 24 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ }
+ )
+ break;
+ case BPP32_8888:
+ CYCLE(
+ memcpy(dst, p, w * 4);
+ )
+ break;
+ case BPP32_MSB: // 32 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 24;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP32_LSB: // 32 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 24;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error 2");
+ }
+ }
+ xi->data = (char *)newbits;
+ }
+
+ if (d == 8 && !trucol) { // 8 bit pixmap
+ int pop[256]; // pixel popularity
+
+ if (image.colorCount() == 0)
+ image.setColorCount(1);
+
+ const QImage &cimage = image;
+ memset(pop, 0, sizeof(int)*256); // reset popularity array
+ for (int i = 0; i < h; i++) { // for each scanline...
+ const uchar* p = cimage.scanLine(i);
+ const uchar *end = p + w;
+ while (p < end) // compute popularity
+ pop[*p++]++;
+ }
+
+ newbits = (uchar *)malloc(nbytes); // copy image into newbits
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ uchar* p = newbits;
+ memcpy(p, cimage.bits(), nbytes); // copy image data into newbits
+
+ /*
+ * The code below picks the most important colors. It is based on the
+ * diversity algorithm, implemented in XV 3.10. XV is (C) by John Bradley.
+ */
+
+ struct PIX { // pixel sort element
+ uchar r,g,b,n; // color + pad
+ int use; // popularity
+ int index; // index in colormap
+ int mindist;
+ };
+ int ncols = 0;
+ for (int i=0; i< cimage.colorCount(); i++) { // compute number of colors
+ if (pop[i] > 0)
+ ncols++;
+ }
+ for (int i = cimage.colorCount(); i < 256; i++) // ignore out-of-range pixels
+ pop[i] = 0;
+
+ // works since we make sure above to have at least
+ // one color in the image
+ if (ncols == 0)
+ ncols = 1;
+
+ PIX pixarr[256]; // pixel array
+ PIX pixarr_sorted[256]; // pixel array (sorted)
+ memset(pixarr, 0, ncols*sizeof(PIX));
+ PIX *px = &pixarr[0];
+ int maxpop = 0;
+ int maxpix = 0;
+ uint j = 0;
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i = 0; i < 256; i++) { // init pixel array
+ if (pop[i] > 0) {
+ px->r = qRed (ctable[i]);
+ px->g = qGreen(ctable[i]);
+ px->b = qBlue (ctable[i]);
+ px->n = 0;
+ px->use = pop[i];
+ if (pop[i] > maxpop) { // select most popular entry
+ maxpop = pop[i];
+ maxpix = j;
+ }
+ px->index = i;
+ px->mindist = 1000000;
+ px++;
+ j++;
+ }
+ }
+ pixarr_sorted[0] = pixarr[maxpix];
+ pixarr[maxpix].use = 0;
+
+ for (int i = 1; i < ncols; i++) { // sort pixels
+ int minpix = -1, mindist = -1;
+ px = &pixarr_sorted[i-1];
+ int r = px->r;
+ int g = px->g;
+ int b = px->b;
+ int dist;
+ if ((i & 1) || i<10) { // sort on max distance
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->mindist > mindist) {
+ mindist = px->mindist;
+ minpix = j;
+ }
+ }
+ }
+ } else { // sort on max popularity
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->use > mindist) {
+ mindist = px->use;
+ minpix = j;
+ }
+ }
+ }
+ }
+ pixarr_sorted[i] = pixarr[minpix];
+ pixarr[minpix].use = 0;
+ }
+
+ QXcbColormap cmap = QXcbColormap::instance(xinfo.screen());
+ uint pix[256]; // pixel translation table
+ px = &pixarr_sorted[0];
+ for (int i = 0; i < ncols; i++) { // allocate colors
+ QColor c(px->r, px->g, px->b);
+ pix[px->index] = cmap.pixel(c);
+ px++;
+ }
+
+ p = newbits;
+ for (size_t i = 0; i < nbytes; i++) { // translate pixels
+ *p = pix[*p];
+ p++;
+ }
+ }
+
+ if (!xi) { // X image not created
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ if (xi->bits_per_pixel == 16) { // convert 8 bpp ==> 16 bpp
+ ushort *p2;
+ int p2inc = xi->bytes_per_line/sizeof(ushort);
+ ushort *newerbits = (ushort *)malloc(xi->bytes_per_line * h);
+ Q_CHECK_PTR(newerbits);
+ if (!newerbits) // no memory
+ return;
+ uchar* p = newbits;
+ for (int y = 0; y < h; y++) { // OOPS: Do right byte order!!
+ p2 = newerbits + p2inc*y;
+ for (int x = 0; x < w; x++)
+ *p2++ = *p++;
+ }
+ free(newbits);
+ newbits = (uchar *)newerbits;
+ } else if (xi->bits_per_pixel != 8) {
+ qWarning("QPixmap::fromImage: Display not supported "
+ "(bpp=%d)", xi->bits_per_pixel);
+ }
+ xi->data = (char *)newbits;
+ }
+
+ hd = XCreatePixmap(dpy,
+ RootWindow(dpy, xinfo.screen()),
+ w, h, dd);
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+ d = dd;
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(dpy, PictStandardA1)
+ : XRenderFindVisualFormat(dpy, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(dpy, hd, format, 0, 0);
+ }
+#endif
+
+ if (alphaCheck.hasAlpha()) {
+ QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags));
+ setMask(m);
+ }
+}
+
+void QX11PlatformPixmap::copy(const QPlatformPixmap *data, const QRect &rect)
+{
+ if (data->pixelType() == BitmapType) {
+ fromImage(data->toImage().copy(rect), Qt::AutoColor);
+ return;
+ }
+
+ const QX11PlatformPixmap *x11Data = static_cast<const QX11PlatformPixmap*>(data);
+
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ flags &= ~Uninitialized;
+ xinfo = x11Data->xinfo;
+ d = x11Data->d;
+ w = rect.width();
+ h = rect.height();
+ is_null = (w <= 0 || h <= 0);
+ hd = XCreatePixmap(xinfo.display(),
+ RootWindow(xinfo.display(), x11Data->xinfo.screen()),
+ w, h, d);
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 32
+ ? XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32)
+ : XRenderFindVisualFormat(xinfo.display(), (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(xinfo.display(), hd, format, 0, 0);
+ }
+#endif // QT_CONFIG(xrender)
+ if (x11Data->x11_mask) {
+ x11_mask = XCreatePixmap(xinfo.display(), hd, w, h, 1);
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ mask_picture = XRenderCreatePicture(xinfo.display(), x11_mask,
+ XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = x11Data->mask_picture;
+ XRenderChangePicture(xinfo.display(), x11Data->picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+
+#if QT_CONFIG(xrender)
+ if (x11Data->picture && x11Data->d == 32) {
+ XRenderComposite(xinfo.display(), PictOpSrc,
+ x11Data->picture, 0, picture,
+ rect.x(), rect.y(), 0, 0, 0, 0, w, h);
+ } else
+#endif
+ {
+ GC gc = XCreateGC(xinfo.display(), hd, 0, 0);
+ XCopyArea(xinfo.display(), x11Data->hd, hd, gc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ if (x11Data->x11_mask) {
+ GC monogc = XCreateGC(xinfo.display(), x11_mask, 0, 0);
+ XCopyArea(xinfo.display(), x11Data->x11_mask, x11_mask, monogc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ XFreeGC(xinfo.display(), monogc);
+ }
+ XFreeGC(xinfo.display(), gc);
+ }
+}
+
+bool QX11PlatformPixmap::scroll(int dx, int dy, const QRect &rect)
+{
+ GC gc = XCreateGC(xinfo.display(), hd, 0, 0);
+ XCopyArea(xinfo.display(), hd, hd, gc,
+ rect.left(), rect.top(), rect.width(), rect.height(),
+ rect.left() + dx, rect.top() + dy);
+ XFreeGC(xinfo.display(), gc);
+ return true;
+}
+
+int QX11PlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch (metric) {
+ case QPaintDevice::PdmDevicePixelRatio:
+ return devicePixelRatio();
+ break;
+ case QPaintDevice::PdmDevicePixelRatioScaled:
+ return devicePixelRatio() * QPaintDevice::devicePixelRatioFScale();
+ break;
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmNumColors:
+ return 1 << d;
+ case QPaintDevice::PdmDepth:
+ return d;
+ case QPaintDevice::PdmWidthMM: {
+ const int screen = xinfo.screen();
+ const int mm = DisplayWidthMM(xinfo.display(), screen) * w
+ / DisplayWidth(xinfo.display(), screen);
+ return mm;
+ }
+ case QPaintDevice::PdmHeightMM: {
+ const int screen = xinfo.screen();
+ const int mm = (DisplayHeightMM(xinfo.display(), screen) * h)
+ / DisplayHeight(xinfo.display(), screen);
+ return mm;
+ }
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return QXcbX11Info::appDpiX(xinfo.screen());
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return QXcbX11Info::appDpiY(xinfo.screen());
+ default:
+ qWarning("QX11PlatformPixmap::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+void QX11PlatformPixmap::fill(const QColor &fillColor)
+{
+ if (fillColor.alpha() != 255) {
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ if (!picture || d != 32)
+ convertToARGB32(/*preserveContents = */false);
+
+ ::Picture src = X11->getSolidFill(xinfo.screen(), fillColor);
+ XRenderComposite(xinfo.display(), PictOpSrc, src, 0, picture,
+ 0, 0, width(), height(),
+ 0, 0, width(), height());
+ } else
+#endif
+ {
+ QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied);
+ im.fill(PREMUL(fillColor.rgba()));
+ release();
+ fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither);
+ }
+ return;
+ }
+
+ GC gc = XCreateGC(xinfo.display(), hd, 0, 0);
+ if (depth() == 1) {
+ XSetForeground(xinfo.display(), gc, qGray(fillColor.rgb()) > 127 ? 0 : 1);
+ } else if (X11->use_xrender && d >= 24) {
+ XSetForeground(xinfo.display(), gc, fillColor.rgba());
+ } else {
+ XSetForeground(xinfo.display(), gc,
+ QXcbColormap::instance(xinfo.screen()).pixel(fillColor));
+ }
+ XFillRectangle(xinfo.display(), hd, gc, 0, 0, width(), height());
+ XFreeGC(xinfo.display(), gc);
+}
+
+QBitmap QX11PlatformPixmap::mask() const
+{
+ QBitmap mask;
+#if QT_CONFIG(xrender)
+ if (picture && d == 32) {
+ // #### slow - there must be a better way..
+ mask = QBitmap::fromImage(toImage().createAlphaMask());
+ } else
+#endif
+ if (d == 1) {
+ QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this);
+ mask = QPixmap(that);
+ } else {
+ mask = mask_to_bitmap(xinfo.screen());
+ }
+ return mask;
+}
+
+void QX11PlatformPixmap::setMask(const QBitmap &newmask)
+{
+ if (newmask.isNull()) { // clear mask
+#if QT_CONFIG(xrender)
+ if (picture && d == 32) {
+ QX11PlatformPixmap newData(pixelType());
+ newData.resize(w, h);
+ newData.fill(Qt::black);
+ XRenderComposite(xinfo.display(), PictOpOver,
+ picture, 0, newData.picture,
+ 0, 0, 0, 0, 0, 0, w, h);
+ release();
+ *this = newData;
+ // the new QX11PlatformPixmap object isn't referenced yet, so
+ // ref it
+ ref.ref();
+
+ // the below is to make sure the QX11PlatformPixmap destructor
+ // doesn't delete our newly created render picture
+ newData.hd = 0;
+ newData.x11_mask = 0;
+ newData.picture = 0;
+ newData.mask_picture = 0;
+ newData.hd2 = 0;
+ } else
+#endif
+ if (x11_mask) {
+#if QT_CONFIG(xrender)
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = 0;
+ XRenderChangePicture(xinfo.display(), picture, CPAlphaMap,
+ &attrs);
+ }
+ if (mask_picture)
+ XRenderFreePicture(xinfo.display(), mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(xinfo.display(), x11_mask);
+ x11_mask = 0;
+ }
+ return;
+ }
+
+#if QT_CONFIG(xrender)
+ if (picture && d == 32) {
+ XRenderComposite(xinfo.display(), PictOpSrc,
+ picture, qt_x11Pixmap(newmask)->x11PictureHandle(),
+ picture, 0, 0, 0, 0, 0, 0, w, h);
+ } else
+#endif
+ if (depth() == 1) {
+ XGCValues vals;
+ vals.function = GXand;
+ GC gc = XCreateGC(xinfo.display(), hd, GCFunction, &vals);
+ XCopyArea(xinfo.display(), qt_x11Pixmap(newmask)->handle(), hd, gc, 0, 0,
+ width(), height(), 0, 0);
+ XFreeGC(xinfo.display(), gc);
+ } else {
+ // ##### should or the masks together
+ if (x11_mask) {
+ XFreePixmap(xinfo.display(), x11_mask);
+#if QT_CONFIG(xrender)
+ if (mask_picture)
+ XRenderFreePicture(xinfo.display(), mask_picture);
+#endif
+ }
+ x11_mask = QX11PlatformPixmap::bitmap_to_mask(newmask, xinfo.screen());
+#if QT_CONFIG(xrender)
+ if (picture) {
+ mask_picture = XRenderCreatePicture(xinfo.display(), x11_mask,
+ XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = mask_picture;
+ XRenderChangePicture(xinfo.display(), picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+}
+
+bool QX11PlatformPixmap::hasAlphaChannel() const
+{
+ if (picture && d == 32)
+ return true;
+
+ if (x11_mask && d == 1)
+ return true;
+
+ return false;
+}
+
+QPixmap QX11PlatformPixmap::transformed(const QTransform &transform, Qt::TransformationMode mode) const
+{
+ if (mode == Qt::SmoothTransformation || transform.type() >= QTransform::TxProject) {
+ QImage image = toImage();
+ return QPixmap::fromImage(image.transformed(transform, mode));
+ }
+
+ uint w = 0;
+ uint h = 0; // size of target pixmap
+ uint ws, hs; // size of source pixmap
+ uchar *dptr; // data in target pixmap
+ uint dbpl, dbytes; // bytes per line/bytes total
+ uchar *sptr; // data in original pixmap
+ int sbpl; // bytes per line in original
+ int bpp; // bits per pixel
+ bool depth1 = depth() == 1;
+ Display *dpy = xinfo.display();
+
+ ws = width();
+ hs = height();
+
+ QTransform mat(transform.m11(), transform.m12(), transform.m13(),
+ transform.m21(), transform.m22(), transform.m23(),
+ 0., 0., 1);
+ bool complex_xform = false;
+ qreal scaledWidth;
+ qreal scaledHeight;
+
+ if (mat.type() <= QTransform::TxScale) {
+ scaledHeight = qAbs(mat.m22()) * hs + 0.9999;
+ scaledWidth = qAbs(mat.m11()) * ws + 0.9999;
+ h = qAbs(int(scaledHeight));
+ w = qAbs(int(scaledWidth));
+ } else { // rotation or shearing
+ QPolygonF a(QRectF(0, 0, ws, hs));
+ a = mat.map(a);
+ QRect r = a.boundingRect().toAlignedRect();
+ w = r.width();
+ h = r.height();
+ scaledWidth = w;
+ scaledHeight = h;
+ complex_xform = true;
+ }
+ mat = QPixmap::trueMatrix(mat, ws, hs); // true matrix
+
+ bool invertible;
+ mat = mat.inverted(&invertible); // invert matrix
+
+ if (h == 0 || w == 0 || !invertible
+ || qAbs(scaledWidth) >= 32768 || qAbs(scaledHeight) >= 32768 )
+ // error, return null pixmap
+ return QPixmap();
+
+ XImage *xi = XGetImage(xinfo.display(), handle(), 0, 0, ws, hs, AllPlanes,
+ depth1 ? XYPixmap : ZPixmap);
+
+ if (!xi)
+ return QPixmap();
+
+ sbpl = xi->bytes_per_line;
+ sptr = (uchar *)xi->data;
+ bpp = xi->bits_per_pixel;
+
+ if (depth1)
+ dbpl = (w+7)/8;
+ else
+ dbpl = ((w*bpp+31)/32)*4;
+ dbytes = dbpl*h;
+
+ dptr = (uchar *)malloc(dbytes); // create buffer for bits
+ Q_CHECK_PTR(dptr);
+ if (depth1) // fill with zeros
+ memset(dptr, 0, dbytes);
+ else if (bpp == 8) // fill with background color
+ memset(dptr, WhitePixel(xinfo.display(), xinfo.screen()), dbytes);
+ else
+ memset(dptr, 0, dbytes);
+
+ // #define QT_DEBUG_XIMAGE
+#if defined(QT_DEBUG_XIMAGE)
+ qDebug("----IMAGE--INFO--------------");
+ qDebug("width............. %d", xi->width);
+ qDebug("height............ %d", xi->height);
+ qDebug("xoffset........... %d", xi->xoffset);
+ qDebug("format............ %d", xi->format);
+ qDebug("byte order........ %d", xi->byte_order);
+ qDebug("bitmap unit....... %d", xi->bitmap_unit);
+ qDebug("bitmap bit order.. %d", xi->bitmap_bit_order);
+ qDebug("depth............. %d", xi->depth);
+ qDebug("bytes per line.... %d", xi->bytes_per_line);
+ qDebug("bits per pixel.... %d", xi->bits_per_pixel);
+#endif
+
+ int type;
+ if (xi->bitmap_bit_order == MSBFirst)
+ type = QT_XFORM_TYPE_MSBFIRST;
+ else
+ type = QT_XFORM_TYPE_LSBFIRST;
+ int xbpl, p_inc;
+ if (depth1) {
+ xbpl = (w+7)/8;
+ p_inc = dbpl - xbpl;
+ } else {
+ xbpl = (w*bpp)/8;
+ p_inc = dbpl - xbpl;
+ }
+
+ if (!qt_xForm_helper(mat, xi->xoffset, type, bpp, dptr, xbpl, p_inc, h, sptr, sbpl, ws, hs)){
+ qWarning("QPixmap::transform: display not supported (bpp=%d)",bpp);
+ QPixmap pm;
+ free(dptr);
+ return pm;
+ }
+
+ qSafeXDestroyImage(xi);
+
+ if (depth1) { // mono bitmap
+ QBitmap bm = QBitmap::fromData(QSize(w, h), dptr,
+ BitmapBitOrder(xinfo.display()) == MSBFirst
+ ? QImage::Format_Mono
+ : QImage::Format_MonoLSB);
+ free(dptr);
+ return bm;
+ } else { // color pixmap
+ QX11PlatformPixmap *x11Data = new QX11PlatformPixmap(QPlatformPixmap::PixmapType);
+ QPixmap pm(x11Data);
+ x11Data->flags &= ~QX11PlatformPixmap::Uninitialized;
+ x11Data->xinfo = xinfo;
+ x11Data->d = d;
+ x11Data->w = w;
+ x11Data->h = h;
+ x11Data->is_null = (w <= 0 || h <= 0);
+ x11Data->hd = XCreatePixmap(xinfo.display(),
+ RootWindow(xinfo.display(), xinfo.screen()),
+ w, h, d);
+ x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = x11Data->d == 32
+ ? XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32)
+ : XRenderFindVisualFormat(xinfo.display(), (Visual *) x11Data->xinfo.visual());
+ x11Data->picture = XRenderCreatePicture(xinfo.display(), x11Data->hd, format, 0, 0);
+ }
+#endif // QT_CONFIG(xrender)
+
+ GC gc = XCreateGC(xinfo.display(), x11Data->hd, 0, 0);
+ xi = XCreateImage(dpy, (Visual*)x11Data->xinfo.visual(),
+ x11Data->d,
+ ZPixmap, 0, (char *)dptr, w, h, 32, 0);
+ XPutImage(dpy, qt_x11Pixmap(pm)->handle(), gc, xi, 0, 0, 0, 0, w, h);
+ qSafeXDestroyImage(xi);
+ XFreeGC(xinfo.display(), gc);
+
+ if (x11_mask) { // xform mask, too
+ pm.setMask(mask_to_bitmap(xinfo.screen()).transformed(transform));
+ } else if (d != 32 && complex_xform) { // need a mask!
+ QBitmap mask(ws, hs);
+ mask.fill(Qt::color1);
+ pm.setMask(mask.transformed(transform));
+ }
+ return pm;
+ }
+}
+
+QImage QX11PlatformPixmap::toImage() const
+{
+ return toImage(QRect(0, 0, w, h));
+}
+
+QImage QX11PlatformPixmap::toImage(const QRect &rect) const
+{
+ Window root_return;
+ int x_return;
+ int y_return;
+ unsigned int width_return;
+ unsigned int height_return;
+ unsigned int border_width_return;
+ unsigned int depth_return;
+
+ XGetGeometry(xinfo.display(), hd, &root_return, &x_return, &y_return, &width_return, &height_return, &border_width_return, &depth_return);
+
+ QXImageWrapper xiWrapper;
+ xiWrapper.xi = XGetImage(xinfo.display(), hd, rect.x(), rect.y(), rect.width(), rect.height(),
+ AllPlanes, (depth() == 1) ? XYPixmap : ZPixmap);
+
+ Q_CHECK_PTR(xiWrapper.xi);
+ if (!xiWrapper.xi)
+ return QImage();
+
+ if (!x11_mask && canTakeQImageFromXImage(xiWrapper))
+ return takeQImageFromXImage(xiWrapper);
+
+ QImage image = toImage(xiWrapper, rect);
+ qSafeXDestroyImage(xiWrapper.xi);
+ return image;
+}
+
+#if QT_CONFIG(xrender)
+static XRenderPictFormat *qt_renderformat_for_depth(const QXcbX11Info &xinfo, int depth)
+{
+ if (depth == 1)
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardA1);
+ else if (depth == 32)
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32);
+ else
+ return XRenderFindVisualFormat(xinfo.display(), (Visual *)xinfo.visual());
+}
+#endif
+
+Q_GLOBAL_STATIC(QX11PaintEngine, qt_x11_paintengine)
+
+QPaintEngine *QX11PlatformPixmap::paintEngine() const
+{
+ QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this);
+
+ if ((flags & Readonly)/* && share_mode == QPixmap::ImplicitlyShared*/) {
+ // if someone wants to draw onto us, copy the shared contents
+ // and turn it into a fully fledged QPixmap
+ ::Pixmap hd_copy = XCreatePixmap(xinfo.display(), RootWindow(xinfo.display(), xinfo.screen()),
+ w, h, d);
+#if QT_CONFIG(xrender)
+ if (picture && d == 32) {
+ XRenderPictFormat *format = qt_renderformat_for_depth(xinfo, d);
+ ::Picture picture_copy = XRenderCreatePicture(xinfo.display(),
+ hd_copy, format,
+ 0, 0);
+
+ XRenderComposite(xinfo.display(), PictOpSrc, picture, 0, picture_copy,
+ 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(xinfo.display(), picture);
+ that->picture = picture_copy;
+ } else
+#endif
+ {
+ GC gc = XCreateGC(xinfo.display(), hd_copy, 0, 0);
+ XCopyArea(xinfo.display(), hd, hd_copy, gc, 0, 0, w, h, 0, 0);
+ XFreeGC(xinfo.display(), gc);
+ }
+ that->hd = hd_copy;
+ that->flags &= ~QX11PlatformPixmap::Readonly;
+ }
+
+ if (qt_x11_paintengine->isActive()) {
+ if (!that->pengine)
+ that->pengine = new QX11PaintEngine;
+
+ return that->pengine;
+ }
+
+ return qt_x11_paintengine();
+}
+
+qreal QX11PlatformPixmap::devicePixelRatio() const
+{
+ return dpr;
+}
+
+void QX11PlatformPixmap::setDevicePixelRatio(qreal scaleFactor)
+{
+ dpr = scaleFactor;
+}
+
+Pixmap QX11PlatformPixmap::x11ConvertToDefaultDepth()
+{
+#if QT_CONFIG(xrender)
+ if (d == xinfo.appDepth() || !X11->use_xrender)
+ return hd;
+ if (!hd2) {
+ hd2 = XCreatePixmap(xinfo.display(), hd, w, h, xinfo.appDepth());
+ XRenderPictFormat *format = XRenderFindVisualFormat(xinfo.display(),
+ (Visual*) xinfo.visual());
+ Picture pic = XRenderCreatePicture(xinfo.display(), hd2, format, 0, 0);
+ XRenderComposite(xinfo.display(), PictOpSrc, picture,
+ XNone, pic, 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(xinfo.display(), pic);
+ }
+ return hd2;
+#else
+ return hd;
+#endif
+}
+
+XID QX11PlatformPixmap::createBitmapFromImage(const QImage &image)
+{
+ QImage img = image.convertToFormat(QImage::Format_MonoLSB);
+ const QRgb c0 = QColor(Qt::black).rgb();
+ const QRgb c1 = QColor(Qt::white).rgb();
+ if (img.color(0) == c0 && img.color(1) == c1) {
+ img.invertPixels();
+ img.setColor(0, c1);
+ img.setColor(1, c0);
+ }
+
+ char *bits;
+ uchar *tmp_bits;
+ int w = img.width();
+ int h = img.height();
+ int bpl = (w + 7) / 8;
+ int ibpl = img.bytesPerLine();
+ if (bpl != ibpl) {
+ tmp_bits = new uchar[bpl*h];
+ bits = (char *)tmp_bits;
+ uchar *p, *b;
+ int y;
+ b = tmp_bits;
+ p = img.scanLine(0);
+ for (y = 0; y < h; y++) {
+ memcpy(b, p, bpl);
+ b += bpl;
+ p += ibpl;
+ }
+ } else {
+ bits = (char *)img.bits();
+ tmp_bits = 0;
+ }
+ XID hd = XCreateBitmapFromData(QXcbX11Info::display(),
+ QXcbX11Info::appRootWindow(),
+ bits, w, h);
+ if (tmp_bits) // Avoid purify complaint
+ delete [] tmp_bits;
+ return hd;
+}
+
+#if QT_CONFIG(xrender)
+void QX11PlatformPixmap::convertToARGB32(bool preserveContents)
+{
+ if (!X11->use_xrender)
+ return;
+
+ // Q_ASSERT(count == 1);
+ if ((flags & Readonly)/* && share_mode == QPixmap::ExplicitlyShared*/)
+ return;
+
+ Pixmap pm = XCreatePixmap(xinfo.display(), RootWindow(xinfo.display(), xinfo.screen()),
+ w, h, 32);
+ Picture p = XRenderCreatePicture(xinfo.display(), pm,
+ XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32), 0, 0);
+ if (picture) {
+ if (preserveContents)
+ XRenderComposite(xinfo.display(), PictOpSrc, picture, 0, p, 0, 0, 0, 0, 0, 0, w, h);
+ if (!(flags & Readonly))
+ XRenderFreePicture(xinfo.display(), picture);
+ }
+ if (hd && !(flags & Readonly))
+ XFreePixmap(xinfo.display(), hd);
+ if (x11_mask) {
+ XFreePixmap(xinfo.display(), x11_mask);
+ if (mask_picture)
+ XRenderFreePicture(xinfo.display(), mask_picture);
+ x11_mask = 0;
+ mask_picture = 0;
+ }
+ hd = pm;
+ picture = p;
+
+ d = 32;
+ xinfo.setDepth(32);
+
+ XVisualInfo visinfo;
+ if (XMatchVisualInfo(xinfo.display(), xinfo.screen(), 32, TrueColor, &visinfo))
+ xinfo.setVisual(visinfo.visual);
+}
+#endif
+
+void QX11PlatformPixmap::release()
+{
+ delete pengine;
+ pengine = 0;
+
+ if (/*!X11*/ QCoreApplication::closingDown()) {
+ // At this point, the X server will already have freed our resources,
+ // so there is nothing to do.
+ return;
+ }
+
+ if (x11_mask) {
+#if QT_CONFIG(xrender)
+ if (mask_picture)
+ XRenderFreePicture(xinfo.display(), mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(xinfo.display(), x11_mask);
+ x11_mask = 0;
+ }
+
+ if (hd) {
+#if QT_CONFIG(xrender)
+ if (picture) {
+ XRenderFreePicture(xinfo.display(), picture);
+ picture = 0;
+ }
+#endif // QT_CONFIG(xrender)
+
+ if (hd2) {
+ XFreePixmap(xinfo.display(), hd2);
+ hd2 = 0;
+ }
+ if (!(flags & Readonly))
+ XFreePixmap(xinfo.display(), hd);
+ hd = 0;
+ }
+}
+
+QImage QX11PlatformPixmap::toImage(const QXImageWrapper &xiWrapper, const QRect &rect) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ int d = depth();
+ Visual *visual = (Visual *)xinfo.visual();
+ bool trucol = (visual->c_class >= TrueColor) && d > 1;
+
+ QImage::Format format = QImage::Format_Mono;
+ if (d > 1 && d <= 8) {
+ d = 8;
+ format = QImage::Format_Indexed8;
+ }
+ // we could run into the situation where d == 8 AND trucol is true, which can
+ // cause problems when converting to and from images. in this case, always treat
+ // the depth as 32...
+ if (d > 8 || trucol) {
+ d = 32;
+ format = QImage::Format_RGB32;
+ }
+
+ if (d == 1 && xi->bitmap_bit_order == LSBFirst)
+ format = QImage::Format_MonoLSB;
+ if (x11_mask && format == QImage::Format_RGB32)
+ format = QImage::Format_ARGB32;
+
+ QImage image(xi->width, xi->height, format);
+ image.setDevicePixelRatio(devicePixelRatio());
+ if (image.isNull()) // could not create image
+ return image;
+
+ QImage alpha;
+ if (x11_mask) {
+ if (rect.contains(QRect(0, 0, w, h)))
+ alpha = mask().toImage();
+ else
+ alpha = mask().toImage().copy(rect);
+ }
+ bool ale = alpha.format() == QImage::Format_MonoLSB;
+
+ if (trucol) { // truecolor
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+
+ const uint red_bits = n_bits(red_mask);
+ const uint green_bits = n_bits(green_mask);
+ const uint blue_bits = n_bits(blue_mask);
+
+ static uint red_table_bits = 0;
+ static uint green_table_bits = 0;
+ static uint blue_table_bits = 0;
+
+ if (red_bits < 8 && red_table_bits != red_bits) {
+ build_scale_table(&red_scale_table, red_bits);
+ red_table_bits = red_bits;
+ }
+ if (blue_bits < 8 && blue_table_bits != blue_bits) {
+ build_scale_table(&blue_scale_table, blue_bits);
+ blue_table_bits = blue_bits;
+ }
+ if (green_bits < 8 && green_table_bits != green_bits) {
+ build_scale_table(&green_scale_table, green_bits);
+ green_table_bits = green_bits;
+ }
+
+ int r, g, b;
+
+ QRgb *dst;
+ uchar *src;
+ uint pixel;
+ int bppc = xi->bits_per_pixel;
+
+ if (bppc > 8 && xi->byte_order == LSBFirst)
+ bppc++;
+
+ for (int y = 0; y < xi->height; ++y) {
+ uchar* asrc = x11_mask ? alpha.scanLine(y) : 0;
+ dst = (QRgb *)image.scanLine(y);
+ src = (uchar *)xi->data + xi->bytes_per_line*y;
+ for (int x = 0; x < xi->width; x++) {
+ switch (bppc) {
+ case 8:
+ pixel = *src++;
+ break;
+ case 16: // 16 bit MSB
+ pixel = src[1] | (uint)src[0] << 8;
+ src += 2;
+ break;
+ case 17: // 16 bit LSB
+ pixel = src[0] | (uint)src[1] << 8;
+ src += 2;
+ break;
+ case 24: // 24 bit MSB
+ pixel = src[2] | (uint)src[1] << 8 | (uint)src[0] << 16;
+ src += 3;
+ break;
+ case 25: // 24 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16;
+ src += 3;
+ break;
+ case 32: // 32 bit MSB
+ pixel = src[3] | (uint)src[2] << 8 | (uint)src[1] << 16 | (uint)src[0] << 24;
+ src += 4;
+ break;
+ case 33: // 32 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16 | (uint)src[3] << 24;
+ src += 4;
+ break;
+ default: // should not really happen
+ x = xi->width; // leave loop
+ y = xi->height;
+ pixel = 0; // eliminate compiler warning
+ qWarning("QPixmap::convertToImage: Invalid depth %d", bppc);
+ }
+ if (red_shift > 0)
+ r = (pixel & red_mask) >> red_shift;
+ else
+ r = (pixel & red_mask) << -red_shift;
+ if (green_shift > 0)
+ g = (pixel & green_mask) >> green_shift;
+ else
+ g = (pixel & green_mask) << -green_shift;
+ if (blue_shift > 0)
+ b = (pixel & blue_mask) >> blue_shift;
+ else
+ b = (pixel & blue_mask) << -blue_shift;
+
+ if (red_bits < 8)
+ r = red_scale_table[r];
+ if (green_bits < 8)
+ g = green_scale_table[g];
+ if (blue_bits < 8)
+ b = blue_scale_table[b];
+
+ if (x11_mask) {
+ if (ale) {
+ *dst++ = (asrc[x >> 3] & (1 << (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ } else {
+ *dst++ = (asrc[x >> 3] & (0x80 >> (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ }
+ } else {
+ *dst++ = qRgb(r, g, b);
+ }
+ }
+ }
+ } else if (xi->bits_per_pixel == d) { // compatible depth
+ char *xidata = xi->data; // copy each scanline
+ int bpl = qMin(image.bytesPerLine(),xi->bytes_per_line);
+ for (int y=0; y<xi->height; y++) {
+ memcpy(image.scanLine(y), xidata, bpl);
+ xidata += xi->bytes_per_line;
+ }
+ } else {
+ /* Typically 2 or 4 bits display depth */
+ qWarning("QPixmap::convertToImage: Display not supported (bpp=%d)",
+ xi->bits_per_pixel);
+ return QImage();
+ }
+
+ if (d == 1) { // bitmap
+ image.setColorCount(2);
+ image.setColor(0, qRgb(255,255,255));
+ image.setColor(1, qRgb(0,0,0));
+ } else if (!trucol) { // pixmap with colormap
+ uchar *p;
+ uchar *end;
+ uchar use[256]; // pixel-in-use table
+ uchar pix[256]; // pixel translation table
+ int ncols, bpl;
+ memset(use, 0, 256);
+ memset(pix, 0, 256);
+ bpl = image.bytesPerLine();
+
+ if (x11_mask) { // which pixels are used?
+ for (int i = 0; i < xi->height; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < xi->width; x++) {
+ if (asrc[x >> 3] & (1 << (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < xi->width; x++) {
+ if (asrc[x >> 3] & (0x80 >> (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < xi->height; i++) {
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end)
+ use[*p++] = 1;
+ }
+ }
+ ncols = 0;
+ for (int i = 0; i < 256; i++) { // build translation table
+ if (use[i])
+ pix[i] = ncols++;
+ }
+ for (int i = 0; i < xi->height; i++) { // translate pixels
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end) {
+ *p = pix[*p];
+ p++;
+ }
+ }
+ if (x11_mask) {
+ int trans;
+ if (ncols < 256) {
+ trans = ncols++;
+ image.setColorCount(ncols); // create color table
+ image.setColor(trans, 0x00000000);
+ } else {
+ image.setColorCount(ncols); // create color table
+ // oh dear... no spare "transparent" pixel.
+ // use first pixel in image (as good as any).
+ trans = image.scanLine(0)[0];
+ }
+ for (int i = 0; i < xi->height; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < xi->width; x++) {
+ if (!(asrc[x >> 3] & (1 << (x & 7))))
+ *p = trans;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < xi->width; x++) {
+ if (!(asrc[x >> 3] & (1 << (7 -(x & 7)))))
+ *p = trans;
+ ++p;
+ }
+ }
+ }
+ } else {
+ image.setColorCount(ncols); // create color table
+ }
+ QVector<QColor> colors = QXcbColormap::instance(xinfo.screen()).colormap();
+ int j = 0;
+ for (int i=0; i<colors.size(); i++) { // translate pixels
+ if (use[i])
+ image.setColor(j++, 0xff000000 | colors.at(i).rgb());
+ }
+ }
+
+ return image;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h
new file mode 100644
index 0000000000..2cbd1fe3d0
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QX11PLATFORMPIXMAP_H
+#define QX11PLATFORMPIXMAP_H
+
+#include <QBitmap>
+#include <QPixmap>
+
+#include <qpa/qplatformpixmap.h>
+#include "qxcbnativepainting.h"
+
+typedef unsigned long XID;
+typedef XID Drawable;
+typedef XID Picture;
+typedef XID Pixmap;
+
+QT_BEGIN_NAMESPACE
+
+class QX11PaintEngine;
+struct QXImageWrapper;
+
+class QX11PlatformPixmap : public QPlatformPixmap
+{
+public:
+ QX11PlatformPixmap(PixelType pixelType);
+ ~QX11PlatformPixmap();
+
+ QPlatformPixmap *createCompatiblePlatformPixmap() const Q_DECL_OVERRIDE;
+ void resize(int width, int height) Q_DECL_OVERRIDE;
+ void fromImage(const QImage &img, Qt::ImageConversionFlags flags) Q_DECL_OVERRIDE;
+ void copy(const QPlatformPixmap *data, const QRect &rect) Q_DECL_OVERRIDE;
+ bool scroll(int dx, int dy, const QRect &rect) Q_DECL_OVERRIDE;
+ int metric(QPaintDevice::PaintDeviceMetric metric) const Q_DECL_OVERRIDE;
+ void fill(const QColor &fillColor) Q_DECL_OVERRIDE;
+ QBitmap mask() const Q_DECL_OVERRIDE;
+ void setMask(const QBitmap &mask) Q_DECL_OVERRIDE;
+ bool hasAlphaChannel() const Q_DECL_OVERRIDE;
+ QPixmap transformed(const QTransform &matrix, Qt::TransformationMode mode) const Q_DECL_OVERRIDE;
+ QImage toImage() const Q_DECL_OVERRIDE;
+ QImage toImage(const QRect &rect) const Q_DECL_OVERRIDE;
+ QPaintEngine *paintEngine() const Q_DECL_OVERRIDE;
+ qreal devicePixelRatio() const Q_DECL_OVERRIDE;
+ void setDevicePixelRatio(qreal scaleFactor) Q_DECL_OVERRIDE;
+
+ inline Drawable handle() const { return hd; }
+ inline Picture x11PictureHandle() const { return picture; }
+ inline const QXcbX11Info *x11_info() const { return &xinfo; }
+
+ Pixmap x11ConvertToDefaultDepth();
+ static XID createBitmapFromImage(const QImage &image);
+
+#if QT_CONFIG(xrender)
+ void convertToARGB32(bool preserveContents = true);
+#endif
+
+private:
+ friend class QX11PaintEngine;
+ friend const QXcbX11Info &qt_x11Info(const QPixmap &pixmap);
+ friend void qt_x11SetScreen(QPixmap &pixmap, int screen);
+
+ void release();
+ QImage toImage(const QXImageWrapper &xi, const QRect &rect) const;
+ QBitmap mask_to_bitmap(int screen) const;
+ static Pixmap bitmap_to_mask(const QBitmap &, int screen);
+ void bitmapFromImage(const QImage &image);
+ bool canTakeQImageFromXImage(const QXImageWrapper &xi) const;
+ QImage takeQImageFromXImage(const QXImageWrapper &xi) const;
+
+ Pixmap hd = 0;
+
+ enum Flag {
+ NoFlags = 0x0,
+ Uninitialized = 0x1,
+ Readonly = 0x2,
+ InvertedWhenBoundToTexture = 0x4,
+ GlSurfaceCreatedWithAlpha = 0x8
+ };
+ uint flags;
+
+ QXcbX11Info xinfo;
+ Pixmap x11_mask;
+ Picture picture;
+ Picture mask_picture;
+ Pixmap hd2; // sorted in the default display depth
+ //QPixmap::ShareMode share_mode;
+ qreal dpr;
+
+ QX11PaintEngine *pengine;
+};
+
+inline QX11PlatformPixmap *qt_x11Pixmap(const QPixmap &pixmap)
+{
+ return (pixmap.handle() && pixmap.handle()->classId() == QPlatformPixmap::X11Class)
+ ? static_cast<QX11PlatformPixmap *>(pixmap.handle())
+ : Q_NULLPTR;
+}
+
+inline Picture qt_x11PictureHandle(const QPixmap &pixmap)
+{
+ if (QX11PlatformPixmap *pm = qt_x11Pixmap(pixmap))
+ return pm->x11PictureHandle();
+
+ return 0;
+}
+
+inline Pixmap qt_x11PixmapHandle(const QPixmap &pixmap)
+{
+ if (QX11PlatformPixmap *pm = qt_x11Pixmap(pixmap))
+ return pm->handle();
+
+ return 0;
+}
+
+inline const QXcbX11Info &qt_x11Info(const QPixmap &pixmap)
+{
+ if (QX11PlatformPixmap *pm = qt_x11Pixmap(pixmap)) {
+ return pm->xinfo;
+ } else {
+ static QXcbX11Info nullX11Info;
+ return nullX11Info;
+ }
+}
+
+int qt_x11SetDefaultScreen(int screen);
+void qt_x11SetScreen(QPixmap &pixmap, int screen);
+
+QT_END_NAMESPACE
+
+#endif // QX11PLATFORMPIXMAP_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h
new file mode 100644
index 0000000000..a0e5131ea7
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_X11_P_H
+#define QT_X11_P_H
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#if QT_CONFIG(xrender)
+# include "qtessellator_p.h"
+# include <X11/extensions/Xrender.h>
+#endif
+
+#if QT_CONFIG(fontconfig)
+#include <fontconfig/fontconfig.h>
+#endif
+
+#if defined(FT_LCD_FILTER_H)
+#include FT_LCD_FILTER_H
+#endif
+
+#if defined(FC_LCD_FILTER)
+
+#ifndef FC_LCD_FILTER_NONE
+#define FC_LCD_FILTER_NONE FC_LCD_NONE
+#endif
+
+#ifndef FC_LCD_FILTER_DEFAULT
+#define FC_LCD_FILTER_DEFAULT FC_LCD_DEFAULT
+#endif
+
+#ifndef FC_LCD_FILTER_LIGHT
+#define FC_LCD_FILTER_LIGHT FC_LCD_LIGHT
+#endif
+
+#ifndef FC_LCD_FILTER_LEGACY
+#define FC_LCD_FILTER_LEGACY FC_LCD_LEGACY
+#endif
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// rename a couple of X defines to get rid of name clashes
+// resolve the conflict between X11's FocusIn and QEvent::FocusIn
+enum {
+ XFocusOut = FocusOut,
+ XFocusIn = FocusIn,
+ XKeyPress = KeyPress,
+ XKeyRelease = KeyRelease,
+ XNone = None,
+ XRevertToParent = RevertToParent,
+ XGrayScale = GrayScale,
+ XCursorShape = CursorShape,
+};
+#undef FocusOut
+#undef FocusIn
+#undef KeyPress
+#undef KeyRelease
+#undef None
+#undef RevertToParent
+#undef GrayScale
+#undef CursorShape
+
+#ifdef FontChange
+#undef FontChange
+#endif
+
+Q_DECLARE_TYPEINFO(XPoint, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(XRectangle, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(XChar2b, Q_PRIMITIVE_TYPE);
+#if QT_CONFIG(xrender)
+Q_DECLARE_TYPEINFO(XGlyphElt32, Q_PRIMITIVE_TYPE);
+#endif
+
+struct QX11InfoData;
+
+enum DesktopEnvironment {
+ DE_UNKNOWN,
+ DE_KDE,
+ DE_GNOME,
+ DE_CDE,
+ DE_MEEGO_COMPOSITOR,
+ DE_4DWM
+};
+
+struct QXcbX11Data {
+ Display *display = nullptr;
+
+ // true if Qt is compiled w/ RENDER support and RENDER is supported on the connected Display
+ bool use_xrender = false;
+ int xrender_major = 0;
+ int xrender_version = 0;
+
+ QX11InfoData *screens = nullptr;
+ Visual **argbVisuals = nullptr;
+ Colormap *argbColormaps = nullptr;
+ int screenCount = 0;
+ int defaultScreen = 0;
+
+ // options
+ int visual_class = 0;
+ int visual_id = 0;
+ int color_count = 0;
+ bool custom_cmap = false;
+
+ // outside visual/colormap
+ Visual *visual = nullptr;
+ Colormap colormap = 0;
+
+#if QT_CONFIG(xrender)
+ enum { solid_fill_count = 16 };
+ struct SolidFills {
+ XRenderColor color;
+ int screen;
+ Picture picture;
+ } solid_fills[solid_fill_count];
+ enum { pattern_fill_count = 16 };
+ struct PatternFills {
+ XRenderColor color;
+ XRenderColor bg_color;
+ int screen;
+ int style;
+ bool opaque;
+ Picture picture;
+ } pattern_fills[pattern_fill_count];
+ Picture getSolidFill(int screen, const QColor &c);
+ XRenderColor preMultiply(const QColor &c);
+#endif
+
+ bool fc_antialias = true;
+ int fc_hint_style = 0;
+
+ DesktopEnvironment desktopEnvironment = DE_GNOME;
+};
+
+extern QXcbX11Data *qt_x11Data;
+#define X11 qt_x11Data
+
+struct QX11InfoData {
+ int screen;
+ int dpiX;
+ int dpiY;
+ int depth;
+ int cells;
+ Colormap colormap;
+ Visual *visual;
+ bool defaultColormap;
+ bool defaultVisual;
+ int subpixel = 0;
+};
+
+template <class T>
+Q_DECL_RELAXED_CONSTEXPR inline int lowest_bit(T v) Q_DECL_NOTHROW
+{
+ int result = qCountTrailingZeroBits(v);
+ return ((result >> 3) == sizeof(T)) ? -1 : result;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_X11_P_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp b/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp
new file mode 100644
index 0000000000..9acb21f95f
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp
@@ -0,0 +1,1494 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtessellator_p.h"
+
+#include <QRect>
+#include <QList>
+#include <QDebug>
+
+#include <qmath.h>
+#include <limits.h>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG
+#ifdef DEBUG
+#define QDEBUG qDebug
+#else
+#define QDEBUG if (1){} else qDebug
+#endif
+
+static const bool emit_clever = true;
+static const bool mark_clever = false;
+
+enum VertexFlags {
+ LineBeforeStarts = 0x1,
+ LineBeforeEnds = 0x2,
+ LineBeforeHorizontal = 0x4,
+ LineAfterStarts = 0x8,
+ LineAfterEnds = 0x10,
+ LineAfterHorizontal = 0x20
+};
+
+
+
+class QTessellatorPrivate {
+public:
+ struct Vertices;
+
+ QTessellatorPrivate() {}
+
+ QRectF collectAndSortVertices(const QPointF *points, int *maxActiveEdges);
+ void cancelCoincidingEdges();
+
+ void emitEdges(QTessellator *tessellator);
+ void processIntersections();
+ void removeEdges();
+ void addEdges();
+ void addIntersections();
+
+ struct Vertex : public QTessellator::Vertex
+ {
+ int flags;
+ };
+
+ struct Intersection
+ {
+ Q27Dot5 y;
+ int edge;
+ bool operator <(const Intersection &other) const {
+ if (y != other.y)
+ return y < other.y;
+ return edge < other.edge;
+ }
+ };
+ struct IntersectionLink
+ {
+ int next;
+ int prev;
+ };
+ typedef QMap<Intersection, IntersectionLink> Intersections;
+
+ struct Edge {
+ Edge(const Vertices &v, int _edge);
+ int edge;
+ const Vertex *v0;
+ const Vertex *v1;
+ Q27Dot5 y_left;
+ Q27Dot5 y_right;
+ signed int winding : 8;
+ bool mark;
+ bool free;
+ bool intersect_left;
+ bool intersect_right;
+ bool isLeftOf(const Edge &other, Q27Dot5 y) const;
+ Q27Dot5 positionAt(Q27Dot5 y) const;
+ bool intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const;
+
+ };
+
+ class EdgeSorter
+ {
+ public:
+ EdgeSorter(int _y) : y(_y) {}
+ bool operator() (const Edge *e1, const Edge *e2);
+ int y;
+ };
+
+ class Scanline {
+ public:
+ Scanline();
+ ~Scanline();
+
+ void init(int maxActiveEdges);
+ void done();
+
+ int findEdgePosition(Q27Dot5 x, Q27Dot5 y) const;
+ int findEdgePosition(const Edge &e) const;
+ int findEdge(int edge) const;
+ void clearMarks();
+
+ void swap(int p1, int p2) {
+ Edge *tmp = edges[p1];
+ edges[p1] = edges[p2];
+ edges[p2] = tmp;
+ }
+ void insert(int pos, const Edge &e);
+ void removeAt(int pos);
+ void markEdges(int pos1, int pos2);
+
+ void prepareLine();
+ void lineDone();
+
+ Edge **old;
+ int old_size;
+
+ Edge **edges;
+ int size;
+
+ private:
+ Edge *edge_table;
+ int first_unused;
+ int max_edges;
+ enum { default_alloc = 32 };
+ };
+
+ struct Vertices {
+ enum { default_alloc = 128 };
+ Vertices();
+ ~Vertices();
+ void init(int maxVertices);
+ void done();
+ Vertex *storage;
+ Vertex **sorted;
+
+ Vertex *operator[] (int i) { return storage + i; }
+ const Vertex *operator[] (int i) const { return storage + i; }
+ int position(const Vertex *v) const {
+ return v - storage;
+ }
+ Vertex *next(Vertex *v) {
+ ++v;
+ if (v == storage + nPoints)
+ v = storage;
+ return v;
+ }
+ const Vertex *next(const Vertex *v) const {
+ ++v;
+ if (v == storage + nPoints)
+ v = storage;
+ return v;
+ }
+ int nextPos(const Vertex *v) const {
+ ++v;
+ if (v == storage + nPoints)
+ return 0;
+ return v - storage;
+ }
+ Vertex *prev(Vertex *v) {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v;
+ }
+ const Vertex *prev(const Vertex *v) const {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v;
+ }
+ int prevPos(const Vertex *v) const {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v - storage;
+ }
+ int nPoints;
+ int allocated;
+ };
+ Vertices vertices;
+ Intersections intersections;
+ Scanline scanline;
+ bool winding;
+ Q27Dot5 y;
+ int currentVertex;
+
+private:
+ void addIntersection(const Edge *e1, const Edge *e2);
+ bool edgeInChain(Intersection i, int edge);
+};
+
+
+QTessellatorPrivate::Edge::Edge(const QTessellatorPrivate::Vertices &vertices, int edge)
+{
+ this->edge = edge;
+ intersect_left = intersect_right = true;
+ mark = false;
+ free = false;
+
+ v0 = vertices[edge];
+ v1 = vertices.next(v0);
+
+ Q_ASSERT(v0->y != v1->y);
+
+ if (v0->y > v1->y) {
+ qSwap(v0, v1);
+ winding = -1;
+ } else {
+ winding = 1;
+ }
+ y_left = y_right = v0->y;
+}
+
+// This is basically the algorithm from graphics gems. The algorithm
+// is cubic in the coordinates at one place. Since we use 64bit
+// integers, this implies, that the allowed range for our coordinates
+// is limited to 21 bits. With 5 bits behind the decimal, this
+// implies that differences in coordaintes can range from 2*SHORT_MIN
+// to 2*SHORT_MAX, giving us efficiently a coordinate system from
+// SHORT_MIN to SHORT_MAX.
+//
+
+// WARNING: It's absolutely critical that the intersect() and isLeftOf() methods use
+// exactly the same algorithm to calulate yi. It's also important to be sure the algorithms
+// are transitive (ie. the conditions below are true for all input data):
+//
+// a.intersect(b) == b.intersect(a)
+// a.isLeftOf(b) != b.isLeftOf(a)
+//
+// This is tricky to get right, so be very careful when changing anything in here!
+
+static inline bool sameSign(qint64 a, qint64 b) {
+ return (((qint64) ((quint64) a ^ (quint64) b)) >= 0 );
+}
+
+bool QTessellatorPrivate::Edge::intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const
+{
+ qint64 a1 = v1->y - v0->y;
+ qint64 b1 = v0->x - v1->x;
+
+ qint64 a2 = other.v1->y - other.v0->y;
+ qint64 b2 = other.v0->x - other.v1->x;
+
+ qint64 det = a1 * b2 - a2 * b1;
+ if (det == 0)
+ return false;
+
+ qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+ qint64 r3 = a1 * other.v0->x + b1 * other.v0->y + c1;
+ qint64 r4 = a1 * other.v1->x + b1 * other.v1->y + c1;
+
+ // Check signs of r3 and r4. If both point 3 and point 4 lie on
+ // same side of line 1, the line segments do not intersect.
+ QDEBUG() << " " << r3 << r4;
+ if (r3 != 0 && r4 != 0 && sameSign( r3, r4 ))
+ return false;
+
+ qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+ qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+ qint64 r2 = a2 * v1->x + b2 * v1->y + c2;
+
+ // Check signs of r1 and r2. If both point 1 and point 2 lie
+ // on same side of second line segment, the line segments do not intersect.
+ QDEBUG() << " " << r1 << r2;
+ if (r1 != 0 && r2 != 0 && sameSign( r1, r2 ))
+ return false;
+
+ // The det/2 is to get rounding instead of truncating. It
+ // is added or subtracted to the numerator, depending upon the
+ // sign of the numerator.
+ qint64 offset = det < 0 ? -det : det;
+ offset >>= 1;
+
+ qint64 num = a2 * c1 - a1 * c2;
+ *y = ( num < 0 ? num - offset : num + offset ) / det;
+
+ *det_positive = (det > 0);
+
+ return true;
+}
+
+#undef SAME_SIGNS
+
+bool QTessellatorPrivate::Edge::isLeftOf(const Edge &other, Q27Dot5 y) const
+{
+// QDEBUG() << "isLeftOf" << edge << other.edge << y;
+ qint64 a1 = v1->y - v0->y;
+ qint64 b1 = v0->x - v1->x;
+ qint64 a2 = other.v1->y - other.v0->y;
+ qint64 b2 = other.v0->x - other.v1->x;
+
+ qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+ qint64 det = a1 * b2 - a2 * b1;
+ if (det == 0) {
+ // lines are parallel. Only need to check side of one point
+ // fixed ordering for coincident edges
+ qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+// QDEBUG() << "det = 0" << r1;
+ if (r1 == 0)
+ return edge < other.edge;
+ return (r1 < 0);
+ }
+
+ // not parallel, need to find the y coordinate of the intersection point
+ qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+ qint64 offset = det < 0 ? -det : det;
+ offset >>= 1;
+
+ qint64 num = a2 * c1 - a1 * c2;
+ qint64 yi = ( num < 0 ? num - offset : num + offset ) / det;
+// QDEBUG() << " num=" << num << "offset=" << offset << "det=" << det;
+
+ return ((yi > y) ^ (det < 0));
+}
+
+static inline bool compareVertex(const QTessellatorPrivate::Vertex *p1,
+ const QTessellatorPrivate::Vertex *p2)
+{
+ if (p1->y == p2->y) {
+ if (p1->x == p2->x)
+ return p1 < p2;
+ return p1->x < p2->x;
+ }
+ return p1->y < p2->y;
+}
+
+Q27Dot5 QTessellatorPrivate::Edge::positionAt(Q27Dot5 y) const
+{
+ if (y == v0->y)
+ return v0->x;
+ else if (y == v1->y)
+ return v1->x;
+
+ qint64 d = v1->x - v0->x;
+ return (v0->x + d*(y - v0->y)/(v1->y-v0->y));
+}
+
+bool QTessellatorPrivate::EdgeSorter::operator() (const Edge *e1, const Edge *e2)
+{
+ return e1->isLeftOf(*e2, y);
+}
+
+
+QTessellatorPrivate::Scanline::Scanline()
+{
+ edges = 0;
+ edge_table = 0;
+ old = 0;
+}
+
+void QTessellatorPrivate::Scanline::init(int maxActiveEdges)
+{
+ maxActiveEdges *= 2;
+ if (!edges || maxActiveEdges > default_alloc) {
+ max_edges = maxActiveEdges;
+ int s = qMax(maxActiveEdges + 1, default_alloc + 1);
+ edges = q_check_ptr((Edge **)realloc(edges, s*sizeof(Edge *)));
+ edge_table = q_check_ptr((Edge *)realloc(edge_table, s*sizeof(Edge)));
+ old = q_check_ptr((Edge **)realloc(old, s*sizeof(Edge *)));
+ }
+ size = 0;
+ old_size = 0;
+ first_unused = 0;
+ for (int i = 0; i < maxActiveEdges; ++i)
+ edge_table[i].edge = i+1;
+ edge_table[maxActiveEdges].edge = -1;
+}
+
+void QTessellatorPrivate::Scanline::done()
+{
+ if (max_edges > default_alloc) {
+ free(edges);
+ free(old);
+ free(edge_table);
+ edges = 0;
+ old = 0;
+ edge_table = 0;
+ }
+}
+
+QTessellatorPrivate::Scanline::~Scanline()
+{
+ free(edges);
+ free(old);
+ free(edge_table);
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(Q27Dot5 x, Q27Dot5 y) const
+{
+ int min = 0;
+ int max = size - 1;
+ while (min < max) {
+ int pos = min + ((max - min + 1) >> 1);
+ Q27Dot5 ax = edges[pos]->positionAt(y);
+ if (ax > x) {
+ max = pos - 1;
+ } else {
+ min = pos;
+ }
+ }
+ return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(const Edge &e) const
+{
+// qDebug() << ">> findEdgePosition";
+ int min = 0;
+ int max = size;
+ while (min < max) {
+ int pos = min + ((max - min) >> 1);
+// qDebug() << " " << min << max << pos << edges[pos]->isLeftOf(e, e.y0);
+ if (edges[pos]->isLeftOf(e, e.v0->y)) {
+ min = pos + 1;
+ } else {
+ max = pos;
+ }
+ }
+// qDebug() << "<< findEdgePosition got" << min;
+ return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdge(int edge) const
+{
+ for (int i = 0; i < size; ++i) {
+ int item_edge = edges[i]->edge;
+ if (item_edge == edge)
+ return i;
+ }
+ //Q_ASSERT(false);
+ return -1;
+}
+
+void QTessellatorPrivate::Scanline::clearMarks()
+{
+ for (int i = 0; i < size; ++i) {
+ edges[i]->mark = false;
+ edges[i]->intersect_left = false;
+ edges[i]->intersect_right = false;
+ }
+}
+
+void QTessellatorPrivate::Scanline::prepareLine()
+{
+ Edge **end = edges + size;
+ Edge **e = edges;
+ Edge **o = old;
+ while (e < end) {
+ *o = *e;
+ ++o;
+ ++e;
+ }
+ old_size = size;
+}
+
+void QTessellatorPrivate::Scanline::lineDone()
+{
+ Edge **end = old + old_size;
+ Edge **e = old;
+ while (e < end) {
+ if ((*e)->free) {
+ (*e)->edge = first_unused;
+ first_unused = (*e - edge_table);
+ }
+ ++e;
+ }
+}
+
+void QTessellatorPrivate::Scanline::insert(int pos, const Edge &e)
+{
+ Edge *edge = edge_table + first_unused;
+ first_unused = edge->edge;
+ Q_ASSERT(first_unused != -1);
+ *edge = e;
+ memmove(edges + pos + 1, edges + pos, (size - pos)*sizeof(Edge *));
+ edges[pos] = edge;
+ ++size;
+}
+
+void QTessellatorPrivate::Scanline::removeAt(int pos)
+{
+ Edge *e = edges[pos];
+ e->free = true;
+ --size;
+ memmove(edges + pos, edges + pos + 1, (size - pos)*sizeof(Edge *));
+}
+
+void QTessellatorPrivate::Scanline::markEdges(int pos1, int pos2)
+{
+ if (pos2 < pos1)
+ return;
+
+ for (int i = pos1; i <= pos2; ++i)
+ edges[i]->mark = true;
+}
+
+
+QTessellatorPrivate::Vertices::Vertices()
+{
+ storage = 0;
+ sorted = 0;
+ allocated = 0;
+ nPoints = 0;
+}
+
+QTessellatorPrivate::Vertices::~Vertices()
+{
+ if (storage) {
+ free(storage);
+ free(sorted);
+ }
+}
+
+void QTessellatorPrivate::Vertices::init(int maxVertices)
+{
+ if (!storage || maxVertices > allocated) {
+ int size = qMax((int)default_alloc, maxVertices);
+ storage = q_check_ptr((Vertex *)realloc(storage, size*sizeof(Vertex)));
+ sorted = q_check_ptr((Vertex **)realloc(sorted, size*sizeof(Vertex *)));
+ allocated = maxVertices;
+ }
+}
+
+void QTessellatorPrivate::Vertices::done()
+{
+ if (allocated > default_alloc) {
+ free(storage);
+ free(sorted);
+ storage = 0;
+ sorted = 0;
+ allocated = 0;
+ }
+}
+
+
+
+static inline void fillTrapezoid(Q27Dot5 y1, Q27Dot5 y2, int left, int right,
+ const QTessellatorPrivate::Vertices &vertices,
+ QTessellator::Trapezoid *trap)
+{
+ trap->top = y1;
+ trap->bottom = y2;
+ const QTessellatorPrivate::Vertex *v = vertices[left];
+ trap->topLeft = v;
+ trap->bottomLeft = vertices.next(v);
+ if (trap->topLeft->y > trap->bottomLeft->y)
+ qSwap(trap->topLeft,trap->bottomLeft);
+ v = vertices[right];
+ trap->topRight = v;
+ trap->bottomRight = vertices.next(v);
+ if (trap->topRight->y > trap->bottomRight->y)
+ qSwap(trap->topRight, trap->bottomRight);
+}
+
+QRectF QTessellatorPrivate::collectAndSortVertices(const QPointF *points, int *maxActiveEdges)
+{
+ *maxActiveEdges = 0;
+ Vertex *v = vertices.storage;
+ Vertex **vv = vertices.sorted;
+
+ qreal xmin(points[0].x());
+ qreal xmax(points[0].x());
+ qreal ymin(points[0].y());
+ qreal ymax(points[0].y());
+
+ // collect vertex data
+ Q27Dot5 y_prev = FloatToQ27Dot5(points[vertices.nPoints-1].y());
+ Q27Dot5 x_next = FloatToQ27Dot5(points[0].x());
+ Q27Dot5 y_next = FloatToQ27Dot5(points[0].y());
+ int j = 0;
+ int i = 0;
+ while (i < vertices.nPoints) {
+ Q27Dot5 y_curr = y_next;
+
+ *vv = v;
+
+ v->x = x_next;
+ v->y = y_next;
+ v->flags = 0;
+
+ next_point:
+
+ xmin = qMin(xmin, points[i+1].x());
+ xmax = qMax(xmax, points[i+1].x());
+ ymin = qMin(ymin, points[i+1].y());
+ ymax = qMax(ymax, points[i+1].y());
+
+ y_next = FloatToQ27Dot5(points[i+1].y());
+ x_next = FloatToQ27Dot5(points[i+1].x());
+
+ // skip vertices on top of each other
+ if (v->x == x_next && v->y == y_next) {
+ ++i;
+ if (i < vertices.nPoints)
+ goto next_point;
+ Vertex *v0 = vertices.storage;
+ v0->flags &= ~(LineBeforeStarts|LineBeforeEnds|LineBeforeHorizontal);
+ if (y_prev < y_curr)
+ v0->flags |= LineBeforeEnds;
+ else if (y_prev > y_curr)
+ v0->flags |= LineBeforeStarts;
+ else
+ v0->flags |= LineBeforeHorizontal;
+ if ((v0->flags & (LineBeforeStarts|LineAfterStarts))
+ && !(v0->flags & (LineAfterEnds|LineBeforeEnds)))
+ *maxActiveEdges += 2;
+ break;
+ }
+
+ if (y_prev < y_curr)
+ v->flags |= LineBeforeEnds;
+ else if (y_prev > y_curr)
+ v->flags |= LineBeforeStarts;
+ else
+ v->flags |= LineBeforeHorizontal;
+
+
+ if (y_curr < y_next)
+ v->flags |= LineAfterStarts;
+ else if (y_curr > y_next)
+ v->flags |= LineAfterEnds;
+ else
+ v->flags |= LineAfterHorizontal;
+ // ### could probably get better limit by looping over sorted list and counting down on ending edges
+ if ((v->flags & (LineBeforeStarts|LineAfterStarts))
+ && !(v->flags & (LineAfterEnds|LineBeforeEnds)))
+ *maxActiveEdges += 2;
+ y_prev = y_curr;
+ ++v;
+ ++vv;
+ ++j;
+ ++i;
+ }
+ vertices.nPoints = j;
+
+ QDEBUG() << "maxActiveEdges=" << *maxActiveEdges;
+ vv = vertices.sorted;
+ std::sort(vv, vv + vertices.nPoints, compareVertex);
+
+ return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
+}
+
+struct QCoincidingEdge {
+ QTessellatorPrivate::Vertex *start;
+ QTessellatorPrivate::Vertex *end;
+ bool used;
+ bool before;
+
+ inline bool operator<(const QCoincidingEdge &e2) const
+ {
+ return end->y == e2.end->y ? end->x < e2.end->x : end->y < e2.end->y;
+ }
+};
+
+static void cancelEdges(QCoincidingEdge &e1, QCoincidingEdge &e2)
+{
+ if (e1.before) {
+ e1.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+ e1.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+ } else {
+ e1.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+ e1.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+ }
+ if (e2.before) {
+ e2.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+ e2.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+ } else {
+ e2.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+ e2.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+ }
+ e1.used = e2.used = true;
+}
+
+void QTessellatorPrivate::cancelCoincidingEdges()
+{
+ Vertex **vv = vertices.sorted;
+
+ QCoincidingEdge *tl = 0;
+ int tlSize = 0;
+
+ for (int i = 0; i < vertices.nPoints - 1; ++i) {
+ Vertex *v = vv[i];
+ int testListSize = 0;
+ while (i < vertices.nPoints - 1) {
+ Vertex *n = vv[i];
+ if (v->x != n->x || v->y != n->y)
+ break;
+
+ if (testListSize > tlSize - 2) {
+ tlSize = qMax(tlSize*2, 16);
+ tl = q_check_ptr((QCoincidingEdge *)realloc(tl, tlSize*sizeof(QCoincidingEdge)));
+ }
+ if (n->flags & (LineBeforeStarts|LineBeforeHorizontal)) {
+ tl[testListSize].start = n;
+ tl[testListSize].end = vertices.prev(n);
+ tl[testListSize].used = false;
+ tl[testListSize].before = true;
+ ++testListSize;
+ }
+ if (n->flags & (LineAfterStarts|LineAfterHorizontal)) {
+ tl[testListSize].start = n;
+ tl[testListSize].end = vertices.next(n);
+ tl[testListSize].used = false;
+ tl[testListSize].before = false;
+ ++testListSize;
+ }
+ ++i;
+ }
+ if (!testListSize)
+ continue;
+
+ std::sort(tl, tl + testListSize);
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_CLANG("-Wfor-loop-analysis")
+ for (int j = 0; j < testListSize; ++j) {
+ if (tl[j].used)
+ continue;
+
+ for (int k = j + 1; k < testListSize; ++k) {
+ if (tl[j].end->x != tl[k].end->x
+ || tl[j].end->y != tl[k].end->y
+ || tl[k].used)
+ break;
+
+ if (!winding || tl[j].before != tl[k].before) {
+ cancelEdges(tl[j], tl[k]);
+ break;
+ }
+ ++k;
+ }
+ ++j;
+ }
+QT_WARNING_POP
+ }
+ free(tl);
+}
+
+
+void QTessellatorPrivate::emitEdges(QTessellator *tessellator)
+{
+ //QDEBUG() << "TRAPS:";
+ if (!scanline.old_size)
+ return;
+
+ // emit edges
+ if (winding) {
+ // winding fill rule
+ int w = 0;
+
+ scanline.old[0]->y_left = y;
+
+ for (int i = 0; i < scanline.old_size - 1; ++i) {
+ Edge *left = scanline.old[i];
+ Edge *right = scanline.old[i+1];
+ w += left->winding;
+// qDebug() << "i=" << i << "edge->winding=" << left->winding << "winding=" << winding;
+ if (w == 0) {
+ left->y_right = y;
+ right->y_left = y;
+ } else if (!emit_clever || left->mark || right->mark) {
+ Q27Dot5 top = qMax(left->y_right, right->y_left);
+ if (top != y) {
+ QTessellator::Trapezoid trap;
+ fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+ tessellator->addTrap(trap);
+// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+ }
+ right->y_left = y;
+ left->y_right = y;
+ }
+ left->mark = false;
+ }
+ if (scanline.old[scanline.old_size - 1]->mark) {
+ scanline.old[scanline.old_size - 1]->y_right = y;
+ scanline.old[scanline.old_size - 1]->mark = false;
+ }
+ } else {
+ // odd-even fill rule
+ for (int i = 0; i < scanline.old_size; i += 2) {
+ Edge *left = scanline.old[i];
+ Edge *right = scanline.old[i+1];
+ if (!emit_clever || left->mark || right->mark) {
+ Q27Dot5 top = qMax(left->y_right, right->y_left);
+ if (top != y) {
+ QTessellator::Trapezoid trap;
+ fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+ tessellator->addTrap(trap);
+ }
+// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+ left->y_left = y;
+ left->y_right = y;
+ right->y_left = y;
+ right->y_right = y;
+ left->mark = right->mark = false;
+ }
+ }
+ }
+}
+
+
+void QTessellatorPrivate::processIntersections()
+{
+ QDEBUG() << "PROCESS INTERSECTIONS";
+ // process intersections
+ while (!intersections.isEmpty()) {
+ Intersections::iterator it = intersections.begin();
+ if (it.key().y != y)
+ break;
+
+ // swap edges
+ QDEBUG() << " swapping intersecting edges ";
+ int min = scanline.size;
+ int max = 0;
+ Q27Dot5 xmin = INT_MAX;
+ Q27Dot5 xmax = INT_MIN;
+ int num = 0;
+ while (1) {
+ const Intersection i = it.key();
+ int next = it->next;
+
+ int edgePos = scanline.findEdge(i.edge);
+ if (edgePos >= 0) {
+ ++num;
+ min = qMin(edgePos, min);
+ max = qMax(edgePos, max);
+ Edge *edge = scanline.edges[edgePos];
+ xmin = qMin(xmin, edge->positionAt(y));
+ xmax = qMax(xmax, edge->positionAt(y));
+ }
+ Intersection key;
+ key.y = y;
+ key.edge = next;
+ it = intersections.find(key);
+ intersections.remove(i);
+ if (it == intersections.end())
+ break;
+ }
+ if (num < 2)
+ continue;
+
+ Q_ASSERT(min != max);
+ QDEBUG() << "sorting between" << min << "and" << max << "xpos=" << xmin << xmax;
+ while (min > 0 && scanline.edges[min - 1]->positionAt(y) >= xmin) {
+ QDEBUG() << " adding edge on left";
+ --min;
+ }
+ while (max < scanline.size - 1 && scanline.edges[max + 1]->positionAt(y) <= xmax) {
+ QDEBUG() << " adding edge on right";
+ ++max;
+ }
+
+ std::sort(scanline.edges + min, scanline.edges + max + 1, EdgeSorter(y));
+#ifdef DEBUG
+ for (int i = min; i <= max; ++i)
+ QDEBUG() << " " << scanline.edges[i]->edge << "at pos" << i;
+#endif
+ for (int i = min; i <= max; ++i) {
+ Edge *edge = scanline.edges[i];
+ edge->intersect_left = true;
+ edge->intersect_right = true;
+ edge->mark = true;
+ }
+ }
+}
+
+void QTessellatorPrivate::removeEdges()
+{
+ int cv = currentVertex;
+ while (cv < vertices.nPoints) {
+ const Vertex *v = vertices.sorted[cv];
+ if (v->y > y)
+ break;
+ if (v->flags & LineBeforeEnds) {
+ QDEBUG() << " removing edge" << vertices.prevPos(v);
+ int pos = scanline.findEdge(vertices.prevPos(v));
+ if (pos == -1)
+ continue;
+ scanline.edges[pos]->mark = true;
+ if (pos > 0)
+ scanline.edges[pos - 1]->intersect_right = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->intersect_left = true;
+ scanline.removeAt(pos);
+ }
+ if (v->flags & LineAfterEnds) {
+ QDEBUG() << " removing edge" << vertices.position(v);
+ int pos = scanline.findEdge(vertices.position(v));
+ if (pos == -1)
+ continue;
+ scanline.edges[pos]->mark = true;
+ if (pos > 0)
+ scanline.edges[pos - 1]->intersect_right = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->intersect_left = true;
+ scanline.removeAt(pos);
+ }
+ ++cv;
+ }
+}
+
+void QTessellatorPrivate::addEdges()
+{
+ while (currentVertex < vertices.nPoints) {
+ const Vertex *v = vertices.sorted[currentVertex];
+ if (v->y > y)
+ break;
+ if (v->flags & LineBeforeStarts) {
+ // add new edge
+ int start = vertices.prevPos(v);
+ Edge e(vertices, start);
+ int pos = scanline.findEdgePosition(e);
+ QDEBUG() << " adding edge" << start << "at position" << pos;
+ scanline.insert(pos, e);
+ if (!mark_clever || !(v->flags & LineAfterEnds)) {
+ if (pos > 0)
+ scanline.edges[pos - 1]->mark = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->mark = true;
+ }
+ }
+ if (v->flags & LineAfterStarts) {
+ Edge e(vertices, vertices.position(v));
+ int pos = scanline.findEdgePosition(e);
+ QDEBUG() << " adding edge" << vertices.position(v) << "at position" << pos;
+ scanline.insert(pos, e);
+ if (!mark_clever || !(v->flags & LineBeforeEnds)) {
+ if (pos > 0)
+ scanline.edges[pos - 1]->mark = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->mark = true;
+ }
+ }
+ if (v->flags & LineAfterHorizontal) {
+ int pos1 = scanline.findEdgePosition(v->x, v->y);
+ const Vertex *next = vertices.next(v);
+ Q_ASSERT(v->y == next->y);
+ int pos2 = scanline.findEdgePosition(next->x, next->y);
+ if (pos2 < pos1)
+ qSwap(pos1, pos2);
+ if (pos1 > 0)
+ --pos1;
+ if (pos2 == scanline.size)
+ --pos2;
+ //QDEBUG() << "marking horizontal edge from " << pos1 << "to" << pos2;
+ scanline.markEdges(pos1, pos2);
+ }
+ ++currentVertex;
+ }
+}
+
+#ifdef DEBUG
+static void checkLinkChain(const QTessellatorPrivate::Intersections &intersections,
+ QTessellatorPrivate::Intersection i)
+{
+// qDebug() << " Link chain: ";
+ int end = i.edge;
+ while (1) {
+ QTessellatorPrivate::IntersectionLink l = intersections.value(i);
+// qDebug() << " " << i.edge << "next=" << l.next << "prev=" << l.prev;
+ if (l.next == end)
+ break;
+ Q_ASSERT(l.next != -1);
+ Q_ASSERT(l.prev != -1);
+
+ QTessellatorPrivate::Intersection i2 = i;
+ i2.edge = l.next;
+ QTessellatorPrivate::IntersectionLink l2 = intersections.value(i2);
+
+ Q_ASSERT(l2.next != -1);
+ Q_ASSERT(l2.prev != -1);
+ Q_ASSERT(l.next == i2.edge);
+ Q_ASSERT(l2.prev == i.edge);
+ i = i2;
+ }
+}
+#endif
+
+bool QTessellatorPrivate::edgeInChain(Intersection i, int edge)
+{
+ int end = i.edge;
+ while (1) {
+ if (i.edge == edge)
+ return true;
+ IntersectionLink l = intersections.value(i);
+ if (l.next == end)
+ break;
+ Q_ASSERT(l.next != -1);
+ Q_ASSERT(l.prev != -1);
+
+ Intersection i2 = i;
+ i2.edge = l.next;
+
+#ifndef QT_NO_DEBUG
+ IntersectionLink l2 = intersections.value(i2);
+ Q_ASSERT(l2.next != -1);
+ Q_ASSERT(l2.prev != -1);
+ Q_ASSERT(l.next == i2.edge);
+ Q_ASSERT(l2.prev == i.edge);
+#endif
+ i = i2;
+ }
+ return false;
+}
+
+
+void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2)
+{
+ const IntersectionLink emptyLink = {-1, -1};
+
+ int next = vertices.nextPos(vertices[e1->edge]);
+ if (e2->edge == next)
+ return;
+ int prev = vertices.prevPos(vertices[e1->edge]);
+ if (e2->edge == prev)
+ return;
+
+ Q27Dot5 yi;
+ bool det_positive;
+ bool isect = e1->intersect(*e2, &yi, &det_positive);
+ QDEBUG("checking edges %d and %d", e1->edge, e2->edge);
+ if (!isect) {
+ QDEBUG() << " no intersection";
+ return;
+ }
+
+ // don't emit an intersection if it's at the start of a line segment or above us
+ if (yi <= y) {
+ if (!det_positive)
+ return;
+ QDEBUG() << " ----->>>>>> WRONG ORDER!";
+ yi = y;
+ }
+ QDEBUG() << " between edges " << e1->edge << "and" << e2->edge << "at point ("
+ << Q27Dot5ToDouble(yi) << ')';
+
+ Intersection i1;
+ i1.y = yi;
+ i1.edge = e1->edge;
+ IntersectionLink link1 = intersections.value(i1, emptyLink);
+ Intersection i2;
+ i2.y = yi;
+ i2.edge = e2->edge;
+ IntersectionLink link2 = intersections.value(i2, emptyLink);
+
+ // new pair of edges
+ if (link1.next == -1 && link2.next == -1) {
+ link1.next = link1.prev = i2.edge;
+ link2.next = link2.prev = i1.edge;
+ } else if (link1.next == i2.edge || link1.prev == i2.edge
+ || link2.next == i1.edge || link2.prev == i1.edge) {
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+ Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+ return;
+ } else if (link1.next == -1 || link2.next == -1) {
+ if (link2.next == -1) {
+ qSwap(i1, i2);
+ qSwap(link1, link2);
+ }
+ Q_ASSERT(link1.next == -1);
+#ifdef DEBUG
+ checkLinkChain(intersections, i2);
+#endif
+ // only i2 in list
+ link1.next = i2.edge;
+ link1.prev = link2.prev;
+ link2.prev = i1.edge;
+ Intersection other;
+ other.y = yi;
+ other.edge = link1.prev;
+ IntersectionLink link = intersections.value(other, emptyLink);
+ Q_ASSERT(link.next == i2.edge);
+ Q_ASSERT(link.prev != -1);
+ link.next = i1.edge;
+ intersections.insert(other, link);
+ } else {
+ bool connected = edgeInChain(i1, i2.edge);
+ if (connected)
+ return;
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+#endif
+ // both already in some list. Have to make sure they are connected
+ // this can be done by cutting open the ring(s) after the two eges and
+ // connecting them again
+ Intersection other1;
+ other1.y = yi;
+ other1.edge = link1.next;
+ IntersectionLink linko1 = intersections.value(other1, emptyLink);
+ Intersection other2;
+ other2.y = yi;
+ other2.edge = link2.next;
+ IntersectionLink linko2 = intersections.value(other2, emptyLink);
+
+ linko1.prev = i2.edge;
+ link2.next = other1.edge;
+
+ linko2.prev = i1.edge;
+ link1.next = other2.edge;
+ intersections.insert(other1, linko1);
+ intersections.insert(other2, linko2);
+ }
+ intersections.insert(i1, link1);
+ intersections.insert(i2, link2);
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+ Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+ return;
+
+}
+
+
+void QTessellatorPrivate::addIntersections()
+{
+ if (scanline.size) {
+ QDEBUG() << "INTERSECTIONS";
+ // check marked edges for intersections
+#ifdef DEBUG
+ for (int i = 0; i < scanline.size; ++i) {
+ Edge *e = scanline.edges[i];
+ QDEBUG() << " " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right
+ << ')';
+ }
+#endif
+
+ for (int i = 0; i < scanline.size - 1; ++i) {
+ Edge *e1 = scanline.edges[i];
+ Edge *e2 = scanline.edges[i + 1];
+ // check for intersection
+ if (e1->intersect_right || e2->intersect_left)
+ addIntersection(e1, e2);
+ }
+ }
+#if 0
+ if (intersections.constBegin().key().y == y) {
+ QDEBUG() << "----------------> intersection on same line";
+ scanline.clearMarks();
+ scanline.processIntersections(y, &intersections);
+ goto redo;
+ }
+#endif
+}
+
+
+QTessellator::QTessellator()
+{
+ d = new QTessellatorPrivate;
+}
+
+QTessellator::~QTessellator()
+{
+ delete d;
+}
+
+void QTessellator::setWinding(bool w)
+{
+ d->winding = w;
+}
+
+
+QRectF QTessellator::tessellate(const QPointF *points, int nPoints)
+{
+ Q_ASSERT(points[0] == points[nPoints-1]);
+ --nPoints;
+
+#ifdef DEBUG
+ QDEBUG()<< "POINTS:";
+ for (int i = 0; i < nPoints; ++i) {
+ QDEBUG() << points[i];
+ }
+#endif
+
+ // collect edges and calculate bounds
+ d->vertices.nPoints = nPoints;
+ d->vertices.init(nPoints);
+
+ int maxActiveEdges = 0;
+ QRectF br = d->collectAndSortVertices(points, &maxActiveEdges);
+ d->cancelCoincidingEdges();
+
+#ifdef DEBUG
+ QDEBUG() << "nPoints = " << nPoints << "using " << d->vertices.nPoints;
+ QDEBUG()<< "VERTICES:";
+ for (int i = 0; i < d->vertices.nPoints; ++i) {
+ QDEBUG() << " " << i << ": "
+ << "point=" << d->vertices.position(d->vertices.sorted[i])
+ << "flags=" << d->vertices.sorted[i]->flags
+ << "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << '/'
+ << Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ')';
+ }
+#endif
+
+ d->scanline.init(maxActiveEdges);
+ d->y = INT_MIN/256;
+ d->currentVertex = 0;
+
+ while (d->currentVertex < d->vertices.nPoints) {
+ d->scanline.clearMarks();
+
+ d->y = d->vertices.sorted[d->currentVertex]->y;
+ if (!d->intersections.isEmpty())
+ d->y = qMin(d->y, d->intersections.constBegin().key().y);
+
+ QDEBUG()<< "===== SCANLINE: y =" << Q27Dot5ToDouble(d->y) << " =====";
+
+ d->scanline.prepareLine();
+ d->processIntersections();
+ d->removeEdges();
+ d->addEdges();
+ d->addIntersections();
+ d->emitEdges(this);
+ d->scanline.lineDone();
+
+#ifdef DEBUG
+ QDEBUG()<< "===== edges:";
+ for (int i = 0; i < d->scanline.size; ++i) {
+ QDEBUG() << " " << d->scanline.edges[i]->edge
+ << "p0= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->x)
+ << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y)
+ << ") p1= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->x)
+ << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ')'
+ << "x=" << Q27Dot5ToDouble(d->scanline.edges[i]->positionAt(d->y))
+ << "isLeftOfNext="
+ << ((i < d->scanline.size - 1)
+ ? d->scanline.edges[i]->isLeftOf(*d->scanline.edges[i+1], d->y)
+ : true);
+ }
+#endif
+}
+
+ d->scanline.done();
+ d->intersections.clear();
+ return br;
+}
+
+// tessellates the given convex polygon
+void QTessellator::tessellateConvex(const QPointF *points, int nPoints)
+{
+ Q_ASSERT(points[0] == points[nPoints-1]);
+ --nPoints;
+
+ d->vertices.nPoints = nPoints;
+ d->vertices.init(nPoints);
+
+ for (int i = 0; i < nPoints; ++i) {
+ d->vertices[i]->x = FloatToQ27Dot5(points[i].x());
+ d->vertices[i]->y = FloatToQ27Dot5(points[i].y());
+ }
+
+ int left = 0, right = 0;
+
+ int top = 0;
+ for (int i = 1; i < nPoints; ++i) {
+ if (d->vertices[i]->y < d->vertices[top]->y)
+ top = i;
+ }
+
+ left = (top + nPoints - 1) % nPoints;
+ right = (top + 1) % nPoints;
+
+ while (d->vertices[left]->x == d->vertices[top]->x && d->vertices[left]->y == d->vertices[top]->y && left != right)
+ left = (left + nPoints - 1) % nPoints;
+
+ while (d->vertices[right]->x == d->vertices[top]->x && d->vertices[right]->y == d->vertices[top]->y && left != right)
+ right = (right + 1) % nPoints;
+
+ if (left == right)
+ return;
+
+ int dir = 1;
+
+ Vertex dLeft = { d->vertices[top]->x - d->vertices[left]->x,
+ d->vertices[top]->y - d->vertices[left]->y };
+
+ Vertex dRight = { d->vertices[right]->x - d->vertices[top]->x,
+ d->vertices[right]->y - d->vertices[top]->y };
+
+ Q27Dot5 cross = dLeft.x * dRight.y - dLeft.y * dRight.x;
+
+ // flip direction if polygon is clockwise
+ if (cross < 0 || (cross == 0 && dLeft.x > 0)) {
+ qSwap(left, right);
+ dir = -1;
+ }
+
+ Vertex *lastLeft = d->vertices[top];
+ Vertex *lastRight = d->vertices[top];
+
+ QTessellator::Trapezoid trap;
+
+ while (lastLeft->y == d->vertices[left]->y && left != right) {
+ lastLeft = d->vertices[left];
+ left = (left + nPoints - dir) % nPoints;
+ }
+
+ while (lastRight->y == d->vertices[right]->y && left != right) {
+ lastRight = d->vertices[right];
+ right = (right + nPoints + dir) % nPoints;
+ }
+
+ while (true) {
+ trap.top = qMax(lastRight->y, lastLeft->y);
+ trap.bottom = qMin(d->vertices[left]->y, d->vertices[right]->y);
+ trap.topLeft = lastLeft;
+ trap.topRight = lastRight;
+ trap.bottomLeft = d->vertices[left];
+ trap.bottomRight = d->vertices[right];
+
+ if (trap.bottom > trap.top)
+ addTrap(trap);
+
+ if (left == right)
+ break;
+
+ if (d->vertices[right]->y < d->vertices[left]->y) {
+ do {
+ lastRight = d->vertices[right];
+ right = (right + nPoints + dir) % nPoints;
+ }
+ while (lastRight->y == d->vertices[right]->y && left != right);
+ } else {
+ do {
+ lastLeft = d->vertices[left];
+ left = (left + nPoints - dir) % nPoints;
+ }
+ while (lastLeft->y == d->vertices[left]->y && left != right);
+ }
+ }
+}
+
+// tessellates the stroke of the line from a_ to b_ with the given width and a flat cap
+void QTessellator::tessellateRect(const QPointF &a_, const QPointF &b_, qreal width)
+{
+ Vertex a = { FloatToQ27Dot5(a_.x()), FloatToQ27Dot5(a_.y()) };
+ Vertex b = { FloatToQ27Dot5(b_.x()), FloatToQ27Dot5(b_.y()) };
+
+ QPointF pa = a_, pb = b_;
+
+ if (a.y > b.y) {
+ qSwap(a, b);
+ qSwap(pa, pb);
+ }
+
+ Vertex delta = { b.x - a.x, b.y - a.y };
+
+ if (delta.x == 0 && delta.y == 0)
+ return;
+
+ qreal hw = 0.5 * width;
+
+ if (delta.x == 0) {
+ Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+ if (halfWidth == 0)
+ return;
+
+ Vertex topLeft = { a.x - halfWidth, a.y };
+ Vertex topRight = { a.x + halfWidth, a.y };
+ Vertex bottomLeft = { a.x - halfWidth, b.y };
+ Vertex bottomRight = { a.x + halfWidth, b.y };
+
+ QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+ addTrap(trap);
+ } else if (delta.y == 0) {
+ Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+ if (halfWidth == 0)
+ return;
+
+ if (a.x > b.x)
+ qSwap(a.x, b.x);
+
+ Vertex topLeft = { a.x, a.y - halfWidth };
+ Vertex topRight = { b.x, a.y - halfWidth };
+ Vertex bottomLeft = { a.x, a.y + halfWidth };
+ Vertex bottomRight = { b.x, a.y + halfWidth };
+
+ QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+ addTrap(trap);
+ } else {
+ QPointF perp(pb.y() - pa.y(), pa.x() - pb.x());
+ qreal length = qSqrt(perp.x() * perp.x() + perp.y() * perp.y());
+
+ if (qFuzzyIsNull(length))
+ return;
+
+ // need the half of the width
+ perp *= hw / length;
+
+ QPointF pta = pa + perp;
+ QPointF ptb = pa - perp;
+ QPointF ptc = pb - perp;
+ QPointF ptd = pb + perp;
+
+ Vertex ta = { FloatToQ27Dot5(pta.x()), FloatToQ27Dot5(pta.y()) };
+ Vertex tb = { FloatToQ27Dot5(ptb.x()), FloatToQ27Dot5(ptb.y()) };
+ Vertex tc = { FloatToQ27Dot5(ptc.x()), FloatToQ27Dot5(ptc.y()) };
+ Vertex td = { FloatToQ27Dot5(ptd.x()), FloatToQ27Dot5(ptd.y()) };
+
+ if (ta.y < tb.y) {
+ if (tb.y < td.y) {
+ QTessellator::Trapezoid top = { ta.y, tb.y, &ta, &tb, &ta, &td };
+ QTessellator::Trapezoid bottom = { td.y, tc.y, &tb, &tc, &td, &tc };
+ addTrap(top);
+ addTrap(bottom);
+
+ QTessellator::Trapezoid middle = { tb.y, td.y, &tb, &tc, &ta, &td };
+ addTrap(middle);
+ } else {
+ QTessellator::Trapezoid top = { ta.y, td.y, &ta, &tb, &ta, &td };
+ QTessellator::Trapezoid bottom = { tb.y, tc.y, &tb, &tc, &td, &tc };
+ addTrap(top);
+ addTrap(bottom);
+
+ if (tb.y != td.y) {
+ QTessellator::Trapezoid middle = { td.y, tb.y, &ta, &tb, &td, &tc };
+ addTrap(middle);
+ }
+ }
+ } else {
+ if (ta.y < tc.y) {
+ QTessellator::Trapezoid top = { tb.y, ta.y, &tb, &tc, &tb, &ta };
+ QTessellator::Trapezoid bottom = { tc.y, td.y, &tc, &td, &ta, &td };
+ addTrap(top);
+ addTrap(bottom);
+
+ QTessellator::Trapezoid middle = { ta.y, tc.y, &tb, &tc, &ta, &td };
+ addTrap(middle);
+ } else {
+ QTessellator::Trapezoid top = { tb.y, tc.y, &tb, &tc, &tb, &ta };
+ QTessellator::Trapezoid bottom = { ta.y, td.y, &tc, &td, &ta, &td };
+ addTrap(top);
+ addTrap(bottom);
+
+ if (ta.y != tc.y) {
+ QTessellator::Trapezoid middle = { tc.y, ta.y, &tc, &td, &tb, &ta };
+ addTrap(middle);
+ }
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qtessellator_p.h b/src/plugins/platforms/xcb/nativepainting/qtessellator_p.h
new file mode 100644
index 0000000000..65ae6bdc41
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qtessellator_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTESSELATOR_P_H
+#define QTESSELATOR_P_H
+
+#include <QPoint>
+#include <QRect>
+
+QT_BEGIN_NAMESPACE
+
+class QTessellatorPrivate;
+
+typedef int Q27Dot5;
+#define Q27Dot5ToDouble(i) ((i)/32.)
+#define FloatToQ27Dot5(i) (int)((i) * 32)
+#define IntToQ27Dot5(i) ((i) << 5)
+#define Q27Dot5ToXFixed(i) ((i) << 11)
+#define Q27Dot5Factor 32
+
+class QTessellator {
+public:
+ QTessellator();
+ virtual ~QTessellator();
+
+ QRectF tessellate(const QPointF *points, int nPoints);
+ void tessellateConvex(const QPointF *points, int nPoints);
+ void tessellateRect(const QPointF &a, const QPointF &b, qreal width);
+
+ void setWinding(bool w);
+
+ struct Vertex {
+ Q27Dot5 x;
+ Q27Dot5 y;
+ };
+ struct Trapezoid {
+ Q27Dot5 top;
+ Q27Dot5 bottom;
+ const Vertex *topLeft;
+ const Vertex *bottomLeft;
+ const Vertex *topRight;
+ const Vertex *bottomRight;
+ };
+ virtual void addTrap(const Trapezoid &trap) = 0;
+
+private:
+ friend class QTessellatorPrivate;
+ QTessellatorPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp
new file mode 100644
index 0000000000..ccb421d868
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbconnection.h"
+#include "qcolormap_x11_p.h"
+#include "qxcbnativepainting.h"
+#include "qt_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QXcbX11Data *qt_x11Data = Q_NULLPTR;
+
+void qt_xcb_native_x11_info_init(QXcbConnection *conn)
+{
+ qt_x11Data = new QXcbX11Data;
+ X11->display = static_cast<Display *>(conn->xlib_display());
+ X11->defaultScreen = DefaultScreen(X11->display);
+ X11->screenCount = ScreenCount(X11->display);
+
+ X11->screens = new QX11InfoData[X11->screenCount];
+ X11->argbVisuals = new Visual *[X11->screenCount];
+ X11->argbColormaps = new Colormap[X11->screenCount];
+
+ for (int s = 0; s < X11->screenCount; s++) {
+ QX11InfoData *screen = X11->screens + s;
+ //screen->ref = 1; // ensures it doesn't get deleted
+ screen->screen = s;
+
+ int widthMM = DisplayWidthMM(X11->display, s);
+ if (widthMM != 0) {
+ screen->dpiX = (DisplayWidth(X11->display, s) * 254 + widthMM * 5) / (widthMM * 10);
+ } else {
+ screen->dpiX = 72;
+ }
+
+ int heightMM = DisplayHeightMM(X11->display, s);
+ if (heightMM != 0) {
+ screen->dpiY = (DisplayHeight(X11->display, s) * 254 + heightMM * 5) / (heightMM * 10);
+ } else {
+ screen->dpiY = 72;
+ }
+
+ X11->argbVisuals[s] = 0;
+ X11->argbColormaps[s] = 0;
+ }
+
+ X11->use_xrender = conn->hasXRender() && !qEnvironmentVariableIsSet("QT_XCB_NATIVE_PAINTING_NO_XRENDER");
+
+#if QT_CONFIG(xrender)
+ memset(X11->solid_fills, 0, sizeof(X11->solid_fills));
+ for (int i = 0; i < X11->solid_fill_count; ++i)
+ X11->solid_fills[i].screen = -1;
+ memset(X11->pattern_fills, 0, sizeof(X11->pattern_fills));
+ for (int i = 0; i < X11->pattern_fill_count; ++i)
+ X11->pattern_fills[i].screen = -1;
+#endif
+
+ QXcbColormap::initialize();
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ // XRender is supported, let's see if we have a PictFormat for the
+ // default visual
+ XRenderPictFormat *format =
+ XRenderFindVisualFormat(X11->display,
+ (Visual *) QXcbX11Info::appVisual(X11->defaultScreen));
+
+ if (!format) {
+ X11->use_xrender = false;
+ }
+ }
+#endif // QT_CONFIG(xrender)
+}
+
+QVector<XRectangle> qt_region_to_xrectangles(const QRegion &r)
+{
+ const int numRects = r.rectCount();
+ const QVector<QRect> input = r.rects();
+ QVector<XRectangle> output(numRects);
+ for (int i = 0; i < numRects; ++i) {
+ const QRect &in = input[i];
+ XRectangle &out = output[i];
+ out.x = qMax(SHRT_MIN, in.x());
+ out.y = qMax(SHRT_MIN, in.y());
+ out.width = qMin((int)USHRT_MAX, in.width());
+ out.height = qMin((int)USHRT_MAX, in.height());
+ }
+ return output;
+}
+
+class QXcbX11InfoData : public QSharedData, public QX11InfoData
+{};
+
+QXcbX11Info::QXcbX11Info()
+ : d(Q_NULLPTR)
+{}
+
+QXcbX11Info::~QXcbX11Info()
+{}
+
+QXcbX11Info::QXcbX11Info(const QXcbX11Info &other)
+ : d(other.d)
+{}
+
+QXcbX11Info &QXcbX11Info::operator=(const QXcbX11Info &other)
+{
+ d = other.d;
+ return *this;
+}
+
+QXcbX11Info QXcbX11Info::fromScreen(int screen)
+{
+ QXcbX11InfoData *xd = new QXcbX11InfoData;
+ xd->screen = screen;
+ xd->depth = QXcbX11Info::appDepth(screen);
+ xd->cells = QXcbX11Info::appCells(screen);
+ xd->colormap = QXcbX11Info::appColormap(screen);
+ xd->defaultColormap = QXcbX11Info::appDefaultColormap(screen);
+ xd->visual = (Visual *)QXcbX11Info::appVisual(screen);
+ xd->defaultVisual = QXcbX11Info::appDefaultVisual(screen);
+
+ QXcbX11Info info;
+ info.d = xd;
+ return info;
+}
+
+void QXcbX11Info::setDepth(int depth)
+{
+ if (!d)
+ *this = fromScreen(appScreen());
+
+ d->depth = depth;
+}
+
+Display *QXcbX11Info::display()
+{
+ return X11 ? X11->display : 0;
+}
+
+int QXcbX11Info::screen() const
+{
+ return d ? d->screen : QXcbX11Info::appScreen();
+}
+
+int QXcbX11Info::depth() const
+{
+ return d ? d->depth : QXcbX11Info::appDepth();
+}
+
+Colormap QXcbX11Info::colormap() const
+{
+ return d ? d->colormap : QXcbX11Info::appColormap();
+}
+
+void *QXcbX11Info::visual() const
+{
+ return d ? d->visual : QXcbX11Info::appVisual();
+}
+
+void QXcbX11Info::setVisual(void *visual)
+{
+ if (!d)
+ *this = fromScreen(appScreen());
+
+ d->visual = (Visual *) visual;
+}
+
+int QXcbX11Info::appScreen()
+{
+ return X11 ? X11->defaultScreen : 0;
+}
+
+int QXcbX11Info::appDepth(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].depth : 32;
+}
+
+int QXcbX11Info::appCells(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].cells : 0;
+}
+
+Colormap QXcbX11Info::appColormap(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].colormap : 0;
+}
+
+void *QXcbX11Info::appVisual(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].visual : 0;
+}
+
+Window QXcbX11Info::appRootWindow(int screen)
+{
+ return X11 ? RootWindow(X11->display, screen == -1 ? X11->defaultScreen : screen) : 0;
+}
+
+bool QXcbX11Info::appDefaultColormap(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultColormap : true;
+}
+
+bool QXcbX11Info::appDefaultVisual(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultVisual : true;
+}
+
+int QXcbX11Info::appDpiX(int screen)
+{
+ if (!X11)
+ return 75;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return 0;
+ return X11->screens[screen].dpiX;
+}
+
+int QXcbX11Info::appDpiY(int screen)
+{
+ if (!X11)
+ return 75;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return 0;
+ return X11->screens[screen].dpiY;
+}
+
+#if QT_CONFIG(xrender)
+Picture QXcbX11Data::getSolidFill(int screen, const QColor &c)
+{
+ if (!X11->use_xrender)
+ return XNone;
+
+ XRenderColor color = preMultiply(c);
+ for (int i = 0; i < X11->solid_fill_count; ++i) {
+ if (X11->solid_fills[i].screen == screen
+ && X11->solid_fills[i].color.alpha == color.alpha
+ && X11->solid_fills[i].color.red == color.red
+ && X11->solid_fills[i].color.green == color.green
+ && X11->solid_fills[i].color.blue == color.blue)
+ return X11->solid_fills[i].picture;
+ }
+ // none found, replace one
+ int i = qrand() % 16;
+
+ if (X11->solid_fills[i].screen != screen && X11->solid_fills[i].picture) {
+ XRenderFreePicture (X11->display, X11->solid_fills[i].picture);
+ X11->solid_fills[i].picture = 0;
+ }
+
+ if (!X11->solid_fills[i].picture) {
+ Pixmap pixmap = XCreatePixmap (X11->display, RootWindow (X11->display, screen), 1, 1, 32);
+ XRenderPictureAttributes attrs;
+ attrs.repeat = True;
+ X11->solid_fills[i].picture = XRenderCreatePicture (X11->display, pixmap,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32),
+ CPRepeat, &attrs);
+ XFreePixmap (X11->display, pixmap);
+ }
+
+ X11->solid_fills[i].color = color;
+ X11->solid_fills[i].screen = screen;
+ XRenderFillRectangle (X11->display, PictOpSrc, X11->solid_fills[i].picture, &color, 0, 0, 1, 1);
+ return X11->solid_fills[i].picture;
+}
+
+XRenderColor QXcbX11Data::preMultiply(const QColor &c)
+{
+ XRenderColor color;
+ const uint A = c.alpha(),
+ R = c.red(),
+ G = c.green(),
+ B = c.blue();
+ color.alpha = (A | A << 8);
+ color.red = (R | R << 8) * color.alpha / 0x10000;
+ color.green = (G | G << 8) * color.alpha / 0x10000;
+ color.blue = (B | B << 8) * color.alpha / 0x10000;
+ return color;
+}
+#endif // QT_CONFIG(xrender)
+
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h
new file mode 100644
index 0000000000..f3011286c9
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBNATIVEPAINTING_H
+#define QXCBNATIVEPAINTING_H
+
+#include <QSharedDataPointer>
+#include "qt_x11_p.h"
+
+typedef struct _FcPattern FcPattern;
+typedef unsigned long XID;
+typedef XID Colormap;
+typedef XID Window;
+typedef struct _XDisplay Display;
+
+QT_BEGIN_NAMESPACE
+
+class QXcbConnection;
+class QPixmap;
+
+void qt_xcb_native_x11_info_init(QXcbConnection *conn);
+QVector<XRectangle> qt_region_to_xrectangles(const QRegion &r);
+
+class QXcbX11InfoData;
+class QXcbX11Info
+{
+public:
+ QXcbX11Info();
+ ~QXcbX11Info();
+ QXcbX11Info(const QXcbX11Info &other);
+ QXcbX11Info &operator=(const QXcbX11Info &other);
+
+ static QXcbX11Info fromScreen(int screen);
+ static Display *display();
+
+ int depth() const;
+ void setDepth(int depth);
+
+ int screen() const;
+ Colormap colormap() const;
+
+ void *visual() const;
+ void setVisual(void *visual);
+
+ static int appScreen();
+ static int appDepth(int screen = -1);
+ static int appCells(int screen = -1);
+ static Colormap appColormap(int screen = -1);
+ static void *appVisual(int screen = -1);
+ static Window appRootWindow(int screen = -1);
+ static bool appDefaultColormap(int screen = -1);
+ static bool appDefaultVisual(int screen = -1);
+ static int appDpiX(int screen = -1);
+ static int appDpiY(int screen = -1);
+
+private:
+ QSharedDataPointer<QXcbX11InfoData> d;
+
+ friend class QX11PaintEngine;
+ friend class QX11PlatformPixmap;
+ friend void qt_x11SetScreen(QPixmap &pixmap, int screen);
+};
+
+QT_END_NAMESPACE
+
+#endif // QXCBNATIVEPAINTING_H
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
index a419caf0fc..420d1ac7c5 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -150,8 +150,6 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI
, m_gc_drawable(0)
, m_xcb_pixmap(0)
{
- Q_XCB_NOOP(connection());
-
const xcb_format_t *fmt = connection()->formatForDepth(depth);
Q_ASSERT(fmt);
@@ -206,11 +204,11 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI
if (!hasShm()) {
m_xcb_pixmap = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_pixmap(xcb_connection(),
- m_xcb_image->depth,
- m_xcb_pixmap,
- screen->screen()->root,
- m_xcb_image->width, m_xcb_image->height));
+ xcb_create_pixmap(xcb_connection(),
+ m_xcb_image->depth,
+ m_xcb_pixmap,
+ screen->screen()->root,
+ m_xcb_image->width, m_xcb_image->height);
}
}
@@ -234,13 +232,13 @@ bool QXcbShmImage::scroll(const QRegion &area, int dx, int dy)
const QRect bounds(QPoint(0, 0), size());
for (const QRect &src : area) {
const QRect dst = src.translated(delta).intersected(bounds);
- Q_XCB_CALL(xcb_copy_area(xcb_connection(),
- m_xcb_pixmap,
- m_xcb_pixmap,
- m_gc,
- src.x(), src.y(),
- dst.x(), dst.y(),
- dst.width(), dst.height()));
+ xcb_copy_area(xcb_connection(),
+ m_xcb_pixmap,
+ m_xcb_pixmap,
+ m_gc,
+ src.x(), src.y(),
+ dst.x(), dst.y(),
+ dst.width(), dst.height());
}
}
@@ -251,7 +249,7 @@ void QXcbShmImage::destroy()
{
const int segmentSize = m_xcb_image ? (m_xcb_image->stride * m_xcb_image->height) : 0;
if (segmentSize && m_shm_info.shmaddr)
- Q_XCB_CALL(xcb_shm_detach(xcb_connection(), m_shm_info.shmseg));
+ xcb_shm_detach(xcb_connection(), m_shm_info.shmseg);
if (segmentSize) {
if (m_shm_info.shmaddr) {
@@ -265,12 +263,12 @@ void QXcbShmImage::destroy()
xcb_image_destroy(m_xcb_image);
if (m_gc)
- Q_XCB_CALL(xcb_free_gc(xcb_connection(), m_gc));
+ xcb_free_gc(xcb_connection(), m_gc);
delete m_graphics_buffer;
m_graphics_buffer = Q_NULLPTR;
if (m_xcb_pixmap) {
- Q_XCB_CALL(xcb_free_pixmap(xcb_connection(), m_xcb_pixmap));
+ xcb_free_pixmap(xcb_connection(), m_xcb_pixmap);
m_xcb_pixmap = 0;
}
}
@@ -279,13 +277,13 @@ void QXcbShmImage::ensureGC(xcb_drawable_t dst)
{
if (m_gc_drawable != dst) {
if (m_gc)
- Q_XCB_CALL(xcb_free_gc(xcb_connection(), m_gc));
+ xcb_free_gc(xcb_connection(), m_gc);
static const uint32_t mask = XCB_GC_GRAPHICS_EXPOSURES;
static const uint32_t values[] = { 0 };
m_gc = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_gc(xcb_connection(), m_gc, dst, mask, values));
+ xcb_create_gc(xcb_connection(), m_gc, dst, mask, values);
m_gc_drawable = dst;
}
@@ -425,10 +423,7 @@ void QXcbShmImage::setClip(const QRegion &region)
if (region.isEmpty()) {
static const uint32_t mask = XCB_GC_CLIP_MASK;
static const uint32_t values[] = { XCB_NONE };
- Q_XCB_CALL(xcb_change_gc(xcb_connection(),
- m_gc,
- mask,
- values));
+ xcb_change_gc(xcb_connection(), m_gc, mask, values);
} else {
const QVector<QRect> qrects = region.rects();
QVector<xcb_rectangle_t> xcb_rects(qrects.size());
@@ -440,18 +435,16 @@ void QXcbShmImage::setClip(const QRegion &region)
xcb_rects[i].height = qrects[i].height();
}
- Q_XCB_CALL(xcb_set_clip_rectangles(xcb_connection(),
- XCB_CLIP_ORDERING_YX_BANDED,
- m_gc,
- 0, 0,
- xcb_rects.size(), xcb_rects.constData()));
+ xcb_set_clip_rectangles(xcb_connection(),
+ XCB_CLIP_ORDERING_YX_BANDED,
+ m_gc,
+ 0, 0,
+ xcb_rects.size(), xcb_rects.constData());
}
}
void QXcbShmImage::put(xcb_drawable_t dst, const QRegion &region, const QPoint &offset)
{
- Q_XCB_NOOP(connection());
-
ensureGC(dst);
setClip(region);
@@ -460,33 +453,32 @@ void QXcbShmImage::put(xcb_drawable_t dst, const QRegion &region, const QPoint &
const QRect source = bounds.translated(offset);
if (hasShm()) {
- Q_XCB_CALL(xcb_shm_put_image(xcb_connection(),
- dst,
- m_gc,
- m_xcb_image->width,
- m_xcb_image->height,
- source.x(), source.y(),
- source.width(), source.height(),
- target.x(), target.y(),
- m_xcb_image->depth,
- m_xcb_image->format,
- 0, // send event?
- m_shm_info.shmseg,
- m_xcb_image->data - m_shm_info.shmaddr));
+ xcb_shm_put_image(xcb_connection(),
+ dst,
+ m_gc,
+ m_xcb_image->width,
+ m_xcb_image->height,
+ source.x(), source.y(),
+ source.width(), source.height(),
+ target.x(), target.y(),
+ m_xcb_image->depth,
+ m_xcb_image->format,
+ 0, // send event?
+ m_shm_info.shmseg,
+ m_xcb_image->data - m_shm_info.shmaddr);
m_dirtyShm |= region.translated(offset);
} else {
flushPixmap(region);
- Q_XCB_CALL(xcb_copy_area(xcb_connection(),
- m_xcb_pixmap,
- dst,
- m_gc,
- source.x(), source.y(),
- target.x(), target.y(),
- source.width(), source.height()));
+ xcb_copy_area(xcb_connection(),
+ m_xcb_pixmap,
+ dst,
+ m_gc,
+ source.x(), source.y(),
+ target.x(), target.y(),
+ source.width(), source.height());
}
setClip(QRegion());
- Q_XCB_NOOP(connection());
}
void QXcbShmImage::preparePaint(const QRegion &region)
@@ -592,8 +584,6 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
if (bounds.isNull())
return;
- Q_XCB_NOOP(connection());
-
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
if (!platformWindow) {
qWarning("QXcbBackingStore::flush: QWindow has no platform window (QTBUG-32681)");
@@ -602,8 +592,6 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
m_image->put(platformWindow->xcb_window(), clipped, offset);
- Q_XCB_NOOP(connection());
-
if (platformWindow->needsSync())
platformWindow->updateSyncRequestCounter();
else
@@ -612,12 +600,10 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
#ifndef QT_NO_OPENGL
void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
- QPlatformTextureList *textures, QOpenGLContext *context,
+ QPlatformTextureList *textures,
bool translucentBackground)
{
- QPlatformBackingStore::composeAndFlush(window, region, offset, textures, context, translucentBackground);
-
- Q_XCB_NOOP(connection());
+ QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground);
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
if (platformWindow->needsSync()) {
@@ -632,7 +618,6 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &)
{
if (m_image && size == m_image->size())
return;
- Q_XCB_NOOP(connection());
QXcbScreen *screen = static_cast<QXcbScreen *>(window()->screen()->handle());
QPlatformWindow *pw = window()->handle();
@@ -649,7 +634,6 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &)
if (win->imageNeedsRgbSwap()) {
m_rgbImage = QImage(size, win->imageFormat());
}
- Q_XCB_NOOP(connection());
}
bool QXcbBackingStore::scroll(const QRegion &area, int dx, int dy)
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.h b/src/plugins/platforms/xcb/qxcbbackingstore.h
index 94b5994004..2e8fbfb7fa 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.h
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.h
@@ -61,7 +61,7 @@ public:
void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
#ifndef QT_NO_OPENGL
void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
- QPlatformTextureList *textures, QOpenGLContext *context,
+ QPlatformTextureList *textures,
bool translucentBackground) override;
#endif
QImage toImage() const override;
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index 01b3bca0d2..30ab669432 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -278,22 +278,22 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c)
#ifndef QT_NO_DEBUG
QByteArray ba("Qt clipboard window");
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_owner,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_owner,
+ atom(QXcbAtom::_NET_WM_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
#endif
if (connection()->hasXFixes()) {
const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
- Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, XCB_ATOM_PRIMARY, mask));
- Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask));
+ xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, XCB_ATOM_PRIMARY, mask);
+ xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask);
}
}
@@ -305,8 +305,7 @@ QXcbClipboard::~QXcbClipboard()
m_timestamp[QClipboard::Selection] != XCB_CURRENT_TIME) {
// First we check if there is a clipboard manager.
- xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
- xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
if (reply && reply->owner != XCB_NONE) {
// we delete the property so the manager saves all TARGETS.
xcb_delete_property(xcb_connection(), m_owner, atom(QXcbAtom::_QT_SELECTION));
@@ -320,7 +319,6 @@ QXcbClipboard::~QXcbClipboard()
"clipboard manager in a reasonable time");
}
}
- free(reply);
}
if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection])
@@ -459,26 +457,26 @@ xcb_window_t QXcbClipboard::requestor() const
QXcbClipboard *that = const_cast<QXcbClipboard *>(this);
xcb_window_t window = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- XCB_COPY_FROM_PARENT, // depth -- same as root
- window, // window id
- platformScreen->screen()->root, // parent window id
- x, y, w, h,
- 0, // border width
- XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
- platformScreen->screen()->root_visual, // visual
- 0, // value mask
- 0)); // value list
+ xcb_create_window(xcb_connection(),
+ XCB_COPY_FROM_PARENT, // depth -- same as root
+ window, // window id
+ platformScreen->screen()->root, // parent window id
+ x, y, w, h,
+ 0, // border width
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
+ platformScreen->screen()->root_visual, // visual
+ 0, // value mask
+ 0); // value list
#ifndef QT_NO_DEBUG
QByteArray ba("Qt clipboard requestor window");
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- window,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ window,
+ atom(QXcbAtom::_NET_WM_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
#endif
uint32_t mask = XCB_EVENT_MASK_PROPERTY_CHANGE;
@@ -759,17 +757,14 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
format = &dummy_format;
// Don't read anything, just get the size of the property data
- xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
if (!reply || reply->type == XCB_NONE) {
- free(reply);
buffer->resize(0);
return false;
}
*type = reply->type;
*format = reply->format;
bytes_left = reply->bytes_after;
- free(reply);
int offset = 0, buffer_offset = 0;
@@ -784,17 +779,15 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
while (bytes_left) {
// more to read...
- xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4));
- reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
- if (!reply || reply->type == XCB_NONE) {
- free(reply);
+ reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4);
+ if (!reply || reply->type == XCB_NONE)
break;
- }
+
*type = reply->type;
*format = reply->format;
bytes_left = reply->bytes_after;
- char *data = (char *)xcb_get_property_value(reply);
- int length = xcb_get_property_value_length(reply);
+ char *data = (char *)xcb_get_property_value(reply.get());
+ int length = xcb_get_property_value_length(reply.get());
// Here we check if we get a buffer overflow and tries to
// recover -- this shouldn't normally happen, but it doesn't
@@ -814,7 +807,6 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
// offset is specified in 32-bit multiples
offset += length / 4;
}
- free(reply);
}
}
@@ -891,13 +883,9 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t win, int
return e;
if (checkManager) {
- xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
- xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(xcb_connection(), cookie, 0);
- if (!reply || reply->owner == XCB_NONE) {
- free(reply);
+ auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
+ if (!reply || reply->owner == XCB_NONE)
return 0;
- }
- free(reply);
}
// process other clipboard events, since someone is probably requesting data from us
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index d36a14b920..536c709dbe 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -109,6 +109,9 @@ Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input")
Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices")
Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events")
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
+Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events")
+Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb") // for general (uncategorized) XCB logging
+Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker")
// this event type was added in libxcb 1.10,
// but we support also older version
@@ -148,8 +151,18 @@ static const char * const xcbConnectionErrors[] = {
"Error during FD passing" /* XCB_CONN_CLOSED_FDPASSING_FAILED */
};
-static int nullErrorHandler(Display *, XErrorEvent *)
+static int nullErrorHandler(Display *dpy, XErrorEvent *err)
{
+#ifndef Q_XCB_DEBUG
+ Q_UNUSED(dpy);
+ Q_UNUSED(err);
+#else
+ const int buflen = 1024;
+ char buf[buflen];
+
+ XGetErrorText(dpy, err->error_code, buf, buflen);
+ fprintf(stderr, "X Error: serial %lu error %d %s\n", err->serial, (int) err->error_code, buf);
+#endif
return 0;
}
@@ -243,11 +256,8 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
} else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
// New XRandR output is available and it's enabled
if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
- xcb_randr_get_output_info_cookie_t outputInfoCookie =
- xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp);
- QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo(
- xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL));
-
+ auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
+ output.output, output.config_timestamp);
// Find a fake screen
const auto scrs = virtualDesktop->screens();
for (QPlatformScreen *scr : scrs) {
@@ -261,12 +271,12 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
if (screen) {
QString nameWas = screen->name();
// Transform the fake screen into a physical screen
- screen->setOutput(output.output, outputInfo.data());
+ screen->setOutput(output.output, outputInfo.get());
updateScreen(screen, output);
qCDebug(lcQpaScreen) << "output" << screen->name()
<< "is connected and enabled; was fake:" << nameWas;
} else {
- screen = createScreen(virtualDesktop, output, outputInfo.data());
+ screen = createScreen(virtualDesktop, output, outputInfo.get());
qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled";
}
QHighDpiScaling::updateHighDpiScaling();
@@ -274,10 +284,8 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
} else if (screen) {
if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
// Screen has been disabled
- xcb_randr_get_output_info_cookie_t outputInfoCookie =
- xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp);
- QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo(
- xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL));
+ auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
+ output.output, output.config_timestamp);
if (outputInfo->crtc == XCB_NONE) {
qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled";
destroyScreen(screen);
@@ -299,16 +307,10 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
{
- xcb_generic_error_t *error = 0;
- xcb_randr_get_output_primary_cookie_t primaryCookie =
- xcb_randr_get_output_primary(xcb_connection(), rootWindow);
- QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary (
- xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error));
- if (!primary || error) {
+ auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), rootWindow);
+ if (!primary)
qWarning("failed to get the primary output of the screen");
- free(error);
- error = NULL;
- }
+
const bool isPrimary = primary ? (primary->output == output) : false;
return isPrimary;
@@ -404,72 +406,59 @@ void QXcbConnection::initializeScreens()
m_virtualDesktops.append(virtualDesktop);
QList<QPlatformScreen *> siblings;
if (has_randr_extension) {
- xcb_generic_error_t *error = NULL;
// RRGetScreenResourcesCurrent is fast but it may return nothing if the
// configuration is not initialized wrt to the hardware. We should call
// RRGetScreenResources in this case.
- QScopedPointer<xcb_randr_get_screen_resources_reply_t, QScopedPointerPodDeleter> resources;
- xcb_randr_get_screen_resources_current_cookie_t resourcesCookie =
- xcb_randr_get_screen_resources_current(xcb_connection(), xcbScreen->root);
- QScopedPointer<xcb_randr_get_screen_resources_current_reply_t, QScopedPointerPodDeleter> resources_current(
- xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, &error));
- if (!resources_current || error) {
+ auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
+ xcb_connection(), xcbScreen->root);
+ if (!resources_current) {
qWarning("failed to get the current screen resources");
- free(error);
} else {
xcb_timestamp_t timestamp = 0;
xcb_randr_output_t *outputs = Q_NULLPTR;
- int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.data());
+ int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get());
if (outputCount) {
timestamp = resources_current->config_timestamp;
- outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.data());
+ outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get());
} else {
- xcb_randr_get_screen_resources_cookie_t resourcesCookie =
- xcb_randr_get_screen_resources(xcb_connection(), xcbScreen->root);
- resources.reset(xcb_randr_get_screen_resources_reply(xcb_connection(), resourcesCookie, &error));
- if (!resources || error) {
+ auto resources = Q_XCB_REPLY(xcb_randr_get_screen_resources,
+ xcb_connection(), xcbScreen->root);
+ if (!resources) {
qWarning("failed to get the screen resources");
- free(error);
} else {
timestamp = resources->config_timestamp;
- outputCount = xcb_randr_get_screen_resources_outputs_length(resources.data());
- outputs = xcb_randr_get_screen_resources_outputs(resources.data());
+ outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get());
+ outputs = xcb_randr_get_screen_resources_outputs(resources.get());
}
}
if (outputCount) {
- xcb_randr_get_output_primary_cookie_t primaryCookie =
- xcb_randr_get_output_primary(xcb_connection(), xcbScreen->root);
- QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary(
- xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error));
- if (!primary || error) {
+ auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
+ if (!primary) {
qWarning("failed to get the primary output of the screen");
- free(error);
} else {
for (int i = 0; i < outputCount; i++) {
- QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> output(
- xcb_randr_get_output_info_reply(xcb_connection(),
- xcb_randr_get_output_info_unchecked(xcb_connection(), outputs[i], timestamp), NULL));
-
+ auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
+ xcb_connection(), outputs[i], timestamp);
// Invalid, disconnected or disabled output
- if (output == NULL)
+ if (!output)
continue;
if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
- QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.data()),
- xcb_randr_get_output_info_name_length(output.data()))));
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
continue;
}
if (output->crtc == XCB_NONE) {
qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
- QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.data()),
- xcb_randr_get_output_info_name_length(output.data()))));
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
continue;
}
- QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.data());
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get());
siblings << screen;
m_screens << screen;
@@ -492,12 +481,9 @@ void QXcbConnection::initializeScreens()
}
} else if (has_xinerama_extension) {
// Xinerama is available
- xcb_xinerama_query_screens_cookie_t cookie = xcb_xinerama_query_screens(m_connection);
- xcb_xinerama_query_screens_reply_t *screens = xcb_xinerama_query_screens_reply(m_connection,
- cookie,
- Q_NULLPTR);
+ auto screens = Q_XCB_REPLY(xcb_xinerama_query_screens, m_connection);
if (screens) {
- xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens);
+ xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens.get());
while (it.rem) {
xcb_xinerama_screen_info_t *screen_info = it.data;
QXcbScreen *screen = new QXcbScreen(this, virtualDesktop,
@@ -507,7 +493,6 @@ void QXcbConnection::initializeScreens()
m_screens << screen;
xcb_xinerama_screen_info_next(&it);
}
- free(screens);
}
}
if (siblings.isEmpty()) {
@@ -664,11 +649,6 @@ QXcbConnection::~QXcbConnection()
#ifndef QT_NO_DRAGANDDROP
delete m_drag;
#endif
-
-#if QT_CONFIG(xinput2)
- finalizeXInput2();
-#endif
-
if (m_reader && m_reader->isRunning()) {
sendConnectionEvent(QXcbAtom::_QT_CLOSE_CONNECTION);
m_reader->wait();
@@ -757,58 +737,73 @@ break;
} \
break;
-//#define XCB_EVENT_DEBUG
-
-void printXcbEvent(const char *message, xcb_generic_event_t *event)
-{
-#ifdef XCB_EVENT_DEBUG
-#define PRINT_XCB_EVENT(ev) \
- case ev: \
- qDebug("QXcbConnection: %s: %d - %s - sequence: %d", message, int(ev), #ev, event->sequence); \
- break;
-
- switch (event->response_type & ~0x80) {
- PRINT_XCB_EVENT(XCB_KEY_PRESS);
- PRINT_XCB_EVENT(XCB_KEY_RELEASE);
- PRINT_XCB_EVENT(XCB_BUTTON_PRESS);
- PRINT_XCB_EVENT(XCB_BUTTON_RELEASE);
- PRINT_XCB_EVENT(XCB_MOTION_NOTIFY);
- PRINT_XCB_EVENT(XCB_ENTER_NOTIFY);
- PRINT_XCB_EVENT(XCB_LEAVE_NOTIFY);
- PRINT_XCB_EVENT(XCB_FOCUS_IN);
- PRINT_XCB_EVENT(XCB_FOCUS_OUT);
- PRINT_XCB_EVENT(XCB_KEYMAP_NOTIFY);
- PRINT_XCB_EVENT(XCB_EXPOSE);
- PRINT_XCB_EVENT(XCB_GRAPHICS_EXPOSURE);
- PRINT_XCB_EVENT(XCB_NO_EXPOSURE);
- PRINT_XCB_EVENT(XCB_VISIBILITY_NOTIFY);
- PRINT_XCB_EVENT(XCB_CREATE_NOTIFY);
- PRINT_XCB_EVENT(XCB_DESTROY_NOTIFY);
- PRINT_XCB_EVENT(XCB_UNMAP_NOTIFY);
- PRINT_XCB_EVENT(XCB_MAP_NOTIFY);
- PRINT_XCB_EVENT(XCB_MAP_REQUEST);
- PRINT_XCB_EVENT(XCB_REPARENT_NOTIFY);
- PRINT_XCB_EVENT(XCB_CONFIGURE_NOTIFY);
- PRINT_XCB_EVENT(XCB_CONFIGURE_REQUEST);
- PRINT_XCB_EVENT(XCB_GRAVITY_NOTIFY);
- PRINT_XCB_EVENT(XCB_RESIZE_REQUEST);
- PRINT_XCB_EVENT(XCB_CIRCULATE_NOTIFY);
- PRINT_XCB_EVENT(XCB_CIRCULATE_REQUEST);
- PRINT_XCB_EVENT(XCB_PROPERTY_NOTIFY);
- PRINT_XCB_EVENT(XCB_SELECTION_CLEAR);
- PRINT_XCB_EVENT(XCB_SELECTION_REQUEST);
- PRINT_XCB_EVENT(XCB_SELECTION_NOTIFY);
- PRINT_XCB_EVENT(XCB_COLORMAP_NOTIFY);
- PRINT_XCB_EVENT(XCB_CLIENT_MESSAGE);
- PRINT_XCB_EVENT(XCB_MAPPING_NOTIFY);
- PRINT_XCB_EVENT(XCB_GE_GENERIC);
- default:
- qDebug("QXcbConnection: %s: unknown event - response_type: %d - sequence: %d", message, int(event->response_type & ~0x80), int(event->sequence));
+void QXcbConnection::printXcbEvent(const QLoggingCategory &log, const char *message,
+ xcb_generic_event_t *event) const
+{
+ quint8 response_type = event->response_type & ~0x80;
+ quint16 sequence = event->sequence;
+
+#define PRINT_AND_RETURN(name) { \
+ qCDebug(log, "%s | %s(%d) | sequence: %d", message, name, response_type, sequence); \
+ return; \
+}
+#define CASE_PRINT_AND_RETURN(name) case name : PRINT_AND_RETURN(#name);
+
+ switch (response_type) {
+ CASE_PRINT_AND_RETURN( XCB_KEY_PRESS );
+ CASE_PRINT_AND_RETURN( XCB_KEY_RELEASE );
+ CASE_PRINT_AND_RETURN( XCB_BUTTON_PRESS );
+ CASE_PRINT_AND_RETURN( XCB_BUTTON_RELEASE );
+ CASE_PRINT_AND_RETURN( XCB_MOTION_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_ENTER_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_LEAVE_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_FOCUS_IN );
+ CASE_PRINT_AND_RETURN( XCB_FOCUS_OUT );
+ CASE_PRINT_AND_RETURN( XCB_KEYMAP_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_EXPOSE );
+ CASE_PRINT_AND_RETURN( XCB_GRAPHICS_EXPOSURE );
+ CASE_PRINT_AND_RETURN( XCB_NO_EXPOSURE );
+ CASE_PRINT_AND_RETURN( XCB_VISIBILITY_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CREATE_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_DESTROY_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_UNMAP_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_MAP_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_MAP_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_REPARENT_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CONFIGURE_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CONFIGURE_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_GRAVITY_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_RESIZE_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_CIRCULATE_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CIRCULATE_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_PROPERTY_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_SELECTION_CLEAR );
+ CASE_PRINT_AND_RETURN( XCB_SELECTION_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_SELECTION_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_COLORMAP_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CLIENT_MESSAGE );
+ CASE_PRINT_AND_RETURN( XCB_MAPPING_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_GE_GENERIC );
}
-#else
- Q_UNUSED(message);
- Q_UNUSED(event);
-#endif
+ // XFixes
+ if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY)
+ PRINT_AND_RETURN("XCB_XFIXES_SELECTION_NOTIFY");
+ // XRandR
+ if (has_randr_extension) {
+ if (response_type == xrandr_first_event + XCB_RANDR_NOTIFY)
+ PRINT_AND_RETURN("XCB_RANDR_NOTIFY");
+ if (response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY)
+ PRINT_AND_RETURN("XCB_RANDR_SCREEN_CHANGE_NOTIFY");
+ }
+ // XKB
+ if (response_type == xkb_first_event)
+ PRINT_AND_RETURN("XCB_XKB_* event");
+
+ // UNKNOWN
+ qCDebug(log, "%s | unknown(%d) | sequence: %d", message, response_type, sequence);
+
+#undef PRINT_AND_RETURN
+#undef CASE_PRINT_AND_RETURN
}
const char *xcb_errors[] =
@@ -959,18 +954,6 @@ const char *xcb_protocol_request_codes[] =
"Unknown"
};
-#ifdef Q_XCB_DEBUG
-void QXcbConnection::log(const char *file, int line, int sequence)
-{
- QMutexLocker locker(&m_callLogMutex);
- CallInfo info;
- info.sequence = sequence;
- info.file = file;
- info.line = line;
- m_callLog << info;
-}
-#endif
-
void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
{
long result = 0;
@@ -986,26 +969,6 @@ void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
int(error->sequence), int(error->resource_id),
int(error->major_code), xcb_protocol_request_codes[clamped_major_code],
int(error->minor_code));
-#ifdef Q_XCB_DEBUG
- QMutexLocker locker(&m_callLogMutex);
- int i = 0;
- for (; i < m_callLog.size(); ++i) {
- if (m_callLog.at(i).sequence == error->sequence) {
- qDebug("Caused by: %s:%d", m_callLog.at(i).file.constData(), m_callLog.at(i).line);
- break;
- } else if (m_callLog.at(i).sequence > error->sequence) {
- qDebug("Caused some time before: %s:%d", m_callLog.at(i).file.constData(),
- m_callLog.at(i).line);
- if (i > 0)
- qDebug("and after: %s:%d", m_callLog.at(i-1).file.constData(),
- m_callLog.at(i-1).line);
- break;
- }
- }
- if (i == m_callLog.size() && !m_callLog.isEmpty())
- qDebug("Caused some time after: %s:%d", qAsConst(m_callLog).first().file.constData(),
- qAsConst(m_callLog).first().line);
-#endif
}
static Qt::MouseButtons translateMouseButtons(int s)
@@ -1075,17 +1038,6 @@ namespace {
void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
{
-#ifdef Q_XCB_DEBUG
- {
- QMutexLocker locker(&m_callLogMutex);
- int i = 0;
- for (; i < m_callLog.size(); ++i)
- if (m_callLog.at(i).sequence >= event->sequence)
- break;
- m_callLog.remove(0, i);
- }
-#endif
-
long result = 0;
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
bool handled = dispatcher && dispatcher->filterNativeEvent(m_nativeInterface->genericEventFilterType(), event, &result);
@@ -1097,34 +1049,33 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
case XCB_EXPOSE:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
- // press/release/motion is only delivered here when XI 2.2+ is _not_ in use
case XCB_BUTTON_PRESS: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
m_keyboard->updateXKBStateFromCore(ev->state);
// the event explicitly contains the state of the three first buttons,
// the rest we need to manage ourselves
- m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
- m_buttons |= translateMouseButton(ev->detail);
+ m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
+ m_buttonState |= translateMouseButton(ev->detail);
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, "legacy mouse press, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
+ qCDebug(lcQpaXInputEvents, "legacy mouse press, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttonState));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
}
case XCB_BUTTON_RELEASE: {
xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event;
m_keyboard->updateXKBStateFromCore(ev->state);
- m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
- m_buttons &= ~translateMouseButton(ev->detail);
+ m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
+ m_buttonState &= ~translateMouseButton(ev->detail);
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, "legacy mouse release, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
+ qCDebug(lcQpaXInputEvents, "legacy mouse release, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttonState));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
}
case XCB_MOTION_NOTIFY: {
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
m_keyboard->updateXKBStateFromCore(ev->state);
- m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
+ m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "legacy mouse move %d,%d button %d state %X", ev->event_x, ev->event_y,
- ev->detail, static_cast<unsigned int>(m_buttons));
+ ev->detail, static_cast<unsigned int>(m_buttonState));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
}
@@ -1140,14 +1091,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
handleClientMessageEvent((xcb_client_message_event_t *)event);
break;
case XCB_ENTER_NOTIFY:
-#ifdef XCB_USE_XINPUT22
- if (isAtLeastXI22() && xi2MouseEvents())
+#if QT_CONFIG(xinput2)
+ if (hasXInput2() && !xi2MouseEventsDisabled())
break;
#endif
HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
case XCB_LEAVE_NOTIFY:
-#ifdef XCB_USE_XINPUT22
- if (isAtLeastXI22() && xi2MouseEvents())
+#if QT_CONFIG(xinput2)
+ if (hasXInput2() && !xi2MouseEventsDisabled())
break;
#endif
m_keyboard->updateXKBStateFromCore(((xcb_leave_notify_event_t *)event)->state);
@@ -1212,7 +1163,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
#if QT_CONFIG(xinput2)
case XCB_GE_GENERIC:
// Here the windowEventListener is invoked from xi2HandleEvent()
- if (m_xi2Enabled && isXIEvent(event, m_xiOpCode))
+ if (hasXInput2() && isXIEvent(event, m_xiOpCode))
xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
break;
#endif
@@ -1223,7 +1174,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
}
if (!handled) {
- if (response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
+ if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
xcb_xfixes_selection_notify_event_t *notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(event);
setTime(notify_event->timestamp);
#ifndef QT_NO_CLIPBOARD
@@ -1275,10 +1226,10 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
if (!handled && m_glIntegration)
handled = m_glIntegration->handleXcbEvent(event, response_type);
- if (handled)
- printXcbEvent("Handled XCB event", event);
- else
- printXcbEvent("Unhandled XCB event", event);
+#if 0
+ if (Q_UNLIKELY(lcQpaEvents().isDebugEnabled()))
+ printXcbEvent(lcQpaEvents(), handled ? "Handled" : "Unhandled", event);
+#endif
}
void QXcbConnection::addPeekFunc(PeekFunc f)
@@ -1286,6 +1237,95 @@ void QXcbConnection::addPeekFunc(PeekFunc f)
m_peekFuncs.append(f);
}
+qint32 QXcbConnection::generatePeekerId()
+{
+ qint32 peekerId = m_peekerIdSource++;
+ m_peekerToCachedIndex.insert(peekerId, 0);
+ return peekerId;
+}
+
+bool QXcbConnection::removePeekerId(qint32 peekerId)
+{
+ if (!m_peekerToCachedIndex.contains(peekerId)) {
+ qCWarning(lcQpaXcb, "failed to remove unknown peeker id: %d", peekerId);
+ return false;
+ }
+ m_peekerToCachedIndex.remove(peekerId);
+ if (m_peekerToCachedIndex.isEmpty()) {
+ m_peekerIdSource = 0; // Once the hash becomes empty, we can start reusing IDs
+ m_peekerIndexCacheDirty = false;
+ }
+ return true;
+}
+
+bool QXcbConnection::peekEventQueue(PeekerCallback peeker, void *peekerData,
+ PeekOptions option, qint32 peekerId)
+{
+ bool peekerIdProvided = peekerId != -1;
+ if (peekerIdProvided && !m_peekerToCachedIndex.contains(peekerId)) {
+ qCWarning(lcQpaXcb, "failed to find index for unknown peeker id: %d", peekerId);
+ return false;
+ }
+
+ bool peekFromCachedIndex = option.testFlag(PeekOption::PeekFromCachedIndex);
+ if (peekFromCachedIndex && !peekerIdProvided) {
+ qCWarning(lcQpaXcb, "PeekOption::PeekFromCachedIndex requires peeker id");
+ return false;
+ }
+
+ if (peekerIdProvided && m_peekerIndexCacheDirty) {
+ // When the main event loop has flushed the buffered XCB events into the window
+ // system event queue, the cached indices are not valid anymore and need reset.
+ auto it = m_peekerToCachedIndex.begin();
+ while (it != m_peekerToCachedIndex.constEnd()) {
+ (*it) = 0;
+ ++it;
+ }
+ m_peekerIndexCacheDirty = false;
+ }
+
+ qint32 peekerIndex = peekFromCachedIndex ? m_peekerToCachedIndex.value(peekerId) : 0;
+ qint32 startingIndex = peekerIndex;
+ bool result = false;
+ m_mainEventLoopFlushedQueue = false;
+
+ QXcbEventArray *eventqueue = m_reader->lock();
+
+ if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) {
+ qCDebug(lcQpaPeeker, "[%d] peeker index: %d | mode: %s | queue size: %d", peekerId,
+ peekerIndex, peekFromCachedIndex ? "cache" : "start", eventqueue->size());
+ }
+ while (peekerIndex < eventqueue->size() && !result && !m_mainEventLoopFlushedQueue) {
+ xcb_generic_event_t *event = eventqueue->at(peekerIndex++);
+ if (!event)
+ continue;
+ if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) {
+ QString debug = QString((QLatin1String("[%1] peeking at index: %2")))
+ .arg(peekerId).arg(peekerIndex - 1);
+ printXcbEvent(lcQpaPeeker(), debug.toLatin1(), event);
+ }
+ // A peeker may call QCoreApplication::processEvents(), which has two implications:
+ // 1) We need to make the lock available for QXcbConnection::processXcbEvents(),
+ // otherwise we will deadlock;
+ // 2) QXcbConnection::processXcbEvents() will flush the queue we are currently
+ // looping through;
+ m_reader->unlock();
+ result = peeker(event, peekerData);
+ m_reader->lock();
+ }
+
+ m_reader->unlock();
+
+ if (peekerIdProvided && peekerIndex != startingIndex && !m_mainEventLoopFlushedQueue) {
+ auto it = m_peekerToCachedIndex.find(peekerId);
+ // Make sure that a peeker callback did not remove the peeker id
+ if (it != m_peekerToCachedIndex.constEnd())
+ (*it) = peekerIndex;
+ }
+
+ return result;
+}
+
QXcbEventReader::QXcbEventReader(QXcbConnection *connection)
: m_connection(connection)
{
@@ -1400,10 +1440,10 @@ void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id)
const xcb_window_t eventListener = xcb_generate_id(m_connection);
xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
xcb_screen_t *screen = it.data;
- Q_XCB_CALL(xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
- eventListener, screen->root,
- 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
- screen->root_visual, 0, 0));
+ xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
+ eventListener, screen->root,
+ 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
+ screen->root_visual, 0, 0);
event.response_type = XCB_CLIENT_MESSAGE;
event.format = 32;
@@ -1412,8 +1452,8 @@ void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id)
event.type = atom(a);
event.data.data32[0] = id;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, eventListener, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event)));
- Q_XCB_CALL(xcb_destroy_window(m_connection, eventListener));
+ xcb_send_event(xcb_connection(), false, eventListener, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event));
+ xcb_destroy_window(m_connection, eventListener);
xcb_flush(xcb_connection());
}
@@ -1471,13 +1511,7 @@ xcb_timestamp_t QXcbConnection::getTimestamp()
xcb_window_t QXcbConnection::getSelectionOwner(xcb_atom_t atom) const
{
- xcb_connection_t *c = xcb_connection();
- xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom);
- xcb_get_selection_owner_reply_t *reply;
- reply = xcb_get_selection_owner_reply(c, cookie, 0);
- xcb_window_t win = reply->owner;
- free(reply);
- return win;
+ return Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom)->owner;
}
xcb_window_t QXcbConnection::getQtSelectionOwner()
@@ -1487,16 +1521,16 @@ xcb_window_t QXcbConnection::getQtSelectionOwner()
int16_t x = 0, y = 0;
uint16_t w = 3, h = 3;
m_qtSelectionOwner = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- XCB_COPY_FROM_PARENT, // depth -- same as root
- m_qtSelectionOwner, // window id
- xcbScreen->root, // parent window id
- x, y, w, h,
- 0, // border width
- XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
- xcbScreen->root_visual, // visual
- 0, // value mask
- 0)); // value list
+ xcb_create_window(xcb_connection(),
+ XCB_COPY_FROM_PARENT, // depth -- same as root
+ m_qtSelectionOwner, // window id
+ xcbScreen->root, // parent window id
+ x, y, w, h,
+ 0, // border width
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
+ xcbScreen->root_visual, // visual
+ 0, // value mask
+ 0); // value list
}
return m_qtSelectionOwner;
}
@@ -1512,47 +1546,47 @@ xcb_window_t QXcbConnection::clientLeader()
if (m_clientLeader == 0) {
m_clientLeader = xcb_generate_id(xcb_connection());
QXcbScreen *screen = primaryScreen();
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- XCB_COPY_FROM_PARENT,
- m_clientLeader,
- screen->root(),
- 0, 0, 1, 1,
- 0,
- XCB_WINDOW_CLASS_INPUT_OUTPUT,
- screen->screen()->root_visual,
- 0, 0));
+ xcb_create_window(xcb_connection(),
+ XCB_COPY_FROM_PARENT,
+ m_clientLeader,
+ screen->root(),
+ 0, 0, 1, 1,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ screen->screen()->root_visual,
+ 0, 0);
#ifndef QT_NO_DEBUG
QByteArray ba("Qt client leader window");
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_clientLeader,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_clientLeader,
+ atom(QXcbAtom::_NET_WM_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
#endif
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_clientLeader,
- atom(QXcbAtom::WM_CLIENT_LEADER),
- XCB_ATOM_WINDOW,
- 32,
- 1,
- &m_clientLeader));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_clientLeader,
+ atom(QXcbAtom::WM_CLIENT_LEADER),
+ XCB_ATOM_WINDOW,
+ 32,
+ 1,
+ &m_clientLeader);
#if QT_CONFIG(xcb_sm)
// If we are session managed, inform the window manager about it
QByteArray session = qGuiApp->sessionId().toLatin1();
if (!session.isEmpty()) {
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_clientLeader,
- atom(QXcbAtom::SM_CLIENT_ID),
- XCB_ATOM_STRING,
- 8,
- session.length(),
- session.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_clientLeader,
+ atom(QXcbAtom::SM_CLIENT_ID),
+ XCB_ATOM_STRING,
+ 8,
+ session.length(),
+ session.constData());
}
#endif
}
@@ -1574,7 +1608,8 @@ void *QXcbConnection::createVisualInfoForDefaultVisualId() const
info.visualid = m_defaultVisualId;
int count = 0;
- XVisualInfo *retVisual = XGetVisualInfo(DISPLAY_FROM_XCB(this), VisualIDMask, &info, &count);
+ Display *dpy = static_cast<Display *>(connection()->xlib_display());
+ XVisualInfo *retVisual = XGetVisualInfo(dpy, VisualIDMask, &info, &count);
Q_ASSERT(count < 2);
return retVisual;
}
@@ -1629,14 +1664,15 @@ bool QXcbConnection::compressEvent(xcb_generic_event_t *event, int currentIndex,
#if QT_CONFIG(xinput2)
// compress XI_* events
if (responseType == XCB_GE_GENERIC) {
- if (!m_xi2Enabled)
+ if (!hasXInput2())
return false;
// compress XI_Motion, but not from tablet devices
if (isXIType(event, m_xiOpCode, XI_Motion)) {
#if QT_CONFIG(tabletevent)
xXIDeviceEvent *xdev = reinterpret_cast<xXIDeviceEvent *>(event);
- if (const_cast<QXcbConnection *>(this)->tabletDataForDevice(xdev->sourceid))
+ if (!QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents) &&
+ const_cast<QXcbConnection *>(this)->tabletDataForDevice(xdev->sourceid))
return false;
#endif // QT_CONFIG(tabletevent)
for (int j = nextIndex; j < eventqueue->size(); ++j) {
@@ -1736,6 +1772,8 @@ void QXcbConnection::processXcbEvents()
m_reader->unlock();
+ m_peekerIndexCacheDirty = m_mainEventLoopFlushedQueue = true;
+
// Indicate with a null event that the event the callbacks are waiting for
// is not in the queue currently.
for (PeekFunc f : qAsConst(m_peekFuncs))
@@ -2003,11 +2041,7 @@ xcb_atom_t QXcbConnection::internAtom(const char *name)
if (!name || *name == 0)
return XCB_NONE;
- xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xcb_connection(), false, strlen(name), name);
- xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookie, 0);
- int atom = reply->atom;
- free(reply);
- return atom;
+ return Q_XCB_REPLY(xcb_intern_atom, xcb_connection(), false, strlen(name), name)->atom;
}
QByteArray QXcbConnection::atomName(xcb_atom_t atom)
@@ -2015,18 +2049,12 @@ QByteArray QXcbConnection::atomName(xcb_atom_t atom)
if (!atom)
return QByteArray();
- xcb_generic_error_t *error = 0;
- xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name(xcb_connection(), atom));
- xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, &error);
- if (error) {
+ auto reply = Q_XCB_REPLY(xcb_get_atom_name, xcb_connection(), atom);
+ if (!reply)
qWarning() << "QXcbConnection::atomName: bad Atom" << atom;
- free(error);
- }
- if (reply) {
- QByteArray result(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply));
- free(reply);
- return result;
- }
+ else
+ return QByteArray(xcb_get_atom_name_name(reply.get()), xcb_get_atom_name_name_length(reply.get()));
+
return QByteArray();
}
@@ -2048,29 +2076,25 @@ const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const
void QXcbConnection::sync()
{
// from xcb_aux_sync
- xcb_get_input_focus_cookie_t cookie = Q_XCB_CALL(xcb_get_input_focus(xcb_connection()));
+ xcb_get_input_focus_cookie_t cookie = xcb_get_input_focus(xcb_connection());
free(xcb_get_input_focus_reply(xcb_connection(), cookie, 0));
}
void QXcbConnection::initializeXFixes()
{
- xcb_generic_error_t *error = 0;
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xfixes_id);
if (!reply || !reply->present)
return;
- xfixes_first_event = reply->first_event;
- xcb_xfixes_query_version_cookie_t xfixes_query_cookie = xcb_xfixes_query_version(m_connection,
- XCB_XFIXES_MAJOR_VERSION,
- XCB_XFIXES_MINOR_VERSION);
- xcb_xfixes_query_version_reply_t *xfixes_query = xcb_xfixes_query_version_reply (m_connection,
- xfixes_query_cookie, &error);
- if (!xfixes_query || error || xfixes_query->major_version < 2) {
+ auto xfixes_query = Q_XCB_REPLY(xcb_xfixes_query_version, m_connection,
+ XCB_XFIXES_MAJOR_VERSION,
+ XCB_XFIXES_MINOR_VERSION);
+ if (!xfixes_query || xfixes_query->major_version < 2) {
qWarning("QXcbConnection: Failed to initialize XFixes");
- free(error);
- xfixes_first_event = 0;
+ return;
}
- free(xfixes_query);
+ xfixes_first_event = reply->first_event;
+ has_xfixes = true;
}
void QXcbConnection::initializeXRender()
@@ -2080,17 +2104,14 @@ void QXcbConnection::initializeXRender()
if (!reply || !reply->present)
return;
- xcb_generic_error_t *error = 0;
- xcb_render_query_version_cookie_t xrender_query_cookie = xcb_render_query_version(m_connection,
- XCB_RENDER_MAJOR_VERSION,
- XCB_RENDER_MINOR_VERSION);
- xcb_render_query_version_reply_t *xrender_query = xcb_render_query_version_reply(m_connection,
- xrender_query_cookie, &error);
- if (!xrender_query || error || (xrender_query->major_version == 0 && xrender_query->minor_version < 5)) {
+ auto xrender_query = Q_XCB_REPLY(xcb_render_query_version, m_connection,
+ XCB_RENDER_MAJOR_VERSION,
+ XCB_RENDER_MINOR_VERSION);
+ if (!xrender_query || (xrender_query->major_version == 0 && xrender_query->minor_version < 5)) {
qWarning("QXcbConnection: Failed to initialize XRender");
- free(error);
+ return;
}
- free(xrender_query);
+ has_render_extension = true;
#endif
}
@@ -2102,21 +2123,16 @@ void QXcbConnection::initializeXRandr()
xrandr_first_event = reply->first_event;
- xcb_generic_error_t *error = 0;
- xcb_randr_query_version_cookie_t xrandr_query_cookie = xcb_randr_query_version(m_connection,
- XCB_RANDR_MAJOR_VERSION,
- XCB_RANDR_MINOR_VERSION);
+ auto xrandr_query = Q_XCB_REPLY(xcb_randr_query_version, m_connection,
+ XCB_RANDR_MAJOR_VERSION,
+ XCB_RANDR_MINOR_VERSION);
has_randr_extension = true;
- xcb_randr_query_version_reply_t *xrandr_query = xcb_randr_query_version_reply(m_connection,
- xrandr_query_cookie, &error);
- if (!xrandr_query || error || (xrandr_query->major_version < 1 || (xrandr_query->major_version == 1 && xrandr_query->minor_version < 2))) {
+ if (!xrandr_query || (xrandr_query->major_version < 1 || (xrandr_query->major_version == 1 && xrandr_query->minor_version < 2))) {
qWarning("QXcbConnection: Failed to initialize XRandr");
- free(error);
has_randr_extension = false;
}
- free(xrandr_query);
xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(m_setup);
for (; rootIter.rem; xcb_screen_next(&rootIter)) {
@@ -2136,14 +2152,8 @@ void QXcbConnection::initializeXinerama()
if (!reply || !reply->present)
return;
- xcb_generic_error_t *error = Q_NULLPTR;
- xcb_xinerama_is_active_cookie_t xinerama_query_cookie = xcb_xinerama_is_active(m_connection);
- xcb_xinerama_is_active_reply_t *xinerama_is_active = xcb_xinerama_is_active_reply(m_connection,
- xinerama_query_cookie,
- &error);
- has_xinerama_extension = xinerama_is_active && !error && xinerama_is_active->state;
- free(error);
- free(xinerama_is_active);
+ auto xinerama_is_active = Q_XCB_REPLY(xcb_xinerama_is_active, m_connection);
+ has_xinerama_extension = xinerama_is_active && xinerama_is_active->state;
}
void QXcbConnection::initializeXShape()
@@ -2153,16 +2163,13 @@ void QXcbConnection::initializeXShape()
return;
has_shape_extension = true;
- xcb_shape_query_version_cookie_t cookie = xcb_shape_query_version(m_connection);
- xcb_shape_query_version_reply_t *shape_query = xcb_shape_query_version_reply(m_connection,
- cookie, NULL);
+ auto shape_query = Q_XCB_REPLY(xcb_shape_query_version, m_connection);
if (!shape_query) {
qWarning("QXcbConnection: Failed to initialize SHAPE extension");
} else if (shape_query->major_version > 1 || (shape_query->major_version == 1 && shape_query->minor_version >= 1)) {
// The input shape is the only thing added in SHAPE 1.1
has_input_shape = true;
}
- free(shape_query);
}
void QXcbConnection::initializeXKB()
@@ -2177,11 +2184,10 @@ void QXcbConnection::initializeXKB()
xkb_first_event = reply->first_event;
xcb_connection_t *c = connection()->xcb_connection();
- xcb_xkb_use_extension_cookie_t xkb_query_cookie;
- xcb_xkb_use_extension_reply_t *xkb_query;
- xkb_query_cookie = xcb_xkb_use_extension(c, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION);
- xkb_query = xcb_xkb_use_extension_reply(c, xkb_query_cookie, 0);
+ auto xkb_query = Q_XCB_REPLY(xcb_xkb_use_extension, c,
+ XKB_X11_MIN_MAJOR_XKB_VERSION,
+ XKB_X11_MIN_MINOR_XKB_VERSION);
if (!xkb_query) {
qWarning("Qt: Failed to initialize XKB extension");
@@ -2190,12 +2196,10 @@ void QXcbConnection::initializeXKB()
qWarning("Qt: Unsupported XKB version (We want %d %d, but X server has %d %d)",
XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION,
xkb_query->serverMajor, xkb_query->serverMinor);
- free(xkb_query);
return;
}
has_xkb = true;
- free(xkb_query);
const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES |
XCB_XKB_MAP_PART_KEY_SYMS |
@@ -2230,62 +2234,6 @@ void QXcbConnection::initializeXKB()
#endif
}
-#if defined(XCB_USE_XINPUT22)
-bool QXcbConnection::xi2MouseEvents() const
-{
- static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
- // FIXME: Don't use XInput2 mouse events when Xinerama extension
- // is enabled, because it causes problems with multi-monitor setup.
- return mouseViaXI2 && !has_xinerama_extension;
-}
-#endif
-
-#if QT_CONFIG(xinput2)
-static int xi2ValuatorOffset(const unsigned char *maskPtr, int maskLen, int number)
-{
- int offset = 0;
- for (int i = 0; i < maskLen; i++) {
- if (number < 8) {
- if ((maskPtr[i] & (1 << number)) == 0)
- return -1;
- }
- for (int j = 0; j < 8; j++) {
- if (j == number)
- return offset;
- if (maskPtr[i] & (1 << j))
- offset++;
- }
- number -= 8;
- }
- return -1;
-}
-
-bool QXcbConnection::xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value)
-{
- const xXIDeviceEvent *xideviceevent = static_cast<const xXIDeviceEvent *>(event);
- const unsigned char *buttonsMaskAddr = (const unsigned char*)&xideviceevent[1];
- const unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
- FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
-
- int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum);
- if (valuatorOffset < 0)
- return false;
-
- *value = valuatorsValuesAddr[valuatorOffset].integral;
- *value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16));
- return true;
-}
-
-void QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event)
-{
- // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
- // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
- // Move this data back to have the same layout in memory as it was on the wire
- // and allow casting, overwriting the full_sequence field.
- memmove((char*) event + 32, (char*) event + 36, event->length * 4);
-}
-#endif // QT_CONFIG(xinput2)
-
QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const
{
if (!m_systemTrayTracker) {
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index edbc8d846e..999dc0630c 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -56,6 +56,9 @@
#include <QtCore/QLoggingCategory>
#include <QtCore/private/qglobal_p.h>
+#include <cstdlib>
+#include <memory>
+
// This is needed to make Qt compile together with XKB. xkb.h is using a variable
// which is called 'explicit', this is a reserved keyword in c++
#if QT_CONFIG(xkb)
@@ -76,19 +79,19 @@
#define XCB_USE_XINPUT22 // XI 2.2 adds multi-point touch support
#endif
#endif
-struct XInput2TouchDeviceData;
#endif // QT_CONFIG(xinput2)
struct xcb_randr_get_output_info_reply_t;
-//#define Q_XCB_DEBUG
-
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInput)
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputDevices)
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputEvents)
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaEvents)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaXcb)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaPeeker)
class QXcbVirtualDesktop;
class QXcbScreen;
@@ -358,7 +361,7 @@ public:
virtual void handleFocusInEvent(const xcb_focus_in_event_t *) {}
virtual void handleFocusOutEvent(const xcb_focus_out_event_t *) {}
virtual void handlePropertyNotifyEvent(const xcb_property_notify_event_t *) {}
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
virtual void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource = Qt::MouseEventNotSynthesized) {}
virtual void handleXIEnterLeave(xcb_ge_event_t *) {}
#endif
@@ -426,26 +429,12 @@ public:
void *xlib_display() const;
void *createVisualInfoForDefaultVisualId() const;
#endif
-
-#if QT_CONFIG(xinput2)
- void xi2Select(xcb_window_t window);
- void xi2SelectStateEvents();
-#endif
-#ifdef XCB_USE_XINPUT21
- bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
-#else
- bool isAtLeastXI21() const { return false; }
-#endif
-#ifdef XCB_USE_XINPUT22
- bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
-#else
- bool isAtLeastXI22() const { return false; }
-#endif
-
void sync();
void handleXcbError(xcb_generic_error_t *error);
void handleXcbEvent(xcb_generic_event_t *event);
+ void printXcbEvent(const QLoggingCategory &log, const char *message,
+ xcb_generic_event_t *event) const;
void addWindowEventListener(xcb_window_t id, QXcbWindowEventListener *eventListener);
void removeWindowEventListener(xcb_window_t id);
@@ -458,27 +447,37 @@ public:
typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *);
void addPeekFunc(PeekFunc f);
+ // Peek at all queued events
+ qint32 generatePeekerId();
+ bool removePeekerId(qint32 peekerId);
+ enum PeekOption { PeekDefault = 0, PeekFromCachedIndex = 1 }; // see qx11info_x11.h
+ Q_DECLARE_FLAGS(PeekOptions, PeekOption)
+ typedef bool (*PeekerCallback)(xcb_generic_event_t *event, void *peekerData);
+ bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr,
+ PeekOptions option = PeekDefault, qint32 peekerId = -1);
+
inline xcb_timestamp_t time() const { return m_time; }
inline void setTime(xcb_timestamp_t t) { if (t > m_time) m_time = t; }
inline xcb_timestamp_t netWmUserTime() const { return m_netWmUserTime; }
inline void setNetWmUserTime(xcb_timestamp_t t) { if (t > m_netWmUserTime) m_netWmUserTime = t; }
- bool hasXFixes() const { return xfixes_first_event > 0; }
+ bool hasXFixes() const { return has_xfixes; }
bool hasXShape() const { return has_shape_extension; }
bool hasXRandr() const { return has_randr_extension; }
bool hasInputShape() const { return has_input_shape; }
bool hasXKB() const { return has_xkb; }
+ bool hasXRender() const { return has_render_extension; }
+ bool hasXInput2() const { return m_xi2Enabled; }
- bool supportsThreadedRendering() const { return m_reader->isRunning(); }
bool threadedEventHandling() const { return m_reader->isRunning(); }
xcb_timestamp_t getTimestamp();
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
xcb_window_t getQtSelectionOwner();
- void setButton(Qt::MouseButton button, bool down) { m_buttons.setFlag(button, down); }
- Qt::MouseButtons buttons() const { return m_buttons; }
+ void setButtonState(Qt::MouseButton button, bool down) { m_buttonState.setFlag(button, down); }
+ Qt::MouseButtons buttonState() const { return m_buttonState; }
Qt::MouseButton translateMouseButton(xcb_button_t s);
QXcbWindow *focusWindow() const { return m_focusWindow; }
@@ -501,27 +500,29 @@ public:
static bool xEmbedSystemTrayAvailable();
static bool xEmbedSystemTrayVisualHasAlphaChannel();
+#if QT_CONFIG(xinput2)
+ void xi2SelectStateEvents();
+ void xi2SelectDeviceEvents(xcb_window_t window);
+ void xi2SelectDeviceEventsCompatibility(xcb_window_t window);
+ bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
+ bool xi2MouseEventsDisabled() const;
+ bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
+ bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
+ Qt::MouseButton xiToQtMouseButton(uint32_t b);
#ifdef XCB_USE_XINPUT21
- void handleEnterEvent();
+ void xi2UpdateScrollingDevices();
#endif
-
#ifdef XCB_USE_XINPUT22
bool startSystemResizeForTouchBegin(xcb_window_t window, const QPoint &point, Qt::Corner corner);
- bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
+ bool isTouchScreen(int id);
+#endif
#endif
- Qt::MouseButton xiToQtMouseButton(uint32_t b);
-
QXcbEventReader *eventReader() const { return m_reader; }
bool canGrab() const { return m_canGrabServer; }
QXcbGlIntegration *glIntegration() const { return m_glIntegration; }
-#ifdef XCB_USE_XINPUT22
- bool xi2MouseEvents() const;
- bool isTouchScreen(int id) const;
-#endif
-
protected:
bool event(QEvent *e) override;
@@ -553,15 +554,35 @@ private:
void destroyScreen(QXcbScreen *screen);
void initializeScreens();
bool compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const;
-#if QT_CONFIG(xinput2)
+
bool m_xi2Enabled = false;
- int m_xi2Minor = 2;
+#if QT_CONFIG(xinput2)
+ int m_xi2Minor = -1;
void initializeXInput2();
- void finalizeXInput2();
+ void xi2SetupDevice(void *info, bool removeExisting = true);
void xi2SetupDevices();
- XInput2TouchDeviceData *touchDeviceForId(int id);
+ struct TouchDeviceData {
+ QTouchDevice *qtTouchDevice = nullptr;
+ QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
+ QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
+ struct ValuatorClassInfo {
+ double min = 0;
+ double max = 0;
+ int number = -1;
+ QXcbAtom::Atom label;
+ };
+ QVector<ValuatorClassInfo> valuatorInfo;
+
+ // Stuff that is relevant only for touchpads
+ QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
+ QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
+ QSizeF size; // device size in mm
+ bool providesTouchOrientation = false;
+ };
+ TouchDeviceData *populateTouchDevices(void *info);
+ TouchDeviceData *touchDeviceForId(int id);
void xi2HandleEvent(xcb_ge_event_t *event);
- void xi2HandleHierachyEvent(void *event);
+ void xi2HandleHierarchyEvent(void *event);
void xi2HandleDeviceChangedEvent(void *event);
int m_xiOpCode, m_xiEventBase, m_xiErrorBase;
#ifdef XCB_USE_XINPUT22
@@ -600,9 +621,12 @@ private:
Qt::Orientations legacyOrientations = 0;
QPointF lastScrollPosition;
};
- void updateScrollingDevice(ScrollingDevice& scrollingDevice, int num_classes, void *classes);
- void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
QHash<int, ScrollingDevice> m_scrollingDevices;
+#ifdef XCB_USE_XINPUT21
+ void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
+ void xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice);
+ ScrollingDevice *scrollingDeviceForId(int id);
+#endif
static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value);
static void xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event);
@@ -638,34 +662,18 @@ private:
void *m_xlib_display = nullptr;
#endif
QXcbEventReader *m_reader = nullptr;
+
#if QT_CONFIG(xinput2)
- QHash<int, XInput2TouchDeviceData*> m_touchDevices;
+ QHash<int, TouchDeviceData> m_touchDevices;
#ifdef XCB_USE_XINPUT22
struct StartSystemResizeInfo {
- xcb_window_t window;
+ xcb_window_t window = XCB_NONE;
uint16_t deviceid;
uint32_t pointid;
Qt::Corner corner;
} m_startSystemResizeInfo;
#endif
#endif
-#ifdef Q_XCB_DEBUG
- struct CallInfo {
- int sequence;
- QByteArray file;
- int line;
- };
- QVector<CallInfo> m_callLog;
- QMutex m_callLogMutex;
- void log(const char *file, int line, int sequence);
- template <typename cookie_t>
- friend cookie_t q_xcb_call_template(const cookie_t &cookie, QXcbConnection *connection,
- const char *file, int line);
- template <typename reply_t>
- friend reply_t *q_xcb_call_template(reply_t *reply, QXcbConnection *connection,
- const char *file, int line);
-#endif
-
WindowMapper m_mapper;
QVector<PeekFunc> m_peekFuncs;
@@ -674,13 +682,15 @@ private:
uint32_t xrandr_first_event = 0;
uint32_t xkb_first_event = 0;
+ bool has_xfixes = false;
bool has_xinerama_extension = false;
bool has_shape_extension = false;
bool has_randr_extension = false;
bool has_input_shape;
bool has_xkb = false;
+ bool has_render_extension = false;
- Qt::MouseButtons m_buttons = 0;
+ Qt::MouseButtons m_buttonState = 0;
QXcbWindow *m_focusWindow = nullptr;
QXcbWindow *m_mouseGrabber = nullptr;
@@ -694,6 +704,10 @@ private:
xcb_window_t m_qtSelectionOwner = 0;
+ bool m_mainEventLoopFlushedQueue = false;
+ qint32 m_peekerIdSource = 0;
+ bool m_peekerIndexCacheDirty = false;
+ QHash<qint32, qint32> m_peekerToCachedIndex;
friend class QXcbEventReader;
};
#if QT_CONFIG(xinput2)
@@ -703,9 +717,6 @@ Q_DECLARE_TYPEINFO(QXcbConnection::TabletData, Q_MOVABLE_TYPE);
#endif
#endif
-#define DISPLAY_FROM_XCB(object) (reinterpret_cast<Display *>(object->connection()->xlib_display()))
-#define CREATE_VISUALINFO_FROM_DEFAULT_VISUALID(object) ((XVisualInfo *)(object->connection()->createVisualInfoForDefaultVisualId()))
-
template<typename T>
xcb_generic_event_t *QXcbConnection::checkEvent(T &checker)
{
@@ -733,6 +744,20 @@ private:
QXcbConnection *m_connection;
};
+#define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection
+
+#define Q_XCB_REPLY(call, ...) \
+ std::unique_ptr<call##_reply_t, decltype(std::free) *>( \
+ call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr), \
+ std::free \
+ )
+
+#define Q_XCB_REPLY_UNCHECKED(call, ...) \
+ std::unique_ptr<call##_reply_t, decltype(std::free) *>( \
+ call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call##_unchecked(__VA_ARGS__), nullptr), \
+ std::free \
+ )
+
template <typename T>
union q_padded_xcb_event {
T event;
@@ -746,30 +771,6 @@ union q_padded_xcb_event {
q_padded_xcb_event<event_type> store = {}; \
auto &event_var = store.event;
-#ifdef Q_XCB_DEBUG
-template <typename cookie_t>
-cookie_t q_xcb_call_template(const cookie_t &cookie, QXcbConnection *connection, const char *file,
- int line)
-{
- connection->log(file, line, cookie.sequence);
- return cookie;
-}
-
-template <typename reply_t>
-reply_t *q_xcb_call_template(reply_t *reply, QXcbConnection *connection, const char *file, int line)
-{
- connection->log(file, line, reply->sequence);
- return reply;
-}
-#define Q_XCB_CALL(x) q_xcb_call_template(x, connection(), __FILE__, __LINE__)
-#define Q_XCB_CALL2(x, connection) q_xcb_call_template(x, connection, __FILE__, __LINE__)
-#define Q_XCB_NOOP(c) q_xcb_call_template(xcb_no_operation(c->xcb_connection()), c, __FILE__, __LINE__);
-#else
-#define Q_XCB_CALL(x) x
-#define Q_XCB_CALL2(x, connection) x
-#define Q_XCB_NOOP(c) (void)c;
-#endif
-
QT_END_NAMESPACE
#endif
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 26a9ba8d26..f90f189146 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -50,19 +50,6 @@
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XI2proto.h>
-struct XInput2TouchDeviceData {
- XIDeviceInfo *xiDeviceInfo = nullptr;
- QTouchDevice *qtTouchDevice = nullptr;
- QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
- QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
-
- // Stuff that is relevant only for touchpads
- QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
- QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
- QSizeF size; // device size in mm
- bool providesTouchOrientation = false;
-};
-
void QXcbConnection::initializeXInput2()
{
// TODO Qt 6 (or perhaps earlier): remove these redundant env variables
@@ -70,30 +57,37 @@ void QXcbConnection::initializeXInput2()
const_cast<QLoggingCategory&>(lcQpaXInput()).setEnabled(QtDebugMsg, true);
if (qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT_DEVICES"))
const_cast<QLoggingCategory&>(lcQpaXInputDevices()).setEnabled(QtDebugMsg, true);
+
Display *xDisplay = static_cast<Display *>(m_xlib_display);
if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
int xiMajor = 2;
- // try 2.2 first, needed for TouchBegin/Update/End
- if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
- m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
- if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
- m_xi2Minor = 0; // for tablet support 2.0 is enough
- m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest;
- } else
- m_xi2Enabled = true;
- } else
- m_xi2Enabled = true;
- if (m_xi2Enabled) {
-#ifdef XCB_USE_XINPUT22
- qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.2 or greater", xiMajor, m_xi2Minor);
- m_startSystemResizeInfo.window = XCB_NONE;
+#if defined(XCB_USE_XINPUT22)
+ m_xi2Minor = 2; // for touch support 2.2 is enough
+#elif defined(XCB_USE_XINPUT21)
+ m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
#else
- qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor);
+ m_xi2Minor = 0; // for tablet support 2.0 is enough
#endif
+ qCDebug(lcQpaXInput, "Plugin build with support for XInput 2 version up "
+ "to %d.%d", xiMajor, m_xi2Minor);
+
+ switch (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor)) {
+ case Success:
+ // Server's supported version can be lower than the version we have
+ // announced to support. In this case Qt client will be limited by
+ // X server's supported version.
+ qCDebug(lcQpaXInput, "Using XInput version %d.%d", xiMajor, m_xi2Minor);
+ m_xi2Enabled = true;
+ xi2SetupDevices();
xi2SelectStateEvents();
+ break;
+ case BadRequest: // Must be an X server with XInput 1
+ qCDebug(lcQpaXInput, "X server does not support XInput 2");
+ break;
+ default: // BadValue
+ qCDebug(lcQpaXInput, "Internal error");
+ break;
}
-
- xi2SetupDevices();
}
}
@@ -113,377 +107,434 @@ void QXcbConnection::xi2SelectStateEvents()
XISelectEvents(dpy, DefaultRootWindow(dpy), &xiEventMask, 1);
}
-void QXcbConnection::xi2SetupDevices()
+void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
{
-#if QT_CONFIG(tabletevent)
- m_tabletData.clear();
+ if (window == rootWindow())
+ return;
+
+ unsigned int bitMask = 0;
+ unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
+ bitMask |= XI_ButtonPressMask;
+ bitMask |= XI_ButtonReleaseMask;
+ bitMask |= XI_MotionMask;
+ // There is a check for enter/leave events in plain xcb enter/leave event handler,
+ // core enter/leave events will be ignored in this case.
+ bitMask |= XI_EnterMask;
+ bitMask |= XI_LeaveMask;
+ bitMask |= XI_PropertyEventMask;
+#ifdef XCB_USE_XINPUT22
+ if (isAtLeastXI22()) {
+ bitMask |= XI_TouchBeginMask;
+ bitMask |= XI_TouchUpdateMask;
+ bitMask |= XI_TouchEndMask;
+ }
#endif
- m_scrollingDevices.clear();
- if (!m_xi2Enabled)
- return;
+ XIEventMask mask;
+ mask.mask_len = sizeof(bitMask);
+ mask.mask = xiBitMask;
+ mask.deviceid = XIAllMasterDevices;
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ Status result = XISelectEvents(dpy, window, &mask, 1);
+ if (result == Success)
+ QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
+ else
+ qCDebug(lcQpaXInput, "failed to select events, window %x, result %d", window, result);
+}
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- int deviceCount = 0;
- XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
- for (int i = 0; i < deviceCount; ++i) {
- // Only non-master pointing devices are relevant here.
- if (devices[i].use != XISlavePointer)
- continue;
- qCDebug(lcQpaXInputDevices) << "input device " << devices[i].name << "ID" << devices[i].deviceid;
+void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
+{
+ XIDeviceInfo *deviceInfo = reinterpret_cast<XIDeviceInfo *>(info);
+ if (removeExisting) {
#if QT_CONFIG(tabletevent)
- TabletData tabletData;
+ for (int i = 0; i < m_tabletData.count(); ++i) {
+ if (m_tabletData.at(i).deviceId == deviceInfo->deviceid) {
+ m_tabletData.remove(i);
+ break;
+ }
+ }
#endif
- ScrollingDevice scrollingDevice;
- for (int c = 0; c < devices[i].num_classes; ++c) {
- switch (devices[i].classes[c]->type) {
- case XIValuatorClass: {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(devices[i].classes[c]);
- const int valuatorAtom = qatom(vci->label);
- qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
+ m_scrollingDevices.remove(deviceInfo->deviceid);
+ m_touchDevices.remove(deviceInfo->deviceid);
+ }
+
+ qCDebug(lcQpaXInputDevices) << "input device " << deviceInfo->name << "ID" << deviceInfo->deviceid;
#if QT_CONFIG(tabletevent)
- if (valuatorAtom < QXcbAtom::NAtoms) {
- TabletData::ValuatorClassInfo info;
- info.minVal = vci->min;
- info.maxVal = vci->max;
- info.number = vci->number;
- tabletData.valuatorInfo[valuatorAtom] = info;
- }
-#endif // QT_CONFIG(tabletevent)
- if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
- scrollingDevice.lastScrollPosition.setX(vci->value);
- else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
- scrollingDevice.lastScrollPosition.setY(vci->value);
- break;
+ TabletData tabletData;
+#endif
+ ScrollingDevice scrollingDevice;
+ for (int c = 0; c < deviceInfo->num_classes; ++c) {
+ XIAnyClassInfo *classinfo = deviceInfo->classes[c];
+ switch (classinfo->type) {
+ case XIValuatorClass: {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+ const int valuatorAtom = qatom(vci->label);
+ qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
+#if QT_CONFIG(tabletevent)
+ if (valuatorAtom < QXcbAtom::NAtoms) {
+ TabletData::ValuatorClassInfo info;
+ info.minVal = vci->min;
+ info.maxVal = vci->max;
+ info.number = vci->number;
+ tabletData.valuatorInfo[valuatorAtom] = info;
}
+#endif // QT_CONFIG(tabletevent)
+ if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
+ scrollingDevice.lastScrollPosition.setX(vci->value);
+ else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
+ scrollingDevice.lastScrollPosition.setY(vci->value);
+ break;
+ }
#ifdef XCB_USE_XINPUT21
- case XIScrollClass: {
- XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(devices[i].classes[c]);
- if (sci->scroll_type == XIScrollTypeVertical) {
- scrollingDevice.orientations |= Qt::Vertical;
- scrollingDevice.verticalIndex = sci->number;
- scrollingDevice.verticalIncrement = sci->increment;
- }
- else if (sci->scroll_type == XIScrollTypeHorizontal) {
- scrollingDevice.orientations |= Qt::Horizontal;
- scrollingDevice.horizontalIndex = sci->number;
- scrollingDevice.horizontalIncrement = sci->increment;
- }
- break;
+ case XIScrollClass: {
+ XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(classinfo);
+ if (sci->scroll_type == XIScrollTypeVertical) {
+ scrollingDevice.orientations |= Qt::Vertical;
+ scrollingDevice.verticalIndex = sci->number;
+ scrollingDevice.verticalIncrement = sci->increment;
}
- case XIButtonClass: {
- XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(devices[i].classes[c]);
- if (bci->num_buttons >= 5) {
- Atom label4 = bci->labels[3];
- Atom label5 = bci->labels[4];
- // Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on
- // button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons.
- if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) &&
- (!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown))
- scrollingDevice.legacyOrientations |= Qt::Vertical;
- }
- if (bci->num_buttons >= 7) {
- Atom label6 = bci->labels[5];
- Atom label7 = bci->labels[6];
- if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
- scrollingDevice.legacyOrientations |= Qt::Horizontal;
- }
- qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
- break;
+ else if (sci->scroll_type == XIScrollTypeHorizontal) {
+ scrollingDevice.orientations |= Qt::Horizontal;
+ scrollingDevice.horizontalIndex = sci->number;
+ scrollingDevice.horizontalIncrement = sci->increment;
+ }
+ break;
+ }
+ case XIButtonClass: {
+ XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(classinfo);
+ if (bci->num_buttons >= 5) {
+ Atom label4 = bci->labels[3];
+ Atom label5 = bci->labels[4];
+ // Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on
+ // button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons.
+ if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) &&
+ (!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown))
+ scrollingDevice.legacyOrientations |= Qt::Vertical;
}
+ if (bci->num_buttons >= 7) {
+ Atom label6 = bci->labels[5];
+ Atom label7 = bci->labels[6];
+ if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
+ scrollingDevice.legacyOrientations |= Qt::Horizontal;
+ }
+ qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
+ break;
+ }
#endif
- case XIKeyClass:
- qCDebug(lcQpaXInputDevices) << " it's a keyboard";
- break;
+ case XIKeyClass:
+ qCDebug(lcQpaXInputDevices) << " it's a keyboard";
+ break;
#ifdef XCB_USE_XINPUT22
- case XITouchClass:
- // will be handled in deviceForId()
- break;
+ case XITouchClass:
+ // will be handled in populateTouchDevices()
+ break;
#endif
- default:
- qCDebug(lcQpaXInputDevices) << " has class" << devices[i].classes[c]->type;
- break;
- }
+ default:
+ qCDebug(lcQpaXInputDevices) << " has class" << classinfo->type;
+ break;
}
- bool isTablet = false;
+ }
+ bool isTablet = false;
#if QT_CONFIG(tabletevent)
- // If we have found the valuators which we expect a tablet to have, it might be a tablet.
- if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) &&
- tabletData.valuatorInfo.contains(QXcbAtom::AbsY) &&
- tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure))
- isTablet = true;
-
- // But we need to be careful not to take the touch and tablet-button devices as tablets.
- QByteArray name = QByteArray(devices[i].name).toLower();
- QString dbgType = QLatin1String("UNKNOWN");
- if (name.contains("eraser")) {
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Eraser;
- dbgType = QLatin1String("eraser");
- } else if (name.contains("cursor") && !(name.contains("cursor controls") && name.contains("trackball"))) {
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Cursor;
- dbgType = QLatin1String("cursor");
- } else if (name.contains("wacom") && name.contains("finger touch")) {
- isTablet = false;
- } else if ((name.contains("pen") || name.contains("stylus")) && isTablet) {
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("wacom") && isTablet && !name.contains("touch")) {
- // combined device (evdev) rather than separate pen/eraser (wacom driver)
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) {
- // some "Genius" tablets
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("waltop") && name.contains("tablet")) {
- // other "Genius" tablets
- // WALTOP International Corp. Slim Tablet
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("uc-logic") && isTablet) {
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else {
- isTablet = false;
- }
+ // If we have found the valuators which we expect a tablet to have, it might be a tablet.
+ if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AbsY) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure))
+ isTablet = true;
+
+ // But we need to be careful not to take the touch and tablet-button devices as tablets.
+ QByteArray name = QByteArray(deviceInfo->name).toLower();
+ QString dbgType = QLatin1String("UNKNOWN");
+ if (name.contains("eraser")) {
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Eraser;
+ dbgType = QLatin1String("eraser");
+ } else if (name.contains("cursor") && !(name.contains("cursor controls") && name.contains("trackball"))) {
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Cursor;
+ dbgType = QLatin1String("cursor");
+ } else if (name.contains("wacom") && name.contains("finger touch")) {
+ isTablet = false;
+ } else if ((name.contains("pen") || name.contains("stylus")) && isTablet) {
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("wacom") && isTablet && !name.contains("touch")) {
+ // combined device (evdev) rather than separate pen/eraser (wacom driver)
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) {
+ // some "Genius" tablets
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("waltop") && name.contains("tablet")) {
+ // other "Genius" tablets
+ // WALTOP International Corp. Slim Tablet
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("uc-logic") && isTablet) {
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else {
+ isTablet = false;
+ }
- if (isTablet) {
- tabletData.deviceId = devices[i].deviceid;
- m_tabletData.append(tabletData);
- qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType;
- }
+ if (isTablet) {
+ tabletData.deviceId = deviceInfo->deviceid;
+ m_tabletData.append(tabletData);
+ qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType;
+ }
#endif // QT_CONFIG(tabletevent)
#ifdef XCB_USE_XINPUT21
- if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
- scrollingDevice.deviceId = devices[i].deviceid;
- // Only use legacy wheel button events when we don't have real scroll valuators.
- scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
- m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
- qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
- }
+ if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
+ scrollingDevice.deviceId = deviceInfo->deviceid;
+ // Only use legacy wheel button events when we don't have real scroll valuators.
+ scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
+ m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
+ qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
+ }
#endif
- if (!isTablet) {
- // touchDeviceForId populates XInput2DeviceData the first time it is called
- // with a new deviceId. On subsequent calls it will return the cached object.
- XInput2TouchDeviceData *dev = touchDeviceForId(devices[i].deviceid);
- if (dev && lcQpaXInputDevices().isDebugEnabled()) {
- if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen)
- qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d",
- dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
- dev->qtTouchDevice->maximumTouchPoints());
- else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
- qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
- dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
- dev->qtTouchDevice->maximumTouchPoints(),
- dev->size.width(), dev->size.height());
- }
+ if (!isTablet) {
+ TouchDeviceData *dev = populateTouchDevices(deviceInfo);
+ if (dev && lcQpaXInputDevices().isDebugEnabled()) {
+ if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen)
+ qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d",
+ dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
+ dev->qtTouchDevice->maximumTouchPoints());
+ else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
+ qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
+ dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
+ dev->qtTouchDevice->maximumTouchPoints(),
+ dev->size.width(), dev->size.height());
}
}
- XIFreeDeviceInfo(devices);
+
}
-void QXcbConnection::finalizeXInput2()
+void QXcbConnection::xi2SetupDevices()
{
- for (XInput2TouchDeviceData *dev : qAsConst(m_touchDevices)) {
- if (dev->xiDeviceInfo)
- XIFreeDeviceInfo(dev->xiDeviceInfo);
- delete dev;
+#if QT_CONFIG(tabletevent)
+ m_tabletData.clear();
+#endif
+ m_scrollingDevices.clear();
+ m_touchDevices.clear();
+
+ Display *xDisplay = static_cast<Display *>(m_xlib_display);
+ int deviceCount = 0;
+ XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
+ for (int i = 0; i < deviceCount; ++i) {
+ // Only non-master pointing devices are relevant here.
+ if (devices[i].use != XISlavePointer)
+ continue;
+ xi2SetupDevice(&devices[i], false);
}
+ XIFreeDeviceInfo(devices);
}
-void QXcbConnection::xi2Select(xcb_window_t window)
+/*! \internal
+
+ Notes on QT_XCB_NO_XI2_MOUSE Handling:
+
+ Here we don't select pointer button press/release and motion events on master devices, instead
+ we select these events directly on slave devices. This means that a master device will fallback
+ to sending core events for every XI_* event that is sent directly by a slave device. For more
+ details see "Event processing for attached slave devices" in XInput2 specification. To prevent
+ handling of the same event twice, we have checks for xi2MouseEventsDisabled() in XI2 event
+ handlers (but this is somewhat inconsistent in some situations). If the purpose for
+ QT_XCB_NO_XI2_MOUSE was so that an application using QAbstractNativeEventFilter would see core
+ mouse events before they are handled by Qt then QT_XCB_NO_XI2_MOUSE won't always work as
+ expected (e.g. we handle scroll event directly from a slave device event, before an application
+ has seen the fallback core event from a master device).
+
+ The commit introducing QT_XCB_NO_XI2_MOUSE also states that setting this envvar "restores the
+ old behavior with broken grabbing". It did not elaborate why grabbing was not fixed for this
+ code path. The issue that this envvar tries to solve seem to be less important than broken
+ grabbing (broken apparently only for touch events). Thus, if you really want core mouse events
+ in your application and do not care about broken touch, then use QT_XCB_NO_XI2 (more on this
+ below) to disable the extension all together. The reason why grabbing might have not been fixed
+ is that calling XIGrabDevice with this code path for some reason always returns AlreadyGrabbed
+ (by debugging X server's code it appears that when we call XIGrabDevice, an X server first grabs
+ pointer via core pointer and then fails to do XI2 grab with AlreadyGrabbed; disclaimer - I did
+ not debug this in great detail). When we try supporting odd setups like QT_XCB_NO_XI2_MOUSE, we
+ are asking for trouble anyways.
+
+ In conclusion, introduction of QT_XCB_NO_XI2_MOUSE causes more issues than solves - the above
+ mentioned inconsistencies, maintenance of this code path and that QT_XCB_NO_XI2_MOUSE replaces
+ less important issue with somewhat more important issue. It also makes us to use less optimal
+ code paths in certain situations (see xi2HandleHierarchyEvent). Using of QT_XCB_NO_XI2 has its
+ drawbacks too - no tablet and touch events. So the only real fix in this case is at an
+ application side (teach the application about xcb_ge_event_t events). Based on this,
+ QT_XCB_NO_XI2_MOUSE will be removed in ### Qt 6. It should not have existed in the first place,
+ native events seen by QAbstractNativeEventFilter is not really a public API, applications should
+ expect changes at this level and do ifdefs if something changes between Qt version.
+*/
+void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
{
- if (!m_xi2Enabled || window == rootWindow())
+ if (window == rootWindow())
return;
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- unsigned int bitMask = 0;
- unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
-
+ unsigned int mask = 0;
+ unsigned char *bitMask = reinterpret_cast<unsigned char *>(&mask);
+ mask |= XI_PropertyEventMask;
#ifdef XCB_USE_XINPUT22
if (isAtLeastXI22()) {
- bitMask |= XI_TouchBeginMask;
- bitMask |= XI_TouchUpdateMask;
- bitMask |= XI_TouchEndMask;
- bitMask |= XI_PropertyEventMask; // for tablets
- if (xi2MouseEvents()) {
- // We want both mouse and touch through XI2 if touch is supported (>= 2.2).
- // The plain xcb press and motion events will not be delivered after this.
- bitMask |= XI_ButtonPressMask;
- bitMask |= XI_ButtonReleaseMask;
- bitMask |= XI_MotionMask;
-
- // There is a check for enter/leave events in plain xcb enter/leave event handler
- bitMask |= XI_EnterMask;
- bitMask |= XI_LeaveMask;
-
- qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch");
- }
- XIEventMask mask;
- mask.mask_len = sizeof(bitMask);
- mask.mask = xiBitMask;
- // When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet
- // events will get disabled. This is preferable, as Qt Quick handles touch events
- // directly, while for other applications QtGui synthesizes mouse events.
- mask.deviceid = XIAllMasterDevices;
- Status result = XISelectEvents(xDisplay, window, &mask, 1);
- if (result == Success)
- QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
- else
- qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result);
+ mask |= XI_TouchBeginMask;
+ mask |= XI_TouchUpdateMask;
+ mask |= XI_TouchEndMask;
}
+#endif
+ XIEventMask xiMask;
+ xiMask.mask_len = sizeof(mask);
+ xiMask.mask = bitMask;
+ xiMask.deviceid = XIAllMasterDevices;
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ Status result = XISelectEvents(dpy, window, &xiMask, 1);
+ if (result == Success)
+ QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
+ else
+ qCDebug(lcQpaXInput, "failed to select events, window %x, result %d", window, result);
- const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents();
-#else
- const bool pointerSelected = false;
-#endif // XCB_USE_XINPUT22
+ mask = XI_ButtonPressMask;
+ mask |= XI_ButtonReleaseMask;
+ mask |= XI_MotionMask;
- QSet<int> tabletDevices;
#if QT_CONFIG(tabletevent)
+ QSet<int> tabletDevices;
if (!m_tabletData.isEmpty()) {
- unsigned int tabletBitMask;
- unsigned char *xiTabletBitMask = reinterpret_cast<unsigned char *>(&tabletBitMask);
- QVector<XIEventMask> xiEventMask(m_tabletData.count());
- tabletBitMask = XI_PropertyEventMask;
- if (!pointerSelected)
- tabletBitMask |= XI_ButtonPressMask | XI_ButtonReleaseMask | XI_MotionMask;
- for (int i = 0; i < m_tabletData.count(); ++i) {
+ const int nrTablets = m_tabletData.count();
+ QVector<XIEventMask> xiEventMask(nrTablets);
+ for (int i = 0; i < nrTablets; ++i) {
int deviceId = m_tabletData.at(i).deviceId;
tabletDevices.insert(deviceId);
xiEventMask[i].deviceid = deviceId;
- xiEventMask[i].mask_len = sizeof(tabletBitMask);
- xiEventMask[i].mask = xiTabletBitMask;
+ xiEventMask[i].mask_len = sizeof(mask);
+ xiEventMask[i].mask = bitMask;
}
- XISelectEvents(xDisplay, window, xiEventMask.data(), m_tabletData.count());
+ XISelectEvents(dpy, window, xiEventMask.data(), nrTablets);
}
-#endif // QT_CONFIG(tabletevent)
+#endif
#ifdef XCB_USE_XINPUT21
- // Enable each scroll device
- if (!m_scrollingDevices.isEmpty() && !pointerSelected) {
- // Only when XI2 mouse events are not enabled, otherwise motion and release are selected already.
+ if (!m_scrollingDevices.isEmpty()) {
QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
- unsigned int scrollBitMask;
- unsigned char *xiScrollBitMask = reinterpret_cast<unsigned char *>(&scrollBitMask);
-
- scrollBitMask = XI_MotionMask;
- scrollBitMask |= XI_ButtonReleaseMask;
- int i=0;
+ int i = 0;
for (const ScrollingDevice& scrollingDevice : qAsConst(m_scrollingDevices)) {
+#if QT_CONFIG(tabletevent)
if (tabletDevices.contains(scrollingDevice.deviceId))
continue; // All necessary events are already captured.
+#endif
xiEventMask[i].deviceid = scrollingDevice.deviceId;
- xiEventMask[i].mask_len = sizeof(scrollBitMask);
- xiEventMask[i].mask = xiScrollBitMask;
+ xiEventMask[i].mask_len = sizeof(mask);
+ xiEventMask[i].mask = bitMask;
i++;
}
- XISelectEvents(xDisplay, window, xiEventMask.data(), i);
+ XISelectEvents(dpy, window, xiEventMask.data(), i);
}
-#else
- Q_UNUSED(xiBitMask);
#endif
}
-XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
+QXcbConnection::TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
{
- XInput2TouchDeviceData *dev = Q_NULLPTR;
- QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constFind(id);
- if (devIt != m_touchDevices.cend()) {
- dev = devIt.value();
- } else {
- int nrDevices = 0;
- QTouchDevice::Capabilities caps = 0;
- dev = new XInput2TouchDeviceData;
- dev->xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), id, &nrDevices);
- if (nrDevices <= 0) {
- delete dev;
- return 0;
- }
- int type = -1;
- int maxTouchPoints = 1;
- bool hasRelativeCoords = false;
- for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
- XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
- switch (classinfo->type) {
+ TouchDeviceData *dev = nullptr;
+ if (m_touchDevices.contains(id))
+ dev = &m_touchDevices[id];
+ return dev;
+}
+
+QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info)
+{
+ XIDeviceInfo *deviceinfo = reinterpret_cast<XIDeviceInfo *>(info);
+ QTouchDevice::Capabilities caps = 0;
+ int type = -1;
+ int maxTouchPoints = 1;
+ bool isTouchDevice = false;
+ bool hasRelativeCoords = false;
+ TouchDeviceData dev;
+ for (int i = 0; i < deviceinfo->num_classes; ++i) {
+ XIAnyClassInfo *classinfo = deviceinfo->classes[i];
+ switch (classinfo->type) {
#ifdef XCB_USE_XINPUT22
- case XITouchClass: {
- XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
- maxTouchPoints = tci->num_touches;
- qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode);
- switch (tci->mode) {
- case XIDependentTouch:
- type = QTouchDevice::TouchPad;
- break;
- case XIDirectTouch:
- type = QTouchDevice::TouchScreen;
- break;
- }
+ case XITouchClass: {
+ XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
+ maxTouchPoints = tci->num_touches;
+ qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode);
+ switch (tci->mode) {
+ case XIDependentTouch:
+ type = QTouchDevice::TouchPad;
+ break;
+ case XIDirectTouch:
+ type = QTouchDevice::TouchScreen;
break;
}
+ break;
+ }
#endif // XCB_USE_XINPUT22
- case XIValuatorClass: {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
- // Some devices (mice) report a resolution of 0; they will be excluded later,
- // for now just prevent a division by zero
- const int vciResolution = vci->resolution ? vci->resolution : 1;
- if (vci->label == atom(QXcbAtom::AbsMTPositionX))
- caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition;
- else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor))
- caps |= QTouchDevice::Area;
- else if (vci->label == atom(QXcbAtom::AbsMTOrientation))
- dev->providesTouchOrientation = true;
- else if (vci->label == atom(QXcbAtom::AbsMTPressure) || vci->label == atom(QXcbAtom::AbsPressure))
- caps |= QTouchDevice::Pressure;
- else if (vci->label == atom(QXcbAtom::RelX)) {
- hasRelativeCoords = true;
- dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
- } else if (vci->label == atom(QXcbAtom::RelY)) {
- hasRelativeCoords = true;
- dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
- } else if (vci->label == atom(QXcbAtom::AbsX)) {
- caps |= QTouchDevice::Position;
- dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
- } else if (vci->label == atom(QXcbAtom::AbsY)) {
- caps |= QTouchDevice::Position;
- dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
- }
- break;
+ case XIValuatorClass: {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+ const QXcbAtom::Atom valuatorAtom = qatom(vci->label);
+ if (valuatorAtom < QXcbAtom::NAtoms) {
+ TouchDeviceData::ValuatorClassInfo info;
+ info.min = vci->min;
+ info.max = vci->max;
+ info.number = vci->number;
+ info.label = valuatorAtom;
+ dev.valuatorInfo.append(info);
}
- default:
- break;
+ // Some devices (mice) report a resolution of 0; they will be excluded later,
+ // for now just prevent a division by zero
+ const int vciResolution = vci->resolution ? vci->resolution : 1;
+ if (valuatorAtom == QXcbAtom::AbsMTPositionX)
+ caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition;
+ else if (valuatorAtom == QXcbAtom::AbsMTTouchMajor)
+ caps |= QTouchDevice::Area;
+ else if (valuatorAtom == QXcbAtom::AbsMTOrientation)
+ dev.providesTouchOrientation = true;
+ else if (valuatorAtom == QXcbAtom::AbsMTPressure || valuatorAtom == QXcbAtom::AbsPressure)
+ caps |= QTouchDevice::Pressure;
+ else if (valuatorAtom == QXcbAtom::RelX) {
+ hasRelativeCoords = true;
+ dev.size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::RelY) {
+ hasRelativeCoords = true;
+ dev.size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::AbsX) {
+ caps |= QTouchDevice::Position;
+ dev.size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::AbsY) {
+ caps |= QTouchDevice::Position;
+ dev.size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
}
+ break;
}
- if (type < 0 && caps && hasRelativeCoords) {
- type = QTouchDevice::TouchPad;
- if (dev->size.width() < 10 || dev->size.height() < 10 ||
- dev->size.width() > 10000 || dev->size.height() > 10000)
- dev->size = QSizeF(130, 110);
- }
- if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
- caps |= QTouchDevice::MouseEmulation;
-
- if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
- dev->qtTouchDevice = new QTouchDevice;
- dev->qtTouchDevice->setName(QString::fromUtf8(dev->xiDeviceInfo->name));
- dev->qtTouchDevice->setType((QTouchDevice::DeviceType)type);
- dev->qtTouchDevice->setCapabilities(caps);
- dev->qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
- if (caps != 0)
- QWindowSystemInterface::registerTouchDevice(dev->qtTouchDevice);
- m_touchDevices[id] = dev;
- } else {
- XIFreeDeviceInfo(dev->xiDeviceInfo);
- delete dev;
- dev = 0;
+ default:
+ break;
}
}
- return dev;
+ if (type < 0 && caps && hasRelativeCoords) {
+ type = QTouchDevice::TouchPad;
+ if (dev.size.width() < 10 || dev.size.height() < 10 ||
+ dev.size.width() > 10000 || dev.size.height() > 10000)
+ dev.size = QSizeF(130, 110);
+ }
+ if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
+ caps |= QTouchDevice::MouseEmulation;
+
+ if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
+ dev.qtTouchDevice = new QTouchDevice;
+ dev.qtTouchDevice->setName(QString::fromUtf8(deviceinfo->name));
+ dev.qtTouchDevice->setType((QTouchDevice::DeviceType)type);
+ dev.qtTouchDevice->setCapabilities(caps);
+ dev.qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
+ if (caps != 0)
+ QWindowSystemInterface::registerTouchDevice(dev.qtTouchDevice);
+ m_touchDevices[deviceinfo->deviceid] = dev;
+ isTouchDevice = true;
+ }
+
+ return isTouchDevice ? &m_touchDevices[deviceinfo->deviceid] : nullptr;
}
#if defined(XCB_USE_XINPUT21) || QT_CONFIG(tabletevent)
@@ -525,7 +576,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
break;
}
case XI_HierarchyChanged:
- xi2HandleHierachyEvent(xiEvent);
+ xi2HandleHierarchyEvent(xiEvent);
return;
case XI_DeviceChanged:
xi2HandleDeviceChangedEvent(xiEvent);
@@ -549,9 +600,8 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
#endif // QT_CONFIG(tabletevent)
#ifdef XCB_USE_XINPUT21
- QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId);
- if (device != m_scrollingDevices.end())
- xi2HandleScrollEvent(xiEvent, device.value());
+ if (ScrollingDevice *device = scrollingDeviceForId(sourceDeviceId))
+ xi2HandleScrollEvent(xiEvent, *device);
#endif // XCB_USE_XINPUT21
#ifdef XCB_USE_XINPUT22
@@ -560,7 +610,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
case XI_ButtonPress:
case XI_ButtonRelease:
case XI_Motion:
- if (xi2MouseEvents() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated))
+ if (!xi2MouseEventsDisabled() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated))
eventListener->handleXIMouseEvent(event);
break;
@@ -576,7 +626,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
xi2ProcessTouch(xiDeviceEvent, platformWindow);
break;
}
- } else if (xiEnterEvent && xi2MouseEvents() && eventListener) {
+ } else if (xiEnterEvent && !xi2MouseEventsDisabled() && eventListener) {
switch (xiEnterEvent->evtype) {
case XI_Enter:
case XI_Leave:
@@ -587,20 +637,25 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
#endif // XCB_USE_XINPUT22
}
+bool QXcbConnection::xi2MouseEventsDisabled() const
+{
+ static bool xi2MouseDisabled = qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
+ // FIXME: Don't use XInput2 mouse events when Xinerama extension
+ // is enabled, because it causes problems with multi-monitor setup.
+ return xi2MouseDisabled || has_xinerama_extension;
+}
+
#ifdef XCB_USE_XINPUT22
-static qreal valuatorNormalized(double value, XIValuatorClassInfo *vci)
+bool QXcbConnection::isTouchScreen(int id)
{
- if (value > vci->max)
- value = vci->max;
- if (value < vci->min)
- value = vci->min;
- return (value - vci->min) / (vci->max - vci->min);
+ auto device = touchDeviceForId(id);
+ return device && device->qtTouchDevice->type() == QTouchDevice::TouchScreen;
}
void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow)
{
xXIDeviceEvent *xiDeviceEvent = static_cast<xXIDeviceEvent *>(xiDevEvent);
- XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
+ TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
Q_ASSERT(dev);
const bool firstTouch = dev->touchPoints.isEmpty();
if (xiDeviceEvent->evtype == XI_TouchBegin) {
@@ -617,53 +672,53 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
qreal nx = -1.0, ny = -1.0;
qreal w = 0.0, h = 0.0;
bool majorAxisIsY = touchPoint.area.height() > touchPoint.area.width();
- for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
- XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
- if (classinfo->type == XIValuatorClass) {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
- int n = vci->number;
- double value;
- if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
- continue;
- if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, " valuator %20s value %lf from range %lf -> %lf",
- atomName(vci->label).constData(), value, vci->min, vci->max );
- if (vci->label == atom(QXcbAtom::RelX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::RelY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
- const qreal sw = screen->geometry().width();
- const qreal sh = screen->geometry().height();
- w = valuatorNormalized(value, vci) * std::sqrt(sw * sw + sh * sh);
- } else if (vci->label == atom(QXcbAtom::AbsMTTouchMinor)) {
- const qreal sw = screen->geometry().width();
- const qreal sh = screen->geometry().height();
- h = valuatorNormalized(value, vci) * std::sqrt(sw * sw + sh * sh);
- } else if (vci->label == atom(QXcbAtom::AbsMTOrientation)) {
- // Find the closest axis.
- // 0 corresponds to the Y axis, vci->max to the X axis.
- // Flipping over the Y axis and rotating by 180 degrees
- // don't change the result, so normalize value to range
- // [0, vci->max] first.
- value = qAbs(value);
- while (value > vci->max)
- value -= 2 * vci->max;
- value = qAbs(value);
- majorAxisIsY = value < vci->max - value;
- } else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
- vci->label == atom(QXcbAtom::AbsPressure)) {
- touchPoint.pressure = valuatorNormalized(value, vci);
- }
+ for (const TouchDeviceData::ValuatorClassInfo vci : dev->valuatorInfo) {
+ double value;
+ if (!xi2GetValuatorValueIfSet(xiDeviceEvent, vci.number, &value))
+ continue;
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
+ qCDebug(lcQpaXInputEvents, " valuator %20s value %lf from range %lf -> %lf",
+ atomName(vci.label).constData(), value, vci.min, vci.max);
+ if (value > vci.max)
+ value = vci.max;
+ if (value < vci.min)
+ value = vci.min;
+ qreal valuatorNormalized = (value - vci.min) / (vci.max - vci.min);
+ if (vci.label == QXcbAtom::RelX) {
+ nx = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::RelY) {
+ ny = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsX) {
+ nx = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsY) {
+ ny = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsMTPositionX) {
+ nx = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsMTPositionY) {
+ ny = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsMTTouchMajor) {
+ const qreal sw = screen->geometry().width();
+ const qreal sh = screen->geometry().height();
+ w = valuatorNormalized * std::sqrt(sw * sw + sh * sh);
+ } else if (vci.label == QXcbAtom::AbsMTTouchMinor) {
+ const qreal sw = screen->geometry().width();
+ const qreal sh = screen->geometry().height();
+ h = valuatorNormalized * std::sqrt(sw * sw + sh * sh);
+ } else if (vci.label == QXcbAtom::AbsMTOrientation) {
+ // Find the closest axis.
+ // 0 corresponds to the Y axis, vci.max to the X axis.
+ // Flipping over the Y axis and rotating by 180 degrees
+ // don't change the result, so normalize value to range
+ // [0, vci.max] first.
+ value = qAbs(value);
+ while (value > vci.max)
+ value -= 2 * vci.max;
+ value = qAbs(value);
+ majorAxisIsY = value < vci.max - value;
+ } else if (vci.label == QXcbAtom::AbsMTPressure || vci.label == QXcbAtom::AbsPressure) {
+ touchPoint.pressure = valuatorNormalized;
}
+
}
// If any value was not updated, use the last-known value.
if (nx == -1.0) {
@@ -765,12 +820,12 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
bool QXcbConnection::startSystemResizeForTouchBegin(xcb_window_t window, const QPoint &point, Qt::Corner corner)
{
- QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constBegin();
+ QHash<int, TouchDeviceData>::const_iterator devIt = m_touchDevices.constBegin();
for (; devIt != m_touchDevices.constEnd(); ++devIt) {
- XInput2TouchDeviceData *deviceData = devIt.value();
- if (deviceData->qtTouchDevice->type() == QTouchDevice::TouchScreen) {
- QHash<int, QPointF>::const_iterator pointIt = deviceData->pointPressedPosition.constBegin();
- for (; pointIt != deviceData->pointPressedPosition.constEnd(); ++pointIt) {
+ TouchDeviceData deviceData = devIt.value();
+ if (deviceData.qtTouchDevice->type() == QTouchDevice::TouchScreen) {
+ QHash<int, QPointF>::const_iterator pointIt = deviceData.pointPressedPosition.constBegin();
+ for (; pointIt != deviceData.pointPressedPosition.constEnd(); ++pointIt) {
if (pointIt.value().toPoint() == point) {
m_startSystemResizeInfo.window = window;
m_startSystemResizeInfo.deviceid = devIt.key();
@@ -783,6 +838,7 @@ bool QXcbConnection::startSystemResizeForTouchBegin(xcb_window_t window, const Q
}
return false;
}
+#endif // XCB_USE_XINPUT22
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
{
@@ -843,59 +899,70 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
return grabbed;
}
-#endif // XCB_USE_XINPUT22
-void QXcbConnection::xi2HandleHierachyEvent(void *event)
+void QXcbConnection::xi2HandleHierarchyEvent(void *event)
{
xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event);
// We only care about hotplugged devices
if (!(xiEvent->flags & (XISlaveRemoved | XISlaveAdded)))
return;
+
xi2SetupDevices();
- // Reselect events for all event-listening windows.
- for (auto it = m_mapper.cbegin(), end = m_mapper.cend(); it != end; ++it)
- xi2Select(it.key());
+
+ if (xi2MouseEventsDisabled()) {
+ // In compatibility mode (a.k.a xi2MouseEventsDisabled() mode) we select events for
+ // each device separately. When a new device appears, we have to select events from
+ // this device on all event-listening windows. This is not needed when events are
+ // selected via XIAllDevices/XIAllMasterDevices (as in xi2SelectDeviceEvents()).
+ for (auto it = m_mapper.cbegin(), end = m_mapper.cend(); it != end; ++it)
+ xi2SelectDeviceEventsCompatibility(it.key());
+ }
}
void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
{
xXIDeviceChangedEvent *xiEvent = reinterpret_cast<xXIDeviceChangedEvent *>(event);
-
- // ### If a slave device changes (XIDeviceChange), we should probably run setup on it again.
- if (xiEvent->reason != XISlaveSwitch)
- return;
-
+ switch (xiEvent->reason) {
+ case XIDeviceChange: {
+ int nrDevices = 0;
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ XIDeviceInfo* deviceInfo = XIQueryDevice(dpy, xiEvent->sourceid, &nrDevices);
+ if (nrDevices <= 0)
+ return;
+ xi2SetupDevice(deviceInfo);
+ XIFreeDeviceInfo(deviceInfo);
+ break;
+ }
+ case XISlaveSwitch: {
#ifdef XCB_USE_XINPUT21
- // This code handles broken scrolling device drivers that reset absolute positions
- // when they are made active. Whenever a new slave device is made active the
- // primary pointer sends a DeviceChanged event with XISlaveSwitch, and the new
- // active slave in sourceid.
-
- QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(xiEvent->sourceid);
- if (device == m_scrollingDevices.end())
- return;
+ if (ScrollingDevice *scrollingDevice = scrollingDeviceForId(xiEvent->sourceid))
+ xi2UpdateScrollingDevice(*scrollingDevice);
+#endif
+ break;
+ }
+ default:
+ qCDebug(lcQpaXInputEvents, "unknown device-changed-event (device %d)", xiEvent->sourceid);
+ break;
+ }
+}
+#ifdef XCB_USE_XINPUT21
+void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice)
+{
int nrDevices = 0;
- XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), xiEvent->sourceid, &nrDevices);
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ XIDeviceInfo* deviceInfo = XIQueryDevice(dpy, scrollingDevice.deviceId, &nrDevices);
if (nrDevices <= 0) {
- qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", xiEvent->sourceid);
+ qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId);
return;
}
- updateScrollingDevice(*device, xiDeviceInfo->num_classes, xiDeviceInfo->classes);
- XIFreeDeviceInfo(xiDeviceInfo);
-#endif
-}
-
-void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int num_classes, void *classInfo)
-{
-#ifdef XCB_USE_XINPUT21
- XIAnyClassInfo **classes = reinterpret_cast<XIAnyClassInfo**>(classInfo);
QPointF lastScrollPosition;
if (lcQpaXInput().isDebugEnabled())
lastScrollPosition = scrollingDevice.lastScrollPosition;
- for (int c = 0; c < num_classes; ++c) {
- if (classes[c]->type == XIValuatorClass) {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classes[c]);
+ for (int c = 0; c < deviceInfo->num_classes; ++c) {
+ XIAnyClassInfo *classInfo = deviceInfo->classes[c];
+ if (classInfo->type == XIValuatorClass) {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classInfo);
const int valuatorAtom = qatom(vci->label);
if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
scrollingDevice.lastScrollPosition.setX(vci->value);
@@ -908,37 +975,30 @@ void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int
lastScrollPosition.x(), lastScrollPosition.y(),
scrollingDevice.lastScrollPosition.x(),
scrollingDevice.lastScrollPosition.y());
-#else
- Q_UNUSED(scrollingDevice);
- Q_UNUSED(num_classes);
- Q_UNUSED(classInfo);
-#endif
+
+ XIFreeDeviceInfo(deviceInfo);
}
-#ifdef XCB_USE_XINPUT21
-void QXcbConnection::handleEnterEvent()
+void QXcbConnection::xi2UpdateScrollingDevices()
{
QHash<int, ScrollingDevice>::iterator it = m_scrollingDevices.begin();
const QHash<int, ScrollingDevice>::iterator end = m_scrollingDevices.end();
while (it != end) {
- ScrollingDevice& scrollingDevice = it.value();
- int nrDevices = 0;
- XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), scrollingDevice.deviceId, &nrDevices);
- if (nrDevices <= 0) {
- qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId);
- it = m_scrollingDevices.erase(it);
- continue;
- }
- updateScrollingDevice(scrollingDevice, xiDeviceInfo->num_classes, xiDeviceInfo->classes);
- XIFreeDeviceInfo(xiDeviceInfo);
+ xi2UpdateScrollingDevice(it.value());
++it;
}
}
-#endif
+
+QXcbConnection::ScrollingDevice *QXcbConnection::scrollingDeviceForId(int id)
+{
+ ScrollingDevice *dev = nullptr;
+ if (m_scrollingDevices.contains(id))
+ dev = &m_scrollingDevices[id];
+ return dev;
+}
void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice)
{
-#ifdef XCB_USE_XINPUT21
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
if (xiEvent->evtype == XI_Motion && scrollingDevice.orientations) {
@@ -1008,10 +1068,51 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
}
}
}
-#else
- Q_UNUSED(event);
- Q_UNUSED(scrollingDevice);
+}
#endif // XCB_USE_XINPUT21
+
+static int xi2ValuatorOffset(const unsigned char *maskPtr, int maskLen, int number)
+{
+ int offset = 0;
+ for (int i = 0; i < maskLen; i++) {
+ if (number < 8) {
+ if ((maskPtr[i] & (1 << number)) == 0)
+ return -1;
+ }
+ for (int j = 0; j < 8; j++) {
+ if (j == number)
+ return offset;
+ if (maskPtr[i] & (1 << j))
+ offset++;
+ }
+ number -= 8;
+ }
+ return -1;
+}
+
+bool QXcbConnection::xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value)
+{
+ const xXIDeviceEvent *xideviceevent = static_cast<const xXIDeviceEvent *>(event);
+ const unsigned char *buttonsMaskAddr = (const unsigned char*)&xideviceevent[1];
+ const unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
+ FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
+
+ int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum);
+ if (valuatorOffset < 0)
+ return false;
+
+ *value = valuatorsValuesAddr[valuatorOffset].integral;
+ *value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16));
+ return true;
+}
+
+void QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event)
+{
+ // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
+ // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
+ // Move this data back to have the same layout in memory as it was on the wire
+ // and allow casting, overwriting the full_sequence field.
+ memmove((char*) event + 32, (char*) event + 36, event->length * 4);
}
Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
@@ -1028,15 +1129,6 @@ Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
return Qt::NoButton;
}
-#ifdef XCB_USE_XINPUT22
-bool QXcbConnection::isTouchScreen(int id) const
-{
- auto device = m_touchDevices.value(id);
- return device && device->qtTouchDevice
- && device->qtTouchDevice->type() == QTouchDevice::TouchScreen;
-}
-#endif
-
#if QT_CONFIG(tabletevent)
static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) {
// keep in sync with wacom_intuos_inout() in Linux kernel driver wacom_wac.c
diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp
index 7c62c2e2b3..da63360333 100644
--- a/src/plugins/platforms/xcb/qxcbcursor.cpp
+++ b/src/plugins/platforms/xcb/qxcbcursor.cpp
@@ -579,7 +579,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
if (cursor)
return cursor;
if (!cursor && cursorId) {
- cursor = XCreateFontCursor(DISPLAY_FROM_XCB(this), cursorId);
+ cursor = XCreateFontCursor(static_cast<Display *>(connection()->xlib_display()), cursorId);
if (cursor)
return cursor;
}
@@ -631,10 +631,9 @@ void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDes
*pos = QPoint();
xcb_window_t root = c->primaryVirtualDesktop()->root();
- xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root);
- xcb_generic_error_t *err = 0;
- xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err);
- if (!err && reply) {
+
+ auto reply = Q_XCB_REPLY(xcb_query_pointer, c->xcb_connection(), root);
+ if (reply) {
if (virtualDesktop) {
const auto virtualDesktops = c->virtualDesktops();
for (QXcbVirtualDesktop *vd : virtualDesktops) {
@@ -648,11 +647,8 @@ void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDes
*pos = QPoint(reply->root_x, reply->root_y);
if (keybMask)
*keybMask = reply->mask;
- free(reply);
return;
}
- free(err);
- free(reply);
}
QPoint QXcbCursor::pos() const
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index 60d142157f..d4521de8e0 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -94,32 +94,27 @@ static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w)
{
xcb_window_t proxy = XCB_NONE;
- xcb_get_property_cookie_t cookie = Q_XCB_CALL2(xcb_get_property(c->xcb_connection(), false, w, c->atom(QXcbAtom::XdndProxy),
- XCB_ATOM_WINDOW, 0, 1), c);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(),
+ false, w, c->atom(QXcbAtom::XdndProxy), XCB_ATOM_WINDOW, 0, 1);
if (reply && reply->type == XCB_ATOM_WINDOW)
- proxy = *((xcb_window_t *)xcb_get_property_value(reply));
- free(reply);
+ proxy = *((xcb_window_t *)xcb_get_property_value(reply.get()));
if (proxy == XCB_NONE)
return proxy;
// exists and is real?
- cookie = Q_XCB_CALL2(xcb_get_property(c->xcb_connection(), false, proxy, c->atom(QXcbAtom::XdndProxy),
- XCB_ATOM_WINDOW, 0, 1), c);
- reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0);
+ reply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(),
+ false, proxy, c->atom(QXcbAtom::XdndProxy), XCB_ATOM_WINDOW, 0, 1);
if (reply && reply->type == XCB_ATOM_WINDOW) {
- xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply));
+ xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply.get()));
if (proxy != p)
proxy = 0;
} else {
proxy = 0;
}
- free(reply);
-
return proxy;
}
@@ -142,7 +137,7 @@ protected:
QXcbDrag::QXcbDrag(QXcbConnection *c) : QXcbObject(c)
{
- dropData = new QXcbDropData(this);
+ m_dropData = new QXcbDropData(this);
init();
cleanup_timer = -1;
@@ -150,7 +145,7 @@ QXcbDrag::QXcbDrag(QXcbConnection *c) : QXcbObject(c)
QXcbDrag::~QXcbDrag()
{
- delete dropData;
+ delete m_dropData;
}
void QXcbDrag::init()
@@ -172,11 +167,6 @@ void QXcbDrag::init()
drag_types.clear();
}
-QMimeData *QXcbDrag::platformDropData()
-{
- return dropData;
-}
-
bool QXcbDrag::eventFilter(QObject *o, QEvent *e)
{
/* We are setting a mouse grab on the QShapedPixmapWindow in order not to
@@ -228,28 +218,19 @@ void QXcbDrag::endDrag()
initiatorWindow.clear();
}
-static xcb_translate_coordinates_reply_t *
-translateCoordinates(QXcbConnection *c, xcb_window_t from, xcb_window_t to, int x, int y)
-{
- xcb_translate_coordinates_cookie_t cookie =
- xcb_translate_coordinates(c->xcb_connection(), from, to, x, y);
- return xcb_translate_coordinates_reply(c->xcb_connection(), cookie, 0);
-}
-
static
bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint & pos, xcb_window_t w, xcb_shape_sk_t shapeType)
{
bool interacts = false;
- xcb_shape_get_rectangles_reply_t *reply = xcb_shape_get_rectangles_reply(connection, xcb_shape_get_rectangles(connection, w, shapeType), NULL);
+ auto reply = Q_XCB_REPLY(xcb_shape_get_rectangles, connection, w, shapeType);
if (reply) {
- xcb_rectangle_t *rectangles = xcb_shape_get_rectangles_rectangles(reply);
+ xcb_rectangle_t *rectangles = xcb_shape_get_rectangles_rectangles(reply.get());
if (rectangles) {
- const int nRectangles = xcb_shape_get_rectangles_rectangles_length(reply);
+ const int nRectangles = xcb_shape_get_rectangles_rectangles_length(reply.get());
for (int i = 0; !interacts && i < nRectangles; ++i) {
interacts = QRect(rectangles[i].x, rectangles[i].y, rectangles[i].width, rectangles[i].height).contains(pos);
}
}
- free(reply);
}
return interacts;
@@ -261,33 +242,25 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md
return 0;
if (md) {
- xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(xcb_connection(), w);
- xcb_get_window_attributes_reply_t *reply = xcb_get_window_attributes_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_window_attributes, xcb_connection(), w);
if (!reply)
return 0;
if (reply->map_state != XCB_MAP_STATE_VIEWABLE)
return 0;
- free(reply);
-
- xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(xcb_connection(), w);
- xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(xcb_connection(), gcookie, 0);
+ auto greply = Q_XCB_REPLY(xcb_get_geometry, xcb_connection(), w);
if (!greply)
return 0;
QRect windowRect(greply->x, greply->y, greply->width, greply->height);
- free(greply);
if (windowRect.contains(pos)) {
bool windowContainsMouse = !ignoreNonXdndAwareWindows;
{
- xcb_get_property_cookie_t cookie =
- Q_XCB_CALL(xcb_get_property(xcb_connection(), false, w, connection()->atom(QXcbAtom::XdndAware),
- XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
-
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
+ false, w, connection()->atom(QXcbAtom::XdndAware),
+ XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
bool isAware = reply && reply->type != XCB_NONE;
- free(reply);
if (isAware) {
const QPoint relPos = pos - windowRect.topLeft();
// When ShapeInput and ShapeBounding are not set they return a single rectangle with the geometry of the window, this is why we
@@ -303,19 +276,16 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md
}
}
- xcb_query_tree_cookie_t cookie = xcb_query_tree (xcb_connection(), w);
- xcb_query_tree_reply_t *reply = xcb_query_tree_reply(xcb_connection(), cookie, 0);
-
+ auto reply = Q_XCB_REPLY(xcb_query_tree, xcb_connection(), w);
if (!reply)
return 0;
- int nc = xcb_query_tree_children_length(reply);
- xcb_window_t *c = xcb_query_tree_children(reply);
+ int nc = xcb_query_tree_children_length(reply.get());
+ xcb_window_t *c = xcb_query_tree_children(reply.get());
xcb_window_t r = 0;
for (uint i = nc; !r && i--;)
r = findRealWindow(pos - windowRect.topLeft(), c[i], md-1, ignoreNonXdndAwareWindows);
- free(reply);
if (r)
return r;
@@ -356,15 +326,14 @@ void QXcbDrag::move(const QPoint &globalPos)
}
xcb_window_t rootwin = current_virtual_desktop->root();
- xcb_translate_coordinates_reply_t *translate =
- ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y());
+ auto translate = Q_XCB_REPLY(xcb_translate_coordinates, connection()->xcb_connection(),
+ rootwin, rootwin, globalPos.x(), globalPos.y());
if (!translate)
return;
xcb_window_t target = translate->child;
int lx = translate->dst_x;
int ly = translate->dst_y;
- free (translate);
if (target && target != rootwin) {
xcb_window_t src = rootwin;
@@ -372,7 +341,8 @@ void QXcbDrag::move(const QPoint &globalPos)
DNDDEBUG << "checking target for XdndAware" << target << lx << ly;
// translate coordinates
- translate = ::translateCoordinates(connection(), src, target, lx, ly);
+ auto translate = Q_XCB_REPLY(xcb_translate_coordinates, connection()->xcb_connection(),
+ src, target, lx, ly);
if (!translate) {
target = 0;
break;
@@ -381,14 +351,11 @@ void QXcbDrag::move(const QPoint &globalPos)
ly = translate->dst_y;
src = target;
xcb_window_t child = translate->child;
- free(translate);
// check if it has XdndAware
- xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, target,
- atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, target,
+ atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
bool aware = reply && reply->type != XCB_NONE;
- free(reply);
if (aware) {
DNDDEBUG << "Found XdndAware on " << target;
break;
@@ -422,16 +389,14 @@ void QXcbDrag::move(const QPoint &globalPos)
int target_version = 1;
if (proxy_target) {
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, proxy_target,
- atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
+ false, proxy_target,
+ atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
if (!reply || reply->type == XCB_NONE)
target = 0;
- target_version = *(uint32_t *)xcb_get_property_value(reply);
+ target_version = *(uint32_t *)xcb_get_property_value(reply.get());
target_version = qMin(xdnd_version, target_version ? target_version : 1);
-
- free(reply);
}
if (target != current_target) {
@@ -714,21 +679,19 @@ void QXcbDrag::handleEnter(QPlatformWindow *window, const xcb_client_message_eve
if (event->data.data32[1] & 1) {
// get the types from XdndTypeList
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, xdnd_dragsource,
- atom(QXcbAtom::XdndTypelist), XCB_ATOM_ATOM,
- 0, xdnd_max_type);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource,
+ atom(QXcbAtom::XdndTypelist), XCB_ATOM_ATOM,
+ 0, xdnd_max_type);
if (reply && reply->type != XCB_NONE && reply->format == 32) {
- int length = xcb_get_property_value_length(reply) / 4;
+ int length = xcb_get_property_value_length(reply.get()) / 4;
if (length > xdnd_max_type)
length = xdnd_max_type;
- xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply);
+ xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
xdnd_types.reserve(length);
for (int i = 0; i < length; ++i)
xdnd_types.append(atoms[i]);
}
- free(reply);
} else {
// get the types from the message
for(int i = 2; i < 5; i++) {
@@ -769,7 +732,7 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
dropData = currentDrag()->mimeData();
supported_actions = currentDrag()->supportedActions();
} else {
- dropData = platformDropData();
+ dropData = m_dropData;
supported_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
}
@@ -812,8 +775,8 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
handle_xdnd_status(&response);
else
#endif
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, current_proxy_target,
- XCB_EVENT_MASK_NO_EVENT, (const char *)&response));
+ xcb_send_event(xcb_connection(), false, current_proxy_target,
+ XCB_EVENT_MASK_NO_EVENT, (const char *)&response);
}
namespace
@@ -990,7 +953,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
dropData = currentDrag()->mimeData();
supported_drop_actions = Qt::DropActions(l[4]);
} else {
- dropData = platformDropData();
+ dropData = m_dropData;
supported_drop_actions = accepted_drop_action;
// Drop coming from another app? Update keyboard modifiers.
@@ -1017,8 +980,8 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE;
finished.data.data32[1] = response.isAccepted(); // flags
finished.data.data32[2] = toXdndAction(response.acceptedAction());
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, current_proxy_target,
- XCB_EVENT_MASK_NO_EVENT, (char *)&finished));
+ xcb_send_event(xcb_connection(), false, current_proxy_target,
+ XCB_EVENT_MASK_NO_EVENT, (char *)&finished);
xdnd_dragsource = 0;
currentWindow.clear();
@@ -1132,28 +1095,20 @@ static xcb_window_t findXdndAwareParent(QXcbConnection *c, xcb_window_t window)
xcb_window_t target = 0;
forever {
// check if window has XdndAware
- xcb_get_property_cookie_t gpCookie = Q_XCB_CALL(
- xcb_get_property(c->xcb_connection(), false, window,
- c->atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
- xcb_get_property_reply_t *gpReply = xcb_get_property_reply(
- c->xcb_connection(), gpCookie, 0);
+ auto gpReply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(), false, window,
+ c->atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
bool aware = gpReply && gpReply->type != XCB_NONE;
- free(gpReply);
if (aware) {
target = window;
break;
}
// try window's parent
- xcb_query_tree_cookie_t qtCookie = Q_XCB_CALL(
- xcb_query_tree_unchecked(c->xcb_connection(), window));
- xcb_query_tree_reply_t *qtReply = xcb_query_tree_reply(
- c->xcb_connection(), qtCookie, NULL);
+ auto qtReply = Q_XCB_REPLY_UNCHECKED(xcb_query_tree, c->xcb_connection(), window);
if (!qtReply)
break;
xcb_window_t root = qtReply->root;
xcb_window_t parent = qtReply->parent;
- free(qtReply);
if (window == root)
break;
window = parent;
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
index 2d152edf76..f261cc1322 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.h
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -74,7 +74,6 @@ public:
QXcbDrag(QXcbConnection *c);
~QXcbDrag();
- QMimeData *platformDropData() override;
bool eventFilter(QObject *o, QEvent *e) override;
void startDrag() override;
@@ -117,7 +116,7 @@ private:
QPointer<QWindow> currentWindow;
QPoint currentPosition;
- QXcbDropData *dropData;
+ QXcbDropData *m_dropData;
Qt::DropAction accepted_drop_action;
QWindow *desktop_proxy;
diff --git a/src/plugins/platforms/xcb/qxcbimage.cpp b/src/plugins/platforms/xcb/qxcbimage.cpp
index c419bd913d..36536e0602 100644
--- a/src/plugins/platforms/xcb/qxcbimage.cpp
+++ b/src/plugins/platforms/xcb/qxcbimage.cpp
@@ -101,19 +101,14 @@ QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap
{
xcb_connection_t *conn = connection->xcb_connection();
- xcb_get_image_cookie_t get_image_cookie =
- xcb_get_image_unchecked(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap,
- 0, 0, width, height, 0xffffffff);
-
- xcb_get_image_reply_t *image_reply =
- xcb_get_image_reply(conn, get_image_cookie, NULL);
-
+ auto image_reply = Q_XCB_REPLY_UNCHECKED(xcb_get_image, conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap,
+ 0, 0, width, height, 0xffffffff);
if (!image_reply) {
return QPixmap();
}
- uint8_t *data = xcb_get_image_data(image_reply);
- uint32_t length = xcb_get_image_data_length(image_reply);
+ uint8_t *data = xcb_get_image_data(image_reply.get());
+ uint32_t length = xcb_get_image_data_length(image_reply.get());
QPixmap result;
@@ -176,7 +171,6 @@ QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap
result = QPixmap::fromImage(image.copy());
}
- free(image_reply);
return result;
}
@@ -214,22 +208,15 @@ xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
xcb_connection_t *conn = screen->xcb_connection();
const int w = image.width();
const int h = image.height();
- xcb_generic_error_t *error = 0;
- xcb_render_query_pict_formats_cookie_t formatsCookie = xcb_render_query_pict_formats(conn);
- xcb_render_query_pict_formats_reply_t *formatsReply = xcb_render_query_pict_formats_reply(conn,
- formatsCookie,
- &error);
- if (!formatsReply || error) {
+ auto formats = Q_XCB_REPLY(xcb_render_query_pict_formats, conn);
+ if (!formats) {
qWarning("qt_xcb_createCursorXRender: query_pict_formats failed");
- free(formatsReply);
- free(error);
return XCB_NONE;
}
- xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formatsReply,
+ xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formats.get(),
XCB_PICT_STANDARD_ARGB_32);
if (!fmt) {
qWarning("qt_xcb_createCursorXRender: Failed to find format PICT_STANDARD_ARGB_32");
- free(formatsReply);
return XCB_NONE;
}
@@ -241,17 +228,15 @@ xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
0, 0, 0);
if (!xi) {
qWarning("qt_xcb_createCursorXRender: xcb_image_create failed");
- free(formatsReply);
return XCB_NONE;
}
xi->data = (uint8_t *) malloc(xi->stride * h);
if (!xi->data) {
qWarning("qt_xcb_createCursorXRender: Failed to malloc() image data");
xcb_image_destroy(xi);
- free(formatsReply);
return XCB_NONE;
}
- memcpy(xi->data, img.constBits(), img.byteCount());
+ memcpy(xi->data, img.constBits(), img.sizeInBytes());
xcb_pixmap_t pix = xcb_generate_id(conn);
xcb_create_pixmap(conn, 32, pix, screen->root(), w, h);
@@ -271,7 +256,6 @@ xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
xcb_image_destroy(xi);
xcb_render_free_picture(conn, pic);
xcb_free_pixmap(conn, pix);
- free(formatsReply);
return cursor;
#else
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index c9ecdceb0d..72d31060db 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -65,6 +65,11 @@
#if QT_CONFIG(xcb_xlib)
#include <X11/Xlib.h>
+#if QT_CONFIG(xcb_native_painting)
+#include "qxcbnativepainting.h"
+#include "qpixmap_x11_p.h"
+#include "qbackingstore_x11_p.h"
+#endif
#endif
#include <qpa/qplatforminputcontextfactory_p.h>
@@ -83,6 +88,11 @@
#include <QtCore/QFileInfo>
+#if QT_CONFIG(vulkan)
+#include "qxcbvulkaninstance.h"
+#include "qxcbvulkanwindow.h"
+#endif
+
QT_BEGIN_NAMESPACE
// Find out if our parent process is gdb by looking at the 'exe' symlink under /proc,.
@@ -200,6 +210,13 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
}
m_fontDatabase.reset(new QGenericUnixFontDatabase());
+
+#if QT_CONFIG(xcb_native_painting)
+ if (nativePaintingEnabled()) {
+ qDebug("QXCB USING NATIVE PAINTING");
+ qt_xcb_native_x11_info_init(defaultConnection());
+ }
+#endif
}
QXcbIntegration::~QXcbIntegration()
@@ -208,15 +225,33 @@ QXcbIntegration::~QXcbIntegration()
m_instance = Q_NULLPTR;
}
+QPlatformPixmap *QXcbIntegration::createPlatformPixmap(QPlatformPixmap::PixelType type) const
+{
+#if QT_CONFIG(xcb_native_painting)
+ if (nativePaintingEnabled())
+ return new QX11PlatformPixmap(type);
+#endif
+
+ return QPlatformIntegration::createPlatformPixmap(type);
+}
+
QPlatformWindow *QXcbIntegration::createPlatformWindow(QWindow *window) const
{
QXcbScreen *screen = static_cast<QXcbScreen *>(window->screen()->handle());
QXcbGlIntegration *glIntegration = screen->connection()->glIntegration();
- if (window->type() != Qt::Desktop && window->supportsOpenGL()) {
- if (glIntegration) {
- QXcbWindow *xcbWindow = glIntegration->createWindow(window);
+ if (window->type() != Qt::Desktop) {
+ if (window->supportsOpenGL()) {
+ if (glIntegration) {
+ QXcbWindow *xcbWindow = glIntegration->createWindow(window);
+ xcbWindow->create();
+ return xcbWindow;
+ }
+#if QT_CONFIG(vulkan)
+ } else if (window->surfaceType() == QSurface::VulkanSurface) {
+ QXcbWindow *xcbWindow = new QXcbVulkanWindow(window);
xcbWindow->create();
return xcbWindow;
+#endif
}
}
@@ -247,6 +282,11 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont
QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *window) const
{
+#if QT_CONFIG(xcb_native_painting)
+ if (nativePaintingEnabled())
+ return new QXcbNativeBackingStore(window);
+#endif
+
return new QXcbBackingStore(window);
}
@@ -498,4 +538,21 @@ void QXcbIntegration::beep() const
xcb_bell(connection, 0);
}
+bool QXcbIntegration::nativePaintingEnabled() const
+{
+#if QT_CONFIG(xcb_native_painting)
+ static bool enabled = qEnvironmentVariableIsSet("QT_XCB_NATIVE_PAINTING");
+ return enabled;
+#else
+ return false;
+#endif
+}
+
+#if QT_CONFIG(vulkan)
+QPlatformVulkanInstance *QXcbIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
+{
+ return new QXcbVulkanInstance(instance);
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h
index b3d72c19d0..186b6c5ddd 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.h
+++ b/src/plugins/platforms/xcb/qxcbintegration.h
@@ -61,6 +61,7 @@ public:
QXcbIntegration(const QStringList &parameters, int &argc, char **argv);
~QXcbIntegration();
+ QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const override;
QPlatformWindow *createPlatformWindow(QWindow *window) const override;
QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override;
#ifndef QT_NO_OPENGL
@@ -114,6 +115,12 @@ public:
void beep() const override;
+ bool nativePaintingEnabled() const;
+
+#if QT_CONFIG(vulkan)
+ QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
+#endif
+
static QXcbIntegration *instance() { return m_instance; }
private:
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 2e29c208c7..e24bd07b6f 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -53,7 +53,7 @@
#include <stdio.h>
#include <X11/keysym.h>
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
#include <X11/extensions/XI2proto.h>
#undef KeyPress
#undef KeyRelease
@@ -612,23 +612,18 @@ Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const
void QXcbKeyboard::readXKBConfig()
{
clearXKBConfig();
- xcb_generic_error_t *error;
- xcb_get_property_cookie_t cookie;
- xcb_get_property_reply_t *config_reply;
xcb_connection_t *c = xcb_connection();
xcb_window_t rootWindow = connection()->rootWindow();
- cookie = xcb_get_property(c, 0, rootWindow,
- atom(QXcbAtom::_XKB_RULES_NAMES), XCB_ATOM_STRING, 0, 1024);
-
- config_reply = xcb_get_property_reply(c, cookie, &error);
+ auto config_reply = Q_XCB_REPLY(xcb_get_property, c, 0, rootWindow,
+ atom(QXcbAtom::_XKB_RULES_NAMES), XCB_ATOM_STRING, 0, 1024);
if (!config_reply) {
qWarning("Qt: Couldn't interpret the _XKB_RULES_NAMES property");
return;
}
- char *xkb_config = (char *)xcb_get_property_value(config_reply);
- int length = xcb_get_property_value_length(config_reply);
+ char *xkb_config = (char *)xcb_get_property_value(config_reply.get());
+ int length = xcb_get_property_value_length(config_reply.get());
// on old X servers xkb_config can be 0 even if config_reply indicates a succesfull read
if (!xkb_config || length == 0)
@@ -653,8 +648,6 @@ void QXcbKeyboard::readXKBConfig()
xkb_names.layout = qstrdup(names[2]);
xkb_names.variant = qstrdup(names[3]);
xkb_names.options = qstrdup(names[4]);
-
- free(config_reply);
}
void QXcbKeyboard::clearXKBConfig()
@@ -807,7 +800,7 @@ void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
}
}
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
{
if (m_config && !connection()->hasXKB()) {
@@ -1172,23 +1165,19 @@ QXcbKeyboard::~QXcbKeyboard()
void QXcbKeyboard::updateVModMapping()
{
#if QT_CONFIG(xkb)
- xcb_xkb_get_names_cookie_t names_cookie;
- xcb_xkb_get_names_reply_t *name_reply;
xcb_xkb_get_names_value_list_t names_list;
memset(&vmod_masks, 0, sizeof(vmod_masks));
- names_cookie = xcb_xkb_get_names(xcb_connection(),
- XCB_XKB_ID_USE_CORE_KBD,
- XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES);
-
- name_reply = xcb_xkb_get_names_reply(xcb_connection(), names_cookie, 0);
+ auto name_reply = Q_XCB_REPLY(xcb_xkb_get_names, xcb_connection(),
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES);
if (!name_reply) {
qWarning("Qt: failed to retrieve the virtual modifier names from XKB");
return;
}
- const void *buffer = xcb_xkb_get_names_value_list(name_reply);
+ const void *buffer = xcb_xkb_get_names_value_list(name_reply.get());
xcb_xkb_get_names_value_list_unpack(buffer,
name_reply->nTypes,
name_reply->indicators,
@@ -1233,32 +1222,27 @@ void QXcbKeyboard::updateVModMapping()
else if (qstrcmp(vmod_name, "Hyper") == 0)
vmod_masks.hyper = bit;
}
-
- free(name_reply);
#endif
}
void QXcbKeyboard::updateVModToRModMapping()
{
#if QT_CONFIG(xkb)
- xcb_xkb_get_map_cookie_t map_cookie;
- xcb_xkb_get_map_reply_t *map_reply;
xcb_xkb_get_map_map_t map;
memset(&rmod_masks, 0, sizeof(rmod_masks));
- map_cookie = xcb_xkb_get_map(xcb_connection(),
- XCB_XKB_ID_USE_CORE_KBD,
- XCB_XKB_MAP_PART_VIRTUAL_MODS,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-
- map_reply = xcb_xkb_get_map_reply(xcb_connection(), map_cookie, 0);
+ auto map_reply = Q_XCB_REPLY(xcb_xkb_get_map,
+ xcb_connection(),
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_MAP_PART_VIRTUAL_MODS,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (!map_reply) {
qWarning("Qt: failed to retrieve the virtual modifier map from XKB");
return;
}
- const void *buffer = xcb_xkb_get_map_map(map_reply);
+ const void *buffer = xcb_xkb_get_map_map(map_reply.get());
xcb_xkb_get_map_map_unpack(buffer,
map_reply->nTypes,
map_reply->nKeySyms,
@@ -1301,7 +1285,6 @@ void QXcbKeyboard::updateVModToRModMapping()
rmod_masks.hyper = modmap;
}
- free(map_reply);
resolveMaskConflicts();
#endif
}
@@ -1315,14 +1298,10 @@ void QXcbKeyboard::updateModifiers()
// process for all modifiers whenever any part of the modifier mapping is changed.
memset(&rmod_masks, 0, sizeof(rmod_masks));
- xcb_generic_error_t *error = 0;
xcb_connection_t *conn = xcb_connection();
- xcb_get_modifier_mapping_cookie_t modMapCookie = xcb_get_modifier_mapping(conn);
- xcb_get_modifier_mapping_reply_t *modMapReply =
- xcb_get_modifier_mapping_reply(conn, modMapCookie, &error);
- if (error) {
+ auto modMapReply = Q_XCB_REPLY(xcb_get_modifier_mapping, conn);
+ if (!modMapReply) {
qWarning("Qt: failed to get modifier mapping");
- free(error);
return;
}
@@ -1338,7 +1317,7 @@ void QXcbKeyboard::updateModifiers()
for (size_t i = 0; i < numSymbols; ++i)
modKeyCodes[i] = xcb_key_symbols_get_keycode(m_key_symbols, symbols[i]);
- xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(modMapReply);
+ xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(modMapReply.get());
const int w = modMapReply->keycodes_per_modifier;
for (size_t i = 0; i < numSymbols; ++i) {
for (int bit = 0; bit < 8; ++bit) {
@@ -1366,7 +1345,6 @@ void QXcbKeyboard::updateModifiers()
for (size_t i = 0; i < numSymbols; ++i)
free(modKeyCodes[i]);
- free(modMapReply);
resolveMaskConflicts();
}
@@ -1449,8 +1427,6 @@ private:
void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code,
quint16 state, xcb_timestamp_t time)
{
- Q_XCB_NOOP(connection());
-
if (!m_config)
return;
@@ -1471,30 +1447,6 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
updateXKBStateFromState(kb_state, state);
xcb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, code);
-
- QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
- QMetaMethod method;
-
- if (inputContext) {
- int methodIndex = inputContext->metaObject()->indexOfMethod("x11FilterEvent(uint,uint,uint,bool)");
- if (methodIndex != -1)
- method = inputContext->metaObject()->method(methodIndex);
- }
-
- if (method.isValid()) {
- bool retval = false;
- method.invoke(inputContext, Qt::DirectConnection,
- Q_RETURN_ARG(bool, retval),
- Q_ARG(uint, sym),
- Q_ARG(uint, code),
- Q_ARG(uint, state),
- Q_ARG(bool, type == QEvent::KeyPress));
- if (retval) {
- xkb_state_unref(kb_state);
- return;
- }
- }
-
QString string = lookupString(kb_state, code);
// Ιf control modifier is set we should prefer latin character, this is
@@ -1526,6 +1478,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
}
bool filtered = false;
+ QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
if (inputContext) {
QKeyEvent event(type, qtcode, modifiers, code, sym, state, string, isAutoRepeat, string.length());
event.setTimestamp(time);
@@ -1548,16 +1501,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (isAutoRepeat && type == QEvent::KeyRelease) {
// since we removed it from the event queue using checkEvent we need to send the key press here
filtered = false;
- if (method.isValid()) {
- method.invoke(inputContext, Qt::DirectConnection,
- Q_RETURN_ARG(bool, filtered),
- Q_ARG(uint, sym),
- Q_ARG(uint, code),
- Q_ARG(uint, state),
- Q_ARG(bool, true));
- }
-
- if (!filtered && inputContext) {
+ if (inputContext) {
QKeyEvent event(QEvent::KeyPress, qtcode, modifiers, code, sym, state, string, isAutoRepeat, string.length());
event.setTimestamp(time);
filtered = inputContext->filterEvent(&event);
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index 74f9da0353..7f1c51fab8 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -74,7 +74,7 @@ public:
void updateXKBMods();
quint32 xkbModMask(quint16 state);
void updateXKBStateFromCore(quint16 state);
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
void updateXKBStateFromXI(void *modInfo, void *groupInfo);
#endif
#if QT_CONFIG(xkb)
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 97dcb8f328..caa9499c45 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -63,6 +63,10 @@
#include "qxcbnativeinterfacehandler.h"
+#if QT_CONFIG(vulkan)
+#include "qxcbvulkanwindow.h"
+#endif
+
QT_BEGIN_NAMESPACE
// return QXcbNativeInterface::ResourceType for the key.
@@ -78,7 +82,11 @@ static int resourceType(const QByteArray &key)
QByteArrayLiteral("rootwindow"),
QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"),
QByteArrayLiteral("atspibus"),
- QByteArrayLiteral("compositingenabled")
+ QByteArrayLiteral("compositingenabled"),
+ QByteArrayLiteral("vksurface"),
+ QByteArrayLiteral("generatepeekerid"),
+ QByteArrayLiteral("removepeekerid"),
+ QByteArrayLiteral("peekeventqueue")
};
const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
const QByteArray *result = std::find(names, end, key);
@@ -117,28 +125,19 @@ xcb_window_t QXcbNativeInterface::locateSystemTray(xcb_connection_t *conn, const
{
if (m_sysTraySelectionAtom == XCB_ATOM_NONE) {
const QByteArray net_sys_tray = QString::fromLatin1("_NET_SYSTEM_TRAY_S%1").arg(screen->screenNumber()).toLatin1();
- xcb_intern_atom_cookie_t intern_c =
- xcb_intern_atom_unchecked(conn, true, net_sys_tray.length(), net_sys_tray);
-
- xcb_intern_atom_reply_t *intern_r = xcb_intern_atom_reply(conn, intern_c, 0);
-
+ auto intern_r = Q_XCB_REPLY_UNCHECKED(xcb_intern_atom, conn,
+ true, net_sys_tray.length(), net_sys_tray);
if (!intern_r)
return XCB_WINDOW_NONE;
m_sysTraySelectionAtom = intern_r->atom;
- free(intern_r);
}
- xcb_get_selection_owner_cookie_t sel_owner_c = xcb_get_selection_owner_unchecked(conn, m_sysTraySelectionAtom);
- xcb_get_selection_owner_reply_t *sel_owner_r = xcb_get_selection_owner_reply(conn, sel_owner_c, 0);
-
+ auto sel_owner_r = Q_XCB_REPLY_UNCHECKED(xcb_get_selection_owner, conn, m_sysTraySelectionAtom);
if (!sel_owner_r)
return XCB_WINDOW_NONE;
- xcb_window_t selection_window = sel_owner_r->owner;
- free(sel_owner_r);
-
- return selection_window;
+ return sel_owner_r->owner;
}
bool QXcbNativeInterface::systrayVisualHasAlphaChannel()
@@ -262,6 +261,14 @@ void *QXcbNativeInterface::nativeResourceForWindow(const QByteArray &resourceStr
case Screen:
result = screenForWindow(window);
break;
+#if QT_CONFIG(vulkan)
+ case VkSurface:
+ if (window->surfaceType() == QSurface::VulkanSurface && window->handle()) {
+ // return a pointer to the VkSurfaceKHR value, not the value itself
+ result = static_cast<QXcbVulkanWindow *>(window->handle())->surface();
+ }
+ break;
+#endif
default:
break;
}
@@ -300,6 +307,13 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterfa
if (lowerCaseResource == "setstartupid")
return NativeResourceForIntegrationFunction(setStartupId);
+ if (lowerCaseResource == "generatepeekerid")
+ return NativeResourceForIntegrationFunction(generatePeekerId);
+ if (lowerCaseResource == "removepeekerid")
+ return NativeResourceForIntegrationFunction(removePeekerId);
+ if (lowerCaseResource == "peekeventqueue")
+ return NativeResourceForIntegrationFunction(peekEventQueue);
+
return 0;
}
@@ -453,21 +467,13 @@ void *QXcbNativeInterface::atspiBus()
QXcbConnection *defaultConnection = integration->defaultConnection();
if (defaultConnection) {
xcb_atom_t atspiBusAtom = defaultConnection->internAtom("AT_SPI_BUS");
- xcb_get_property_cookie_t cookie = Q_XCB_CALL2(xcb_get_property(
- defaultConnection->xcb_connection(),
- false, defaultConnection->rootWindow(),
- atspiBusAtom, XCB_ATOM_STRING, 0, 128),
- defaultConnection);
- xcb_get_property_reply_t *reply = Q_XCB_CALL2(xcb_get_property_reply(
- defaultConnection->xcb_connection(),
- cookie, 0),
- defaultConnection);
+ auto reply = Q_XCB_REPLY(xcb_get_property, defaultConnection->xcb_connection(),
+ false, defaultConnection->rootWindow(),
+ atspiBusAtom, XCB_ATOM_STRING, 0, 128);
Q_ASSERT(!reply->bytes_after);
- char *data = (char *)xcb_get_property_value(reply);
- int length = xcb_get_property_value_length(reply);
- QByteArray *busAddress = new QByteArray(data, length);
- free(reply);
- return busAddress;
+ char *data = (char *)xcb_get_property_value(reply.get());
+ int length = xcb_get_property_value_length(reply.get());
+ return new QByteArray(data, length);
}
return 0;
}
@@ -486,6 +492,25 @@ void QXcbNativeInterface::setAppUserTime(QScreen* screen, xcb_timestamp_t time)
}
}
+qint32 QXcbNativeInterface::generatePeekerId()
+{
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ return integration->defaultConnection()->generatePeekerId();
+}
+
+bool QXcbNativeInterface::removePeekerId(qint32 peekerId)
+{
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ return integration->defaultConnection()->removePeekerId(peekerId);
+}
+
+bool QXcbNativeInterface::peekEventQueue(QXcbConnection::PeekerCallback peeker, void *peekerData,
+ QXcbConnection::PeekOptions option, qint32 peekerId)
+{
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ return integration->defaultConnection()->peekEventQueue(peeker, peekerData, option, peekerId);
+}
+
void QXcbNativeInterface::setStartupId(const char *data)
{
QByteArray startupId(data);
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
index 4186d77f4d..fb0db727aa 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
@@ -46,12 +46,11 @@
#include <QtCore/QRect>
#include "qxcbexport.h"
+#include "qxcbconnection.h"
QT_BEGIN_NAMESPACE
-class QWidget;
class QXcbScreen;
-class QXcbConnection;
class QXcbNativeInterfaceHandler;
class Q_XCB_EXPORT QXcbNativeInterface : public QPlatformNativeInterface
@@ -73,7 +72,11 @@ public:
ScreenSubpixelType,
ScreenAntialiasingEnabled,
AtspiBus,
- CompositingEnabled
+ CompositingEnabled,
+ VkSurface,
+ GeneratePeekerId,
+ RemovePeekerId,
+ PeekEventQueue
};
QXcbNativeInterface();
@@ -113,6 +116,12 @@ public:
static void setAppTime(QScreen *screen, xcb_timestamp_t time);
static void setAppUserTime(QScreen *screen, xcb_timestamp_t time);
+ static qint32 generatePeekerId();
+ static bool removePeekerId(qint32 peekerId);
+ static bool peekEventQueue(QXcbConnection::PeekerCallback peeker, void *peekerData = nullptr,
+ QXcbConnection::PeekOptions option = QXcbConnection::PeekDefault,
+ qint32 peekerId = -1);
+
Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const;
Q_INVOKABLE void setParentRelativeBackPixmap(QWindow *window);
Q_INVOKABLE bool systrayVisualHasAlphaChannel();
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 5e136b5d7e..ec0f9ba561 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -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()
@@ -123,18 +184,33 @@ void QXcbVirtualDesktop::subscribeToXFixesSelectionNotify()
const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
- Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->getQtSelectionOwner(), m_net_wm_cm_atom, mask));
+ xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->getQtSelectionOwner(), m_net_wm_cm_atom, mask);
}
}
+/*! \internal
+
+ Using _NET_WORKAREA to calculate the available desktop geometry on multi-head systems (systems
+ with more than one monitor) is unreliable. Different WMs have different interpretations of what
+ _NET_WORKAREA means with multiple attached monitors. This gets worse when monitors have
+ different dimensions and/or screens are not virtually aligned. In Qt we want the available
+ geometry per monitor (QScreen), not desktop (represented by _NET_WORKAREA). WM specification
+ does not have an atom for this. Thus, QScreen is limted by the lack of support from the
+ underlying system.
+
+ One option could be that Qt does WM's job of calculating this by subtracting geometries of
+ _NET_WM_STRUT_PARTIAL and windows where _NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_DOCK.
+ But this won't work on Gnome 3 shell as it seems that on this desktop environment the tool panel
+ is painted directly on the root window. Maybe there is some Gnome/GTK API that could be used
+ to get height of the panel, but I did not find one. Maybe other WMs have their own tricks, so
+ the reliability of this approach is questionable.
+ */
QRect QXcbVirtualDesktop::getWorkArea() const
{
QRect r;
- xcb_get_property_reply_t * workArea =
- xcb_get_property_reply(xcb_connection(),
- xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
- atom(QXcbAtom::_NET_WORKAREA),
- XCB_ATOM_CARDINAL, 0, 1024), NULL);
+ auto workArea = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), false, screen()->root,
+ atom(QXcbAtom::_NET_WORKAREA),
+ XCB_ATOM_CARDINAL, 0, 1024);
if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) {
// If workArea->value_len > 4, the remaining ones seem to be for WM's virtual desktops
// (don't mess with QXcbVirtualDesktop which represents an X screen).
@@ -142,12 +218,11 @@ QRect QXcbVirtualDesktop::getWorkArea() const
// "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop.
// But for now just assume the first 4 values give us the geometry of the
// "work area", AKA "available geometry"
- uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea);
+ uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea.get());
r = QRect(geom[0], geom[1], geom[2], geom[3]);
} else {
r = QRect(QPoint(), size());
}
- free(workArea);
return r;
}
@@ -167,6 +242,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)
@@ -181,14 +420,11 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe
{
if (connection->hasXRandr()) {
xcb_randr_select_input(xcb_connection(), screen()->root, true);
- xcb_randr_get_crtc_info_cookie_t crtcCookie =
- xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, output ? output->timestamp : 0);
- xcb_randr_get_crtc_info_reply_t *crtc =
- xcb_randr_get_crtc_info_reply(xcb_connection(), crtcCookie, NULL);
+ auto crtc = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_crtc_info, xcb_connection(),
+ m_crtc, output ? output->timestamp : 0);
if (crtc) {
updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation);
updateRefreshRate(crtc->mode);
- free(crtc);
}
} else if (xineramaScreenInfo) {
m_geometry = QRect(xineramaScreenInfo->x_org, xineramaScreenInfo->y_org,
@@ -208,74 +444,19 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe
if (m_sizeMillimeters.isEmpty())
m_sizeMillimeters = m_virtualSizeMillimeters;
- readXResources();
-
- QScopedPointer<xcb_get_window_attributes_reply_t, QScopedPointerPodDeleter> rootAttribs(
- xcb_get_window_attributes_reply(xcb_connection(),
- xcb_get_window_attributes_unchecked(xcb_connection(), screen()->root), NULL));
- const quint32 existingEventMask = rootAttribs.isNull() ? 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);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(),
- xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
- atom(QXcbAtom::_NET_SUPPORTING_WM_CHECK),
- XCB_ATOM_WINDOW, 0, 1024), NULL);
-
- if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
- xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply));
-
- if (windowManager != XCB_WINDOW_NONE) {
- xcb_get_property_reply_t *windowManagerReply =
- xcb_get_property_reply(xcb_connection(),
- xcb_get_property_unchecked(xcb_connection(), false, windowManager,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING), 0, 1024), NULL);
- if (windowManagerReply && windowManagerReply->format == 8 && windowManagerReply->type == atom(QXcbAtom::UTF8_STRING)) {
- m_windowManagerName = QString::fromUtf8((const char *)xcb_get_property_value(windowManagerReply), xcb_get_property_value_length(windowManagerReply));
- }
-
- free(windowManagerReply);
- }
- }
- free(reply);
+ m_cursor = new QXcbCursor(connection, this);
- 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;
+ // Parse EDID
+ if (m_edid.parse(getEdid()))
+ qCDebug(lcQpaScreen, "EDID data for output \"%s\": identifier '%s', manufacturer '%s', model '%s', serial '%s', physical size: %.2fx%.2f",
+ name().toLatin1().constData(),
+ m_edid.identifier.toLatin1().constData(),
+ m_edid.manufacturer.toLatin1().constData(),
+ m_edid.model.toLatin1().constData(),
+ m_edid.serialNumber.toLatin1().constData(),
+ m_edid.physicalSize.width(), m_edid.physicalSize.height());
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);
+ qCDebug(lcQpaScreen) << "Failed to parse EDID data for output" << name(); // keep this debug, not warning
}
QXcbScreen::~QXcbScreen()
@@ -300,6 +481,21 @@ QString QXcbScreen::getOutputName(xcb_randr_get_output_info_reply_t *outputInfo)
return name;
}
+QString QXcbScreen::manufacturer() const
+{
+ return m_edid.manufacturer;
+}
+
+QString QXcbScreen::model() const
+{
+ return m_edid.model;
+}
+
+QString QXcbScreen::serialNumber() const
+{
+ return m_edid.serialNumber;
+}
+
QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
{
xcb_window_t root = screen()->root;
@@ -311,12 +507,7 @@ QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
xcb_window_t child = root;
do {
- xcb_translate_coordinates_cookie_t translate_cookie =
- xcb_translate_coordinates_unchecked(xcb_connection(), parent, child, x, y);
-
- xcb_translate_coordinates_reply_t *translate_reply =
- xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
-
+ auto translate_reply = Q_XCB_REPLY_UNCHECKED(xcb_translate_coordinates, xcb_connection(), parent, child, x, y);
if (!translate_reply) {
return 0;
}
@@ -326,8 +517,6 @@ QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
x = translate_reply->dst_x;
y = translate_reply->dst_y;
- free(translate_reply);
-
if (!child || child == root)
return 0;
@@ -350,62 +539,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
@@ -434,20 +573,12 @@ void QXcbScreen::sendStartupMessage(const QByteArray &message) const
} while (sent < length);
}
-const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
+QRect QXcbScreen::availableGeometry() 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;
+ static bool enforceNetWorkarea = !qEnvironmentVariableIsEmpty("QT_RELY_ON_NET_WORKAREA_ATOM");
+ bool isMultiHeadSystem = virtualSiblings().length() > 1;
+ bool useScreenGeometry = isMultiHeadSystem && !enforceNetWorkarea;
+ return useScreenGeometry ? m_geometry : m_availableGeometry;
}
QImage::Format QXcbScreen::format() const
@@ -468,8 +599,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();
}
@@ -581,19 +713,14 @@ void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
if (!connection()->hasXRandr())
return;
- xcb_randr_get_crtc_info_cookie_t crtcCookie =
- xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, timestamp);
- xcb_randr_get_crtc_info_reply_t *crtc =
- xcb_randr_get_crtc_info_reply(xcb_connection(), crtcCookie, NULL);
- if (crtc) {
+ auto crtc = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_crtc_info, xcb_connection(),
+ m_crtc, timestamp);
+ if (crtc)
updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation);
- free(crtc);
- }
}
-void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation)
+void QXcbScreen::updateGeometry(const QRect &geometry, uint8_t rotation)
{
- QRect xGeometry = geom;
switch (rotation) {
case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal
m_orientation = Qt::LandscapeOrientation;
@@ -617,12 +744,12 @@ void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation)
// is known (probably back-calculated from DPI and resolution),
// e.g. on VNC or with some hardware.
if (m_sizeMillimeters.isEmpty())
- m_sizeMillimeters = sizeInMillimeters(xGeometry.size(), virtualDpi());
+ m_sizeMillimeters = sizeInMillimeters(geometry.size(), virtualDpi());
- qreal dpi = xGeometry.width() / physicalSize().width() * qreal(25.4);
+ qreal dpi = geometry.width() / physicalSize().width() * qreal(25.4);
m_pixelDensity = qMax(1, qRound(dpi/96));
- m_geometry = QRect(xGeometry.topLeft(), xGeometry.size());
- m_availableGeometry = xGeometry & m_virtualDesktop->workArea();
+ m_geometry = geometry;
+ m_availableGeometry = geometry & m_virtualDesktop->workArea();
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry);
}
@@ -645,13 +772,11 @@ void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode)
// we can safely use get_screen_resources_current here, because in order to
// get here, we must have called get_screen_resources before
- xcb_randr_get_screen_resources_current_cookie_t resourcesCookie =
- xcb_randr_get_screen_resources_current_unchecked(xcb_connection(), screen()->root);
- xcb_randr_get_screen_resources_current_reply_t *resources =
- xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, NULL);
+ auto resources = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_screen_resources_current,
+ xcb_connection(), screen()->root);
if (resources) {
xcb_randr_mode_info_iterator_t modesIter =
- xcb_randr_get_screen_resources_current_modes_iterator(resources);
+ xcb_randr_get_screen_resources_current_modes_iterator(resources.get());
for (; modesIter.rem; xcb_randr_mode_info_next(&modesIter)) {
xcb_randr_mode_info_t *modeInfo = modesIter.data;
if (modeInfo->id == mode) {
@@ -662,29 +787,19 @@ void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode)
}
}
- free(resources);
QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), m_refreshRate);
}
}
-static xcb_get_geometry_reply_t *getGeometryUnchecked(xcb_connection_t *connection, xcb_window_t window)
-{
- const xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry_unchecked(connection, window);
- return xcb_get_geometry_reply(connection, geometry_cookie, NULL);
-}
-
static inline bool translate(xcb_connection_t *connection, xcb_window_t child, xcb_window_t parent,
int *x, int *y)
{
- const xcb_translate_coordinates_cookie_t translate_cookie =
- xcb_translate_coordinates_unchecked(connection, child, parent, *x, *y);
- xcb_translate_coordinates_reply_t *translate_reply =
- xcb_translate_coordinates_reply(connection, translate_cookie, NULL);
+ auto translate_reply = Q_XCB_REPLY_UNCHECKED(xcb_translate_coordinates,
+ connection, child, parent, *x, *y);
if (!translate_reply)
return false;
*x = translate_reply->dst_x;
*y = translate_reply->dst_y;
- free(translate_reply);
return true;
}
@@ -698,22 +813,20 @@ QPixmap QXcbScreen::grabWindow(WId window, int xIn, int yIn, int width, int heig
QXcbScreen *screen = const_cast<QXcbScreen *>(this);
xcb_window_t root = screen->root();
- xcb_get_geometry_reply_t *rootReply = getGeometryUnchecked(xcb_connection(), root);
+ auto rootReply = Q_XCB_REPLY_UNCHECKED(xcb_get_geometry, xcb_connection(), root);
if (!rootReply)
return QPixmap();
const quint8 rootDepth = rootReply->depth;
- free(rootReply);
QSize windowSize;
quint8 effectiveDepth = 0;
if (window) {
- xcb_get_geometry_reply_t *windowReply = getGeometryUnchecked(xcb_connection(), window);
+ auto windowReply = Q_XCB_REPLY_UNCHECKED(xcb_get_geometry, xcb_connection(), window);
if (!windowReply)
return QPixmap();
windowSize = QSize(windowReply->width, windowReply->height);
effectiveDepth = windowReply->depth;
- free(windowReply);
if (effectiveDepth == rootDepth) {
// if the depth of the specified window and the root window are the
// same, grab pixels from the root window (so that we get the any
@@ -738,14 +851,12 @@ QPixmap QXcbScreen::grabWindow(WId window, int xIn, int yIn, int width, int heig
if (height < 0)
height = windowSize.height() - yIn;
- xcb_get_window_attributes_reply_t *attributes_reply =
- xcb_get_window_attributes_reply(xcb_connection(), xcb_get_window_attributes_unchecked(xcb_connection(), window), NULL);
+ auto attributes_reply = Q_XCB_REPLY_UNCHECKED(xcb_get_window_attributes, xcb_connection(), window);
if (!attributes_reply)
return QPixmap();
const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual);
- free(attributes_reply);
xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection());
xcb_create_pixmap(xcb_connection(), effectiveDepth, pixmap, window, width, height);
@@ -765,101 +876,41 @@ QPixmap QXcbScreen::grabWindow(WId window, int xIn, int yIn, int width, int heig
return result;
}
-static bool parseXftInt(const QByteArray& stringValue, int *value)
+QXcbXSettings *QXcbScreen::xSettings() const
{
- Q_ASSERT(value != 0);
- bool ok;
- *value = stringValue.toInt(&ok);
- return ok;
+ return m_virtualDesktop->xSettings();
}
-static QFontEngine::HintStyle parseXftHintStyle(const QByteArray& stringValue)
+QByteArray QXcbScreen::getOutputProperty(xcb_atom_t atom) const
{
- 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);
-}
+ QByteArray result;
-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;
+ auto reply = Q_XCB_REPLY(xcb_randr_get_output_property, xcb_connection(),
+ m_output, atom, XCB_ATOM_ANY, 0, 100, false, false);
+ if (reply && reply->type == XCB_ATOM_INTEGER && reply->format == 8) {
+ quint8 *data = new quint8[reply->num_items];
+ memcpy(data, xcb_randr_get_output_property_data(reply.get()), reply->num_items);
+ result = QByteArray(reinterpret_cast<const char *>(data), reply->num_items);
+ delete[] data;
+ }
- return QFontEngine::SubpixelAntialiasingType(-1);
+ return result;
}
-bool QXcbScreen::xResource(const QByteArray &identifier,
- const QByteArray &expectedIdentifier,
- QByteArray& stringValue)
+QByteArray QXcbScreen::getEdid() const
{
- if (identifier.startsWith(expectedIdentifier)) {
- stringValue = identifier.mid(expectedIdentifier.size());
- return true;
+ // Try a bunch of atoms
+ xcb_atom_t atom = connection()->internAtom("EDID");
+ QByteArray result = getOutputProperty(atom);
+ if (result.isEmpty()) {
+ atom = connection()->internAtom("EDID_DATA");
+ result = getOutputProperty(atom);
}
- return false;
-}
-
-void QXcbScreen::readXResources()
-{
- int offset = 0;
- QByteArray resources;
- while(1) {
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(),
- xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
- XCB_ATOM_RESOURCE_MANAGER,
- XCB_ATOM_STRING, offset/4, 8192), NULL);
- bool more = false;
- if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
- resources += QByteArray((const char *)xcb_get_property_value(reply), xcb_get_property_value_length(reply));
- offset += xcb_get_property_value_length(reply);
- more = reply->bytes_after != 0;
- }
-
- if (reply)
- free(reply);
-
- if (!more)
- break;
+ if (result.isEmpty()) {
+ atom = connection()->internAtom("XFree86_DDC_EDID1_RAWDATA");
+ result = getOutputProperty(atom);
}
-
- 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();
+ return result;
}
static inline void formatRect(QDebug &debug, const QRect r)
diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h
index 4163be2969..842738b622 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.h
+++ b/src/plugins/platforms/xcb/qxcbscreen.h
@@ -53,6 +53,8 @@
#include <private/qfontengine_p.h>
+#include <QtEdidSupport/private/qedidparser_p.h>
+
QT_BEGIN_NAMESPACE
class QXcbConnection;
@@ -91,9 +93,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 +124,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
@@ -119,8 +149,12 @@ public:
QWindow *topLevelAt(const QPoint &point) const override;
+ QString manufacturer() const override;
+ QString model() const override;
+ QString serialNumber() const override;
+
QRect geometry() const override { return m_geometry; }
- QRect availableGeometry() const override {return m_availableGeometry;}
+ QRect availableGeometry() const override;
int depth() const override { return screen()->root_depth; }
QImage::Format format() const override;
QSizeF physicalSize() const override { return m_sizeMillimeters; }
@@ -152,37 +186,35 @@ 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; }
void handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event);
- void updateGeometry(const QRect &geom, uint8_t rotation);
+ void updateGeometry(const QRect &geometry, uint8_t rotation);
void updateGeometry(xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME);
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;
+ QByteArray getOutputProperty(xcb_atom_t atom) const;
+ QByteArray getEdid() const;
+
QXcbVirtualDesktop *m_virtualDesktop;
xcb_randr_output_t m_output;
xcb_randr_crtc_t m_crtc;
@@ -198,17 +230,10 @@ 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;
+ QEdidParser m_edid;
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
index fb0a4a3939..c98879c7df 100644
--- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
+++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
@@ -85,13 +85,10 @@ QXcbSystemTrayTracker::QXcbSystemTrayTracker(QXcbConnection *connection,
xcb_window_t QXcbSystemTrayTracker::locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection)
{
- xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(connection->xcb_connection(), selection);
- xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(connection->xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_selection_owner, connection->xcb_connection(), selection);
if (!reply)
return 0;
- const xcb_window_t result = reply->owner;
- free(reply);
- return result;
+ return reply->owner;
}
// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Request a window
@@ -119,7 +116,7 @@ xcb_window_t QXcbSystemTrayTracker::trayWindow()
m_connection->addWindowEventListener(m_trayWindow, this);
const quint32 mask = XCB_CW_EVENT_MASK;
const quint32 value = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
- Q_XCB_CALL2(xcb_change_window_attributes(m_connection->xcb_connection(), m_trayWindow, mask, &value), m_connection);
+ xcb_change_window_attributes(m_connection->xcb_connection(), m_trayWindow, mask, &value);
}
}
return m_trayWindow;
@@ -130,23 +127,16 @@ xcb_window_t QXcbSystemTrayTracker::trayWindow()
// does not work for the QWindow parented on the tray.
QRect QXcbSystemTrayTracker::systemTrayWindowGlobalGeometry(xcb_window_t window) const
{
-
xcb_connection_t *conn = m_connection->xcb_connection();
- xcb_get_geometry_reply_t *geomReply =
- xcb_get_geometry_reply(conn, xcb_get_geometry(conn, window), 0);
+ auto geomReply = Q_XCB_REPLY(xcb_get_geometry, conn, window);
if (!geomReply)
return QRect();
- xcb_translate_coordinates_reply_t *translateReply =
- xcb_translate_coordinates_reply(conn, xcb_translate_coordinates(conn, window, m_connection->rootWindow(), 0, 0), 0);
- if (!translateReply) {
- free(geomReply);
+ auto translateReply = Q_XCB_REPLY(xcb_translate_coordinates, conn, window, m_connection->rootWindow(), 0, 0);
+ if (!translateReply)
return QRect();
- }
- const QRect result(QPoint(translateReply->dst_x, translateReply->dst_y), QSize(geomReply->width, geomReply->height));
- free(translateReply);
- return result;
+ return QRect(QPoint(translateReply->dst_x, translateReply->dst_y), QSize(geomReply->width, geomReply->height));
}
inline void QXcbSystemTrayTracker::emitSystemTrayWindowChanged()
@@ -180,24 +170,18 @@ bool QXcbSystemTrayTracker::visualHasAlphaChannel()
xcb_atom_t tray_atom = m_connection->atom(QXcbAtom::_NET_SYSTEM_TRAY_VISUAL);
// Get the xcb property for the _NET_SYSTEM_TRAY_VISUAL atom
- xcb_get_property_cookie_t systray_atom_cookie;
- xcb_get_property_reply_t *systray_atom_reply;
-
- systray_atom_cookie = xcb_get_property_unchecked(m_connection->xcb_connection(), false, m_trayWindow,
+ auto systray_atom_reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, m_connection->xcb_connection(),
+ false, m_trayWindow,
tray_atom, XCB_ATOM_VISUALID, 0, 1);
- systray_atom_reply = xcb_get_property_reply(m_connection->xcb_connection(), systray_atom_cookie, 0);
-
if (!systray_atom_reply)
return false;
xcb_visualid_t systrayVisualId = XCB_NONE;
- if (systray_atom_reply->value_len > 0 && xcb_get_property_value_length(systray_atom_reply) > 0) {
- xcb_visualid_t * vids = (uint32_t *)xcb_get_property_value(systray_atom_reply);
+ if (systray_atom_reply->value_len > 0 && xcb_get_property_value_length(systray_atom_reply.get()) > 0) {
+ xcb_visualid_t * vids = (uint32_t *)xcb_get_property_value(systray_atom_reply.get());
systrayVisualId = vids[0];
}
- free(systray_atom_reply);
-
if (systrayVisualId != XCB_NONE) {
quint8 depth = m_connection->primaryScreen()->depthOfVisual(systrayVisualId);
return depth == 32;
diff --git a/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp b/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp
new file mode 100644
index 0000000000..4d540defa9
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbvulkaninstance.h"
+#include "qxcbwindow.h"
+#include "qxcbscreen.h"
+
+QT_BEGIN_NAMESPACE
+
+QXcbVulkanInstance::QXcbVulkanInstance(QVulkanInstance *instance)
+ : m_instance(instance),
+ m_getPhysDevPresSupport(nullptr),
+ m_createSurface(nullptr),
+ m_destroySurface(nullptr)
+{
+ if (qEnvironmentVariableIsSet("QT_VULKAN_LIB"))
+ m_lib.setFileName(QString::fromUtf8(qgetenv("QT_VULKAN_LIB")));
+ else
+ m_lib.setFileName(QStringLiteral("vulkan"));
+
+ if (!m_lib.load()) {
+ qWarning("Failed to load %s: %s", qPrintable(m_lib.fileName()), qPrintable(m_lib.errorString()));
+ return;
+ }
+
+ init(&m_lib);
+}
+
+QXcbVulkanInstance::~QXcbVulkanInstance()
+{
+}
+
+void QXcbVulkanInstance::createOrAdoptInstance()
+{
+ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_KHR_xcb_surface"));
+
+ if (!m_vkInst)
+ return;
+
+ m_getPhysDevPresSupport = reinterpret_cast<PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceXcbPresentationSupportKHR"));
+ if (!m_getPhysDevPresSupport)
+ qWarning("Failed to find vkGetPhysicalDeviceXcbPresentationSupportKHR");
+}
+
+bool QXcbVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
+ uint32_t queueFamilyIndex,
+ QWindow *window)
+{
+ if (!m_getPhysDevPresSupport || !m_getPhysDevSurfaceSupport)
+ return true;
+
+ QXcbWindow *w = static_cast<QXcbWindow *>(window->handle());
+ if (!w) {
+ qWarning("Attempted to call supportsPresent() without a valid platform window");
+ return false;
+ }
+ xcb_connection_t *connection = w->xcbScreen()->xcb_connection();
+ bool ok = m_getPhysDevPresSupport(physicalDevice, queueFamilyIndex, connection, w->visualId());
+
+ VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
+ VkBool32 supported = false;
+ m_getPhysDevSurfaceSupport(physicalDevice, queueFamilyIndex, surface, &supported);
+ ok &= bool(supported);
+
+ return ok;
+}
+
+VkSurfaceKHR QXcbVulkanInstance::createSurface(QXcbWindow *window)
+{
+ VkSurfaceKHR surface = 0;
+
+ if (!m_createSurface) {
+ m_createSurface = reinterpret_cast<PFN_vkCreateXcbSurfaceKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkCreateXcbSurfaceKHR"));
+ }
+ if (!m_createSurface) {
+ qWarning("Failed to find vkCreateXcbSurfaceKHR");
+ return surface;
+ }
+ if (!m_destroySurface) {
+ m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
+ }
+ if (!m_destroySurface) {
+ qWarning("Failed to find vkDestroySurfaceKHR");
+ return surface;
+ }
+
+ VkXcbSurfaceCreateInfoKHR surfaceInfo;
+ memset(&surfaceInfo, 0, sizeof(surfaceInfo));
+ surfaceInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
+ surfaceInfo.connection = window->xcbScreen()->xcb_connection();
+ surfaceInfo.window = window->xcb_window();
+ VkResult err = m_createSurface(m_vkInst, &surfaceInfo, nullptr, &surface);
+ if (err != VK_SUCCESS)
+ qWarning("Failed to create Vulkan surface: %d", err);
+
+ return surface;
+}
+
+void QXcbVulkanInstance::destroySurface(VkSurfaceKHR surface)
+{
+ if (m_destroySurface && surface)
+ m_destroySurface(m_vkInst, surface, nullptr);
+}
+
+void QXcbVulkanInstance::presentQueued(QWindow *window)
+{
+ QXcbWindow *w = static_cast<QXcbWindow *>(window->handle());
+ if (!w) {
+ qWarning("Attempted to call presentQueued() without a valid platform window");
+ return;
+ }
+
+ if (w->needsSync())
+ w->postSyncWindowRequest();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbvulkaninstance.h b/src/plugins/platforms/xcb/qxcbvulkaninstance.h
new file mode 100644
index 0000000000..dbe057d944
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbvulkaninstance.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBVULKANINSTANCE_H
+#define QXCBVULKANINSTANCE_H
+
+#if defined(VULKAN_H_) && !defined(VK_USE_PLATFORM_XCB_KHR)
+#error "vulkan.h included without xcb WSI"
+#endif
+
+#define VK_USE_PLATFORM_XCB_KHR
+
+#include <QtVulkanSupport/private/qbasicvulkanplatforminstance_p.h>
+#include <QLibrary>
+
+QT_BEGIN_NAMESPACE
+
+class QXcbWindow;
+
+class QXcbVulkanInstance : public QBasicPlatformVulkanInstance
+{
+public:
+ QXcbVulkanInstance(QVulkanInstance *instance);
+ ~QXcbVulkanInstance();
+
+ void createOrAdoptInstance() override;
+ bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override;
+ void presentQueued(QWindow *window) override;
+
+ VkSurfaceKHR createSurface(QXcbWindow *window);
+ void destroySurface(VkSurfaceKHR surface);
+
+private:
+ QVulkanInstance *m_instance;
+ QLibrary m_lib;
+ PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR m_getPhysDevPresSupport;
+ PFN_vkCreateXcbSurfaceKHR m_createSurface;
+ PFN_vkDestroySurfaceKHR m_destroySurface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QXCBVULKANINSTANCE_H
diff --git a/src/plugins/platforms/xcb/qxcbvulkanwindow.cpp b/src/plugins/platforms/xcb/qxcbvulkanwindow.cpp
new file mode 100644
index 0000000000..25bc340f97
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbvulkanwindow.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbvulkanwindow.h"
+
+QT_BEGIN_NAMESPACE
+
+QXcbVulkanWindow::QXcbVulkanWindow(QWindow *window)
+ : QXcbWindow(window),
+ m_surface(0)
+{
+}
+
+QXcbVulkanWindow::~QXcbVulkanWindow()
+{
+ if (m_surface) {
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (inst)
+ static_cast<QXcbVulkanInstance *>(inst->handle())->destroySurface(m_surface);
+ }
+}
+
+void QXcbVulkanWindow::resolveFormat(const QSurfaceFormat &format)
+{
+ m_format = format;
+ if (m_format.redBufferSize() <= 0)
+ m_format.setRedBufferSize(8);
+ if (m_format.greenBufferSize() <= 0)
+ m_format.setGreenBufferSize(8);
+ if (m_format.blueBufferSize() <= 0)
+ m_format.setBlueBufferSize(8);
+}
+
+// No createVisual() needed, use the default that picks one based on the R/G/B/A size.
+
+VkSurfaceKHR *QXcbVulkanWindow::surface()
+{
+ if (m_surface)
+ return &m_surface;
+
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (!inst) {
+ qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
+ return nullptr;
+ }
+ QXcbVulkanInstance *xcbinst = static_cast<QXcbVulkanInstance *>(inst->handle());
+ m_surface = xcbinst->createSurface(this);
+
+ return &m_surface;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbvulkanwindow.h b/src/plugins/platforms/xcb/qxcbvulkanwindow.h
new file mode 100644
index 0000000000..43c96820c5
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbvulkanwindow.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBVULKANWINDOW_H
+#define QXCBVULKANWINDOW_H
+
+#include "qxcbwindow.h"
+#include "qxcbvulkaninstance.h"
+
+QT_BEGIN_NAMESPACE
+
+class QXcbVulkanWindow : public QXcbWindow
+{
+public:
+ QXcbVulkanWindow(QWindow *window);
+ ~QXcbVulkanWindow();
+
+ VkSurfaceKHR *surface();
+
+protected:
+ void resolveFormat(const QSurfaceFormat &format) override;
+
+private:
+ VkSurfaceKHR m_surface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QXCBVULKANWINDOW_H
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index c4649ac818..c8a668b72c 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -227,6 +227,12 @@ static inline QImage::Format imageFormatForVisual(int depth, quint32 red_mask, q
return QImage::Format_RGB555;
}
break;
+#if QT_CONFIG(xcb_native_painting)
+ case 8:
+ if (QXcbIntegration::instance() && QXcbIntegration::instance()->nativePaintingEnabled())
+ return QImage::Format_Indexed8;
+ break;
+#endif
default:
break;
}
@@ -420,6 +426,19 @@ void QXcbWindow::create()
qWarning() << "Failed to use requested visual id.";
}
+ if (parent()) {
+ // When using a Vulkan QWindow via QWidget::createWindowContainer() we
+ // must make sure the visuals are compatible. Now, the parent will be
+ // of RasterGLSurface which typically chooses a GLX/EGL compatible
+ // visual which may not be what the Vulkan window would choose.
+ // Therefore, take the parent's visual.
+ if (window()->surfaceType() == QSurface::VulkanSurface
+ && parent()->window()->surfaceType() != QSurface::VulkanSurface)
+ {
+ visual = platformScreen->visualForId(static_cast<QXcbWindow *>(parent())->visualId());
+ }
+ }
+
if (!visual)
visual = createVisual();
@@ -445,11 +464,11 @@ void QXcbWindow::create()
if ((window()->supportsOpenGL() && haveOpenGL) || m_format.hasAlpha()) {
m_cmap = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_colormap(xcb_connection(),
- XCB_COLORMAP_ALLOC_NONE,
- m_cmap,
- xcb_parent_id,
- m_visualId));
+ xcb_create_colormap(xcb_connection(),
+ XCB_COLORMAP_ALLOC_NONE,
+ m_cmap,
+ xcb_parent_id,
+ m_visualId);
mask |= XCB_CW_COLORMAP;
}
@@ -465,23 +484,23 @@ void QXcbWindow::create()
};
m_window = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- m_depth,
- m_window, // window id
- xcb_parent_id, // parent window id
- rect.x(),
- rect.y(),
- rect.width(),
- rect.height(),
- 0, // border width
- XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
- m_visualId, // visual
- mask,
- values));
+ xcb_create_window(xcb_connection(),
+ m_depth,
+ m_window, // window id
+ xcb_parent_id, // parent window id
+ rect.x(),
+ rect.y(),
+ rect.width(),
+ rect.height(),
+ 0, // border width
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
+ m_visualId, // visual
+ mask,
+ values);
connection()->addWindowEventListener(m_window, this);
- Q_XCB_CALL(xcb_change_window_attributes(xcb_connection(), m_window, mask, values));
+ xcb_change_window_attributes(xcb_connection(), m_window, mask, values);
propagateSizeHints();
@@ -499,43 +518,43 @@ void QXcbWindow::create()
if (window()->flags() & Qt::WindowContextHelpButtonHint)
properties[propertyCount++] = atom(QXcbAtom::_NET_WM_CONTEXT_HELP);
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::WM_PROTOCOLS),
- XCB_ATOM_ATOM,
- 32,
- propertyCount,
- properties));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::WM_PROTOCOLS),
+ XCB_ATOM_ATOM,
+ 32,
+ propertyCount,
+ properties);
m_syncValue.hi = 0;
m_syncValue.lo = 0;
const QByteArray wmClass = QXcbIntegration::instance()->wmClass();
if (!wmClass.isEmpty()) {
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE,
- m_window, atom(QXcbAtom::WM_CLASS),
- XCB_ATOM_STRING, 8, wmClass.size(), wmClass.constData()));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE,
+ m_window, atom(QXcbAtom::WM_CLASS),
+ XCB_ATOM_STRING, 8, wmClass.size(), wmClass.constData());
}
if (m_usingSyncProtocol) {
m_syncCounter = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue));
+ xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue);
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::_NET_WM_SYNC_REQUEST_COUNTER),
- XCB_ATOM_CARDINAL,
- 32,
- 1,
- &m_syncCounter));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_NET_WM_SYNC_REQUEST_COUNTER),
+ XCB_ATOM_CARDINAL,
+ 32,
+ 1,
+ &m_syncCounter);
}
// set the PID to let the WM kill the application if unresponsive
quint32 pid = getpid();
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32,
- 1, &pid));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32,
+ 1, &pid);
xcb_wm_hints_t hints;
memset(&hints, 0, sizeof(hints));
@@ -546,23 +565,27 @@ void QXcbWindow::create()
xcb_set_wm_hints(xcb_connection(), m_window, &hints);
xcb_window_t leader = connection()->clientLeader();
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32,
- 1, &leader));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32,
+ 1, &leader);
/* Add XEMBED info; this operation doesn't initiate the embedding. */
quint32 data[] = { XEMBED_VERSION, XEMBED_MAPPED };
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::_XEMBED_INFO),
- atom(QXcbAtom::_XEMBED_INFO),
- 32, 2, (void *)data));
-
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::_XEMBED_INFO),
+ atom(QXcbAtom::_XEMBED_INFO),
+ 32, 2, (void *)data);
#if QT_CONFIG(xinput2)
- connection()->xi2Select(m_window);
+ if (connection()->hasXInput2()) {
+ if (connection()->xi2MouseEventsDisabled())
+ connection()->xi2SelectDeviceEventsCompatibility(m_window);
+ else
+ connection()->xi2SelectDeviceEvents(m_window);
+ }
#endif
- setWindowState(window()->windowState());
+ setWindowState(window()->windowStates());
setWindowFlags(window()->flags());
setWindowTitle(window()->title());
@@ -571,7 +594,7 @@ void QXcbWindow::create()
#if QT_CONFIG(xcb_xlib)
// force sync to read outstanding requests - see QTBUG-29106
- XSync(DISPLAY_FROM_XCB(platformScreen), false);
+ XSync(static_cast<Display*>(platformScreen->connection()->xlib_display()), false);
#endif
#ifndef QT_NO_DRAGANDDROP
@@ -581,6 +604,9 @@ void QXcbWindow::create()
const qreal opacity = qt_window_private(window())->opacity;
if (!qFuzzyCompare(opacity, qreal(1.0)))
setOpacity(opacity);
+
+ setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
+
if (window()->isTopLevel())
setWindowIcon(window()->icon());
@@ -618,7 +644,7 @@ void QXcbWindow::destroy()
connection()->setMouseGrabber(Q_NULLPTR);
if (m_syncCounter && m_usingSyncProtocol)
- Q_XCB_CALL(xcb_sync_destroy_counter(xcb_connection(), m_syncCounter));
+ xcb_sync_destroy_counter(xcb_connection(), m_syncCounter);
if (m_window) {
if (m_netWmUserTimeWindow) {
xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
@@ -629,7 +655,7 @@ void QXcbWindow::destroy()
m_netWmUserTimeWindow = XCB_NONE;
}
connection()->removeWindowEventListener(m_window);
- Q_XCB_CALL(xcb_destroy_window(xcb_connection(), m_window));
+ xcb_destroy_window(xcb_connection(), m_window);
m_window = 0;
}
if (m_cmap) {
@@ -664,7 +690,7 @@ void QXcbWindow::setGeometry(const QRect &rect)
qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX),
qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX),
};
- Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)));
+ xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values));
} else {
const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
const qint32 values[] = {
@@ -673,7 +699,7 @@ void QXcbWindow::setGeometry(const QRect &rect)
qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX),
qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX),
};
- Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)));
+ xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values));
}
xcb_flush(xcb_connection());
@@ -683,12 +709,10 @@ QMargins QXcbWindow::frameMargins() const
{
if (m_dirtyFrameMargins) {
if (connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_FRAME_EXTENTS))) {
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, m_window,
- atom(QXcbAtom::_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4);
- QScopedPointer<xcb_get_property_reply_t, QScopedPointerPodDeleter> reply(
- xcb_get_property_reply(xcb_connection(), cookie, NULL));
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, m_window,
+ atom(QXcbAtom::_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4);
if (reply && reply->type == XCB_ATOM_CARDINAL && reply->format == 32 && reply->value_len == 4) {
- quint32 *data = (quint32 *)xcb_get_property_value(reply.data());
+ quint32 *data = (quint32 *)xcb_get_property_value(reply.get());
// _NET_FRAME_EXTENTS format is left, right, top, bottom
m_frameMargins = QMargins(data[0], data[2], data[1], data[3]);
m_dirtyFrameMargins = false;
@@ -707,9 +731,7 @@ QMargins QXcbWindow::frameMargins() const
connection()->wmSupport()->virtualRoots();
while (!foundRoot) {
- xcb_query_tree_cookie_t cookie = xcb_query_tree_unchecked(xcb_connection(), parent);
-
- xcb_query_tree_reply_t *reply = xcb_query_tree_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_query_tree, xcb_connection(), parent);
if (reply) {
if (reply->root == reply->parent || virtualRoots.indexOf(reply->parent) != -1 || reply->parent == XCB_WINDOW_NONE) {
foundRoot = true;
@@ -717,8 +739,6 @@ QMargins QXcbWindow::frameMargins() const
window = parent;
parent = reply->parent;
}
-
- free(reply);
} else {
m_dirtyFrameMargins = false;
m_frameMargins = QMargins();
@@ -728,23 +748,12 @@ QMargins QXcbWindow::frameMargins() const
QPoint offset;
- xcb_translate_coordinates_reply_t *reply =
- xcb_translate_coordinates_reply(
- xcb_connection(),
- xcb_translate_coordinates(xcb_connection(), window, parent, 0, 0),
- NULL);
-
+ auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(), window, parent, 0, 0);
if (reply) {
offset = QPoint(reply->dst_x, reply->dst_y);
- free(reply);
}
- xcb_get_geometry_reply_t *geom =
- xcb_get_geometry_reply(
- xcb_connection(),
- xcb_get_geometry(xcb_connection(), parent),
- NULL);
-
+ auto geom = Q_XCB_REPLY(xcb_get_geometry, xcb_connection(), parent);
if (geom) {
// --
// add the border_width for the window managers frame... some window managers
@@ -761,8 +770,6 @@ QMargins QXcbWindow::frameMargins() const
int bottom = geom->height + geom->border_width - geometry().height() - offset.y();
m_frameMargins = QMargins(left, top, right, bottom);
-
- free(geom);
}
m_dirtyFrameMargins = false;
@@ -789,12 +796,13 @@ static inline bool testShowWithoutActivating(const QWindow *window)
void QXcbWindow::show()
{
if (window()->isTopLevel()) {
+
xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
xcb_wm_hints_t hints;
xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL);
- if (window()->windowState() & Qt::WindowMinimized)
+ if (window()->windowStates() & Qt::WindowMinimized)
xcb_wm_hints_set_iconic(&hints);
else
xcb_wm_hints_set_normal(&hints);
@@ -820,13 +828,13 @@ void QXcbWindow::show()
if (!transientXcbParent)
transientXcbParent = connection()->clientLeader();
if (transientXcbParent) { // ICCCM 4.1.2.6
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32,
- 1, &transientXcbParent));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32,
+ 1, &transientXcbParent);
}
}
if (!transientXcbParent)
- Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR));
+ xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR);
// update _MOTIF_WM_HINTS
updateMotifWmHintsBeforeMap();
@@ -843,7 +851,7 @@ void QXcbWindow::show()
if (window()->objectName() == QLatin1String("QSystemTrayIconSysWindow"))
return; // defer showing until XEMBED_EMBEDDED_NOTIFY
- Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
+ xcb_map_window(xcb_connection(), m_window);
if (QGuiApplication::modalWindow() == window())
requestActivateWindow();
@@ -855,7 +863,7 @@ void QXcbWindow::show()
void QXcbWindow::hide()
{
- Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window));
+ xcb_unmap_window(xcb_connection(), m_window);
// send synthetic UnmapNotify event according to icccm 4.1.4
Q_DECLARE_XCB_EVENT(event, xcb_unmap_notify_event_t);
@@ -863,8 +871,8 @@ void QXcbWindow::hide()
event.event = xcbScreen()->root();
event.window = m_window;
event.from_configure = false;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xcbScreen()->root(),
- XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
+ xcb_send_event(xcb_connection(), false, xcbScreen()->root(),
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event);
xcb_flush(xcb_connection());
@@ -1014,15 +1022,11 @@ static QtMotifWmHints getMotifWmHints(QXcbConnection *c, xcb_window_t window)
{
QtMotifWmHints hints;
- xcb_get_property_cookie_t get_cookie =
- xcb_get_property_unchecked(c->xcb_connection(), 0, window, c->atom(QXcbAtom::_MOTIF_WM_HINTS),
- c->atom(QXcbAtom::_MOTIF_WM_HINTS), 0, 20);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(c->xcb_connection(), get_cookie, NULL);
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, c->xcb_connection(), 0, window,
+ c->atom(QXcbAtom::_MOTIF_WM_HINTS), c->atom(QXcbAtom::_MOTIF_WM_HINTS), 0, 20);
if (reply && reply->format == 32 && reply->type == c->atom(QXcbAtom::_MOTIF_WM_HINTS)) {
- hints = *((QtMotifWmHints *)xcb_get_property_value(reply));
+ hints = *((QtMotifWmHints *)xcb_get_property_value(reply.get()));
} else {
hints.flags = 0L;
hints.functions = MWM_FUNC_ALL;
@@ -1031,24 +1035,22 @@ static QtMotifWmHints getMotifWmHints(QXcbConnection *c, xcb_window_t window)
hints.status = 0L;
}
- free(reply);
-
return hints;
}
static void setMotifWmHints(QXcbConnection *c, xcb_window_t window, const QtMotifWmHints &hints)
{
if (hints.flags != 0l) {
- Q_XCB_CALL2(xcb_change_property(c->xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- window,
- c->atom(QXcbAtom::_MOTIF_WM_HINTS),
- c->atom(QXcbAtom::_MOTIF_WM_HINTS),
- 32,
- 5,
- &hints), c);
+ xcb_change_property(c->xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ window,
+ c->atom(QXcbAtom::_MOTIF_WM_HINTS),
+ c->atom(QXcbAtom::_MOTIF_WM_HINTS),
+ 32,
+ 5,
+ &hints);
} else {
- Q_XCB_CALL2(xcb_delete_property(c->xcb_connection(), window, c->atom(QXcbAtom::_MOTIF_WM_HINTS)), c);
+ xcb_delete_property(c->xcb_connection(), window, c->atom(QXcbAtom::_MOTIF_WM_HINTS));
}
}
@@ -1056,15 +1058,12 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates()
{
NetWmStates result(0);
- xcb_get_property_cookie_t get_cookie =
- xcb_get_property_unchecked(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
- XCB_ATOM_ATOM, 0, 1024);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
+ 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
+ XCB_ATOM_ATOM, 0, 1024);
if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
- const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply));
+ const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
const xcb_atom_t *statesEnd = states + reply->length;
if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
result |= NetWmStateAbove;
@@ -1082,7 +1081,6 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates()
result |= NetWmStateStaysOnTop;
if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
result |= NetWmStateDemandsAttention;
- free(reply);
} else {
#ifdef NET_WM_STATE_DEBUG
printf("getting net wm state (%x), empty\n", m_window);
@@ -1096,21 +1094,15 @@ void QXcbWindow::setNetWmStates(NetWmStates states)
{
QVector<xcb_atom_t> atoms;
- xcb_get_property_cookie_t get_cookie =
- xcb_get_property_unchecked(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
- XCB_ATOM_ATOM, 0, 1024);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
-
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
+ 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
+ XCB_ATOM_ATOM, 0, 1024);
if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM && reply->value_len > 0) {
- const xcb_atom_t *data = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply));
+ const xcb_atom_t *data = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
atoms.resize(reply->value_len);
memcpy((void *)&atoms.first(), (void *)data, reply->value_len * sizeof(xcb_atom_t));
}
- free(reply);
-
if (states & NetWmStateAbove && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE));
if (states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_BELOW)))
@@ -1129,11 +1121,11 @@ void QXcbWindow::setNetWmStates(NetWmStates states)
atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION));
if (atoms.isEmpty()) {
- Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE)));
+ xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE));
} else {
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32,
- atoms.count(), atoms.constData()));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32,
+ atoms.count(), atoms.constData());
}
xcb_flush(xcb_connection());
}
@@ -1256,67 +1248,48 @@ void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two)
event.data.data32[3] = 0;
event.data.data32[4] = 0;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
+ xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (const char *)&event);
}
-void QXcbWindow::setWindowState(Qt::WindowState state)
+void QXcbWindow::setWindowState(Qt::WindowStates state)
{
if (state == m_windowState)
return;
- // unset old state
- switch (m_windowState) {
- case Qt::WindowMinimized:
- Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
- break;
- case Qt::WindowMaximized:
- changeNetWmState(false,
- atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
- atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
- break;
- case Qt::WindowFullScreen:
- changeNetWmState(false, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
- break;
- default:
- break;
+ if ((m_windowState & Qt::WindowMinimized) && !(state & Qt::WindowMinimized)) {
+ xcb_map_window(xcb_connection(), m_window);
+ } else if (!(m_windowState & Qt::WindowMinimized) && (state & Qt::WindowMinimized)) {
+ xcb_client_message_event_t event;
+
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.sequence = 0;
+ event.window = m_window;
+ event.type = atom(QXcbAtom::WM_CHANGE_STATE);
+ event.data.data32[0] = XCB_WM_STATE_ICONIC;
+ event.data.data32[1] = 0;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (const char *)&event);
+ m_minimized = true;
}
- // set new state
- switch (state) {
- case Qt::WindowMinimized:
- {
- xcb_client_message_event_t event;
-
- event.response_type = XCB_CLIENT_MESSAGE;
- event.format = 32;
- event.sequence = 0;
- event.window = m_window;
- event.type = atom(QXcbAtom::WM_CHANGE_STATE);
- event.data.data32[0] = XCB_WM_STATE_ICONIC;
- event.data.data32[1] = 0;
- event.data.data32[2] = 0;
- event.data.data32[3] = 0;
- event.data.data32[4] = 0;
-
- Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
- }
- break;
- case Qt::WindowMaximized:
- changeNetWmState(true,
- atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
+ if ((m_windowState ^ state) & Qt::WindowMaximized) {
+ changeNetWmState(state & Qt::WindowMaximized, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
- break;
- case Qt::WindowFullScreen:
- changeNetWmState(true, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
- break;
- case Qt::WindowNoState:
- break;
- default:
- break;
}
- connection()->sync();
+ if ((m_windowState ^ state) & Qt::WindowFullScreen) {
+ changeNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
+ }
+ connection()->sync();
m_windowState = state;
}
@@ -1388,10 +1361,10 @@ void QXcbWindow::updateNetWmStateBeforeMap()
states |= NetWmStateBelow;
}
- if (window()->windowState() & Qt::WindowFullScreen)
+ if (window()->windowStates() & Qt::WindowFullScreen)
states |= NetWmStateFullScreen;
- if (window()->windowState() & Qt::WindowMaximized) {
+ if (window()->windowStates() & Qt::WindowMaximized) {
states |= NetWmStateMaximizedHorz;
states |= NetWmStateMaximizedVert;
}
@@ -1424,30 +1397,30 @@ void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp)
if (m_netWmUserTimeWindow || isSupportedByWM) {
if (!m_netWmUserTimeWindow) {
m_netWmUserTimeWindow = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- XCB_COPY_FROM_PARENT, // depth -- same as root
- m_netWmUserTimeWindow, // window id
- m_window, // parent window id
- -1, -1, 1, 1,
- 0, // border width
- XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
- m_visualId, // visual
- 0, // value mask
- 0)); // value list
+ xcb_create_window(xcb_connection(),
+ XCB_COPY_FROM_PARENT, // depth -- same as root
+ m_netWmUserTimeWindow, // window id
+ m_window, // parent window id
+ -1, -1, 1, 1,
+ 0, // border width
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
+ m_visualId, // visual
+ 0, // value mask
+ 0); // value list
wid = m_netWmUserTimeWindow;
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW),
XCB_ATOM_WINDOW, 32, 1, &m_netWmUserTimeWindow);
xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME));
#ifndef QT_NO_DEBUG
QByteArray ba("Qt NET_WM user time window");
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_netWmUserTimeWindow,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_netWmUserTimeWindow,
+ atom(QXcbAtom::_NET_WM_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
#endif
} else if (!isSupportedByWM) {
// WM no longer supports it, then we should remove the
@@ -1521,26 +1494,27 @@ void QXcbWindow::setParent(const QPlatformWindow *parent)
xcb_parent_id = xcbScreen()->root();
m_embedded = false;
}
- Q_XCB_CALL(xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y()));
+ xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y());
}
void QXcbWindow::setWindowTitle(const QString &title)
{
QString fullTitle = formatWindowTitle(title, QString::fromUtf8(" \xe2\x80\x94 ")); // unicode character U+2014, EM DASH
const QByteArray ba = std::move(fullTitle).toUtf8();
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
+ xcb_change_property(xcb_connection(),
XCB_PROP_MODE_REPLACE,
m_window,
atom(QXcbAtom::_NET_WM_NAME),
atom(QXcbAtom::UTF8_STRING),
8,
ba.length(),
- ba.constData()));
+ ba.constData());
#if QT_CONFIG(xcb_xlib)
- XTextProperty *text = qstringToXTP(DISPLAY_FROM_XCB(this), title);
+ Display *dpy = static_cast<Display *>(connection()->xlib_display());
+ XTextProperty *text = qstringToXTP(dpy, title);
if (text)
- XSetWMName(DISPLAY_FROM_XCB(this), m_window, text);
+ XSetWMName(dpy, m_window, text);
#endif
xcb_flush(xcb_connection());
}
@@ -1548,14 +1522,14 @@ void QXcbWindow::setWindowTitle(const QString &title)
void QXcbWindow::setWindowIconText(const QString &title)
{
const QByteArray ba = title.toUtf8();
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::_NET_WM_ICON_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_NET_WM_ICON_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
}
void QXcbWindow::setWindowIcon(const QIcon &icon)
@@ -1585,18 +1559,18 @@ void QXcbWindow::setWindowIcon(const QIcon &icon)
}
if (!icon_data.isEmpty()) {
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::_NET_WM_ICON),
- atom(QXcbAtom::CARDINAL),
- 32,
- icon_data.size(),
- (unsigned char *) icon_data.data()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_NET_WM_ICON),
+ atom(QXcbAtom::CARDINAL),
+ 32,
+ icon_data.size(),
+ (unsigned char *) icon_data.data());
} else {
- Q_XCB_CALL(xcb_delete_property(xcb_connection(),
- m_window,
- atom(QXcbAtom::_NET_WM_ICON)));
+ xcb_delete_property(xcb_connection(),
+ m_window,
+ atom(QXcbAtom::_NET_WM_ICON));
}
}
@@ -1604,14 +1578,14 @@ void QXcbWindow::raise()
{
const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
const quint32 values[] = { XCB_STACK_MODE_ABOVE };
- Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values));
+ xcb_configure_window(xcb_connection(), m_window, mask, values);
}
void QXcbWindow::lower()
{
const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
const quint32 values[] = { XCB_STACK_MODE_BELOW };
- Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values));
+ xcb_configure_window(xcb_connection(), m_window, mask, values);
}
// Adapt the geometry to match the WM expection with regards
@@ -1705,9 +1679,11 @@ void QXcbWindow::requestActivateWindow()
event.data.data32[3] = 0;
event.data.data32[4] = 0;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
+ xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (const char *)&event);
} else {
- Q_XCB_CALL(xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, connection()->time()));
+ xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, connection()->time());
}
connection()->sync();
@@ -1751,15 +1727,11 @@ QXcbWindowFunctions::WmWindowTypes QXcbWindow::wmWindowTypes() const
{
QXcbWindowFunctions::WmWindowTypes result(0);
- xcb_get_property_cookie_t get_cookie =
- xcb_get_property_unchecked(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE),
- XCB_ATOM_ATOM, 0, 1024);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
-
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
+ 0, m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE),
+ XCB_ATOM_ATOM, 0, 1024);
if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
- const xcb_atom_t *types = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply));
+ const xcb_atom_t *types = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
const xcb_atom_t *types_end = types + reply->length;
for (; types != types_end; types++) {
QXcbAtom::Atom type = connection()->qatom(*types);
@@ -1813,7 +1785,6 @@ QXcbWindowFunctions::WmWindowTypes QXcbWindow::wmWindowTypes() const
break;
}
}
- free(reply);
}
return result;
}
@@ -1896,20 +1867,20 @@ void QXcbWindow::setWmWindowType(QXcbWindowFunctions::WmWindowTypes types, Qt::W
atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL));
if (atoms.isEmpty()) {
- Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE)));
+ xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE));
} else {
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 32,
- atoms.count(), atoms.constData()));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 32,
+ atoms.count(), atoms.constData());
}
xcb_flush(xcb_connection());
}
void QXcbWindow::setWmWindowRole(const QByteArray &role)
{
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::WM_WINDOW_ROLE), XCB_ATOM_STRING, 8,
- role.size(), role.constData()));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::WM_WINDOW_ROLE), XCB_ATOM_STRING, 8,
+ role.size(), role.constData());
}
void QXcbWindow::setParentRelativeBackPixmapStatic(QWindow *window)
@@ -1922,7 +1893,7 @@ void QXcbWindow::setParentRelativeBackPixmap()
{
const quint32 mask = XCB_CW_BACK_PIXMAP;
const quint32 values[] = { XCB_BACK_PIXMAP_PARENT_RELATIVE };
- Q_XCB_CALL(xcb_change_window_attributes(xcb_connection(), m_window, mask, values));
+ xcb_change_window_attributes(xcb_connection(), m_window, mask, values);
}
bool QXcbWindow::requestSystemTrayWindowDockStatic(const QWindow *window)
@@ -2013,11 +1984,7 @@ void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event)
{
QRect rect(event->x, event->y, event->width, event->height);
- if (m_exposeRegion.isEmpty())
- m_exposeRegion = rect;
- else
- m_exposeRegion |= rect;
-
+ m_exposeRegion |= rect;
bool pending = compressExposeEvent(m_exposeRegion);
// if count is non-zero there are more expose events pending
@@ -2099,13 +2066,11 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *
QPoint pos(event->x, event->y);
if (!parent() && !fromSendEvent) {
// Do not trust the position, query it instead.
- xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcb_window(),
- xcbScreen()->root(), 0, 0);
- xcb_translate_coordinates_reply_t *reply = xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
+ xcb_window(), xcbScreen()->root(), 0, 0);
if (reply) {
pos.setX(reply->dst_x);
pos.setY(reply->dst_y);
- free(reply);
}
}
@@ -2114,14 +2079,6 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *
if (!newScreen)
return;
- // Persist the actual geometry so that QWindow::geometry() can
- // be queried in the resize event.
- QPlatformWindow::setGeometry(actualGeometry);
-
- // FIXME: In the case of the requestedGeometry not matching the actualGeometry due
- // to e.g. the window manager applying restrictions to the geometry, the application
- // will never see a move/resize event if the actualGeometry is the same as the current
- // geometry, and may think the requested geometry was fulfilled.
QWindowSystemInterface::handleGeometryChange(window(), actualGeometry);
// QPlatformScreen::screen() is updated asynchronously, so we can't compare it
@@ -2160,15 +2117,12 @@ QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const
return pos;
QPoint ret;
- xcb_translate_coordinates_cookie_t cookie =
- xcb_translate_coordinates(xcb_connection(), xcb_window(), xcbScreen()->root(),
- pos.x(), pos.y());
- xcb_translate_coordinates_reply_t *reply =
- xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
+ xcb_window(), xcbScreen()->root(),
+ pos.x(), pos.y());
if (reply) {
ret.setX(reply->dst_x);
ret.setY(reply->dst_y);
- free(reply);
}
return ret;
@@ -2180,15 +2134,12 @@ QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const
return pos;
QPoint ret;
- xcb_translate_coordinates_cookie_t cookie =
- xcb_translate_coordinates(xcb_connection(), xcbScreen()->root(), xcb_window(),
- pos.x(), pos.y());
- xcb_translate_coordinates_reply_t *reply =
- xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
+ xcbScreen()->root(), xcb_window(),
+ pos.x(), pos.y());
if (reply) {
ret.setX(reply->dst_x);
ret.setY(reply->dst_y);
- free(reply);
}
return ret;
@@ -2240,16 +2191,24 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in
QPoint global(root_x, root_y);
if (isWheel) {
+#if QT_CONFIG(xinput2)
if (!connection()->isAtLeastXI21()) {
- // Logic borrowed from qapplication_x11.cpp
- int delta = 120 * ((detail == 4 || detail == 6) ? 1 : -1);
- bool hor = (((detail == 4 || detail == 5)
- && (modifiers & Qt::AltModifier))
- || (detail == 6 || detail == 7));
-
- QWindowSystemInterface::handleWheelEvent(window(), timestamp,
- local, global, delta, hor ? Qt::Horizontal : Qt::Vertical, modifiers);
+#endif
+ QPoint angleDelta;
+ if (detail == 4)
+ angleDelta.setY(120);
+ else if (detail == 5)
+ angleDelta.setY(-120);
+ else if (detail == 6)
+ angleDelta.setX(120);
+ else if (detail == 7)
+ angleDelta.setX(-120);
+ if (modifiers & Qt::AltModifier)
+ std::swap(angleDelta.rx(), angleDelta.ry());
+ QWindowSystemInterface::handleWheelEvent(window(), timestamp, local, global, QPoint(), angleDelta, modifiers);
+#if QT_CONFIG(xinput2)
}
+#endif
return;
}
@@ -2269,7 +2228,7 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x,
return;
}
- if (connection()->buttons() == Qt::NoButton)
+ if (connection()->buttonState() == Qt::NoButton)
connection()->setMousePressWindow(Q_NULLPTR);
handleMouseEvent(timestamp, local, global, modifiers, source);
@@ -2284,9 +2243,10 @@ static inline bool doCheckUnGrabAncestor(QXcbConnection *conn)
* not pressed, otherwise (e.g. on Alt+Tab) it can igonre important enter/leave events.
*/
if (conn) {
- const bool mouseButtonsPressed = (conn->buttons() != Qt::NoButton);
-#ifdef XCB_USE_XINPUT22
- return mouseButtonsPressed || (conn->isAtLeastXI22() && conn->xi2MouseEvents());
+
+ const bool mouseButtonsPressed = (conn->buttonState() != Qt::NoButton);
+#if QT_CONFIG(xinput2)
+ return mouseButtonsPressed || (conn->hasXInput2() && !conn->xi2MouseEventsDisabled());
#else
return mouseButtonsPressed;
#endif
@@ -2335,7 +2295,8 @@ void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, in
{
connection()->setTime(timestamp);
#ifdef XCB_USE_XINPUT21
- connection()->handleEnterEvent();
+ // Updates scroll valuators, as user might have done some scrolling outside our X client.
+ connection()->xi2UpdateScrollingDevices();
#endif
const QPoint global = QPoint(root_x, root_y);
@@ -2378,7 +2339,7 @@ void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, i
// "mousePressWindow" can be NULL i.e. if a window will be grabbed or unmapped, so set it again here.
// Unset "mousePressWindow" when mouse button isn't pressed - in some cases the release event won't arrive.
- const bool isMouseButtonPressed = (connection()->buttons() != Qt::NoButton);
+ const bool isMouseButtonPressed = (connection()->buttonState() != Qt::NoButton);
const bool hasMousePressWindow = (connection()->mousePressWindow() != Q_NULLPTR);
if (isMouseButtonPressed && !hasMousePressWindow)
connection()->setMousePressWindow(this);
@@ -2388,7 +2349,6 @@ void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, i
handleMouseEvent(timestamp, local, global, modifiers, source);
}
-// Handlers for plain xcb events. Used only when XI 2.2 or newer is not available.
void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
{
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
@@ -2409,13 +2369,12 @@ void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers, event->time);
}
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
static inline int fixed1616ToInt(FP1616 val)
{
return int((qreal(val >> 16)) + (val & 0xFFFF) / (qreal)0xFFFF);
}
-// With XI 2.2+ press/release/motion comes here instead of the above handlers.
void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource source)
{
QXcbConnection *conn = connection();
@@ -2433,7 +2392,7 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource
return;
}
for (int i = 1; i <= 15; ++i)
- conn->setButton(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i));
+ conn->setButtonState(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i));
}
const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective_mods);
@@ -2457,13 +2416,13 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource
case XI_ButtonPress:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse press, button %d, time %d, source %s", button, ev->time, sourceName);
- conn->setButton(button, true);
+ conn->setButtonState(button, true);
handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, source);
break;
case XI_ButtonRelease:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse release, button %d, time %d, source %s", button, ev->time, sourceName);
- conn->setButton(button, false);
+ conn->setButtonState(button, false);
handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, source);
break;
case XI_Motion:
@@ -2477,7 +2436,6 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource
}
}
-// With XI 2.2+ enter/leave comes here and are blocked in plain xcb events
void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
{
xXIEnterEvent *ev = reinterpret_cast<xXIEnterEvent *>(event);
@@ -2497,12 +2455,14 @@ void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
case XI_Enter: {
const int event_x = fixed1616ToInt(ev->event_x);
const int event_y = fixed1616ToInt(ev->event_y);
- qCDebug(lcQpaXInput, "XI2 mouse enter %d,%d, mode %d, detail %d, time %d", event_x, event_y, ev->mode, ev->detail, ev->time);
+ qCDebug(lcQpaXInputEvents, "XI2 mouse enter %d,%d, mode %d, detail %d, time %d",
+ event_x, event_y, ev->mode, ev->detail, ev->time);
handleEnterNotifyEvent(event_x, event_y, root_x, root_y, ev->mode, ev->detail, ev->time);
break;
}
case XI_Leave:
- qCDebug(lcQpaXInput, "XI2 mouse leave, mode %d, detail %d, time %d", ev->mode, ev->detail, ev->time);
+ qCDebug(lcQpaXInputEvents, "XI2 mouse leave, mode %d, detail %d, time %d",
+ ev->mode, ev->detail, ev->time);
connection()->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);
handleLeaveNotifyEvent(root_x, root_y, ev->mode, ev->detail, ev->time);
break;
@@ -2516,7 +2476,7 @@ void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, con
Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source)
{
connection()->setTime(time);
- QWindowSystemInterface::handleMouseEvent(window(), time, local, global, connection()->buttons(), modifiers, source);
+ QWindowSystemInterface::handleMouseEvent(window(), time, local, global, connection()->buttonState(), modifiers, source);
}
void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
@@ -2539,44 +2499,33 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev
if (propertyDeleted)
return;
- Qt::WindowState newState = Qt::WindowNoState;
- if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'.
- const xcb_get_property_cookie_t get_cookie =
- xcb_get_property(xcb_connection(), 0, m_window, atom(QXcbAtom::WM_STATE),
- XCB_ATOM_ANY, 0, 1024);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
+ Qt::WindowStates newState = Qt::WindowNoState;
+ if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'.
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
+ 0, m_window, atom(QXcbAtom::WM_STATE),
+ XCB_ATOM_ANY, 0, 1024);
if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) {
- const quint32 *data = (const quint32 *)xcb_get_property_value(reply);
- if (reply->length != 0) {
- if (data[0] == XCB_WM_STATE_ICONIC
- || (data[0] == XCB_WM_STATE_WITHDRAWN
- && m_lastWindowStateEvent == Qt::WindowMinimized)) {
- newState = Qt::WindowMinimized;
- }
- }
+ const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get());
+ if (reply->length != 0)
+ m_minimized = (data[0] == XCB_WM_STATE_ICONIC
+ || (data[0] == XCB_WM_STATE_WITHDRAWN && m_minimized));
}
- free(reply);
- } else { // _NET_WM_STATE can't change minimized state
- if (m_lastWindowStateEvent == Qt::WindowMinimized)
- newState = Qt::WindowMinimized;
- }
-
- if (newState != Qt::WindowMinimized) { // Something else changed, get _NET_WM_STATE.
- const NetWmStates states = netWmStates();
- if (states & NetWmStateFullScreen)
- newState = Qt::WindowFullScreen;
- else if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert))
- newState = Qt::WindowMaximized;
}
+ if (m_minimized)
+ newState = Qt::WindowMinimized;
+
+ const NetWmStates states = netWmStates();
+ if (states & NetWmStateFullScreen)
+ newState |= Qt::WindowFullScreen;
+ if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert))
+ newState |= Qt::WindowMaximized;
// Send Window state, compress events in case other flags (modality, etc) are changed.
if (m_lastWindowStateEvent != newState) {
QWindowSystemInterface::handleWindowStateChanged(window(), newState);
m_lastWindowStateEvent = newState;
m_windowState = newState;
- if (m_windowState == Qt::WindowMinimized && connection()->mouseGrabber() == this)
+ if ((m_windowState & Qt::WindowMinimized) && connection()->mouseGrabber() == this)
connection()->setMouseGrabber(Q_NULLPTR);
}
return;
@@ -2611,7 +2560,7 @@ void QXcbWindow::updateSyncRequestCounter()
return;
}
if (m_usingSyncProtocol && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) {
- Q_XCB_CALL(xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue));
+ xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue);
xcb_flush(xcb_connection());
m_syncValue.lo = 0;
@@ -2635,21 +2584,20 @@ bool QXcbWindow::setKeyboardGrabEnabled(bool grab)
xcb_ungrab_keyboard(xcb_connection(), XCB_TIME_CURRENT_TIME);
return true;
}
- xcb_grab_keyboard_cookie_t cookie = xcb_grab_keyboard(xcb_connection(), false,
- m_window, XCB_TIME_CURRENT_TIME,
- XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
- xcb_grab_keyboard_reply_t *reply = xcb_grab_keyboard_reply(xcb_connection(), cookie, NULL);
- bool result = !(!reply || reply->status != XCB_GRAB_STATUS_SUCCESS);
- free(reply);
- return result;
+
+ auto reply = Q_XCB_REPLY(xcb_grab_keyboard, xcb_connection(), false,
+ m_window, XCB_TIME_CURRENT_TIME,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ return reply && reply->status == XCB_GRAB_STATUS_SUCCESS;
}
bool QXcbWindow::setMouseGrabEnabled(bool grab)
{
if (!grab && connection()->mouseGrabber() == this)
connection()->setMouseGrabber(Q_NULLPTR);
-#ifdef XCB_USE_XINPUT22
- if (connection()->isAtLeastXI22() && connection()->xi2MouseEvents()) {
+
+#if QT_CONFIG(xinput2)
+ if (connection()->hasXInput2() && !connection()->xi2MouseEventsDisabled()) {
bool result = connection()->xi2SetMouseGrabEnabled(m_window, grab);
if (grab && result)
connection()->setMouseGrabber(this);
@@ -2663,16 +2611,16 @@ bool QXcbWindow::setMouseGrabEnabled(bool grab)
xcb_ungrab_pointer(xcb_connection(), XCB_TIME_CURRENT_TIME);
return true;
}
- xcb_grab_pointer_cookie_t cookie = xcb_grab_pointer(xcb_connection(), false, m_window,
- (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
- | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW
- | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION),
- XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
- XCB_WINDOW_NONE, XCB_CURSOR_NONE,
- XCB_TIME_CURRENT_TIME);
- xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(xcb_connection(), cookie, NULL);
- bool result = !(!reply || reply->status != XCB_GRAB_STATUS_SUCCESS);
- free(reply);
+
+ auto reply = Q_XCB_REPLY(xcb_grab_pointer, xcb_connection(),
+ false, m_window,
+ (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
+ | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW
+ | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION),
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
+ XCB_WINDOW_NONE, XCB_CURSOR_NONE,
+ XCB_TIME_CURRENT_TIME);
+ bool result = reply && reply->status == XCB_GRAB_STATUS_SUCCESS;
if (result)
connection()->setMouseGrabber(this);
return result;
@@ -2780,8 +2728,7 @@ void QXcbWindow::sendXEmbedMessage(xcb_window_t window, quint32 message,
event.data.data32[2] = detail;
event.data.data32[3] = data1;
event.data.data32[4] = data2;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, window,
- XCB_EVENT_MASK_NO_EVENT, (const char *)&event));
+ xcb_send_event(xcb_connection(), false, window, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
}
static bool activeWindowChangeQueued(const QWindow *window)
@@ -2803,12 +2750,12 @@ void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event)
case XEMBED_WINDOW_DEACTIVATE:
break;
case XEMBED_EMBEDDED_NOTIFY:
- Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
+ xcb_map_window(xcb_connection(), m_window);
xcbScreen()->windowShown(this);
// Without Qt::WA_TranslucentBackground, we use a ParentRelative BackPixmap.
// Clear the whole tray icon window to its background color as early as possible
// so that we can get a clean result from grabWindow() later.
- Q_XCB_CALL(xcb_clear_area(xcb_connection(), false, m_window, 0, 0, geometry().width(), geometry().height()));
+ xcb_clear_area(xcb_connection(), false, m_window, 0, 0, geometry().width(), geometry().height());
xcb_flush(xcb_connection());
break;
case XEMBED_FOCUS_IN:
@@ -2855,14 +2802,14 @@ void QXcbWindow::setOpacity(qreal level)
quint32 value = qRound64(qBound(qreal(0), level, qreal(1)) * 0xffffffff);
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::_NET_WM_WINDOW_OPACITY),
- XCB_ATOM_CARDINAL,
- 32,
- 1,
- (uchar *)&value));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_NET_WM_WINDOW_OPACITY),
+ XCB_ATOM_CARDINAL,
+ 32,
+ 1,
+ (uchar *)&value);
}
void QXcbWindow::setMask(const QRegion &region)
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index f38343b6c2..1ce9b0a42f 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -82,7 +82,7 @@ public:
void setVisible(bool visible) override;
void setWindowFlags(Qt::WindowFlags flags) override;
- void setWindowState(Qt::WindowState state) override;
+ void setWindowState(Qt::WindowStates state) override;
WId winId() const override;
void setParent(const QPlatformWindow *window) override;
@@ -138,7 +138,7 @@ public:
void handleFocusInEvent(const xcb_focus_in_event_t *event) override;
void handleFocusOutEvent(const xcb_focus_out_event_t *event) override;
void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) override;
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized) override;
void handleXIEnterLeave(xcb_ge_event_t *) override;
#endif
@@ -244,7 +244,7 @@ protected:
xcb_sync_int64_t m_syncValue;
xcb_sync_counter_t m_syncCounter = 0;
- Qt::WindowState m_windowState = Qt::WindowNoState;
+ Qt::WindowStates m_windowState = Qt::WindowNoState;
xcb_gravity_t m_gravity = XCB_GRAVITY_STATIC;
@@ -254,6 +254,7 @@ protected:
bool m_deferredActivation = false;
bool m_embedded = false;
bool m_alertState = false;
+ bool m_minimized = false;
xcb_window_t m_netWmUserTimeWindow = XCB_NONE;
QSurfaceFormat m_format;
@@ -265,7 +266,8 @@ protected:
QSize m_oldWindowSize;
xcb_visualid_t m_visualId = 0;
- int m_lastWindowStateEvent = -1;
+ // Last sent state. Initialized to an invalid state, on purpose.
+ Qt::WindowStates m_lastWindowStateEvent = Qt::WindowActive;
enum SyncState {
NoSyncNeeded,
diff --git a/src/plugins/platforms/xcb/qxcbwmsupport.cpp b/src/plugins/platforms/xcb/qxcbwmsupport.cpp
index 470f021314..2619892b19 100644
--- a/src/plugins/platforms/xcb/qxcbwmsupport.cpp
+++ b/src/plugins/platforms/xcb/qxcbwmsupport.cpp
@@ -66,16 +66,15 @@ void QXcbWMSupport::updateNetWMAtoms()
int offset = 0;
int remaining = 0;
do {
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_SUPPORTED), XCB_ATOM_ATOM, offset, 1024);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, root, atom(QXcbAtom::_NET_SUPPORTED), XCB_ATOM_ATOM, offset, 1024);
if (!reply)
break;
remaining = 0;
if (reply->type == XCB_ATOM_ATOM && reply->format == 32) {
- int len = xcb_get_property_value_length(reply)/sizeof(xcb_atom_t);
- xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply);
+ int len = xcb_get_property_value_length(reply.get())/sizeof(xcb_atom_t);
+ xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
int s = net_wm_atoms.size();
net_wm_atoms.resize(s + len);
memcpy(net_wm_atoms.data() + s, atoms, len*sizeof(xcb_atom_t));
@@ -83,8 +82,6 @@ void QXcbWMSupport::updateNetWMAtoms()
remaining = reply->bytes_after;
offset += len;
}
-
- free(reply);
} while (remaining > 0);
}
@@ -100,16 +97,16 @@ void QXcbWMSupport::updateVirtualRoots()
int offset = 0;
int remaining = 0;
do {
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, offset, 1024);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
+ false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, offset, 1024);
if (!reply)
break;
remaining = 0;
if (reply->type == XCB_ATOM_WINDOW && reply->format == 32) {
- int len = xcb_get_property_value_length(reply)/sizeof(xcb_window_t);
- xcb_window_t *roots = (xcb_window_t *)xcb_get_property_value(reply);
+ int len = xcb_get_property_value_length(reply.get())/sizeof(xcb_window_t);
+ xcb_window_t *roots = (xcb_window_t *)xcb_get_property_value(reply.get());
int s = net_virtual_roots.size();
net_virtual_roots.resize(s + len);
memcpy(net_virtual_roots.data() + s, roots, len*sizeof(xcb_window_t));
@@ -118,10 +115,10 @@ void QXcbWMSupport::updateVirtualRoots()
offset += len;
}
- free(reply);
} while (remaining > 0);
-#ifdef Q_XCB_DEBUG
+//#define VIRTUAL_ROOTS_DEBUG
+#ifdef VIRTUAL_ROOTS_DEBUG
qDebug("======== updateVirtualRoots");
for (int i = 0; i < net_virtual_roots.size(); ++i)
qDebug() << connection()->atomName(net_virtual_roots.at(i));
diff --git a/src/plugins/platforms/xcb/qxcbxsettings.cpp b/src/plugins/platforms/xcb/qxcbxsettings.cpp
index 88933c6c22..bd398ea049 100644
--- a/src/plugins/platforms/xcb/qxcbxsettings.cpp
+++ b/src/plugins/platforms/xcb/qxcbxsettings.cpp
@@ -106,26 +106,23 @@ public:
QByteArray settings;
xcb_atom_t _xsettings_atom = screen->connection()->atom(QXcbAtom::_XSETTINGS_SETTINGS);
while (1) {
- xcb_get_property_cookie_t get_prop_cookie =
- xcb_get_property_unchecked(screen->xcb_connection(),
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property,
+ screen->xcb_connection(),
false,
x_settings_window,
_xsettings_atom,
_xsettings_atom,
offset/4,
8192);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(screen->xcb_connection(), get_prop_cookie, NULL);
bool more = false;
if (!reply)
return settings;
- const auto property_value_length = xcb_get_property_value_length(reply);
- settings.append(static_cast<const char *>(xcb_get_property_value(reply)), property_value_length);
+ const auto property_value_length = xcb_get_property_value_length(reply.get());
+ settings.append(static_cast<const char *>(xcb_get_property_value(reply.get())), property_value_length);
offset += property_value_length;
more = reply->bytes_after != 0;
- free(reply);
-
if (!more)
break;
}
@@ -228,34 +225,24 @@ QXcbXSettings::QXcbXSettings(QXcbVirtualDesktop *screen)
{
QByteArray settings_atom_for_screen("_XSETTINGS_S");
settings_atom_for_screen.append(QByteArray::number(screen->number()));
- xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(screen->xcb_connection(),
- true,
- settings_atom_for_screen.length(),
- settings_atom_for_screen.constData());
- xcb_generic_error_t *error = 0;
- xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(screen->xcb_connection(),atom_cookie,&error);
- if (error) {
- free(error);
+ auto atom_reply = Q_XCB_REPLY(xcb_intern_atom,
+ screen->xcb_connection(),
+ true,
+ settings_atom_for_screen.length(),
+ settings_atom_for_screen.constData());
+ if (!atom_reply)
return;
- }
- xcb_atom_t selection_owner_atom = atom_reply->atom;
- free(atom_reply);
- xcb_get_selection_owner_cookie_t selection_cookie =
- xcb_get_selection_owner(screen->xcb_connection(), selection_owner_atom);
+ xcb_atom_t selection_owner_atom = atom_reply->atom;
- xcb_get_selection_owner_reply_t *selection_result =
- xcb_get_selection_owner_reply(screen->xcb_connection(), selection_cookie, &error);
- if (error) {
- free(error);
+ auto selection_result = Q_XCB_REPLY(xcb_get_selection_owner,
+ screen->xcb_connection(), selection_owner_atom);
+ if (!selection_result)
return;
- }
d_ptr->x_settings_window = selection_result->owner;
- free(selection_result);
- if (!d_ptr->x_settings_window) {
+ if (!d_ptr->x_settings_window)
return;
- }
const uint32_t event = XCB_CW_EVENT_MASK;
const uint32_t event_mask[] = { XCB_EVENT_MASK_STRUCTURE_NOTIFY|XCB_EVENT_MASK_PROPERTY_CHANGE };
diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
index 284711075e..a98a7892dd 100644
--- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro
+++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
@@ -5,11 +5,14 @@ DEFINES += QT_NO_FOREACH
QT += \
core-private gui-private \
service_support-private theme_support-private \
- eventdispatcher_support-private fontdatabase_support-private
+ eventdispatcher_support-private fontdatabase_support-private \
+ edid_support-private
qtHaveModule(linuxaccessibility_support-private): \
QT += linuxaccessibility_support-private
+qtConfig(vulkan): QT += vulkan_support-private
+
SOURCES = \
qxcbclipboard.cpp \
qxcbconnection.cpp \
@@ -65,6 +68,17 @@ qtConfig(xcb-sm) {
}
include(gl_integrations/gl_integrations.pri)
+include(nativepainting/nativepainting.pri)
+
+qtConfig(vulkan) {
+ SOURCES += \
+ qxcbvulkaninstance.cpp \
+ qxcbvulkanwindow.cpp
+
+ HEADERS += \
+ qxcbvulkaninstance.h \
+ qxcbvulkanwindow.h
+}
!qtConfig(system-xcb) {
QMAKE_USE += xcb-static xcb