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

source code of qtwayland/src/compositor/compositor_api/qwaylandquickitem.cpp