1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
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 General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QTest> |
30 | #include <qbackendnodetester.h> |
31 | #include "testdevice.h" |
32 | |
33 | #include <Qt3DInput/private/actioninput_p.h> |
34 | #include <Qt3DInput/private/inputhandler_p.h> |
35 | #include <Qt3DInput/private/inputmanagers_p.h> |
36 | #include <Qt3DInput/private/inputsequence_p.h> |
37 | #include <Qt3DInput/QActionInput> |
38 | #include <Qt3DInput/QInputSequence> |
39 | |
40 | class tst_InputSequence : public Qt3DCore::QBackendNodeTester |
41 | { |
42 | Q_OBJECT |
43 | private Q_SLOTS: |
44 | void shouldMirrorPeerProperties() |
45 | { |
46 | // GIVEN |
47 | Qt3DInput::Input::InputSequence backendInputSequence; |
48 | Qt3DInput::QInputSequence inputSequence; |
49 | Qt3DInput::QActionInput actionInput; |
50 | |
51 | inputSequence.setTimeout(250); |
52 | inputSequence.setButtonInterval(100); |
53 | inputSequence.addSequence(input: &actionInput); |
54 | |
55 | // WHEN |
56 | simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence); |
57 | |
58 | // THEN |
59 | QCOMPARE(backendInputSequence.peerId(), inputSequence.id()); |
60 | QCOMPARE(backendInputSequence.isEnabled(), inputSequence.isEnabled()); |
61 | QCOMPARE(backendInputSequence.timeout(), inputSequence.timeout() * 1000000); |
62 | QCOMPARE(backendInputSequence.buttonInterval(), inputSequence.buttonInterval() * 1000000); |
63 | QCOMPARE(backendInputSequence.sequences().size(), inputSequence.sequences().size()); |
64 | |
65 | const int inputsCount = backendInputSequence.sequences().size(); |
66 | if (inputsCount > 0) { |
67 | for (int i = 0; i < inputsCount; ++i) |
68 | QCOMPARE(backendInputSequence.sequences().at(i), inputSequence.sequences().at(i)->id()); |
69 | } |
70 | } |
71 | |
72 | void shouldHaveInitialAndCleanedUpStates() |
73 | { |
74 | // GIVEN |
75 | Qt3DInput::Input::InputSequence backendInputSequence; |
76 | |
77 | // THEN |
78 | QVERIFY(backendInputSequence.peerId().isNull()); |
79 | QCOMPARE(backendInputSequence.isEnabled(), false); |
80 | QCOMPARE(backendInputSequence.timeout(), 0); |
81 | QCOMPARE(backendInputSequence.buttonInterval(), 0); |
82 | QCOMPARE(backendInputSequence.sequences().size(), 0); |
83 | |
84 | // GIVEN |
85 | Qt3DInput::QInputSequence inputSequence; |
86 | Qt3DInput::QActionInput actionInput; |
87 | |
88 | inputSequence.setTimeout(250); |
89 | inputSequence.setButtonInterval(100); |
90 | inputSequence.addSequence(input: &actionInput); |
91 | |
92 | // WHEN |
93 | simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence); |
94 | backendInputSequence.cleanup(); |
95 | |
96 | // THEN |
97 | QCOMPARE(backendInputSequence.isEnabled(), false); |
98 | QCOMPARE(backendInputSequence.timeout(), 0); |
99 | QCOMPARE(backendInputSequence.buttonInterval(), 0); |
100 | QCOMPARE(backendInputSequence.sequences().size(), 0); |
101 | } |
102 | |
103 | void shouldHandlePropertyChanges() |
104 | { |
105 | // GIVEN |
106 | Qt3DInput::QInputSequence inputSequence; |
107 | Qt3DInput::Input::InputSequence backendInputSequence; |
108 | simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence); |
109 | |
110 | // WHEN |
111 | inputSequence.setTimeout(250); |
112 | backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false); |
113 | |
114 | // THEN |
115 | QCOMPARE(backendInputSequence.timeout(), 250000000); |
116 | |
117 | // WHEN |
118 | inputSequence.setButtonInterval(150); |
119 | backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false); |
120 | |
121 | // THEN |
122 | QCOMPARE(backendInputSequence.buttonInterval(), 150000000); |
123 | |
124 | // WHEN |
125 | inputSequence.setEnabled(false); |
126 | backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false); |
127 | |
128 | // THEN |
129 | QCOMPARE(backendInputSequence.isEnabled(), false); |
130 | |
131 | // WHEN |
132 | Qt3DInput::QActionInput input; |
133 | const Qt3DCore::QNodeId inputId = input.id(); |
134 | inputSequence.addSequence(input: &input); |
135 | backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false); |
136 | |
137 | // THEN |
138 | QCOMPARE(backendInputSequence.sequences().size(), 1); |
139 | QCOMPARE(backendInputSequence.sequences().first(), inputId); |
140 | |
141 | // WHEN |
142 | inputSequence.removeSequence(input: &input); |
143 | backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false); |
144 | |
145 | // THEN |
146 | QCOMPARE(backendInputSequence.sequences().size(), 0); |
147 | } |
148 | |
149 | void shouldActivateWhenSequenceIsConsumedInOrderOnly() |
150 | { |
151 | // GIVEN |
152 | TestDeviceIntegration deviceIntegration; |
153 | TestDevice *device = deviceIntegration.createPhysicalDevice(name: "keyboard" ); |
154 | TestDeviceBackendNode *deviceBackend = deviceIntegration.physicalDevice(id: device->id()); |
155 | Qt3DInput::Input::InputHandler handler; |
156 | handler.addInputDeviceIntegration(inputIntegration: &deviceIntegration); |
157 | |
158 | auto firstInput = new Qt3DInput::QActionInput; |
159 | firstInput->setButtons(QVector<int>() << Qt::Key_Q << Qt::Key_A); |
160 | firstInput->setSourceDevice(device); |
161 | auto backendFirstInput = handler.actionInputManager()->getOrCreateResource(id: firstInput->id()); |
162 | simulateInitializationSync(frontend: firstInput, backend: backendFirstInput); |
163 | |
164 | auto secondInput = new Qt3DInput::QActionInput; |
165 | secondInput->setButtons(QVector<int>() << Qt::Key_S << Qt::Key_W); |
166 | secondInput->setSourceDevice(device); |
167 | auto backendSecondInput = handler.actionInputManager()->getOrCreateResource(id: secondInput->id()); |
168 | simulateInitializationSync(frontend: secondInput, backend: backendSecondInput); |
169 | |
170 | auto thirdInput = new Qt3DInput::QActionInput; |
171 | thirdInput->setButtons(QVector<int>() << Qt::Key_D << Qt::Key_E); |
172 | thirdInput->setSourceDevice(device); |
173 | auto backendThirdInput = handler.actionInputManager()->getOrCreateResource(id: thirdInput->id()); |
174 | simulateInitializationSync(frontend: thirdInput, backend: backendThirdInput); |
175 | |
176 | Qt3DInput::Input::InputSequence backendInputSequence; |
177 | Qt3DInput::QInputSequence inputSequence; |
178 | inputSequence.setEnabled(true); |
179 | inputSequence.setButtonInterval(150); |
180 | inputSequence.setTimeout(450); |
181 | inputSequence.addSequence(input: firstInput); |
182 | inputSequence.addSequence(input: secondInput); |
183 | inputSequence.addSequence(input: thirdInput); |
184 | simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence); |
185 | |
186 | // WHEN |
187 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Up, pressed: true); |
188 | |
189 | // THEN |
190 | QCOMPARE(backendInputSequence.process(&handler, 1000000000), false); |
191 | |
192 | // WHEN |
193 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Up, pressed: false); |
194 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true); |
195 | |
196 | // THEN |
197 | QCOMPARE(backendInputSequence.process(&handler, 1100000000), false); |
198 | |
199 | // WHEN |
200 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false); |
201 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true); |
202 | |
203 | // THEN |
204 | QCOMPARE(backendInputSequence.process(&handler, 1200000000), false); |
205 | |
206 | // WHEN |
207 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false); |
208 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: true); |
209 | |
210 | // THEN |
211 | QCOMPARE(backendInputSequence.process(&handler, 1300000000), true); |
212 | |
213 | // WHEN |
214 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: false); |
215 | |
216 | // THEN |
217 | QCOMPARE(backendInputSequence.process(&handler, 1400000000), false); |
218 | |
219 | // WHEN |
220 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true); |
221 | |
222 | // THEN |
223 | QCOMPARE(backendInputSequence.process(&handler, 1500000000), false); |
224 | |
225 | // WHEN |
226 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false); |
227 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true); |
228 | |
229 | // THEN |
230 | QCOMPARE(backendInputSequence.process(&handler, 1600000000), false); |
231 | |
232 | // WHEN |
233 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false); |
234 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: true); |
235 | |
236 | // THEN |
237 | QCOMPARE(backendInputSequence.process(&handler, 1700000000), true); |
238 | |
239 | // WHEN |
240 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: false); |
241 | |
242 | // THEN |
243 | QCOMPARE(backendInputSequence.process(&handler, 1800000000), false); |
244 | |
245 | |
246 | // Now out of order |
247 | |
248 | // WHEN |
249 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true); |
250 | |
251 | // THEN |
252 | QCOMPARE(backendInputSequence.process(&handler, 1900000000), false); |
253 | |
254 | // WHEN |
255 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false); |
256 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true); |
257 | |
258 | // THEN |
259 | QCOMPARE(backendInputSequence.process(&handler, 2000000000), false); |
260 | |
261 | // WHEN |
262 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false); |
263 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_D, pressed: true); |
264 | |
265 | // THEN |
266 | QCOMPARE(backendInputSequence.process(&handler, 2100000000), false); |
267 | |
268 | // WHEN |
269 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_D, pressed: false); |
270 | |
271 | // THEN |
272 | QCOMPARE(backendInputSequence.process(&handler, 22000000000), false); |
273 | } |
274 | |
275 | void shouldRespectSequenceTimeout() |
276 | { |
277 | // GIVEN |
278 | TestDeviceIntegration deviceIntegration; |
279 | TestDevice *device = deviceIntegration.createPhysicalDevice(name: "keyboard" ); |
280 | TestDeviceBackendNode *deviceBackend = deviceIntegration.physicalDevice(id: device->id()); |
281 | Qt3DInput::Input::InputHandler handler; |
282 | handler.addInputDeviceIntegration(inputIntegration: &deviceIntegration); |
283 | |
284 | auto firstInput = new Qt3DInput::QActionInput; |
285 | firstInput->setButtons(QVector<int>() << Qt::Key_Q << Qt::Key_A); |
286 | firstInput->setSourceDevice(device); |
287 | auto backendFirstInput = handler.actionInputManager()->getOrCreateResource(id: firstInput->id()); |
288 | simulateInitializationSync(frontend: firstInput, backend: backendFirstInput); |
289 | |
290 | auto secondInput = new Qt3DInput::QActionInput; |
291 | secondInput->setButtons(QVector<int>() << Qt::Key_S << Qt::Key_W); |
292 | secondInput->setSourceDevice(device); |
293 | auto backendSecondInput = handler.actionInputManager()->getOrCreateResource(id: secondInput->id()); |
294 | simulateInitializationSync(frontend: secondInput, backend: backendSecondInput); |
295 | |
296 | auto thirdInput = new Qt3DInput::QActionInput; |
297 | thirdInput->setButtons(QVector<int>() << Qt::Key_D << Qt::Key_E); |
298 | thirdInput->setSourceDevice(device); |
299 | auto backendThirdInput = handler.actionInputManager()->getOrCreateResource(id: thirdInput->id()); |
300 | simulateInitializationSync(frontend: thirdInput, backend: backendThirdInput); |
301 | |
302 | Qt3DInput::Input::InputSequence backendInputSequence; |
303 | Qt3DInput::QInputSequence inputSequence; |
304 | inputSequence.setEnabled(true); |
305 | inputSequence.setButtonInterval(250); |
306 | inputSequence.setTimeout(450); |
307 | inputSequence.addSequence(input: firstInput); |
308 | inputSequence.addSequence(input: secondInput); |
309 | inputSequence.addSequence(input: thirdInput); |
310 | simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence); |
311 | |
312 | // WHEN |
313 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true); |
314 | |
315 | // THEN |
316 | QCOMPARE(backendInputSequence.process(&handler, 1100000000), false); |
317 | |
318 | // WHEN |
319 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false); |
320 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true); |
321 | |
322 | // THEN |
323 | QCOMPARE(backendInputSequence.process(&handler, 1300000000), false); |
324 | |
325 | // WHEN |
326 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false); |
327 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: true); |
328 | |
329 | // THEN |
330 | QCOMPARE(backendInputSequence.process(&handler, 1600000000), false); // Too late |
331 | } |
332 | |
333 | void shouldRespectSequenceButtonInterval() |
334 | { |
335 | // GIVEN |
336 | TestDeviceIntegration deviceIntegration; |
337 | TestDevice *device = deviceIntegration.createPhysicalDevice(name: "keyboard" ); |
338 | TestDeviceBackendNode *deviceBackend = deviceIntegration.physicalDevice(id: device->id()); |
339 | Qt3DInput::Input::InputHandler handler; |
340 | handler.addInputDeviceIntegration(inputIntegration: &deviceIntegration); |
341 | |
342 | auto firstInput = new Qt3DInput::QActionInput; |
343 | firstInput->setButtons(QVector<int>() << Qt::Key_Q << Qt::Key_A); |
344 | firstInput->setSourceDevice(device); |
345 | auto backendFirstInput = handler.actionInputManager()->getOrCreateResource(id: firstInput->id()); |
346 | simulateInitializationSync(frontend: firstInput, backend: backendFirstInput); |
347 | |
348 | auto secondInput = new Qt3DInput::QActionInput; |
349 | secondInput->setButtons(QVector<int>() << Qt::Key_S << Qt::Key_W); |
350 | secondInput->setSourceDevice(device); |
351 | auto backendSecondInput = handler.actionInputManager()->getOrCreateResource(id: secondInput->id()); |
352 | simulateInitializationSync(frontend: secondInput, backend: backendSecondInput); |
353 | |
354 | auto thirdInput = new Qt3DInput::QActionInput; |
355 | thirdInput->setButtons(QVector<int>() << Qt::Key_D << Qt::Key_E); |
356 | thirdInput->setSourceDevice(device); |
357 | auto backendThirdInput = handler.actionInputManager()->getOrCreateResource(id: thirdInput->id()); |
358 | simulateInitializationSync(frontend: thirdInput, backend: backendThirdInput); |
359 | |
360 | Qt3DInput::Input::InputSequence backendInputSequence; |
361 | Qt3DInput::QInputSequence inputSequence; |
362 | inputSequence.setEnabled(true); |
363 | inputSequence.setButtonInterval(100); |
364 | inputSequence.setTimeout(450); |
365 | inputSequence.addSequence(input: firstInput); |
366 | inputSequence.addSequence(input: secondInput); |
367 | inputSequence.addSequence(input: thirdInput); |
368 | simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence); |
369 | |
370 | // WHEN |
371 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true); |
372 | |
373 | // THEN |
374 | QCOMPARE(backendInputSequence.process(&handler, 1100000000), false); |
375 | |
376 | // WHEN |
377 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false); |
378 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true); |
379 | |
380 | // THEN |
381 | QCOMPARE(backendInputSequence.process(&handler, 1250000000), false); // Too late |
382 | |
383 | // WHEN |
384 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false); |
385 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: true); |
386 | |
387 | // THEN |
388 | QCOMPARE(backendInputSequence.process(&handler, 1300000000), false); |
389 | } |
390 | |
391 | void shouldNotProcessWhenDisabled() |
392 | { |
393 | // GIVEN |
394 | TestDeviceIntegration deviceIntegration; |
395 | TestDevice *device = deviceIntegration.createPhysicalDevice(name: "keyboard" ); |
396 | TestDeviceBackendNode *deviceBackend = deviceIntegration.physicalDevice(id: device->id()); |
397 | Qt3DInput::Input::InputHandler handler; |
398 | handler.addInputDeviceIntegration(inputIntegration: &deviceIntegration); |
399 | |
400 | auto firstInput = new Qt3DInput::QActionInput; |
401 | firstInput->setButtons(QVector<int>() << Qt::Key_Q); |
402 | firstInput->setSourceDevice(device); |
403 | auto backendFirstInput = handler.actionInputManager()->getOrCreateResource(id: firstInput->id()); |
404 | simulateInitializationSync(frontend: firstInput, backend: backendFirstInput); |
405 | |
406 | auto secondInput = new Qt3DInput::QActionInput; |
407 | secondInput->setButtons(QVector<int>() << Qt::Key_S); |
408 | secondInput->setSourceDevice(device); |
409 | auto backendSecondInput = handler.actionInputManager()->getOrCreateResource(id: secondInput->id()); |
410 | simulateInitializationSync(frontend: secondInput, backend: backendSecondInput); |
411 | |
412 | Qt3DInput::Input::InputSequence backendInputSequence; |
413 | Qt3DInput::QInputSequence inputSequence; |
414 | inputSequence.setEnabled(false); |
415 | inputSequence.setButtonInterval(150); |
416 | inputSequence.setTimeout(450); |
417 | inputSequence.addSequence(input: firstInput); |
418 | inputSequence.addSequence(input: secondInput); |
419 | simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence); |
420 | |
421 | // WHEN |
422 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true); |
423 | |
424 | // THEN |
425 | QCOMPARE(backendInputSequence.process(&handler, 1000000000), false); |
426 | |
427 | // WHEN |
428 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false); |
429 | deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true); |
430 | |
431 | // THEN |
432 | QCOMPARE(backendInputSequence.process(&handler, 1100000000), false); |
433 | } |
434 | }; |
435 | |
436 | QTEST_APPLESS_MAIN(tst_InputSequence) |
437 | |
438 | #include "tst_inputsequence.moc" |
439 | |