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 QSTATEMACHINE_P_H |
5 | #define QSTATEMACHINE_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 "private/qstate_p.h" |
19 | |
20 | #include <QtCore/qcoreevent.h> |
21 | #include <QtCore/qhash.h> |
22 | #include <QtCore/qlist.h> |
23 | #include <QtCore/qmutex.h> |
24 | #include <QtCore/qpair.h> |
25 | #include <QtCore/qpointer.h> |
26 | #include <QtCore/qset.h> |
27 | |
28 | #include <QtCore/private/qfreelist_p.h> |
29 | |
30 | QT_REQUIRE_CONFIG(statemachine); |
31 | |
32 | QT_BEGIN_NAMESPACE |
33 | |
34 | class QEvent; |
35 | #if QT_CONFIG(qeventtransition) |
36 | class QEventTransition; |
37 | #endif |
38 | class QSignalEventGenerator; |
39 | class QSignalTransition; |
40 | class QAbstractState; |
41 | class QAbstractTransition; |
42 | class QFinalState; |
43 | class QHistoryState; |
44 | class QState; |
45 | |
46 | #if QT_CONFIG(animation) |
47 | class QAbstractAnimation; |
48 | #endif |
49 | |
50 | struct CalculationCache; |
51 | class QStateMachine; |
52 | class Q_STATEMACHINE_EXPORT QStateMachinePrivate : public QStatePrivate |
53 | { |
54 | Q_DECLARE_PUBLIC(QStateMachine) |
55 | public: |
56 | enum State { |
57 | NotRunning, |
58 | Starting, |
59 | Running |
60 | }; |
61 | enum EventProcessingMode { |
62 | DirectProcessing, |
63 | QueuedProcessing |
64 | }; |
65 | enum StopProcessingReason { |
66 | EventQueueEmpty, |
67 | Finished, |
68 | Stopped |
69 | }; |
70 | |
71 | QStateMachinePrivate(); |
72 | ~QStateMachinePrivate(); |
73 | |
74 | static QStateMachinePrivate *get(QStateMachine *q) |
75 | { return q ? q->d_func() : nullptr; } |
76 | |
77 | QState *findLCA(const QList<QAbstractState*> &states, bool onlyCompound = false); |
78 | QState *findLCCA(const QList<QAbstractState*> &states); |
79 | |
80 | static bool transitionStateEntryLessThan(QAbstractTransition *t1, QAbstractTransition *t2); |
81 | static bool stateEntryLessThan(QAbstractState *s1, QAbstractState *s2); |
82 | static bool stateExitLessThan(QAbstractState *s1, QAbstractState *s2); |
83 | |
84 | QAbstractState *findErrorState(QAbstractState *context); |
85 | void setError(QStateMachine::Error error, QAbstractState *currentContext); |
86 | |
87 | // private slots |
88 | void _q_start(); |
89 | void _q_process(); |
90 | #if QT_CONFIG(animation) |
91 | void _q_animationFinished(); |
92 | #endif |
93 | void _q_startDelayedEventTimer(int id, int delay); |
94 | void _q_killDelayedEventTimer(int id, int timerId); |
95 | |
96 | QState *rootState() const; |
97 | |
98 | void clearHistory(); |
99 | QAbstractTransition *createInitialTransition() const; |
100 | |
101 | void removeConflictingTransitions(QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache); |
102 | void microstep(QEvent *event, const QList<QAbstractTransition*> &transitionList, CalculationCache *cache); |
103 | QList<QAbstractTransition *> selectTransitions(QEvent *event, CalculationCache *cache); |
104 | virtual void noMicrostep(); |
105 | virtual void processedPendingEvents(bool didChange); |
106 | virtual void beginMacrostep(); |
107 | virtual void endMacrostep(bool didChange); |
108 | virtual void exitInterpreter(); |
109 | virtual void exitStates(QEvent *event, const QList<QAbstractState *> &statesToExit_sorted, |
110 | const QHash<QAbstractState *, QList<QPropertyAssignment>> &assignmentsForEnteredStates); |
111 | QList<QAbstractState*> computeExitSet(const QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache); |
112 | QSet<QAbstractState*> computeExitSet_Unordered(const QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache); |
113 | QSet<QAbstractState*> computeExitSet_Unordered(QAbstractTransition *t, CalculationCache *cache); |
114 | void executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &transitionList); |
115 | virtual void enterStates(QEvent *event, const QList<QAbstractState*> &exitedStates_sorted, |
116 | const QList<QAbstractState*> &statesToEnter_sorted, |
117 | const QSet<QAbstractState*> &statesForDefaultEntry, |
118 | QHash<QAbstractState *, QList<QPropertyAssignment>> &propertyAssignmentsForState |
119 | #if QT_CONFIG(animation) |
120 | , const QList<QAbstractAnimation*> &selectedAnimations |
121 | #endif |
122 | ); |
123 | QList<QAbstractState*> computeEntrySet(const QList<QAbstractTransition*> &enabledTransitions, |
124 | QSet<QAbstractState*> &statesForDefaultEntry, CalculationCache *cache); |
125 | QAbstractState *getTransitionDomain(QAbstractTransition *t, |
126 | const QList<QAbstractState *> &effectiveTargetStates, |
127 | CalculationCache *cache); |
128 | void addDescendantStatesToEnter(QAbstractState *state, |
129 | QSet<QAbstractState*> &statesToEnter, |
130 | QSet<QAbstractState*> &statesForDefaultEntry); |
131 | void addAncestorStatesToEnter(QAbstractState *s, QAbstractState *ancestor, |
132 | QSet<QAbstractState*> &statesToEnter, |
133 | QSet<QAbstractState*> &statesForDefaultEntry); |
134 | |
135 | static QState *toStandardState(QAbstractState *state); |
136 | static const QState *toStandardState(const QAbstractState *state); |
137 | static QFinalState *toFinalState(QAbstractState *state); |
138 | static QHistoryState *toHistoryState(QAbstractState *state); |
139 | |
140 | bool isInFinalState(QAbstractState *s) const; |
141 | static bool isFinal(const QAbstractState *s); |
142 | static bool isParallel(const QAbstractState *s); |
143 | bool isCompound(const QAbstractState *s) const; |
144 | bool isAtomic(const QAbstractState *s) const; |
145 | |
146 | void goToState(QAbstractState *targetState); |
147 | |
148 | void registerTransitions(QAbstractState *state); |
149 | void maybeRegisterTransition(QAbstractTransition *transition); |
150 | void registerTransition(QAbstractTransition *transition); |
151 | void maybeRegisterSignalTransition(QSignalTransition *transition); |
152 | void registerSignalTransition(QSignalTransition *transition); |
153 | void unregisterSignalTransition(QSignalTransition *transition); |
154 | void registerMultiThreadedSignalTransitions(); |
155 | #if QT_CONFIG(qeventtransition) |
156 | void maybeRegisterEventTransition(QEventTransition *transition); |
157 | void registerEventTransition(QEventTransition *transition); |
158 | void unregisterEventTransition(QEventTransition *transition); |
159 | void handleFilteredEvent(QObject *watched, QEvent *event); |
160 | #endif |
161 | void unregisterTransition(QAbstractTransition *transition); |
162 | void unregisterAllTransitions(); |
163 | void handleTransitionSignal(QObject *sender, int signalIndex, |
164 | void **args); |
165 | |
166 | void postInternalEvent(QEvent *e); |
167 | void postExternalEvent(QEvent *e); |
168 | QEvent *dequeueInternalEvent(); |
169 | QEvent *dequeueExternalEvent(); |
170 | bool isInternalEventQueueEmpty(); |
171 | bool isExternalEventQueueEmpty(); |
172 | void processEvents(EventProcessingMode processingMode); |
173 | void cancelAllDelayedEvents(); |
174 | |
175 | virtual void emitStateFinished(QState *forState, QFinalState *guiltyState); |
176 | virtual void startupHook(); |
177 | |
178 | #ifndef QT_NO_PROPERTIES |
179 | class RestorableId { |
180 | QPointer<QObject> guard; |
181 | QObject *obj; |
182 | QByteArray prop; |
183 | friend size_t qHash(const RestorableId &key, size_t seed) |
184 | noexcept(noexcept(qHash(key: std::declval<QByteArray>()))) |
185 | { return qHash(key: qMakePair(value1: key.obj, value2: key.prop), seed); } |
186 | friend bool operator==(const RestorableId &lhs, const RestorableId &rhs) noexcept |
187 | { return lhs.obj == rhs.obj && lhs.prop == rhs.prop; } |
188 | friend bool operator!=(const RestorableId &lhs, const RestorableId &rhs) noexcept |
189 | { return !operator==(lhs, rhs); } |
190 | public: |
191 | explicit RestorableId(QObject *o, QByteArray p) noexcept : guard(o), obj(o), prop(std::move(p)) {} |
192 | QObject *object() const noexcept { return guard; } |
193 | QByteArray propertyName() const noexcept { return prop; } |
194 | }; |
195 | QHash<QAbstractState*, QHash<RestorableId, QVariant> > registeredRestorablesForState; |
196 | bool hasRestorable(QAbstractState *state, QObject *object, const QByteArray &propertyName) const; |
197 | QVariant savedValueForRestorable(const QList<QAbstractState*> &exitedStates_sorted, |
198 | QObject *object, const QByteArray &propertyName) const; |
199 | void registerRestorable(QAbstractState *state, QObject *object, const QByteArray &propertyName, |
200 | const QVariant &value); |
201 | void unregisterRestorables(const QList<QAbstractState*> &states, QObject *object, |
202 | const QByteArray &propertyName); |
203 | QList<QPropertyAssignment> restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const; |
204 | QHash<RestorableId, QVariant> computePendingRestorables(const QList<QAbstractState*> &statesToExit_sorted) const; |
205 | QHash<QAbstractState *, QList<QPropertyAssignment>> computePropertyAssignments( |
206 | const QList<QAbstractState*> &statesToEnter_sorted, |
207 | QHash<RestorableId, QVariant> &pendingRestorables) const; |
208 | #endif |
209 | |
210 | State state; |
211 | bool processing; |
212 | bool processingScheduled; |
213 | bool stop; |
214 | StopProcessingReason stopProcessingReason; |
215 | QSet<QAbstractState*> configuration; |
216 | QList<QEvent*> internalEventQueue; |
217 | QList<QEvent*> externalEventQueue; |
218 | QMutex internalEventMutex; |
219 | QMutex externalEventMutex; |
220 | |
221 | QStateMachine::Error error; |
222 | Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QStateMachinePrivate, QState::RestorePolicy, |
223 | globalRestorePolicy, QState::DontRestoreProperties); |
224 | Q_OBJECT_BINDABLE_PROPERTY(QStateMachinePrivate, QString, errorString); |
225 | |
226 | QSet<QAbstractState *> pendingErrorStates; |
227 | QSet<QAbstractState *> pendingErrorStatesForDefaultEntry; |
228 | |
229 | #if QT_CONFIG(animation) |
230 | Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QStateMachinePrivate, bool, animated, true); |
231 | |
232 | struct InitializeAnimationResult { |
233 | QList<QAbstractAnimation*> handledAnimations; |
234 | QList<QAbstractAnimation*> localResetEndValues; |
235 | |
236 | void swap(InitializeAnimationResult &other) noexcept |
237 | { |
238 | qSwap(value1&: handledAnimations, value2&: other.handledAnimations); |
239 | qSwap(value1&: localResetEndValues, value2&: other.localResetEndValues); |
240 | } |
241 | }; |
242 | |
243 | InitializeAnimationResult |
244 | initializeAnimation(QAbstractAnimation *abstractAnimation, |
245 | const QPropertyAssignment &prop); |
246 | |
247 | QHash<QAbstractState*, QList<QAbstractAnimation*> > animationsForState; |
248 | QHash<QAbstractAnimation*, QPropertyAssignment> propertyForAnimation; |
249 | QHash<QAbstractAnimation*, QAbstractState*> stateForAnimation; |
250 | QSet<QAbstractAnimation*> resetAnimationEndValues; |
251 | |
252 | QList<QAbstractAnimation *> defaultAnimations; |
253 | QMultiHash<QAbstractState *, QAbstractAnimation *> defaultAnimationsForSource; |
254 | QMultiHash<QAbstractState *, QAbstractAnimation *> defaultAnimationsForTarget; |
255 | |
256 | QList<QAbstractAnimation *> selectAnimations(const QList<QAbstractTransition *> &transitionList) const; |
257 | void terminateActiveAnimations(QAbstractState *state, |
258 | const QHash<QAbstractState *, QList<QPropertyAssignment>> &assignmentsForEnteredStates); |
259 | void initializeAnimations(QAbstractState *state, const QList<QAbstractAnimation*> &selectedAnimations, |
260 | const QList<QAbstractState *> &exitedStates_sorted, |
261 | QHash<QAbstractState *, QList<QPropertyAssignment>> &assignmentsForEnteredStates); |
262 | #endif // animation |
263 | |
264 | QSignalEventGenerator *signalEventGenerator; |
265 | |
266 | QHash<const QObject *, QList<int>> connections; |
267 | QMutex connectionsMutex; |
268 | #if QT_CONFIG(qeventtransition) |
269 | QHash<QObject*, QHash<QEvent::Type, int> > qobjectEvents; |
270 | #endif |
271 | struct FreeListDefaultConstants |
272 | { |
273 | // used by QFreeList, make sure to define all of when customizing |
274 | enum { |
275 | InitialNextValue = 0, |
276 | IndexMask = 0x00ffffff, |
277 | SerialMask = ~IndexMask & ~0x80000000, |
278 | SerialCounter = IndexMask + 1, |
279 | MaxIndex = IndexMask, |
280 | BlockCount = 4 |
281 | }; |
282 | |
283 | static const int Sizes[BlockCount]; |
284 | }; |
285 | QFreeList<void, FreeListDefaultConstants> delayedEventIdFreeList; |
286 | |
287 | struct DelayedEvent { |
288 | QEvent *event; |
289 | int timerId; |
290 | DelayedEvent(QEvent *e, int tid) |
291 | : event(e), timerId(tid) {} |
292 | DelayedEvent() |
293 | : event(nullptr), timerId(0) {} |
294 | }; |
295 | QHash<int, DelayedEvent> delayedEvents; |
296 | QHash<int, int> timerIdToDelayedEventId; |
297 | QMutex delayedEventsMutex; |
298 | }; |
299 | #if QT_CONFIG(animation) |
300 | Q_DECLARE_SHARED(QStateMachinePrivate::InitializeAnimationResult) |
301 | #endif |
302 | |
303 | QT_END_NAMESPACE |
304 | |
305 | #endif |
306 | |