1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickwindow.h"
41#include "qquickwindow_p.h"
42
43#include "qquickitem.h"
44#include "qquickitem_p.h"
45#include "qquickevents_p_p.h"
46
47#if QT_CONFIG(quick_draganddrop)
48#include <private/qquickdrag_p.h>
49#endif
50#include <private/qquickhoverhandler_p.h>
51#include <private/qquickpointerhandler_p.h>
52
53#include <QtQuick/private/qsgrenderer_p.h>
54#include <QtQuick/private/qsgplaintexture_p.h>
55#include <private/qsgrenderloop_p.h>
56#include <private/qsgrhisupport_p.h>
57#include <private/qquickrendercontrol_p.h>
58#include <private/qquickanimatorcontroller_p.h>
59#include <private/qquickprofiler_p.h>
60
61#include <private/qguiapplication_p.h>
62#include <QtGui/QInputMethod>
63
64#include <private/qabstractanimation_p.h>
65
66#include <QtGui/qpainter.h>
67#include <QtGui/qevent.h>
68#include <QtGui/qmatrix4x4.h>
69#include <QtGui/qpa/qplatformtheme.h>
70#include <QtCore/qvarlengtharray.h>
71#include <QtCore/qabstractanimation.h>
72#include <QtCore/QLibraryInfo>
73#include <QtCore/QRunnable>
74#include <QtQml/qqmlincubator.h>
75#include <QtQml/qqmlinfo.h>
76#include <QtQml/private/qqmlmetatype_p.h>
77
78#include <QtQuick/private/qquickpixmapcache_p.h>
79
80#include <private/qqmldebugserviceinterfaces_p.h>
81#include <private/qqmldebugconnector_p.h>
82#if QT_CONFIG(opengl)
83# include <private/qopenglvertexarrayobject_p.h>
84# include <private/qsgdefaultrendercontext_p.h>
85#endif
86#ifndef QT_NO_DEBUG_STREAM
87#include <private/qdebug_p.h>
88#endif
89
90#include <QtGui/private/qrhi_p.h>
91
92QT_BEGIN_NAMESPACE
93
94Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch")
95Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target")
96Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse")
97Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target")
98Q_LOGGING_CATEGORY(lcTablet, "qt.quick.tablet")
99Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target")
100Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target")
101Q_LOGGING_CATEGORY(DBG_HOVER_TRACE, "qt.quick.hover.trace")
102Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus")
103Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty")
104Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
105
106extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
107extern Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1);
108
109bool QQuickWindowPrivate::defaultAlphaBuffer = false;
110
111#if defined(QT_QUICK_DEFAULT_TEXT_RENDER_TYPE)
112QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QT_QUICK_DEFAULT_TEXT_RENDER_TYPE;
113#else
114QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QtTextRendering;
115#endif
116
117void QQuickWindowPrivate::updateFocusItemTransform()
118{
119#if QT_CONFIG(im)
120 Q_Q(QQuickWindow);
121 QQuickItem *focus = q->activeFocusItem();
122 if (focus && QGuiApplication::focusObject() == focus) {
123 QQuickItemPrivate *focusPrivate = QQuickItemPrivate::get(item: focus);
124 QGuiApplication::inputMethod()->setInputItemTransform(focusPrivate->itemToWindowTransform());
125 QGuiApplication::inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height));
126 focus->updateInputMethod(queries: Qt::ImInputItemClipRectangle);
127 }
128#endif
129}
130
131class QQuickWindowIncubationController : public QObject, public QQmlIncubationController
132{
133 Q_OBJECT
134
135public:
136 QQuickWindowIncubationController(QSGRenderLoop *loop)
137 : m_renderLoop(loop), m_timer(0)
138 {
139 // Allow incubation for 1/3 of a frame.
140 m_incubation_time = qMax(a: 1, b: int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3);
141
142 QAnimationDriver *animationDriver = m_renderLoop->animationDriver();
143 if (animationDriver) {
144 connect(sender: animationDriver, SIGNAL(stopped()), receiver: this, SLOT(animationStopped()));
145 connect(sender: m_renderLoop, SIGNAL(timeToIncubate()), receiver: this, SLOT(incubate()));
146 }
147 }
148
149protected:
150 void timerEvent(QTimerEvent *) override
151 {
152 killTimer(id: m_timer);
153 m_timer = 0;
154 incubate();
155 }
156
157 void incubateAgain() {
158 if (m_timer == 0) {
159 // Wait for a while before processing the next batch. Using a
160 // timer to avoid starvation of system events.
161 m_timer = startTimer(interval: m_incubation_time);
162 }
163 }
164
165public slots:
166 void incubate() {
167 if (m_renderLoop && incubatingObjectCount()) {
168 if (m_renderLoop->interleaveIncubation()) {
169 incubateFor(msecs: m_incubation_time);
170 } else {
171 incubateFor(msecs: m_incubation_time * 2);
172 if (incubatingObjectCount())
173 incubateAgain();
174 }
175 }
176 }
177
178 void animationStopped() { incubate(); }
179
180protected:
181 void incubatingObjectCountChanged(int count) override
182 {
183 if (count && m_renderLoop && !m_renderLoop->interleaveIncubation())
184 incubateAgain();
185 }
186
187private:
188 QPointer<QSGRenderLoop> m_renderLoop;
189 int m_incubation_time;
190 int m_timer;
191};
192
193#include "qquickwindow.moc"
194#include "moc_qquickwindow_p.cpp"
195
196
197#if QT_CONFIG(accessibility)
198/*!
199 Returns an accessibility interface for this window, or 0 if such an
200 interface cannot be created.
201*/
202QAccessibleInterface *QQuickWindow::accessibleRoot() const
203{
204 return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this));
205}
206#endif
207
208
209/*
210Focus behavior
211==============
212
213Prior to being added to a valid window items can set and clear focus with no
214effect. Only once items are added to a window (by way of having a parent set that
215already belongs to a window) do the focus rules apply. Focus goes back to
216having no effect if an item is removed from a window.
217
218When an item is moved into a new focus scope (either being added to a window
219for the first time, or having its parent changed), if the focus scope already has
220a scope focused item that takes precedence over the item being added. Otherwise,
221the focus of the added tree is used. In the case of a tree of items being
222added to a window for the first time, which may have a conflicted focus state (two
223or more items in one scope having focus set), the same rule is applied item by item -
224thus the first item that has focus will get it (assuming the scope doesn't already
225have a scope focused item), and the other items will have their focus cleared.
226*/
227
228QQuickRootItem::QQuickRootItem()
229{
230}
231
232/*! \reimp */
233void QQuickWindow::exposeEvent(QExposeEvent *)
234{
235 Q_D(QQuickWindow);
236 if (d->windowManager)
237 d->windowManager->exposureChanged(window: this);
238}
239
240/*! \reimp */
241void QQuickWindow::resizeEvent(QResizeEvent *ev)
242{
243 Q_D(QQuickWindow);
244 if (d->contentItem)
245 d->contentItem->setSize(ev->size());
246 if (d->windowManager)
247 d->windowManager->resize(this);
248}
249
250/*! \reimp */
251void QQuickWindow::showEvent(QShowEvent *)
252{
253 Q_D(QQuickWindow);
254 if (d->windowManager)
255 d->windowManager->show(window: this);
256}
257
258/*! \reimp */
259void QQuickWindow::hideEvent(QHideEvent *)
260{
261 Q_D(QQuickWindow);
262 if (d->windowManager)
263 d->windowManager->hide(window: this);
264}
265
266/*! \reimp */
267void QQuickWindow::focusOutEvent(QFocusEvent *ev)
268{
269 Q_D(QQuickWindow);
270 if (d->contentItem)
271 d->contentItem->setFocus(focus: false, reason: ev->reason());
272}
273
274/*! \reimp */
275void QQuickWindow::focusInEvent(QFocusEvent *ev)
276{
277 Q_D(QQuickWindow);
278 if (d->contentItem)
279 d->contentItem->setFocus(focus: true, reason: ev->reason());
280 d->updateFocusItemTransform();
281}
282
283#if QT_CONFIG(im)
284static bool transformDirtyOnItemOrAncestor(const QQuickItem *item)
285{
286 while (item) {
287 if (QQuickItemPrivate::get(item)->dirtyAttributes & (
288 QQuickItemPrivate::TransformOrigin |
289 QQuickItemPrivate::Transform |
290 QQuickItemPrivate::BasicTransform |
291 QQuickItemPrivate::Position |
292 QQuickItemPrivate::Size |
293 QQuickItemPrivate::ParentChanged |
294 QQuickItemPrivate::Clip)) {
295 return true;
296 }
297 item = item->parentItem();
298 }
299 return false;
300}
301#endif
302
303/*!
304 * \internal
305
306 A "polish loop" can occur inside QQuickWindowPrivate::polishItems(). It is when an item calls
307 polish() on an(other?) item from updatePolish(). If this anomaly happens repeatedly and without
308 interruption (of a well-behaved updatePolish() that doesn't call polish()), it is a strong
309 indication that we are heading towards an infinite polish loop. A polish loop is not a bug in
310 Qt Quick - it is a bug caused by ill-behaved items put in the scene.
311
312 We can detect this sequence of polish loops easily, since the
313 QQuickWindowPrivate::itemsToPolish is basically a stack: polish() will push to it, and
314 polishItems() will pop from it.
315 Therefore if updatePolish() calls polish(), the immediate next item polishItems() processes is
316 the item that was polished by the previous call to updatePolish().
317 We therefore just need to count the number of polish loops we detected in _sequence_.
318*/
319struct PolishLoopDetector
320{
321 PolishLoopDetector(const QVector<QQuickItem*> &itemsToPolish)
322 : itemsToPolish(itemsToPolish)
323 {
324 }
325
326 /*
327 * returns true when it detected a likely infinite loop
328 * (suggests it should abort the polish loop)
329 **/
330 bool check(QQuickItem *item, int itemsRemainingBeforeUpdatePolish)
331 {
332 if (itemsToPolish.count() > itemsRemainingBeforeUpdatePolish) {
333 // Detected potential polish loop.
334 ++numPolishLoopsInSequence;
335 if (numPolishLoopsInSequence >= 1000) {
336 // Start to warn about polish loop after 1000 consecutive polish loops
337 if (numPolishLoopsInSequence == 100000) {
338 // We have looped 100,000 times without actually reducing the list of items to
339 // polish, give up for now.
340 // This is not a fix, just a remedy so that the application can be somewhat
341 // responsive.
342 numPolishLoopsInSequence = 0;
343 return true;
344 } else if (numPolishLoopsInSequence < 1005) {
345 // Show the 5 next items involved in the polish loop.
346 // (most likely they will be the same 5 items...)
347 QQuickItem *guiltyItem = itemsToPolish.last();
348 qmlWarning(me: item) << "possible QQuickItem::polish() loop";
349
350 auto typeAndObjectName = [](QQuickItem *item) {
351 QString typeName = QQmlMetaType::prettyTypeName(object: item);
352 QString objName = item->objectName();
353 if (!objName.isNull())
354 return QLatin1String("%1(%2)").arg(args&: typeName, args&: objName);
355 return typeName;
356 };
357
358 qmlWarning(me: guiltyItem) << typeAndObjectName(guiltyItem)
359 << " called polish() inside updatePolish() of " << typeAndObjectName(item);
360
361 if (numPolishLoopsInSequence == 1004)
362 // Enough warnings. Reset counter in order to speed things up and re-detect
363 // more loops
364 numPolishLoopsInSequence = 0;
365 }
366 }
367 } else {
368 numPolishLoopsInSequence = 0;
369 }
370 return false;
371 }
372 const QVector<QQuickItem*> &itemsToPolish; // Just a ref to the one in polishItems()
373 int numPolishLoopsInSequence = 0;
374};
375
376void QQuickWindowPrivate::polishItems()
377{
378 // An item can trigger polish on another item, or itself for that matter,
379 // during its updatePolish() call. Because of this, we cannot simply
380 // iterate through the set, we must continue pulling items out until it
381 // is empty.
382 // In the case where polish is called from updatePolish() either directly
383 // or indirectly, we use a PolishLoopDetector to determine if a warning should
384 // be printed to the user.
385
386 PolishLoopDetector polishLoopDetector(itemsToPolish);
387 while (!itemsToPolish.isEmpty()) {
388 QQuickItem *item = itemsToPolish.takeLast();
389 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
390 itemPrivate->polishScheduled = false;
391 const int itemsRemaining = itemsToPolish.count();
392 itemPrivate->updatePolish();
393 item->updatePolish();
394 if (polishLoopDetector.check(item, itemsRemainingBeforeUpdatePolish: itemsRemaining) == true)
395 break;
396 }
397
398#if QT_CONFIG(im)
399 if (QQuickItem *focusItem = q_func()->activeFocusItem()) {
400 // If the current focus item, or any of its anchestors, has changed location
401 // inside the window, we need inform IM about it. This to ensure that overlays
402 // such as selection handles will be updated.
403 const bool isActiveFocusItem = (focusItem == QGuiApplication::focusObject());
404 const bool hasImEnabled = focusItem->inputMethodQuery(query: Qt::ImEnabled).toBool();
405 if (isActiveFocusItem && hasImEnabled && transformDirtyOnItemOrAncestor(item: focusItem))
406 updateFocusItemTransform();
407 }
408#endif
409}
410
411/*!
412 * Schedules the window to render another frame.
413 *
414 * Calling QQuickWindow::update() differs from QQuickItem::update() in that
415 * it always triggers a repaint, regardless of changes in the underlying
416 * scene graph or not.
417 */
418void QQuickWindow::update()
419{
420 Q_D(QQuickWindow);
421 if (d->windowManager)
422 d->windowManager->update(window: this);
423 else if (d->renderControl)
424 QQuickRenderControlPrivate::get(renderControl: d->renderControl)->update();
425}
426
427static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio)
428{
429 if (item->flags() & QQuickItem::ItemHasContents) {
430 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
431 itemPrivate->itemChange(QQuickItem::ItemDevicePixelRatioHasChanged, pixelRatio);
432 }
433
434 QList <QQuickItem *> items = item->childItems();
435 for (int i = 0; i < items.size(); ++i)
436 updatePixelRatioHelper(item: items.at(i), pixelRatio);
437}
438
439void QQuickWindow::physicalDpiChanged()
440{
441 Q_D(QQuickWindow);
442 const qreal newPixelRatio = screen()->devicePixelRatio();
443 if (qFuzzyCompare(p1: newPixelRatio, p2: d->devicePixelRatio))
444 return;
445 d->devicePixelRatio = newPixelRatio;
446 if (d->contentItem)
447 updatePixelRatioHelper(item: d->contentItem, pixelRatio: newPixelRatio);
448}
449
450void QQuickWindow::handleScreenChanged(QScreen *screen)
451{
452 Q_D(QQuickWindow);
453 // we connected to the initial screen in QQuickWindowPrivate::init, but the screen changed
454 disconnect(d->physicalDpiChangedConnection);
455 if (screen) {
456 physicalDpiChanged();
457 // When physical DPI changes on the same screen, either the resolution or the device pixel
458 // ratio changed. We must check what it is. Device pixel ratio does not have its own
459 // ...Changed() signal. Reconnect, same as in QQuickWindowPrivate::init.
460 d->physicalDpiChangedConnection = connect(sender: screen, signal: &QScreen::physicalDotsPerInchChanged,
461 receiver: this, slot: &QQuickWindow::physicalDpiChanged);
462 }
463
464 d->forcePolish();
465}
466
467void forcePolishHelper(QQuickItem *item)
468{
469 if (item->flags() & QQuickItem::ItemHasContents) {
470 item->polish();
471 }
472
473 QList <QQuickItem *> items = item->childItems();
474 for (int i=0; i<items.size(); ++i)
475 forcePolishHelper(item: items.at(i));
476}
477
478/*!
479 Schedules polish events on all items in the scene.
480*/
481void QQuickWindowPrivate::forcePolish()
482{
483 Q_Q(QQuickWindow);
484 if (!q->screen())
485 return;
486 forcePolishHelper(item: contentItem);
487}
488
489void forceUpdate(QQuickItem *item)
490{
491 if (item->flags() & QQuickItem::ItemHasContents)
492 item->update();
493 QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
494
495 QList <QQuickItem *> items = item->childItems();
496 for (int i=0; i<items.size(); ++i)
497 forceUpdate(item: items.at(i));
498}
499
500void QQuickWindowPrivate::syncSceneGraph()
501{
502 Q_Q(QQuickWindow);
503
504 // Calculate the dpr the same way renderSceneGraph() will.
505 qreal devicePixelRatio = q->effectiveDevicePixelRatio();
506 if (renderTargetId && !QQuickRenderControl::renderWindowFor(win: q))
507 devicePixelRatio = 1;
508
509 context->prepareSync(devicePixelRatio, cb: rhi ? swapchain->currentFrameCommandBuffer() : nullptr);
510
511 animationController->beforeNodeSync();
512
513 emit q->beforeSynchronizing();
514 runAndClearJobs(jobs: &beforeSynchronizingJobs);
515 if (!renderer) {
516 forceUpdate(item: contentItem);
517
518 QSGRootNode *rootNode = new QSGRootNode;
519 rootNode->appendChildNode(node: QQuickItemPrivate::get(item: contentItem)->itemNode());
520 renderer = context->createRenderer();
521 renderer->setRootNode(rootNode);
522 }
523
524 updateDirtyNodes();
525
526 animationController->afterNodeSync();
527
528 // Copy the current state of clearing from window into renderer.
529 renderer->setClearColor(clearColor);
530 QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearStencilBuffer | QSGAbstractRenderer::ClearDepthBuffer;
531 if (clearBeforeRendering)
532 mode |= QSGAbstractRenderer::ClearColorBuffer;
533 renderer->setClearMode(mode);
534
535 renderer->setCustomRenderMode(customRenderMode);
536
537 emit q->afterSynchronizing();
538 runAndClearJobs(jobs: &afterSynchronizingJobs);
539}
540
541void QQuickWindowPrivate::emitBeforeRenderPassRecording(void *ud)
542{
543 QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud);
544 emit w->beforeRenderPassRecording();
545}
546
547void QQuickWindowPrivate::emitAfterRenderPassRecording(void *ud)
548{
549 QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud);
550 emit w->afterRenderPassRecording();
551}
552
553void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfaceSize)
554{
555 Q_Q(QQuickWindow);
556 if (!renderer)
557 return;
558
559 if (rhi) {
560 // ### no offscreen ("renderTargetId") support yet
561 context->beginNextRhiFrame(renderer,
562 rt: swapchain->currentFrameRenderTarget(),
563 rp: rpDescForSwapchain,
564 cb: swapchain->currentFrameCommandBuffer(),
565 mainPassRecordingStart: emitBeforeRenderPassRecording,
566 mainPassRecordingEnd: emitAfterRenderPassRecording,
567 callbackUserData: q);
568 } else {
569 context->beginNextFrame(renderer,
570 mainPassRecordingStart: emitBeforeRenderPassRecording,
571 mainPassRecordingEnd: emitAfterRenderPassRecording,
572 callbackUserData: q);
573 }
574
575 animationController->advance();
576 emit q->beforeRendering();
577 runAndClearJobs(jobs: &beforeRenderingJobs);
578 if (!customRenderStage || !customRenderStage->render()) {
579 int fboId = 0;
580 const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
581 if (renderTargetId) {
582 QRect rect(QPoint(0, 0), renderTargetSize);
583 fboId = renderTargetId;
584 renderer->setDeviceRect(rect);
585 renderer->setViewportRect(rect);
586 if (QQuickRenderControl::renderWindowFor(win: q)) {
587 renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size));
588 renderer->setDevicePixelRatio(devicePixelRatio);
589 } else {
590 renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size()));
591 renderer->setDevicePixelRatio(1);
592 }
593 } else {
594 QSize pixelSize;
595 QSizeF logicalSize;
596 if (surfaceSize.isEmpty()) {
597 pixelSize = size * devicePixelRatio;
598 logicalSize = size;
599 } else {
600 pixelSize = surfaceSize;
601 logicalSize = QSizeF(surfaceSize) / devicePixelRatio;
602 }
603 QRect rect(QPoint(0, 0), pixelSize);
604 renderer->setDeviceRect(rect);
605 renderer->setViewportRect(rect);
606 const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
607 QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
608 if (flipY)
609 matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
610 renderer->setProjectionMatrixToRect(rect: QRectF(QPoint(0, 0), logicalSize), flags: matrixFlags);
611 renderer->setDevicePixelRatio(devicePixelRatio);
612 }
613
614 if (rhi)
615 context->renderNextRhiFrame(renderer);
616 else
617 context->renderNextFrame(renderer, fboId);
618 }
619 emit q->afterRendering();
620 runAndClearJobs(jobs: &afterRenderingJobs);
621
622 if (rhi)
623 context->endNextRhiFrame(renderer);
624 else
625 context->endNextFrame(renderer);
626
627 if (renderer && renderer->hasCustomRenderModeWithContinuousUpdate()) {
628 // For the overdraw visualizer. This update is not urgent so avoid a
629 // direct update() call, this is only here to keep the overdraw
630 // visualization box rotating even when the scene is static.
631 QCoreApplication::postEvent(receiver: q, event: new QEvent(QEvent::Type(FullUpdateRequest)));
632 }
633}
634
635QQuickWindowPrivate::QQuickWindowPrivate()
636 : contentItem(nullptr)
637 , activeFocusItem(nullptr)
638#if QT_CONFIG(cursor)
639 , cursorItem(nullptr)
640 , cursorHandler(nullptr)
641#endif
642#if QT_CONFIG(quick_draganddrop)
643 , dragGrabber(nullptr)
644#endif
645 , touchMouseId(-1)
646 , touchMouseDevice(nullptr)
647 , touchMousePressTimestamp(0)
648 , dirtyItemList(nullptr)
649 , devicePixelRatio(0)
650 , context(nullptr)
651 , renderer(nullptr)
652 , windowManager(nullptr)
653 , renderControl(nullptr)
654 , pointerEventRecursionGuard(0)
655 , customRenderStage(nullptr)
656 , clearColor(Qt::white)
657 , clearBeforeRendering(true)
658 , persistentGLContext(true)
659 , persistentSceneGraph(true)
660 , lastWheelEventAccepted(false)
661 , componentCompleted(true)
662 , allowChildEventFiltering(true)
663 , allowDoubleClick(true)
664 , lastFocusReason(Qt::OtherFocusReason)
665 , renderTarget(nullptr)
666 , renderTargetId(0)
667 , vaoHelper(nullptr)
668 , incubationController(nullptr)
669 , hasActiveSwapchain(false)
670 , hasRenderableSwapchain(false)
671 , swapchainJustBecameRenderable(false)
672{
673#if QT_CONFIG(quick_draganddrop)
674 dragGrabber = new QQuickDragGrabber;
675#endif
676}
677
678QQuickWindowPrivate::~QQuickWindowPrivate()
679{
680 delete customRenderStage;
681 if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
682 service->removeWindow(q_func());
683}
684
685void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
686{
687 q_ptr = c;
688
689
690 Q_Q(QQuickWindow);
691
692 contentItem = new QQuickRootItem;
693 QQml_setParent_noEvent(object: contentItem, parent: c);
694 QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership);
695 QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(item: contentItem);
696 contentItemPrivate->window = q;
697 contentItemPrivate->windowRefCount = 1;
698 contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
699 contentItem->setSize(q->size());
700
701 customRenderMode = qgetenv(varName: "QSG_VISUALIZE");
702 renderControl = control;
703 if (renderControl)
704 QQuickRenderControlPrivate::get(renderControl)->window = q;
705
706 if (!renderControl)
707 windowManager = QSGRenderLoop::instance();
708
709 Q_ASSERT(windowManager || renderControl);
710
711 if (QScreen *screen = q->screen()) {
712 devicePixelRatio = screen->devicePixelRatio();
713 // if the screen changes, then QQuickWindow::handleScreenChanged disconnects
714 // and connects to the new screen
715 physicalDpiChangedConnection = QObject::connect(sender: screen, signal: &QScreen::physicalDotsPerInchChanged,
716 receiver: q, slot: &QQuickWindow::physicalDpiChanged);
717 }
718
719 QSGContext *sg;
720 if (renderControl) {
721 QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl);
722 sg = renderControlPriv->sg;
723 context = renderControlPriv->rc;
724 } else {
725 windowManager->addWindow(win: q);
726 sg = windowManager->sceneGraphContext();
727 context = windowManager->createRenderContext(sg);
728 }
729
730 q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface);
731 q->setFormat(sg->defaultSurfaceFormat());
732#if QT_CONFIG(vulkan)
733 if (q->surfaceType() == QSurface::VulkanSurface)
734 q->setVulkanInstance(QSGRhiSupport::vulkanInstance());
735#endif
736
737 animationController.reset(other: new QQuickAnimatorController(q));
738
739 QObject::connect(sender: context, SIGNAL(initialized()), receiver: q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
740 QObject::connect(sender: context, SIGNAL(invalidated()), receiver: q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
741 QObject::connect(sender: context, SIGNAL(invalidated()), receiver: q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
742
743 QObject::connect(sender: q, SIGNAL(focusObjectChanged(QObject*)), receiver: q, SIGNAL(activeFocusItemChanged()));
744 QObject::connect(sender: q, SIGNAL(screenChanged(QScreen*)), receiver: q, SLOT(handleScreenChanged(QScreen*)));
745 QObject::connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
746 receiver: q, SLOT(handleApplicationStateChanged(Qt::ApplicationState)));
747 QObject::connect(sender: q, SIGNAL(frameSwapped()), receiver: q, SLOT(runJobsAfterSwap()), Qt::DirectConnection);
748
749 if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
750 service->addWindow(q);
751}
752
753void QQuickWindow::handleApplicationStateChanged(Qt::ApplicationState state)
754{
755 Q_D(QQuickWindow);
756 if (state != Qt::ApplicationActive && d->contentItem)
757 d->contentItem->windowDeactivateEvent();
758}
759
760/*!
761 \property QQuickWindow::data
762 \internal
763*/
764
765QQmlListProperty<QObject> QQuickWindowPrivate::data()
766{
767 return QQmlListProperty<QObject>(q_func(), nullptr,
768 QQuickWindowPrivate::data_append,
769 QQuickWindowPrivate::data_count,
770 QQuickWindowPrivate::data_at,
771 QQuickWindowPrivate::data_clear,
772 QQuickWindowPrivate::data_replace,
773 QQuickWindowPrivate::data_removeLast);
774}
775
776static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
777{
778 Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
779 // The touch point local position and velocity are not yet transformed.
780 QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(point: p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
781 Qt::LeftButton, (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton), event->modifiers());
782 me->setAccepted(true);
783 me->setTimestamp(event->timestamp());
784 QVector2D transformedVelocity = p.velocity();
785 if (transformNeeded) {
786 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
787 QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
788 transformedVelocity = transformMatrix.mapVector(vector: p.velocity()).toVector2D();
789 }
790 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(event: me, caps: event->device()->capabilities(), velocity: transformedVelocity);
791 QGuiApplicationPrivate::setMouseEventSource(event: me, source: Qt::MouseEventSynthesizedByQt);
792 return me;
793}
794
795bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos)
796{
797 bool doubleClicked = false;
798
799 if (touchMousePressTimestamp > 0) {
800 QPoint distanceBetweenPresses = newPressPos - touchMousePressPos;
801 const int doubleTapDistance = QGuiApplication::styleHints()->touchDoubleTapDistance();
802 doubleClicked = (qAbs(t: distanceBetweenPresses.x()) <= doubleTapDistance) && (qAbs(t: distanceBetweenPresses.y()) <= doubleTapDistance);
803
804 if (doubleClicked) {
805 ulong timeBetweenPresses = newPressEventTimestamp - touchMousePressTimestamp;
806 ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->
807 mouseDoubleClickInterval());
808 doubleClicked = timeBetweenPresses < doubleClickInterval;
809 }
810 }
811 if (doubleClicked) {
812 touchMousePressTimestamp = 0;
813 } else {
814 touchMousePressTimestamp = newPressEventTimestamp;
815 touchMousePressPos = newPressPos;
816 }
817
818 return doubleClicked;
819}
820
821void QQuickWindowPrivate::cancelTouchMouseSynthesis()
822{
823 qCDebug(DBG_TOUCH_TARGET);
824 touchMouseId = -1;
825 touchMouseDevice = nullptr;
826}
827
828bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent)
829{
830 Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
831 Q_Q(QQuickWindow);
832 auto device = pointerEvent->device();
833
834 // A touch event from a trackpad is likely to be followed by a mouse or gesture event, so mouse event synth is redundant
835 if (device->type() == QQuickPointerDevice::TouchPad && device->capabilities().testFlag(flag: QQuickPointerDevice::MouseEmulation)) {
836 qCDebug(DBG_TOUCH_TARGET) << "skipping delivery of synth-mouse event from" << device;
837 return false;
838 }
839
840 // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here.
841 Q_ASSERT(pointerEvent->asPointerTouchEvent());
842 QScopedPointer<QTouchEvent> event(pointerEvent->asPointerTouchEvent()->touchEventForItem(item));
843 if (event.isNull())
844 return false;
845
846 // For each point, check if it is accepted, if not, try the next point.
847 // Any of the fingers can become the mouse one.
848 // This can happen because a mouse area might not accept an event at some point but another.
849 for (int i = 0; i < event->touchPoints().count(); ++i) {
850 const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
851 // A new touch point
852 if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
853 QPointF pos = item->mapFromScene(point: p.scenePos());
854
855 // probably redundant, we check bounds in the calling function (matchingNewPoints)
856 if (!item->contains(point: pos))
857 break;
858
859 qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << p.id() << "->" << item;
860 QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(type: QEvent::MouseButtonPress, p, event: event.data(), item, transformNeeded: false));
861
862 // Send a single press and see if that's accepted
863 QCoreApplication::sendEvent(receiver: item, event: mousePress.data());
864 event->setAccepted(mousePress->isAccepted());
865 if (mousePress->isAccepted()) {
866 touchMouseDevice = device;
867 touchMouseId = p.id();
868 if (!q->mouseGrabberItem())
869 item->grabMouse();
870 if (auto pointerEventPoint = pointerEvent->pointById(pointId: p.id()))
871 pointerEventPoint->setGrabberItem(item);
872
873 if (checkIfDoubleTapped(newPressEventTimestamp: event->timestamp(), newPressPos: p.screenPos().toPoint())) {
874 // since we synth the mouse event from from touch, we respect the
875 // QPlatformTheme::TouchDoubleTapDistance instead of QPlatformTheme::MouseDoubleClickDistance
876 QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(type: QEvent::MouseButtonDblClick, p, event: event.data(), item, transformNeeded: false));
877 QCoreApplication::sendEvent(receiver: item, event: mouseDoubleClick.data());
878 event->setAccepted(mouseDoubleClick->isAccepted());
879 if (!mouseDoubleClick->isAccepted())
880 cancelTouchMouseSynthesis();
881 }
882
883 return true;
884 }
885 // try the next point
886
887 // Touch point was there before and moved
888 } else if (touchMouseDevice == device && p.id() == touchMouseId) {
889 if (p.state() & Qt::TouchPointMoved) {
890 if (touchMousePressTimestamp != 0) {
891 const int doubleTapDistance = QGuiApplicationPrivate::platformTheme()->themeHint(hint: QPlatformTheme::TouchDoubleTapDistance).toInt();
892 const QPoint moveDelta = p.screenPos().toPoint() - touchMousePressPos;
893 if (moveDelta.x() >= doubleTapDistance || moveDelta.y() >= doubleTapDistance)
894 touchMousePressTimestamp = 0; // Got dragged too far, dismiss the double tap
895 }
896 if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) {
897 QScopedPointer<QMouseEvent> me(touchToMouseEvent(type: QEvent::MouseMove, p, event: event.data(), item: mouseGrabberItem, transformNeeded: false));
898 QCoreApplication::sendEvent(receiver: item, event: me.data());
899 event->setAccepted(me->isAccepted());
900 if (me->isAccepted()) {
901 qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << p.id() << "->" << mouseGrabberItem;
902 }
903 return event->isAccepted();
904 } else {
905 // no grabber, check if we care about mouse hover
906 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
907 // hover for touch???
908 QScopedPointer<QMouseEvent> me(touchToMouseEvent(type: QEvent::MouseMove, p, event: event.data(), item, transformNeeded: false));
909 if (lastMousePosition.isNull())
910 lastMousePosition = me->windowPos();
911 QPointF last = lastMousePosition;
912 lastMousePosition = me->windowPos();
913
914 bool accepted = me->isAccepted();
915 bool delivered = deliverHoverEvent(contentItem, scenePos: me->windowPos(), lastScenePos: last, modifiers: me->modifiers(), timestamp: me->timestamp(), accepted);
916 if (!delivered) {
917 //take care of any exits
918 accepted = clearHover(timestamp: me->timestamp());
919 }
920 me->setAccepted(accepted);
921 break;
922 }
923 } else if (p.state() & Qt::TouchPointReleased) {
924 // currently handled point was released
925 if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) {
926 QScopedPointer<QMouseEvent> me(touchToMouseEvent(type: QEvent::MouseButtonRelease, p, event: event.data(), item: mouseGrabberItem, transformNeeded: false));
927 QCoreApplication::sendEvent(receiver: item, event: me.data());
928
929 if (item->acceptHoverEvents() && p.screenPos() != QGuiApplicationPrivate::lastCursorPosition) {
930 QPointF localMousePos(qInf(), qInf());
931 if (QWindow *w = item->window())
932 localMousePos = item->mapFromScene(point: w->mapFromGlobal(pos: QGuiApplicationPrivate::lastCursorPosition.toPoint()));
933 QMouseEvent mm(QEvent::MouseMove, localMousePos, QGuiApplicationPrivate::lastCursorPosition,
934 Qt::NoButton, Qt::NoButton, event->modifiers());
935 QCoreApplication::sendEvent(receiver: item, event: &mm);
936 }
937 if (q->mouseGrabberItem()) // might have ungrabbed due to event
938 q->mouseGrabberItem()->ungrabMouse();
939
940 cancelTouchMouseSynthesis();
941 return me->isAccepted();
942 }
943 }
944 break;
945 }
946 }
947 return false;
948}
949
950void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids)
951{
952 QQuickPointerEvent *ev = nullptr;
953 for (int i = 0; i < ids.count(); ++i) {
954 int id = ids.at(i);
955 if (Q_UNLIKELY(id < 0)) {
956 qWarning(msg: "ignoring grab of touchpoint %d", id);
957 continue;
958 }
959 if (id == touchMouseId) {
960 auto point = pointerEventInstance(device: touchMouseDevice)->pointById(pointId: id);
961 auto touchMouseGrabber = point->grabberItem();
962 if (touchMouseGrabber) {
963 point->setExclusiveGrabber(nullptr);
964 touchMouseGrabber->mouseUngrabEvent();
965 touchMouseGrabber->touchUngrabEvent();
966 cancelTouchMouseSynthesis();
967 }
968 qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: mouse grabber changed due to grabTouchPoints:" << touchMouseGrabber << "-> null";
969 }
970
971 // optimization to avoid the loop over devices below:
972 // all ids are probably from the same event, so we don't have to search
973 if (ev) {
974 auto point = ev->pointById(pointId: id);
975 if (point && point->exclusiveGrabber() != grabber) {
976 point->setExclusiveGrabber(grabber);
977 continue; // next id in the ids loop
978 }
979 }
980 // search all devices for a QQuickPointerEvent instance that is delivering the point with id
981 const auto touchDevices = QQuickPointerDevice::touchDevices();
982 for (auto device : touchDevices) {
983 QQuickPointerEvent *pev = pointerEventInstance(device);
984 auto point = pev->pointById(pointId: id);
985 if (point) {
986 ev = pev;
987 if (point->exclusiveGrabber() != grabber)
988 point->setExclusiveGrabber(grabber);
989 break; // out of touchDevices loop
990 }
991 }
992 }
993}
994
995/*!
996 Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber.
997 This should not be called when processing a release event - that's redundant.
998 It is called in other cases, when the points may not be released, but the item
999 nevertheless must lose its grab due to becoming disabled, invisible, etc.
1000 QQuickEventPoint::setGrabberItem() calls touchUngrabEvent() when all points are released,
1001 but if not all points are released, it cannot be sure whether to call touchUngrabEvent()
1002 or not; so we have to do it here.
1003*/
1004void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch)
1005{
1006 Q_Q(QQuickWindow);
1007 if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) {
1008 bool fromTouch = isDeliveringTouchAsMouse();
1009 auto point = fromTouch ?
1010 pointerEventInstance(device: touchMouseDevice)->pointById(pointId: touchMouseId) :
1011 pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice())->point(i: 0);
1012 QQuickItem *oldGrabber = point->grabberItem();
1013 qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << oldGrabber << "-> null";
1014 point->setGrabberItem(nullptr);
1015 sendUngrabEvent(grabber: oldGrabber, touch: fromTouch);
1016 }
1017 if (Q_LIKELY(touch)) {
1018 bool ungrab = false;
1019 const auto touchDevices = QQuickPointerDevice::touchDevices();
1020 for (auto device : touchDevices) {
1021 if (auto pointerEvent = queryPointerEventInstance(device)) {
1022 for (int i = 0; i < pointerEvent->pointCount(); ++i) {
1023 if (pointerEvent->point(i)->exclusiveGrabber() == grabber) {
1024 pointerEvent->point(i)->setGrabberItem(nullptr);
1025 ungrab = true;
1026 }
1027 }
1028 }
1029 }
1030 if (ungrab)
1031 grabber->touchUngrabEvent();
1032 }
1033}
1034
1035void QQuickWindowPrivate::sendUngrabEvent(QQuickItem *grabber, bool touch)
1036{
1037 if (!grabber)
1038 return;
1039 QEvent e(QEvent::UngrabMouse);
1040 hasFiltered.clear();
1041 if (!sendFilteredMouseEvent(event: &e, receiver: grabber, filteringParent: grabber->parentItem())) {
1042 grabber->mouseUngrabEvent();
1043 if (touch)
1044 grabber->touchUngrabEvent();
1045 }
1046}
1047
1048/*!
1049Translates the data in \a touchEvent to this window. This method leaves the item local positions in
1050\a touchEvent untouched (these are filled in later).
1051*/
1052void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
1053{
1054 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
1055 for (int i = 0; i < touchPoints.count(); ++i) {
1056 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1057 touchPoint.setScenePos(touchPoint.pos());
1058 touchPoint.setStartScenePos(touchPoint.startPos());
1059 touchPoint.setLastScenePos(touchPoint.lastPos());
1060 }
1061 touchEvent->setTouchPoints(touchPoints);
1062}
1063
1064
1065static inline bool windowHasFocus(QQuickWindow *win)
1066{
1067 const QWindow *focusWindow = QGuiApplication::focusWindow();
1068 return win == focusWindow || QQuickRenderControl::renderWindowFor(win) == focusWindow;
1069}
1070
1071#ifdef Q_OS_WEBOS
1072// Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
1073static inline bool singleWindowOnScreen(QQuickWindow *win)
1074{
1075 const QWindowList windowList = QGuiApplication::allWindows();
1076 for (int i = 0; i < windowList.count(); i++) {
1077 QWindow *ii = windowList.at(i);
1078 if (ii == win)
1079 continue;
1080 if (ii->screen() == win->screen())
1081 return false;
1082 }
1083
1084 return true;
1085}
1086#endif
1087
1088/*!
1089Set the focus inside \a scope to be \a item.
1090If the scope contains the active focus item, it will be changed to \a item.
1091Calls notifyFocusChangesRecur for all changed items.
1092*/
1093void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
1094{
1095 Q_Q(QQuickWindow);
1096
1097 Q_ASSERT(item);
1098 Q_ASSERT(scope || item == contentItem);
1099
1100 qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::setFocusInScope():";
1101 qCDebug(DBG_FOCUS) << " scope:" << (QObject *)scope;
1102 if (scope)
1103 qCDebug(DBG_FOCUS) << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(item: scope)->subFocusItem;
1104 qCDebug(DBG_FOCUS) << " item:" << (QObject *)item;
1105 qCDebug(DBG_FOCUS) << " activeFocusItem:" << (QObject *)activeFocusItem;
1106
1107 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(item: scope) : nullptr;
1108 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1109
1110 QQuickItem *oldActiveFocusItem = nullptr;
1111 QQuickItem *currentActiveFocusItem = activeFocusItem;
1112 QQuickItem *newActiveFocusItem = nullptr;
1113 bool sendFocusIn = false;
1114
1115 lastFocusReason = reason;
1116
1117 QVarLengthArray<QQuickItem *, 20> changed;
1118
1119 // Does this change the active focus?
1120 if (item == contentItem || scopePrivate->activeFocus) {
1121 oldActiveFocusItem = activeFocusItem;
1122 if (item->isEnabled()) {
1123 newActiveFocusItem = item;
1124 while (newActiveFocusItem->isFocusScope()
1125 && newActiveFocusItem->scopedFocusItem()
1126 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
1127 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
1128 }
1129 } else {
1130 newActiveFocusItem = scope;
1131 }
1132
1133 if (oldActiveFocusItem) {
1134#if QT_CONFIG(im)
1135 QGuiApplication::inputMethod()->commit();
1136#endif
1137
1138 activeFocusItem = nullptr;
1139
1140 QQuickItem *afi = oldActiveFocusItem;
1141 while (afi && afi != scope) {
1142 if (QQuickItemPrivate::get(item: afi)->activeFocus) {
1143 QQuickItemPrivate::get(item: afi)->activeFocus = false;
1144 changed << afi;
1145 }
1146 afi = afi->parentItem();
1147 }
1148 }
1149 }
1150
1151 if (item != contentItem && !(options & DontChangeSubFocusItem)) {
1152 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
1153 if (oldSubFocusItem) {
1154 QQuickItemPrivate::get(item: oldSubFocusItem)->focus = false;
1155 changed << oldSubFocusItem;
1156 }
1157
1158 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, focus: true);
1159 }
1160
1161 if (!(options & DontChangeFocusProperty)) {
1162 if (item != contentItem
1163 || windowHasFocus(win: q)
1164#ifdef Q_OS_WEBOS
1165 // Allow focused if there is only one window in the screen where it belongs.
1166 // Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
1167 || singleWindowOnScreen(q)
1168#endif
1169 ) {
1170 itemPrivate->focus = true;
1171 changed << item;
1172 }
1173 }
1174
1175 if (newActiveFocusItem && contentItem->hasFocus()) {
1176 activeFocusItem = newActiveFocusItem;
1177
1178 QQuickItemPrivate::get(item: newActiveFocusItem)->activeFocus = true;
1179 changed << newActiveFocusItem;
1180
1181 QQuickItem *afi = newActiveFocusItem->parentItem();
1182 while (afi && afi != scope) {
1183 if (afi->isFocusScope()) {
1184 QQuickItemPrivate::get(item: afi)->activeFocus = true;
1185 changed << afi;
1186 }
1187 afi = afi->parentItem();
1188 }
1189 updateFocusItemTransform();
1190 sendFocusIn = true;
1191 }
1192
1193 // Now that all the state is changed, emit signals & events
1194 // We must do this last, as this process may result in further changes to focus.
1195 if (oldActiveFocusItem) {
1196 QFocusEvent event(QEvent::FocusOut, reason);
1197 QCoreApplication::sendEvent(receiver: oldActiveFocusItem, event: &event);
1198 }
1199
1200 // Make sure that the FocusOut didn't result in another focus change.
1201 if (sendFocusIn && activeFocusItem == newActiveFocusItem) {
1202 QFocusEvent event(QEvent::FocusIn, reason);
1203 QCoreApplication::sendEvent(receiver: newActiveFocusItem, event: &event);
1204 }
1205
1206 if (activeFocusItem != currentActiveFocusItem)
1207 emit q->focusObjectChanged(object: activeFocusItem);
1208
1209 if (!changed.isEmpty())
1210 notifyFocusChangesRecur(item: changed.data(), remaining: changed.count() - 1);
1211}
1212
1213void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
1214{
1215 Q_Q(QQuickWindow);
1216
1217 Q_ASSERT(item);
1218 Q_ASSERT(scope || item == contentItem);
1219
1220 qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::clearFocusInScope():";
1221 qCDebug(DBG_FOCUS) << " scope:" << (QObject *)scope;
1222 qCDebug(DBG_FOCUS) << " item:" << (QObject *)item;
1223 qCDebug(DBG_FOCUS) << " activeFocusItem:" << (QObject *)activeFocusItem;
1224
1225 QQuickItemPrivate *scopePrivate = nullptr;
1226 if (scope) {
1227 scopePrivate = QQuickItemPrivate::get(item: scope);
1228 if ( !scopePrivate->subFocusItem )
1229 return;//No focus, nothing to do.
1230 }
1231
1232 QQuickItem *currentActiveFocusItem = activeFocusItem;
1233 QQuickItem *oldActiveFocusItem = nullptr;
1234 QQuickItem *newActiveFocusItem = nullptr;
1235
1236 lastFocusReason = reason;
1237
1238 QVarLengthArray<QQuickItem *, 20> changed;
1239
1240 Q_ASSERT(item == contentItem || item == scopePrivate->subFocusItem);
1241
1242 // Does this change the active focus?
1243 if (item == contentItem || scopePrivate->activeFocus) {
1244 oldActiveFocusItem = activeFocusItem;
1245 newActiveFocusItem = scope;
1246
1247#if QT_CONFIG(im)
1248 QGuiApplication::inputMethod()->commit();
1249#endif
1250
1251 activeFocusItem = nullptr;
1252
1253 if (oldActiveFocusItem) {
1254 QQuickItem *afi = oldActiveFocusItem;
1255 while (afi && afi != scope) {
1256 if (QQuickItemPrivate::get(item: afi)->activeFocus) {
1257 QQuickItemPrivate::get(item: afi)->activeFocus = false;
1258 changed << afi;
1259 }
1260 afi = afi->parentItem();
1261 }
1262 }
1263 }
1264
1265 if (item != contentItem && !(options & DontChangeSubFocusItem)) {
1266 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
1267 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
1268 QQuickItemPrivate::get(item: oldSubFocusItem)->focus = false;
1269 changed << oldSubFocusItem;
1270 }
1271
1272 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, focus: false);
1273
1274 } else if (!(options & DontChangeFocusProperty)) {
1275 QQuickItemPrivate::get(item)->focus = false;
1276 changed << item;
1277 }
1278
1279 if (newActiveFocusItem) {
1280 Q_ASSERT(newActiveFocusItem == scope);
1281 activeFocusItem = scope;
1282 updateFocusItemTransform();
1283 }
1284
1285 // Now that all the state is changed, emit signals & events
1286 // We must do this last, as this process may result in further changes to
1287 // focus.
1288 if (oldActiveFocusItem) {
1289 QFocusEvent event(QEvent::FocusOut, reason);
1290 QCoreApplication::sendEvent(receiver: oldActiveFocusItem, event: &event);
1291 }
1292
1293 // Make sure that the FocusOut didn't result in another focus change.
1294 if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) {
1295 QFocusEvent event(QEvent::FocusIn, reason);
1296 QCoreApplication::sendEvent(receiver: newActiveFocusItem, event: &event);
1297 }
1298
1299 if (activeFocusItem != currentActiveFocusItem)
1300 emit q->focusObjectChanged(object: activeFocusItem);
1301
1302 if (!changed.isEmpty())
1303 notifyFocusChangesRecur(item: changed.data(), remaining: changed.count() - 1);
1304}
1305
1306void QQuickWindowPrivate::clearFocusObject()
1307{
1308 if (activeFocusItem == contentItem)
1309 return;
1310
1311 clearFocusInScope(scope: contentItem, item: QQuickItemPrivate::get(item: contentItem)->subFocusItem, reason: Qt::OtherFocusReason);
1312}
1313
1314void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
1315{
1316 QPointer<QQuickItem> item(*items);
1317
1318 if (remaining)
1319 notifyFocusChangesRecur(items: items + 1, remaining: remaining - 1);
1320
1321 if (item) {
1322 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1323
1324 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
1325 itemPrivate->notifiedFocus = itemPrivate->focus;
1326 emit item->focusChanged(itemPrivate->focus);
1327 }
1328
1329 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
1330 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
1331 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
1332 emit item->activeFocusChanged(itemPrivate->activeFocus);
1333 }
1334 }
1335}
1336
1337void QQuickWindowPrivate::dirtyItem(QQuickItem *)
1338{
1339 Q_Q(QQuickWindow);
1340 q->maybeUpdate();
1341}
1342
1343void QQuickWindowPrivate::cleanup(QSGNode *n)
1344{
1345 Q_Q(QQuickWindow);
1346
1347 Q_ASSERT(!cleanupNodeList.contains(n));
1348 cleanupNodeList.append(t: n);
1349 q->maybeUpdate();
1350}
1351
1352/*!
1353 \qmltype Window
1354 \instantiates QQuickWindow
1355 \inqmlmodule QtQuick.Window
1356 \ingroup qtquick-visual
1357 \brief Creates a new top-level window.
1358
1359 The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the
1360 window for use with \c {QtQuick 2.x} graphical types.
1361
1362 To use this type, you will need to import the module with the following line:
1363 \code
1364 import QtQuick.Window 2.2
1365 \endcode
1366
1367 Omitting this import will allow you to have a QML environment without
1368 access to window system features.
1369
1370 A Window can be declared inside an Item or inside another Window; in that
1371 case the inner Window will automatically become "transient for" the outer
1372 Window: that is, most platforms will show it centered upon the outer window
1373 by default, and there may be other platform-dependent behaviors, depending
1374 also on the \l flags. If the nested window is intended to be a dialog in
1375 your application, you should also set \l flags to Qt.Dialog, because some
1376 window managers will not provide the centering behavior without that flag.
1377 You can also declare multiple windows inside a top-level \l QtObject, in which
1378 case the windows will have no transient relationship.
1379
1380 Alternatively you can set or bind \l x and \l y to position the Window
1381 explicitly on the screen.
1382
1383 When the user attempts to close a window, the \l closing signal will be
1384 emitted. You can force the window to stay open (for example to prompt the
1385 user to save changes) by writing an \c onClosing handler and setting
1386 \c {close.accepted = false}.
1387*/
1388/*!
1389 \class QQuickWindow
1390 \since 5.0
1391
1392 \inmodule QtQuick
1393
1394 \brief The QQuickWindow class provides the window for displaying a graphical QML scene.
1395
1396 QQuickWindow provides the graphical scene management needed to interact with and display
1397 a scene of QQuickItems.
1398
1399 A QQuickWindow always has a single invisible root item. To add items to this window,
1400 reparent the items to the root item or to an existing item in the scene.
1401
1402 For easily displaying a scene from a QML file, see \l{QQuickView}.
1403
1404 \section1 Rendering
1405
1406 QQuickWindow uses a scene graph to represent what needs to be rendered.
1407 This scene graph is disconnected from the QML scene and
1408 potentially lives in another thread, depending on the platform
1409 implementation. Since the rendering scene graph lives
1410 independently from the QML scene, it can also be completely
1411 released without affecting the state of the QML scene.
1412
1413 The sceneGraphInitialized() signal is emitted on the rendering
1414 thread before the QML scene is rendered to the screen for the
1415 first time. If the rendering scene graph has been released, the
1416 signal will be emitted again before the next frame is rendered.
1417
1418
1419 \section2 Integration with OpenGL
1420
1421 When using the default OpenGL adaptation, it is possible to integrate
1422 OpenGL calls directly into the QQuickWindow using the same OpenGL
1423 context as the Qt Quick Scene Graph. This is done by connecting to the
1424 QQuickWindow::beforeRendering() or QQuickWindow::afterRendering()
1425 signal.
1426
1427 \note When using QQuickWindow::beforeRendering(), make sure to
1428 disable clearing before rendering with
1429 QQuickWindow::setClearBeforeRendering().
1430
1431
1432 \section2 Exposure and Visibility
1433
1434 When a QQuickWindow instance is deliberately hidden with hide() or
1435 setVisible(false), it will stop rendering and its scene graph and
1436 graphics context might be released. The sceneGraphInvalidated()
1437 signal will be emitted when this happens.
1438
1439 \warning It is crucial that graphics operations and interaction with
1440 the scene graph happens exclusively on the rendering thread,
1441 primarily during the updatePaintNode() phase.
1442
1443 \warning As signals related to rendering might be emitted from the
1444 rendering thread, connections should be made using
1445 Qt::DirectConnection.
1446
1447
1448 \section2 Resource Management
1449
1450 QML will try to cache images and scene graph nodes to
1451 improve performance, but in some low-memory scenarios it might be
1452 required to aggressively release these resources. The
1453 releaseResources() can be used to force the clean up of certain
1454 resources. Calling releaseResources() may result in the entire
1455 scene graph and in the case of the OpenGL adaptation the associated
1456 context will be deleted. The sceneGraphInvalidated() signal will be
1457 emitted when this happens.
1458
1459 \note All classes with QSG prefix should be used solely on the scene graph's
1460 rendering thread. See \l {Scene Graph and Rendering} for more information.
1461
1462 \section2 Context and Surface Formats
1463
1464 While it is possible to specify a QSurfaceFormat for every QQuickWindow by
1465 calling the member function setFormat(), windows may also be created from
1466 QML by using the Window and ApplicationWindow elements. In this case there
1467 is no C++ code involved in the creation of the window instance, yet
1468 applications may still wish to set certain surface format values, for
1469 example to request a given OpenGL version or profile. Such applications can
1470 call the static function QSurfaceFormat::setDefaultFormat() at startup. The
1471 specified format will be used for all Quick windows created afterwards.
1472
1473 \sa {Scene Graph - OpenGL Under QML}
1474*/
1475
1476/*!
1477 Constructs a window for displaying a QML scene with parent window \a parent.
1478*/
1479QQuickWindow::QQuickWindow(QWindow *parent)
1480 : QQuickWindow(*new QQuickWindowPrivate, parent)
1481{
1482}
1483
1484
1485
1486/*!
1487 \internal
1488*/
1489QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
1490 : QWindow(dd, parent)
1491{
1492 Q_D(QQuickWindow);
1493 d->init(c: this);
1494}
1495
1496/*!
1497 \internal
1498*/
1499QQuickWindow::QQuickWindow(QQuickRenderControl *control)
1500 : QWindow(*(new QQuickWindowPrivate), nullptr)
1501{
1502 Q_D(QQuickWindow);
1503 d->init(c: this, control);
1504}
1505
1506/*!
1507 \internal
1508*/
1509QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control)
1510 : QWindow(dd, nullptr)
1511{
1512 Q_D(QQuickWindow);
1513 d->init(c: this, control);
1514}
1515
1516/*!
1517 Destroys the window.
1518*/
1519QQuickWindow::~QQuickWindow()
1520{
1521 Q_D(QQuickWindow);
1522
1523 if (d->renderControl) {
1524 QQuickRenderControlPrivate::get(renderControl: d->renderControl)->windowDestroyed();
1525 } else if (d->windowManager) {
1526 d->windowManager->removeWindow(win: this);
1527 d->windowManager->windowDestroyed(window: this);
1528 }
1529
1530 delete d->incubationController; d->incubationController = nullptr;
1531#if QT_CONFIG(quick_draganddrop)
1532 delete d->dragGrabber; d->dragGrabber = nullptr;
1533#endif
1534 QQuickRootItem *root = d->contentItem;
1535 d->contentItem = nullptr;
1536 delete root;
1537 qDeleteAll(c: d->pointerEventInstances);
1538 d->pointerEventInstances.clear();
1539
1540 d->renderJobMutex.lock();
1541 qDeleteAll(c: d->beforeSynchronizingJobs);
1542 d->beforeSynchronizingJobs.clear();
1543 qDeleteAll(c: d->afterSynchronizingJobs);
1544 d->afterSynchronizingJobs.clear();
1545 qDeleteAll(c: d->beforeRenderingJobs);
1546 d->beforeRenderingJobs.clear();
1547 qDeleteAll(c: d->afterRenderingJobs);
1548 d->afterRenderingJobs.clear();
1549 qDeleteAll(c: d->afterSwapJobs);
1550 d->afterSwapJobs.clear();
1551 d->renderJobMutex.unlock();
1552
1553 // It is important that the pixmap cache is cleaned up during shutdown.
1554 // Besides playing nice, this also solves a practical problem that
1555 // QQuickTextureFactory implementations in other libraries need
1556 // have their destructors loaded while they the library is still
1557 // loaded into memory.
1558 QQuickPixmap::purgeCache();
1559}
1560
1561/*!
1562 This function tries to release redundant resources currently held by the QML scene.
1563
1564 Calling this function might result in the scene graph and the OpenGL context used
1565 for rendering being released to release graphics memory. If this happens, the
1566 sceneGraphInvalidated() signal will be called, allowing users to clean up their
1567 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
1568 functions can be used to prevent this from happening, if handling the cleanup is
1569 not feasible in the application, at the cost of higher memory usage.
1570
1571 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph()
1572 */
1573
1574void QQuickWindow::releaseResources()
1575{
1576 Q_D(QQuickWindow);
1577 if (d->windowManager)
1578 d->windowManager->releaseResources(window: this);
1579 QQuickPixmap::purgeCache();
1580}
1581
1582
1583
1584/*!
1585 Sets whether the OpenGL context should be preserved, and cannot be
1586 released until the last window is deleted, to \a persistent. The
1587 default value is true.
1588
1589 The OpenGL context can be released to free up graphics resources
1590 when the window is obscured, hidden or not rendering. When this
1591 happens is implementation specific.
1592
1593 The QOpenGLContext::aboutToBeDestroyed() signal is emitted from
1594 the QQuickWindow::openglContext() when the OpenGL context is about
1595 to be released. The QQuickWindow::sceneGraphInitialized() signal
1596 is emitted when a new OpenGL context is created for this
1597 window. Make a Qt::DirectConnection to these signals to be
1598 notified.
1599
1600 The OpenGL context is still released when the last QQuickWindow is
1601 deleted.
1602
1603 \note This only has an effect when using the default OpenGL scene
1604 graph adaptation.
1605
1606 \sa setPersistentSceneGraph(),
1607 QOpenGLContext::aboutToBeDestroyed(), sceneGraphInitialized()
1608 */
1609
1610void QQuickWindow::setPersistentOpenGLContext(bool persistent)
1611{
1612 Q_D(QQuickWindow);
1613 d->persistentGLContext = persistent;
1614}
1615
1616
1617
1618/*!
1619 Returns whether the OpenGL context can be released during the
1620 lifetime of the QQuickWindow.
1621
1622 \note This is a hint. When and how this happens is implementation
1623 specific. It also only has an effect when using the default OpenGL
1624 scene graph adaptation
1625 */
1626
1627bool QQuickWindow::isPersistentOpenGLContext() const
1628{
1629 Q_D(const QQuickWindow);
1630 return d->persistentGLContext;
1631}
1632
1633
1634
1635/*!
1636 Sets whether the scene graph nodes and resources are \a persistent.
1637 Persistent means the nodes and resources cannot be released.
1638 The default value is \c true.
1639
1640 The scene graph nodes and resources can be released to free up
1641 graphics resources when the window is obscured, hidden or not
1642 rendering. When this happens is implementation specific.
1643
1644 The QQuickWindow::sceneGraphInvalidated() signal is emitted when
1645 cleanup occurs. The QQuickWindow::sceneGraphInitialized() signal
1646 is emitted when a new scene graph is recreated for this
1647 window. Make a Qt::DirectConnection to these signals to be
1648 notified.
1649
1650 The scene graph nodes and resources are still released when the
1651 last QQuickWindow is deleted.
1652
1653 \sa setPersistentOpenGLContext(),
1654 sceneGraphInvalidated(), sceneGraphInitialized()
1655 */
1656
1657void QQuickWindow::setPersistentSceneGraph(bool persistent)
1658{
1659 Q_D(QQuickWindow);
1660 d->persistentSceneGraph = persistent;
1661}
1662
1663
1664
1665/*!
1666 Returns whether the scene graph nodes and resources can be
1667 released during the lifetime of this QQuickWindow.
1668
1669 \note This is a hint. When and how this happens is implementation
1670 specific.
1671 */
1672
1673bool QQuickWindow::isPersistentSceneGraph() const
1674{
1675 Q_D(const QQuickWindow);
1676 return d->persistentSceneGraph;
1677}
1678
1679
1680
1681
1682/*!
1683 \qmlattachedproperty Item Window::contentItem
1684 \since 5.4
1685
1686 This attached property holds the invisible root item of the scene or
1687 \c null if the item is not in a window. The Window attached property
1688 can be attached to any Item.
1689*/
1690
1691/*!
1692 \property QQuickWindow::contentItem
1693 \brief The invisible root item of the scene.
1694
1695 A QQuickWindow always has a single invisible root item containing all of its content.
1696 To add items to this window, reparent the items to the contentItem or to an existing
1697 item in the scene.
1698*/
1699QQuickItem *QQuickWindow::contentItem() const
1700{
1701 Q_D(const QQuickWindow);
1702
1703 return d->contentItem;
1704}
1705
1706/*!
1707 \property QQuickWindow::activeFocusItem
1708
1709 \brief The item which currently has active focus or \c null if there is
1710 no item with active focus.
1711
1712 \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick}
1713*/
1714QQuickItem *QQuickWindow::activeFocusItem() const
1715{
1716 Q_D(const QQuickWindow);
1717
1718 return d->activeFocusItem;
1719}
1720
1721/*!
1722 \internal
1723 \reimp
1724*/
1725QObject *QQuickWindow::focusObject() const
1726{
1727 Q_D(const QQuickWindow);
1728
1729 if (d->activeFocusItem)
1730 return d->activeFocusItem;
1731 return const_cast<QQuickWindow*>(this);
1732}
1733
1734
1735/*!
1736 Returns the item which currently has the mouse grab.
1737*/
1738QQuickItem *QQuickWindow::mouseGrabberItem() const
1739{
1740 Q_D(const QQuickWindow);
1741
1742 if (d->isDeliveringTouchAsMouse()) {
1743 if (QQuickPointerEvent *event = d->queryPointerEventInstance(device: d->touchMouseDevice)) {
1744 auto point = event->pointById(pointId: d->touchMouseId);
1745 return point ? point->grabberItem() : nullptr;
1746 }
1747 } else if (QQuickPointerEvent *event = d->queryPointerEventInstance(device: QQuickPointerDevice::genericMouseDevice())) {
1748 Q_ASSERT(event->pointCount());
1749 return event->point(i: 0)->grabberItem();
1750 }
1751 return nullptr;
1752}
1753
1754
1755bool QQuickWindowPrivate::clearHover(ulong timestamp)
1756{
1757 Q_Q(QQuickWindow);
1758 if (hoverItems.isEmpty())
1759 return false;
1760
1761 QPointF pos = q->mapFromGlobal(pos: QGuiApplicationPrivate::lastCursorPosition.toPoint());
1762
1763 bool accepted = false;
1764 for (QQuickItem* item : qAsConst(t&: hoverItems)) {
1765 accepted = sendHoverEvent(QEvent::HoverLeave, item, scenePos: pos, lastScenePos: pos, modifiers: QGuiApplication::keyboardModifiers(), timestamp, accepted: true) || accepted;
1766#if QT_CONFIG(cursor)
1767 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1768 if (itemPrivate->hasPointerHandlers()) {
1769 pos = q->mapFromGlobal(pos: QCursor::pos());
1770 QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove);
1771 pointerEvent->point(i: 0)->reset(state: Qt::TouchPointMoved, scenePosition: pos, pointId: quint64(1) << 24 /* mouse has device ID 1 */, timestamp, velocity: QVector2D());
1772 pointerEvent->point(i: 0)->setAccepted(true);
1773 pointerEvent->localize(target: item);
1774 for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
1775 if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(object: h))
1776 hh->handlePointerEvent(event: pointerEvent);
1777 }
1778#endif
1779 }
1780 hoverItems.clear();
1781 return accepted;
1782}
1783
1784/*! \reimp */
1785bool QQuickWindow::event(QEvent *e)
1786{
1787 Q_D(QQuickWindow);
1788
1789 switch (e->type()) {
1790
1791 case QEvent::TouchBegin:
1792 case QEvent::TouchUpdate:
1793 case QEvent::TouchEnd: {
1794 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
1795 d->handleTouchEvent(touch);
1796 if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
1797 // we consume all touch events ourselves to avoid duplicate
1798 // mouse delivery by QtGui mouse synthesis
1799 e->accept();
1800 }
1801 return true;
1802 }
1803 break;
1804 case QEvent::TouchCancel:
1805 // return in order to avoid the QWindow::event below
1806 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
1807 break;
1808 case QEvent::Enter: {
1809 if (!d->contentItem)
1810 return false;
1811 QEnterEvent *enter = static_cast<QEnterEvent*>(e);
1812 bool accepted = enter->isAccepted();
1813 bool delivered = d->deliverHoverEvent(d->contentItem, scenePos: enter->windowPos(), lastScenePos: d->lastMousePosition,
1814 modifiers: QGuiApplication::keyboardModifiers(), timestamp: 0L, accepted);
1815 d->lastMousePosition = enter->windowPos();
1816 enter->setAccepted(accepted);
1817#if QT_CONFIG(cursor)
1818 d->updateCursor(scenePos: mapFromGlobal(pos: QCursor::pos()));
1819#endif
1820 return delivered;
1821 }
1822 break;
1823 case QEvent::Leave:
1824 d->clearHover();
1825 d->lastMousePosition = QPointF();
1826 break;
1827#if QT_CONFIG(quick_draganddrop)
1828 case QEvent::DragEnter:
1829 case QEvent::DragLeave:
1830 case QEvent::DragMove:
1831 case QEvent::Drop:
1832 d->deliverDragEvent(d->dragGrabber, e);
1833 break;
1834#endif
1835 case QEvent::WindowDeactivate:
1836 if (d->contentItem)
1837 d->contentItem->windowDeactivateEvent();
1838 break;
1839 case QEvent::Close: {
1840 // TOOD Qt 6 (binary incompatible)
1841 // closeEvent(static_cast<QCloseEvent *>(e));
1842 QQuickCloseEvent qev;
1843 qev.setAccepted(e->isAccepted());
1844 emit closing(close: &qev);
1845 e->setAccepted(qev.isAccepted());
1846 } break;
1847 case QEvent::PlatformSurface:
1848 if ((static_cast<QPlatformSurfaceEvent *>(e))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1849 // Ensure that the rendering thread is notified before
1850 // the QPlatformWindow is destroyed.
1851 if (d->windowManager)
1852 d->windowManager->hide(window: this);
1853 }
1854 break;
1855 case QEvent::FocusAboutToChange:
1856#if QT_CONFIG(im)
1857 if (d->activeFocusItem)
1858 qGuiApp->inputMethod()->commit();
1859#endif
1860 if (mouseGrabberItem())
1861 mouseGrabberItem()->ungrabMouse();
1862 break;
1863 case QEvent::UpdateRequest: {
1864 if (d->windowManager)
1865 d->windowManager->handleUpdateRequest(this);
1866 break;
1867 }
1868#if QT_CONFIG(gestures)
1869 case QEvent::NativeGesture:
1870 d->deliverSinglePointEventUntilAccepted(d->pointerEventInstance(ev: e));
1871 break;
1872#endif
1873 case QEvent::ShortcutOverride:
1874 if (d->activeFocusItem)
1875 QCoreApplication::sendEvent(receiver: d->activeFocusItem, event: e);
1876 return true;
1877 case QEvent::LanguageChange:
1878 if (d->contentItem)
1879 QCoreApplication::sendEvent(receiver: d->contentItem, event: e);
1880 break;
1881 case QEvent::InputMethod:
1882 case QEvent::InputMethodQuery:
1883 {
1884 QQuickItem *target = d->activeFocusItem;
1885 // while an input method delivers the event, this window might still be inactive
1886 if (!target) {
1887 target = d->contentItem;
1888 if (!target || !target->isEnabled())
1889 break;
1890 // see setFocusInScope for a similar loop
1891 while (target->isFocusScope() && target->scopedFocusItem() && target->scopedFocusItem()->isEnabled())
1892 target = target->scopedFocusItem();
1893 }
1894 if (target) {
1895 QCoreApplication::sendEvent(receiver: target, event: e);
1896 return true;
1897 }
1898 }
1899 break;
1900 default:
1901 break;
1902 }
1903
1904 if (e->type() == QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))
1905 update();
1906 else if (e->type() == QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure))
1907 d->windowManager->handleContextCreationFailure(window: this);
1908
1909 return QWindow::event(e);
1910}
1911
1912/*! \reimp */
1913void QQuickWindow::keyPressEvent(QKeyEvent *e)
1914{
1915 Q_D(QQuickWindow);
1916 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(),
1917 e->modifiers());
1918 d->deliverKeyEvent(e);
1919}
1920
1921/*! \reimp */
1922void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
1923{
1924 Q_D(QQuickWindow);
1925 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(),
1926 e->modifiers());
1927 d->deliverKeyEvent(e);
1928}
1929
1930void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e)
1931{
1932 if (activeFocusItem) {
1933 QQuickItem *item = activeFocusItem;
1934
1935 // In case of generated event, trigger ShortcutOverride event
1936 if (e->type() == QEvent::KeyPress && e->spontaneous() == false)
1937 qt_sendShortcutOverrideEvent(o: item, timestamp: e->timestamp(),
1938 k: e->key(), mods: e->modifiers(), text: e->text(),
1939 autorep: e->isAutoRepeat(), count: e->count());
1940
1941 e->accept();
1942 QCoreApplication::sendEvent(receiver: item, event: e);
1943 while (!e->isAccepted() && (item = item->parentItem())) {
1944 e->accept();
1945 QCoreApplication::sendEvent(receiver: item, event: e);
1946 }
1947 }
1948}
1949
1950QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
1951{
1952 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1953 QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event);
1954 QMouseEvent *me = new QMouseEvent(event->type(),
1955 transformedLocalPos ? *transformedLocalPos : event->localPos(),
1956 event->windowPos(), event->screenPos(),
1957 event->button(), event->buttons(), event->modifiers());
1958 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(event: me, caps, velocity);
1959 QGuiApplicationPrivate::setMouseEventSource(event: me, source: QGuiApplicationPrivate::mouseEventSource(event));
1960 me->setTimestamp(event->timestamp());
1961 return me;
1962}
1963
1964void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &passiveGrabbers,
1965 QQuickPointerEvent *pointerEvent)
1966{
1967 const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets();
1968 QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult;
1969 hasFiltered.clear();
1970 for (auto handler : passiveGrabbers) {
1971 // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
1972 if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(t: handler)) {
1973 bool alreadyFiltered = false;
1974 QQuickItem *par = handler->parentItem();
1975
1976 // see if we already have sent a filter event to the parent
1977 auto it = std::find_if(first: sendFilteredPointerEventResult.begin(), last: sendFilteredPointerEventResult.end(),
1978 pred: [par](const QPair<QQuickItem *, bool> &pair) { return pair.first == par; });
1979 if (it != sendFilteredPointerEventResult.end()) {
1980 // Yes, the event was already filtered to that parent, do not call it again but use
1981 // the result of the previous call to determine if we should call the handler.
1982 alreadyFiltered = it->second;
1983 } else {
1984 alreadyFiltered = sendFilteredPointerEvent(event: pointerEvent, receiver: par);
1985 sendFilteredPointerEventResult << qMakePair<QQuickItem*, bool>(x: par, y: alreadyFiltered);
1986 }
1987 if (!alreadyFiltered) {
1988 pointerEvent->localize(target: handler->parentItem());
1989 handler->handlePointerEvent(event: pointerEvent);
1990 }
1991 }
1992 }
1993}
1994
1995
1996
1997void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent)
1998{
1999 Q_Q(QQuickWindow);
2000 auto point = pointerEvent->point(i: 0);
2001 lastMousePosition = point->scenePosition();
2002 const bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton);
2003 QQuickItem *grabberItem = point->grabberItem();
2004 if (!grabberItem && isDeliveringTouchAsMouse())
2005 grabberItem = q->mouseGrabberItem();
2006
2007 if (grabberItem) {
2008 bool handled = false;
2009 hasFiltered.clear();
2010 if (sendFilteredPointerEvent(event: pointerEvent, receiver: grabberItem))
2011 handled = true;
2012 // if the grabber is an Item:
2013 // if the update consists of changing button state, don't accept it unless
2014 // the button is one in which the grabber is interested
2015 Qt::MouseButtons acceptedButtons = grabberItem->acceptedMouseButtons();
2016 if (!handled && pointerEvent->button() != Qt::NoButton && acceptedButtons
2017 && !(acceptedButtons & pointerEvent->button())) {
2018 pointerEvent->setAccepted(false);
2019 handled = true;
2020 }
2021
2022 // send update
2023 if (!handled) {
2024 QPointF localPos = grabberItem->mapFromScene(point: lastMousePosition);
2025 auto me = pointerEvent->asMouseEvent(localPos);
2026 me->accept();
2027 QCoreApplication::sendEvent(receiver: grabberItem, event: me);
2028 point->setAccepted(me->isAccepted());
2029 }
2030
2031 // release event: ungrab if no buttons are pressed anymore
2032 if (mouseIsReleased)
2033 removeGrabber(grabber: grabberItem, mouse: true, touch: isDeliveringTouchAsMouse());
2034 deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent);
2035 } else if (auto handler = point->grabberPointerHandler()) {
2036 pointerEvent->localize(target: handler->parentItem());
2037 hasFiltered.clear();
2038 if (!sendFilteredPointerEvent(event: pointerEvent, receiver: handler->parentItem()))
2039 handler->handlePointerEvent(event: pointerEvent);
2040 if (mouseIsReleased)
2041 point->setGrabberPointerHandler(exclusiveGrabber: nullptr, exclusive: true);
2042 deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent);
2043 } else {
2044 bool delivered = false;
2045 if (pointerEvent->isPressEvent()) {
2046 // send initial press
2047 delivered = deliverPressOrReleaseEvent(pointerEvent);
2048 } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) {
2049 // if this is an update or release from an actual mouse,
2050 // and the point wasn't grabbed, deliver only to PointerHandlers:
2051 // passive grabbers first, then the rest
2052 deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent);
2053
2054 // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
2055 if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) {
2056 QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, checkMouseButtons: false, checkAcceptsTouch: false);
2057 for (QQuickItem *item : targetItems) {
2058 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2059 if (!itemPrivate->extra.isAllocated() || itemPrivate->extra->pointerHandlers.isEmpty())
2060 continue;
2061 pointerEvent->localize(target: item);
2062 hasFiltered.clear();
2063 if (!sendFilteredPointerEvent(event: pointerEvent, receiver: item)) {
2064 if (itemPrivate->handlePointerEvent(pointerEvent, avoidExclusiveGrabber: true)) // avoid re-delivering to grabbers
2065 delivered = true;
2066 }
2067 if (point->exclusiveGrabber())
2068 break;
2069 }
2070 }
2071 }
2072
2073 if (!delivered)
2074 // make sure not to accept unhandled events
2075 pointerEvent->setAccepted(false);
2076 }
2077}
2078
2079bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
2080 const QPointF &scenePos, const QPointF &lastScenePos,
2081 Qt::KeyboardModifiers modifiers, ulong timestamp,
2082 bool accepted)
2083{
2084 const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
2085
2086 //create copy of event
2087 QHoverEvent hoverEvent(type, transform.map(p: scenePos), transform.map(p: lastScenePos), modifiers);
2088 hoverEvent.setTimestamp(timestamp);
2089 hoverEvent.setAccepted(accepted);
2090
2091 hasFiltered.clear();
2092 if (sendFilteredMouseEvent(event: &hoverEvent, receiver: item, filteringParent: item->parentItem()))
2093 return true;
2094
2095 QCoreApplication::sendEvent(receiver: item, event: &hoverEvent);
2096
2097 return hoverEvent.isAccepted();
2098}
2099
2100bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
2101 Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted)
2102{
2103 Q_Q(QQuickWindow);
2104 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2105
2106 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
2107 QPointF p = item->mapFromScene(point: scenePos);
2108 if (!item->contains(point: p))
2109 return false;
2110 }
2111
2112 qCDebug(DBG_HOVER_TRACE) << q << item << scenePos << lastScenePos << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled;
2113 if (itemPrivate->subtreeHoverEnabled) {
2114 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
2115 for (int ii = children.count() - 1; ii >= 0; --ii) {
2116 QQuickItem *child = children.at(i: ii);
2117 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(item: child)->culled)
2118 continue;
2119 if (deliverHoverEvent(item: child, scenePos, lastScenePos, modifiers, timestamp, accepted))
2120 return true;
2121 }
2122 }
2123
2124 if (itemPrivate->hasPointerHandlers()) {
2125 QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove);
2126 pointerEvent->point(i: 0)->reset(state: Qt::TouchPointMoved, scenePosition: scenePos, pointId: quint64(1) << 24 /* mouse has device ID 1 */, timestamp, velocity: QVector2D());
2127 pointerEvent->point(i: 0)->setAccepted(true);
2128 pointerEvent->localize(target: item);
2129 for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
2130 if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(object: h))
2131 hh->handlePointerEvent(event: pointerEvent);
2132 }
2133
2134 if (itemPrivate->hoverEnabled) {
2135 QPointF p = item->mapFromScene(point: scenePos);
2136 if (item->contains(point: p)) {
2137 if (!hoverItems.isEmpty() && hoverItems.at(i: 0) == item) {
2138 //move
2139 accepted = sendHoverEvent(type: QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
2140 } else {
2141 QList<QQuickItem *> itemsToHover;
2142 QQuickItem* parent = item;
2143 itemsToHover << item;
2144 while ((parent = parent->parentItem()))
2145 itemsToHover << parent;
2146
2147 // Leaving from previous hovered items until we reach the item or one of its ancestors.
2148 while (!hoverItems.isEmpty() && !itemsToHover.contains(t: hoverItems.at(i: 0))) {
2149 QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
2150 sendHoverEvent(type: QEvent::HoverLeave, item: hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted);
2151 QQuickItemPrivate *hoverLeaveItemPrivate = QQuickItemPrivate::get(item: hoverLeaveItem);
2152 if (hoverLeaveItemPrivate->hasPointerHandlers()) {
2153 for (QQuickPointerHandler *handler : hoverLeaveItemPrivate->extra->pointerHandlers) {
2154 if (auto *hh = qmlobject_cast<QQuickHoverHandler *>(object: handler)) {
2155 QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove);
2156 pointerEvent->point(i: 0)->cancelPassiveGrab(handler: hh);
2157 }
2158 }
2159 }
2160 }
2161
2162 if (!hoverItems.isEmpty() && hoverItems.at(i: 0) == item) {//Not entering a new Item
2163 // ### Shouldn't we send moves for the parent items as well?
2164 accepted = sendHoverEvent(type: QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
2165 } else {
2166 // Enter items that are not entered yet.
2167 int startIdx = -1;
2168 if (!hoverItems.isEmpty())
2169 startIdx = itemsToHover.indexOf(t: hoverItems.at(i: 0)) - 1;
2170 if (startIdx == -1)
2171 startIdx = itemsToHover.count() - 1;
2172
2173 for (int i = startIdx; i >= 0; i--) {
2174 QQuickItem *itemToHover = itemsToHover.at(i);
2175 QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(item: itemToHover);
2176 // The item may be about to be deleted or reparented to another window
2177 // due to another hover event delivered in this function. If that is the
2178 // case, sending a hover event here will cause a crash or other bad
2179 // behavior when the leave event is generated. Checking
2180 // itemToHoverPrivate->window here prevents that case.
2181 if (itemToHoverPrivate->window == q && itemToHoverPrivate->hoverEnabled) {
2182 hoverItems.prepend(t: itemToHover);
2183 sendHoverEvent(type: QEvent::HoverEnter, item: itemToHover, scenePos, lastScenePos, modifiers, timestamp, accepted);
2184 }
2185 }
2186 }
2187 }
2188 return true;
2189 }
2190 }
2191
2192 return false;
2193}
2194
2195// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
2196// in the usual reverse-paint-order until propagation is stopped
2197bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEvent *event)
2198{
2199 Q_ASSERT(event->pointCount() == 1);
2200 QQuickEventPoint *point = event->point(i: 0);
2201 QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, checkMouseButtons: false, checkAcceptsTouch: false);
2202
2203 for (QQuickItem *item : targetItems) {
2204 if (!item->window())
2205 continue;
2206 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2207 event->localize(target: item);
2208 // Let Pointer Handlers have the first shot
2209 itemPrivate->handlePointerEvent(event);
2210 if (point->isAccepted())
2211 return true;
2212 QPointF g = item->window()->mapToGlobal(pos: point->scenePosition().toPoint());
2213#if QT_CONFIG(wheelevent)
2214 // Let the Item have a chance to handle it
2215 if (QQuickPointerScrollEvent *pse = event->asPointerScrollEvent()) {
2216 QWheelEvent wheel(point->position(), g, pse->pixelDelta().toPoint(), pse->angleDelta().toPoint(),
2217 pse->buttons(), pse->modifiers(), pse->phase(),
2218 pse->isInverted(), pse->synthSource());
2219 wheel.setTimestamp(pse->timestamp());
2220 wheel.accept();
2221 QCoreApplication::sendEvent(receiver: item, event: &wheel);
2222 if (wheel.isAccepted()) {
2223 qCDebug(lcWheelTarget) << &wheel << "->" << item;
2224 event->setAccepted(true);
2225 return true;
2226 }
2227 }
2228#endif
2229#if QT_CONFIG(gestures)
2230 if (QQuickPointerNativeGestureEvent *pnge = event->asPointerNativeGestureEvent()) {
2231 QNativeGestureEvent nge(pnge->type(), pnge->device()->qTouchDevice(), point->position(), point->scenePosition(), g,
2232 pnge->value(), 0L, 0L); // TODO can't copy things I can't access
2233 nge.accept();
2234 QCoreApplication::sendEvent(receiver: item, event: &nge);
2235 if (nge.isAccepted()) {
2236 qCDebug(lcGestureTarget) << &nge << "->" << item;
2237 event->setAccepted(true);
2238 return true;
2239 }
2240 }
2241#endif // gestures
2242 }
2243
2244 return false; // it wasn't handled
2245}
2246
2247#if QT_CONFIG(wheelevent)
2248/*! \reimp */
2249void QQuickWindow::wheelEvent(QWheelEvent *event)
2250{
2251 Q_D(QQuickWindow);
2252 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel,
2253 event->angleDelta().x(), event->angleDelta().y());
2254
2255 qCDebug(DBG_MOUSE) << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta() << event->phase();
2256
2257 //if the actual wheel event was accepted, accept the compatibility wheel event and return early
2258 if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate)
2259 return;
2260
2261 event->ignore();
2262 d->deliverPointerEvent(d->pointerEventInstance(ev: event));
2263 d->lastWheelEventAccepted = event->isAccepted();
2264}
2265#endif // wheelevent
2266
2267#if QT_CONFIG(tabletevent)
2268/*! \reimp */
2269void QQuickWindow::tabletEvent(QTabletEvent *event)
2270{
2271 Q_D(QQuickWindow);
2272 qCDebug(lcTablet) << event;
2273 // TODO Qt 6: make sure TabletEnterProximity and TabletLeaveProximity are delivered here
2274 d->deliverPointerEvent(d->pointerEventInstance(ev: event));
2275}
2276#endif // tabletevent
2277
2278bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
2279{
2280 qCDebug(DBG_TOUCH) << event;
2281 Q_Q(QQuickWindow);
2282
2283 if (QQuickItem *grabber = q->mouseGrabberItem())
2284 sendUngrabEvent(grabber, touch: true);
2285 cancelTouchMouseSynthesis();
2286
2287 // A TouchCancel event will typically not contain any points.
2288 // Deliver it to all items and handlers that have active touches.
2289 QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::touchDevice(d: event->device()));
2290 for (int i = 0; i < pointerEvent->pointCount(); ++i)
2291 pointerEvent->point(i)->cancelExclusiveGrabImpl(cancelEvent: event);
2292
2293 // The next touch event can only be a TouchBegin, so clean up.
2294 pointerEvent->clearGrabbers();
2295 return true;
2296}
2297
2298void QQuickWindowPrivate::deliverDelayedTouchEvent()
2299{
2300 // Deliver and delete delayedTouch.
2301 // Set delayedTouch to 0 before delivery to avoid redelivery in case of
2302 // event loop recursions (e.g if it the touch starts a dnd session).
2303 QScopedPointer<QTouchEvent> e(delayedTouch.take());
2304 deliverPointerEvent(pointerEventInstance(ev: e.data()));
2305}
2306
2307bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event)
2308{
2309 Q_Q(QQuickWindow);
2310 Qt::TouchPointStates states = event->touchPointStates();
2311 if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) == 0)
2312 || ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) != 0)) {
2313 // we can only compress something that isn't a press or release
2314 return false;
2315 }
2316
2317 if (!delayedTouch) {
2318 delayedTouch.reset(other: new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
2319 delayedTouch->setTimestamp(event->timestamp());
2320 if (renderControl)
2321 QQuickRenderControlPrivate::get(renderControl)->maybeUpdate();
2322 else if (windowManager)
2323 windowManager->maybeUpdate(window: q);
2324 return true;
2325 }
2326
2327 // check if this looks like the last touch event
2328 if (delayedTouch->type() == event->type() &&
2329 delayedTouch->device() == event->device() &&
2330 delayedTouch->modifiers() == event->modifiers() &&
2331 delayedTouch->touchPoints().count() == event->touchPoints().count())
2332 {
2333 // possible match.. is it really the same?
2334 bool mismatch = false;
2335
2336 QList<QTouchEvent::TouchPoint> tpts = event->touchPoints();
2337 Qt::TouchPointStates states;
2338 for (int i = 0; i < event->touchPoints().count(); ++i) {
2339 const QTouchEvent::TouchPoint &tp = tpts.at(i);
2340 const QTouchEvent::TouchPoint &tpDelayed = delayedTouch->touchPoints().at(i);
2341 if (tp.id() != tpDelayed.id()) {
2342 mismatch = true;
2343 break;
2344 }
2345
2346 if (tpDelayed.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary)
2347 tpts[i].setState(Qt::TouchPointMoved);
2348 tpts[i].setLastPos(tpDelayed.lastPos());
2349 tpts[i].setLastScenePos(tpDelayed.lastScenePos());
2350 tpts[i].setLastScreenPos(tpDelayed.lastScreenPos());
2351 tpts[i].setLastNormalizedPos(tpDelayed.lastNormalizedPos());
2352
2353 states |= tpts.at(i).state();
2354 }
2355
2356 // matching touch event? then merge the new event into the old one
2357 if (!mismatch) {
2358 delayedTouch->setTouchPoints(tpts);
2359 delayedTouch->setTimestamp(event->timestamp());
2360 return true;
2361 }
2362 }
2363
2364 // merging wasn't possible, so deliver the delayed event first, and then delay this one
2365 deliverDelayedTouchEvent();
2366 delayedTouch.reset(other: new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
2367 delayedTouch->setTimestamp(event->timestamp());
2368 return true;
2369}
2370
2371// entry point for touch event delivery:
2372// - translate the event to window coordinates
2373// - compress the event instead of delivering it if applicable
2374// - call deliverTouchPoints to actually dispatch the points
2375void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event)
2376{
2377 translateTouchEvent(touchEvent: event);
2378 if (event->touchPoints().size()) {
2379 auto point = event->touchPoints().at(i: 0);
2380 if (point.state() == Qt::TouchPointReleased) {
2381 lastMousePosition = QPointF();
2382 } else {
2383 lastMousePosition = point.pos();
2384 }
2385 }
2386
2387 qCDebug(DBG_TOUCH) << event;
2388
2389 static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet(varName: "QML_NO_TOUCH_COMPRESSION");
2390
2391 if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) {
2392 deliverPointerEvent(pointerEventInstance(ev: event));
2393 return;
2394 }
2395
2396 if (!compressTouchEvent(event)) {
2397 if (delayedTouch)
2398 deliverDelayedTouchEvent();
2399 deliverPointerEvent(pointerEventInstance(ev: event));
2400 }
2401}
2402
2403/*! \reimp */
2404void QQuickWindow::mousePressEvent(QMouseEvent *event)
2405{
2406 Q_D(QQuickWindow);
2407 d->handleMouseEvent(event);
2408}
2409/*! \reimp */
2410void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
2411{
2412 Q_D(QQuickWindow);
2413 d->handleMouseEvent(event);
2414}
2415/*! \reimp */
2416void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
2417{
2418 Q_D(QQuickWindow);
2419 d->handleMouseEvent(event);
2420}
2421/*! \reimp */
2422void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
2423{
2424 Q_D(QQuickWindow);
2425 d->handleMouseEvent(event);
2426}
2427
2428void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
2429{
2430 if (event->source() == Qt::MouseEventSynthesizedBySystem) {
2431 event->accept();
2432 return;
2433 }
2434 qCDebug(DBG_MOUSE) << "QQuickWindow::handleMouseEvent()" << event->type() << event->localPos() << event->button() << event->buttons();
2435
2436 switch (event->type()) {
2437 case QEvent::MouseButtonPress:
2438 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(),
2439 event->buttons());
2440 deliverPointerEvent(pointerEventInstance(ev: event));
2441 break;
2442 case QEvent::MouseButtonRelease:
2443 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
2444 event->buttons());
2445 deliverPointerEvent(pointerEventInstance(ev: event));
2446#if QT_CONFIG(cursor)
2447 updateCursor(scenePos: event->windowPos());
2448#endif
2449 break;
2450 case QEvent::MouseButtonDblClick:
2451 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
2452 event->button(), event->buttons());
2453 if (allowDoubleClick)
2454 deliverPointerEvent(pointerEventInstance(ev: event));
2455 break;
2456 case QEvent::MouseMove:
2457 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
2458 event->localPos().x(), event->localPos().y());
2459
2460 qCDebug(DBG_HOVER_TRACE) << this;
2461
2462 #if QT_CONFIG(cursor)
2463 updateCursor(scenePos: event->windowPos());
2464 #endif
2465
2466 if (!pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice())->point(i: 0)->exclusiveGrabber()) {
2467 QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition;
2468 lastMousePosition = event->windowPos();
2469
2470 bool accepted = event->isAccepted();
2471 bool delivered = deliverHoverEvent(item: contentItem, scenePos: event->windowPos(), lastScenePos: last, modifiers: event->modifiers(), timestamp: event->timestamp(), accepted);
2472 if (!delivered) {
2473 //take care of any exits
2474 accepted = clearHover(timestamp: event->timestamp());
2475 }
2476 event->setAccepted(accepted);
2477 }
2478 deliverPointerEvent(pointerEventInstance(ev: event));
2479 break;
2480 default:
2481 Q_ASSERT(false);
2482 break;
2483 }
2484}
2485
2486void QQuickWindowPrivate::flushFrameSynchronousEvents()
2487{
2488 Q_Q(QQuickWindow);
2489
2490 if (delayedTouch) {
2491 deliverDelayedTouchEvent();
2492
2493 // Touch events which constantly start animations (such as a behavior tracking
2494 // the mouse point) need animations to start.
2495 QQmlAnimationTimer *ut = QQmlAnimationTimer::instance();
2496 if (ut && ut->hasStartAnimationPending())
2497 ut->startAnimations();
2498 }
2499
2500 // In webOS we already have the alternative to the issue that this
2501 // wanted to address and thus skipping this part won't break anything.
2502#if !defined(Q_OS_WEBOS)
2503 // Once per frame, if any items are dirty, send a synthetic hover,
2504 // in case items have changed position, visibility, etc.
2505 // For instance, during animation (including the case of a ListView
2506 // whose delegates contain MouseAreas), a MouseArea needs to know
2507 // whether it has moved into a position where it is now under the cursor.
2508 if (!q->mouseGrabberItem() && !lastMousePosition.isNull() && dirtyItemList) {
2509 bool accepted = false;
2510 bool delivered = deliverHoverEvent(item: contentItem, scenePos: lastMousePosition, lastScenePos: lastMousePosition, modifiers: QGuiApplication::keyboardModifiers(), timestamp: 0, accepted);
2511 if (!delivered)
2512 clearHover(); // take care of any exits
2513 }
2514#endif
2515}
2516
2517QQuickPointerEvent *QQuickWindowPrivate::queryPointerEventInstance(QQuickPointerDevice *device, QEvent::Type eventType) const
2518{
2519 // Search for a matching reusable event object.
2520 for (QQuickPointerEvent *e : pointerEventInstances) {
2521 // If device can generate native gestures (e.g. a trackpad), there might be multiple QQuickPointerEvents:
2522 // QQuickPointerNativeGestureEvent, QQuickPointerScrollEvent, and QQuickPointerTouchEvent.
2523 // Use eventType to disambiguate.
2524#if QT_CONFIG(gestures)
2525 if ((eventType == QEvent::NativeGesture) != bool(e->asPointerNativeGestureEvent()))
2526 continue;
2527#endif
2528 if ((eventType == QEvent::Wheel) != bool(e->asPointerScrollEvent()))
2529 continue;
2530 // Otherwise we assume there's only one event type per device.
2531 // More disambiguation tests might need to be added above if that changes later.
2532 if (e->device() == device)
2533 return e;
2534 }
2535 return nullptr;
2536}
2537
2538QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevice *device, QEvent::Type eventType) const
2539{
2540 QQuickPointerEvent *ev = queryPointerEventInstance(device, eventType);
2541 if (ev)
2542 return ev;
2543 QQuickWindow *q = const_cast<QQuickWindow*>(q_func());
2544 switch (device->type()) {
2545 case QQuickPointerDevice::Mouse:
2546 // QWindowSystemInterface::handleMouseEvent() does not take a device parameter:
2547 // we assume all mouse events come from one mouse (the "core pointer").
2548 // So when the event is a mouse event, device == QQuickPointerDevice::genericMouseDevice()
2549 if (eventType == QEvent::Wheel)
2550 ev = new QQuickPointerScrollEvent(q, device);
2551 else
2552 ev = new QQuickPointerMouseEvent(q, device);
2553 break;
2554 case QQuickPointerDevice::TouchPad:
2555 case QQuickPointerDevice::TouchScreen:
2556#if QT_CONFIG(gestures)
2557 if (eventType == QEvent::NativeGesture)
2558 ev = new QQuickPointerNativeGestureEvent(q, device);
2559 else // assume QEvent::Type is one of TouchBegin/Update/End
2560#endif
2561 ev = new QQuickPointerTouchEvent(q, device);
2562 break;
2563#if QT_CONFIG(tabletevent)
2564 case QQuickPointerDevice::Stylus:
2565 case QQuickPointerDevice::Airbrush:
2566 case QQuickPointerDevice::Puck:
2567 ev = new QQuickPointerTabletEvent(q, device);
2568 break;
2569#endif
2570 default:
2571 break;
2572 }
2573 pointerEventInstances << ev;
2574 return ev;
2575}
2576
2577/*!
2578 \internal
2579 Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event.
2580
2581 There is a unique instance per QQuickPointerDevice, which is determined
2582 from \a event's device.
2583*/
2584QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) const
2585{
2586 QQuickPointerDevice *dev = nullptr;
2587 switch (event->type()) {
2588 case QEvent::MouseButtonPress:
2589 case QEvent::MouseButtonRelease:
2590 case QEvent::MouseButtonDblClick:
2591 case QEvent::MouseMove:
2592 case QEvent::Wheel:
2593 dev = QQuickPointerDevice::genericMouseDevice();
2594 break;
2595 case QEvent::TouchBegin:
2596 case QEvent::TouchUpdate:
2597 case QEvent::TouchEnd:
2598 case QEvent::TouchCancel:
2599 dev = QQuickPointerDevice::touchDevice(d: static_cast<QTouchEvent *>(event)->device());
2600 break;
2601#if QT_CONFIG(tabletevent)
2602 case QEvent::TabletPress:
2603 case QEvent::TabletMove:
2604 case QEvent::TabletRelease:
2605 case QEvent::TabletEnterProximity:
2606 case QEvent::TabletLeaveProximity:
2607 dev = QQuickPointerDevice::tabletDevice(event: static_cast<QTabletEvent *>(event));
2608 break;
2609#endif
2610#if QT_CONFIG(gestures)
2611 case QEvent::NativeGesture:
2612 dev = QQuickPointerDevice::touchDevice(d: static_cast<QNativeGestureEvent *>(event)->device());
2613 break;
2614#endif
2615 default:
2616 break;
2617 }
2618
2619 Q_ASSERT(dev);
2620 auto pev = pointerEventInstance(device: dev, eventType: event->type());
2621 Q_ASSERT(pev);
2622 return pev->reset(ev: event);
2623}
2624
2625void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
2626{
2627 Q_Q(QQuickWindow);
2628 // If users spin the eventloop as a result of event delivery, we disable
2629 // event compression and send events directly. This is because we consider
2630 // the usecase a bit evil, but we at least don't want to lose events.
2631 ++pointerEventRecursionGuard;
2632
2633 skipDelivery.clear();
2634 if (event->asPointerMouseEvent()) {
2635 deliverMouseEvent(pointerEvent: event->asPointerMouseEvent());
2636 // failsafe: never allow any kind of grab to persist after release
2637 if (event->isReleaseEvent() && event->buttons() == Qt::NoButton) {
2638 QQuickItem *oldGrabber = q->mouseGrabberItem();
2639 event->clearGrabbers();
2640 sendUngrabEvent(grabber: oldGrabber, touch: false);
2641 }
2642 } else if (event->asPointerTouchEvent()) {
2643 deliverTouchEvent(event->asPointerTouchEvent());
2644 } else {
2645 deliverSinglePointEventUntilAccepted(event);
2646 // If any handler got interested in the tablet event, we don't want to receive a synth-mouse event from QtGui
2647 // TODO Qt 6: QTabletEvent will be accepted by default, like other events
2648 if (event->asPointerTabletEvent() &&
2649 (!event->point(i: 0)->passiveGrabbers().isEmpty() || event->point(i: 0)->exclusiveGrabber()))
2650 event->setAccepted(true);
2651 }
2652
2653 event->reset(ev: nullptr);
2654
2655 --pointerEventRecursionGuard;
2656}
2657
2658// check if item or any of its child items contain the point, or if any pointer handler "wants" the point
2659// FIXME: should this be iterative instead of recursive?
2660// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added.
2661// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR
2662// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't.
2663QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const
2664{
2665 QVector<QQuickItem *> targets;
2666 auto itemPrivate = QQuickItemPrivate::get(item);
2667 QPointF itemPos = item->mapFromScene(point: point->scenePosition());
2668 // if the item clips, we can potentially return early
2669 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
2670 if (!item->contains(point: itemPos))
2671 return targets;
2672 }
2673
2674 // recurse for children
2675 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
2676 for (int ii = children.count() - 1; ii >= 0; --ii) {
2677 QQuickItem *child = children.at(i: ii);
2678 auto childPrivate = QQuickItemPrivate::get(item: child);
2679 if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
2680 continue;
2681 targets << pointerTargets(item: child, point, checkMouseButtons, checkAcceptsTouch);
2682 }
2683
2684 bool relevant = item->contains(point: itemPos);
2685 if (itemPrivate->hasPointerHandlers()) {
2686 if (!relevant)
2687 if (itemPrivate->anyPointerHandlerWants(point))
2688 relevant = true;
2689 } else {
2690 if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
2691 relevant = false;
2692 if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
2693 relevant = false;
2694 }
2695 if (relevant)
2696 targets << item; // add this item last: children take precedence
2697 return targets;
2698}
2699
2700// return the joined lists
2701// list1 has priority, common items come last
2702QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const
2703{
2704 QVector<QQuickItem *> targets = list1;
2705 // start at the end of list2
2706 // if item not in list, append it
2707 // if item found, move to next one, inserting before the last found one
2708 int insertPosition = targets.length();
2709 for (int i = list2.length() - 1; i >= 0; --i) {
2710 int newInsertPosition = targets.lastIndexOf(t: list2.at(i), from: insertPosition);
2711 if (newInsertPosition >= 0) {
2712 Q_ASSERT(newInsertPosition <= insertPosition);
2713 insertPosition = newInsertPosition;
2714 }
2715 // check for duplicates, only insert if the item isn't there already
2716 if (insertPosition == targets.size() || list2.at(i) != targets.at(i: insertPosition))
2717 targets.insert(i: insertPosition, t: list2.at(i));
2718 }
2719 return targets;
2720}
2721
2722void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
2723{
2724 qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent();
2725
2726 if (event->isPressEvent())
2727 deliverPressOrReleaseEvent(event);
2728 if (!event->allUpdatedPointsAccepted())
2729 deliverUpdatedTouchPoints(event);
2730 if (event->isReleaseEvent())
2731 deliverPressOrReleaseEvent(event, handlersOnly: true);
2732
2733 // Remove released points from itemForTouchPointId
2734 bool allReleased = true;
2735 int pointCount = event->pointCount();
2736 for (int i = 0; i < pointCount; ++i) {
2737 QQuickEventPoint *point = event->point(i);
2738 if (point->state() == QQuickEventPoint::Released) {
2739 int id = point->pointId();
2740 qCDebug(DBG_TOUCH_TARGET) << "TP" << Qt::hex << id << "released";
2741 point->setGrabberItem(nullptr);
2742 if (id == touchMouseId)
2743 cancelTouchMouseSynthesis();
2744 } else {
2745 allReleased = false;
2746 }
2747 }
2748
2749 if (allReleased) {
2750 if (Q_UNLIKELY(!event->exclusiveGrabbers().isEmpty()))
2751 qWarning() << "No release received for some grabbers" << event->exclusiveGrabbers();
2752 event->clearGrabbers();
2753 }
2754}
2755
2756// Deliver touch points to existing grabbers
2757void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event)
2758{
2759 bool done = false;
2760 const auto grabbers = event->exclusiveGrabbers();
2761 for (auto grabber : grabbers) {
2762 // The grabber is guaranteed to be either an item or a handler.
2763 QQuickItem *receiver = qmlobject_cast<QQuickItem *>(object: grabber);
2764 if (!receiver) {
2765 // The grabber is not an item? It's a handler then. Let it have the event first.
2766 QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber);
2767 receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem();
2768 hasFiltered.clear();
2769 if (sendFilteredPointerEvent(event, receiver))
2770 done = true;
2771 event->localize(target: receiver);
2772 handler->handlePointerEvent(event);
2773 }
2774 if (done)
2775 break;
2776 // If the grabber is an item or the grabbing handler didn't handle it,
2777 // then deliver the event to the item (which may have multiple handlers).
2778 deliverMatchingPointsToItem(item: receiver, pointerEvent: event);
2779 }
2780
2781 // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once)
2782 int pointCount = event->pointCount();
2783 for (int i = 0; i < pointCount; ++i) {
2784 QQuickEventPoint *point = event->point(i);
2785 deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent: event);
2786 }
2787
2788 if (done)
2789 return;
2790
2791 // If some points weren't grabbed, deliver only to non-grabber PointerHandlers in reverse paint order
2792 if (!event->allPointsGrabbed()) {
2793 QVector<QQuickItem *> targetItems;
2794 for (int i = 0; i < pointCount; ++i) {
2795 QQuickEventPoint *point = event->point(i);
2796 if (point->state() == QQuickEventPoint::Pressed)
2797 continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints
2798 QVector<QQuickItem *> targetItemsForPoint = pointerTargets(item: contentItem, point, checkMouseButtons: false, checkAcceptsTouch: false);
2799 if (targetItems.count()) {
2800 targetItems = mergePointerTargets(list1: targetItems, list2: targetItemsForPoint);
2801 } else {
2802 targetItems = targetItemsForPoint;
2803 }
2804 }
2805 for (QQuickItem *item : targetItems) {
2806 if (grabbers.contains(t: item))
2807 continue;
2808 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2809 event->localize(target: item);
2810 itemPrivate->handlePointerEvent(event, avoidExclusiveGrabber: true); // avoid re-delivering to grabbers
2811 if (event->allPointsGrabbed())
2812 break;
2813 }
2814 }
2815}
2816
2817// Deliver an event containing newly pressed or released touch points
2818bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly)
2819{
2820 int pointCount = event->pointCount();
2821 QVector<QQuickItem *> targetItems;
2822 bool isTouchEvent = (event->asPointerTouchEvent() != nullptr);
2823 if (isTouchEvent && event->isPressEvent() && isDeliveringTouchAsMouse()) {
2824 if (const QQuickEventPoint *point = pointerEventInstance(device: touchMouseDevice)->pointById(pointId: touchMouseId)) {
2825 // When a second point is pressed, if the first point's existing
2826 // grabber was a pointer handler while a filtering parent is filtering
2827 // the same first point _as mouse_: we're starting over with delivery,
2828 // so we need to allow the second point to now be sent as a synth-mouse
2829 // instead of the first one, so that filtering parents (maybe even the
2830 // same one) can get a chance to see the second touchpoint as a
2831 // synth-mouse and perhaps grab it. Ideally we would always do this
2832 // when a new touchpoint is pressed, but this compromise fixes
2833 // QTBUG-70998 and avoids breaking tst_FlickableInterop::touchDragSliderAndFlickable
2834 if (point->grabberPointerHandler())
2835 cancelTouchMouseSynthesis();
2836 } else {
2837 qCWarning(DBG_TOUCH_TARGET) << "during delivery of touch press, synth-mouse ID" << Qt::hex << touchMouseId << "is missing from" << event;
2838 }
2839 }
2840 for (int i = 0; i < pointCount; ++i) {
2841 auto point = event->point(i);
2842 if (point->state() == QQuickEventPoint::Pressed && !event->isDoubleClickEvent())
2843 point->clearPassiveGrabbers();
2844 point->setAccepted(false); // because otherwise touchEventForItem will ignore it
2845 if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released)
2846 point->setGrabberPointerHandler(exclusiveGrabber: nullptr, exclusive: true);
2847 QVector<QQuickItem *> targetItemsForPoint = pointerTargets(item: contentItem, point, checkMouseButtons: !isTouchEvent, checkAcceptsTouch: isTouchEvent);
2848 if (targetItems.count()) {
2849 targetItems = mergePointerTargets(list1: targetItems, list2: targetItemsForPoint);
2850 } else {
2851 targetItems = targetItemsForPoint;
2852 }
2853 }
2854
2855 for (QQuickItem *item : targetItems) {
2856 if (!event->m_event) {
2857 qWarning(msg: "event went missing during delivery! (nested sendEvent() is not allowed)");
2858 break;
2859 }
2860 hasFiltered.clear();
2861 if (!handlersOnly && sendFilteredPointerEvent(event, receiver: item)) {
2862 if (event->isAccepted()) {
2863 for (int i = 0; i < event->pointCount(); ++i)
2864 event->point(i)->setAccepted();
2865 return true;
2866 }
2867 skipDelivery.append(t: item);
2868 }
2869
2870 // Do not deliverMatchingPointsTo any item for which the filtering parent already intercepted the event,
2871 // nor to any item which already had a chance to filter.
2872 if (skipDelivery.contains(t: item))
2873 continue;
2874 if (!event->m_event) {
2875 qWarning(msg: "event went missing during delivery! (nested sendEvent() is not allowed)");
2876 break;
2877 }
2878 deliverMatchingPointsToItem(item, pointerEvent: event, handlersOnly);
2879 if (event->allPointsAccepted())
2880 handlersOnly = true;
2881 }
2882
2883 return event->allPointsAccepted();
2884}
2885
2886void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly)
2887{
2888 Q_Q(QQuickWindow);
2889 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2890#if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2891 // QTBUG-85379
2892 // In QT_VERSION below 6.0.0 touchEnabled for QtQuickItems is set by default to true
2893 // It causes delivering touch events to Items which are not interested
2894 // In some cases (like using Material Style in Android) it may cause a crash
2895 if (itemPrivate->wasDeleted)
2896 return;
2897#endif
2898 pointerEvent->localize(target: item);
2899
2900 // Let the Item's handlers (if any) have the event first.
2901 // However, double click should never be delivered to handlers.
2902 if (!pointerEvent->isDoubleClickEvent()) {
2903 bool wasAccepted = pointerEvent->allPointsAccepted();
2904 itemPrivate->handlePointerEvent(pointerEvent);
2905 allowDoubleClick = wasAccepted || !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted());
2906 }
2907 if (handlersOnly)
2908 return;
2909
2910 // If all points are released and the item is not the grabber, it doesn't get the event.
2911 // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario.
2912 if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent()
2913 && !pointerEvent->exclusiveGrabbers().contains(t: item))
2914 return;
2915
2916 // TODO: unite this mouse point delivery with the synthetic mouse event below
2917 auto event = pointerEvent->asPointerMouseEvent();
2918 if (event && item->acceptedMouseButtons() & event->button()) {
2919 auto point = event->point(i: 0);
2920 // The only reason to already have a mouse grabber here is
2921 // synthetic events - flickable sends one when setPressDelay is used.
2922 auto oldMouseGrabber = q->mouseGrabberItem();
2923 QPointF localPos = item->mapFromScene(point: point->scenePosition());
2924 QMouseEvent *me = event->asMouseEvent(localPos);
2925 me->accept();
2926 QCoreApplication::sendEvent(receiver: item, event: me);
2927 if (me->isAccepted()) {
2928 auto mouseGrabber = q->mouseGrabberItem();
2929 if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
2930 item->mouseUngrabEvent();
2931 } else if (item->isEnabled() && item->isVisible()) {
2932 item->grabMouse();
2933 }
2934 point->setAccepted(true);
2935 }
2936 return;
2937 }
2938
2939 QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent();
2940 if (!ptEvent)
2941 return;
2942
2943 QScopedPointer<QTouchEvent> touchEvent(ptEvent->touchEventForItem(item));
2944 if (!touchEvent)
2945 return;
2946
2947 qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item;
2948 bool eventAccepted = false;
2949
2950 // If any parent filters the event, we're done.
2951 hasFiltered.clear();
2952 if (sendFilteredPointerEvent(event: pointerEvent, receiver: item))
2953 return;
2954
2955 // Deliver the touch event to the given item
2956 qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item;
2957 QCoreApplication::sendEvent(receiver: item, event: touchEvent.data());
2958 eventAccepted = touchEvent->isAccepted();
2959
2960 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
2961 if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
2962 if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
2963 // send mouse event
2964 if (deliverTouchAsMouse(item, pointerEvent: ptEvent))
2965 eventAccepted = true;
2966 }
2967 }
2968
2969 if (eventAccepted) {
2970 // If the touch was accepted (regardless by whom or in what form),
2971 // update accepted new points.
2972 bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent();
2973 for (const auto &point: qAsConst(t: touchEvent->touchPoints())) {
2974 if (auto pointerEventPoint = ptEvent->pointById(pointId: point.id())) {
2975 pointerEventPoint->setAccepted();
2976 if (isPressOrRelease)
2977 pointerEventPoint->setGrabberItem(item);
2978 }
2979 }
2980 } else {
2981 // But if the event was not accepted then we know this item
2982 // will not be interested in further updates for those touchpoint IDs either.
2983 for (const auto &point: qAsConst(t: touchEvent->touchPoints())) {
2984 if (point.state() == Qt::TouchPointPressed) {
2985 if (auto *tp = ptEvent->pointById(pointId: point.id())) {
2986 if (tp->exclusiveGrabber() == item) {
2987 qCDebug(DBG_TOUCH_TARGET) << "TP" << Qt::hex << point.id() << "disassociated";
2988 tp->setGrabberItem(nullptr);
2989 }
2990 }
2991 }
2992 }
2993 }
2994}
2995
2996#if QT_CONFIG(quick_draganddrop)
2997void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
2998{
2999 grabber->resetTarget();
3000 QQuickDragGrabber::iterator grabItem = grabber->begin();
3001 if (grabItem != grabber->end()) {
3002 Q_ASSERT(event->type() != QEvent::DragEnter);
3003 if (event->type() == QEvent::Drop) {
3004 QDropEvent *e = static_cast<QDropEvent *>(event);
3005 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(at: grabItem)) {
3006 QPointF p = (**grabItem)->mapFromScene(point: e->pos());
3007 QDropEvent translatedEvent(
3008 p.toPoint(),
3009 e->possibleActions(),
3010 e->mimeData(),
3011 e->mouseButtons(),
3012 e->keyboardModifiers());
3013 QQuickDropEventEx::copyActions(to: &translatedEvent, from: *e);
3014 QCoreApplication::sendEvent(receiver: **grabItem, event: &translatedEvent);
3015 e->setAccepted(translatedEvent.isAccepted());
3016 e->setDropAction(translatedEvent.dropAction());
3017 grabber->setTarget(*