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 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | typedef void (*Callback)(QQmlNotifierEndpoint *, void **); |
12 | |
13 | void QQmlBoundSignal_callback(QQmlNotifierEndpoint *, void **); |
14 | void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **); |
15 | void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *, void **); |
16 | void QQmlPropertyGuard_callback(QQmlNotifierEndpoint *, void **); |
17 | |
18 | static Callback QQmlNotifier_callbacks[] = { |
19 | nullptr, |
20 | QQmlBoundSignal_callback, |
21 | QQmlJavaScriptExpressionGuard_callback, |
22 | QQmlVMEMetaObjectEndpoint_callback, |
23 | QQmlPropertyGuard_callback |
24 | }; |
25 | |
26 | namespace { |
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 | |
40 | void QQmlNotifier::notify(QQmlData *ddata, int notifierIndex) |
41 | { |
42 | if (QQmlNotifierEndpoint *ep = ddata->notify(index: notifierIndex)) |
43 | emitNotify(ep, a: nullptr); |
44 | } |
45 | |
46 | void 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 | */ |
82 | void 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 | |
114 | QT_END_NAMESPACE |
115 | |
116 | |