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