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 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | class QQmlNotifierEndpoint; |
25 | class QQmlData; |
26 | class Q_QML_PRIVATE_EXPORT QQmlNotifier |
27 | { |
28 | public: |
29 | inline QQmlNotifier(); |
30 | inline ~QQmlNotifier(); |
31 | inline void notify(); |
32 | |
33 | static void notify(QQmlData *ddata, int notifierIndex); |
34 | |
35 | private: |
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 | |
44 | class QQmlEngine; |
45 | class QQmlNotifierEndpoint |
46 | { |
47 | QQmlNotifierEndpoint *next; |
48 | QQmlNotifierEndpoint **prev; |
49 | public: |
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 | |
87 | private: |
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 | |
103 | QQmlNotifier::QQmlNotifier() |
104 | { |
105 | } |
106 | |
107 | QQmlNotifier::~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 | |
121 | void QQmlNotifier::notify() |
122 | { |
123 | void *args[] = { nullptr }; |
124 | if (endpoints) emitNotify(endpoints, a: args); |
125 | } |
126 | |
127 | QQmlNotifierEndpoint::QQmlNotifierEndpoint(Callback callback) |
128 | : next(nullptr), prev(nullptr), senderPtr(0), callback(callback), needsConnectNotify(false), sourceSignal(-1) |
129 | { |
130 | } |
131 | |
132 | QQmlNotifierEndpoint::~QQmlNotifierEndpoint() |
133 | { |
134 | disconnect(); |
135 | } |
136 | |
137 | bool 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 | */ |
146 | bool QQmlNotifierEndpoint::isConnected(QObject *source, int sourceSignal) const |
147 | { |
148 | return this->sourceSignal != -1 && senderAsObject() == source && |
149 | this->sourceSignal == sourceSignal; |
150 | } |
151 | |
152 | bool QQmlNotifierEndpoint::isConnected(QQmlNotifier *notifier) const |
153 | { |
154 | return sourceSignal == -1 && senderAsNotifier() == notifier; |
155 | } |
156 | |
157 | void QQmlNotifierEndpoint::connect(QQmlNotifier *notifier) |
158 | { |
159 | disconnect(); |
160 | |
161 | next = notifier->endpoints; |
162 | if (next) { next->prev = &next; } |
163 | notifier->endpoints = this; |
164 | prev = ¬ifier->endpoints; |
165 | setSender(qintptr(notifier)); |
166 | } |
167 | |
168 | void 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 | /*! |
193 | Returns true if a notify is in progress. This means that the signal or QQmlNotifier |
194 | that this endpoing is connected to has been triggered, but this endpoint's callback has not |
195 | yet been called. |
196 | |
197 | An in progress notify can be cancelled by calling cancelNotify. |
198 | */ |
199 | bool QQmlNotifierEndpoint::isNotifying() const |
200 | { |
201 | return senderPtr & 0x1; |
202 | } |
203 | |
204 | void 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 | |
215 | void 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 | /*! |
224 | Cancel any notifies that are in progress. |
225 | */ |
226 | void 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 | |
236 | qintptr QQmlNotifierEndpoint::sender() const |
237 | { |
238 | return isNotifying() ? *(qintptr *)(senderPtr & ~0x1) : senderPtr; |
239 | } |
240 | |
241 | void 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 | |
250 | QObject *QQmlNotifierEndpoint::senderAsObject() const |
251 | { |
252 | return (QObject *)(sender()); |
253 | } |
254 | |
255 | QQmlNotifier *QQmlNotifierEndpoint::senderAsNotifier() const |
256 | { |
257 | return (QQmlNotifier *)(sender()); |
258 | } |
259 | |
260 | QT_END_NAMESPACE |
261 | |
262 | #endif // QQMLNOTIFIER_P_H |
263 | |
264 | |