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 delete m_sgTex;
381 }
382
383 void setBufferRef(QWaylandQuickItem *surfaceItem, const QWaylandBufferRef &buffer)
384 {
385 Q_ASSERT(QThread::currentThread() == thread());
386 m_ref = buffer;
387 delete m_sgTex;
388 m_sgTex = nullptr;
389 if (m_ref.hasBuffer()) {
390 if (buffer.isSharedMemory()) {
391 m_sgTex = surfaceItem->window()->createTextureFromImage(buffer.image());
392 } else {
393#if QT_CONFIG(opengl)
394 QQuickWindow::CreateTextureOptions opt;
395 QWaylandQuickSurface *surface = qobject_cast<QWaylandQuickSurface *>(surfaceItem->surface());
396 if (surface && surface->useTextureAlpha() && !surface->isOpaque()) {
397 opt |= QQuickWindow::TextureHasAlphaChannel;
398 }
399
400 auto texture = buffer.toOpenGLTexture();
401 GLuint textureId = texture->textureId();
402 auto size = surface->bufferSize();
403 m_sgTex = QNativeInterface::QSGOpenGLTexture::fromNative(textureId, surfaceItem->window(), size, opt);
404#else
405 qCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported";
406#endif
407 }
408 }
409 emit textureChanged();
410 }
411
412 QSGTexture *texture() const override
413 {
414 if (m_sgTex)
415 m_sgTex->setFiltering(m_smooth ? QSGTexture::Linear : QSGTexture::Nearest);
416 return m_sgTex;
417 }
418
419 void setSmooth(bool smooth) { m_smooth = smooth; }
420private:
421 bool m_smooth = false;
422 QSGTexture *m_sgTex = nullptr;
423 QWaylandBufferRef m_ref;
424};
425
426void QWaylandQuickItemPrivate::handleDragUpdate(QWaylandSeat *seat, const QPointF &globalPosition)
427{
428#if QT_CONFIG(draganddrop)
429 Q_Q(QWaylandQuickItem);
430 QWaylandQuickOutput *currentOutput = qobject_cast<QWaylandQuickOutput *>(q->view()->output());
431 //TODO: also check if dragging onto other outputs
432 QWaylandQuickItem *targetItem = qobject_cast<QWaylandQuickItem *>(currentOutput->pickClickableItem(q->mapToScene(globalPosition)));
433 QWaylandSurface *targetSurface = targetItem ? targetItem->surface() : nullptr;
434 if (targetSurface) {
435 QPointF position = q->mapToItem(targetItem, globalPosition);
436 QPointF surfacePosition = targetItem->mapToSurface(position);
437 seat->drag()->dragMove(targetSurface, surfacePosition);
438 }
439#else
440 Q_UNUSED(seat);
441 Q_UNUSED(globalPosition);
442#endif // QT_CONFIG(draganddrop)
443}
444
445void QWaylandQuickItemPrivate::handleDragEnded(QWaylandSeat *seat)
446{
447#if QT_CONFIG(draganddrop)
448 isDragging = false;
449 seat->drag()->drop();
450#else
451 Q_UNUSED(seat);
452#endif // QT_CONFIG(draganddrop)
453}
454
455/*!
456 * \qmltype WaylandQuickItem
457 * \nativetype QWaylandQuickItem
458 * \inqmlmodule QtWayland.Compositor
459 * \since 5.8
460 * \brief Provides a Qt Quick item that represents a WaylandView.
461 *
462 * Qt Quick-based Wayland compositors can use this type to display a client's
463 * contents on an output device. It passes user input to the
464 * client.
465 */
466
467/*!
468 * \class QWaylandQuickItem
469 * \inmodule QtWaylandCompositor
470 * \since 5.8
471 * \brief The QWaylandQuickItem class provides a Qt Quick item representing a QWaylandView.
472 *
473 * When writing a QWaylandCompositor in Qt Quick, this class can be used to display a
474 * client's contents on an output device and will pass user input to the
475 * client.
476 */
477
478/*!
479 * Constructs a QWaylandQuickItem with the given \a parent.
480 */
481QWaylandQuickItem::QWaylandQuickItem(QQuickItem *parent)
482 : QWaylandQuickItem(*new QWaylandQuickItemPrivate(), parent)
483{
484}
485
486/*!
487 * \internal
488 */
489QWaylandQuickItem::QWaylandQuickItem(QWaylandQuickItemPrivate &dd, QQuickItem *parent)
490 : QQuickItem(dd, parent)
491{
492 d_func()->init();
493 connect(this, &QQuickItem::activeFocusChanged, this, &QWaylandQuickItem::updateFocus);
494}
495
496/*!
497 * Destroy the QWaylandQuickItem.
498 */
499QWaylandQuickItem::~QWaylandQuickItem()
500{
501 Q_D(QWaylandQuickItem);
502 disconnect(this, &QQuickItem::windowChanged, this, &QWaylandQuickItem::updateWindow);
503 disconnect(this, &QQuickItem::activeFocusChanged, this, &QWaylandQuickItem::updateFocus);
504 QMutexLocker locker(d->mutex);
505 if (d->provider) {
506 disconnect(d->texProviderConnection);
507 d->provider->deleteLater();
508 }
509}
510
511/*!
512 * \qmlproperty WaylandCompositor QtWayland.Compositor::WaylandQuickItem::compositor
513 *
514 * This property holds the compositor for the surface rendered by this WaylandQuickItem.
515 */
516
517/*!
518 * \property QWaylandQuickItem::compositor
519 *
520 * This property holds the compositor for the surface rendered by this QWaylandQuickItem.
521 */
522QWaylandCompositor *QWaylandQuickItem::compositor() const
523{
524 Q_D(const QWaylandQuickItem);
525 return d->view->surface() ? d->view->surface()->compositor() : nullptr;
526}
527
528/*!
529 * Returns the view rendered by this QWaylandQuickItem.
530 */
531QWaylandView *QWaylandQuickItem::view() const
532{
533 Q_D(const QWaylandQuickItem);
534 return d->view.data();
535}
536
537/*!
538 * \qmlproperty WaylandSurface QtWayland.Compositor::WaylandQuickItem::surface
539 *
540 * This property holds the surface rendered by this WaylandQuickItem.
541 */
542
543/*!
544 * \property QWaylandQuickItem::surface
545 *
546 * This property holds the surface rendered by this QWaylandQuickItem.
547 */
548
549QWaylandSurface *QWaylandQuickItem::surface() const
550{
551 Q_D(const QWaylandQuickItem);
552 return d->view->surface();
553}
554
555void QWaylandQuickItem::setSurface(QWaylandSurface *surface)
556{
557 Q_D(QWaylandQuickItem);
558 QWaylandSurface *oldSurf = d->view->surface();
559 QWaylandCompositor *oldComp = d->view->surface() ? d->view->surface()->compositor() : nullptr;
560 d->view->setSurface(surface);
561 QWaylandCompositor *newComp = d->view->surface() ? d->view->surface()->compositor() : nullptr;
562 if (oldComp != newComp)
563 emit compositorChanged();
564 if (oldSurf != surface)
565 emit surfaceChanged();
566
567 updateFocus();
568 update();
569}
570
571/*!
572 * \qmlproperty enum QtWayland.Compositor::WaylandQuickItem::origin
573 *
574 * This property holds the origin of the QWaylandQuickItem.
575 */
576
577/*!
578 * \property QWaylandQuickItem::origin
579 *
580 * This property holds the origin of the QWaylandQuickItem.
581 */
582QWaylandSurface::Origin QWaylandQuickItem::origin() const
583{
584 Q_D(const QWaylandQuickItem);
585 return d->origin;
586}
587
588bool QWaylandQuickItem::isTextureProvider() const
589{
590 Q_D(const QWaylandQuickItem);
591 return QQuickItem::isTextureProvider() || d->provider;
592}
593
594/*!
595 * Returns the texture provider of this QWaylandQuickItem.
596 */
597QSGTextureProvider *QWaylandQuickItem::textureProvider() const
598{
599 Q_D(const QWaylandQuickItem);
600
601 if (QQuickItem::isTextureProvider())
602 return QQuickItem::textureProvider();
603
604 return d->provider;
605}
606
607/*!
608 * \internal
609 */
610void QWaylandQuickItem::mousePressEvent(QMouseEvent *event)
611{
612 Q_D(QWaylandQuickItem);
613 if (!d->shouldSendInputEvents()) {
614 event->ignore();
615 return;
616 }
617
618 if (!inputRegionContains(event->position())) {
619 event->ignore();
620 return;
621 }
622
623 QWaylandSeat *seat = compositor()->seatFor(event);
624
625 if (d->focusOnClick)
626 takeFocus(seat);
627
628 seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->position()), event->scenePosition());
629 seat->sendMousePressEvent(event->button());
630 d->hoverPos = event->position();
631}
632
633/*!
634 * \internal
635 */
636void QWaylandQuickItem::mouseMoveEvent(QMouseEvent *event)
637{
638 Q_D(QWaylandQuickItem);
639 if (d->shouldSendInputEvents()) {
640 QWaylandSeat *seat = compositor()->seatFor(event);
641 if (d->isDragging) {
642 d->handleDragUpdate(seat, event->position());
643 } else {
644 seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->position()), event->scenePosition());
645 d->hoverPos = event->position();
646 }
647 } else {
648 emit mouseMove(event->scenePosition());
649 event->ignore();
650 }
651}
652
653/*!
654 * \internal
655 */
656void QWaylandQuickItem::mouseReleaseEvent(QMouseEvent *event)
657{
658 Q_D(QWaylandQuickItem);
659 if (d->shouldSendInputEvents()) {
660 QWaylandSeat *seat = compositor()->seatFor(event);
661 if (d->isDragging)
662 d->handleDragEnded(seat);
663 else
664 seat->sendMouseReleaseEvent(event->button());
665 } else {
666 emit mouseRelease();
667 event->ignore();
668 }
669}
670
671/*!
672 * \internal
673 */
674void QWaylandQuickItem::hoverEnterEvent(QHoverEvent *event)
675{
676 Q_D(QWaylandQuickItem);
677 if (!inputRegionContains(event->position())) {
678 event->ignore();
679 return;
680 }
681 if (d->shouldSendInputEvents()) {
682 QWaylandSeat *seat = compositor()->seatFor(event);
683 seat->sendMouseMoveEvent(d->view.data(), event->position(), mapToScene(event->position()));
684 d->hoverPos = event->position();
685 } else {
686 event->ignore();
687 }
688}
689
690/*!
691 * \internal
692 */
693void QWaylandQuickItem::hoverMoveEvent(QHoverEvent *event)
694{
695 Q_D(QWaylandQuickItem);
696 if (surface()) {
697 if (!inputRegionContains(event->position())) {
698 event->ignore();
699 return;
700 }
701 }
702 if (d->shouldSendInputEvents()) {
703 QWaylandSeat *seat = compositor()->seatFor(event);
704 if (event->position() != d->hoverPos) {
705 seat->sendMouseMoveEvent(d->view.data(), mapToSurface(event->position()), mapToScene(event->position()));
706 d->hoverPos = event->position();
707 }
708 } else {
709 event->ignore();
710 }
711}
712
713/*!
714 * \internal
715 */
716void QWaylandQuickItem::hoverLeaveEvent(QHoverEvent *event)
717{
718 Q_D(QWaylandQuickItem);
719 if (d->shouldSendInputEvents()) {
720 QWaylandSeat *seat = compositor()->seatFor(event);
721 if (d->view.data() == seat->mouseFocus()) {
722 seat->setMouseFocus(nullptr);
723 return;
724 }
725 }
726
727 event->ignore();
728}
729
730#if QT_CONFIG(wheelevent)
731/*!
732 * \internal
733 */
734void QWaylandQuickItem::wheelEvent(QWheelEvent *event)
735{
736 Q_D(QWaylandQuickItem);
737 if (d->shouldSendInputEvents()) {
738 if (!inputRegionContains(event->position())) {
739 event->ignore();
740 return;
741 }
742
743 QWaylandSeat *seat = compositor()->seatFor(event);
744 // TODO: fix this to send a single event, when diagonal scrolling is supported
745 if (event->angleDelta().x() != 0)
746 seat->sendMouseWheelEvent(Qt::Horizontal, event->angleDelta().x());
747 if (event->angleDelta().y() != 0)
748 seat->sendMouseWheelEvent(Qt::Vertical, event->angleDelta().y());
749 } else {
750 event->ignore();
751 }
752}
753#endif
754
755/*!
756 * \internal
757 */
758void QWaylandQuickItem::keyPressEvent(QKeyEvent *event)
759{
760 Q_D(QWaylandQuickItem);
761 if (d->shouldSendInputEvents()) {
762 QWaylandSeat *seat = compositor()->seatFor(event);
763 if (seat->setKeyboardFocus(d->view->surface()))
764 seat->sendFullKeyEvent(event);
765 else
766 qWarning() << "Unable to set keyboard focus, cannot send key press event";
767 } else {
768 event->ignore();
769 }
770}
771
772/*!
773 * \internal
774 */
775void QWaylandQuickItem::keyReleaseEvent(QKeyEvent *event)
776{
777 Q_D(QWaylandQuickItem);
778 if (d->shouldSendInputEvents()) {
779 QWaylandSeat *seat = compositor()->seatFor(event);
780 seat->sendFullKeyEvent(event);
781 } else {
782 event->ignore();
783 }
784}
785
786/*!
787 * \internal
788 */
789void QWaylandQuickItem::touchEvent(QTouchEvent *event)
790{
791 Q_D(QWaylandQuickItem);
792 if (d->shouldSendInputEvents() && d->touchEventsEnabled) {
793 QWaylandSeat *seat = compositor()->seatFor(event);
794
795 QPointF pointPos;
796 const QList<QTouchEvent::TouchPoint> &points = event->points();
797 if (!points.isEmpty())
798 pointPos = points.at(0).position();
799
800 if (event->type() == QEvent::TouchBegin && !inputRegionContains(pointPos)) {
801 event->ignore();
802 return;
803 }
804
805 if (event->type() == QEvent::TouchUpdate && d->isDragging)
806 d->handleDragUpdate(seat, pointPos);
807
808 event->accept();
809 if (seat->mouseFocus() != d->view.data()) {
810 seat->sendMouseMoveEvent(d->view.data(), pointPos, mapToScene(pointPos));
811 }
812 seat->sendFullTouchEvent(surface(), event);
813
814 if (event->type() == QEvent::TouchBegin) {
815 d->touchingSeats.append(seat);
816 } else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) {
817 if (d->isDragging)
818 d->handleDragEnded(seat);
819 d->touchingSeats.removeOne(seat);
820 }
821
822 if (event->type() == QEvent::TouchBegin && d->focusOnClick)
823 takeFocus(seat);
824 } else {
825 event->ignore();
826 }
827}
828
829void QWaylandQuickItem::touchUngrabEvent()
830{
831 Q_D(QWaylandQuickItem);
832
833 if (d->shouldSendInputEvents())
834 for (auto seat : d->touchingSeats)
835 seat->sendTouchCancelEvent(surface()->client());
836
837 d->touchingSeats.clear();
838}
839
840#if QT_CONFIG(im)
841/*!
842 * \internal
843 */
844void QWaylandQuickItem::inputMethodEvent(QInputMethodEvent *event)
845{
846 Q_D(QWaylandQuickItem);
847 if (d->shouldSendInputEvents()) {
848 d->oldSurface->inputMethodControl()->inputMethodEvent(event);
849 } else {
850 event->ignore();
851 }
852}
853#endif
854
855/*!
856 * \internal
857 */
858void QWaylandQuickItem::surfaceChangedEvent(QWaylandSurface *newSurface, QWaylandSurface *oldSurface)
859{
860 Q_UNUSED(newSurface);
861 Q_UNUSED(oldSurface);
862}
863
864void QWaylandQuickItem::handleSubsurfaceAdded(QWaylandSurface *childSurface)
865{
866 Q_D(QWaylandQuickItem);
867 if (d->subsurfaceHandler.isNull()) {
868 QWaylandQuickItem *childItem = new QWaylandQuickItem;
869 childItem->setSurface(childSurface);
870 childItem->setVisible(true);
871 childItem->setParentItem(this);
872 childItem->setParent(this);
873 connect(childSurface, &QWaylandSurface::subsurfacePositionChanged, childItem, &QWaylandQuickItem::handleSubsurfacePosition);
874 connect(childSurface, &QWaylandSurface::destroyed, childItem, &QObject::deleteLater);
875 } else {
876 bool success = QMetaObject::invokeMethod(d->subsurfaceHandler, "handleSubsurfaceAdded", Q_ARG(QWaylandSurface *, childSurface));
877 if (!success)
878 success = QMetaObject::invokeMethod(d->subsurfaceHandler, "handleSubsurfaceAdded",
879 Q_ARG(QVariant, QVariant::fromValue(childSurface)));
880
881 if (!success)
882 qWarning("QWaylandQuickItem: subsurfaceHandler does not implement handleSubsurfaceAdded()");
883 }
884}
885
886void QWaylandQuickItem::handlePlaceAbove(QWaylandSurface *referenceSurface)
887{
888 Q_D(QWaylandQuickItem);
889 auto *parent = qobject_cast<QWaylandQuickItem*>(parentItem());
890 if (!parent)
891 return;
892
893 if (parent->surface() == referenceSurface) {
894 d->placeAboveParent();
895 } else if (auto *sibling = d->findSibling(referenceSurface)) {
896 d->placeAboveSibling(sibling);
897 } else {
898 qWarning() << "Couldn't find QWaylandQuickItem for surface" << referenceSurface
899 << "when handling wl_subsurface.place_above";
900 }
901}
902
903void QWaylandQuickItem::handlePlaceBelow(QWaylandSurface *referenceSurface)
904{
905 Q_D(QWaylandQuickItem);
906 QWaylandQuickItem *parent = qobject_cast<QWaylandQuickItem*>(parentItem());
907 if (!parent)
908 return;
909
910 if (parent->surface() == referenceSurface) {
911 d->placeBelowParent();
912 } else if (auto *sibling = d->findSibling(referenceSurface)) {
913 d->placeBelowSibling(sibling);
914 } else {
915 qWarning() << "Couldn't find QWaylandQuickItem for surface" << referenceSurface
916 << "when handling wl_subsurface.place_below";
917 }
918}
919
920void QWaylandQuickItem::updateFocus()
921{
922 Q_D(const QWaylandQuickItem);
923 if (hasActiveFocus() && compositor())
924 compositor()->defaultSeat()->setKeyboardFocus(d->view->surface());
925}
926
927/*!
928 \qmlproperty object QtWayland.Compositor::WaylandQuickItem::subsurfaceHandler
929
930 This property provides a way to override the default subsurface behavior.
931
932 By default, Qt will create a new SurfaceItem as a child of this item, and maintain the correct position.
933
934 To override the default, assign a handler object to this property. The handler should implement
935 a handleSubsurfaceAdded(WaylandSurface) function.
936
937 \code
938 ShellSurfaceItem {
939 subsurfaceHandler: QtObject {
940 function handleSubsurfaceAdded(child) {
941 // create custom surface item, and connect the subsurfacePositionChanged signal
942 }
943 }
944 }
945 \endcode
946
947 The default value of this property is \c null.
948 */
949
950
951QObject *QWaylandQuickItem::subsurfaceHandler() const
952{
953 Q_D(const QWaylandQuickItem);
954 return d->subsurfaceHandler.data();
955}
956
957void QWaylandQuickItem::setSubsurfaceHandler(QObject *handler)
958{
959 Q_D(QWaylandQuickItem);
960 if (d->subsurfaceHandler.data() != handler) {
961 d->subsurfaceHandler = handler;
962 emit subsurfaceHandlerChanged();
963 }
964}
965
966/*!
967 * \qmlproperty WaylandOutput QtWayland.Compositor::WaylandQuickItem::output
968 *
969 * This property holds the output on which this item is displayed.
970 */
971/*!
972 * \property QWaylandQuickItem::output
973 *
974 * This property holds the output on which this item is displayed.
975 */
976QWaylandOutput *QWaylandQuickItem::output() const
977{
978 Q_D(const QWaylandQuickItem);
979 return d->view->output();
980}
981
982void QWaylandQuickItem::setOutput(QWaylandOutput *output)
983{
984 Q_D(QWaylandQuickItem);
985 d->view->setOutput(output);
986}
987
988/*!
989 * \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::bufferLocked
990 *
991 * This property holds whether the item's buffer is currently locked. As long as
992 * the buffer is locked, it will not be released and returned to the client.
993 *
994 * The default is false.
995 */
996/*!
997 * \property QWaylandQuickItem::bufferLocked
998 *
999 * This property holds whether the item's buffer is currently locked. As long as
1000 * the buffer is locked, it will not be released and returned to the client.
1001 *
1002 * The default is false.
1003 */
1004bool QWaylandQuickItem::isBufferLocked() const
1005{
1006 Q_D(const QWaylandQuickItem);
1007 return d->view->isBufferLocked();
1008}
1009
1010void QWaylandQuickItem::setBufferLocked(bool locked)
1011{
1012 Q_D(QWaylandQuickItem);
1013 d->view->setBufferLocked(locked);
1014
1015 // Apply the latest surface size
1016 if (!locked)
1017 updateSize();
1018}
1019
1020/*!
1021 * \property QWaylandQuickItem::allowDiscardFrontBuffer
1022 *
1023 * By default, the item locks the current buffer until a new buffer is available
1024 * and updatePaintNode() is called. Set this property to true to allow Qt to release the buffer
1025 * immediately when the throttling view is no longer using it. This is useful for items that have
1026 * slow update intervals.
1027 */
1028bool QWaylandQuickItem::allowDiscardFrontBuffer() const
1029{
1030 Q_D(const QWaylandQuickItem);
1031 return d->view->allowDiscardFrontBuffer();
1032}
1033
1034void QWaylandQuickItem::setAllowDiscardFrontBuffer(bool discard)
1035{
1036 Q_D(QWaylandQuickItem);
1037 d->view->setAllowDiscardFrontBuffer(discard);
1038}
1039
1040/*!
1041 * \qmlmethod WaylandQuickItem::setPrimary()
1042 *
1043 * Makes this WaylandQuickItem the primary view for the surface.
1044 */
1045
1046/*!
1047 * Makes this QWaylandQuickItem's view the primary view for the surface.
1048 *
1049 * \sa QWaylandSurface::primaryView
1050 */
1051void QWaylandQuickItem::setPrimary()
1052{
1053 Q_D(QWaylandQuickItem);
1054 d->view->setPrimary();
1055}
1056
1057/*!
1058 * \internal
1059 */
1060void QWaylandQuickItem::handleSurfaceChanged()
1061{
1062 Q_D(QWaylandQuickItem);
1063 if (d->oldSurface) {
1064 disconnect(d->oldSurface.data(), &QWaylandSurface::hasContentChanged, this, &QWaylandQuickItem::surfaceMappedChanged);
1065 disconnect(d->oldSurface.data(), &QWaylandSurface::parentChanged, this, &QWaylandQuickItem::parentChanged);
1066 disconnect(d->oldSurface.data(), &QWaylandSurface::destinationSizeChanged, this, &QWaylandQuickItem::updateSize);
1067 disconnect(d->oldSurface.data(), &QWaylandSurface::bufferScaleChanged, this, &QWaylandQuickItem::updateSize);
1068 disconnect(d->oldSurface.data(), &QWaylandSurface::configure, this, &QWaylandQuickItem::updateBuffer);
1069 disconnect(d->oldSurface.data(), &QWaylandSurface::redraw, this, &QQuickItem::update);
1070 disconnect(d->oldSurface.data(), &QWaylandSurface::childAdded, this, &QWaylandQuickItem::handleSubsurfaceAdded);
1071 disconnect(d->oldSurface.data(), &QWaylandSurface::subsurfacePlaceAbove, this, &QWaylandQuickItem::handlePlaceAbove);
1072 disconnect(d->oldSurface.data(), &QWaylandSurface::subsurfacePlaceBelow, this, &QWaylandQuickItem::handlePlaceBelow);
1073#if QT_CONFIG(draganddrop)
1074 disconnect(d->oldSurface.data(), &QWaylandSurface::dragStarted, this, &QWaylandQuickItem::handleDragStarted);
1075#endif
1076#if QT_CONFIG(im)
1077 disconnect(d->oldSurface->inputMethodControl(), &QWaylandInputMethodControl::updateInputMethod, this, &QWaylandQuickItem::updateInputMethod);
1078#endif
1079 }
1080 if (QWaylandSurface *newSurface = d->view->surface()) {
1081 connect(newSurface, &QWaylandSurface::hasContentChanged, this, &QWaylandQuickItem::surfaceMappedChanged);
1082 connect(newSurface, &QWaylandSurface::parentChanged, this, &QWaylandQuickItem::parentChanged);
1083 connect(newSurface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandQuickItem::updateSize);
1084 connect(newSurface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandQuickItem::updateSize);
1085 connect(newSurface, &QWaylandSurface::configure, this, &QWaylandQuickItem::updateBuffer);
1086 connect(newSurface, &QWaylandSurface::redraw, this, &QQuickItem::update);
1087 connect(newSurface, &QWaylandSurface::childAdded, this, &QWaylandQuickItem::handleSubsurfaceAdded);
1088 connect(newSurface, &QWaylandSurface::subsurfacePlaceAbove, this, &QWaylandQuickItem::handlePlaceAbove);
1089 connect(newSurface, &QWaylandSurface::subsurfacePlaceBelow, this, &QWaylandQuickItem::handlePlaceBelow);
1090#if QT_CONFIG(draganddrop)
1091 connect(newSurface, &QWaylandSurface::dragStarted, this, &QWaylandQuickItem::handleDragStarted);
1092#endif
1093#if QT_CONFIG(im)
1094 connect(newSurface->inputMethodControl(), &QWaylandInputMethodControl::updateInputMethod, this, &QWaylandQuickItem::updateInputMethod);
1095#endif
1096
1097 if (newSurface->origin() != d->origin) {
1098 d->origin = newSurface->origin();
1099 emit originChanged();
1100 }
1101 if (window()) {
1102 QWaylandOutput *output = newSurface->compositor()->outputFor(window());
1103 d->view->setOutput(output);
1104 }
1105 for (auto subsurface : QWaylandSurfacePrivate::get(newSurface)->subsurfaceChildren) {
1106 if (!subsurface.isNull())
1107 handleSubsurfaceAdded(subsurface.data());
1108 }
1109
1110 updateSize();
1111 }
1112 surfaceChangedEvent(d->view->surface(), d->oldSurface);
1113 d->oldSurface = d->view->surface();
1114#if QT_CONFIG(im)
1115 updateInputMethod(Qt::ImQueryInput);
1116#endif
1117}
1118
1119/*!
1120 * Calling this function causes the item to take the focus of the
1121 * input \a device.
1122 */
1123void QWaylandQuickItem::takeFocus(QWaylandSeat *device)
1124{
1125 forceActiveFocus();
1126
1127 if (!surface() || !surface()->client())
1128 return;
1129
1130 QWaylandSeat *target = device;
1131 if (!target) {
1132 target = compositor()->defaultSeat();
1133 }
1134 target->setKeyboardFocus(surface());
1135
1136 qCDebug(qLcWaylandCompositorInputMethods) << Q_FUNC_INFO << " surface:" << surface()
1137 << ", client:" << surface()->client()
1138 << ", textinputprotocol:" << (int)(surface()->client()->textInputProtocols());
1139 if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::TextInputV2)) {
1140 QWaylandTextInput *textInput = QWaylandTextInput::findIn(target);
1141 if (textInput)
1142 textInput->setFocus(surface());
1143 }
1144
1145 if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::TextInputV3)) {
1146 QWaylandTextInputV3 *textInputV3 = QWaylandTextInputV3::findIn(target);
1147 if (textInputV3)
1148 textInputV3->setFocus(surface());
1149 }
1150
1151 if (surface()->client()->textInputProtocols().testFlag(QWaylandClient::TextInputProtocol::QtTextInputMethodV1)) {
1152 QWaylandQtTextInputMethod *textInputMethod = QWaylandQtTextInputMethod::findIn(target);
1153 if (textInputMethod)
1154 textInputMethod->setFocus(surface());
1155 }
1156}
1157
1158/*!
1159 * \internal
1160 */
1161void QWaylandQuickItem::surfaceMappedChanged()
1162{
1163 update();
1164}
1165
1166/*!
1167 * \internal
1168 */
1169void QWaylandQuickItem::parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent)
1170{
1171 Q_UNUSED(oldParent);
1172
1173 if (newParent) {
1174 setPaintEnabled(true);
1175 setVisible(true);
1176 setOpacity(1);
1177 setEnabled(true);
1178 }
1179}
1180
1181/*!
1182 * \internal
1183 */
1184void QWaylandQuickItem::updateSize()
1185{
1186 Q_D(QWaylandQuickItem);
1187
1188 // No resize if buffer is locked
1189 if (isBufferLocked()) {
1190 qWarning() << "No update on item size as the buffer is currently locked";
1191 return;
1192 }
1193
1194 QSize size(0, 0);
1195 if (surface())
1196 size = surface()->destinationSize() * d->scaleFactor();
1197
1198 setImplicitSize(size.width(), size.height());
1199}
1200
1201/*!
1202 * \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::focusOnClick
1203 *
1204 * This property specifies whether the WaylandQuickItem should take focus when
1205 * it is clicked or touched.
1206 *
1207 * The default is \c true.
1208 */
1209
1210/*!
1211 * \property QWaylandQuickItem::focusOnClick
1212 *
1213 * This property specifies whether the QWaylandQuickItem should take focus when
1214 * it is clicked or touched.
1215 *
1216 * The default is \c true.
1217 */
1218bool QWaylandQuickItem::focusOnClick() const
1219{
1220 Q_D(const QWaylandQuickItem);
1221 return d->focusOnClick;
1222}
1223
1224void QWaylandQuickItem::setFocusOnClick(bool focus)
1225{
1226 Q_D(QWaylandQuickItem);
1227 if (d->focusOnClick == focus)
1228 return;
1229
1230 d->focusOnClick = focus;
1231 emit focusOnClickChanged();
1232}
1233
1234/*!
1235 * Returns \c true if the input region of this item's surface contains the
1236 * position given by \a localPosition.
1237 */
1238bool QWaylandQuickItem::inputRegionContains(const QPointF &localPosition) const
1239{
1240 if (QWaylandSurface *s = surface())
1241 return s->inputRegionContains(mapToSurface(localPosition));
1242 return false;
1243}
1244
1245/*!
1246 * \qmlmethod point WaylandQuickItem::mapToSurface(point point)
1247 *
1248 * Maps the given \a point in this item's coordinate system to the equivalent
1249 * point within the Wayland surface's coordinate system, and returns the mapped
1250 * coordinate.
1251 */
1252
1253/*!
1254 * Maps the given \a point in this item's coordinate system to the equivalent
1255 * point within the Wayland surface's coordinate system, and returns the mapped
1256 * coordinate.
1257 */
1258QPointF QWaylandQuickItem::mapToSurface(const QPointF &point) const
1259{
1260 Q_D(const QWaylandQuickItem);
1261 if (!surface() || surface()->destinationSize().isEmpty())
1262 return point / d->scaleFactor();
1263
1264 qreal xScale = width() / surface()->destinationSize().width();
1265 qreal yScale = height() / surface()->destinationSize().height();
1266
1267 return QPointF(point.x() / xScale, point.y() / yScale);
1268}
1269
1270/*!
1271 * \qmlmethod point WaylandQuickItem::mapFromSurface(point point)
1272 * \since 5.13
1273 *
1274 * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
1275 * point within this item's coordinate system, and returns the mapped coordinate.
1276 */
1277
1278/*!
1279 * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
1280 * point within this item's coordinate system, and returns the mapped coordinate.
1281 *
1282 * \since 5.13
1283 */
1284QPointF QWaylandQuickItem::mapFromSurface(const QPointF &point) const
1285{
1286 Q_D(const QWaylandQuickItem);
1287 if (!surface() || surface()->destinationSize().isEmpty())
1288 return point * d->scaleFactor();
1289
1290 qreal xScale = width() / surface()->destinationSize().width();
1291 qreal yScale = height() / surface()->destinationSize().height();
1292
1293 return QPointF(point.x() * xScale, point.y() * yScale);
1294}
1295
1296#if QT_CONFIG(im)
1297QVariant QWaylandQuickItem::inputMethodQuery(Qt::InputMethodQuery query) const
1298{
1299 return inputMethodQuery(query, QVariant());
1300}
1301
1302QVariant QWaylandQuickItem::inputMethodQuery(Qt::InputMethodQuery query, QVariant argument) const
1303{
1304 Q_D(const QWaylandQuickItem);
1305
1306 if (query == Qt::ImEnabled)
1307 return QVariant((flags() & ItemAcceptsInputMethod) != 0);
1308
1309 if (d->oldSurface)
1310 return d->oldSurface->inputMethodControl()->inputMethodQuery(query, argument);
1311
1312 return QVariant();
1313}
1314#endif
1315
1316/*!
1317 \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::paintEnabled
1318
1319 Returns true if the item is hidden, though the texture
1320 is still updated. As opposed to hiding the item by
1321 setting \l{Item::visible}{visible} to \c false, setting this property to \c false
1322 will not prevent mouse or keyboard input from reaching item.
1323*/
1324
1325/*!
1326 \property QWaylandQuickItem::paintEnabled
1327
1328 Holds \c true if the item is hidden, though the texture
1329 is still updated. As opposed to hiding the item by
1330 setting \l{QQuickItem::}{visible} to \c false, setting this property to \c false
1331 will not prevent mouse or keyboard input from reaching item.
1332*/
1333bool QWaylandQuickItem::isPaintEnabled() const
1334{
1335 Q_D(const QWaylandQuickItem);
1336 return d->paintEnabled;
1337}
1338
1339void QWaylandQuickItem::setPaintEnabled(bool enabled)
1340{
1341 Q_D(QWaylandQuickItem);
1342
1343 if (enabled != d->paintEnabled) {
1344 d->paintEnabled = enabled;
1345 emit paintEnabledChanged();
1346 }
1347
1348 update();
1349}
1350
1351/*!
1352 \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::touchEventsEnabled
1353
1354 This property holds \c true if touch events are forwarded to the client
1355 surface, \c false otherwise.
1356*/
1357
1358/*!
1359 \property QWaylandQuickItem::touchEventsEnabled
1360
1361 This property holds \c true if touch events are forwarded to the client
1362 surface, \c false otherwise.
1363*/
1364bool QWaylandQuickItem::touchEventsEnabled() const
1365{
1366 Q_D(const QWaylandQuickItem);
1367 return d->touchEventsEnabled;
1368}
1369
1370void QWaylandQuickItem::updateBuffer(bool hasBuffer)
1371{
1372 Q_D(QWaylandQuickItem);
1373 Q_UNUSED(hasBuffer);
1374 if (d->origin != surface()->origin()) {
1375 d->origin = surface()->origin();
1376 emit originChanged();
1377 }
1378}
1379
1380void QWaylandQuickItem::updateWindow()
1381{
1382 Q_D(QWaylandQuickItem);
1383
1384 QQuickWindow *newWindow = window();
1385 if (newWindow == d->connectedWindow)
1386 return;
1387
1388 if (d->connectedWindow) {
1389 disconnect(d->connectedWindow, &QQuickWindow::beforeSynchronizing, this, &QWaylandQuickItem::beforeSync);
1390 disconnect(d->connectedWindow, &QQuickWindow::screenChanged, this, &QWaylandQuickItem::updateSize);
1391 }
1392
1393 d->connectedWindow = newWindow;
1394
1395 if (d->connectedWindow) {
1396 connect(d->connectedWindow, &QQuickWindow::beforeSynchronizing, this, &QWaylandQuickItem::beforeSync, Qt::DirectConnection);
1397 connect(d->connectedWindow, &QQuickWindow::screenChanged, this, &QWaylandQuickItem::updateSize); // new screen may have new dpr
1398
1399 if (compositor()) {
1400 QWaylandOutput *output = compositor()->outputFor(d->connectedWindow);
1401 Q_ASSERT(output);
1402 d->view->setOutput(output);
1403 }
1404
1405 updateSize(); // because scaleFactor depends on devicePixelRatio, which may be different for the new window
1406 }
1407}
1408
1409void QWaylandQuickItem::updateOutput()
1410{
1411 Q_D(QWaylandQuickItem);
1412 if (d->view->output() == d->connectedOutput)
1413 return;
1414
1415 if (d->connectedOutput)
1416 disconnect(d->connectedOutput, &QWaylandOutput::scaleFactorChanged, this, &QWaylandQuickItem::updateSize);
1417
1418 d->connectedOutput = d->view->output();
1419
1420 if (d->connectedOutput)
1421 connect(d->connectedOutput, &QWaylandOutput::scaleFactorChanged, this, &QWaylandQuickItem::updateSize);
1422
1423 updateSize();
1424}
1425
1426void QWaylandQuickItem::beforeSync()
1427{
1428 Q_D(QWaylandQuickItem);
1429 if (d->view->advance()) {
1430 d->newTexture = true;
1431 update();
1432 }
1433}
1434
1435#if QT_CONFIG(im)
1436void QWaylandQuickItem::updateInputMethod(Qt::InputMethodQueries queries)
1437{
1438 Q_D(QWaylandQuickItem);
1439
1440 setFlag(QQuickItem::ItemAcceptsInputMethod,
1441 d->oldSurface ? d->oldSurface->inputMethodControl()->enabled() : false);
1442 QQuickItem::updateInputMethod(queries | Qt::ImEnabled);
1443}
1444#endif
1445
1446/*!
1447 * \qmlsignal void QtWayland.Compositor::WaylandQuickItem::surfaceDestroyed()
1448 *
1449 * This signal is emitted when the client has destroyed the \c wl_surface associated
1450 * with the WaylandQuickItem. The handler for this signal is expected to either destroy the
1451 * WaylandQuickItem immediately or start a close animation and then destroy the Item.
1452 *
1453 * If an animation is started, bufferLocked should be set to ensure the item keeps its content
1454 * until the animation finishes
1455 *
1456 * \sa bufferLocked
1457 */
1458
1459/*!
1460 * \fn void QWaylandQuickItem::surfaceDestroyed()
1461 *
1462 * This signal is emitted when the client has destroyed the \c wl_surface associated
1463 * with the QWaylandQuickItem. The handler for this signal is expected to either destroy the
1464 * QWaylandQuickItem immediately or start a close animation and then destroy the Item.
1465 *
1466 * If an animation is started, bufferLocked should be set to ensure the item keeps its content
1467 * until the animation finishes
1468 *
1469 * \sa QWaylandQuickItem::bufferLocked
1470 */
1471
1472QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1473{
1474 Q_D(QWaylandQuickItem);
1475 d->lastMatrix = data->transformNode->combinedMatrix();
1476 const bool bufferHasContent = d->view->currentBuffer().hasContent();
1477
1478 if (d->view->isBufferLocked() && d->paintEnabled)
1479 return oldNode;
1480
1481 if (!bufferHasContent || !d->paintEnabled || !surface()) {
1482 delete oldNode;
1483 return nullptr;
1484 }
1485
1486 QWaylandBufferRef ref = d->view->currentBuffer();
1487 const bool invertY = ref.origin() == QWaylandSurface::OriginBottomLeft;
1488 const QRectF rect = invertY ? QRectF(0, height(), width(), -height())
1489 : QRectF(0, 0, width(), height());
1490
1491 if (ref.isSharedMemory()
1492#if QT_CONFIG(opengl)
1493 || bufferTypes[ref.bufferFormatEgl()].canProvideTexture
1494#endif
1495 ) {
1496#if QT_CONFIG(opengl)
1497 if (oldNode && !d->paintByProvider) {
1498 // Need to re-create a node
1499 delete oldNode;
1500 oldNode = nullptr;
1501 }
1502 d->paintByProvider = true;
1503#endif
1504 // This case could covered by the more general path below, but this is more efficient (especially when using ShaderEffect items).
1505 QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(oldNode);
1506
1507 if (!node) {
1508 node = new QSGSimpleTextureNode();
1509 if (smooth())
1510 node->setFiltering(QSGTexture::Linear);
1511 d->newTexture = true;
1512 }
1513
1514 if (!d->provider) {
1515 d->provider = new QWaylandSurfaceTextureProvider();
1516 if (compositor()) {
1517 d->texProviderConnection =
1518 QObject::connect(
1519 compositor(),
1520 &QObject::destroyed,
1521 this,
1522 [this](QObject*) {
1523 auto *itemPriv = QWaylandQuickItemPrivate::get(this);
1524 if (itemPriv->provider) {
1525 itemPriv->provider->deleteLater();
1526 itemPriv->provider = nullptr;
1527 }
1528 disconnect(itemPriv->texProviderConnection); }
1529 );
1530 }
1531 }
1532
1533 if (d->newTexture) {
1534 d->newTexture = false;
1535 d->provider->setBufferRef(this, ref);
1536 node->setTexture(d->provider->texture());
1537 }
1538
1539 d->provider->setSmooth(smooth());
1540 node->setRect(rect);
1541
1542 qreal scale = surface()->bufferScale();
1543 QRectF source = surface()->sourceGeometry();
1544 node->setSourceRect(QRectF(source.topLeft() * scale, source.size() * scale));
1545
1546 return node;
1547 }
1548
1549#if QT_CONFIG(opengl)
1550 Q_ASSERT(!d->provider);
1551
1552 if (oldNode && d->paintByProvider) {
1553 // Need to re-create a node
1554 delete oldNode;
1555 oldNode = nullptr;
1556 }
1557 d->paintByProvider = false;
1558
1559 QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode);
1560
1561 if (!node) {
1562 node = new QSGGeometryNode;
1563 d->newTexture = true;
1564 }
1565
1566 QSGGeometry *geometry = node->geometry();
1567 QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(node->material());
1568
1569 if (!geometry)
1570 geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
1571
1572 if (!material)
1573 material = new QWaylandBufferMaterial(ref.bufferFormatEgl());
1574
1575 if (d->newTexture) {
1576 d->newTexture = false;
1577 material->setBufferRef(this, ref);
1578 }
1579
1580 const QSize surfaceSize = ref.size() / surface()->bufferScale();
1581 const QRectF sourceGeometry = surface()->sourceGeometry();
1582 const QRectF normalizedCoordinates =
1583 sourceGeometry.isValid()
1584 ? QRectF(sourceGeometry.x() / surfaceSize.width(),
1585 sourceGeometry.y() / surfaceSize.height(),
1586 sourceGeometry.width() / surfaceSize.width(),
1587 sourceGeometry.height() / surfaceSize.height())
1588 : QRectF(0, 0, 1, 1);
1589
1590 QSGGeometry::updateTexturedRectGeometry(geometry, rect, normalizedCoordinates);
1591
1592 node->setGeometry(geometry);
1593 node->setFlag(QSGNode::OwnsGeometry, true);
1594
1595 node->setMaterial(material);
1596 node->setFlag(QSGNode::OwnsMaterial, true);
1597
1598 return node;
1599#else
1600 qCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported";
1601 return nullptr;
1602#endif // QT_CONFIG(opengl)
1603}
1604
1605void QWaylandQuickItem::setTouchEventsEnabled(bool enabled)
1606{
1607 Q_D(QWaylandQuickItem);
1608 if (d->touchEventsEnabled != enabled) {
1609 d->touchEventsEnabled = enabled;
1610 emit touchEventsEnabledChanged();
1611 }
1612}
1613
1614bool QWaylandQuickItem::inputEventsEnabled() const
1615{
1616 Q_D(const QWaylandQuickItem);
1617 return d->inputEventsEnabled;
1618}
1619
1620void QWaylandQuickItem::setInputEventsEnabled(bool enabled)
1621{
1622 Q_D(QWaylandQuickItem);
1623 if (d->inputEventsEnabled != enabled) {
1624 if (enabled)
1625 setEnabled(true);
1626 d->setInputEventsEnabled(enabled);
1627 emit inputEventsEnabledChanged();
1628 }
1629}
1630
1631void QWaylandQuickItem::lower()
1632{
1633 Q_D(QWaylandQuickItem);
1634 d->lower();
1635}
1636
1638{
1639 Q_Q(QWaylandQuickItem);
1640 QQuickItem *parent = q->parentItem();
1641 Q_ASSERT(parent);
1642 QQuickItem *bottom = parent->childItems().constFirst();
1643 if (q != bottom)
1644 q->stackBefore(bottom);
1645}
1646
1647void QWaylandQuickItem::raise()
1648{
1649 Q_D(QWaylandQuickItem);
1650 d->raise();
1651}
1652
1654{
1655 Q_Q(QWaylandQuickItem);
1656 QQuickItem *parent = q->parentItem();
1657 Q_ASSERT(parent);
1658 QQuickItem *top = parent->childItems().constLast();
1659 if (q != top)
1660 q->stackAfter(top);
1661}
1662
1663void QWaylandQuickItem::sendMouseMoveEvent(const QPointF &position, QWaylandSeat *seat)
1664{
1665 if (seat == nullptr)
1666 seat = compositor()->defaultSeat();
1667
1668 if (!seat) {
1669 qWarning() << "No seat, can't send mouse event";
1670 return;
1671 }
1672
1673 seat->sendMouseMoveEvent(view(), position);
1674}
1675
1676/*!
1677 * \internal
1678 *
1679 * Sets the position of this item relative to the parent item.
1680 */
1681void QWaylandQuickItem::handleSubsurfacePosition(const QPoint &pos)
1682{
1683 Q_D(QWaylandQuickItem);
1684 QQuickItem::setPosition(pos * d->scaleFactor());
1685}
1686
1687#if QT_CONFIG(draganddrop)
1688void QWaylandQuickItem::handleDragStarted(QWaylandDrag *drag)
1689{
1690 Q_D(QWaylandQuickItem);
1691 Q_ASSERT(drag->origin() == surface());
1692 drag->seat()->setMouseFocus(nullptr);
1693 d->isDragging = true;
1694}
1695#endif
1696
1698{
1699 qreal f = view->output() ? view->output()->scaleFactor() : 1;
1700#if !defined(Q_OS_MACOS)
1701 if (window)
1702 f /= window->devicePixelRatio();
1703#endif
1704 return f;
1705}
1706
1707QWaylandQuickItem *QWaylandQuickItemPrivate::findSibling(QWaylandSurface *surface) const
1708{
1709 Q_Q(const QWaylandQuickItem);
1710 auto *parent = q->parentItem();
1711 if (!parent)
1712 return nullptr;
1713
1714 const auto siblings = q->parentItem()->childItems();
1715 for (auto *sibling : siblings) {
1716 auto *waylandItem = qobject_cast<QWaylandQuickItem *>(sibling);
1717 if (waylandItem && waylandItem->surface() == surface)
1718 return waylandItem;
1719 }
1720 return nullptr;
1721}
1722
1723void QWaylandQuickItemPrivate::placeAboveSibling(QWaylandQuickItem *sibling)
1724{
1725 Q_Q(QWaylandQuickItem);
1726 q->stackAfter(sibling);
1727 q->setZ(sibling->z());
1728 belowParent = sibling->d_func()->belowParent;
1729}
1730
1731void QWaylandQuickItemPrivate::placeBelowSibling(QWaylandQuickItem *sibling)
1732{
1733 Q_Q(QWaylandQuickItem);
1734 q->stackBefore(sibling);
1735 q->setZ(sibling->z());
1736 belowParent = sibling->d_func()->belowParent;
1737}
1738
1739//### does not handle changes in z value if parent is a subsurface
1741{
1742 Q_Q(QWaylandQuickItem);
1743 const auto siblings = q->parentItem()->childItems();
1744
1745 // Stack below first (bottom) sibling above parent
1746 bool foundSibling = false;
1747 for (auto it = siblings.cbegin(); it != siblings.cend(); ++it) {
1748 QWaylandQuickItem *sibling = qobject_cast<QWaylandQuickItem*>(*it);
1749 if (sibling && !sibling->d_func()->belowParent) {
1750 q->stackBefore(sibling);
1751 foundSibling = true;
1752 break;
1753 }
1754 }
1755
1756 // No other subsurfaces above parent
1757 if (!foundSibling && siblings.last() != q)
1758 q->stackAfter(siblings.last());
1759
1760 q->setZ(q->parentItem()->z());
1761 belowParent = false;
1762}
1763
1764//### does not handle changes in z value if parent is a subsurface
1766{
1767 Q_Q(QWaylandQuickItem);
1768 const auto siblings = q->parentItem()->childItems();
1769
1770 // Stack above last (top) sibling below parent
1771 bool foundSibling = false;
1772 for (auto it = siblings.crbegin(); it != siblings.crend(); ++it) {
1773 QWaylandQuickItem *sibling = qobject_cast<QWaylandQuickItem*>(*it);
1774 if (sibling && sibling->d_func()->belowParent) {
1775 q->stackAfter(sibling);
1776 foundSibling = true;
1777 break;
1778 }
1779 }
1780
1781 // No other subsurfaces below parent
1782 if (!foundSibling && siblings.first() != q)
1783 q->stackBefore(siblings.first());
1784
1785 q->setZ(q->parentItem()->z() - 1.0);
1786 belowParent = true;
1787}
1788
1789QT_END_NAMESPACE
1790
1791#include "moc_qwaylandquickitem.cpp"
QSGTexture * texture() const override
Returns a pointer to the texture object.
void setBufferRef(QWaylandQuickItem *surfaceItem, const QWaylandBufferRef &buffer)