diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
33 files changed, 1005 insertions, 496 deletions
diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h index 926e5e22df..07e983a499 100644 --- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h +++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h @@ -69,7 +69,7 @@ public: #endif virtual QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const = 0; - virtual QXcbNativeInterfaceHandler *nativeInterfaceHandler() const { return Q_NULLPTR; } + virtual QXcbNativeInterfaceHandler *nativeInterfaceHandler() const { return nullptr; } }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbnativeinterfacehandler.cpp b/src/plugins/platforms/xcb/gl_integrations/qxcbnativeinterfacehandler.cpp index ac992b859d..e18656c6ec 100644 --- a/src/plugins/platforms/xcb/gl_integrations/qxcbnativeinterfacehandler.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/qxcbnativeinterfacehandler.cpp @@ -56,37 +56,37 @@ QXcbNativeInterfaceHandler::~QXcbNativeInterfaceHandler() QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForIntegration(const QByteArray &resource) const { Q_UNUSED(resource); - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForContext(const QByteArray &resource) const { Q_UNUSED(resource); - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForScreen(const QByteArray &resource) const { Q_UNUSED(resource); - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForWindow(const QByteArray &resource) const { Q_UNUSED(resource); - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForBackingStore(const QByteArray &resource) const { Q_UNUSED(resource); - return Q_NULLPTR; + return nullptr; } QFunctionPointer QXcbNativeInterfaceHandler::platformFunction(const QByteArray &function) const { Q_UNUSED(function); - return Q_NULLPTR; + return nullptr; } QT_END_NAMESPACE 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 fe50afa62a..c3ce8d8745 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h @@ -59,6 +59,14 @@ public: void swapBuffers(QPlatformSurface *surface) { QEGLPlatformContext::swapBuffers(surface); + if (surface->surface()->surfaceClass() == QSurface::Window) { + QXcbWindow *platformWindow = static_cast<QXcbWindow *>(surface); + // OpenGL context might be bound to a non-gui thread use QueuedConnection to sync + // the window from the platformWindow's thread as QXcbWindow is no QObject, an + // event is sent to QXcbConnection. (this is faster than a metacall) + if (platformWindow->needsSync()) + platformWindow->postSyncWindowRequest(); + } } bool makeCurrent(QPlatformSurface *surface) 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 7aa1d631df..fe18bc24db 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE QXcbEglIntegration::QXcbEglIntegration() - : m_connection(Q_NULLPTR) + : m_connection(nullptr) , m_egl_display(EGL_NO_DISPLAY) { qCDebug(lcQpaGl) << "Xcb EGL gl-integration created"; diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglnativeinterfacehandler.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglnativeinterfacehandler.cpp index 30f5e3a00d..c0e3f820fe 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglnativeinterfacehandler.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglnativeinterfacehandler.cpp @@ -77,7 +77,7 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbEglNativeInte default: break; } - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForContextFunction QXcbEglNativeInterfaceHandler::nativeResourceFunctionForContext(const QByteArray &resource) const @@ -90,7 +90,7 @@ QPlatformNativeInterface::NativeResourceForContextFunction QXcbEglNativeInterfac default: break; } - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForWindowFunction QXcbEglNativeInterfaceHandler::nativeResourceFunctionForWindow(const QByteArray &resource) const @@ -101,7 +101,7 @@ QPlatformNativeInterface::NativeResourceForWindowFunction QXcbEglNativeInterface default: break; } - return Q_NULLPTR; + return nullptr; } void *QXcbEglNativeInterfaceHandler::eglDisplay() @@ -114,11 +114,11 @@ void *QXcbEglNativeInterfaceHandler::eglDisplay() void *QXcbEglNativeInterfaceHandler::eglDisplayForWindow(QWindow *window) { Q_ASSERT(window); - if (window->supportsOpenGL() && window->handle() == Q_NULLPTR) + if (window->supportsOpenGL() && window->handle() == nullptr) return eglDisplay(); else if (window->supportsOpenGL()) return static_cast<QXcbEglWindow *>(window->handle())->glIntegration()->eglDisplay(); - return Q_NULLPTR; + return nullptr; } void *QXcbEglNativeInterfaceHandler::eglContextForContext(QOpenGLContext *context) diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp index 9c3fd26d49..65beac227c 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE QXcbEglWindow::QXcbEglWindow(QWindow *window, QXcbEglIntegration *glIntegration) : QXcbWindow(window) , m_glIntegration(glIntegration) - , m_config(Q_NULLPTR) + , m_config(nullptr) , m_surface(EGL_NO_SURFACE) { } 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 3bc8590d36..56a737e882 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp @@ -321,7 +321,7 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share) if (!m_context && m_shareContext) { // re-try without a shared glx context m_shareContext = 0; - m_context = glXCreateContext(m_display, visualInfo, Q_NULLPTR, true); + m_context = glXCreateContext(m_display, visualInfo, nullptr, true); } // Create a temporary window so that we can make the new context current @@ -470,7 +470,7 @@ static QXcbScreen *screenForPlatformSurface(QPlatformSurface *surface) } else if (surfaceClass == QSurface::Offscreen) { return static_cast<QXcbScreen *>(static_cast<QGLXPbuffer *>(surface)->screen()); } - return Q_NULLPTR; + return nullptr; } QVariant QGLXContext::nativeHandle() const 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 377066df61..13f03f8bf3 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp @@ -87,7 +87,7 @@ QT_BEGIN_NAMESPACE #endif QXcbGlxIntegration::QXcbGlxIntegration() - : m_connection(Q_NULLPTR) + : m_connection(nullptr) , m_glx_first_event(0) { qCDebug(lcQpaGl) << "Xcb GLX gl-integration created"; diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxnativeinterfacehandler.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxnativeinterfacehandler.cpp index 638fdd46b5..e9bb4460ff 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxnativeinterfacehandler.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxnativeinterfacehandler.cpp @@ -72,7 +72,7 @@ QPlatformNativeInterface::NativeResourceForContextFunction QXcbGlxNativeInterfac default: break; } - return Q_NULLPTR; + return nullptr; } void *QXcbGlxNativeInterfaceHandler::glxContextForContext(QOpenGLContext *context) 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 145a11a5e3..d682ea87fb 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp @@ -58,7 +58,7 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual() { QXcbScreen *scr = xcbScreen(); if (!scr) - return Q_NULLPTR; + return nullptr; qDebug(lcQpaGl) << "Requested format before FBConfig/Visual selection:" << m_format; @@ -74,7 +74,7 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual() 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; + return nullptr; } const xcb_visualtype_t *xcb_visualtype = scr->visualForId(visualInfo->visualid); XFree(visualInfo); diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp index bdf78b5255..9b31998620 100644 --- a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp @@ -121,7 +121,7 @@ void QXcbNativeBackingStore::flush(QWindow *window, const QRegion ®ion, const else #endif { - GC gc = XCreateGC(display(), wid, 0, Q_NULLPTR); + GC gc = XCreateGC(display(), wid, 0, nullptr); if (clipRects.size() != 1) XSetClipRectangles(display(), gc, 0, 0, clipRects.data(), clipRects.size(), YXBanded); @@ -161,7 +161,7 @@ void QXcbNativeBackingStore::resize(const QSize &size, const QRegion &staticCont QRect br = staticContents.boundingRect().intersected(QRect(QPoint(0, 0), size)); if (!br.isEmpty()) { - GC gc = XCreateGC(display(), to, 0, Q_NULLPTR); + GC gc = XCreateGC(display(), to, 0, nullptr); XCopyArea(display(), from, to, gc, br.x(), br.y(), br.width(), br.height(), br.x(), br.y()); XFreeGC(display(), gc); } @@ -178,7 +178,7 @@ bool QXcbNativeBackingStore::scroll(const QRegion &area, int dx, int dy) QRect rect = area.boundingRect(); Pixmap pix = qt_x11PixmapHandle(m_pixmap); - GC gc = XCreateGC(display(), pix, 0, Q_NULLPTR); + GC gc = XCreateGC(display(), pix, 0, nullptr); XCopyArea(display(), pix, pix, gc, rect.x(), rect.y(), rect.width(), rect.height(), rect.x()+dx, rect.y()+dy); diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h index 5338b5fe08..9b01c0a3fc 100644 --- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h +++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h @@ -62,10 +62,10 @@ public: QX11PaintEngine(); ~QX11PaintEngine(); - bool begin(QPaintDevice *pdev) Q_DECL_OVERRIDE; - bool end() Q_DECL_OVERRIDE; + bool begin(QPaintDevice *pdev) override; + bool end() override; - void updateState(const QPaintEngineState &state) Q_DECL_OVERRIDE; + void updateState(const QPaintEngineState &state) override; void updatePen(const QPen &pen); void updateBrush(const QBrush &brush, const QPointF &pt); @@ -74,31 +74,31 @@ public: void updateMatrix(const QTransform &matrix); void updateClipRegion_dev(const QRegion ®ion, Qt::ClipOperation op); - void drawLines(const QLine *lines, int lineCount) Q_DECL_OVERRIDE; - void drawLines(const QLineF *lines, int lineCount) Q_DECL_OVERRIDE; + void drawLines(const QLine *lines, int lineCount) override; + void drawLines(const QLineF *lines, int lineCount) override; - void drawRects(const QRect *rects, int rectCount) Q_DECL_OVERRIDE; - void drawRects(const QRectF *rects, int rectCount) Q_DECL_OVERRIDE; + void drawRects(const QRect *rects, int rectCount) override; + void drawRects(const QRectF *rects, int rectCount) override; - void drawPoints(const QPoint *points, int pointCount) Q_DECL_OVERRIDE; - void drawPoints(const QPointF *points, int pointCount) Q_DECL_OVERRIDE; + void drawPoints(const QPoint *points, int pointCount) override; + void drawPoints(const QPointF *points, int pointCount) override; - void drawEllipse(const QRect &r) Q_DECL_OVERRIDE; - void drawEllipse(const QRectF &r) Q_DECL_OVERRIDE; + void drawEllipse(const QRect &r) override; + void drawEllipse(const QRectF &r) 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 + virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override; + inline void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) 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 drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override; + void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) override; + void drawPath(const QPainterPath &path) override; + void drawTextItem(const QPointF &p, const QTextItem &textItem) override; void drawImage(const QRectF &r, const QImage &img, const QRectF &sr, - Qt::ImageConversionFlags flags = Qt::AutoColor) Q_DECL_OVERRIDE; + Qt::ImageConversionFlags flags = Qt::AutoColor) override; virtual Drawable handle() const; - inline Type type() const Q_DECL_OVERRIDE { return QPaintEngine::X11; } + inline Type type() const override { return QPaintEngine::X11; } QPainter::RenderHints supportedRenderHints() const; diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h index 8bff9e0d93..7392cbfccf 100644 --- a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h +++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h @@ -62,22 +62,22 @@ 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; + QPlatformPixmap *createCompatiblePlatformPixmap() const override; + void resize(int width, int height) override; + void fromImage(const QImage &img, Qt::ImageConversionFlags flags) override; + void copy(const QPlatformPixmap *data, const QRect &rect) override; + bool scroll(int dx, int dy, const QRect &rect) override; + int metric(QPaintDevice::PaintDeviceMetric metric) const override; + void fill(const QColor &fillColor) override; + QBitmap mask() const override; + void setMask(const QBitmap &mask) override; + bool hasAlphaChannel() const override; + QPixmap transformed(const QTransform &matrix, Qt::TransformationMode mode) const override; + QImage toImage() const override; + QImage toImage(const QRect &rect) const override; + QPaintEngine *paintEngine() const override; + qreal devicePixelRatio() const override; + void setDevicePixelRatio(qreal scaleFactor) override; inline Drawable handle() const { return hd; } inline Picture x11PictureHandle() const { return picture; } @@ -129,7 +129,7 @@ inline QX11PlatformPixmap *qt_x11Pixmap(const QPixmap &pixmap) { return (pixmap.handle() && pixmap.handle()->classId() == QPlatformPixmap::X11Class) ? static_cast<QX11PlatformPixmap *>(pixmap.handle()) - : Q_NULLPTR; + : nullptr; } inline Picture qt_x11PictureHandle(const QPixmap &pixmap) diff --git a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp index 4a587ceb36..57b1882e4b 100644 --- a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp @@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE -QXcbX11Data *qt_x11Data = Q_NULLPTR; +QXcbX11Data *qt_x11Data = nullptr; void qt_xcb_native_x11_info_init(QXcbConnection *conn) { @@ -111,7 +111,7 @@ void qt_xcb_native_x11_info_init(QXcbConnection *conn) QVector<XRectangle> qt_region_to_xrectangles(const QRegion &r) { const int numRects = r.rectCount(); - const QVector<QRect> input = r.rects(); + const auto input = r.begin(); QVector<XRectangle> output(numRects); for (int i = 0; i < numRects; ++i) { const QRect &in = input[i]; @@ -128,7 +128,7 @@ class QXcbX11InfoData : public QSharedData, public QX11InfoData {}; QXcbX11Info::QXcbX11Info() - : d(Q_NULLPTR) + : d(nullptr) {} QXcbX11Info::~QXcbX11Info() diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index 420d1ac7c5..1cf45c96d1 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -69,6 +69,8 @@ public: QXcbShmImage(QXcbScreen *connection, const QSize &size, uint depth, QImage::Format format); ~QXcbShmImage() { destroy(); } + void flushScrolledRegion(bool clientSideScroll); + bool scroll(const QRegion &area, int dx, int dy); QImage *image() { return &m_qimage; } @@ -86,7 +88,8 @@ private: void destroy(); void ensureGC(xcb_drawable_t dst); - void flushPixmap(const QRegion ®ion); + void shmPutImage(xcb_drawable_t drawable, const QRegion ®ion, const QPoint &offset = QPoint()); + void flushPixmap(const QRegion ®ion, bool fullRegion = false); void setClip(const QRegion ®ion); xcb_shm_segment_info_t m_shm_info; @@ -99,18 +102,26 @@ private: xcb_gcontext_t m_gc; xcb_drawable_t m_gc_drawable; - // When using shared memory this is the region currently shared with the server - QRegion m_dirtyShm; - + // When using shared memory these variables are used only for server-side scrolling. // When not using shared memory, we maintain a server-side pixmap with the backing // store as well as repainted content not yet flushed to the pixmap. We only flush // the regions we need and only when these are marked dirty. This way we can just // do a server-side copy on expose instead of sending the pixels every time xcb_pixmap_t m_xcb_pixmap; QRegion m_pendingFlush; + + // This is the scrolled region which is stored in server-side pixmap + QRegion m_scrolledRegion; + + // When using shared memory this is the region currently shared with the server + QRegion m_dirtyShm; + + // When not using shared memory this is a temporary buffer which is uploaded + // as a pixmap region to server QByteArray m_flushBuffer; bool m_hasAlpha; + bool m_clientSideScroll; }; class QXcbShmGraphicsBuffer : public QPlatformGraphicsBuffer @@ -145,10 +156,11 @@ private: QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QImage::Format format) : QXcbObject(screen->connection()) - , m_graphics_buffer(Q_NULLPTR) + , m_graphics_buffer(nullptr) , m_gc(0) , m_gc_drawable(0) , m_xcb_pixmap(0) + , m_clientSideScroll(false) { const xcb_format_t *fmt = connection()->formatForDepth(depth); Q_ASSERT(fmt); @@ -202,13 +214,59 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI m_qimage = QImage( (uchar*) m_xcb_image->data, m_xcb_image->width, m_xcb_image->height, m_xcb_image->stride, format); m_graphics_buffer = new QXcbShmGraphicsBuffer(&m_qimage); - if (!hasShm()) { - m_xcb_pixmap = xcb_generate_id(xcb_connection()); - xcb_create_pixmap(xcb_connection(), - m_xcb_image->depth, - m_xcb_pixmap, - screen->screen()->root, - m_xcb_image->width, m_xcb_image->height); + m_xcb_pixmap = xcb_generate_id(xcb_connection()); + xcb_create_pixmap(xcb_connection(), + m_xcb_image->depth, + m_xcb_pixmap, + screen->screen()->root, + m_xcb_image->width, m_xcb_image->height); +} + +void QXcbShmImage::flushScrolledRegion(bool clientSideScroll) +{ + if (m_clientSideScroll == clientSideScroll) + return; + + m_clientSideScroll = clientSideScroll; + + if (m_scrolledRegion.isNull()) + return; + + if (hasShm() && m_dirtyShm.intersects(m_scrolledRegion)) { + connection()->sync(); + m_dirtyShm = QRegion(); + } + + if (m_clientSideScroll) { + // Copy scrolled image region from server-side pixmap to client-side memory + for (const QRect &rect : m_scrolledRegion) { + const int w = rect.width(); + const int h = rect.height(); + + auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_image, + xcb_connection(), + m_xcb_image->format, + m_xcb_pixmap, + rect.x(), rect.y(), + w, h, + ~0u); + + if (reply && reply->depth == m_xcb_image->depth) { + const QImage img(xcb_get_image_data(reply.get()), w, h, m_qimage.format()); + + QPainter p(&m_qimage); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.drawImage(rect.topLeft(), img); + } + } + m_scrolledRegion = QRegion(); + } else { + // Copy scrolled image region from client-side memory to server-side pixmap + ensureGC(m_xcb_pixmap); + if (hasShm()) + shmPutImage(m_xcb_pixmap, m_scrolledRegion); + else + flushPixmap(m_scrolledRegion, true); } } @@ -216,21 +274,28 @@ extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &o bool QXcbShmImage::scroll(const QRegion &area, int dx, int dy) { - if (image()->isNull()) - return false; + const QRect bounds(QPoint(), size()); + const QRegion scrollArea(area & bounds); + const QPoint delta(dx, dy); - if (hasShm()) - preparePaint(area); + if (m_clientSideScroll) { + if (m_qimage.isNull()) + return false; - const QPoint delta(dx, dy); - for (const QRect &rect : area) - qt_scrollRectInImage(*image(), rect, delta); + if (hasShm()) + preparePaint(scrollArea); + + for (const QRect &rect : scrollArea) + qt_scrollRectInImage(m_qimage, rect, delta); + } else { + if (hasShm()) + shmPutImage(m_xcb_pixmap, m_pendingFlush.intersected(scrollArea)); + else + flushPixmap(scrollArea); - if (m_xcb_pixmap) { - flushPixmap(area); ensureGC(m_xcb_pixmap); - const QRect bounds(QPoint(0, 0), size()); - for (const QRect &src : area) { + + for (const QRect &src : scrollArea) { const QRect dst = src.translated(delta).intersected(bounds); xcb_copy_area(xcb_connection(), m_xcb_pixmap, @@ -242,6 +307,12 @@ bool QXcbShmImage::scroll(const QRegion &area, int dx, int dy) } } + m_scrolledRegion |= scrollArea.translated(delta).intersected(bounds); + if (hasShm()) { + m_pendingFlush -= scrollArea; + m_pendingFlush -= m_scrolledRegion; + } + return true; } @@ -265,12 +336,10 @@ void QXcbShmImage::destroy() if (m_gc) xcb_free_gc(xcb_connection(), m_gc); delete m_graphics_buffer; - m_graphics_buffer = Q_NULLPTR; + m_graphics_buffer = nullptr; - if (m_xcb_pixmap) { - xcb_free_pixmap(xcb_connection(), m_xcb_pixmap); - m_xcb_pixmap = 0; - } + xcb_free_pixmap(xcb_connection(), m_xcb_pixmap); + m_xcb_pixmap = 0; } void QXcbShmImage::ensureGC(xcb_drawable_t dst) @@ -353,10 +422,35 @@ static inline quint32 round_up_scanline(quint32 base, quint32 pad) return (base + pad - 1) & -pad; } -void QXcbShmImage::flushPixmap(const QRegion ®ion) +void QXcbShmImage::shmPutImage(xcb_drawable_t drawable, const QRegion ®ion, const QPoint &offset) +{ + for (const QRect &rect : region) { + const QPoint source = rect.translated(offset).topLeft(); + xcb_shm_put_image(xcb_connection(), + drawable, + m_gc, + m_xcb_image->width, + m_xcb_image->height, + source.x(), source.y(), + rect.width(), rect.height(), + rect.x(), rect.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); +} + +void QXcbShmImage::flushPixmap(const QRegion ®ion, bool fullRegion) { - const QVector<QRect> rects = m_pendingFlush.intersected(region).rects(); - m_pendingFlush -= region; + if (!fullRegion) { + auto actualRegion = m_pendingFlush.intersected(region); + m_pendingFlush -= region; + flushPixmap(actualRegion, true); + return; + } xcb_image_t xcb_subimage; memset(&xcb_subimage, 0, sizeof(xcb_image_t)); @@ -372,7 +466,7 @@ void QXcbShmImage::flushPixmap(const QRegion ®ion) const bool needsByteSwap = xcb_subimage.byte_order != m_xcb_image->byte_order; - for (const QRect &rect : rects) { + for (const QRect &rect : region) { // We must make sure that each request is not larger than max_req_size. // Each request takes req_size + m_xcb_image->stride * height bytes. static const uint32_t req_size = sizeof(xcb_put_image_request_t); @@ -425,16 +519,7 @@ void QXcbShmImage::setClip(const QRegion ®ion) static const uint32_t values[] = { XCB_NONE }; xcb_change_gc(xcb_connection(), m_gc, mask, values); } else { - const QVector<QRect> qrects = region.rects(); - QVector<xcb_rectangle_t> xcb_rects(qrects.size()); - - for (int i = 0; i < qrects.size(); i++) { - xcb_rects[i].x = qrects[i].x(); - xcb_rects[i].y = qrects[i].y(); - xcb_rects[i].width = qrects[i].width(); - xcb_rects[i].height = qrects[i].height(); - } - + const auto xcb_rects = qRegionToXcbRectangleList(region); xcb_set_clip_rectangles(xcb_connection(), XCB_CLIP_ORDERING_YX_BANDED, m_gc, @@ -445,29 +530,32 @@ void QXcbShmImage::setClip(const QRegion ®ion) void QXcbShmImage::put(xcb_drawable_t dst, const QRegion ®ion, const QPoint &offset) { + Q_ASSERT(!m_clientSideScroll); + ensureGC(dst); setClip(region); - const QRect bounds = region.boundingRect(); - const QPoint target = bounds.topLeft(); - const QRect source = bounds.translated(offset); - if (hasShm()) { - xcb_shm_put_image(xcb_connection(), + // Copy scrolled area on server-side from pixmap to window + const QRegion scrolledRegion = m_scrolledRegion.translated(-offset); + for (const QRect &rect : scrolledRegion) { + const QPoint source = rect.translated(offset).topLeft(); + xcb_copy_area(xcb_connection(), + m_xcb_pixmap, 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); + rect.x(), rect.y(), + rect.width(), rect.height()); + } + + // Copy non-scrolled image from client-side memory to server-side window + const QRegion notScrolledArea = region - scrolledRegion; + shmPutImage(dst, notScrolledArea, offset); } else { + const QRect bounds = region.boundingRect(); + const QPoint target = bounds.topLeft(); + const QRect source = bounds.translated(offset); flushPixmap(region); xcb_copy_area(xcb_connection(), m_xcb_pixmap, @@ -489,9 +577,9 @@ void QXcbShmImage::preparePaint(const QRegion ®ion) connection()->sync(); m_dirtyShm = QRegion(); } - } else { - m_pendingFlush |= region; } + m_scrolledRegion -= region; + m_pendingFlush |= region; } QXcbBackingStore::QXcbBackingStore(QWindow *window) @@ -565,7 +653,7 @@ QImage QXcbBackingStore::toImage() const QPlatformGraphicsBuffer *QXcbBackingStore::graphicsBuffer() const { - return m_image ? m_image->graphicsBuffer() : Q_NULLPTR; + return m_image ? m_image->graphicsBuffer() : nullptr; } void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) @@ -573,6 +661,8 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin if (!m_image || m_image->size().isEmpty()) return; + m_image->flushScrolledRegion(false); + QSize imageSize = m_image->size(); QRegion clipped = region; @@ -603,6 +693,11 @@ void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, c QPlatformTextureList *textures, bool translucentBackground) { + if (!m_image || m_image->size().isEmpty()) + return; + + m_image->flushScrolledRegion(true); + QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground); QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle()); diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index 30ab669432..b091928e8c 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -899,10 +899,7 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t win, int connection()->flush(); // sleep 50 ms, so we don't use up CPU cycles all the time. - struct timeval usleep_tv; - usleep_tv.tv_sec = 0; - usleep_tv.tv_usec = 50000; - select(0, 0, 0, 0, &usleep_tv); + QThread::msleep(50); } while (timer.elapsed() < timeout); return 0; diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 536c709dbe..c5eae20266 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -369,7 +369,7 @@ void QXcbConnection::destroyScreen(QXcbScreen *screen) // If there are no other screens on the same virtual desktop, // then transform the physical screen into a fake screen. const QString nameWas = screen->name(); - screen->setOutput(XCB_NONE, Q_NULLPTR); + screen->setOutput(XCB_NONE, nullptr); qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen; } else { // There is more than one screen on the same virtual desktop, remove the screen @@ -395,7 +395,7 @@ void QXcbConnection::initializeScreens() { xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); int xcbScreenNumber = 0; // screen number in the xcb sense - QXcbScreen *primaryScreen = Q_NULLPTR; + QXcbScreen *primaryScreen = nullptr; while (it.rem) { // Each "screen" in xcb terminology is a virtual desktop, // potentially a collection of separate juxtaposed monitors. @@ -415,7 +415,7 @@ void QXcbConnection::initializeScreens() qWarning("failed to get the current screen resources"); } else { xcb_timestamp_t timestamp = 0; - xcb_randr_output_t *outputs = Q_NULLPTR; + xcb_randr_output_t *outputs = nullptr; int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get()); if (outputCount) { timestamp = resources_current->config_timestamp; @@ -487,7 +487,7 @@ void QXcbConnection::initializeScreens() while (it.rem) { xcb_xinerama_screen_info_t *screen_info = it.data; QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, - XCB_NONE, Q_NULLPTR, + XCB_NONE, nullptr, screen_info, it.index); siblings << screen; m_screens << screen; @@ -498,7 +498,7 @@ void QXcbConnection::initializeScreens() if (siblings.isEmpty()) { // If there are no XRandR outputs or XRandR extension is missing, // then create a fake/legacy screen. - QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, Q_NULLPTR); + QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr); qCDebug(lcQpaScreen) << "created fake screen" << screen; m_screens << screen; if (m_primaryScreenNumber == xcbScreenNumber) { @@ -631,7 +631,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra if (m_glIntegration && !m_glIntegration->initialize(this)) { qCDebug(lcQpaGl) << "Failed to initialize xcb gl-integration" << glIntegrationNames.at(i); delete m_glIntegration; - m_glIntegration = Q_NULLPTR; + m_glIntegration = nullptr; } } if (!m_glIntegration) @@ -689,7 +689,7 @@ QXcbScreen *QXcbConnection::primaryScreen() const return m_screens.first(); } - return Q_NULLPTR; + return nullptr; } void QXcbConnection::addWindowEventListener(xcb_window_t id, QXcbWindowEventListener *eventListener) @@ -983,6 +983,12 @@ static Qt::MouseButtons translateMouseButtons(int s) return ret; } +void QXcbConnection::setButtonState(Qt::MouseButton button, bool down) +{ + m_buttonState.setFlag(button, down); + m_button = button; +} + Qt::MouseButton QXcbConnection::translateMouseButton(xcb_button_t s) { switch (s) { @@ -1055,7 +1061,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) // the event explicitly contains the state of the three first buttons, // the rest we need to manage ourselves m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state); - m_buttonState |= translateMouseButton(ev->detail); + setButtonState(translateMouseButton(ev->detail), true); if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) 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); @@ -1064,7 +1070,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event; m_keyboard->updateXKBStateFromCore(ev->state); m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state); - m_buttonState &= ~translateMouseButton(ev->detail); + setButtonState(translateMouseButton(ev->detail), false); if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) 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); @@ -1413,7 +1419,7 @@ void QXcbConnection::setFocusWindow(QWindow *w) void QXcbConnection::setMouseGrabber(QXcbWindow *w) { m_mouseGrabber = w; - m_mousePressWindow = Q_NULLPTR; + m_mousePressWindow = nullptr; } void QXcbConnection::setMousePressWindow(QXcbWindow *w) { @@ -2070,7 +2076,8 @@ const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const xcb_format_next(&iterator); } - return 0; + qWarning() << "XCB failed to find an xcb_format_t for depth:" << depth; + return nullptr; } void QXcbConnection::sync() diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 999dc0630c..92825acb6d 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -409,6 +409,15 @@ public: const xcb_setup_t *setup() const { return m_setup; } const xcb_format_t *formatForDepth(uint8_t depth) const; + bool imageNeedsEndianSwap() const + { +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + return m_setup->image_byte_order != XCB_IMAGE_ORDER_MSB_FIRST; +#else + return m_setup->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST; +#endif + } + QXcbKeyboard *keyboard() const { return m_keyboard; } #ifndef QT_NO_CLIPBOARD @@ -476,8 +485,9 @@ public: xcb_window_t getSelectionOwner(xcb_atom_t atom) const; xcb_window_t getQtSelectionOwner(); - void setButtonState(Qt::MouseButton button, bool down) { m_buttonState.setFlag(button, down); } + void setButtonState(Qt::MouseButton button, bool down); Qt::MouseButtons buttonState() const { return m_buttonState; } + Qt::MouseButton button() const { return m_button; } Qt::MouseButton translateMouseButton(xcb_button_t s); QXcbWindow *focusWindow() const { return m_focusWindow; } @@ -691,6 +701,7 @@ private: bool has_render_extension = false; Qt::MouseButtons m_buttonState = 0; + Qt::MouseButton m_button = Qt::NoButton; QXcbWindow *m_focusWindow = nullptr; QXcbWindow *m_mouseGrabber = nullptr; @@ -701,6 +712,7 @@ private: QXcbSystemTrayTracker *m_systemTrayTracker = nullptr; QXcbGlIntegration *m_glIntegration = nullptr; bool m_xiGrab = false; + QVector<int> m_xiMasterPointerIds; xcb_window_t m_qtSelectionOwner = 0; @@ -746,16 +758,18 @@ private: #define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection +struct QStdFreeDeleter { + void operator()(void *p) const Q_DECL_NOTHROW { return std::free(p); } +}; + #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 \ + std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \ + call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr) \ ) #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 \ + std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \ + call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call##_unchecked(__VA_ARGS__), nullptr) \ ) template <typename T> diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 335ec344d5..ba6481082a 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -52,12 +52,6 @@ void QXcbConnection::initializeXInput2() { - // TODO Qt 6 (or perhaps earlier): remove these redundant env variables - if (qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT")) - 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; @@ -325,12 +319,18 @@ void QXcbConnection::xi2SetupDevices() Display *xDisplay = static_cast<Display *>(m_xlib_display); int deviceCount = 0; XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount); + m_xiMasterPointerIds.clear(); for (int i = 0; i < deviceCount; ++i) { - // Only non-master pointing devices are relevant here. - if (devices[i].use != XISlavePointer) + XIDeviceInfo deviceInfo = devices[i]; + if (deviceInfo.use == XIMasterPointer) { + m_xiMasterPointerIds.append(deviceInfo.deviceid); continue; - xi2SetupDevice(&devices[i], false); + } + if (deviceInfo.use == XISlavePointer) // only slave pointer devices are relevant here + xi2SetupDevice(&deviceInfo, false); } + if (m_xiMasterPointerIds.size() > 1) + qCDebug(lcQpaXInputDevices) << "multi-pointer X detected"; XIFreeDeviceInfo(devices); } @@ -849,62 +849,54 @@ bool QXcbConnection::startSystemResizeForTouchBegin(xcb_window_t window, const Q bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab) { - if (grab && !canGrab()) - return false; - - int num_devices = 0; Display *xDisplay = static_cast<Display *>(xlib_display()); - XIDeviceInfo *info = XIQueryDevice(xDisplay, XIAllMasterDevices, &num_devices); - if (!info) - return false; - - XIEventMask evmask; - unsigned char mask[XIMaskLen(XI_LASTEVENT)]; - evmask.mask = mask; - evmask.mask_len = sizeof(mask); - memset(mask, 0, sizeof(mask)); - evmask.deviceid = XIAllMasterDevices; - - XISetMask(mask, XI_ButtonPress); - XISetMask(mask, XI_ButtonRelease); - XISetMask(mask, XI_Motion); - XISetMask(mask, XI_Enter); - XISetMask(mask, XI_Leave); - XISetMask(mask, XI_TouchBegin); - XISetMask(mask, XI_TouchUpdate); - XISetMask(mask, XI_TouchEnd); - - bool grabbed = true; - for (int i = 0; i < num_devices; i++) { - int id = info[i].deviceid, n = 0; - XIDeviceInfo *deviceInfo = XIQueryDevice(xDisplay, id, &n); - if (deviceInfo) { - const bool grabbable = deviceInfo->use != XIMasterKeyboard; - XIFreeDeviceInfo(deviceInfo); - if (!grabbable) - continue; - } - if (!grab) { - Status result = XIUngrabDevice(xDisplay, id, CurrentTime); - if (result != Success) { - grabbed = false; - qCDebug(lcQpaXInput, "XInput 2.2: failed to ungrab events for device %d (result %d)", id, result); - } - } else { - Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None, XIGrabModeAsync, - XIGrabModeAsync, False, &evmask); + bool ok = false; + + if (grab) { // grab + XIEventMask evmask; + unsigned char mask[XIMaskLen(XI_LASTEVENT)]; + evmask.mask = mask; + evmask.mask_len = sizeof(mask); + memset(mask, 0, sizeof(mask)); + XISetMask(mask, XI_ButtonPress); + XISetMask(mask, XI_ButtonRelease); + XISetMask(mask, XI_Motion); + XISetMask(mask, XI_Enter); + XISetMask(mask, XI_Leave); + XISetMask(mask, XI_TouchBegin); + XISetMask(mask, XI_TouchUpdate); + XISetMask(mask, XI_TouchEnd); + + for (int id : m_xiMasterPointerIds) { + evmask.deviceid = id; + Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None, + XIGrabModeAsync, XIGrabModeAsync, False, &evmask); if (result != Success) { - grabbed = false; - qCDebug(lcQpaXInput, "XInput 2.2: failed to grab events for device %d on window %x (result %d)", id, w, result); + qCDebug(lcQpaXInput, "failed to grab events for device %d on window %x" + "(result %d)", id, w, result); + } else { + // Managed to grab at least one of master pointers, that should be enough + // to properly dismiss windows that rely on mouse grabbing. + ok = true; } } + } else { // ungrab + for (int id : m_xiMasterPointerIds) { + Status result = XIUngrabDevice(xDisplay, id, CurrentTime); + if (result != Success) + qCDebug(lcQpaXInput, "XIUngrabDevice failed - id: %d (result %d)", id, result); + } + // XIUngrabDevice does not seem to wait for a reply from X server (similar to + // xcb_ungrab_pointer). Ungrabbing won't fail, unless NoSuchExtension error + // has occurred due to a programming error somewhere else in the stack. That + // would mean that things will crash soon anyway. + ok = true; } - XIFreeDeviceInfo(info); - - m_xiGrab = grabbed; + if (ok) + m_xiGrab = grab; - return grabbed; + return ok; } void QXcbConnection::xi2HandleHierarchyEvent(void *event) @@ -1342,7 +1334,7 @@ QXcbConnection::TabletData *QXcbConnection::tabletDataForDevice(int id) if (m_tabletData.at(i).deviceId == id) return &m_tabletData[i]; } - return Q_NULLPTR; + return nullptr; } #endif // QT_CONFIG(tabletevent) diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index da63360333..34b7d0d236 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -255,29 +255,29 @@ static const uint8_t * const cursor_bits20[] = { forbidden_bits, forbiddenm_bits }; -static const char * const cursorNames[] = { - "left_ptr", - "up_arrow", - "cross", - "wait", - "ibeam", - "size_ver", - "size_hor", - "size_bdiag", - "size_fdiag", - "size_all", - "blank", - "split_v", - "split_h", - "pointing_hand", - "forbidden", - "whats_this", - "left_ptr_watch", - "openhand", - "closedhand", - "copy", - "move", - "link" +static const std::vector<const char *> cursorNames[] = { + { "left_ptr", "default", "top_left_arrow", "left_arrow" }, + { "up_arrow" }, + { "cross" }, + { "wait", "watch", "0426c94ea35c87780ff01dc239897213" }, + { "ibeam", "text", "xterm" }, + { "size_ver", "ns-resize", "v_double_arrow", "00008160000006810000408080010102" }, + { "size_hor", "ew-resize", "h_double_arrow", "028006030e0e7ebffc7f7070c0600140" }, + { "size_bdiag", "nesw-resize", "50585d75b494802d0151028115016902", "fcf1c3c7cd4491d801f1e1c78f100000" }, + { "size_fdiag", "nwse-resize", "38c5dff7c7b8962045400281044508d2", "c7088f0f3e6c8088236ef8e1e3e70000" }, + { "size_all" }, + { "blank" }, + { "split_v", "row-resize", "sb_v_double_arrow", "2870a09082c103050810ffdffffe0204", "c07385c7190e701020ff7ffffd08103c" }, + { "split_h", "col-resize", "sb_h_double_arrow", "043a9f68147c53184671403ffa811cc5", "14fef782d02440884392942c11205230" }, + { "pointing_hand", "pointer", "hand1", "e29285e634086352946a0e7090d73106" }, + { "forbidden", "not-allowed", "crossed_circle", "circle", "03b6e0fcb3499374a867c041f52298f0" }, + { "whats_this", "help", "question_arrow", "5c6cd98b3f3ebcb1f9c7f1c204630408", "d9ce0ab605698f320427677b458ad60b" }, + { "left_ptr_watch", "half-busy", "progress", "00000000000000020006000e7e9ffc3f", "08e8e1c95fe2fc01f976f1e063a24ccd" }, + { "openhand", "fleur", "5aca4d189052212118709018842178c0", "9d800788f1b08800ae810202380a0822" }, + { "closedhand", "grabbing", "208530c400c041818281048008011002" }, + { "dnd-copy", "copy" }, + { "dnd-move", "move" }, + { "dnd-link", "link" } }; QXcbCursorCacheKey::QXcbCursorCacheKey(const QCursor &c) @@ -535,22 +535,13 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) xcb_cursor_t cursor = XCB_NONE; if (!ptrXcursorLibraryLoadCursor || !dpy) return cursor; - switch (cshape) { - case Qt::DragCopyCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); - break; - case Qt::DragMoveCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); - break; - case Qt::DragLinkCursor: - cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); - break; - default: - break; - } - if (!cursor) { - cursor = ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); + + for (const char *cursorName: cursorNames[cshape]) { + cursor = ptrXcursorLibraryLoadCursor(dpy, cursorName); + if (cursor != XCB_NONE) + break; } + return cursor; } #endif // QT_CONFIG(xcb_xlib) / QT_CONFIG(library) @@ -565,7 +556,6 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) #if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) if (cshape >= 0 && cshape <= Qt::LastCursor) { void *dpy = connection()->xlib_display(); - // special case for non-standard dnd-* cursors cursor = loadCursor(dpy, cshape); if (!cursor && !m_gtkCursorThemeInitialized && m_screen->xSettings()->initialized()) { QByteArray gtkCursorTheme = m_screen->xSettings()->setting("Gtk/CursorThemeName").toByteArray(); @@ -598,7 +588,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) } if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) { - const char *name = cursorNames[cshape]; + const char *name = cursorNames[cshape].front(); xcb_xfixes_set_cursor_name(conn, cursor, strlen(name), name); } @@ -660,7 +650,7 @@ QPoint QXcbCursor::pos() const void QXcbCursor::setPos(const QPoint &pos) { - QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR; + QXcbVirtualDesktop *virtualDesktop = nullptr; queryPointer(connection(), &virtualDesktop, 0); xcb_warp_pointer(xcb_connection(), XCB_NONE, virtualDesktop->root(), 0, 0, 0, 0, pos.x(), pos.y()); xcb_flush(xcb_connection()); diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index 1e963268ef..8ea0ebf966 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -211,7 +211,7 @@ void QXcbDrag::startDrag() setScreen(current_virtual_desktop->screens().constFirst()->screen()); initiatorWindow = QGuiApplicationPrivate::currentMouseWindow; QBasicDrag::startDrag(); - if (connection()->mouseGrabber() == Q_NULLPTR) + if (connection()->mouseGrabber() == nullptr) shapedPixmapWindow()->setMouseGrabEnabled(true); } @@ -315,7 +315,7 @@ void QXcbDrag::move(const QPoint &globalPos) if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid()) return; - QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR; + QXcbVirtualDesktop *virtualDesktop = nullptr; QPoint cursorPos; QXcbCursor::queryPointer(connection(), &virtualDesktop, &cursorPos); QXcbScreen *screen = virtualDesktop->screenAt(cursorPos); @@ -324,7 +324,7 @@ void QXcbDrag::move(const QPoint &globalPos) if (virtualDesktop != current_virtual_desktop) { setUseCompositing(virtualDesktop->compositingActive()); recreateShapedPixmapWindow(static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos); - if (connection()->mouseGrabber() == Q_NULLPTR) + if (connection()->mouseGrabber() == nullptr) shapedPixmapWindow()->setMouseGrabEnabled(true); current_virtual_desktop = virtualDesktop; diff --git a/src/plugins/platforms/xcb/qxcbimage.cpp b/src/plugins/platforms/xcb/qxcbimage.cpp index 36536e0602..e18a08755b 100644 --- a/src/plugins/platforms/xcb/qxcbimage.cpp +++ b/src/plugins/platforms/xcb/qxcbimage.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qxcbimage.h" +#include <QtCore/QtEndian> #include <QtGui/QColor> #include <QtGui/private/qimage_p.h> #include <QtGui/private/qdrawhelper_p.h> @@ -52,47 +53,108 @@ extern "C" { #undef template #endif +#include "qxcbconnection.h" +#include "qxcbintegration.h" + +namespace { + +QImage::Format imageFormatForMasks(int depth, int bits_per_pixel, int red_mask, int blue_mask) +{ + if (bits_per_pixel == 32) { + switch (depth) { + case 32: + if (red_mask == 0xff0000 && blue_mask == 0xff) + return QImage::Format_ARGB32_Premultiplied; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + if (red_mask == 0xff && blue_mask == 0xff0000) + return QImage::Format_RGBA8888_Premultiplied; +#else + if (red_mask == 0xff000000 && blue_mask == 0xff00) + return QImage::Format_RGBA8888_Premultiplied; +#endif + if (red_mask == 0x3ff && blue_mask == 0x3ff00000) + return QImage::Format_A2BGR30_Premultiplied; + if (red_mask == 0x3ff00000 && blue_mask == 0x3ff) + return QImage::Format_A2RGB30_Premultiplied; + break; + case 30: + if (red_mask == 0x3ff && blue_mask == 0x3ff00000) + return QImage::Format_BGR30; + if (blue_mask == 0x3ff && red_mask == 0x3ff00000) + return QImage::Format_RGB30; + break; + case 24: + if (red_mask == 0xff0000 && blue_mask == 0xff) + return QImage::Format_RGB32; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + if (red_mask == 0xff && blue_mask == 0xff0000) + return QImage::Format_RGBX8888; +#else + if (red_mask == 0xff000000 && blue_mask == 0xff00) + return QImage::Format_RGBX8888; +#endif + break; + } + } else if (bits_per_pixel == 16) { + if (depth == 16 && red_mask == 0xf800 && blue_mask == 0x1f) + return QImage::Format_RGB16; + if (depth == 15 && red_mask == 0x7c00 && blue_mask == 0x1f) + return QImage::Format_RGB555; + } + return QImage::Format_Invalid; +} + +} // namespace + QT_BEGIN_NAMESPACE -QImage::Format qt_xcb_imageFormatForVisual(QXcbConnection *connection, uint8_t depth, - const xcb_visualtype_t *visual) +bool qt_xcb_imageFormatForVisual(QXcbConnection *connection, uint8_t depth, const xcb_visualtype_t *visual, + QImage::Format *imageFormat, bool *needsRgbSwap) { + Q_ASSERT(connection && visual && imageFormat); + + if (needsRgbSwap) + *needsRgbSwap = false; + *imageFormat = QImage::Format_Invalid; + + if (depth == 8) { + if (visual->_class == XCB_VISUAL_CLASS_GRAY_SCALE) { + *imageFormat = QImage::Format_Grayscale8; + return true; + } +#if QT_CONFIG(xcb_native_painting) + if (QXcbIntegration::instance() && QXcbIntegration::instance()->nativePaintingEnabled()) { + *imageFormat = QImage::Format_Indexed8; + return true; + } +#endif + return false; + } + const xcb_format_t *format = connection->formatForDepth(depth); + if (!format) + return false; + + const bool connectionEndianSwap = connection->imageNeedsEndianSwap(); + // We swap the masks and see if we can recognize it as a host format + const quint32 red_mask = connectionEndianSwap ? qbswap(visual->red_mask) : visual->red_mask; + const quint32 blue_mask = connectionEndianSwap ? qbswap(visual->blue_mask) : visual->blue_mask; - if (!visual || !format) - return QImage::Format_Invalid; - - if (depth == 32 && format->bits_per_pixel == 32 && visual->red_mask == 0xff0000 - && visual->green_mask == 0xff00 && visual->blue_mask == 0xff) - return QImage::Format_ARGB32_Premultiplied; - - if (depth == 30 && format->bits_per_pixel == 32 && visual->red_mask == 0x3ff - && visual->green_mask == 0x0ffc00 && visual->blue_mask == 0x3ff00000) - return QImage::Format_BGR30; - - if (depth == 30 && format->bits_per_pixel == 32 && visual->blue_mask == 0x3ff - && visual->green_mask == 0x0ffc00 && visual->red_mask == 0x3ff00000) - return QImage::Format_RGB30; - - if (depth == 24 && format->bits_per_pixel == 32 && visual->red_mask == 0xff0000 - && visual->green_mask == 0xff00 && visual->blue_mask == 0xff) - return QImage::Format_RGB32; - - if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { - if (depth == 24 && format->bits_per_pixel == 32 && visual->blue_mask == 0xff0000 - && visual->green_mask == 0xff00 && visual->red_mask == 0xff) - return QImage::Format_RGBX8888; - } else { - if (depth == 24 && format->bits_per_pixel == 32 && visual->blue_mask == 0xff00 - && visual->green_mask == 0xff0000 && visual->red_mask == 0xff000000) - return QImage::Format_RGBX8888; + *imageFormat = imageFormatForMasks(depth, format->bits_per_pixel, red_mask, blue_mask); + if (*imageFormat != QImage::Format_Invalid) + return true; + + if (needsRgbSwap) { + *imageFormat = imageFormatForMasks(depth, format->bits_per_pixel, blue_mask, red_mask); + if (*imageFormat != QImage::Format_Invalid) { + *needsRgbSwap = true; + return true; + } } - if (depth == 16 && format->bits_per_pixel == 16 && visual->red_mask == 0xf800 - && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f) - return QImage::Format_RGB16; + qWarning("Unsupported screen format: depth: %d, bits_per_pixel: %d, red_mask: %x, blue_mask: %x", depth, format->bits_per_pixel, red_mask, blue_mask); - return QImage::Format_Invalid; + return false; } QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap, @@ -112,44 +174,14 @@ QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap QPixmap result; - QImage::Format format = qt_xcb_imageFormatForVisual(connection, depth, visual); - if (format != QImage::Format_Invalid) { + QImage::Format format; + bool needsRgbSwap; + if (qt_xcb_imageFormatForVisual(connection, depth, visual, &format, &needsRgbSwap)) { uint32_t bytes_per_line = length / height; QImage image(const_cast<uint8_t *>(data), width, height, bytes_per_line, format); - uint8_t image_byte_order = connection->setup()->image_byte_order; - - // we may have to swap the byte order - if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && image_byte_order == XCB_IMAGE_ORDER_MSB_FIRST) - || (QSysInfo::ByteOrder == QSysInfo::BigEndian && image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) - { - for (int i=0; i < image.height(); i++) { - switch (format) { - case QImage::Format_RGB16: { - ushort *p = (ushort*)image.scanLine(i); - ushort *end = p + image.width(); - while (p < end) { - *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff); - p++; - } - break; - } - case QImage::Format_RGB32: - case QImage::Format_ARGB32_Premultiplied: - case QImage::Format_RGBX8888: { - 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++; - } - break; - } - default: - Q_ASSERT(false); - } - } - } + + if (needsRgbSwap) + image = std::move(image).rgbSwapped(); // fix-up alpha channel if (format == QImage::Format_RGB32 || format == QImage::Format_RGBX8888) { diff --git a/src/plugins/platforms/xcb/qxcbimage.h b/src/plugins/platforms/xcb/qxcbimage.h index a9071a45de..d718089ec2 100644 --- a/src/plugins/platforms/xcb/qxcbimage.h +++ b/src/plugins/platforms/xcb/qxcbimage.h @@ -48,8 +48,8 @@ QT_BEGIN_NAMESPACE -QImage::Format qt_xcb_imageFormatForVisual(QXcbConnection *connection, - uint8_t depth, const xcb_visualtype_t *visual); +bool qt_xcb_imageFormatForVisual(QXcbConnection *connection, uint8_t depth, const xcb_visualtype_t *visual, + QImage::Format *imageFormat, bool *needsRgbSwap = nullptr); QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap, int width, int height, int depth, const xcb_visualtype_t *visual); diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 72d31060db..6afa7bc934 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -121,7 +121,7 @@ static bool runningUnderDebugger() #endif } -QXcbIntegration *QXcbIntegration::m_instance = Q_NULLPTR; +QXcbIntegration *QXcbIntegration::m_instance = nullptr; QXcbIntegration::QXcbIntegration(const QStringList ¶meters, int &argc, char **argv) : m_services(new QGenericUnixServices) @@ -222,7 +222,7 @@ QXcbIntegration::QXcbIntegration(const QStringList ¶meters, int &argc, char QXcbIntegration::~QXcbIntegration() { qDeleteAll(m_connections); - m_instance = Q_NULLPTR; + m_instance = nullptr; } QPlatformPixmap *QXcbIntegration::createPlatformPixmap(QPlatformPixmap::PixelType type) const @@ -274,7 +274,7 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont QXcbGlIntegration *glIntegration = screen->connection()->glIntegration(); if (!glIntegration) { qWarning("QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enabled"); - return Q_NULLPTR; + return nullptr; } return glIntegration->createPlatformOpenGLContext(context); } @@ -296,7 +296,7 @@ QPlatformOffscreenSurface *QXcbIntegration::createPlatformOffscreenSurface(QOffs QXcbGlIntegration *glIntegration = screen->connection()->glIntegration(); if (!glIntegration) { qWarning("QXcbIntegration: Cannot create platform offscreen surface, neither GLX nor EGL are enabled"); - return Q_NULLPTR; + return nullptr; } return glIntegration->createPlatformOffscreenSurface(surface); } diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index e24bd07b6f..136faec855 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -59,6 +59,12 @@ #undef KeyRelease #endif +#if QT_CONFIG(xcb_xlib) +#include <X11/Xutil.h> +#undef KeyPress +#undef KeyRelease +#endif + #ifndef XK_ISO_Left_Tab #define XK_ISO_Left_Tab 0xFE20 #endif @@ -678,9 +684,303 @@ void QXcbKeyboard::printKeymapError(const char *error) const "directory contains recent enough contents, to update please see http://cgit.freedesktop.org/xkeyboard-config/ ."); } +#if QT_CONFIG(xcb_xlib) +/* Look at a pair of unshifted and shifted key symbols. + * If the 'unshifted' symbol is uppercase and there is no shifted symbol, + * return the matching lowercase symbol; otherwise return 0. + * The caller can then use the previously 'unshifted' symbol as the new + * 'shifted' (uppercase) symbol and the symbol returned by the function + * as the new 'unshifted' (lowercase) symbol.) */ +static xcb_keysym_t getUnshiftedXKey(xcb_keysym_t unshifted, xcb_keysym_t shifted) +{ + if (shifted != XKB_KEY_NoSymbol) // Has a shifted symbol + return 0; + + KeySym xlower; + KeySym xupper; + /* libxkbcommon >= 0.8.0 will have public API functions providing + * functionality equivalent to XConvertCase(), use these once the + * minimal libxkbcommon version is high enough. After that the + * xcb-xlib dependency can be removed */ + XConvertCase(static_cast<KeySym>(unshifted), &xlower, &xupper); + + if (xlower != xupper // Check if symbol is cased + && unshifted == static_cast<xcb_keysym_t>(xupper)) { // Unshifted must be upper case + return static_cast<xcb_keysym_t>(xlower); + } + return 0; +} + +static QByteArray symbolsGroupString(const xcb_keysym_t *symbols, int count) +{ + // Don't output trailing NoSymbols + while (count > 0 && symbols[count - 1] == XKB_KEY_NoSymbol) + count--; + + QByteArray groupString; + for (int symIndex = 0; symIndex < count; symIndex++) { + xcb_keysym_t sym = symbols[symIndex]; + char symString[64]; + if (sym == XKB_KEY_NoSymbol) + strcpy(symString, "NoSymbol"); + else + xkb_keysym_get_name(sym, symString, sizeof(symString)); + + if (!groupString.isEmpty()) + groupString += ", "; + groupString += symString; + } + return groupString; +} + +struct xkb_keymap *QXcbKeyboard::keymapFromCore() +{ + /* Construct an XKB keymap string from information queried from + * the X server */ + QByteArray keymap; + keymap += "xkb_keymap {\n"; + + const xcb_keycode_t minKeycode = connection()->setup()->min_keycode; + const xcb_keycode_t maxKeycode = connection()->setup()->max_keycode; + + // Generate symbolic names from keycodes + { + keymap += + "xkb_keycodes \"core\" {\n" + "\tminimum = " + QByteArray::number(minKeycode) + ";\n" + "\tmaximum = " + QByteArray::number(maxKeycode) + ";\n"; + for (int code = minKeycode; code <= maxKeycode; code++) { + auto codeStr = QByteArray::number(code); + keymap += "<K" + codeStr + "> = " + codeStr + ";\n"; + } + /* TODO: indicators? + */ + keymap += "};\n"; // xkb_keycodes + } + + /* Set up default types (xkbcommon automatically assigns these to + * symbols, but doesn't have shift info) */ + keymap += + "xkb_types \"core\" {\n" + "virtual_modifiers NumLock,Alt,LevelThree;\n" + "type \"ONE_LEVEL\" {\n" + "modifiers= none;\n" + "level_name[Level1] = \"Any\";\n" + "};\n" + "type \"TWO_LEVEL\" {\n" + "modifiers= Shift;\n" + "map[Shift]= Level2;\n" + "level_name[Level1] = \"Base\";\n" + "level_name[Level2] = \"Shift\";\n" + "};\n" + "type \"ALPHABETIC\" {\n" + "modifiers= Shift+Lock;\n" + "map[Shift]= Level2;\n" + "map[Lock]= Level2;\n" + "level_name[Level1] = \"Base\";\n" + "level_name[Level2] = \"Caps\";\n" + "};\n" + "type \"KEYPAD\" {\n" + "modifiers= Shift+NumLock;\n" + "map[Shift]= Level2;\n" + "map[NumLock]= Level2;\n" + "level_name[Level1] = \"Base\";\n" + "level_name[Level2] = \"Number\";\n" + "};\n" + "type \"FOUR_LEVEL\" {\n" + "modifiers= Shift+LevelThree;\n" + "map[Shift]= Level2;\n" + "map[LevelThree]= Level3;\n" + "map[Shift+LevelThree]= Level4;\n" + "level_name[Level1] = \"Base\";\n" + "level_name[Level2] = \"Shift\";\n" + "level_name[Level3] = \"Alt Base\";\n" + "level_name[Level4] = \"Shift Alt\";\n" + "};\n" + "type \"FOUR_LEVEL_ALPHABETIC\" {\n" + "modifiers= Shift+Lock+LevelThree;\n" + "map[Shift]= Level2;\n" + "map[Lock]= Level2;\n" + "map[LevelThree]= Level3;\n" + "map[Shift+LevelThree]= Level4;\n" + "map[Lock+LevelThree]= Level4;\n" + "map[Shift+Lock+LevelThree]= Level3;\n" + "level_name[Level1] = \"Base\";\n" + "level_name[Level2] = \"Shift\";\n" + "level_name[Level3] = \"Alt Base\";\n" + "level_name[Level4] = \"Shift Alt\";\n" + "};\n" + "type \"FOUR_LEVEL_SEMIALPHABETIC\" {\n" + "modifiers= Shift+Lock+LevelThree;\n" + "map[Shift]= Level2;\n" + "map[Lock]= Level2;\n" + "map[LevelThree]= Level3;\n" + "map[Shift+LevelThree]= Level4;\n" + "map[Lock+LevelThree]= Level3;\n" + "preserve[Lock+LevelThree]= Lock;\n" + "map[Shift+Lock+LevelThree]= Level4;\n" + "preserve[Shift+Lock+LevelThree]= Lock;\n" + "level_name[Level1] = \"Base\";\n" + "level_name[Level2] = \"Shift\";\n" + "level_name[Level3] = \"Alt Base\";\n" + "level_name[Level4] = \"Shift Alt\";\n" + "};\n" + "type \"FOUR_LEVEL_KEYPAD\" {\n" + "modifiers= Shift+NumLock+LevelThree;\n" + "map[Shift]= Level2;\n" + "map[NumLock]= Level2;\n" + "map[LevelThree]= Level3;\n" + "map[Shift+LevelThree]= Level4;\n" + "map[NumLock+LevelThree]= Level4;\n" + "map[Shift+NumLock+LevelThree]= Level3;\n" + "level_name[Level1] = \"Base\";\n" + "level_name[Level2] = \"Number\";\n" + "level_name[Level3] = \"Alt Base\";\n" + "level_name[Level4] = \"Alt Number\";\n" + "};\n" + "};\n"; // xkb_types + + // Generate mapping between symbolic names and keysyms + { + QVector<xcb_keysym_t> xkeymap; + int keysymsPerKeycode = 0; + { + int keycodeCount = maxKeycode - minKeycode + 1; + if (auto keymapReply = Q_XCB_REPLY(xcb_get_keyboard_mapping, xcb_connection(), + minKeycode, keycodeCount)) { + keysymsPerKeycode = keymapReply->keysyms_per_keycode; + int numSyms = keycodeCount * keysymsPerKeycode; + auto keymapPtr = xcb_get_keyboard_mapping_keysyms(keymapReply.get()); + xkeymap.resize(numSyms); + for (int i = 0; i < numSyms; i++) + xkeymap[i] = keymapPtr[i]; + } + } + if (xkeymap.isEmpty()) + return nullptr; + + KeysymModifierMap keysymMods(keysymsToModifiers()); + static const char *const builtinModifiers[] = + { "Shift", "Lock", "Control", "Mod1", "Mod2", "Mod3", "Mod4", "Mod5" }; + + /* Level 3 symbols (e.g. AltGr+something) seem to come in two flavors: + * - as a proper level 3 in group 1, at least on recent X.org versions + * - 'disguised' as group 2, on 'legacy' X servers + * In the 2nd case, remap group 2 to level 3, that seems to work better + * in practice */ + bool mapGroup2ToLevel3 = keysymsPerKeycode < 5; + + keymap += "xkb_symbols \"core\" {\n"; + for (int code = minKeycode; code <= maxKeycode; code++) { + auto codeMap = xkeymap.constData() + (code - minKeycode) * keysymsPerKeycode; + + const int maxGroup1 = 4; // We only support 4 shift states anyway + const int maxGroup2 = 2; // Only 3rd and 4th keysym are group 2 + xcb_keysym_t symbolsGroup1[maxGroup1]; + xcb_keysym_t symbolsGroup2[maxGroup2]; + for (int i = 0; i < maxGroup1 + maxGroup2; i++) { + xcb_keysym_t sym = i < keysymsPerKeycode ? codeMap[i] : XKB_KEY_NoSymbol; + if (mapGroup2ToLevel3) { + // Merge into single group + if (i < maxGroup1) + symbolsGroup1[i] = sym; + } else { + // Preserve groups + if (i < 2) + symbolsGroup1[i] = sym; + else if (i < 4) + symbolsGroup2[i - 2] = sym; + else + symbolsGroup1[i - 2] = sym; + } + } + + /* Fix symbols so the unshifted and shifted symbols have + * lower resp. upper case */ + if (auto lowered = getUnshiftedXKey(symbolsGroup1[0], symbolsGroup1[1])) { + symbolsGroup1[1] = symbolsGroup1[0]; + symbolsGroup1[0] = lowered; + } + if (auto lowered = getUnshiftedXKey(symbolsGroup2[0], symbolsGroup2[1])) { + symbolsGroup2[1] = symbolsGroup2[0]; + symbolsGroup2[0] = lowered; + } + + QByteArray groupStr1 = symbolsGroupString(symbolsGroup1, maxGroup1); + if (groupStr1.isEmpty()) + continue; + + keymap += "key <K" + QByteArray::number(code) + "> { "; + keymap += "symbols[Group1] = [ " + groupStr1 + " ]"; + QByteArray groupStr2 = symbolsGroupString(symbolsGroup2, maxGroup2); + if (!groupStr2.isEmpty()) + keymap += ", symbols[Group2] = [ " + groupStr2 + " ]"; + + // See if this key code is for a modifier + xcb_keysym_t modifierSym = XKB_KEY_NoSymbol; + for (int symIndex = 0; symIndex < keysymsPerKeycode; symIndex++) { + xcb_keysym_t sym = codeMap[symIndex]; + + if (sym == XKB_KEY_Alt_L + || sym == XKB_KEY_Meta_L + || sym == XKB_KEY_Mode_switch + || sym == XKB_KEY_Super_L + || sym == XKB_KEY_Super_R + || sym == XKB_KEY_Hyper_L + || sym == XKB_KEY_Hyper_R) { + modifierSym = sym; + break; + } + } + + // AltGr + if (modifierSym == XKB_KEY_Mode_switch) + keymap += ", virtualMods=LevelThree"; + keymap += " };\n"; // key + + // Generate modifier mappings + int modNum = keysymMods.value(modifierSym, -1); + if (modNum != -1) { + // Here modNum is always < 8 (see keysymsToModifiers()) + keymap += QByteArray("modifier_map ") + builtinModifiers[modNum] + + " { <K" + QByteArray::number(code) + "> };\n"; + } + } + // TODO: indicators? + keymap += "};\n"; // xkb_symbols + } + + // We need an "Alt" modifier, provide via the xkb_compatibility section + keymap += + "xkb_compatibility \"core\" {\n" + "virtual_modifiers NumLock,Alt,LevelThree;\n" + "interpret Alt_L+AnyOf(all) {\n" + "virtualModifier= Alt;\n" + "action= SetMods(modifiers=modMapMods,clearLocks);\n" + "};\n" + "interpret Alt_R+AnyOf(all) {\n" + "virtualModifier= Alt;\n" + "action= SetMods(modifiers=modMapMods,clearLocks);\n" + "};\n" + "};\n"; + + /* TODO: There is an issue with modifier state not being handled + * correctly if using Xming with XKEYBOARD disabled. */ + + keymap += "};\n"; // xkb_keymap + + return xkb_keymap_new_from_buffer(xkb_context, + keymap.constData(), + keymap.size(), + XKB_KEYMAP_FORMAT_TEXT_V1, + static_cast<xkb_keymap_compile_flags>(0)); +} +#endif + void QXcbKeyboard::updateKeymap() { m_config = true; + m_keymap_is_core = false; // set xkb context object if (!xkb_context) { if (qEnvironmentVariableIsSet("QT_XKB_CONFIG_ROOT")) { @@ -714,9 +1014,23 @@ void QXcbKeyboard::updateKeymap() } #endif if (!xkb_keymap) { - // Compile a keymap from RMLVO (rules, models, layouts, variants and options) names + // Read xkb RMLVO (rules, models, layouts, variants and options) names readXKBConfig(); - xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0); +#if QT_CONFIG(xcb_xlib) + bool rmlvo_is_incomplete = !xkb_names.rules || !(*xkb_names.rules) + || !xkb_names.model || !(*xkb_names.model) + || !xkb_names.layout || !(*xkb_names.layout); + if (rmlvo_is_incomplete) { + // Try to build xkb map from core mapping information + xkb_keymap = keymapFromCore(); + m_keymap_is_core = xkb_keymap != 0; + } +#endif + if (!xkb_keymap) { + // Compile a keymap from RMLVO + xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, + static_cast<xkb_keymap_compile_flags> (0)); + } if (!xkb_keymap) { // last fallback is to used hard-coded keymap name, see DEFAULT_XKB_* in xkbcommon.pri qWarning() << "Qt: Could not determine keyboard configuration data" @@ -908,9 +1222,11 @@ xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const // If user layouts don't contain any layout that results in a latin key, we query a // key from "US" layout, this allows for latin-key-based shorcuts to work even when // users have only one (non-latin) layout set. + // But don't do this if using keymap obtained through the core protocol, as the key + // codes may not match up with those expected by the XKB keymap. xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED); xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED); - if (sym == XKB_KEY_NoSymbol && !m_hasLatinLayout) { + if (sym == XKB_KEY_NoSymbol && !m_hasLatinLayout && !m_keymap_is_core) { if (!latin_keymap) { const struct xkb_rule_names names = { xkb_names.rules, xkb_names.model, "us", 0, 0 }; latin_keymap = xkb_keymap_new_from_names(xkb_context, &names, (xkb_keymap_compile_flags)0); @@ -1289,20 +1605,56 @@ void QXcbKeyboard::updateVModToRModMapping() #endif } +// Small helper: set modifier bit, if modifier position is valid +static inline void applyModifier(uint *mask, int modifierBit) +{ + if (modifierBit >= 0 && modifierBit < 8) + *mask |= 1 << modifierBit; +} + void QXcbKeyboard::updateModifiers() { + memset(&rmod_masks, 0, sizeof(rmod_masks)); + + // Compute X modifier bits for Qt modifiers + KeysymModifierMap keysymMods(keysymsToModifiers()); + applyModifier(&rmod_masks.alt, keysymMods.value(XK_Alt_L, -1)); + applyModifier(&rmod_masks.alt, keysymMods.value(XK_Alt_R, -1)); + applyModifier(&rmod_masks.meta, keysymMods.value(XK_Meta_L, -1)); + applyModifier(&rmod_masks.meta, keysymMods.value(XK_Meta_R, -1)); + applyModifier(&rmod_masks.altgr, keysymMods.value(XK_Mode_switch, -1)); + applyModifier(&rmod_masks.super, keysymMods.value(XK_Super_L, -1)); + applyModifier(&rmod_masks.super, keysymMods.value(XK_Super_R, -1)); + applyModifier(&rmod_masks.hyper, keysymMods.value(XK_Hyper_L, -1)); + applyModifier(&rmod_masks.hyper, keysymMods.value(XK_Hyper_R, -1)); + + resolveMaskConflicts(); +} + +// Small helper: check if an array of xcb_keycode_t contains a certain code +static inline bool keycodes_contains(xcb_keycode_t *codes, xcb_keycode_t which) +{ + while (*codes != XCB_NO_SYMBOL) { + if (*codes == which) return true; + codes++; + } + return false; +} + +QXcbKeyboard::KeysymModifierMap QXcbKeyboard::keysymsToModifiers() +{ // The core protocol does not provide a convenient way to determine the mapping // of modifier bits. Clients must retrieve and search the modifier map to determine // the keycodes bound to each modifier, and then retrieve and search the keyboard // mapping to determine the keysyms bound to the keycodes. They must repeat this // process for all modifiers whenever any part of the modifier mapping is changed. - memset(&rmod_masks, 0, sizeof(rmod_masks)); - xcb_connection_t *conn = xcb_connection(); - auto modMapReply = Q_XCB_REPLY(xcb_get_modifier_mapping, conn); + KeysymModifierMap map; + + auto modMapReply = Q_XCB_REPLY(xcb_get_modifier_mapping, xcb_connection()); if (!modMapReply) { qWarning("Qt: failed to get modifier mapping"); - return; + return map; } // for Alt and Meta L and R are the same @@ -1318,34 +1670,62 @@ void QXcbKeyboard::updateModifiers() modKeyCodes[i] = xcb_key_symbols_get_keycode(m_key_symbols, symbols[i]); 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) { - uint mask = 1 << bit; - for (int x = 0; x < w; ++x) { - xcb_keycode_t keyCode = modMap[x + bit * w]; - xcb_keycode_t *itk = modKeyCodes[i]; - while (itk && *itk != XCB_NO_SYMBOL) - if (*itk++ == keyCode) { - uint sym = symbols[i]; - if ((sym == XK_Alt_L || sym == XK_Alt_R)) - rmod_masks.alt = mask; - if ((sym == XK_Meta_L || sym == XK_Meta_R)) - rmod_masks.meta = mask; - if (sym == XK_Mode_switch) - rmod_masks.altgr = mask; - if ((sym == XK_Super_L) || (sym == XK_Super_R)) - rmod_masks.super = mask; - if ((sym == XK_Hyper_L) || (sym == XK_Hyper_R)) - rmod_masks.hyper = mask; - } + const int modMapLength = xcb_get_modifier_mapping_keycodes_length(modMapReply.get()); + /* For each modifier of "Shift, Lock, Control, Mod1, Mod2, Mod3, + * Mod4, and Mod5" the modifier map contains keycodes_per_modifier + * key codes that are associated with a modifier. + * + * As an example, take this 'xmodmap' output: + * xmodmap: up to 4 keys per modifier, (keycodes in parentheses): + * + * shift Shift_L (0x32), Shift_R (0x3e) + * lock Caps_Lock (0x42) + * control Control_L (0x25), Control_R (0x69) + * mod1 Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd) + * mod2 Num_Lock (0x4d) + * mod3 + * mod4 Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf) + * mod5 ISO_Level3_Shift (0x5c), Mode_switch (0xcb) + * + * The corresponding raw modifier map would contain keycodes for: + * Shift_L (0x32), Shift_R (0x3e), 0, 0, + * Caps_Lock (0x42), 0, 0, 0, + * Control_L (0x25), Control_R (0x69), 0, 0, + * Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd), 0, + * Num_Lock (0x4d), 0, 0, 0, + * 0,0,0,0, + * Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf), + * ISO_Level3_Shift (0x5c), Mode_switch (0xcb), 0, 0 + */ + + /* Create a map between a modifier keysym (as per the symbols array) + * and the modifier bit it's associated with (if any). + * As modMap contains key codes, search modKeyCodes for a match; + * if one is found we can look up the associated keysym. + * Together with the modifier index this will be used + * to compute a mapping between X modifier bits and Qt's + * modifiers (Alt, Ctrl etc). */ + for (int i = 0; i < modMapLength; i++) { + if (modMap[i] == XCB_NO_SYMBOL) + continue; + // Get key symbol for key code + for (size_t k = 0; k < numSymbols; k++) { + if (modKeyCodes[k] && keycodes_contains(modKeyCodes[k], modMap[i])) { + // Key code is for modifier. Record mapping + xcb_keysym_t sym = symbols[k]; + /* As per modMap layout explanation above, dividing + * by keycodes_per_modifier gives the 'row' in the + * modifier map, which in turn is the modifier bit. */ + map[sym] = i / modMapReply->keycodes_per_modifier; + break; } } } for (size_t i = 0; i < numSymbols; ++i) free(modKeyCodes[i]); - resolveMaskConflicts(); + + return map; } void QXcbKeyboard::resolveMaskConflicts() diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h index 7f1c51fab8..7ee8e9e90d 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -94,8 +94,13 @@ protected: void readXKBConfig(); void clearXKBConfig(); +#if QT_CONFIG(xcb_xlib) + struct xkb_keymap *keymapFromCore(); +#endif // when XKEYBOARD not present on the X server void updateModifiers(); + typedef QMap<xcb_keysym_t, int> KeysymModifierMap; + KeysymModifierMap keysymsToModifiers(); // when XKEYBOARD is present on the X server void updateVModMapping(); void updateVModToRModMapping(); @@ -107,6 +112,7 @@ private: void updateXKBStateFromState(struct xkb_state *kb_state, quint16 state); bool m_config = false; + bool m_keymap_is_core = false; xcb_keycode_t m_autorepeat_code = 0; struct xkb_context *xkb_context = nullptr; diff --git a/src/plugins/platforms/xcb/qxcbmime.cpp b/src/plugins/platforms/xcb/qxcbmime.cpp index 2848446098..58e2e8c0e6 100644 --- a/src/plugins/platforms/xcb/qxcbmime.cpp +++ b/src/plugins/platforms/xcb/qxcbmime.cpp @@ -160,9 +160,10 @@ QVector<xcb_atom_t> QXcbMime::mimeAtomsForFormat(QXcbConnection *connection, con return atoms; } -QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &data, const QString &format, +QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &d, const QString &format, QVariant::Type requestedType, const QByteArray &encoding) { + QByteArray data = d; QString atomName = mimeAtomToString(connection, a); // qDebug() << "mimeConvertDataToFormat" << format << atomName << data; @@ -182,8 +183,11 @@ QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, // special cases for string types if (format == QLatin1String("text/plain")) { - if (a == connection->atom(QXcbAtom::UTF8_STRING)) + if (data.endsWith('\0')) + data.chop(1); + if (a == connection->atom(QXcbAtom::UTF8_STRING)) { return QString::fromUtf8(data); + } if (a == XCB_ATOM_STRING || a == connection->atom(QXcbAtom::TEXT)) return QString::fromLatin1(data); @@ -221,6 +225,9 @@ QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, } } } + // 8 byte encoding, remove a possible 0 at the end + if (data.endsWith('\0')) + data.chop(1); } if (atomName == format) diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index caa9499c45..d989761297 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -101,7 +101,7 @@ QXcbNativeInterface::QXcbNativeInterface() : static inline QXcbSystemTrayTracker *systemTrayTracker(const QScreen *s) { if (!s) - return Q_NULLPTR; + return nullptr; return static_cast<const QXcbScreen *>(s->handle())->connection()->systemTrayTracker(); } @@ -194,7 +194,7 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceStr { if (!screen) { qWarning("nativeResourceForScreen: null screen"); - return Q_NULLPTR; + return nullptr; } QByteArray lowerCaseResource = resourceString.toLower(); @@ -236,7 +236,7 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceStr break; case CompositingEnabled: if (QXcbVirtualDesktop *vd = xcbScreen->virtualDesktop()) - result = vd->compositingActive() ? this : Q_NULLPTR; + result = vd->compositingActive() ? this : nullptr; break; default: break; @@ -294,7 +294,7 @@ void *QXcbNativeInterface::nativeResourceForCursor(const QByteArray &resource, c } } } - return Q_NULLPTR; + return nullptr; } #endif // !QT_NO_CURSOR @@ -323,7 +323,7 @@ QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterface:: QPlatformNativeInterface::NativeResourceForContextFunction func = handlerNativeResourceFunctionForContext(lowerCaseResource); if (func) return func; - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterface::nativeResourceFunctionForScreen(const QByteArray &resource) @@ -390,13 +390,13 @@ QFunctionPointer QXcbNativeInterface::platformFunction(const QByteArray &functio if (function == QXcbScreenFunctions::virtualDesktopNumberIdentifier()) return QFunctionPointer(QXcbScreenFunctions::VirtualDesktopNumber(QXcbScreen::virtualDesktopNumberStatic)); - return Q_NULLPTR; + return nullptr; } void *QXcbNativeInterface::appTime(const QXcbScreen *screen) { if (!screen) - return Q_NULLPTR; + return nullptr; return reinterpret_cast<void *>(quintptr(screen->connection()->time())); } @@ -404,7 +404,7 @@ void *QXcbNativeInterface::appTime(const QXcbScreen *screen) void *QXcbNativeInterface::appUserTime(const QXcbScreen *screen) { if (!screen) - return Q_NULLPTR; + return nullptr; return reinterpret_cast<void *>(quintptr(screen->connection()->netWmUserTime())); } @@ -412,7 +412,7 @@ void *QXcbNativeInterface::appUserTime(const QXcbScreen *screen) void *QXcbNativeInterface::getTimestamp(const QXcbScreen *screen) { if (!screen) - return Q_NULLPTR; + return nullptr; return reinterpret_cast<void *>(quintptr(screen->connection()->getTimestamp())); } @@ -452,7 +452,7 @@ void *QXcbNativeInterface::display() if (defaultConnection) return defaultConnection->xlib_display(); #endif - return Q_NULLPTR; + return nullptr; } void *QXcbNativeInterface::connection() @@ -525,10 +525,10 @@ QXcbScreen *QXcbNativeInterface::qPlatformScreenForWindow(QWindow *window) QXcbScreen *screen; if (window) { QScreen *qs = window->screen(); - screen = static_cast<QXcbScreen *>(qs ? qs->handle() : Q_NULLPTR); + screen = static_cast<QXcbScreen *>(qs ? qs->handle() : nullptr); } else { QScreen *qs = QGuiApplication::primaryScreen(); - screen = static_cast<QXcbScreen *>(qs ? qs->handle() : Q_NULLPTR); + screen = static_cast<QXcbScreen *>(qs ? qs->handle() : nullptr); } return screen; } @@ -537,23 +537,23 @@ void *QXcbNativeInterface::displayForWindow(QWindow *window) { #if QT_CONFIG(xcb_xlib) QXcbScreen *screen = qPlatformScreenForWindow(window); - return screen ? screen->connection()->xlib_display() : Q_NULLPTR; + return screen ? screen->connection()->xlib_display() : nullptr; #else Q_UNUSED(window); - return Q_NULLPTR; + return nullptr; #endif } void *QXcbNativeInterface::connectionForWindow(QWindow *window) { QXcbScreen *screen = qPlatformScreenForWindow(window); - return screen ? screen->xcb_connection() : Q_NULLPTR; + return screen ? screen->xcb_connection() : nullptr; } void *QXcbNativeInterface::screenForWindow(QWindow *window) { QXcbScreen *screen = qPlatformScreenForWindow(window); - return screen ? screen->screen() : Q_NULLPTR; + return screen ? screen->screen() : nullptr; } void QXcbNativeInterface::addHandler(QXcbNativeInterfaceHandler *handler) @@ -575,7 +575,7 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterfa if (result) return result; } - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterface::handlerNativeResourceFunctionForContext(const QByteArray &resource) const @@ -586,7 +586,7 @@ QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterface:: if (result) return result; } - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterface::handlerNativeResourceFunctionForScreen(const QByteArray &resource) const @@ -597,7 +597,7 @@ QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterface::h if (result) return result; } - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterface::handlerNativeResourceFunctionForWindow(const QByteArray &resource) const @@ -608,7 +608,7 @@ QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterface::h if (result) return result; } - return Q_NULLPTR; + return nullptr; } QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterface::handlerNativeResourceFunctionForBackingStore(const QByteArray &resource) const @@ -619,7 +619,7 @@ QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterf if (result) return result; } - return Q_NULLPTR; + return nullptr; } QFunctionPointer QXcbNativeInterface::handlerPlatformFunction(const QByteArray &function) const @@ -630,7 +630,7 @@ QFunctionPointer QXcbNativeInterface::handlerPlatformFunction(const QByteArray & if (func) return func; } - return Q_NULLPTR; + return nullptr; } void *QXcbNativeInterface::handlerNativeResourceForIntegration(const QByteArray &resource) const @@ -638,7 +638,7 @@ void *QXcbNativeInterface::handlerNativeResourceForIntegration(const QByteArray NativeResourceForIntegrationFunction func = handlerNativeResourceFunctionForIntegration(resource); if (func) return func(); - return Q_NULLPTR; + return nullptr; } void *QXcbNativeInterface::handlerNativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) const @@ -646,7 +646,7 @@ void *QXcbNativeInterface::handlerNativeResourceForContext(const QByteArray &res NativeResourceForContextFunction func = handlerNativeResourceFunctionForContext(resource); if (func) return func(context); - return Q_NULLPTR; + return nullptr; } void *QXcbNativeInterface::handlerNativeResourceForScreen(const QByteArray &resource, QScreen *screen) const @@ -654,7 +654,7 @@ void *QXcbNativeInterface::handlerNativeResourceForScreen(const QByteArray &reso NativeResourceForScreenFunction func = handlerNativeResourceFunctionForScreen(resource); if (func) return func(screen); - return Q_NULLPTR; + return nullptr; } void *QXcbNativeInterface::handlerNativeResourceForWindow(const QByteArray &resource, QWindow *window) const @@ -662,7 +662,7 @@ void *QXcbNativeInterface::handlerNativeResourceForWindow(const QByteArray &reso NativeResourceForWindowFunction func = handlerNativeResourceFunctionForWindow(resource); if (func) return func(window); - return Q_NULLPTR; + return nullptr; } void *QXcbNativeInterface::handlerNativeResourceForBackingStore(const QByteArray &resource, QBackingStore *backingStore) const @@ -670,7 +670,7 @@ void *QXcbNativeInterface::handlerNativeResourceForBackingStore(const QByteArray NativeResourceForBackingStoreFunction func = handlerNativeResourceFunctionForBackingStore(resource); if (func) return func(backingStore); - return Q_NULLPTR; + return nullptr; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 67c96b2d80..e3d9bc7a3d 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -140,7 +140,7 @@ QXcbScreen *QXcbVirtualDesktop::screenAt(const QPoint &pos) const if (screen->virtualDesktop() == this && screen->geometry().contains(pos)) return screen; } - return Q_NULLPTR; + return nullptr; } void QXcbVirtualDesktop::addScreen(QPlatformScreen *s) @@ -590,7 +590,9 @@ QRect QXcbScreen::availableGeometry() const QImage::Format QXcbScreen::format() const { - return qt_xcb_imageFormatForVisual(connection(), screen()->root_depth, visualForId(screen()->root_visual)); + QImage::Format format; + qt_xcb_imageFormatForVisual(connection(), screen()->root_depth, visualForId(screen()->root_visual), &format); + return format; } QDpi QXcbScreen::virtualDpi() const diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 842738b622..4a9b1bd209 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -140,7 +140,7 @@ class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen public: QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop, xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *outputInfo, - const xcb_xinerama_screen_info_t *xineramaScreenInfo = Q_NULLPTR, int xineramaScreenIdx = -1); + const xcb_xinerama_screen_info_t *xineramaScreenInfo = nullptr, int xineramaScreenIdx = -1); ~QXcbScreen(); QString getOutputName(xcb_randr_get_output_info_reply_t *outputInfo); diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index c8a668b72c..20d030b5c9 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -51,6 +51,7 @@ #include "qxcbscreen.h" #include "qxcbdrag.h" #include "qxcbkeyboard.h" +#include "qxcbimage.h" #include "qxcbwmsupport.h" #include "qxcbimage.h" #include "qxcbnativeinterface.h" @@ -176,80 +177,23 @@ static inline bool isTransient(const QWindow *w) || w->type() == Qt::Popup; } -static inline QImage::Format imageFormatForVisual(int depth, quint32 red_mask, quint32 blue_mask, bool *rgbSwap) +void QXcbWindow::setImageFormatForVisual(const xcb_visualtype_t *visual) { - if (rgbSwap) - *rgbSwap = false; - switch (depth) { - case 32: - if (blue_mask == 0xff) - return QImage::Format_ARGB32_Premultiplied; - if (red_mask == 0x3ff) - return QImage::Format_A2BGR30_Premultiplied; - if (blue_mask == 0x3ff) - return QImage::Format_A2RGB30_Premultiplied; - if (red_mask == 0xff) { - if (rgbSwap) - *rgbSwap = true; - return QImage::Format_ARGB32_Premultiplied; - } - break; - case 30: - if (red_mask == 0x3ff) - return QImage::Format_BGR30; - if (blue_mask == 0x3ff) - return QImage::Format_RGB30; - break; - case 24: - if (blue_mask == 0xff) - return QImage::Format_RGB32; - if (red_mask == 0xff) { - if (rgbSwap) - *rgbSwap = true; - return QImage::Format_RGB32; - } - break; - case 16: - if (blue_mask == 0x1f) - return QImage::Format_RGB16; - if (red_mask == 0x1f) { - if (rgbSwap) - *rgbSwap = true; - return QImage::Format_RGB16; - } - break; - case 15: - if (blue_mask == 0x1f) - return QImage::Format_RGB555; - if (red_mask == 0x1f) { - if (rgbSwap) - *rgbSwap = true; - 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; - } - qWarning("Unsupported screen format: depth: %d, red_mask: %x, blue_mask: %x", depth, red_mask, blue_mask); + if (qt_xcb_imageFormatForVisual(connection(), m_depth, visual, &m_imageFormat, &m_imageRgbSwap)) + return; - switch (depth) { + switch (m_depth) { + case 32: case 24: qWarning("Using RGB32 fallback, if this works your X11 server is reporting a bad screen format."); - return QImage::Format_RGB32; + m_imageFormat = QImage::Format_RGB32; + break; case 16: qWarning("Using RGB16 fallback, if this works your X11 server is reporting a bad screen format."); - return QImage::Format_RGB16; + m_imageFormat = QImage::Format_RGB16; default: break; } - - return QImage::Format_Invalid; } static inline bool positionIncludesFrame(QWindow *w) @@ -287,7 +231,7 @@ static inline XTextProperty* qstringToXTP(Display *dpy, const QString& s) if (!mapper || errCode < 0) { mapper = QTextCodec::codecForName("latin1"); if (!mapper || !mapper->canEncode(s)) - return Q_NULLPTR; + return nullptr; #endif static QByteArray qcs; qcs = s.toLatin1(); @@ -322,7 +266,7 @@ static QWindow *childWindowAt(QWindow *win, const QPoint &p) && win->geometry().contains(win->parent()->mapFromGlobal(p))) { return win; } - return Q_NULLPTR; + return nullptr; } static const char *wm_window_type_property_id = "_q_xcb_wm_window_type"; @@ -381,7 +325,7 @@ void QXcbWindow::create() } if (!visual) visual = platformScreen->visualForId(m_visualId); - m_imageFormat = imageFormatForVisual(m_depth, visual->red_mask, visual->blue_mask, &m_imageRgbSwap); + setImageFormatForVisual(visual); connection()->addWindowEventListener(m_window, this); return; } @@ -418,7 +362,7 @@ void QXcbWindow::create() resolveFormat(platformScreen->surfaceFormatFor(window()->requestedFormat())); - const xcb_visualtype_t *visual = Q_NULLPTR; + const xcb_visualtype_t *visual = nullptr; if (connection()->hasDefaultVisualId()) { visual = platformScreen->visualForId(connection()->defaultVisualId()); @@ -451,7 +395,7 @@ void QXcbWindow::create() m_visualId = visual->visual_id; m_depth = platformScreen->depthOfVisual(m_visualId); - m_imageFormat = imageFormatForVisual(m_depth, visual->red_mask, visual->blue_mask, &m_imageRgbSwap); + setImageFormatForVisual(visual); quint32 mask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL @@ -641,7 +585,7 @@ void QXcbWindow::destroy() if (connection()->focusWindow() == this) doFocusOut(); if (connection()->mouseGrabber() == this) - connection()->setMouseGrabber(Q_NULLPTR); + connection()->setMouseGrabber(nullptr); if (m_syncCounter && m_usingSyncProtocol) xcb_sync_destroy_counter(xcb_connection(), m_syncCounter); @@ -700,6 +644,15 @@ void QXcbWindow::setGeometry(const QRect &rect) qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX), }; xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)); + if (window()->parent() && !window()->transientParent()) { + // Wait for server reply for parented windows to ensure that a few window + // moves will come as a one event. This is important when native widget is + // moved a few times in X and Y directions causing native scroll. Widget + // must get single event to not trigger unwanted widget position changes + // and then expose events causing backingstore flushes with incorrect + // offset causing image crruption. + connection()->sync(); + } } xcb_flush(xcb_connection()); @@ -877,12 +830,12 @@ void QXcbWindow::hide() xcb_flush(xcb_connection()); if (connection()->mouseGrabber() == this) - connection()->setMouseGrabber(Q_NULLPTR); + connection()->setMouseGrabber(nullptr); if (QPlatformWindow *w = connection()->mousePressWindow()) { // Unset mousePressWindow when it (or one of its parents) is unmapped while (w) { if (w == this) { - connection()->setMousePressWindow(Q_NULLPTR); + connection()->setMousePressWindow(nullptr); break; } w = w->parent(); @@ -900,7 +853,7 @@ void QXcbWindow::hide() // Find the top level window at cursor position. // Don't use QGuiApplication::topLevelAt(): search only the virtual siblings of this window's screen - QWindow *enterWindow = Q_NULLPTR; + QWindow *enterWindow = nullptr; const auto screens = xcbScreen()->virtualSiblings(); for (QPlatformScreen *screen : screens) { if (screen->geometry().contains(cursorPos)) { @@ -2000,13 +1953,14 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even return; if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) { - if (event->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) { + xcb_atom_t protocolAtom = event->data.data32[0]; + if (protocolAtom == atom(QXcbAtom::WM_DELETE_WINDOW)) { QWindowSystemInterface::handleCloseEvent(window()); - } else if (event->data.data32[0] == atom(QXcbAtom::WM_TAKE_FOCUS)) { + } else if (protocolAtom == atom(QXcbAtom::WM_TAKE_FOCUS)) { connection()->setTime(event->data.data32[1]); relayFocusToModalWindow(); return; - } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) { + } else if (protocolAtom == atom(QXcbAtom::_NET_WM_PING)) { if (event->window == xcbScreen()->root()) return; @@ -2015,20 +1969,23 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even reply.response_type = XCB_CLIENT_MESSAGE; reply.window = xcbScreen()->root(); - xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&reply); + xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (const char *)&reply); xcb_flush(xcb_connection()); - } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) { + } else if (protocolAtom == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) { connection()->setTime(event->data.data32[1]); m_syncValue.lo = event->data.data32[2]; m_syncValue.hi = event->data.data32[3]; if (m_usingSyncProtocol) m_syncState = SyncReceived; #ifndef QT_NO_WHATSTHIS - } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) { + } else if (protocolAtom == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) { QWindowSystemInterface::handleEnterWhatsThisEvent(); #endif } else { - qWarning() << "QXcbWindow: Unhandled WM_PROTOCOLS message:" << connection()->atomName(event->data.data32[0]); + qCWarning(lcQpaXcb, "Unhandled WM_PROTOCOLS (%s)", + connection()->atomName(protocolAtom).constData()); } #ifndef QT_NO_DRAGANDDROP } else if (event->type == atom(QXcbAtom::XdndEnter)) { @@ -2056,7 +2013,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even || event->type == atom(QXcbAtom::_GTK_LOAD_ICONTHEMES)) { //silence the _COMPIZ and _GTK messages for now } else { - qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type); + qCWarning(lcQpaXcb) << "Unhandled client message: " << connection()->atomName(event->type); } } @@ -2165,7 +2122,8 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event) } void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y, - int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source) + int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, + QEvent::Type type, Qt::MouseEventSource source) { const bool isWheel = detail >= 4 && detail <= 7; if (!isWheel && window() != QGuiApplication::focusWindow()) { @@ -2214,11 +2172,12 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in connection()->setMousePressWindow(this); - handleMouseEvent(timestamp, local, global, modifiers, source); + handleMouseEvent(timestamp, local, global, modifiers, type, source); } void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y, - int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source) + int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, + QEvent::Type type, Qt::MouseEventSource source) { QPoint local(event_x, event_y); QPoint global(root_x, root_y); @@ -2229,9 +2188,9 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, } if (connection()->buttonState() == Qt::NoButton) - connection()->setMousePressWindow(Q_NULLPTR); + connection()->setMousePressWindow(nullptr); - handleMouseEvent(timestamp, local, global, modifiers, source); + handleMouseEvent(timestamp, local, global, modifiers, type, source); } static inline bool doCheckUnGrabAncestor(QXcbConnection *conn) @@ -2254,7 +2213,7 @@ static inline bool doCheckUnGrabAncestor(QXcbConnection *conn) return true; } -static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR) +static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = nullptr) { return ((doCheckUnGrabAncestor(conn) && mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) @@ -2263,7 +2222,7 @@ static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL); } -static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR) +static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = nullptr) { return ((doCheckUnGrabAncestor(conn) && mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) @@ -2332,7 +2291,8 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y, } void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y, - Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source) + Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, + QEvent::Type type, Qt::MouseEventSource source) { QPoint local(event_x, event_y); QPoint global(root_x, root_y); @@ -2340,33 +2300,34 @@ 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()->buttonState() != Qt::NoButton); - const bool hasMousePressWindow = (connection()->mousePressWindow() != Q_NULLPTR); + const bool hasMousePressWindow = (connection()->mousePressWindow() != nullptr); if (isMouseButtonPressed && !hasMousePressWindow) connection()->setMousePressWindow(this); else if (hasMousePressWindow && !isMouseButtonPressed) - connection()->setMousePressWindow(Q_NULLPTR); + connection()->setMousePressWindow(nullptr); - handleMouseEvent(timestamp, local, global, modifiers, source); + handleMouseEvent(timestamp, local, global, modifiers, type, source); } void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event) { Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state); handleButtonPressEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail, - modifiers, event->time); + modifiers, event->time, QEvent::MouseButtonPress); } void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event) { Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state); handleButtonReleaseEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail, - modifiers, event->time); + modifiers, event->time, QEvent::MouseButtonRelease); } void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event) { Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state); - handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers, event->time); + handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers, + event->time, QEvent::MouseMove); } #if QT_CONFIG(xinput2) @@ -2417,18 +2378,18 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) qCDebug(lcQpaXInputEvents, "XI2 mouse press, button %d, time %d, source %s", button, ev->time, sourceName); conn->setButtonState(button, true); - handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, source); + handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonPress, 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->setButtonState(button, false); - handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, source); + handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonRelease, source); break; case XI_Motion: if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) qCDebug(lcQpaXInputEvents, "XI2 mouse motion %d,%d, time %d, source %s", event_x, event_y, ev->time, sourceName); - handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time, source); + handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time, QEvent::MouseMove, source); break; default: qWarning() << "Unrecognized XI2 mouse event" << ev->evtype; @@ -2473,10 +2434,13 @@ void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event) QXcbWindow *QXcbWindow::toWindow() { return this; } void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global, - Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source) + Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source) { connection()->setTime(time); - QWindowSystemInterface::handleMouseEvent(window(), time, local, global, connection()->buttonState(), modifiers, source); + Qt::MouseButton button = type == QEvent::MouseMove ? Qt::NoButton : connection()->button(); + QWindowSystemInterface::handleMouseEvent(window(), time, local, global, + connection()->buttonState(), button, + type, modifiers, source); } void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event) @@ -2526,7 +2490,7 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev m_lastWindowStateEvent = newState; m_windowState = newState; if ((m_windowState & Qt::WindowMinimized) && connection()->mouseGrabber() == this) - connection()->setMouseGrabber(Q_NULLPTR); + connection()->setMouseGrabber(nullptr); } return; } else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) { @@ -2594,7 +2558,10 @@ bool QXcbWindow::setKeyboardGrabEnabled(bool grab) bool QXcbWindow::setMouseGrabEnabled(bool grab) { if (!grab && connection()->mouseGrabber() == this) - connection()->setMouseGrabber(Q_NULLPTR); + connection()->setMouseGrabber(nullptr); + + if (grab && !connection()->canGrab()) + return false; #if QT_CONFIG(xinput2) if (connection()->hasXInput2() && !connection()->xi2MouseEventsDisabled()) { @@ -2604,8 +2571,6 @@ bool QXcbWindow::setMouseGrabEnabled(bool grab) return result; } #endif - if (grab && !connection()->canGrab()) - return false; if (!grab) { xcb_ungrab_pointer(xcb_connection(), XCB_TIME_CURRENT_TIME); @@ -2812,6 +2777,15 @@ void QXcbWindow::setOpacity(qreal level) (uchar *)&value); } +QVector<xcb_rectangle_t> qRegionToXcbRectangleList(const QRegion ®ion) +{ + QVector<xcb_rectangle_t> rects; + rects.reserve(region.rectCount()); + for (const QRect &r : region) + rects.push_back(qRectToXCBRectangle(r)); + return rects; +} + void QXcbWindow::setMask(const QRegion ®ion) { if (!connection()->hasXShape()) @@ -2820,10 +2794,7 @@ void QXcbWindow::setMask(const QRegion ®ion) xcb_shape_mask(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, xcb_window(), 0, 0, XCB_NONE); } else { - QVector<xcb_rectangle_t> rects; - rects.reserve(region.rectCount()); - for (const QRect &r : region) - rects.push_back(qRectToXCBRectangle(r)); + const auto rects = qRegionToXcbRectangleList(region); xcb_shape_rectangles(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED, xcb_window(), 0, 0, rects.size(), &rects[0]); diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 1ce9b0a42f..638ab26cea 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -146,7 +146,7 @@ public: QXcbWindow *toWindow() override; void handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global, - Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source); + Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source); void updateNetWmUserTime(xcb_timestamp_t timestamp); @@ -188,6 +188,7 @@ public Q_SLOTS: protected: virtual void resolveFormat(const QSurfaceFormat &format) { m_format = format; } virtual const xcb_visualtype_t *createVisual(); + void setImageFormatForVisual(const xcb_visualtype_t *visual); QXcbScreen *parentScreen(); @@ -220,13 +221,16 @@ protected: bool compressExposeEvent(QRegion &exposeRegion); void handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y, - int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); + int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, + QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); void handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y, - int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); + int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, + QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); void handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y, - Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); + Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, + QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); void handleEnterNotifyEvent(int event_x, int event_y, int root_x, int root_y, quint8 mode, quint8 detail, xcb_timestamp_t timestamp); @@ -292,6 +296,8 @@ protected: void create() override {} // No-op }; +QVector<xcb_rectangle_t> qRegionToXcbRectangleList(const QRegion ®ion); + QT_END_NAMESPACE Q_DECLARE_METATYPE(QXcbWindow*) diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro index 01d493156d..a2c56a3dcf 100644 --- a/src/plugins/platforms/xcb/xcb-plugin.pro +++ b/src/plugins/platforms/xcb/xcb-plugin.pro @@ -4,6 +4,8 @@ QT += core-private gui-private xcb_qpa_lib-private DEFINES += QT_NO_FOREACH +macos: CONFIG += no_app_extension_api_only + SOURCES = \ qxcbmain.cpp OTHER_FILES += xcb.json README |