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#ifndef QQMLNOTIFIER_P_H
5#define QQMLNOTIFIER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/qmetaobject.h>
19#include <private/qmetaobject_p.h>
20#include <private/qtqmlglobal_p.h>
21
22QT_BEGIN_NAMESPACE
23
24class QQmlNotifierEndpoint;
25class QQmlData;
26class Q_QML_PRIVATE_EXPORT QQmlNotifier
27{
28public:
29 inline QQmlNotifier();
30 inline ~QQmlNotifier();
31 inline void notify();
32
33 static void notify(QQmlData *ddata, int notifierIndex);
34
35private:
36 friend class QQmlData;
37 friend class QQmlNotifierEndpoint;
38 friend class QQmlThreadNotifierProxyObject;
39
40 static void emitNotify(QQmlNotifierEndpoint *, void **a);
41 QQmlNotifierEndpoint *endpoints = nullptr;
42};
43
44class QQmlEngine;
45class QQmlNotifierEndpoint
46{
47 QQmlNotifierEndpoint *next;
48 QQmlNotifierEndpoint **prev;
49public:
50 // QQmlNotifierEndpoint can only invoke one of a set of pre-defined callbacks.
51 // To add another callback, extend this enum and add the callback to the top
52 // of qqmlnotifier.cpp. Four bits are reserved for the callback, so there can
53 // be up to 15 of them (0 is reserved).
54 enum Callback {
55 None = 0,
56 QQmlBoundSignal = 1,
57 QQmlJavaScriptExpressionGuard = 2,
58 QQmlVMEMetaObjectEndpoint = 3,
59 QQmlPropertyGuard = 4,
60 };
61
62 inline QQmlNotifierEndpoint(Callback callback);
63 inline ~QQmlNotifierEndpoint();
64
65 inline bool isConnected() const;
66 inline bool isConnected(QObject *source, int sourceSignal) const;
67 inline bool isConnected(QQmlNotifier *) const;
68
69 void connect(QObject *source, int sourceSignal, QQmlEngine *engine, bool doNotify = true);
70 inline void connect(QQmlNotifier *);
71 inline void disconnect();
72
73 inline bool isNotifying() const;
74 inline void startNotifying(qintptr *originalSenderPtr);
75 inline void stopNotifying(qintptr *originalSenderPtr);
76
77 inline void cancelNotify();
78
79 inline int signalIndex() const { return sourceSignal; }
80
81 inline qintptr sender() const;
82 inline void setSender(qintptr sender);
83
84 inline QObject *senderAsObject() const;
85 inline QQmlNotifier *senderAsNotifier() const;
86
87private:
88 friend class QQmlData;
89 friend class QQmlNotifier;
90
91 // Contains either the QObject*, or the QQmlNotifier* that this
92 // endpoint is connected to. While the endpoint is notifying, the
93 // senderPtr points to another qintptr that contains this value.
94 qintptr senderPtr;
95
96 Callback callback:4;
97 int needsConnectNotify:1;
98 // The index is in the range returned by QObjectPrivate::signalIndex().
99 // This is different from QMetaMethod::methodIndex().
100 signed int sourceSignal:27;
101};
102
103QQmlNotifier::QQmlNotifier()
104{
105}
106
107QQmlNotifier::~QQmlNotifier()
108{
109 QQmlNotifierEndpoint *endpoint = endpoints;
110 while (endpoint) {
111 QQmlNotifierEndpoint *n = endpoint;
112 endpoint = n->next;
113 n->setSender(0x0);
114 n->next = nullptr;
115 n->prev = nullptr;
116 n->sourceSignal = -1;
117 }
118 endpoints = nullptr;
119}
120
121void QQmlNotifier::notify()
122{
123 void *args[] = { nullptr };
124 if (endpoints) emitNotify(endpoints, a: args);
125}
126
127QQmlNotifierEndpoint::QQmlNotifierEndpoint(Callback callback)
128: next(nullptr), prev(nullptr), senderPtr(0), callback(callback), needsConnectNotify(false), sourceSignal(-1)
129{
130}
131
132QQmlNotifierEndpoint::~QQmlNotifierEndpoint()
133{
134 disconnect();
135}
136
137bool QQmlNotifierEndpoint::isConnected() const
138{
139 return prev != nullptr;
140}
141
142/*! \internal
143 \a sourceSignal MUST be in the signal index range (see QObjectPrivate::signalIndex()).
144 This is different from QMetaMethod::methodIndex().
145*/
146bool QQmlNotifierEndpoint::isConnected(QObject *source, int sourceSignal) const
147{
148 return this->sourceSignal != -1 && senderAsObject() == source &&
149 this->sourceSignal == sourceSignal;
150}
151
152bool QQmlNotifierEndpoint::isConnected(QQmlNotifier *notifier) const
153{
154 return sourceSignal == -1 && senderAsNotifier() == notifier;
155}
156
157void QQmlNotifierEndpoint::connect(QQmlNotifier *notifier)
158{
159 disconnect();
160
161 next = notifier->endpoints;
162 if (next) { next->prev = &next; }
163 notifier->endpoints = this;
164 prev = &notifier->endpoints;
165 setSender(qintptr(notifier));
166}
167
168void QQmlNotifierEndpoint::disconnect()
169{
170 // Remove from notifier chain before calling disconnectNotify(), so that that
171 // QObject::receivers() returns the correct value in there
172 if (next) next->prev = prev;
173 if (prev) *prev = next;
174
175 if (sourceSignal != -1 && needsConnectNotify) {
176 QObject * const obj = senderAsObject();
177 Q_ASSERT(obj);
178 QObjectPrivate * const priv = QObjectPrivate::get(o: obj);
179
180 // In some degenerate cases an object being destructed might be unable
181 // to produce a metaObject(). Therefore we check here.
182 if (const QMetaObject *mo = obj->metaObject())
183 priv->disconnectNotify(signal: QMetaObjectPrivate::signal(m: mo, signal_index: sourceSignal));
184 }
185
186 setSender(0x0);
187 next = nullptr;
188 prev = nullptr;
189 sourceSignal = -1;
190}
191
192/*!
193Returns true if a notify is in progress. This means that the signal or QQmlNotifier
194that this endpoing is connected to has been triggered, but this endpoint's callback has not
195yet been called.
196
197An in progress notify can be cancelled by calling cancelNotify.
198*/
199bool QQmlNotifierEndpoint::isNotifying() const
200{
201 return senderPtr & 0x1;
202}
203
204void QQmlNotifierEndpoint::startNotifying(qintptr *originalSenderPtr)
205{
206 Q_ASSERT(*originalSenderPtr == 0);
207 // Set the endpoint to notifying:
208 // - Save the original senderPtr,
209 *originalSenderPtr = senderPtr;
210 // - Take a pointer of it,
211 // - And assign that to the senderPtr, including a flag to signify "notifying".
212 senderPtr = qintptr(originalSenderPtr) | 0x1;
213}
214
215void QQmlNotifierEndpoint::stopNotifying(qintptr *originalSenderPtr)
216{
217 // End of notifying, restore values
218 Q_ASSERT((senderPtr & ~0x1) == qintptr(originalSenderPtr));
219 senderPtr = *originalSenderPtr;
220 *originalSenderPtr = 0;
221}
222
223/*!
224Cancel any notifies that are in progress.
225*/
226void QQmlNotifierEndpoint::cancelNotify()
227{
228 if (isNotifying()) {
229 auto *ptr = (qintptr *)(senderPtr & ~0x1);
230 Q_ASSERT(ptr);
231 senderPtr = *ptr;
232 *ptr = 0;
233 }
234}
235
236qintptr QQmlNotifierEndpoint::sender() const
237{
238 return isNotifying() ? *(qintptr *)(senderPtr & ~0x1) : senderPtr;
239}
240
241void QQmlNotifierEndpoint::setSender(qintptr sender)
242{
243 // If we're just notifying, we write through to the originalSenderPtr
244 if (isNotifying())
245 *(qintptr *)(senderPtr & ~0x1) = sender;
246 else
247 senderPtr = sender;
248}
249
250QObject *QQmlNotifierEndpoint::senderAsObject() const
251{
252 return (QObject *)(sender());
253}
254
255QQmlNotifier *QQmlNotifierEndpoint::senderAsNotifier() const
256{
257 return (QQmlNotifier *)(sender());
258}
259
260QT_END_NAMESPACE
261
262#endif // QQMLNOTIFIER_P_H
263
264

source code of qtdeclarative/src/qml/qml/qqmlnotifier_p.h