1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmlnotifier_p.h"
5#include "qqmlproperty_p.h"
6#include <QtCore/qdebug.h>
7#include <private/qthread_p.h>
8
9QT_BEGIN_NAMESPACE
10
11typedef void (*Callback)(QQmlNotifierEndpoint *, void **);
12
13void QQmlBoundSignal_callback(QQmlNotifierEndpoint *, void **);
14void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **);
15void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *, void **);
16void QQmlPropertyGuard_callback(QQmlNotifierEndpoint *, void **);
17
18static Callback QQmlNotifier_callbacks[] = {
19 nullptr,
20 QQmlBoundSignal_callback,
21 QQmlJavaScriptExpressionGuard_callback,
22 QQmlVMEMetaObjectEndpoint_callback,
23 QQmlPropertyGuard_callback
24};
25
26namespace {
27 struct NotifyListTraversalData {
28 NotifyListTraversalData(QQmlNotifierEndpoint *ep = nullptr)
29 : originalSenderPtr(0)
30 , disconnectWatch(nullptr)
31 , endpoint(ep)
32 {}
33
34 qintptr originalSenderPtr;
35 qintptr *disconnectWatch;
36 QQmlNotifierEndpoint *endpoint;
37 };
38}
39
40void QQmlNotifier::notify(QQmlData *ddata, int notifierIndex)
41{
42 if (QQmlNotifierEndpoint *ep = ddata->notify(index: notifierIndex))
43 emitNotify(ep, a: nullptr);
44}
45
46void QQmlNotifier::emitNotify(QQmlNotifierEndpoint *endpoint, void **a)
47{
48 QVarLengthArray<NotifyListTraversalData> stack;
49 while (endpoint) {
50 stack.append(t: NotifyListTraversalData(endpoint));
51 endpoint = endpoint->next;
52 }
53
54 int i = 0;
55 for (; i < stack.size(); ++i) {
56 NotifyListTraversalData &data = stack[i];
57
58 if (!data.endpoint->isNotifying()) {
59 data.endpoint->startNotifying(originalSenderPtr: &data.originalSenderPtr);
60 data.disconnectWatch = &data.originalSenderPtr;
61 } else {
62 data.disconnectWatch = (qintptr *)(data.endpoint->senderPtr & ~0x1);
63 }
64 }
65
66 while (--i >= 0) {
67 NotifyListTraversalData &data = stack[i];
68 if (*data.disconnectWatch) {
69 Q_ASSERT(QQmlNotifier_callbacks[data.endpoint->callback]);
70 QQmlNotifier_callbacks[data.endpoint->callback](data.endpoint, a);
71 if (data.disconnectWatch == &data.originalSenderPtr && data.originalSenderPtr) {
72 data.endpoint->stopNotifying(originalSenderPtr: &data.originalSenderPtr);
73 }
74 }
75 }
76}
77
78/*! \internal
79 \a sourceSignal MUST be in the signal index range (see QObjectPrivate::signalIndex()).
80 This is different from QMetaMethod::methodIndex().
81*/
82void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine *engine, bool doNotify)
83{
84 disconnect();
85
86 Q_ASSERT(engine);
87 if (QObjectPrivate::get(o: source)->threadData.loadRelaxed()->threadId.loadRelaxed() !=
88 QObjectPrivate::get(o: engine)->threadData.loadRelaxed()->threadId.loadRelaxed()) {
89
90 QString sourceName;
91 QDebug(&sourceName) << source;
92 sourceName = sourceName.left(n: sourceName.size() - 1);
93 QString engineName;
94 QDebug(&engineName).nospace() << engine;
95 engineName = engineName.left(n: engineName.size() - 1);
96
97 qFatal(msg: "QQmlEngine: Illegal attempt to connect to %s that is in"
98 " a different thread than the QML engine %s.", qPrintable(sourceName),
99 qPrintable(engineName));
100 }
101
102 setSender(qintptr(source));
103 this->sourceSignal = sourceSignal;
104 QQmlPropertyPrivate::flushSignal(sender: source, signal_index: sourceSignal);
105 QQmlData *ddata = QQmlData::get(object: source, create: true);
106 ddata->addNotify(index: sourceSignal, this);
107 if (doNotify) {
108 needsConnectNotify = doNotify;
109 QObjectPrivate * const priv = QObjectPrivate::get(o: source);
110 priv->connectNotify(signal: QMetaObjectPrivate::signal(m: source->metaObject(), signal_index: sourceSignal));
111 }
112}
113
114QT_END_NAMESPACE
115
116

source code of qtdeclarative/src/qml/qml/qqmlnotifier.cpp