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#include "qwaylandtextinputv3.h"
10#include "qwaylandqttextinputmethod.h"
11#include "qwaylandquickoutput.h"
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 { .vertexShaderSourceFile: "", .fragmentShaderSourceFile: "", .planeCount: 0, .canProvideTexture: false, .materialFlags: {}, .materialType: {} },
62
63 // BufferFormatEgl_RGB (GL_TEXTURE_2D)
64 {
65 .vertexShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
66 .fragmentShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface_rgbx.frag.qsb",
67 .planeCount: 1, .canProvideTexture: true,
68 .materialFlags: QSGMaterial::Blending,
69 .materialType: {}
70 },
71
72 // BufferFormatEgl_RGBA (GL_TEXTURE_2D)
73 {
74 .vertexShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
75 .fragmentShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface_rgba.frag.qsb",
76 .planeCount: 1, .canProvideTexture: true,
77 .materialFlags: QSGMaterial::Blending,
78 .materialType: {}
79 },
80
81 // BufferFormatEgl_EXTERNAL_OES (GL_TEXTURE_EXTERNAL_OES)
82 {
83 .vertexShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
84 .fragmentShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface_oes_external.frag",
85 .planeCount: 1, .canProvideTexture: false,
86 .materialFlags: QSGMaterial::Blending,
87 .materialType: {}
88 },
89
90 // BufferFormatEgl_Y_U_V (GL_TEXTURE_2D)
91 {
92 .vertexShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
93 .fragmentShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface_y_u_v.frag.qsb",
94 .planeCount: 3, .canProvideTexture: false,
95 .materialFlags: QSGMaterial::Blending,
96 .materialType: {}
97 },
98
99 // BufferFormatEgl_Y_UV (GL_TEXTURE_2D)
100 {
101 .vertexShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
102 .fragmentShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface_y_uv.frag.qsb",
103 .planeCount: 2, .canProvideTexture: false,
104 .materialFlags: QSGMaterial::Blending,
105 .materialType: {}
106 },
107
108 // BufferFormatEgl_Y_XUXV (GL_TEXTURE_2D)
109 {
110 .vertexShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface.vert.qsb",
111 .fragmentShaderSourceFile: ":/qt-project.org/wayland/compositor/shaders/surface_y_xuxv.frag.qsb",
112 .planeCount: 2, .canProvideTexture: false,
113 .materialFlags: QSGMaterial::Blending,
114 .materialType: {}
115 }
116};
117
118QWaylandBufferMaterialShader::QWaylandBufferMaterialShader(QWaylandBufferRef::BufferFormatEgl format)
119{
120 Q_UNUSED(format);
121 setShaderFileName(stage: VertexStage, filename: QString::fromLatin1(ba: bufferTypes[format].vertexShaderSourceFile));
122 auto fragmentShaderSourceFile = QString::fromLatin1(ba: bufferTypes[format].fragmentShaderSourceFile);
123
124 if (format == QWaylandBufferRef::BufferFormatEgl_EXTERNAL_OES)
125 setupExternalOESShader(fragmentShaderSourceFile);
126 else
127 setShaderFileName(stage: FragmentStage, filename: fragmentShaderSourceFile);
128}
129
130void QWaylandBufferMaterialShader::setupExternalOESShader(const QString &shaderFilename)
131{
132#if QT_CONFIG(opengl)
133 QFile shaderFile(shaderFilename);
134 if (!shaderFile.open(flags: 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: &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(key: QShaderKey(QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs)), shader: QShaderCode(fsGLES));
198 shaderPack.setShader(key: QShaderKey(QShader::GlslShader, QShaderVersion(120)), shader: QShaderCode(fsGL));
199
200 setShader(stage: FragmentStage, shader: 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(dest: buf->data(), src: m.constData(), n: 64);
215 changed = true;
216 }
217
218 if (state.isOpacityDirty()) {
219 const float opacity = state.opacity();
220 memcpy(dest: buf->data() + 64, src: &opacity, n: 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(idx: 0);
236 break;
237 case 2:
238 *texture = material->m_scenegraphTextures.at(idx: 1);
239 break;
240 case 3:
241 *texture = material->m_scenegraphTextures.at(idx: 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(rhi: state.rhi(), resourceUpdates: state.resourceUpdateBatch());
252}
253
254QWaylandBufferMaterial::QWaylandBufferMaterial(QWaylandBufferRef::BufferFormatEgl format)
255 : m_format(format)
256{
257 setFlag(flags: bufferTypes[m_format].materialFlags);
258}
259
260QWaylandBufferMaterial::~QWaylandBufferMaterial()
261{
262 qDeleteAll(c: 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(msg: "plane index is out of range");
271 return;
272 }
273
274 texture->bind();
275 setTextureParameters(texture->target());
276
277 ensureTextures(count: 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(count: bufferTypes[m_format].planeCount);
293
294 switch (m_textures.size()) {
295 case 3:
296 if (m_textures[2])
297 m_textures[2]->bind(unit: 2);
298 Q_FALLTHROUGH();
299 case 2:
300 if (m_textures[1])
301 m_textures[1]->bind(unit: 1);
302 Q_FALLTHROUGH();
303 case 1:
304 if (m_textures[0])
305 m_textures[0]->bind(unit: 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 *>(object: 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(textureId: texture->textureId(),
351 window: surfaceItem->window(),
352 size: ref.size(),
353 options: opt);
354 } else {
355 scenegraphTexture = QNativeInterface::QSGOpenGLTexture::fromNative(textureId: texture->textureId(),
356 window: surfaceItem->window(),
357 size: ref.size(),
358 options: 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
371class QWaylandSurfaceTextureProvider : public QSGTextureProvider
372{
373public:
374 QWaylandSurfaceTextureProvider()
375 {
376 }
377
378 ~QWaylandSurfaceTextureProvider() override
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(image: buffer.image());
392 } else {
393#if QT_CONFIG(opengl)
394 QQuickWindow::CreateTextureOptions opt;
395 QWaylandQuickSurface *surface = qobject_cast<QWaylandQuickSurface *>(object: 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, window: surfaceItem->window(), size, options: 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 *>(object: q->view()->output());
431 //TODO: also check if dragging onto other outputs
432 QWaylandQuickItem *targetItem = qobject_cast<QWaylandQuickItem *>(object: currentOutput->pickClickableItem(position: q->mapToScene(point: globalPosition)));
433 QWaylandSurface *targetSurface = targetItem ? targetItem->surface() : nullptr;
434 if (targetSurface) {
435 QPointF position = q->mapToItem(item: targetItem, point: globalPosition);
436 QPointF surfacePosition = targetItem->mapToSurface(point: position);
437 seat->drag()->dragMove(target: targetSurface, pos: 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(sender: this, signal: &QQuickItem::activeFocusChanged, context: this, slot: &QWaylandQuickItem::updateFocus);
494}
495
496/*!
497 * Destroy the QWaylandQuickItem.
498 */
499QWaylandQuickItem::~QWaylandQuickItem()
500{
501 Q_D(QWaylandQuickItem);
502 disconnect(sender: this, signal: &QQuickItem::windowChanged, receiver: this, slot: &QWaylandQuickItem::updateWindow);
503 disconnect(sender: this, signal: &QQuickItem::activeFocusChanged, receiver: this, slot: &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(localPosition: event->position())) {
619 event->ignore();
620 return;
621 }
622
623 QWaylandSeat *seat = compositor()->seatFor(inputEvent: event);
624
625 if (d->focusOnClick)
626 takeFocus(device: seat);
627
628 seat->sendMouseMoveEvent(surface: d->view.data(), localPos: mapToSurface(point: event->position()), outputSpacePos: event->scenePosition());
629 seat->sendMousePressEvent(button: 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(inputEvent: event);
641 if (d->isDragging) {
642 d->handleDragUpdate(seat, globalPosition: event->position());
643 } else {
644 seat->sendMouseMoveEvent(surface: d->view.data(), localPos: mapToSurface(point: event->position()), outputSpacePos: event->scenePosition());
645 d->hoverPos = event->position();
646 }
647 } else {
648 emit mouseMove(windowPosition: 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(inputEvent: event);
661 if (d->isDragging)
662 d->handleDragEnded(seat);
663 else
664 seat->sendMouseReleaseEvent(button: 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(localPosition: event->position())) {
678 event->ignore();
679 return;
680 }
681 if (d->shouldSendInputEvents()) {
682 QWaylandSeat *seat = compositor()->seatFor(inputEvent: event);
683 seat->sendMouseMoveEvent(surface: d->view.data(), localPos: event->position(), outputSpacePos: mapToScene(point: 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(localPosition: event->position())) {
698 event->ignore();
699 return;
700 }
701 }
702 if (d->shouldSendInputEvents()) {
703 QWaylandSeat *seat = compositor()->seatFor(inputEvent: event);
704 if (event->position() != d->hoverPos) {
705 seat->sendMouseMoveEvent(surface: d->view.data(), localPos: mapToSurface(point: event->position()), outputSpacePos: mapToScene(point: 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(inputEvent: event);
721 seat->setMouseFocus(nullptr);
722 } else {
723 event->ignore();
724 }
725}
726
727#if QT_CONFIG(wheelevent)
728/*!
729 * \internal
730 */
731void QWaylandQuickItem::wheelEvent(QWheelEvent *event)
732{
733 Q_D(QWaylandQuickItem);
734 if (d->shouldSendInputEvents()) {
735 if (!inputRegionContains(localPosition: event->position())) {
736 event->ignore();
737 return;
738 }
739
740 QWaylandSeat *seat = compositor()->seatFor(inputEvent: event);
741 // TODO: fix this to send a single event, when diagonal scrolling is supported
742 if (event->angleDelta().x() != 0)
743 seat->sendMouseWheelEvent(orientation: Qt::Horizontal, delta: event->angleDelta().x());
744 if (event->angleDelta().y() != 0)
745 seat->sendMouseWheelEvent(orientation: Qt::Vertical, delta: event->angleDelta().y());
746 } else {
747 event->ignore();
748 }
749}
750#endif
751
752/*!
753 * \internal
754 */
755void QWaylandQuickItem::keyPressEvent(QKeyEvent *event)
756{
757 Q_D(QWaylandQuickItem);
758 if (d->shouldSendInputEvents()) {
759 QWaylandSeat *seat = compositor()->seatFor(inputEvent: event);
760 if (seat->setKeyboardFocus(d->view->surface()))
761 seat->sendFullKeyEvent(event);
762 else
763 qWarning() << "Unable to set keyboard focus, cannot send key press event";
764 } else {
765 event->ignore();
766 }
767}
768
769/*!
770 * \internal
771 */
772void QWaylandQuickItem::keyReleaseEvent(QKeyEvent *event)
773{
774 Q_D(QWaylandQuickItem);
775 if (d->shouldSendInputEvents()) {
776 QWaylandSeat *seat = compositor()->seatFor(inputEvent: event);
777 seat->sendFullKeyEvent(event);
778 } else {
779 event->ignore();
780 }
781}
782
783/*!
784 * \internal
785 */
786void QWaylandQuickItem::touchEvent(QTouchEvent *event)
787{
788 Q_D(QWaylandQuickItem);
789 if (d->shouldSendInputEvents() && d->touchEventsEnabled) {
790 QWaylandSeat *seat = compositor()->seatFor(inputEvent: event);
791
792 QPointF pointPos;
793 const QList<QTouchEvent::TouchPoint> &points = event->points();
794 if (!points.isEmpty())
795 pointPos = points.at(i: 0).position();
796
797 if (event->type() == QEvent::TouchBegin && !inputRegionContains(localPosition: pointPos)) {
798 event->ignore();
799 return;
800 }
801
802 if (event->type() == QEvent::TouchUpdate && d->isDragging)
803 d->handleDragUpdate(seat, globalPosition: pointPos);
804
805 event->accept();
806 if (seat->mouseFocus() != d->view.data()) {
807 seat->sendMouseMoveEvent(surface: d->view.data(), localPos: pointPos, outputSpacePos: mapToScene(point: pointPos));
808 }
809 seat->sendFullTouchEvent(surface: surface(), event);
810
811 if (event->type() == QEvent::TouchBegin) {
812 d->touchingSeats.append(t: seat);
813 } else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) {
814 if (d->isDragging)
815 d->handleDragEnded(seat);
816 d->touchingSeats.removeOne(t: seat);
817 }
818
819 if (event->type() == QEvent::TouchBegin && d->focusOnClick)
820 takeFocus(device: seat);
821 } else {
822 event->ignore();
823 }
824}
825
826void QWaylandQuickItem::touchUngrabEvent()
827{
828 Q_D(QWaylandQuickItem);
829
830 if (d->shouldSendInputEvents())
831 for (auto seat : d->touchingSeats)
832 seat->sendTouchCancelEvent(client: surface()->client());
833
834 d->touchingSeats.clear();
835}
836
837#if QT_CONFIG(im)
838/*!
839 * \internal
840 */
841void QWaylandQuickItem::inputMethodEvent(QInputMethodEvent *event)
842{
843 Q_D(QWaylandQuickItem);
844 if (d->shouldSendInputEvents()) {
845 d->oldSurface->inputMethodControl()->inputMethodEvent(event);
846 } else {
847 event->ignore();
848 }
849}
850#endif
851
852/*!
853 * \internal
854 */
855void QWaylandQuickItem::surfaceChangedEvent(QWaylandSurface *newSurface, QWaylandSurface *oldSurface)
856{
857 Q_UNUSED(newSurface);
858 Q_UNUSED(oldSurface);
859}
860
861void QWaylandQuickItem::handleSubsurfaceAdded(QWaylandSurface *childSurface)
862{
863 Q_D(QWaylandQuickItem);
864 if (d->subsurfaceHandler.isNull()) {
865 QWaylandQuickItem *childItem = new QWaylandQuickItem;
866 childItem->setSurface(childSurface);
867 childItem->setVisible(true);
868 childItem->setParentItem(this);
869 childItem->setParent(this);
870 connect(sender: childSurface, signal: &QWaylandSurface::subsurfacePositionChanged, context: childItem, slot: &QWaylandQuickItem::handleSubsurfacePosition);
871 connect(sender: childSurface, signal: &QWaylandSurface::destroyed, context: childItem, slot: &QObject::deleteLater);
872 } else {
873 bool success = QMetaObject::invokeMethod(obj: d->subsurfaceHandler, member: "handleSubsurfaceAdded", Q_ARG(QWaylandSurface *, childSurface));
874 if (!success)
875 success = QMetaObject::invokeMethod(obj: d->subsurfaceHandler, member: "handleSubsurfaceAdded",
876 Q_ARG(QVariant, QVariant::fromValue(childSurface)));
877
878 if (!success)
879 qWarning(msg: "QWaylandQuickItem: subsurfaceHandler does not implement handleSubsurfaceAdded()");
880 }
881}
882
883void QWaylandQuickItem::handlePlaceAbove(QWaylandSurface *referenceSurface)
884{
885 Q_D(QWaylandQuickItem);
886 auto *parent = qobject_cast<QWaylandQuickItem*>(object: parentItem());
887 if (!parent)
888 return;
889
890 if (parent->surface() == referenceSurface) {
891 d->placeAboveParent();
892 } else if (auto *sibling = d->findSibling(surface: referenceSurface)) {
893 d->placeAboveSibling(sibling);
894 } else {
895 qWarning() << "Couldn't find QWaylandQuickItem for surface" << referenceSurface
896 << "when handling wl_subsurface.place_above";
897 }
898}
899
900void QWaylandQuickItem::handlePlaceBelow(QWaylandSurface *referenceSurface)
901{
902 Q_D(QWaylandQuickItem);
903 QWaylandQuickItem *parent = qobject_cast<QWaylandQuickItem*>(object: parentItem());
904 if (!parent)
905 return;
906
907 if (parent->surface() == referenceSurface) {
908 d->placeBelowParent();
909 } else if (auto *sibling = d->findSibling(surface: referenceSurface)) {
910 d->placeBelowSibling(sibling);
911 } else {
912 qWarning() << "Couldn't find QWaylandQuickItem for surface" << referenceSurface
913 << "when handling wl_subsurface.place_below";
914 }
915}
916
917void QWaylandQuickItem::updateFocus()
918{
919 Q_D(const QWaylandQuickItem);
920 if (hasActiveFocus() && compositor())
921 compositor()->defaultSeat()->setKeyboardFocus(d->view->surface());
922}
923
924/*!
925 \qmlproperty object QtWayland.Compositor::WaylandQuickItem::subsurfaceHandler
926
927 This property provides a way to override the default subsurface behavior.
928
929 By default, Qt will create a new SurfaceItem as a child of this item, and maintain the correct position.
930
931 To override the default, assign a handler object to this property. The handler should implement
932 a handleSubsurfaceAdded(WaylandSurface) function.
933
934 \code
935 ShellSurfaceItem {
936 subsurfaceHandler: QtObject {
937 function handleSubsurfaceAdded(child) {
938 // create custom surface item, and connect the subsurfacePositionChanged signal
939 }
940 }
941 }
942 \endcode
943
944 The default value of this property is \c null.
945 */
946
947
948QObject *QWaylandQuickItem::subsurfaceHandler() const
949{
950 Q_D(const QWaylandQuickItem);
951 return d->subsurfaceHandler.data();
952}
953
954void QWaylandQuickItem::setSubsurfaceHandler(QObject *handler)
955{
956 Q_D(QWaylandQuickItem);
957 if (d->subsurfaceHandler.data() != handler) {
958 d->subsurfaceHandler = handler;
959 emit subsurfaceHandlerChanged();
960 }
961}
962
963/*!
964 * \qmlproperty WaylandOutput QtWayland.Compositor::WaylandQuickItem::output
965 *
966 * This property holds the output on which this item is displayed.
967 */
968/*!
969 * \property QWaylandQuickItem::output
970 *
971 * This property holds the output on which this item is displayed.
972 */
973QWaylandOutput *QWaylandQuickItem::output() const
974{
975 Q_D(const QWaylandQuickItem);
976 return d->view->output();
977}
978
979void QWaylandQuickItem::setOutput(QWaylandOutput *output)
980{
981 Q_D(QWaylandQuickItem);
982 d->view->setOutput(output);
983}
984
985/*!
986 * \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::bufferLocked
987 *
988 * This property holds whether the item's buffer is currently locked. As long as
989 * the buffer is locked, it will not be released and returned to the client.
990 *
991 * The default is false.
992 */
993/*!
994 * \property QWaylandQuickItem::bufferLocked
995 *
996 * This property holds whether the item's buffer is currently locked. As long as
997 * the buffer is locked, it will not be released and returned to the client.
998 *
999 * The default is false.
1000 */
1001bool QWaylandQuickItem::isBufferLocked() const
1002{
1003 Q_D(const QWaylandQuickItem);
1004 return d->view->isBufferLocked();
1005}
1006
1007void QWaylandQuickItem::setBufferLocked(bool locked)
1008{
1009 Q_D(QWaylandQuickItem);
1010 d->view->setBufferLocked(locked);
1011
1012 // Apply the latest surface size
1013 if (!locked)
1014 updateSize();
1015}
1016
1017/*!
1018 * \property QWaylandQuickItem::allowDiscardFrontBuffer
1019 *
1020 * By default, the item locks the current buffer until a new buffer is available
1021 * and updatePaintNode() is called. Set this property to true to allow Qt to release the buffer
1022 * immediately when the throttling view is no longer using it. This is useful for items that have
1023 * slow update intervals.
1024 */
1025bool QWaylandQuickItem::allowDiscardFrontBuffer() const
1026{
1027 Q_D(const QWaylandQuickItem);
1028 return d->view->allowDiscardFrontBuffer();
1029}
1030
1031void QWaylandQuickItem::setAllowDiscardFrontBuffer(bool discard)
1032{
1033 Q_D(QWaylandQuickItem);
1034 d->view->setAllowDiscardFrontBuffer(discard);
1035}
1036
1037/*!
1038 * \qmlmethod WaylandQuickItem::setPrimary()
1039 *
1040 * Makes this WaylandQuickItem the primary view for the surface.
1041 */
1042
1043/*!
1044 * Makes this QWaylandQuickItem's view the primary view for the surface.
1045 *
1046 * \sa QWaylandSurface::primaryView
1047 */
1048void QWaylandQuickItem::setPrimary()
1049{
1050 Q_D(QWaylandQuickItem);
1051 d->view->setPrimary();
1052}
1053
1054/*!
1055 * \internal
1056 */
1057void QWaylandQuickItem::handleSurfaceChanged()
1058{
1059 Q_D(QWaylandQuickItem);
1060 if (d->oldSurface) {
1061 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::hasContentChanged, receiver: this, slot: &QWaylandQuickItem::surfaceMappedChanged);
1062 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::parentChanged, receiver: this, slot: &QWaylandQuickItem::parentChanged);
1063 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::destinationSizeChanged, receiver: this, slot: &QWaylandQuickItem::updateSize);
1064 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::bufferScaleChanged, receiver: this, slot: &QWaylandQuickItem::updateSize);
1065 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::configure, receiver: this, slot: &QWaylandQuickItem::updateBuffer);
1066 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::redraw, receiver: this, slot: &QQuickItem::update);
1067 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::childAdded, receiver: this, slot: &QWaylandQuickItem::handleSubsurfaceAdded);
1068 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::subsurfacePlaceAbove, receiver: this, slot: &QWaylandQuickItem::handlePlaceAbove);
1069 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::subsurfacePlaceBelow, receiver: this, slot: &QWaylandQuickItem::handlePlaceBelow);
1070#if QT_CONFIG(draganddrop)
1071 disconnect(sender: d->oldSurface.data(), signal: &QWaylandSurface::dragStarted, receiver: this, slot: &QWaylandQuickItem::handleDragStarted);
1072#endif
1073#if QT_CONFIG(im)
1074 disconnect(sender: d->oldSurface->inputMethodControl(), signal: &QWaylandInputMethodControl::updateInputMethod, receiver: this, slot: &QWaylandQuickItem::updateInputMethod);
1075#endif
1076 }
1077 if (QWaylandSurface *newSurface = d->view->surface()) {
1078 connect(sender: newSurface, signal: &QWaylandSurface::hasContentChanged, context: this, slot: &QWaylandQuickItem::surfaceMappedChanged);
1079 connect(sender: newSurface, signal: &QWaylandSurface::parentChanged, context: this, slot: &QWaylandQuickItem::parentChanged);
1080 connect(sender: newSurface, signal: &QWaylandSurface::destinationSizeChanged, context: this, slot: &QWaylandQuickItem::updateSize);
1081 connect(sender: newSurface, signal: &QWaylandSurface::bufferScaleChanged, context: this, slot: &QWaylandQuickItem::updateSize);
1082 connect(sender: newSurface, signal: &QWaylandSurface::configure, context: this, slot: &QWaylandQuickItem::updateBuffer);
1083 connect(sender: newSurface, signal: &QWaylandSurface::redraw, context: this, slot: &QQuickItem::update);
1084 connect(sender: newSurface, signal: &QWaylandSurface::childAdded, context: this, slot: &QWaylandQuickItem::handleSubsurfaceAdded);
1085 connect(sender: newSurface, signal: &QWaylandSurface::subsurfacePlaceAbove, context: this, slot: &QWaylandQuickItem::handlePlaceAbove);
1086 connect(sender: newSurface, signal: &QWaylandSurface::subsurfacePlaceBelow, context: this, slot: &QWaylandQuickItem::handlePlaceBelow);
1087#if QT_CONFIG(draganddrop)
1088 connect(sender: newSurface, signal: &QWaylandSurface::dragStarted, context: this, slot: &QWaylandQuickItem::handleDragStarted);
1089#endif
1090#if QT_CONFIG(im)
1091 connect(sender: newSurface->inputMethodControl(), signal: &QWaylandInputMethodControl::updateInputMethod, context: this, slot: &QWaylandQuickItem::updateInputMethod);
1092#endif
1093
1094 if (newSurface->origin() != d->origin) {
1095 d->origin = newSurface->origin();
1096 emit originChanged();
1097 }
1098 if (window()) {
1099 QWaylandOutput *output = newSurface->compositor()->outputFor(window: window());
1100 d->view->setOutput(output);
1101 }
1102 for (auto subsurface : QWaylandSurfacePrivate::get(surface: newSurface)->subsurfaceChildren) {
1103 if (!subsurface.isNull())
1104 handleSubsurfaceAdded(childSurface: subsurface.data());
1105 }
1106
1107 updateSize();
1108 }
1109 surfaceChangedEvent(newSurface: d->view->surface(), oldSurface: d->oldSurface);
1110 d->oldSurface = d->view->surface();
1111#if QT_CONFIG(im)
1112 updateInputMethod(queries: Qt::ImQueryInput);
1113#endif
1114}
1115
1116/*!
1117 * Calling this function causes the item to take the focus of the
1118 * input \a device.
1119 */
1120void QWaylandQuickItem::takeFocus(QWaylandSeat *device)
1121{
1122 forceActiveFocus();
1123
1124 if (!surface() || !surface()->client())
1125 return;
1126
1127 QWaylandSeat *target = device;
1128 if (!target) {
1129 target = compositor()->defaultSeat();
1130 }
1131 target->setKeyboardFocus(surface());
1132
1133 qCDebug(qLcWaylandCompositorInputMethods) << Q_FUNC_INFO << " surface:" << surface()
1134 << ", client:" << surface()->client()
1135 << ", textinputprotocol:" << (int)(surface()->client()->textInputProtocols());
1136 if (surface()->client()->textInputProtocols().testFlag(flag: QWaylandClient::TextInputProtocol::TextInputV2)) {
1137 QWaylandTextInput *textInput = QWaylandTextInput::findIn(container: target);
1138 if (textInput)
1139 textInput->setFocus(surface());
1140 }
1141
1142 if (surface()->client()->textInputProtocols().testFlag(flag: QWaylandClient::TextInputProtocol::TextInputV3)) {
1143 QWaylandTextInputV3 *textInputV3 = QWaylandTextInputV3::findIn(container: target);
1144 if (textInputV3)
1145 textInputV3->setFocus(surface());
1146 }
1147
1148 if (surface()->client()->textInputProtocols().testFlag(flag: QWaylandClient::TextInputProtocol::QtTextInputMethodV1)) {
1149 QWaylandQtTextInputMethod *textInputMethod = QWaylandQtTextInputMethod::findIn(container: target);
1150 if (textInputMethod)
1151 textInputMethod->setFocus(surface());
1152 }
1153}
1154
1155/*!
1156 * \internal
1157 */
1158void QWaylandQuickItem::surfaceMappedChanged()
1159{
1160 update();
1161}
1162
1163/*!
1164 * \internal
1165 */
1166void QWaylandQuickItem::parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent)
1167{
1168 Q_UNUSED(oldParent);
1169
1170 if (newParent) {
1171 setPaintEnabled(true);
1172 setVisible(true);
1173 setOpacity(1);
1174 setEnabled(true);
1175 }
1176}
1177
1178/*!
1179 * \internal
1180 */
1181void QWaylandQuickItem::updateSize()
1182{
1183 Q_D(QWaylandQuickItem);
1184
1185 // No resize if buffer is locked
1186 if (isBufferLocked()) {
1187 qWarning() << "No update on item size as the buffer is currently locked";
1188 return;
1189 }
1190
1191 QSize size(0, 0);
1192 if (surface())
1193 size = surface()->destinationSize() * d->scaleFactor();
1194
1195 setImplicitSize(size.width(), size.height());
1196}
1197
1198/*!
1199 * \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::focusOnClick
1200 *
1201 * This property specifies whether the WaylandQuickItem should take focus when
1202 * it is clicked or touched.
1203 *
1204 * The default is \c true.
1205 */
1206
1207/*!
1208 * \property QWaylandQuickItem::focusOnClick
1209 *
1210 * This property specifies whether the QWaylandQuickItem should take focus when
1211 * it is clicked or touched.
1212 *
1213 * The default is \c true.
1214 */
1215bool QWaylandQuickItem::focusOnClick() const
1216{
1217 Q_D(const QWaylandQuickItem);
1218 return d->focusOnClick;
1219}
1220
1221void QWaylandQuickItem::setFocusOnClick(bool focus)
1222{
1223 Q_D(QWaylandQuickItem);
1224 if (d->focusOnClick == focus)
1225 return;
1226
1227 d->focusOnClick = focus;
1228 emit focusOnClickChanged();
1229}
1230
1231/*!
1232 * Returns \c true if the input region of this item's surface contains the
1233 * position given by \a localPosition.
1234 */
1235bool QWaylandQuickItem::inputRegionContains(const QPointF &localPosition) const
1236{
1237 if (QWaylandSurface *s = surface())
1238 return s->inputRegionContains(position: mapToSurface(point: localPosition));
1239 return false;
1240}
1241
1242/*!
1243 * \qmlmethod point WaylandQuickItem::mapToSurface(point point)
1244 *
1245 * Maps the given \a point in this item's coordinate system to the equivalent
1246 * point within the Wayland surface's coordinate system, and returns the mapped
1247 * coordinate.
1248 */
1249
1250/*!
1251 * Maps the given \a point in this item's coordinate system to the equivalent
1252 * point within the Wayland surface's coordinate system, and returns the mapped
1253 * coordinate.
1254 */
1255QPointF QWaylandQuickItem::mapToSurface(const QPointF &point) const
1256{
1257 Q_D(const QWaylandQuickItem);
1258 if (!surface() || surface()->destinationSize().isEmpty())
1259 return point / d->scaleFactor();
1260
1261 qreal xScale = width() / surface()->destinationSize().width();
1262 qreal yScale = height() / surface()->destinationSize().height();
1263
1264 return QPointF(point.x() / xScale, point.y() / yScale);
1265}
1266
1267/*!
1268 * \qmlmethod point WaylandQuickItem::mapFromSurface(point point)
1269 * \since 5.13
1270 *
1271 * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
1272 * point within this item's coordinate system, and returns the mapped coordinate.
1273 */
1274
1275/*!
1276 * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
1277 * point within this item's coordinate system, and returns the mapped coordinate.
1278 *
1279 * \since 5.13
1280 */
1281QPointF QWaylandQuickItem::mapFromSurface(const QPointF &point) const
1282{
1283 Q_D(const QWaylandQuickItem);
1284 if (!surface() || surface()->destinationSize().isEmpty())
1285 return point * d->scaleFactor();
1286
1287 qreal xScale = width() / surface()->destinationSize().width();
1288 qreal yScale = height() / surface()->destinationSize().height();
1289
1290 return QPointF(point.x() * xScale, point.y() * yScale);
1291}
1292
1293#if QT_CONFIG(im)
1294QVariant QWaylandQuickItem::inputMethodQuery(Qt::InputMethodQuery query) const
1295{
1296 return inputMethodQuery(query, argument: QVariant());
1297}
1298
1299QVariant QWaylandQuickItem::inputMethodQuery(Qt::InputMethodQuery query, QVariant argument) const
1300{
1301 Q_D(const QWaylandQuickItem);
1302
1303 if (query == Qt::ImEnabled)
1304 return QVariant((flags() & ItemAcceptsInputMethod) != 0);
1305
1306 if (d->oldSurface)
1307 return d->oldSurface->inputMethodControl()->inputMethodQuery(query, argument);
1308
1309 return QVariant();
1310}
1311#endif
1312
1313/*!
1314 \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::paintEnabled
1315
1316 Returns true if the item is hidden, though the texture
1317 is still updated. As opposed to hiding the item by
1318 setting \l{Item::visible}{visible} to \c false, setting this property to \c false
1319 will not prevent mouse or keyboard input from reaching item.
1320*/
1321
1322/*!
1323 \property QWaylandQuickItem::paintEnabled
1324
1325 Holds \c true if the item is hidden, though the texture
1326 is still updated. As opposed to hiding the item by
1327 setting \l{QQuickItem::}{visible} to \c false, setting this property to \c false
1328 will not prevent mouse or keyboard input from reaching item.
1329*/
1330bool QWaylandQuickItem::isPaintEnabled() const
1331{
1332 Q_D(const QWaylandQuickItem);
1333 return d->paintEnabled;
1334}
1335
1336void QWaylandQuickItem::setPaintEnabled(bool enabled)
1337{
1338 Q_D(QWaylandQuickItem);
1339
1340 if (enabled != d->paintEnabled) {
1341 d->paintEnabled = enabled;
1342 emit paintEnabledChanged();
1343 }
1344
1345 update();
1346}
1347
1348/*!
1349 \qmlproperty bool QtWayland.Compositor::WaylandQuickItem::touchEventsEnabled
1350
1351 This property holds \c true if touch events are forwarded to the client
1352 surface, \c false otherwise.
1353*/
1354
1355/*!
1356 \property QWaylandQuickItem::touchEventsEnabled
1357
1358 This property holds \c true if touch events are forwarded to the client
1359 surface, \c false otherwise.
1360*/
1361bool QWaylandQuickItem::touchEventsEnabled() const
1362{
1363 Q_D(const QWaylandQuickItem);
1364 return d->touchEventsEnabled;
1365}
1366
1367void QWaylandQuickItem::updateBuffer(bool hasBuffer)
1368{
1369 Q_D(QWaylandQuickItem);
1370 Q_UNUSED(hasBuffer);
1371 if (d->origin != surface()->origin()) {
1372 d->origin = surface()->origin();
1373 emit originChanged();
1374 }
1375}
1376
1377void QWaylandQuickItem::updateWindow()
1378{
1379 Q_D(QWaylandQuickItem);
1380
1381 QQuickWindow *newWindow = window();
1382 if (newWindow == d->connectedWindow)
1383 return;
1384
1385 if (d->connectedWindow) {
1386 disconnect(sender: d->connectedWindow, signal: &QQuickWindow::beforeSynchronizing, receiver: this, slot: &QWaylandQuickItem::beforeSync);
1387 disconnect(sender: d->connectedWindow, signal: &QQuickWindow::screenChanged, receiver: this, slot: &QWaylandQuickItem::updateSize);
1388 }
1389
1390 d->connectedWindow = newWindow;
1391
1392 if (d->connectedWindow) {
1393 connect(sender: d->connectedWindow, signal: &QQuickWindow::beforeSynchronizing, context: this, slot: &QWaylandQuickItem::beforeSync, type: Qt::DirectConnection);
1394 connect(sender: d->connectedWindow, signal: &QQuickWindow::screenChanged, context: this, slot: &QWaylandQuickItem::updateSize); // new screen may have new dpr
1395
1396 if (compositor()) {
1397 QWaylandOutput *output = compositor()->outputFor(window: d->connectedWindow);
1398 Q_ASSERT(output);
1399 d->view->setOutput(output);
1400 }
1401
1402 updateSize(); // because scaleFactor depends on devicePixelRatio, which may be different for the new window
1403 }
1404}
1405
1406void QWaylandQuickItem::updateOutput()
1407{
1408 Q_D(QWaylandQuickItem);
1409 if (d->view->output() == d->connectedOutput)
1410 return;
1411
1412 if (d->connectedOutput)
1413 disconnect(sender: d->connectedOutput, signal: &QWaylandOutput::scaleFactorChanged, receiver: this, slot: &QWaylandQuickItem::updateSize);
1414
1415 d->connectedOutput = d->view->output();
1416
1417 if (d->connectedOutput)
1418 connect(sender: d->connectedOutput, signal: &QWaylandOutput::scaleFactorChanged, context: this, slot: &QWaylandQuickItem::updateSize);
1419
1420 updateSize();
1421}
1422
1423void QWaylandQuickItem::beforeSync()
1424{
1425 Q_D(QWaylandQuickItem);
1426 if (d->view->advance()) {
1427 d->newTexture = true;
1428 update();
1429 }
1430}
1431
1432#if QT_CONFIG(im)
1433void QWaylandQuickItem::updateInputMethod(Qt::InputMethodQueries queries)
1434{
1435 Q_D(QWaylandQuickItem);
1436
1437 setFlag(flag: QQuickItem::ItemAcceptsInputMethod,
1438 enabled: d->oldSurface ? d->oldSurface->inputMethodControl()->enabled() : false);
1439 QQuickItem::updateInputMethod(queries: queries | Qt::ImEnabled);
1440}
1441#endif
1442
1443/*!
1444 * \qmlsignal void QtWayland.Compositor::WaylandQuickItem::surfaceDestroyed()
1445 *
1446 * This signal is emitted when the client has destroyed the \c wl_surface associated
1447 * with the WaylandQuickItem. The handler for this signal is expected to either destroy the
1448 * WaylandQuickItem immediately or start a close animation and then destroy the Item.
1449 *
1450 * If an animation is started, bufferLocked should be set to ensure the item keeps its content
1451 * until the animation finishes
1452 *
1453 * \sa bufferLocked
1454 */
1455
1456/*!
1457 * \fn void QWaylandQuickItem::surfaceDestroyed()
1458 *
1459 * This signal is emitted when the client has destroyed the \c wl_surface associated
1460 * with the QWaylandQuickItem. The handler for this signal is expected to either destroy the
1461 * QWaylandQuickItem immediately or start a close animation and then destroy the Item.
1462 *
1463 * If an animation is started, bufferLocked should be set to ensure the item keeps its content
1464 * until the animation finishes
1465 *
1466 * \sa QWaylandQuickItem::bufferLocked
1467 */
1468
1469QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1470{
1471 Q_D(QWaylandQuickItem);
1472 d->lastMatrix = data->transformNode->combinedMatrix();
1473 const bool bufferHasContent = d->view->currentBuffer().hasContent();
1474
1475 if (d->view->isBufferLocked() && d->paintEnabled)
1476 return oldNode;
1477
1478 if (!bufferHasContent || !d->paintEnabled || !surface()) {
1479 delete oldNode;
1480 return nullptr;
1481 }
1482
1483 QWaylandBufferRef ref = d->view->currentBuffer();
1484 const bool invertY = ref.origin() == QWaylandSurface::OriginBottomLeft;
1485 const QRectF rect = invertY ? QRectF(0, height(), width(), -height())
1486 : QRectF(0, 0, width(), height());
1487
1488 if (ref.isSharedMemory()
1489#if QT_CONFIG(opengl)
1490 || bufferTypes[ref.bufferFormatEgl()].canProvideTexture
1491#endif
1492 ) {
1493#if QT_CONFIG(opengl)
1494 if (oldNode && !d->paintByProvider) {
1495 // Need to re-create a node
1496 delete oldNode;
1497 oldNode = nullptr;
1498 }
1499 d->paintByProvider = true;
1500#endif
1501 // This case could covered by the more general path below, but this is more efficient (especially when using ShaderEffect items).
1502 QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(oldNode);
1503
1504 if (!node) {
1505 node = new QSGSimpleTextureNode();
1506 if (smooth())
1507 node->setFiltering(QSGTexture::Linear);
1508 d->newTexture = true;
1509 }
1510
1511 if (!d->provider) {
1512 d->provider = new QWaylandSurfaceTextureProvider();
1513 if (compositor()) {
1514 d->texProviderConnection =
1515 QObject::connect(
1516 sender: compositor(),
1517 signal: &QObject::destroyed,
1518 context: this,
1519 slot: [this](QObject*) {
1520 auto *itemPriv = QWaylandQuickItemPrivate::get(item: this);
1521 if (itemPriv->provider) {
1522 itemPriv->provider->deleteLater();
1523 itemPriv->provider = nullptr;
1524 }
1525 disconnect(itemPriv->texProviderConnection); }
1526 );
1527 }
1528 }
1529
1530 if (d->newTexture) {
1531 d->newTexture = false;
1532 d->provider->setBufferRef(surfaceItem: this, buffer: ref);
1533 node->setTexture(d->provider->texture());
1534 }
1535
1536 d->provider->setSmooth(smooth());
1537 node->setRect(rect);
1538
1539 qreal scale = surface()->bufferScale();
1540 QRectF source = surface()->sourceGeometry();
1541 node->setSourceRect(QRectF(source.topLeft() * scale, source.size() * scale));
1542
1543 return node;
1544 }
1545
1546#if QT_CONFIG(opengl)
1547 Q_ASSERT(!d->provider);
1548
1549 if (oldNode && d->paintByProvider) {
1550 // Need to re-create a node
1551 delete oldNode;
1552 oldNode = nullptr;
1553 }
1554 d->paintByProvider = false;
1555
1556 QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode);
1557
1558 if (!node) {
1559 node = new QSGGeometryNode;
1560 d->newTexture = true;
1561 }
1562
1563 QSGGeometry *geometry = node->geometry();
1564 QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(node->material());
1565
1566 if (!geometry)
1567 geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
1568
1569 if (!material)
1570 material = new QWaylandBufferMaterial(ref.bufferFormatEgl());
1571
1572 if (d->newTexture) {
1573 d->newTexture = false;
1574 material->setBufferRef(surfaceItem: this, ref);
1575 }
1576
1577 const QSize surfaceSize = ref.size() / surface()->bufferScale();
1578 const QRectF sourceGeometry = surface()->sourceGeometry();
1579 const QRectF normalizedCoordinates =
1580 sourceGeometry.isValid()
1581 ? QRectF(sourceGeometry.x() / surfaceSize.width(),
1582 sourceGeometry.y() / surfaceSize.height(),
1583 sourceGeometry.width() / surfaceSize.width(),
1584 sourceGeometry.height() / surfaceSize.height())
1585 : QRectF(0, 0, 1, 1);
1586
1587 QSGGeometry::updateTexturedRectGeometry(g: geometry, rect, sourceRect: normalizedCoordinates);
1588
1589 node->setGeometry(geometry);
1590 node->setFlag(QSGNode::OwnsGeometry, true);
1591
1592 node->setMaterial(material);
1593 node->setFlag(QSGNode::OwnsMaterial, true);
1594
1595 return node;
1596#else
1597 qCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported";
1598 return nullptr;
1599#endif // QT_CONFIG(opengl)
1600}
1601
1602void QWaylandQuickItem::setTouchEventsEnabled(bool enabled)
1603{
1604 Q_D(QWaylandQuickItem);
1605 if (d->touchEventsEnabled != enabled) {
1606 d->touchEventsEnabled = enabled;
1607 emit touchEventsEnabledChanged();
1608 }
1609}
1610
1611bool QWaylandQuickItem::inputEventsEnabled() const
1612{
1613 Q_D(const QWaylandQuickItem);
1614 return d->inputEventsEnabled;
1615}
1616
1617void QWaylandQuickItem::setInputEventsEnabled(bool enabled)
1618{
1619 Q_D(QWaylandQuickItem);
1620 if (d->inputEventsEnabled != enabled) {
1621 if (enabled)
1622 setEnabled(true);
1623 d->setInputEventsEnabled(enabled);
1624 emit inputEventsEnabledChanged();
1625 }
1626}
1627
1628void QWaylandQuickItem::lower()
1629{
1630 Q_D(QWaylandQuickItem);
1631 d->lower();
1632}
1633
1634void QWaylandQuickItemPrivate::lower()
1635{
1636 Q_Q(QWaylandQuickItem);
1637 QQuickItem *parent = q->parentItem();
1638 Q_ASSERT(parent);
1639 QQuickItem *bottom = parent->childItems().constFirst();
1640 if (q != bottom)
1641 q->stackBefore(bottom);
1642}
1643
1644void QWaylandQuickItem::raise()
1645{
1646 Q_D(QWaylandQuickItem);
1647 d->raise();
1648}
1649
1650void QWaylandQuickItemPrivate::raise()
1651{
1652 Q_Q(QWaylandQuickItem);
1653 QQuickItem *parent = q->parentItem();
1654 Q_ASSERT(parent);
1655 QQuickItem *top = parent->childItems().constLast();
1656 if (q != top)
1657 q->stackAfter(top);
1658}
1659
1660void QWaylandQuickItem::sendMouseMoveEvent(const QPointF &position, QWaylandSeat *seat)
1661{
1662 if (seat == nullptr)
1663 seat = compositor()->defaultSeat();
1664
1665 if (!seat) {
1666 qWarning() << "No seat, can't send mouse event";
1667 return;
1668 }
1669
1670 seat->sendMouseMoveEvent(surface: view(), localPos: position);
1671}
1672
1673/*!
1674 * \internal
1675 *
1676 * Sets the position of this item relative to the parent item.
1677 */
1678void QWaylandQuickItem::handleSubsurfacePosition(const QPoint &pos)
1679{
1680 Q_D(QWaylandQuickItem);
1681 QQuickItem::setPosition(pos * d->scaleFactor());
1682}
1683
1684#if QT_CONFIG(draganddrop)
1685void QWaylandQuickItem::handleDragStarted(QWaylandDrag *drag)
1686{
1687 Q_D(QWaylandQuickItem);
1688 Q_ASSERT(drag->origin() == surface());
1689 drag->seat()->setMouseFocus(nullptr);
1690 d->isDragging = true;
1691}
1692#endif
1693
1694qreal QWaylandQuickItemPrivate::scaleFactor() const
1695{
1696 qreal f = view->output() ? view->output()->scaleFactor() : 1;
1697#if !defined(Q_OS_MACOS)
1698 if (window)
1699 f /= window->devicePixelRatio();
1700#endif
1701 return f;
1702}
1703
1704QWaylandQuickItem *QWaylandQuickItemPrivate::findSibling(QWaylandSurface *surface) const
1705{
1706 Q_Q(const QWaylandQuickItem);
1707 auto *parent = q->parentItem();
1708 if (!parent)
1709 return nullptr;
1710
1711 const auto siblings = q->parentItem()->childItems();
1712 for (auto *sibling : siblings) {
1713 auto *waylandItem = qobject_cast<QWaylandQuickItem *>(object: sibling);
1714 if (waylandItem && waylandItem->surface() == surface)
1715 return waylandItem;
1716 }
1717 return nullptr;
1718}
1719
1720void QWaylandQuickItemPrivate::placeAboveSibling(QWaylandQuickItem *sibling)
1721{
1722 Q_Q(QWaylandQuickItem);
1723 q->stackAfter(sibling);
1724 q->setZ(sibling->z());
1725 belowParent = sibling->d_func()->belowParent;
1726}
1727
1728void QWaylandQuickItemPrivate::placeBelowSibling(QWaylandQuickItem *sibling)
1729{
1730 Q_Q(QWaylandQuickItem);
1731 q->stackBefore(sibling);
1732 q->setZ(sibling->z());
1733 belowParent = sibling->d_func()->belowParent;
1734}
1735
1736//### does not handle changes in z value if parent is a subsurface
1737void QWaylandQuickItemPrivate::placeAboveParent()
1738{
1739 Q_Q(QWaylandQuickItem);
1740 const auto siblings = q->parentItem()->childItems();
1741
1742 // Stack below first (bottom) sibling above parent
1743 bool foundSibling = false;
1744 for (auto it = siblings.cbegin(); it != siblings.cend(); ++it) {
1745 QWaylandQuickItem *sibling = qobject_cast<QWaylandQuickItem*>(object: *it);
1746 if (sibling && !sibling->d_func()->belowParent) {
1747 q->stackBefore(sibling);
1748 foundSibling = true;
1749 break;
1750 }
1751 }
1752
1753 // No other subsurfaces above parent
1754 if (!foundSibling && siblings.last() != q)
1755 q->stackAfter(siblings.last());
1756
1757 q->setZ(q->parentItem()->z());
1758 belowParent = false;
1759}
1760
1761//### does not handle changes in z value if parent is a subsurface
1762void QWaylandQuickItemPrivate::placeBelowParent()
1763{
1764 Q_Q(QWaylandQuickItem);
1765 const auto siblings = q->parentItem()->childItems();
1766
1767 // Stack above last (top) sibling below parent
1768 bool foundSibling = false;
1769 for (auto it = siblings.crbegin(); it != siblings.crend(); ++it) {
1770 QWaylandQuickItem *sibling = qobject_cast<QWaylandQuickItem*>(object: *it);
1771 if (sibling && sibling->d_func()->belowParent) {
1772 q->stackAfter(sibling);
1773 foundSibling = true;
1774 break;
1775 }
1776 }
1777
1778 // No other subsurfaces below parent
1779 if (!foundSibling && siblings.first() != q)
1780 q->stackBefore(siblings.first());
1781
1782 q->setZ(q->parentItem()->z() - 1.0);
1783 belowParent = true;
1784}
1785
1786QT_END_NAMESPACE
1787
1788#include "moc_qwaylandquickitem.cpp"
1789

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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