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