Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandquickitem.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
13#include <QtWaylandCompositor/qwaylandcompositor.h>
14#include <QtWaylandCompositor/qwaylandseat.h>
15#include <QtWaylandCompositor/qwaylandbufferref.h>
16#if QT_CONFIG(draganddrop)
17#include <QtWaylandCompositor/QWaylandDrag>
18#endif
19#include <QtWaylandCompositor/private/qwlclientbufferintegration_p.h>
20#include <QtWaylandCompositor/private/qwaylandsurface_p.h>
21
22#if QT_CONFIG(opengl)
23# include <QtOpenGL/QOpenGLTexture>
24# include <QtGui/QOpenGLFunctions>
25#endif
26
27#include <QtGui/QKeyEvent>
28#include <QtGui/QGuiApplication>
29#include <QtGui/QScreen>
30
31#include <QtQuick/QSGSimpleTextureNode>
32#include <QtQuick/QQuickWindow>
33#include <QtQuick/qsgtexture.h>
34
35#include <QtCore/QFile>
36#include <QtCore/QMutexLocker>
37#include <QtCore/QMutex>
38
39#include <wayland-server-core.h>
40#include <QThread>
41
42#if QT_CONFIG(opengl)
43#include <QtGui/private/qshaderdescription_p.h>
44#endif
45
46#ifndef GL_TEXTURE_EXTERNAL_OES
47#define GL_TEXTURE_EXTERNAL_OES 0x8D65
48#endif
49
50QT_BEGIN_NAMESPACE
51
52#if QT_CONFIG(opengl)
53static const struct {
54 const char * const vertexShaderSourceFile;
55 const char * const fragmentShaderSourceFile;
56 int planeCount;
57 bool canProvideTexture;
58 QSGMaterial::Flags materialFlags;
59 QSGMaterialType materialType;
60} bufferTypes[] = {
61 // BufferFormatEgl_Null
62 { "", "", 0, false, {}, {} },
63
64 // BufferFormatEgl_RGB (GL_TEXTURE_2D)
65 {
66 ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
67 ":/qt-project.org/wayland/compositor/shaders/surface_rgbx.frag.qsb",
68 1, true,
69 QSGMaterial::Blending,
70 {}
71 },
72
73 // BufferFormatEgl_RGBA (GL_TEXTURE_2D)
74 {
75 ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
76 ":/qt-project.org/wayland/compositor/shaders/surface_rgba.frag.qsb",
77 1, true,
78 QSGMaterial::Blending,
79 {}
80 },
81
82 // BufferFormatEgl_EXTERNAL_OES (GL_TEXTURE_EXTERNAL_OES)
83 {
84 ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
85 ":/qt-project.org/wayland/compositor/shaders/surface_oes_external.frag",
86 1, false,
87 QSGMaterial::Blending,
88 {}
89 },
90
91 // BufferFormatEgl_Y_U_V (GL_TEXTURE_2D)
92 {
93 ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
94 ":/qt-project.org/wayland/compositor/shaders/surface_y_u_v.frag.qsb",
95 3, false,
96 QSGMaterial::Blending,
97 {}
98 },
99
100 // BufferFormatEgl_Y_UV (GL_TEXTURE_2D)
101 {
102 ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
103 ":/qt-project.org/wayland/compositor/shaders/surface_y_uv.frag.qsb",
104 2, false,
105 QSGMaterial::Blending,
106 {}
107 },
108
109 // BufferFormatEgl_Y_XUXV (GL_TEXTURE_2D)
110 {
111 ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
112 ":/qt-project.org/wayland/compositor/shaders/surface_y_xuxv.frag.qsb",
113 2, false,
114 QSGMaterial::Blending,
115 {}
116 }
117};
118
119QWaylandBufferMaterialShader::QWaylandBufferMaterialShader(QWaylandBufferRef::BufferFormatEgl format)
120{
121 Q_UNUSED(format);
122 setShaderFileName(VertexStage, QString::fromLatin1(bufferTypes[format].vertexShaderSourceFile));
123 auto fragmentShaderSourceFile = QString::fromLatin1(bufferTypes[format].fragmentShaderSourceFile);
124
125 if (format == QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES)
126 setupExternalOESShader(fragmentShaderSourceFile);
127 else
128 setShaderFileName(FragmentStage, fragmentShaderSourceFile);
129}
130
131void QWaylandBufferMaterialShader::setupExternalOESShader(const QString &shaderFilename)
132{
133#if QT_CONFIG(opengl)
134 QFile shaderFile(shaderFilename);
135 if (!shaderFile.open(QIODevice::ReadOnly)) {
136 qCWarning(qLcWaylandCompositor) << "Cannot find external OES shader file:" << shaderFilename;
137 return;
138 }
139 QByteArray FS = shaderFile.readAll();
140
141 static const char *FS_GLES_PREAMBLE =
142 "#extension GL_OES_EGL_image_external : require\n"
143 "precision highp float;\n";
144 static const char *FS_GL_PREAMBLE =
145 "#version 120\n"
146 "#extension GL_OES_EGL_image_external : require\n";
147 QByteArray fsGLES = FS_GLES_PREAMBLE + FS;
148 QByteArray fsGL = FS_GL_PREAMBLE + FS;
149
150 QShaderDescription desc;
151 QShaderDescriptionPrivate *descData = QShaderDescriptionPrivate::get(&desc);
152
153 QShaderDescription::InOutVariable texCoordInput;
154 texCoordInput.name = "v_texcoord";
155 texCoordInput.type = QShaderDescription::Vec2;
156 texCoordInput.location = 0;
157
158 descData->inVars = { texCoordInput };
159
160 QShaderDescription::InOutVariable fragColorOutput;
161 fragColorOutput.name = "gl_FragColor";
162 fragColorOutput.type = QShaderDescription::Vec4;
163 fragColorOutput.location = 0;
164
165 descData->outVars = { fragColorOutput };
166
167 QShaderDescription::BlockVariable matrixBlockVar;
168 matrixBlockVar.name = "qt_Matrix";
169 matrixBlockVar.type = QShaderDescription::Mat4;
170 matrixBlockVar.offset = 0;
171 matrixBlockVar.size = 64;
172
173 QShaderDescription::BlockVariable opacityBlockVar;
174 opacityBlockVar.name = "qt_Opacity";
175 opacityBlockVar.type = QShaderDescription::Float;
176 opacityBlockVar.offset = 64;
177 opacityBlockVar.size = 4;
178
179 QShaderDescription::UniformBlock ubufStruct;
180 ubufStruct.blockName = "buf";
181 ubufStruct.structName = "ubuf";
182 ubufStruct.size = 64 + 4;
183 ubufStruct.binding = 0;
184 ubufStruct.members = { matrixBlockVar, opacityBlockVar };
185
186 descData->uniformBlocks = { ubufStruct };
187
188 QShaderDescription::InOutVariable samplerTex0;
189 samplerTex0.name = "tex0";
190 samplerTex0.type = QShaderDescription::SamplerExternalOES;
191 samplerTex0.binding = 1;
192
193 descData->combinedImageSamplers = { samplerTex0 };
194
195 QShader shaderPack;
196 shaderPack.setStage(QShader::FragmentStage);
197 shaderPack.setDescription(desc);
198 shaderPack.setShader(QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)), QShaderCode(fsGLES));
199 shaderPack.setShader(QShaderKey(QShader::GlslShader, QShaderVersion(120)), QShaderCode(fsGL));
200
201 setShader(FragmentStage, shaderPack);
202#else
203 Q_UNUSED(shaderFilename);
204#endif
205}
206
207bool QWaylandBufferMaterialShader::updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *)
208{
209 bool changed = false;
210 QByteArray *buf = state.uniformData();
211 Q_ASSERT(buf->size() >= 68);
212
213 if (state.isMatrixDirty()) {
214 const QMatrix4x4 m = state.combinedMatrix();
215 memcpy(buf->data(), m.constData(), 64);
216 changed = true;
217 }
218
219 if (state.isOpacityDirty()) {
220 const float opacity = state.opacity();
221 memcpy(buf->data() + 64, &opacity, 4);
222 changed = true;
223 }
224
225 return changed;
226}
227
228void QWaylandBufferMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
229 QSGMaterial *newMaterial, QSGMaterial *)
230{
231 Q_UNUSED(state);
232
233 QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(newMaterial);
234 switch (binding) {
235 case 1:
236 *texture = material->m_scenegraphTextures.at(0);
237 break;
238 case 2:
239 *texture = material->m_scenegraphTextures.at(1);
240 break;
241 case 3:
242 *texture = material->m_scenegraphTextures.at(2);
243 break;
244 default:
245 return;
246 }
247
248 // This is for the shared memory case, and is a no-op for others,
249 // this is where the upload from the QImage happens when not yet done.
250 // ### or is this too late? (if buffer.image() disappears in the meantime then this is the wrong...)
251 if (*texture)
252 (*texture)->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
253}
254
255QWaylandBufferMaterial::QWaylandBufferMaterial(QWaylandBufferRef::BufferFormatEgl format)
256 : m_format(format)
257{
258 setFlag(bufferTypes[m_format].materialFlags);
259}
260
261QWaylandBufferMaterial::~QWaylandBufferMaterial()
262{
263 qDeleteAll(m_scenegraphTextures);
264}
265
266void QWaylandBufferMaterial::setTextureForPlane(int plane,
267 QOpenGLTexture *texture,
268 QSGTexture *scenegraphTexture)
269{
270 if (plane < 0 || plane >= bufferTypes[m_format].planeCount) {
271 qWarning("plane index is out of range");
272 return;
273 }
274
275 texture->bind();
276 setTextureParameters(texture->target());
277
278 ensureTextures(plane - 1);
279
280 if (m_textures.size() <= plane) {
281 m_textures << texture;
282 m_scenegraphTextures << scenegraphTexture;
283 } else {
284 delete m_scenegraphTextures[plane];
285
286 m_textures[plane] = texture;
287 m_scenegraphTextures[plane] = scenegraphTexture;
288 }
289}
290
291void QWaylandBufferMaterial::bind()
292{
293 ensureTextures(bufferTypes[m_format].planeCount);
294
295 switch (m_textures.size()) {
296 case 3:
297 if (m_textures[2])
298 m_textures[2]->bind(2);
299 Q_FALLTHROUGH();
300 case 2:
301 if (m_textures[1])
302 m_textures[1]->bind(1);
303 Q_FALLTHROUGH();
304 case 1:
305 if (m_textures[0])
306 m_textures[0]->bind(0);
307 }
308}
309
310QSGMaterialType *QWaylandBufferMaterial::type() const
311{
312 return const_cast<QSGMaterialType *>(&bufferTypes[m_format].materialType);
313}
314
315QSGMaterialShader *QWaylandBufferMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
316{
317 Q_UNUSED(renderMode);
318 return new QWaylandBufferMaterialShader(m_format);
319}
320
321
322void QWaylandBufferMaterial::setTextureParameters(GLenum target)
323{
324 QOpenGLFunctions *gl = QOpenGLContext::currentContext()->functions();
325 gl->glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
326 gl->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
327 gl->glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
328 gl->glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
329}
330
331//TODO move this into a separate centralized texture management class
332void QWaylandBufferMaterial::ensureTextures(int count)
333{
334 for (int plane = m_textures.size(); plane < count; plane++) {
335 m_textures << nullptr;
336 m_scenegraphTextures << nullptr;
337 }
338}
339
340void QWaylandBufferMaterial::setBufferRef(QWaylandQuickItem *surfaceItem, const QWaylandBufferRef &ref)
341{
342 m_bufferRef = ref;
343 for (int plane = 0; plane < bufferTypes[ref.bufferFormatEgl()].planeCount; plane++) {
344 if (auto texture = ref.toOpenGLTexture(plane)) {
345 QQuickWindow::CreateTextureOptions opt;
346 QWaylandQuickSurface *waylandSurface = qobject_cast<QWaylandQuickSurface *>(surfaceItem->surface());
347 if (waylandSurface != nullptr && waylandSurface->useTextureAlpha() && !waylandSurface->isOpaque())
348 opt |= QQuickWindow::TextureHasAlphaChannel;
349 QSGTexture *scenegraphTexture;
350 if (ref.bufferFormatEgl() == QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES) {
351 scenegraphTexture = QNativeInterface::QSGOpenGLTexture::fromNativeExternalOES(texture->textureId(),
352 surfaceItem->window(),
353 ref.size(),
354 opt);
355 } else {
356 scenegraphTexture = QNativeInterface::QSGOpenGLTexture::fromNative(texture->textureId(),
357 surfaceItem->window(),
358 ref.size(),
359 opt);
360 }
361 scenegraphTexture->setFiltering(surfaceItem->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
362 setTextureForPlane(plane, texture, scenegraphTexture);
363 }
364 }
365
366 bind();
367}
368#endif // QT_CONFIG(opengl)
369
370QMutex *QWaylandQuickItemPrivate::mutex = nullptr;
371
373{
374public:
378
380 {
381 // Note: If the ClientBuffer is deleted from the render thread, it happens on a deferred delete
382 // which is processed during the sync phase. Or it may be deleted from the main thread. In
383 // either case, we know the compositor is not shutting down simultaneously. Therefore, we can
384 // safely rely on the value of m_compositorDestroyed to stay the same for the duration of this
385 // destructor.
386 //
387 // Calling setDestroyed() on the buffer will cause it to skip its final sendRelease()
388 // on destruction, which would otherwise cause a crash when the compositor has been
389 // destroyed.
390 if (m_compositorDestroyed.loadAcquire() && m_ref.hasBuffer())
391 m_ref.buffer()->setDestroyed();
392
393 delete m_sgTex;
394 }
395
396 void setBufferRef(QWaylandQuickItem *surfaceItem, const QWaylandBufferRef &buffer)
397 {
398 Q_ASSERT(QThread::currentThread() == thread());
399 m_ref = buffer;
400 delete m_sgTex;
401 m_sgTex = nullptr;
402 if (m_ref.hasBuffer()) {
403 if (buffer.isSharedMemory()) {
404 m_sgTex = surfaceItem->window()->createTextureFromImage(buffer.image());
405 } else {
406#if QT_CONFIG(opengl)
407 QQuickWindow::CreateTextureOptions opt;
408 QWaylandQuickSurface *surface = qobject_cast<QWaylandQuickSurface *>(surfaceItem->surface());
409 if (surface && surface->useTextureAlpha() && !surface->isOpaque()) {
410 opt |= QQuickWindow::TextureHasAlphaChannel;
411 }
412
413 auto texture = buffer.toOpenGLTexture();
414 GLuint textureId = texture->textureId();
415 auto size = surface->bufferSize();
416 m_sgTex = QNativeInterface::QSGOpenGLTexture::fromNative(textureId, surfaceItem->window(), size, opt);
417#else
418 qCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported";
419#endif
420 }
421 }
422 emit textureChanged();
423 }
424
425 QSGTexture *texture() const override
426 {
427 if (m_sgTex)
428 m_sgTex->setFiltering(m_smooth ? QSGTexture::Linear : QSGTexture::Nearest);
429 return m_sgTex;
430 }
431
432 void setSmooth(bool smooth) { m_smooth = smooth; }
433
435 {
436 m_compositorDestroyed.storeRelease(1);
437 deleteLater();
438 }
439
440private:
441 bool m_smooth = false;
442 QSGTexture *m_sgTex = nullptr;
443
444 QAtomicInt m_compositorDestroyed;
445 QWaylandBufferRef m_ref;
446};
447
448void QWaylandQuickItemPrivate::handleDragUpdate(QWaylandSeat *seat, const QPointF &globalPosition)
449{
450#if QT_CONFIG(draganddrop)
451 Q_Q(QWaylandQuickItem);
452 QWaylandQuickOutput *currentOutput = qobject_cast<QWaylandQuickOutput *>(q->view()->output());
453 //TODO: also check if dragging onto other outputs
454 QWaylandQuickItem *targetItem = qobject_cast<QWaylandQuickItem *>(currentOutput->pickClickableItem(q->mapToScene(globalPosition)));
455 QWaylandSurface *targetSurface = targetItem ? targetItem->surface() : nullptr;
456 if (targetSurface) {
457 QPointF position = q->mapToItem(targetItem, globalPosition);
458 QPointF surfacePosition = targetItem->mapToSurface(position);
459 seat->drag()->dragMove(targetSurface, surfacePosition);
460 }
461#else
462 Q_UNUSED(seat);
463 Q_UNUSED(globalPosition);
464#endif // QT_CONFIG(draganddrop)
465}
466
467void QWaylandQuickItemPrivate::handleDragEnded(QWaylandSeat *seat)
468{
469#if QT_CONFIG(draganddrop)
470 isDragging = false;
471 seat->drag()->drop();
472#else
473 Q_UNUSED(seat);
474#endif // QT_CONFIG(draganddrop)
475}
476
477/*!
478 * \qmltype WaylandQuickItem
479 * \nativetype QWaylandQuickItem
480 * \inqmlmodule QtWayland.Compositor
481 * \since 5.8
482 * \brief The WaylandQuickItem type provides a Qt Quick item which represents the surface of a Wayland client.
483 *
484 * Qt Quick-based Wayland compositors can use this type to display a client's
485 * contents on an output device. It passes user input to the
486 * client.
487 */
488
489/*!
490 * \class QWaylandQuickItem
491 * \inmodule QtWaylandCompositor
492 * \since 5.8
493 * \brief The QWaylandQuickItem class provides a Qt Quick item which represents the surface of a Wayland client.
494 *
495 * When writing a QWaylandCompositor in Qt Quick, this class can be used to display a
496 * client's contents on an output device and will pass user input to the
497 * client.
498 */
499
500/*!
501 * Constructs a QWaylandQuickItem with the given \a parent.
502 */
503QWaylandQuickItem::QWaylandQuickItem(QQuickItem *parent)
504 : QWaylandQuickItem(*new QWaylandQuickItemPrivate(), parent)
505{
506}
507
508/*!
509 * \internal
510 */
511QWaylandQuickItem::QWaylandQuickItem(QWaylandQuickItemPrivate &dd, QQuickItem *parent)
512 : QQuickItem(dd, parent)
513{
514 d_func()->init();
515 connect(this, &QQuickItem::activeFocusChanged, this, &QWaylandQuickItem::updateFocus);
516}
517
518/*!
519 * Destroy the QWaylandQuickItem.
520 */
521QWaylandQuickItem::~QWaylandQuickItem()
522{
523 Q_D(QWaylandQuickItem);
524 disconnect(this, &QQuickItem::windowChanged, this, &QWaylandQuickItem::updateWindow);
525 disconnect(this, &QQuickItem::activeFocusChanged, this, &QWaylandQuickItem::updateFocus);
526 QMutexLocker locker(d->mutex);
527 if (d->provider) {
528 disconnect(d->texProviderConnection);
529 d->provider->deleteLater();
530 }
531}
532
533/*!
534 * \qmlproperty WaylandCompositor QtWayland.Compositor::WaylandQuickItem::compositor
535 *
536 * This property holds the compositor for the surface rendered by this WaylandQuickItem.
537 */
538
539/*!
540 * \property QWaylandQuickItem::compositor
541 *
542 * This property holds the compositor for the surface rendered by this QWaylandQuickItem.
543 */
544QWaylandCompositor *QWaylandQuickItem::compositor() const
545{
546 Q_D(const QWaylandQuickItem);
547 return d->view->surface() ? d->view->surface()->compositor() : nullptr;
548}
549
550/*!
551 * Returns the view rendered by this QWaylandQuickItem.
552 */
553QWaylandView *QWaylandQuickItem::view() const
554{
555 Q_D(const QWaylandQuickItem);
556 return d->view.data();
557}
558
559/*!
560 * \qmlproperty WaylandSurface QtWayland.Compositor::WaylandQuickItem::surface
561 *
562 * This property holds the surface rendered by this WaylandQuickItem.
563 */
564
565/*!
566 * \property QWaylandQuickItem::surface
567 *
568 * This property holds the surface rendered by this QWaylandQuickItem.
569 */
570
571QWaylandSurface *QWaylandQuickItem::surface() const
572{
573 Q_D(const QWaylandQuickItem);
574 return d->view->surface();
575}
576
577void QWaylandQuickItem::setSurface(QWaylandSurface *surface)
578{
579 Q_D(QWaylandQuickItem);
580 QWaylandSurface *oldSurf = d->view->surface();
581 if (oldSurf == surface)
582 return;
583
584 QWaylandCompositor *oldComp = oldSurf ? oldSurf->compositor() : nullptr;
585 QWaylandCompositor *newComp = surface ? surface->compositor() : nullptr;
586 if (oldComp != newComp)
587 emit compositorChanged();
588
589 d->view->setSurface(surface);
590
591 updateFocus();
592 update();
593}
594
595/*!
596 * \qmlproperty enum QtWayland.Compositor::WaylandQuickItem::origin
597 *
598 * This property reflects the origin of the surface's current buffer, that is,
599 * which corner of the buffer corresponds to coordinate (0, 0).
600 *
601 * \sa WaylandSurface::origin
602 */
603
604/*!
605 * \property QWaylandQuickItem::origin
606 *
607 * This property reflects the origin of the surface's current buffer, that is,
608 * which corner of the buffer corresponds to coordinate (0, 0).
609 *
610 * \sa QWaylandSurface::origin
611 */
612QWaylandSurface::Origin QWaylandQuickItem::origin() const
613{
614 Q_D(const QWaylandQuickItem);
615 return d->origin;
616}
617
618bool QWaylandQuickItem::isTextureProvider() const
619{
620 Q_D(const QWaylandQuickItem);
621 return QQuickItem::isTextureProvider() || d->provider;
622}
623
624/*!
625 * Returns the texture provider of this QWaylandQuickItem.
626 */
627QSGTextureProvider *QWaylandQuickItem::textureProvider() const
628{
629 Q_D(const QWaylandQuickItem);
630
631 if (QQuickItem::isTextureProvider())
632 return QQuickItem::textureProvider();
633
634 return d->provider;
635}
636
637/*!
638 * \internal
639 */
640void QWaylandQuickItem::mousePressEvent(QMouseEvent *event)
641{
642 Q_D(QWaylandQuickItem);
643 if (!d->shouldSendInputEvents()) {
644 event->ignore();
645 return;
646 }
647
648 if (!inputRegionContains(event->position())) {
649 event->ignore();
650 return;
651 }
652
653 QWaylandSeat *seat = compositor()->seatFor(event);
654
655 if (d->focusOnClick)
656 takeFocus(seat);
657
658 seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->position()), event->scenePosition());
659 seat->sendMousePressEvent(event->button());
660 d->hoverPos = event->position();
661}
662
663/*!
664 * \internal
665 */
666void QWaylandQuickItem::mouseMoveEvent(QMouseEvent *event)
667{
668 Q_D(QWaylandQuickItem);
669 if (d->shouldSendInputEvents()) {
670 QWaylandSeat *seat = compositor()->seatFor(event);
671 if (d->isDragging) {
672 d->handleDragUpdate(seat, event->position());
673 } else {
674 seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->position()), event->scenePosition());
675 d->hoverPos = event->position();
676 }
677 } else {
678 emit mouseMove(event->scenePosition());
679 event->ignore();
680 }
681}
682
683/*!
684 * \internal
685 */
686void QWaylandQuickItem::mouseReleaseEvent(QMouseEvent *event)
687{
688 Q_D(QWaylandQuickItem);
689 if (d->shouldSendInputEvents()) {
690 QWaylandSeat *seat = compositor()->seatFor(event);
691 if (d->isDragging)
692 d->handleDragEnded(seat);
693 else
694 seat->sendMouseReleaseEvent(event->button());
695 } else {
696 emit mouseRelease();
697 event->ignore();
698 }
699}
700
701/*!
702 * \internal
703 */
704void QWaylandQuickItem::hoverEnterEvent(QHoverEvent *event)
705{
706 Q_D(QWaylandQuickItem);
707 if (!inputRegionContains(event->position())) {
708 event->ignore();
709 return;
710 }
711 if (d->shouldSendInputEvents()) {
712 QWaylandSeat *seat = compositor()->seatFor(event);
713 seat->sendMouseMoveEvent(d->view.data(), event->position(), mapToScene(event->position()));
714 d->hoverPos = event->position();
715 } else {
716 event->ignore();
717 }
718}
719
720/*!
721 * \internal
722 */
723void QWaylandQuickItem::hoverMoveEvent(QHoverEvent *event)
724{
725 Q_D(QWaylandQuickItem);
726 if (surface()) {
727 if (!inputRegionContains(event->position())) {
728 event->ignore();
729 return;
730 }
731 }
732 if (d->shouldSendInputEvents()) {
733 QWaylandSeat *seat = compositor()->seatFor(event);
734 if (event->position() != d->hoverPos) {
735 seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->position()), mapToScene(event->position()));
736 d->hoverPos = event->position();
737 }
738 } else {
739 event->ignore();
740 }
741}
742
743/*!
744 * \internal
745 */
746void QWaylandQuickItem::hoverLeaveEvent(QHoverEvent *event)
747{
748 Q_D(QWaylandQuickItem);
749 if (d->shouldSendInputEvents()) {
750 QWaylandSeat *seat = compositor()->seatFor(event);
751 if (d->view.data() == seat->mouseFocus()) {
752 seat->setMouseFocus(nullptr);
753 return;
754 }
755 }
756
757 event->ignore();
758}
759
760#if QT_CONFIG(wheelevent)
761/*!
762 * \internal
763 */
764void QWaylandQuickItem::wheelEvent(QWheelEvent *event)
765{
766 Q_D(QWaylandQuickItem);
767 if (d->shouldSendInputEvents()) {
768 if (!inputRegionContains(event->position())) {
769 event->ignore();
770 return;
771 }
772
773 QWaylandSeat *seat = compositor()->seatFor(event);
774 // TODO: fix this to send a single event, when diagonal scrolling is supported
775 if (event->angleDelta().x() != 0)
776 seat->sendMouseWheelEvent(Qt::Horizontal, event->angleDelta().x());
777 if (event->angleDelta().y() != 0)
778 seat->sendMouseWheelEvent(Qt::Vertical, event->angleDelta().y());
779 } else {
780 event->ignore();
781 }
782}
783#endif
784
785/*!
786 * \internal
787 */
788void QWaylandQuickItem::keyPressEvent(QKeyEvent *event)
789{
790 Q_D(QWaylandQuickItem);
791 if (d->shouldSendInputEvents()) {
792 QWaylandSeat *seat = compositor()->seatFor(event);
793 if (seat->setKeyboardFocus(d->view->surface()))
794 seat->sendFullKeyEvent(event);
795 else
796 qWarning() << "Unable to set keyboard focus, cannot send key press event";
797 } else {
798 event->ignore();
799 }
800}
801
802/*!
803 * \internal
804 */
805void QWaylandQuickItem::keyReleaseEvent(QKeyEvent *event)
806{
807 Q_D(QWaylandQuickItem);
808 if (d->shouldSendInputEvents()) {
809 QWaylandSeat *seat = compositor()->seatFor(event);
810 seat->sendFullKeyEvent(event);
811 } else {
812 event->ignore();
813 }
814}
815
816/*!
817 * \internal
818 */
819void QWaylandQuickItem::touchEvent(QTouchEvent *event)
820{
821 Q_D(QWaylandQuickItem);
822 if (d->shouldSendInputEvents() && d->touchEventsEnabled) {
823 QWaylandSeat *seat = compositor()->seatFor(event);
824
825 QPointF pointPos;
826 const QList<QTouchEvent::TouchPoint> &points = event->points();
827 if (!points.isEmpty())
828 pointPos = points.at(0).position();
829
830 if (event->type() == QEvent::TouchBegin && !inputRegionContains(pointPos)) {
831 event->ignore();
832 return;
833 }
834
835 if (event->type() == QEvent::TouchUpdate && d->isDragging)
836 d->handleDragUpdate(seat, pointPos);
837
838 event->accept();
839 if (seat->mouseFocus() != d->view.data()) {
840 seat->sendMouseMoveEvent(d->view.data(), pointPos, mapToScene(pointPos));
841 }
842 seat->sendFullTouchEvent(surface(), event);
843
844 if (event->type() == QEvent::TouchBegin) {
845 d->touchingSeats.append(seat);
846 } else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) {
847 if (d->isDragging)
848 d->handleDragEnded(seat);
849 d->touchingSeats.removeOne(seat);
850 }
851
852 if (event->type() == QEvent::TouchBegin && d->focusOnClick)
853 takeFocus(seat);
854 } else {
855 event->ignore();
856 }
857}
858
859void QWaylandQuickItem::touchUngrabEvent()
860{
861 Q_D(QWaylandQuickItem);
862
863 if (d->shouldSendInputEvents()) {
864 for (auto seat : std::as_const(d->touchingSeats))
865 seat->sendTouchCancelEvent(surface()->client());
866 }
867
868 d->touchingSeats.clear();
869}
870
871#if QT_CONFIG(im)
872/*!
873 * \internal
874 */
875void QWaylandQuickItem::inputMethodEvent(QInputMethodEvent *event)
876{
877 Q_D(QWaylandQuickItem);
878 if (d->shouldSendInputEvents()) {
879 d->oldSurface->inputMethodControl()->inputMethodEvent(event);
880 } else {
881 event->ignore();
882 }
883}
884#endif
885
886/*!
887 * \internal
888 */
889void QWaylandQuickItem::surfaceChangedEvent(QWaylandSurface *newSurface, QWaylandSurface *oldSurface)
890{
891 Q_UNUSED(newSurface);
892 Q_UNUSED(oldSurface);
893}
894
895void QWaylandQuickItem::handleSubsurfaceAdded(QWaylandSurface *childSurface)
896{
897 Q_D(QWaylandQuickItem);
898 if (d->subsurfaceHandler.isNull()) {
899 QWaylandQuickItem *childItem = new QWaylandQuickItem;
900 childItem->setSurface(childSurface);
901 childItem->setVisible(true);
902 childItem->setParentItem(this);
903 childItem->setParent(this);
904 childItem->setPosition(QWaylandSurfacePrivate::get(childSurface)->subsurfacePosition() * d->scaleFactor());
905 connect(childSurface, &QWaylandSurface::subsurfacePositionChanged, childItem, &QWaylandQuickItem::handleSubsurfacePosition);
906 connect(childSurface, &QWaylandSurface::destroyed, childItem, &QObject::deleteLater);
907 } else {
908 bool success = QMetaObject::invokeMethod(d->subsurfaceHandler, "handleSubsurfaceAdded", Q_ARG(QWaylandSurface *, childSurface));
909 if (!success)
910 success = QMetaObject::invokeMethod(d->subsurfaceHandler, "handleSubsurfaceAdded",
911 Q_ARG(QVariant, QVariant::fromValue(childSurface)));
912
913 if (!success)
914 qWarning("QWaylandQuickItem: subsurfaceHandler does not implement handleSubsurfaceAdded()");
915 }
916}
917
918void QWaylandQuickItem::handlePlaceAbove(QWaylandSurface *referenceSurface)
919{
920 Q_D(QWaylandQuickItem);
921 auto *parent = qobject_cast<QWaylandQuickItem*>(parentItem());
922 if (!parent)
923 return;
924
925 if (parent->surface() == referenceSurface) {
926 d->placeAboveParent();
927 } else if (auto *sibling = d->findSibling(referenceSurface)) {
928 d->placeAboveSibling(sibling);
929 } else {
930 qWarning() << "Couldn't find QWaylandQuickItem for surface" << referenceSurface
931 << "when handling wl_subsurface.place_above";
932 }
933}
934
935void QWaylandQuickItem::handlePlaceBelow(QWaylandSurface *referenceSurface)
936{
937 Q_D(QWaylandQuickItem);
938 QWaylandQuickItem *parent = qobject_cast<QWaylandQuickItem*>(parentItem());
939 if (!parent)
940 return;
941
942 if (parent->surface() == referenceSurface) {
943 d->placeBelowParent();
944 } else if (auto *sibling = d->findSibling(referenceSurface)) {
945 d->placeBelowSibling(sibling);
946 } else {
947 qWarning() << "Couldn't find QWaylandQuickItem for surface" << referenceSurface
948 << "when handling wl_subsurface.place_below";
949 }
950}
951
952void QWaylandQuickItem::updateFocus()
953{
954 Q_D(const QWaylandQuickItem);
955 if (hasActiveFocus() && compositor())
956 compositor()->defaultSeat()->setKeyboardFocus(d->view->surface());
957}
958
959/*!
960 \qmlproperty object QtWayland.Compositor::WaylandQuickItem::subsurfaceHandler
961
962 This property provides a way to override the default subsurface behavior.
963
964 By default, Qt will create a new SurfaceItem as a child of this item, and maintain the correct position.
965
966 To override the default, assign a handler object to this property. The handler should implement
967 a handleSubsurfaceAdded(WaylandSurface) function.
968
969 \code
970 ShellSurfaceItem {
971 subsurfaceHandler: QtObject {
972 function handleSubsurfaceAdded(child) {
973 // create custom surface item, and connect the subsurfacePositionChanged signal
974 }
975 }
976 }
977 \endcode
978
979 The default value of this property is \c null.
980 */
981
982/*!
983 * \property QWaylandQuickItem::subsurfaceHandler
984 *
985 * This property provides a way to override the default subsurface behavior.
986 *
987 * By default, Qt will create a new QWaylandQuickItem as a child of this item
988 * and maintain the correct position.
989 *
990 * To override the default behavior, assign a handler object to this property.
991 * The handler must implement a \c handleSubsurfaceAdded(QWaylandSurface*)
992 * method.
993 *
994 * The default value of this property is \nullptr.
995 */
996QObject *QWaylandQuickItem::subsurfaceHandler() const
997{
998 Q_D(const QWaylandQuickItem);
999 return d->subsurfaceHandler.data();
1000}
1001
1002void QWaylandQuickItem::setSubsurfaceHandler(QObject *handler)
1003{
1004 Q_D(QWaylandQuickItem);
1005 if (d->subsurfaceHandler.data() != handler) {
1006 d->subsurfaceHandler = handler;
1007 emit subsurfaceHandlerChanged();
1008 }
1009}
1010
1011/*!
1012 * \qmlproperty WaylandOutput QtWayland.Compositor::WaylandQuickItem::output
1013 *
1014 * This property holds the output on which this item is displayed.
1015 */
1016/*!
1017 * \property QWaylandQuickItem::output
1018 *
1019 * This property holds the output on which this item is displayed.
1020 */
1021QWaylandOutput *QWaylandQuickItem::output() const
1022{
1023 Q_D(const QWaylandQuickItem);
1024 return d->view->output();
1025}
1026
1027void QWaylandQuickItem::setOutput(QWaylandOutput *output)
1028{
1029 Q_D(QWaylandQuickItem);
1030 d->view->setOutput(output);
1031}
1032
1033/*!
1034 * \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::bufferLocked
1035 *
1036 * This property holds whether the item's buffer is currently locked. As long as
1037 * the buffer is locked, it will not be released and returned to the client.
1038 *
1039 * The default is false.
1040 */
1041/*!
1042 * \property QWaylandQuickItem::bufferLocked
1043 *
1044 * This property holds whether the item's buffer is currently locked. As long as
1045 * the buffer is locked, it will not be released and returned to the client.
1046 *
1047 * The default is false.
1048 */
1049bool QWaylandQuickItem::isBufferLocked() const
1050{
1051 Q_D(const QWaylandQuickItem);
1052 return d->view->isBufferLocked();
1053}
1054
1055void QWaylandQuickItem::setBufferLocked(bool locked)
1056{
1057 Q_D(QWaylandQuickItem);
1058 d->view->setBufferLocked(locked);
1059}
1060
1061/*!
1062 * \property QWaylandQuickItem::allowDiscardFrontBuffer
1063 *
1064 * By default, the item locks the current buffer until a new buffer is available
1065 * and updatePaintNode() is called. Set this property to true to allow Qt to release the buffer
1066 * immediately when the throttling view is no longer using it. This is useful for items that have
1067 * slow update intervals.
1068 */
1069bool QWaylandQuickItem::allowDiscardFrontBuffer() const
1070{
1071 Q_D(const QWaylandQuickItem);
1072 return d->view->allowDiscardFrontBuffer();
1073}
1074
1075void QWaylandQuickItem::setAllowDiscardFrontBuffer(bool discard)
1076{
1077 Q_D(QWaylandQuickItem);
1078 d->view->setAllowDiscardFrontBuffer(discard);
1079}
1080
1081/*!
1082 * \qmlmethod void WaylandQuickItem::setPrimary()
1083 *
1084 * Makes this WaylandQuickItem the primary view for the surface.
1085 */
1086
1087/*!
1088 * Makes this QWaylandQuickItem's view the primary view for the surface.
1089 *
1090 * \sa QWaylandSurface::primaryView
1091 */
1092void QWaylandQuickItem::setPrimary()
1093{
1094 Q_D(QWaylandQuickItem);
1095 d->view->setPrimary();
1096}
1097
1098/*!
1099 * \internal
1100 */
1101void QWaylandQuickItem::handleSurfaceChanged()
1102{
1103 Q_D(QWaylandQuickItem);
1104 if (d->oldSurface) {
1105 disconnect(d->oldSurface.data(), &QWaylandSurface::parentChanged, this, &QWaylandQuickItem::parentChanged);
1106 disconnect(d->oldSurface.data(), &QWaylandSurface::destinationSizeChanged, this, &QWaylandQuickItem::updateSize);
1107 disconnect(d->oldSurface.data(), &QWaylandSurface::bufferScaleChanged, this, &QWaylandQuickItem::updateSize);
1108 disconnect(d->oldSurface.data(), &QWaylandSurface::configure, this, &QWaylandQuickItem::updateBuffer);
1109 disconnect(d->oldSurface.data(), &QWaylandSurface::redraw, this, &QQuickItem::update);
1110 disconnect(d->oldSurface.data(), &QWaylandSurface::childAdded, this, &QWaylandQuickItem::handleSubsurfaceAdded);
1111 disconnect(d->oldSurface.data(), &QWaylandSurface::subsurfacePlaceAbove, this, &QWaylandQuickItem::handlePlaceAbove);
1112 disconnect(d->oldSurface.data(), &QWaylandSurface::subsurfacePlaceBelow, this, &QWaylandQuickItem::handlePlaceBelow);
1113#if QT_CONFIG(draganddrop)
1114 disconnect(d->oldSurface.data(), &QWaylandSurface::dragStarted, this, &QWaylandQuickItem::handleDragStarted);
1115#endif
1116#if QT_CONFIG(im)
1117 disconnect(d->oldSurface->inputMethodControl(), &QWaylandInputMethodControl::updateInputMethod, this, &QWaylandQuickItem::updateInputMethod);
1118#endif
1119 }
1120 if (QWaylandSurface *newSurface = d->view->surface()) {
1121 connect(newSurface, &QWaylandSurface::parentChanged, this, &QWaylandQuickItem::parentChanged);
1122 connect(newSurface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandQuickItem::updateSize);
1123 connect(newSurface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandQuickItem::updateSize);
1124 connect(newSurface, &QWaylandSurface::configure, this, &QWaylandQuickItem::updateBuffer);
1125 connect(newSurface, &QWaylandSurface::redraw, this, &QQuickItem::update);
1126 connect(newSurface, &QWaylandSurface::childAdded, this, &QWaylandQuickItem::handleSubsurfaceAdded);
1127 connect(newSurface, &QWaylandSurface::subsurfacePlaceAbove, this, &QWaylandQuickItem::handlePlaceAbove);
1128 connect(newSurface, &QWaylandSurface::subsurfacePlaceBelow, this, &QWaylandQuickItem::handlePlaceBelow);
1129#if QT_CONFIG(draganddrop)
1130 connect(newSurface, &QWaylandSurface::dragStarted, this, &QWaylandQuickItem::handleDragStarted);
1131#endif
1132#if QT_CONFIG(im)
1133 connect(newSurface->inputMethodControl(), &QWaylandInputMethodControl::updateInputMethod, this, &QWaylandQuickItem::updateInputMethod);
1134#endif
1135
1136 if (newSurface->origin() != d->origin) {
1137 d->origin = newSurface->origin();
1138 emit originChanged();
1139 }
1140 if (window()) {
1141 QWaylandOutput *output = newSurface->compositor()->outputFor(window());
1142 d->view->setOutput(output);
1143 }
1144 for (const auto &subsurface : std::as_const(QWaylandSurfacePrivate::get(newSurface)->subsurfaceChildren)) {
1145 if (!subsurface.isNull())
1146 handleSubsurfaceAdded(subsurface.data());
1147 }
1148
1149 updateSize();
1150 }
1151 surfaceChangedEvent(d->view->surface(), d->oldSurface);
1152 d->oldSurface = d->view->surface();
1153#if QT_CONFIG(im)
1154 updateInputMethod(Qt::ImQueryInput);
1155#endif
1156}
1157
1158/*!
1159 * Calling this function causes the item to take the focus of the
1160 * input \a device.
1161 */
1162void QWaylandQuickItem::takeFocus(QWaylandSeat *device)
1163{
1164 forceActiveFocus();
1165
1166 if (!surface() || !surface()->client())
1167 return;
1168
1169 QWaylandSeat *target = device;
1170 if (!target) {
1171 target = compositor()->defaultSeat();
1172 }
1173 target->setKeyboardFocus(surface());
1174
1175 qCDebug(qLcWaylandCompositorInputMethods) << Q_FUNC_INFO << " surface:" << surface()
1176 << ", client:" << surface()->client()
1177 << ", textinputprotocol:" << (int)(surface()->client()->textInputProtocols());
1178 if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::TextInputV2)) {
1179 QWaylandTextInput *textInput = QWaylandTextInput::findIn(target);
1180 if (textInput)
1181 textInput->setFocus(surface());
1182 }
1183
1184 if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::TextInputV3)) {
1185 QWaylandTextInputV3 *textInputV3 = QWaylandTextInputV3::findIn(target);
1186 if (textInputV3)
1187 textInputV3->setFocus(surface());
1188 }
1189
1190 if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::QtTextInputMethodV1)) {
1191 QWaylandQtTextInputMethod *textInputMethod = QWaylandQtTextInputMethod::findIn(target);
1192 if (textInputMethod)
1193 textInputMethod->setFocus(surface());
1194 }
1195}
1196
1197/*!
1198 * \internal
1199 */
1200void QWaylandQuickItem::handleBufferLockedChanged()
1201{
1202 Q_D(QWaylandQuickItem);
1203
1204 // Apply the latest surface size
1205 if (!d->view->isBufferLocked()) {
1206 updateSize();
1207 update();
1208 }
1209}
1210
1211#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1212void QWaylandQuickItem::surfaceMappedChanged()
1213{
1214 update();
1215}
1216#endif
1217
1218/*!
1219 * \internal
1220 */
1221void QWaylandQuickItem::parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent)
1222{
1223 Q_UNUSED(oldParent);
1224
1225 if (newParent) {
1226 setPaintEnabled(true);
1227 setVisible(true);
1228 setOpacity(1);
1229 setEnabled(true);
1230 }
1231}
1232
1233/*!
1234 * \internal
1235 */
1236void QWaylandQuickItem::updateSize()
1237{
1238 Q_D(QWaylandQuickItem);
1239
1240 // No resize if buffer is locked
1241 if (isBufferLocked()) {
1242 qWarning() << "No update on item size as the buffer is currently locked";
1243 return;
1244 }
1245
1246 QSize size(0, 0);
1247 if (surface())
1248 size = surface()->destinationSize() * d->scaleFactor();
1249
1250 setImplicitSize(size.width(), size.height());
1251}
1252
1253/*!
1254 * \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::focusOnClick
1255 *
1256 * This property specifies whether the WaylandQuickItem should take focus when
1257 * it is clicked or touched.
1258 *
1259 * The default is \c true.
1260 */
1261
1262/*!
1263 * \property QWaylandQuickItem::focusOnClick
1264 *
1265 * This property specifies whether the QWaylandQuickItem should take focus when
1266 * it is clicked or touched.
1267 *
1268 * The default is \c true.
1269 */
1270bool QWaylandQuickItem::focusOnClick() const
1271{
1272 Q_D(const QWaylandQuickItem);
1273 return d->focusOnClick;
1274}
1275
1276void QWaylandQuickItem::setFocusOnClick(bool focus)
1277{
1278 Q_D(QWaylandQuickItem);
1279 if (d->focusOnClick == focus)
1280 return;
1281
1282 d->focusOnClick = focus;
1283 emit focusOnClickChanged();
1284}
1285
1286/*!
1287 * Returns \c true if the input region of this item's surface contains the
1288 * position given by \a localPosition.
1289 */
1290bool QWaylandQuickItem::inputRegionContains(const QPointF &localPosition) const
1291{
1292 if (QWaylandSurface *s = surface())
1293 return s->inputRegionContains(mapToSurface(localPosition));
1294 return false;
1295}
1296
1297/*!
1298 * \qmlmethod point WaylandQuickItem::mapToSurface(point point)
1299 *
1300 * Maps the given \a point in this item's coordinate system to the equivalent
1301 * point within the Wayland surface's coordinate system, and returns the mapped
1302 * coordinate.
1303 */
1304
1305/*!
1306 * Maps the given \a point in this item's coordinate system to the equivalent
1307 * point within the Wayland surface's coordinate system, and returns the mapped
1308 * coordinate.
1309 */
1310QPointF QWaylandQuickItem::mapToSurface(const QPointF &point) const
1311{
1312 Q_D(const QWaylandQuickItem);
1313 if (!surface() || surface()->destinationSize().isEmpty())
1314 return point / d->scaleFactor();
1315
1316 qreal xScale = width() / surface()->destinationSize().width();
1317 qreal yScale = height() / surface()->destinationSize().height();
1318
1319 return QPointF(point.x() / xScale, point.y() / yScale);
1320}
1321
1322/*!
1323 * \qmlmethod point WaylandQuickItem::mapFromSurface(point point)
1324 * \since 5.13
1325 *
1326 * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
1327 * point within this item's coordinate system, and returns the mapped coordinate.
1328 */
1329
1330/*!
1331 * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
1332 * point within this item's coordinate system, and returns the mapped coordinate.
1333 *
1334 * \since 5.13
1335 */
1336QPointF QWaylandQuickItem::mapFromSurface(const QPointF &point) const
1337{
1338 Q_D(const QWaylandQuickItem);
1339 if (!surface() || surface()->destinationSize().isEmpty())
1340 return point * d->scaleFactor();
1341
1342 qreal xScale = width() / surface()->destinationSize().width();
1343 qreal yScale = height() / surface()->destinationSize().height();
1344
1345 return QPointF(point.x() * xScale, point.y() * yScale);
1346}
1347
1348#if QT_CONFIG(im)
1349QVariant QWaylandQuickItem::inputMethodQuery(Qt::InputMethodQuery query) const
1350{
1351 return inputMethodQuery(query, QVariant());
1352}
1353
1354QVariant QWaylandQuickItem::inputMethodQuery(Qt::InputMethodQuery query, QVariant argument) const
1355{
1356 Q_D(const QWaylandQuickItem);
1357
1358 if (query == Qt::ImEnabled)
1359 return QVariant((flags() & ItemAcceptsInputMethod) != 0);
1360
1361 if (d->oldSurface)
1362 return d->oldSurface->inputMethodControl()->inputMethodQuery(query, argument);
1363
1364 return QVariant();
1365}
1366#endif
1367
1368/*!
1369 \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::paintEnabled
1370 \default true
1371
1372 Setting this property to \c false will stop rendering the item. As opposed
1373 to hiding the item by setting \l{Item::visible}{visible} to \c false, it
1374 will not prevent mouse or keyboard input from reaching the item.
1375*/
1376
1377/*!
1378 \property QWaylandQuickItem::paintEnabled
1379
1380 Setting this property to \c false will stop rendering the item. As opposed
1381 to hiding the item by setting \l{Item::visible}{visible} to \c false, it
1382 will not prevent mouse or keyboard input from reaching the item.
1383*/
1384bool QWaylandQuickItem::isPaintEnabled() const
1385{
1386 Q_D(const QWaylandQuickItem);
1387 return d->paintEnabled;
1388}
1389
1390void QWaylandQuickItem::setPaintEnabled(bool enabled)
1391{
1392 Q_D(QWaylandQuickItem);
1393
1394 if (enabled != d->paintEnabled) {
1395 d->paintEnabled = enabled;
1396 emit paintEnabledChanged();
1397 }
1398
1399 update();
1400}
1401
1402/*!
1403 \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::touchEventsEnabled
1404
1405 This property holds \c true if touch events are forwarded to the client
1406 surface, \c false otherwise.
1407*/
1408
1409/*!
1410 \property QWaylandQuickItem::touchEventsEnabled
1411
1412 This property holds \c true if touch events are forwarded to the client
1413 surface, \c false otherwise.
1414*/
1415bool QWaylandQuickItem::touchEventsEnabled() const
1416{
1417 Q_D(const QWaylandQuickItem);
1418 return d->touchEventsEnabled;
1419}
1420
1421void QWaylandQuickItem::updateBuffer(bool hasBuffer)
1422{
1423 Q_D(QWaylandQuickItem);
1424 Q_UNUSED(hasBuffer);
1425 if (d->origin != surface()->origin()) {
1426 d->origin = surface()->origin();
1427 emit originChanged();
1428 }
1429}
1430
1431void QWaylandQuickItem::updateWindow()
1432{
1433 Q_D(QWaylandQuickItem);
1434
1435 QQuickWindow *newWindow = window();
1436 if (newWindow == d->connectedWindow)
1437 return;
1438
1439 if (d->connectedWindow) {
1440 disconnect(d->connectedWindow, &QQuickWindow::beforeSynchronizing, this, &QWaylandQuickItem::beforeSync);
1441 disconnect(d->connectedWindow, &QQuickWindow::screenChanged, this, &QWaylandQuickItem::updateSize);
1442 }
1443
1444 d->connectedWindow = newWindow;
1445
1446 if (d->connectedWindow) {
1447 connect(d->connectedWindow, &QQuickWindow::beforeSynchronizing, this, &QWaylandQuickItem::beforeSync, Qt::DirectConnection);
1448 connect(d->connectedWindow, &QQuickWindow::screenChanged, this, &QWaylandQuickItem::updateSize); // new screen may have new dpr
1449
1450 if (compositor()) {
1451 QWaylandOutput *output = compositor()->outputFor(d->connectedWindow);
1452 Q_ASSERT(output);
1453 d->view->setOutput(output);
1454 }
1455
1456 updateSize(); // because scaleFactor depends on devicePixelRatio, which may be different for the new window
1457 }
1458}
1459
1460void QWaylandQuickItem::updateOutput()
1461{
1462 Q_D(QWaylandQuickItem);
1463 if (d->view->output() == d->connectedOutput)
1464 return;
1465
1466 if (d->connectedOutput)
1467 disconnect(d->connectedOutput, &QWaylandOutput::scaleFactorChanged, this, &QWaylandQuickItem::updateSize);
1468
1469 d->connectedOutput = d->view->output();
1470
1471 if (d->connectedOutput)
1472 connect(d->connectedOutput, &QWaylandOutput::scaleFactorChanged, this, &QWaylandQuickItem::updateSize);
1473
1474 updateSize();
1475}
1476
1477void QWaylandQuickItem::beforeSync()
1478{
1479 Q_D(QWaylandQuickItem);
1480 if (d->view->advance()) {
1481 d->newTexture = true;
1482 update();
1483 }
1484}
1485
1486#if QT_CONFIG(im)
1487void QWaylandQuickItem::updateInputMethod(Qt::InputMethodQueries queries)
1488{
1489 Q_D(QWaylandQuickItem);
1490
1491 setFlag(QQuickItem::ItemAcceptsInputMethod,
1492 d->oldSurface ? d->oldSurface->inputMethodControl()->enabled() : false);
1493 QQuickItem::updateInputMethod(queries | Qt::ImEnabled);
1494}
1495#endif
1496
1497/*!
1498 * \qmlsignal void QtWayland.Compositor::WaylandQuickItem::surfaceDestroyed()
1499 *
1500 * This signal is emitted when the client has destroyed the \c wl_surface associated
1501 * with the WaylandQuickItem. The handler for this signal is expected to either destroy the
1502 * WaylandQuickItem immediately or start a close animation and then destroy the Item.
1503 *
1504 * If an animation is started, bufferLocked should be set to ensure the item keeps its content
1505 * until the animation finishes
1506 *
1507 * \sa bufferLocked
1508 */
1509
1510/*!
1511 * \fn void QWaylandQuickItem::surfaceDestroyed()
1512 *
1513 * This signal is emitted when the client has destroyed the \c wl_surface associated
1514 * with the QWaylandQuickItem. The handler for this signal is expected to either destroy the
1515 * QWaylandQuickItem immediately or start a close animation and then destroy the Item.
1516 *
1517 * If an animation is started, bufferLocked should be set to ensure the item keeps its content
1518 * until the animation finishes
1519 *
1520 * \sa QWaylandQuickItem::bufferLocked
1521 */
1522
1523QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1524{
1525 Q_D(QWaylandQuickItem);
1526 d->lastMatrix = data->transformNode->combinedMatrix();
1527 const bool bufferHasContent = d->view->currentBuffer().hasContent();
1528
1529 if (d->view->isBufferLocked() && d->paintEnabled)
1530 return oldNode;
1531
1532 if (!bufferHasContent || !d->paintEnabled || !surface()) {
1533 delete oldNode;
1534 return nullptr;
1535 }
1536
1537 QWaylandBufferRef ref = d->view->currentBuffer();
1538 const bool invertY = ref.origin() == QWaylandSurface::OriginBottomLeft;
1539 const QRectF rect = invertY ? QRectF(0, height(), width(), -height())
1540 : QRectF(0, 0, width(), height());
1541
1542 if (ref.isSharedMemory()
1543#if QT_CONFIG(opengl)
1544 || bufferTypes[ref.bufferFormatEgl()].canProvideTexture
1545#endif
1546 ) {
1547#if QT_CONFIG(opengl)
1548 if (oldNode && !d->paintByProvider) {
1549 // Need to re-create a node
1550 delete oldNode;
1551 oldNode = nullptr;
1552 }
1553 d->paintByProvider = true;
1554#endif
1555 // This case could covered by the more general path below, but this is more efficient (especially when using ShaderEffect items).
1556 QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(oldNode);
1557
1558 if (!node) {
1559 node = new QSGSimpleTextureNode();
1560 if (smooth())
1561 node->setFiltering(QSGTexture::Linear);
1562 d->newTexture = true;
1563 }
1564
1565 if (!d->provider) {
1566 d->provider = new QWaylandSurfaceTextureProvider();
1567 if (auto *c = compositor()) {
1568 d->texProviderConnection =
1569 QObject::connect(c, &QObject::destroyed, this, [d = d] {
1570 if (d->provider) {
1571 d->provider->reportCompositorDestroyed();
1572 d->provider = nullptr;
1573 }
1574 QObject::disconnect(d->texProviderConnection);
1575 });
1576 }
1577 }
1578
1579 if (d->newTexture) {
1580 d->newTexture = false;
1581 d->provider->setBufferRef(this, ref);
1582 node->setTexture(d->provider->texture());
1583 }
1584
1585 d->provider->setSmooth(smooth());
1586 node->setRect(rect);
1587
1588 qreal scale = surface()->bufferScale();
1589 QRectF source = surface()->sourceGeometry();
1590 node->setSourceRect(QRectF(source.topLeft() * scale, source.size() * scale));
1591
1592 return node;
1593 }
1594
1595#if QT_CONFIG(opengl)
1596 Q_ASSERT(!d->provider);
1597
1598 if (oldNode && d->paintByProvider) {
1599 // Need to re-create a node
1600 delete oldNode;
1601 oldNode = nullptr;
1602 }
1603 d->paintByProvider = false;
1604
1605 QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode);
1606
1607 if (!node) {
1608 node = new QSGGeometryNode;
1609 d->newTexture = true;
1610 }
1611
1612 QSGGeometry *geometry = node->geometry();
1613 QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(node->material());
1614
1615 if (!geometry)
1616 geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
1617
1618 if (!material)
1619 material = new QWaylandBufferMaterial(ref.bufferFormatEgl());
1620
1621 if (d->newTexture) {
1622 d->newTexture = false;
1623 material->setBufferRef(this, ref);
1624 }
1625
1626 const QSize surfaceSize = ref.size() / surface()->bufferScale();
1627 const QRectF sourceGeometry = surface()->sourceGeometry();
1628 const QRectF normalizedCoordinates =
1629 sourceGeometry.isValid()
1630 ? QRectF(sourceGeometry.x() / surfaceSize.width(),
1631 sourceGeometry.y() / surfaceSize.height(),
1632 sourceGeometry.width() / surfaceSize.width(),
1633 sourceGeometry.height() / surfaceSize.height())
1634 : QRectF(0, 0, 1, 1);
1635
1636 QSGGeometry::updateTexturedRectGeometry(geometry, rect, normalizedCoordinates);
1637
1638 node->setGeometry(geometry);
1639 node->setFlag(QSGNode::OwnsGeometry, true);
1640
1641 node->setMaterial(material);
1642 node->setFlag(QSGNode::OwnsMaterial, true);
1643
1644 return node;
1645#else
1646 qCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported";
1647 return nullptr;
1648#endif // QT_CONFIG(opengl)
1649}
1650
1651void QWaylandQuickItem::setTouchEventsEnabled(bool enabled)
1652{
1653 Q_D(QWaylandQuickItem);
1654 if (d->touchEventsEnabled != enabled) {
1655 d->touchEventsEnabled = enabled;
1656 emit touchEventsEnabledChanged();
1657 }
1658}
1659
1660/*!
1661 * \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::inputEventsEnabled
1662 *
1663 * This property holds whether input events are enabled for this item.
1664 *
1665 * When \c true, the item will process and handle input events such as keyboard
1666 * and mouse events; when \c false, input events are ignored.
1667 */
1668
1669/*!
1670 * \property QWaylandQuickItem::inputEventsEnabled
1671 *
1672 * This property holds whether input events are enabled for this item.
1673 *
1674 * When \c true, the item will process and handle input events such as keyboard
1675 * and mouse events; when \c false, input events are ignored.
1676 */
1677bool QWaylandQuickItem::inputEventsEnabled() const
1678{
1679 Q_D(const QWaylandQuickItem);
1680 return d->inputEventsEnabled;
1681}
1682
1683void QWaylandQuickItem::setInputEventsEnabled(bool enabled)
1684{
1685 Q_D(QWaylandQuickItem);
1686 if (d->inputEventsEnabled != enabled) {
1687 if (enabled)
1688 setEnabled(true);
1689 d->setInputEventsEnabled(enabled);
1690 emit inputEventsEnabledChanged();
1691 }
1692}
1693
1694void QWaylandQuickItem::lower()
1695{
1696 Q_D(QWaylandQuickItem);
1697 d->lower();
1698}
1699
1701{
1702 Q_Q(QWaylandQuickItem);
1703 QQuickItem *parent = q->parentItem();
1704 Q_ASSERT(parent);
1705 QQuickItem *bottom = parent->childItems().constFirst();
1706 if (q != bottom)
1707 q->stackBefore(bottom);
1708}
1709
1710void QWaylandQuickItem::raise()
1711{
1712 Q_D(QWaylandQuickItem);
1713 d->raise();
1714}
1715
1717{
1718 Q_Q(QWaylandQuickItem);
1719 QQuickItem *parent = q->parentItem();
1720 Q_ASSERT(parent);
1721 QQuickItem *top = parent->childItems().constLast();
1722 if (q != top)
1723 q->stackAfter(top);
1724}
1725
1726void QWaylandQuickItem::sendMouseMoveEvent(const QPointF &position, QWaylandSeat *seat)
1727{
1728 if (seat == nullptr)
1729 seat = compositor()->defaultSeat();
1730
1731 if (!seat) {
1732 qWarning() << "No seat, can't send mouse event";
1733 return;
1734 }
1735
1736 seat->sendMouseMoveEvent(view(), position);
1737}
1738
1739/*!
1740 * \internal
1741 *
1742 * Sets the position of this item relative to the parent item.
1743 */
1744void QWaylandQuickItem::handleSubsurfacePosition(const QPoint &pos)
1745{
1746 Q_D(QWaylandQuickItem);
1747 QQuickItem::setPosition(pos * d->scaleFactor());
1748}
1749
1750#if QT_CONFIG(draganddrop)
1751void QWaylandQuickItem::handleDragStarted(QWaylandDrag *drag)
1752{
1753 Q_D(QWaylandQuickItem);
1754 Q_ASSERT(drag->origin() == surface());
1755 drag->seat()->setMouseFocus(nullptr);
1756 d->isDragging = true;
1757}
1758#endif
1759
1761{
1762 qreal f = view->output() ? view->output()->scaleFactor() : 1;
1763#if !defined(Q_OS_MACOS)
1764 if (window)
1765 f /= window->devicePixelRatio();
1766#endif
1767 return f;
1768}
1769
1770QWaylandQuickItem *QWaylandQuickItemPrivate::findSibling(QWaylandSurface *surface) const
1771{
1772 Q_Q(const QWaylandQuickItem);
1773 auto *parent = q->parentItem();
1774 if (!parent)
1775 return nullptr;
1776
1777 const auto siblings = q->parentItem()->childItems();
1778 for (auto *sibling : siblings) {
1779 auto *waylandItem = qobject_cast<QWaylandQuickItem *>(sibling);
1780 if (waylandItem && waylandItem->surface() == surface)
1781 return waylandItem;
1782 }
1783 return nullptr;
1784}
1785
1786void QWaylandQuickItemPrivate::placeAboveSibling(QWaylandQuickItem *sibling)
1787{
1788 Q_Q(QWaylandQuickItem);
1789 q->stackAfter(sibling);
1790 q->setZ(sibling->z());
1791 belowParent = sibling->d_func()->belowParent;
1792}
1793
1794void QWaylandQuickItemPrivate::placeBelowSibling(QWaylandQuickItem *sibling)
1795{
1796 Q_Q(QWaylandQuickItem);
1797 q->stackBefore(sibling);
1798 q->setZ(sibling->z());
1799 belowParent = sibling->d_func()->belowParent;
1800}
1801
1802//### does not handle changes in z value if parent is a subsurface
1804{
1805 Q_Q(QWaylandQuickItem);
1806 const auto siblings = q->parentItem()->childItems();
1807
1808 // Stack below first (bottom) sibling above parent
1809 bool foundSibling = false;
1810 for (auto it = siblings.cbegin(); it != siblings.cend(); ++it) {
1811 QWaylandQuickItem *sibling = qobject_cast<QWaylandQuickItem*>(*it);
1812 if (sibling && !sibling->d_func()->belowParent) {
1813 q->stackBefore(sibling);
1814 foundSibling = true;
1815 break;
1816 }
1817 }
1818
1819 // No other subsurfaces above parent
1820 if (!foundSibling && siblings.last() != q)
1821 q->stackAfter(siblings.last());
1822
1823 q->setZ(q->parentItem()->z());
1824 belowParent = false;
1825}
1826
1827//### does not handle changes in z value if parent is a subsurface
1829{
1830 Q_Q(QWaylandQuickItem);
1831 const auto siblings = q->parentItem()->childItems();
1832
1833 // Stack above last (top) sibling below parent
1834 bool foundSibling = false;
1835 for (auto it = siblings.crbegin(); it != siblings.crend(); ++it) {
1836 QWaylandQuickItem *sibling = qobject_cast<QWaylandQuickItem*>(*it);
1837 if (sibling && sibling->d_func()->belowParent) {
1838 q->stackAfter(sibling);
1839 foundSibling = true;
1840 break;
1841 }
1842 }
1843
1844 // No other subsurfaces below parent
1845 if (!foundSibling && siblings.first() != q)
1846 q->stackBefore(siblings.first());
1847
1848 q->setZ(q->parentItem()->z() - 1.0);
1849 belowParent = true;
1850}
1851
1852QT_END_NAMESPACE
1853
1854#include "moc_qwaylandquickitem.cpp"
QSGTexture * texture() const override
Returns a pointer to the texture object.
void setBufferRef(QWaylandQuickItem *surfaceItem, const QWaylandBufferRef &buffer)