diff options
Diffstat (limited to 'src/compositor/compositor_api/qwaylandquickitem.cpp')
-rw-r--r-- | src/compositor/compositor_api/qwaylandquickitem.cpp | 554 |
1 files changed, 398 insertions, 156 deletions
diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp index 4a5880bb2..c643598be 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.cpp +++ b/src/compositor/compositor_api/qwaylandquickitem.cpp @@ -1,37 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWaylandCompositor module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 or (at your option) any later version -** approved by the KDE Free Qt Foundation. The licenses are as published by -** the Free Software Foundation and appearing in the file LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwaylandquickitem.h" #include "qwaylandquickitem_p.h" #include "qwaylandquicksurface.h" #include "qwaylandinputmethodcontrol.h" #include "qwaylandtextinput.h" +#include "qwaylandtextinputv3.h" +#include "qwaylandqttextinputmethod.h" #include "qwaylandquickoutput.h" #include <QtWaylandCompositor/qwaylandcompositor.h> #include <QtWaylandCompositor/qwaylandseat.h> @@ -42,21 +18,30 @@ #include <QtWaylandCompositor/private/qwlclientbufferintegration_p.h> #include <QtWaylandCompositor/private/qwaylandsurface_p.h> +#if QT_CONFIG(opengl) +# include <QtOpenGL/QOpenGLTexture> +# include <QtGui/QOpenGLFunctions> +#endif + #include <QtGui/QKeyEvent> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> -#include <QtGui/QOpenGLFunctions> -#include <QtGui/QOpenGLTexture> #include <QtQuick/QSGSimpleTextureNode> #include <QtQuick/QQuickWindow> +#include <QtQuick/qsgtexture.h> +#include <QtCore/QFile> #include <QtCore/QMutexLocker> #include <QtCore/QMutex> #include <wayland-server-core.h> #include <QThread> +#if QT_CONFIG(opengl) +#include <QtGui/private/qshaderdescription_p.h> +#endif + #ifndef GL_TEXTURE_EXTERNAL_OES #define GL_TEXTURE_EXTERNAL_OES 0x8D65 #endif @@ -67,126 +52,219 @@ QT_BEGIN_NAMESPACE static const struct { const char * const vertexShaderSourceFile; const char * const fragmentShaderSourceFile; - GLenum textureTarget; int planeCount; bool canProvideTexture; QSGMaterial::Flags materialFlags; QSGMaterialType materialType; } bufferTypes[] = { // BufferFormatEgl_Null - { "", "", 0, 0, false, 0, {} }, + { "", "", 0, false, {}, {} }, - // BufferFormatEgl_RGB + // BufferFormatEgl_RGB (GL_TEXTURE_2D) { - ":/qt-project.org/wayland/compositor/shaders/surface.vert", - ":/qt-project.org/wayland/compositor/shaders/surface_rgbx.frag", - GL_TEXTURE_2D, 1, true, + ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb", + ":/qt-project.org/wayland/compositor/shaders/surface_rgbx.frag.qsb", + 1, true, QSGMaterial::Blending, {} }, - // BufferFormatEgl_RGBA + // BufferFormatEgl_RGBA (GL_TEXTURE_2D) { - ":/qt-project.org/wayland/compositor/shaders/surface.vert", - ":/qt-project.org/wayland/compositor/shaders/surface_rgba.frag", - GL_TEXTURE_2D, 1, true, + ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb", + ":/qt-project.org/wayland/compositor/shaders/surface_rgba.frag.qsb", + 1, true, QSGMaterial::Blending, {} }, - // BufferFormatEgl_EXTERNAL_OES + // BufferFormatEgl_EXTERNAL_OES (GL_TEXTURE_EXTERNAL_OES) { - ":/qt-project.org/wayland/compositor/shaders/surface.vert", + ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb", ":/qt-project.org/wayland/compositor/shaders/surface_oes_external.frag", - GL_TEXTURE_EXTERNAL_OES, 1, false, + 1, false, QSGMaterial::Blending, {} }, - // BufferFormatEgl_Y_U_V + // BufferFormatEgl_Y_U_V (GL_TEXTURE_2D) { - ":/qt-project.org/wayland/compositor/shaders/surface.vert", - ":/qt-project.org/wayland/compositor/shaders/surface_y_u_v.frag", - GL_TEXTURE_2D, 3, false, + ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb", + ":/qt-project.org/wayland/compositor/shaders/surface_y_u_v.frag.qsb", + 3, false, QSGMaterial::Blending, {} }, - // BufferFormatEgl_Y_UV + // BufferFormatEgl_Y_UV (GL_TEXTURE_2D) { - ":/qt-project.org/wayland/compositor/shaders/surface.vert", - ":/qt-project.org/wayland/compositor/shaders/surface_y_uv.frag", - GL_TEXTURE_2D, 2, false, + ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb", + ":/qt-project.org/wayland/compositor/shaders/surface_y_uv.frag.qsb", + 2, false, QSGMaterial::Blending, {} }, - // BufferFormatEgl_Y_XUXV + // BufferFormatEgl_Y_XUXV (GL_TEXTURE_2D) { - ":/qt-project.org/wayland/compositor/shaders/surface.vert", - ":/qt-project.org/wayland/compositor/shaders/surface_y_xuxv.frag", - GL_TEXTURE_2D, 2, false, + ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb", + ":/qt-project.org/wayland/compositor/shaders/surface_y_xuxv.frag.qsb", + 2, false, QSGMaterial::Blending, {} } }; QWaylandBufferMaterialShader::QWaylandBufferMaterialShader(QWaylandBufferRef::BufferFormatEgl format) - : m_format(format) { - setShaderSourceFile(QOpenGLShader::Vertex, QString::fromLatin1(bufferTypes[format].vertexShaderSourceFile)); - setShaderSourceFile(QOpenGLShader::Fragment, QString::fromLatin1(bufferTypes[format].fragmentShaderSourceFile)); + Q_UNUSED(format); + setShaderFileName(VertexStage, QString::fromLatin1(bufferTypes[format].vertexShaderSourceFile)); + auto fragmentShaderSourceFile = QString::fromLatin1(bufferTypes[format].fragmentShaderSourceFile); + + if (format == QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES) + setupExternalOESShader(fragmentShaderSourceFile); + else + setShaderFileName(FragmentStage, fragmentShaderSourceFile); } -void QWaylandBufferMaterialShader::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +void QWaylandBufferMaterialShader::setupExternalOESShader(const QString &shaderFilename) { - QSGMaterialShader::updateState(state, newEffect, oldEffect); +#if QT_CONFIG(opengl) + QFile shaderFile(shaderFilename); + if (!shaderFile.open(QIODevice::ReadOnly)) { + qCWarning(qLcWaylandCompositor) << "Cannot find external OES shader file:" << shaderFilename; + return; + } + QByteArray FS = shaderFile.readAll(); + + static const char *FS_GLES_PREAMBLE = + "#extension GL_OES_EGL_image_external : require\n" + "precision highp float;\n"; + static const char *FS_GL_PREAMBLE = + "#version 120\n" + "#extension GL_OES_EGL_image_external : require\n"; + QByteArray fsGLES = FS_GLES_PREAMBLE + FS; + QByteArray fsGL = FS_GL_PREAMBLE + FS; + + QShaderDescription desc; + QShaderDescriptionPrivate *descData = QShaderDescriptionPrivate::get(&desc); + + QShaderDescription::InOutVariable texCoordInput; + texCoordInput.name = "v_texcoord"; + texCoordInput.type = QShaderDescription::Vec2; + texCoordInput.location = 0; + + descData->inVars = { texCoordInput }; + + QShaderDescription::InOutVariable fragColorOutput; + fragColorOutput.name = "gl_FragColor"; + fragColorOutput.type = QShaderDescription::Vec4; + fragColorOutput.location = 0; + + descData->outVars = { fragColorOutput }; + + QShaderDescription::BlockVariable matrixBlockVar; + matrixBlockVar.name = "qt_Matrix"; + matrixBlockVar.type = QShaderDescription::Mat4; + matrixBlockVar.offset = 0; + matrixBlockVar.size = 64; + + QShaderDescription::BlockVariable opacityBlockVar; + opacityBlockVar.name = "qt_Opacity"; + opacityBlockVar.type = QShaderDescription::Float; + opacityBlockVar.offset = 64; + opacityBlockVar.size = 4; + + QShaderDescription::UniformBlock ubufStruct; + ubufStruct.blockName = "buf"; + ubufStruct.structName = "ubuf"; + ubufStruct.size = 64 + 4; + ubufStruct.binding = 0; + ubufStruct.members = { matrixBlockVar, opacityBlockVar }; + + descData->uniformBlocks = { ubufStruct }; + + QShaderDescription::InOutVariable samplerTex0; + samplerTex0.name = "tex0"; + samplerTex0.type = QShaderDescription::SamplerExternalOES; + samplerTex0.binding = 1; + + descData->combinedImageSamplers = { samplerTex0 }; + + QShader shaderPack; + shaderPack.setStage(QShader::FragmentStage); + shaderPack.setDescription(desc); + shaderPack.setShader(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)), QShaderCode(fsGLES)); + shaderPack.setShader(QShaderKey(QShader::GlslShader, QShaderVersion(120)), QShaderCode(fsGL)); + + setShader(FragmentStage, shaderPack); +#else + Q_UNUSED(shaderFilename); +#endif +} - QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(newEffect); - material->bind(); +bool QWaylandBufferMaterialShader::updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *) +{ + bool changed = false; + QByteArray *buf = state.uniformData(); + Q_ASSERT(buf->size() >= 68); - if (state.isMatrixDirty()) - program()->setUniformValue(m_id_matrix, state.combinedMatrix()); + if (state.isMatrixDirty()) { + const QMatrix4x4 m = state.combinedMatrix(); + memcpy(buf->data(), m.constData(), 64); + changed = true; + } - if (state.isOpacityDirty()) - program()->setUniformValue(m_id_opacity, state.opacity()); -} + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(buf->data() + 64, &opacity, 4); + changed = true; + } -const char * const *QWaylandBufferMaterialShader::attributeNames() const -{ - static char const *const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", nullptr }; - return attr; + return changed; } -void QWaylandBufferMaterialShader::initialize() +void QWaylandBufferMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture, + QSGMaterial *newMaterial, QSGMaterial *) { - QSGMaterialShader::initialize(); - - m_id_matrix = program()->uniformLocation("qt_Matrix"); - m_id_opacity = program()->uniformLocation("qt_Opacity"); + Q_UNUSED(state); - for (int i = 0; i < bufferTypes[m_format].planeCount; i++) { - m_id_tex << program()->uniformLocation("tex" + QByteArray::number(i)); - program()->setUniformValue(m_id_tex[i], i); + QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(newMaterial); + switch (binding) { + case 1: + *texture = material->m_scenegraphTextures.at(0); + break; + case 2: + *texture = material->m_scenegraphTextures.at(1); + break; + case 3: + *texture = material->m_scenegraphTextures.at(2); + break; + default: + return; } - Q_ASSERT(m_id_tex.size() == bufferTypes[m_format].planeCount); + // This is for the shared memory case, and is a no-op for others, + // this is where the upload from the QImage happens when not yet done. + // ### or is this too late? (if buffer.image() disappears in the meantime then this is the wrong...) + if (*texture) + (*texture)->commitTextureOperations(state.rhi(), state.resourceUpdateBatch()); } QWaylandBufferMaterial::QWaylandBufferMaterial(QWaylandBufferRef::BufferFormatEgl format) : m_format(format) { - QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions(); - - gl->glBindTexture(bufferTypes[m_format].textureTarget, 0); setFlag(bufferTypes[m_format].materialFlags); } QWaylandBufferMaterial::~QWaylandBufferMaterial() { + qDeleteAll(m_scenegraphTextures); } -void QWaylandBufferMaterial::setTextureForPlane(int plane, QOpenGLTexture *texture) +void QWaylandBufferMaterial::setTextureForPlane(int plane, + QOpenGLTexture *texture, + QSGTexture *scenegraphTexture) { if (plane < 0 || plane >= bufferTypes[m_format].planeCount) { qWarning("plane index is out of range"); @@ -198,10 +276,15 @@ void QWaylandBufferMaterial::setTextureForPlane(int plane, QOpenGLTexture *textu ensureTextures(plane - 1); - if (m_textures.size() <= plane) + if (m_textures.size() <= plane) { m_textures << texture; - else + m_scenegraphTextures << scenegraphTexture; + } else { + delete m_scenegraphTextures[plane]; + m_textures[plane] = texture; + m_scenegraphTextures[plane] = scenegraphTexture; + } } void QWaylandBufferMaterial::bind() @@ -228,8 +311,9 @@ QSGMaterialType *QWaylandBufferMaterial::type() const return const_cast<QSGMaterialType *>(&bufferTypes[m_format].materialType); } -QSGMaterialShader *QWaylandBufferMaterial::createShader() const +QSGMaterialShader *QWaylandBufferMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const { + Q_UNUSED(renderMode); return new QWaylandBufferMaterialShader(m_format); } @@ -248,8 +332,38 @@ void QWaylandBufferMaterial::ensureTextures(int count) { for (int plane = m_textures.size(); plane < count; plane++) { m_textures << nullptr; + m_scenegraphTextures << nullptr; } } + +void QWaylandBufferMaterial::setBufferRef(QWaylandQuickItem *surfaceItem, const QWaylandBufferRef &ref) +{ + m_bufferRef = ref; + for (int plane = 0; plane < bufferTypes[ref.bufferFormatEgl()].planeCount; plane++) { + if (auto texture = ref.toOpenGLTexture(plane)) { + QQuickWindow::CreateTextureOptions opt; + QWaylandQuickSurface *waylandSurface = qobject_cast<QWaylandQuickSurface *>(surfaceItem->surface()); + if (waylandSurface != nullptr && waylandSurface->useTextureAlpha() && !waylandSurface->isOpaque()) + opt |= QQuickWindow::TextureHasAlphaChannel; + QSGTexture *scenegraphTexture; + if (ref.bufferFormatEgl() == QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES) { + scenegraphTexture = QNativeInterface::QSGOpenGLTexture::fromNativeExternalOES(texture->textureId(), + surfaceItem->window(), + ref.size(), + opt); + } else { + scenegraphTexture = QNativeInterface::QSGOpenGLTexture::fromNative(texture->textureId(), + surfaceItem->window(), + ref.size(), + opt); + } + scenegraphTexture->setFiltering(surfaceItem->smooth() ? QSGTexture::Linear : QSGTexture::Nearest); + setTextureForPlane(plane, texture, scenegraphTexture); + } + } + + bind(); +} #endif // QT_CONFIG(opengl) QMutex *QWaylandQuickItemPrivate::mutex = nullptr; @@ -263,8 +377,7 @@ public: ~QWaylandSurfaceTextureProvider() override { - if (m_sgTex) - m_sgTex->deleteLater(); + delete m_sgTex; } void setBufferRef(QWaylandQuickItem *surfaceItem, const QWaylandBufferRef &buffer) @@ -276,21 +389,18 @@ public: if (m_ref.hasBuffer()) { if (buffer.isSharedMemory()) { m_sgTex = surfaceItem->window()->createTextureFromImage(buffer.image()); -#if QT_CONFIG(opengl) - if (m_sgTex) - m_sgTex->bind(); -#endif } else { #if QT_CONFIG(opengl) QQuickWindow::CreateTextureOptions opt; QWaylandQuickSurface *surface = qobject_cast<QWaylandQuickSurface *>(surfaceItem->surface()); - if (surface && surface->useTextureAlpha()) { + if (surface && surface->useTextureAlpha() && !surface->isOpaque()) { opt |= QQuickWindow::TextureHasAlphaChannel; } auto texture = buffer.toOpenGLTexture(); + GLuint textureId = texture->textureId(); auto size = surface->bufferSize(); - m_sgTex = surfaceItem->window()->createTextureFromId(texture->textureId(), size, opt); + m_sgTex = QNativeInterface::QSGOpenGLTexture::fromNative(textureId, surfaceItem->window(), size, opt); #else qCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported"; #endif @@ -315,6 +425,7 @@ private: /*! * \qmltype WaylandQuickItem + * \instantiates QWaylandQuickItem * \inqmlmodule QtWayland.Compositor * \since 5.8 * \brief Provides a Qt Quick item that represents a WaylandView. @@ -339,9 +450,8 @@ private: * Constructs a QWaylandQuickItem with the given \a parent. */ QWaylandQuickItem::QWaylandQuickItem(QQuickItem *parent) - : QQuickItem(*new QWaylandQuickItemPrivate(), parent) + : QWaylandQuickItem(*new QWaylandQuickItemPrivate(), parent) { - d_func()->init(); } /*! @@ -351,6 +461,7 @@ QWaylandQuickItem::QWaylandQuickItem(QWaylandQuickItemPrivate &dd, QQuickItem *p : QQuickItem(dd, parent) { d_func()->init(); + connect(this, &QQuickItem::activeFocusChanged, this, &QWaylandQuickItem::updateFocus); } /*! @@ -360,13 +471,16 @@ QWaylandQuickItem::~QWaylandQuickItem() { Q_D(QWaylandQuickItem); disconnect(this, &QQuickItem::windowChanged, this, &QWaylandQuickItem::updateWindow); + disconnect(this, &QQuickItem::activeFocusChanged, this, &QWaylandQuickItem::updateFocus); QMutexLocker locker(d->mutex); - if (d->provider) + if (d->provider) { + disconnect(d->texProviderConnection); d->provider->deleteLater(); + } } /*! - * \qmlproperty WaylandCompositor QtWaylandCompositor::WaylandQuickItem::compositor + * \qmlproperty WaylandCompositor QtWayland.Compositor::WaylandQuickItem::compositor * * This property holds the compositor for the surface rendered by this WaylandQuickItem. */ @@ -392,7 +506,7 @@ QWaylandView *QWaylandQuickItem::view() const } /*! - * \qmlproperty WaylandSurface QtWaylandCompositor::WaylandQuickItem::surface + * \qmlproperty WaylandSurface QtWayland.Compositor::WaylandQuickItem::surface * * This property holds the surface rendered by this WaylandQuickItem. */ @@ -412,16 +526,21 @@ QWaylandSurface *QWaylandQuickItem::surface() const void QWaylandQuickItem::setSurface(QWaylandSurface *surface) { Q_D(QWaylandQuickItem); + QWaylandSurface *oldSurf = d->view->surface(); QWaylandCompositor *oldComp = d->view->surface() ? d->view->surface()->compositor() : nullptr; d->view->setSurface(surface); QWaylandCompositor *newComp = d->view->surface() ? d->view->surface()->compositor() : nullptr; if (oldComp != newComp) emit compositorChanged(); + if (oldSurf != surface) + emit surfaceChanged(); + + updateFocus(); update(); } /*! - * \qmlproperty enum QtWaylandCompositor::WaylandQuickItem::origin + * \qmlproperty enum QtWayland.Compositor::WaylandQuickItem::origin * * This property holds the origin of the QWaylandQuickItem. */ @@ -467,7 +586,7 @@ void QWaylandQuickItem::mousePressEvent(QMouseEvent *event) return; } - if (!inputRegionContains(event->localPos())) { + if (!inputRegionContains(event->position())) { event->ignore(); return; } @@ -477,9 +596,9 @@ void QWaylandQuickItem::mousePressEvent(QMouseEvent *event) if (d->focusOnClick) takeFocus(seat); - seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->localPos()), event->windowPos()); + seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->position()), event->scenePosition()); seat->sendMousePressEvent(event->button()); - d->hoverPos = event->localPos(); + d->hoverPos = event->position(); } /*! @@ -494,21 +613,21 @@ void QWaylandQuickItem::mouseMoveEvent(QMouseEvent *event) if (d->isDragging) { QWaylandQuickOutput *currentOutput = qobject_cast<QWaylandQuickOutput *>(view()->output()); //TODO: also check if dragging onto other outputs - QWaylandQuickItem *targetItem = qobject_cast<QWaylandQuickItem *>(currentOutput->pickClickableItem(mapToScene(event->localPos()))); + QWaylandQuickItem *targetItem = qobject_cast<QWaylandQuickItem *>(currentOutput->pickClickableItem(mapToScene(event->position()))); QWaylandSurface *targetSurface = targetItem ? targetItem->surface() : nullptr; if (targetSurface) { - QPointF position = mapToItem(targetItem, event->localPos()); + QPointF position = mapToItem(targetItem, event->position()); QPointF surfacePosition = targetItem->mapToSurface(position); seat->drag()->dragMove(targetSurface, surfacePosition); } } else #endif // QT_CONFIG(draganddrop) { - seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->localPos()), event->windowPos()); - d->hoverPos = event->localPos(); + seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->position()), event->scenePosition()); + d->hoverPos = event->position(); } } else { - emit mouseMove(event->windowPos()); + emit mouseMove(event->scenePosition()); event->ignore(); } } @@ -542,14 +661,14 @@ void QWaylandQuickItem::mouseReleaseEvent(QMouseEvent *event) void QWaylandQuickItem::hoverEnterEvent(QHoverEvent *event) { Q_D(QWaylandQuickItem); - if (!inputRegionContains(event->posF())) { + if (!inputRegionContains(event->position())) { event->ignore(); return; } if (d->shouldSendInputEvents()) { QWaylandSeat *seat = compositor()->seatFor(event); - seat->sendMouseMoveEvent(d->view.data(), event->posF(), mapToScene(event->posF())); - d->hoverPos = event->posF(); + seat->sendMouseMoveEvent(d->view.data(), event->position(), mapToScene(event->position())); + d->hoverPos = event->position(); } else { event->ignore(); } @@ -562,16 +681,16 @@ void QWaylandQuickItem::hoverMoveEvent(QHoverEvent *event) { Q_D(QWaylandQuickItem); if (surface()) { - if (!inputRegionContains(event->posF())) { + if (!inputRegionContains(event->position())) { event->ignore(); return; } } if (d->shouldSendInputEvents()) { QWaylandSeat *seat = compositor()->seatFor(event); - if (event->posF() != d->hoverPos) { - seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->posF()), mapToScene(event->posF())); - d->hoverPos = event->posF(); + if (event->position() != d->hoverPos) { + seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->position()), mapToScene(event->position())); + d->hoverPos = event->position(); } } else { event->ignore(); @@ -640,7 +759,7 @@ void QWaylandQuickItem::keyPressEvent(QKeyEvent *event) void QWaylandQuickItem::keyReleaseEvent(QKeyEvent *event) { Q_D(QWaylandQuickItem); - if (d->shouldSendInputEvents() && hasFocus()) { + if (d->shouldSendInputEvents()) { QWaylandSeat *seat = compositor()->seatFor(event); seat->sendFullKeyEvent(event); } else { @@ -658,9 +777,9 @@ void QWaylandQuickItem::touchEvent(QTouchEvent *event) QWaylandSeat *seat = compositor()->seatFor(event); QPointF pointPos; - const QList<QTouchEvent::TouchPoint> &points = event->touchPoints(); + const QList<QTouchEvent::TouchPoint> &points = event->points(); if (!points.isEmpty()) - pointPos = points.at(0).pos(); + pointPos = points.at(0).position(); if (event->type() == QEvent::TouchBegin && !inputRegionContains(pointPos)) { event->ignore(); @@ -729,10 +848,16 @@ void QWaylandQuickItem::handleSubsurfaceAdded(QWaylandSurface *childSurface) childItem->setSurface(childSurface); childItem->setVisible(true); childItem->setParentItem(this); + childItem->setParent(this); connect(childSurface, &QWaylandSurface::subsurfacePositionChanged, childItem, &QWaylandQuickItem::handleSubsurfacePosition); + connect(childSurface, &QWaylandSurface::destroyed, childItem, &QObject::deleteLater); } else { bool success = QMetaObject::invokeMethod(d->subsurfaceHandler, "handleSubsurfaceAdded", Q_ARG(QWaylandSurface *, childSurface)); if (!success) + success = QMetaObject::invokeMethod(d->subsurfaceHandler, "handleSubsurfaceAdded", + Q_ARG(QVariant, QVariant::fromValue(childSurface))); + + if (!success) qWarning("QWaylandQuickItem: subsurfaceHandler does not implement handleSubsurfaceAdded()"); } } @@ -771,8 +896,15 @@ void QWaylandQuickItem::handlePlaceBelow(QWaylandSurface *referenceSurface) } } +void QWaylandQuickItem::updateFocus() +{ + Q_D(const QWaylandQuickItem); + if (hasActiveFocus() && compositor()) + compositor()->defaultSeat()->setKeyboardFocus(d->view->surface()); +} + /*! - \qmlproperty object QtWaylandCompositor::WaylandQuickItem::subsurfaceHandler + \qmlproperty object QtWayland.Compositor::WaylandQuickItem::subsurfaceHandler This property provides a way to override the default subsurface behavior. @@ -784,8 +916,9 @@ void QWaylandQuickItem::handlePlaceBelow(QWaylandSurface *referenceSurface) \code ShellSurfaceItem { subsurfaceHandler: QtObject { - function handleSubsurfaceAdded(child) { - //create custom surface item, and connect the subsurfacePositionChanged signal + function handleSubsurfaceAdded(child) { + // create custom surface item, and connect the subsurfacePositionChanged signal + } } } \endcode @@ -810,6 +943,11 @@ void QWaylandQuickItem::setSubsurfaceHandler(QObject *handler) } /*! + * \qmlproperty WaylandOutput QtWayland.Compositor::WaylandQuickItem::output + * + * This property holds the output on which this item is displayed. + */ +/*! * \property QWaylandQuickItem::output * * This property holds the output on which this item is displayed. @@ -827,7 +965,7 @@ void QWaylandQuickItem::setOutput(QWaylandOutput *output) } /*! - * \qmlproperty bool QtWaylandCompositor::WaylandQuickItem::bufferLocked + * \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::bufferLocked * * This property holds whether the item's buffer is currently locked. As long as * the buffer is locked, it will not be released and returned to the client. @@ -852,6 +990,10 @@ void QWaylandQuickItem::setBufferLocked(bool locked) { Q_D(QWaylandQuickItem); d->view->setBufferLocked(locked); + + // Apply the latest surface size + if (!locked) + updateSize(); } /*! @@ -961,7 +1103,7 @@ void QWaylandQuickItem::takeFocus(QWaylandSeat *device) { forceActiveFocus(); - if (!surface()) + if (!surface() || !surface()->client()) return; QWaylandSeat *target = device; @@ -969,9 +1111,27 @@ void QWaylandQuickItem::takeFocus(QWaylandSeat *device) target = compositor()->defaultSeat(); } target->setKeyboardFocus(surface()); - QWaylandTextInput *textInput = QWaylandTextInput::findIn(target); - if (textInput) - textInput->setFocus(surface()); + + qCDebug(qLcWaylandCompositorInputMethods) << Q_FUNC_INFO << " surface:" << surface() + << ", client:" << surface()->client() + << ", textinputprotocol:" << (int)(surface()->client()->textInputProtocols()); + if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::TextInputV2)) { + QWaylandTextInput *textInput = QWaylandTextInput::findIn(target); + if (textInput) + textInput->setFocus(surface()); + } + + if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::TextInputV3)) { + QWaylandTextInputV3 *textInputV3 = QWaylandTextInputV3::findIn(target); + if (textInputV3) + textInputV3->setFocus(surface()); + } + + if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::QtTextInputMethodV1)) { + QWaylandQtTextInputMethod *textInputMethod = QWaylandQtTextInputMethod::findIn(target); + if (textInputMethod) + textInputMethod->setFocus(surface()); + } } /*! @@ -1004,6 +1164,12 @@ void QWaylandQuickItem::updateSize() { Q_D(QWaylandQuickItem); + // No resize if buffer is locked + if (isBufferLocked()) { + qWarning() << "No update on item size as the buffer is currently locked"; + return; + } + QSize size(0, 0); if (surface()) size = surface()->destinationSize() * d->scaleFactor(); @@ -1012,7 +1178,7 @@ void QWaylandQuickItem::updateSize() } /*! - * \qmlproperty bool QtWaylandCompositor::WaylandQuickItem::focusOnClick + * \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::focusOnClick * * This property specifies whether the WaylandQuickItem should take focus when * it is clicked or touched. @@ -1127,7 +1293,7 @@ QVariant QWaylandQuickItem::inputMethodQuery(Qt::InputMethodQuery query, QVarian #endif /*! - \qmlproperty bool QtWaylandCompositor::WaylandQuickItem::paintEnabled + \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::paintEnabled Returns true if the item is hidden, though the texture is still updated. As opposed to hiding the item by @@ -1136,12 +1302,14 @@ QVariant QWaylandQuickItem::inputMethodQuery(Qt::InputMethodQuery query, QVarian */ /*! - Returns true if the item is hidden, though the texture + \property QWaylandQuickItem::paintEnabled + + Holds \c true if the item is hidden, though the texture is still updated. As opposed to hiding the item by - setting \l{Item::visible}{visible} to \c false, setting this property to \c false + setting \l{QQuickItem::}{visible} to \c false, setting this property to \c false will not prevent mouse or keyboard input from reaching item. */ -bool QWaylandQuickItem::paintEnabled() const +bool QWaylandQuickItem::isPaintEnabled() const { Q_D(const QWaylandQuickItem); return d->paintEnabled; @@ -1150,10 +1318,28 @@ bool QWaylandQuickItem::paintEnabled() const void QWaylandQuickItem::setPaintEnabled(bool enabled) { Q_D(QWaylandQuickItem); - d->paintEnabled = enabled; + + if (enabled != d->paintEnabled) { + d->paintEnabled = enabled; + emit paintEnabledChanged(); + } + update(); } +/*! + \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::touchEventsEnabled + + This property holds \c true if touch events are forwarded to the client + surface, \c false otherwise. +*/ + +/*! + \property QWaylandQuickItem::touchEventsEnabled + + This property holds \c true if touch events are forwarded to the client + surface, \c false otherwise. +*/ bool QWaylandQuickItem::touchEventsEnabled() const { Q_D(const QWaylandQuickItem); @@ -1188,15 +1374,15 @@ void QWaylandQuickItem::updateWindow() if (d->connectedWindow) { connect(d->connectedWindow, &QQuickWindow::beforeSynchronizing, this, &QWaylandQuickItem::beforeSync, Qt::DirectConnection); connect(d->connectedWindow, &QQuickWindow::screenChanged, this, &QWaylandQuickItem::updateSize); // new screen may have new dpr - } - if (compositor() && d->connectedWindow) { - QWaylandOutput *output = compositor()->outputFor(d->connectedWindow); - Q_ASSERT(output); - d->view->setOutput(output); - } + if (compositor()) { + QWaylandOutput *output = compositor()->outputFor(d->connectedWindow); + Q_ASSERT(output); + d->view->setOutput(output); + } - updateSize(); // because scaleFactor depends on devicePixelRatio, which may be different for the new window + updateSize(); // because scaleFactor depends on devicePixelRatio, which may be different for the new window + } } void QWaylandQuickItem::updateOutput() @@ -1237,7 +1423,7 @@ void QWaylandQuickItem::updateInputMethod(Qt::InputMethodQueries queries) #endif /*! - * \qmlsignal void QtWaylandCompositor::WaylandQuickItem::surfaceDestroyed() + * \qmlsignal void QtWayland.Compositor::WaylandQuickItem::surfaceDestroyed() * * This signal is emitted when the client has destroyed the \c wl_surface associated * with the WaylandQuickItem. The handler for this signal is expected to either destroy the @@ -1268,7 +1454,7 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat d->lastMatrix = data->transformNode->combinedMatrix(); const bool bufferHasContent = d->view->currentBuffer().hasContent(); - if (d->view->isBufferLocked() && !bufferHasContent && d->paintEnabled) + if (d->view->isBufferLocked() && d->paintEnabled) return oldNode; if (!bufferHasContent || !d->paintEnabled || !surface()) { @@ -1286,16 +1472,42 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat || bufferTypes[ref.bufferFormatEgl()].canProvideTexture #endif ) { +#if QT_CONFIG(opengl) + if (oldNode && !d->paintByProvider) { + // Need to re-create a node + delete oldNode; + oldNode = nullptr; + } + d->paintByProvider = true; +#endif // This case could covered by the more general path below, but this is more efficient (especially when using ShaderEffect items). QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(oldNode); if (!node) { node = new QSGSimpleTextureNode(); + if (smooth()) + node->setFiltering(QSGTexture::Linear); d->newTexture = true; } - if (!d->provider) + if (!d->provider) { d->provider = new QWaylandSurfaceTextureProvider(); + if (compositor()) { + d->texProviderConnection = + QObject::connect( + compositor(), + &QObject::destroyed, + this, + [this](QObject*) { + auto *itemPriv = QWaylandQuickItemPrivate::get(this); + if (itemPriv->provider) { + itemPriv->provider->deleteLater(); + itemPriv->provider = nullptr; + } + disconnect(itemPriv->texProviderConnection); } + ); + } + } if (d->newTexture) { d->newTexture = false; @@ -1316,6 +1528,13 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat #if QT_CONFIG(opengl) Q_ASSERT(!d->provider); + if (oldNode && d->paintByProvider) { + // Need to re-create a node + delete oldNode; + oldNode = nullptr; + } + d->paintByProvider = false; + QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode); if (!node) { @@ -1334,13 +1553,20 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat if (d->newTexture) { d->newTexture = false; - for (int plane = 0; plane < bufferTypes[ref.bufferFormatEgl()].planeCount; plane++) - if (auto texture = ref.toOpenGLTexture(plane)) - material->setTextureForPlane(plane, texture); - material->bind(); + material->setBufferRef(this, ref); } - QSGGeometry::updateTexturedRectGeometry(geometry, rect, QRectF(0, 0, 1, 1)); + const QSize surfaceSize = ref.size() / surface()->bufferScale(); + const QRectF sourceGeometry = surface()->sourceGeometry(); + const QRectF normalizedCoordinates = + sourceGeometry.isValid() + ? QRectF(sourceGeometry.x() / surfaceSize.width(), + sourceGeometry.y() / surfaceSize.height(), + sourceGeometry.width() / surfaceSize.width(), + sourceGeometry.height() / surfaceSize.height()) + : QRectF(0, 0, 1, 1); + + QSGGeometry::updateTexturedRectGeometry(geometry, rect, normalizedCoordinates); node->setGeometry(geometry); node->setFlag(QSGNode::OwnsGeometry, true); @@ -1383,20 +1609,34 @@ void QWaylandQuickItem::setInputEventsEnabled(bool enabled) void QWaylandQuickItem::lower() { - QQuickItem *parent = parentItem(); + Q_D(QWaylandQuickItem); + d->lower(); +} + +void QWaylandQuickItemPrivate::lower() +{ + Q_Q(QWaylandQuickItem); + QQuickItem *parent = q->parentItem(); Q_ASSERT(parent); - QQuickItem *bottom = parent->childItems().first(); - if (this != bottom) - stackBefore(bottom); + QQuickItem *bottom = parent->childItems().constFirst(); + if (q != bottom) + q->stackBefore(bottom); } void QWaylandQuickItem::raise() { - QQuickItem *parent = parentItem(); + Q_D(QWaylandQuickItem); + d->raise(); +} + +void QWaylandQuickItemPrivate::raise() +{ + Q_Q(QWaylandQuickItem); + QQuickItem *parent = q->parentItem(); Q_ASSERT(parent); - QQuickItem *top = parent->childItems().last(); - if (this != top) - stackAfter(top); + QQuickItem *top = parent->childItems().constLast(); + if (q != top) + q->stackAfter(top); } void QWaylandQuickItem::sendMouseMoveEvent(const QPointF &position, QWaylandSeat *seat) @@ -1526,3 +1766,5 @@ void QWaylandQuickItemPrivate::placeBelowParent() } QT_END_NAMESPACE + +#include "moc_qwaylandquickitem.cpp" |