1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
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 "qinputaspect.h"
5#include "qinputaspect_p.h"
6
7#include <Qt3DInput/qabstractphysicaldevice.h>
8#include <Qt3DInput/qaction.h>
9#include <Qt3DInput/qactioninput.h>
10#include <Qt3DInput/qanalogaxisinput.h>
11#include <Qt3DInput/qaxis.h>
12#include <Qt3DInput/qaxisaccumulator.h>
13#include <Qt3DInput/qaxissetting.h>
14#include <Qt3DInput/qbuttonaxisinput.h>
15#include <Qt3DInput/qinputchord.h>
16#include <Qt3DInput/qinputsequence.h>
17#include <Qt3DInput/qinputsettings.h>
18#include <Qt3DInput/qkeyboarddevice.h>
19#include <Qt3DInput/qkeyboardhandler.h>
20#include <Qt3DInput/qlogicaldevice.h>
21#include <Qt3DInput/qmousedevice.h>
22#include <Qt3DInput/qmousehandler.h>
23#include <QtCore/QDir>
24
25#include <QtCore/QLibraryInfo>
26#include <QtCore/QPluginLoader>
27
28#include <Qt3DInput/private/backendnode_p.h>
29#include <Qt3DInput/private/action_p.h>
30#include <Qt3DInput/private/actioninput_p.h>
31#include <Qt3DInput/private/axis_p.h>
32#include <Qt3DInput/private/axisaccumulator_p.h>
33#include <Qt3DInput/private/axisaccumulatorjob_p.h>
34#include <Qt3DInput/private/axissetting_p.h>
35#include <Qt3DInput/private/buttonaxisinput_p.h>
36#include <Qt3DInput/private/genericdevicebackendnode_p.h>
37#include <Qt3DInput/private/inputbackendnodefunctor_p.h>
38#include <Qt3DInput/private/inputchord_p.h>
39#include <Qt3DInput/private/inputhandler_p.h>
40#include <Qt3DInput/private/inputmanagers_p.h>
41#include <Qt3DInput/private/inputsequence_p.h>
42#include <Qt3DInput/private/inputsettings_p.h>
43#include <Qt3DInput/private/keyboarddevice_p.h>
44#include <Qt3DInput/private/keyboardhandler_p.h>
45#include <Qt3DInput/private/keyboardmousegenericdeviceintegration_p.h>
46#include <Qt3DInput/private/loadproxydevicejob_p.h>
47#include <Qt3DInput/private/logicaldevice_p.h>
48#include <Qt3DInput/private/mousedevice_p.h>
49#include <Qt3DInput/private/mousehandler_p.h>
50#include <Qt3DInput/private/qabstractphysicaldeviceproxy_p.h>
51#include <Qt3DInput/private/qgenericinputdevice_p.h>
52#include <Qt3DInput/private/qinputdeviceintegration_p.h>
53#include <Qt3DInput/private/qinputdeviceintegrationfactory_p.h>
54#include <Qt3DInput/private/updateaxisactionjob_p.h>
55#include <Qt3DCore/private/qeventfilterservice_p.h>
56#include <Qt3DCore/private/qservicelocator_p.h>
57#include <Qt3DCore/private/qaspectmanager_p.h>
58#include <Qt3DCore/private/vector_helper_p.h>
59
60#ifdef HAVE_QGAMEPAD
61# include <Qt3DInput/private/qgamepadinput_p.h>
62#endif
63
64QT_BEGIN_NAMESPACE
65
66
67namespace Qt3DInput {
68
69using namespace Qt3DCore;
70
71QInputAspectPrivate::QInputAspectPrivate()
72 : QAbstractAspectPrivate()
73 , m_inputHandler(new Input::InputHandler())
74 , m_keyboardMouseIntegration(new Input::KeyboardMouseGenericDeviceIntegration(m_inputHandler.data()))
75 , m_time(0)
76{
77}
78
79/*!
80 \class Qt3DInput::QInputAspect
81 \inherits Qt3DCore::QAbstractAspect
82 \inmodule Qt3DInput
83 \brief Responsible for creating physical devices and handling associated jobs.
84 \since 5.5
85 \brief Handles mapping between front and backend nodes
86
87 QInputAspect is responsible for creating physical devices.
88 It is also the object responsible establishing the jobs to run at a particular time from the current input setup.
89*/
90
91/*!
92 * Constructs a new QInputAspect with \a parent.
93 */
94QInputAspect::QInputAspect(QObject *parent)
95 : QInputAspect(*new QInputAspectPrivate, parent)
96{
97}
98
99/*! \internal */
100QInputAspect::QInputAspect(QInputAspectPrivate &dd, QObject *parent)
101 : QAbstractAspect(dd, parent)
102{
103 setObjectName(QStringLiteral("Input Aspect"));
104
105 qRegisterMetaType<Qt3DInput::QAbstractPhysicalDevice*>();
106
107 registerBackendType<QKeyboardDevice>(functor: QBackendNodeMapperPtr(new Input::KeyboardDeviceFunctor(this, d_func()->m_inputHandler.data())));
108 registerBackendType<QKeyboardHandler>(functor: QBackendNodeMapperPtr(new Input::KeyboardHandlerFunctor(d_func()->m_inputHandler.data())));
109 registerBackendType<QMouseDevice>(functor: QBackendNodeMapperPtr(new Input::MouseDeviceFunctor(this, d_func()->m_inputHandler.data())));
110 registerBackendType<QMouseHandler>(functor: QBackendNodeMapperPtr(new Input::MouseHandlerFunctor(d_func()->m_inputHandler.data())));
111 registerBackendType<QAxis>(functor: QBackendNodeMapperPtr(new Input::InputNodeFunctor<Input::Axis, Input::AxisManager>(d_func()->m_inputHandler->axisManager())));
112 registerBackendType<QAxisAccumulator>(functor: QBackendNodeMapperPtr(new Input::InputNodeFunctor<Input::AxisAccumulator, Input::AxisAccumulatorManager>(d_func()->m_inputHandler->axisAccumulatorManager())));
113 registerBackendType<QAnalogAxisInput>(functor: QBackendNodeMapperPtr(new Input::InputNodeFunctor<Input::AnalogAxisInput, Input::AnalogAxisInputManager>(d_func()->m_inputHandler->analogAxisInputManager())));
114 registerBackendType<QButtonAxisInput>(functor: QBackendNodeMapperPtr(new Input::InputNodeFunctor<Input::ButtonAxisInput, Input::ButtonAxisInputManager>(d_func()->m_inputHandler->buttonAxisInputManager())));
115 registerBackendType<QAxisSetting>(functor: QBackendNodeMapperPtr(new Input::InputNodeFunctor<Input::AxisSetting, Input::AxisSettingManager>(d_func()->m_inputHandler->axisSettingManager())));
116 registerBackendType<Qt3DInput::QAction>(functor: QBackendNodeMapperPtr(new Input::InputNodeFunctor<Input::Action, Input::ActionManager>(d_func()->m_inputHandler->actionManager())));
117 registerBackendType<QActionInput>(functor: QBackendNodeMapperPtr(new Input::InputNodeFunctor<Input::ActionInput, Input::ActionInputManager>(d_func()->m_inputHandler->actionInputManager())));
118 registerBackendType<QInputChord>(functor: QBackendNodeMapperPtr(new Input::InputNodeFunctor<Input::InputChord, Input::InputChordManager>(d_func()->m_inputHandler->inputChordManager())));
119 registerBackendType<QInputSequence>(functor: QBackendNodeMapperPtr(new Input::InputNodeFunctor<Input::InputSequence, Input::InputSequenceManager>(d_func()->m_inputHandler->inputSequenceManager())));
120 registerBackendType<QLogicalDevice>(functor: QBackendNodeMapperPtr(new Input::LogicalDeviceNodeFunctor(d_func()->m_inputHandler->logicalDeviceManager())));
121 registerBackendType<QGenericInputDevice>(functor: QBackendNodeMapperPtr(new Input::GenericDeviceBackendFunctor(this, d_func()->m_inputHandler.data())));
122 registerBackendType<QInputSettings>(functor: QBackendNodeMapperPtr(new Input::InputSettingsFunctor(d_func()->m_inputHandler.data())));
123 registerBackendType<QAbstractPhysicalDeviceProxy>(functor: QBackendNodeMapperPtr(new Input::PhysicalDeviceProxyNodeFunctor(d_func()->m_inputHandler->physicalDeviceProxyManager())));
124
125#ifdef HAVE_QGAMEPAD
126 registerBackendType<QGamepadInput>(QBackendNodeMapperPtr(new Input::GenericDeviceBackendFunctor(this, d_func()->m_inputHandler.data())));
127#endif
128
129 Q_D(QInputAspect);
130 // Plugins are QInputDeviceIntegration instances
131 d->loadInputDevicePlugins();
132
133 // KeyboardDevice and MouseDevice also provide their own QInputDeviceIntegration
134 d->m_inputHandler->addInputDeviceIntegration(inputIntegration: d->m_keyboardMouseIntegration.data());
135}
136
137/*! \internal */
138QInputAspect::~QInputAspect()
139{
140}
141
142/*
143 Create each of the detected input device integrations through the Integration Factory
144 */
145void QInputAspectPrivate::loadInputDevicePlugins()
146{
147 const QStringList keys = QInputDeviceIntegrationFactory::keys();
148 for (const QString &key : keys) {
149 Qt3DInput::QInputDeviceIntegration *integration = QInputDeviceIntegrationFactory::create(name: key, args: QStringList());
150 if (integration != nullptr) {
151 m_inputHandler->addInputDeviceIntegration(inputIntegration: integration);
152 // Initialize will allow the InputDeviceIntegration to
153 // register their frontend / backend types,
154 // create their managers
155 // launch a thread to listen to the actual physical device....
156 integration->initialize(aspect: q_func());
157 }
158 }
159}
160
161/*!
162 Create a physical device identified by \a name using the input device integrations present
163 returns a \c nullptr if it is not found.
164
165 \note Caller is responsible for ownership.
166*/
167QAbstractPhysicalDevice *QInputAspect::createPhysicalDevice(const QString &name)
168{
169 Q_D(QInputAspect);
170 return d->m_inputHandler->createPhysicalDevice(name);
171}
172
173/*!
174 Returns a list of all available physical devices.
175 */
176QStringList QInputAspect::availablePhysicalDevices() const
177{
178 Q_D(const QInputAspect);
179 QStringList deviceNamesList;
180 const auto deviceIntegrations = d->m_inputHandler->inputDeviceIntegrations();
181 for (const QInputDeviceIntegration *integration : deviceIntegrations)
182 deviceNamesList += integration->deviceNames();
183 return deviceNamesList;
184}
185
186/*!
187 \internal
188 */
189std::vector<QAspectJobPtr> QInputAspect::jobsToExecute(qint64 time)
190{
191 Q_D(QInputAspect);
192 const qint64 deltaTime = time - d->m_time;
193 const float dt = static_cast<float>(deltaTime) / 1.0e9f;
194 d->m_time = time;
195
196 std::vector<QAspectJobPtr> jobs;
197
198 d->m_inputHandler->updateEventSource();
199
200 // Mouse and keyboard handlers will have seen the events already.
201 // All we need now is to update the axis and the accumulators since
202 // they depend on time, and other bookkeeping.
203
204 const auto integrations = d->m_inputHandler->inputDeviceIntegrations();
205 for (QInputDeviceIntegration *integration : integrations) {
206 std::vector<QAspectJobPtr> integrationJobs = integration->jobsToExecute(time);
207 Qt3DCore::moveAtEnd(destination&: jobs, source: std::move(integrationJobs));
208 }
209
210 const QList<Qt3DCore::QNodeId> proxiesToLoad = d->m_inputHandler->physicalDeviceProxyManager()->takePendingProxiesToLoad();
211 if (!proxiesToLoad.isEmpty()) {
212 // Since loading wrappers occurs quite rarely, no point in keeping the job in a
213 // member variable
214 auto loadWrappersJob = Input::LoadProxyDeviceJobPtr::create();
215 loadWrappersJob->setProxiesToLoad(std::move(proxiesToLoad));
216 loadWrappersJob->setInputHandler(d->m_inputHandler.data());
217 jobs.push_back(x: loadWrappersJob);
218 }
219
220 // All the jobs added up until this point are independents
221 // but the axis action jobs will be dependent on these
222 const std::vector<QAspectJobPtr> dependsOnJobs = jobs;
223
224 // Jobs that update Axis/Action (store combined axis/action value)
225 const auto devHandles = d->m_inputHandler->logicalDeviceManager()->activeDevices();
226 std::vector<QAspectJobPtr> axisActionJobs;
227 axisActionJobs.reserve(n: devHandles.size());
228 for (const Input::HLogicalDevice &devHandle : devHandles) {
229 const auto device = d->m_inputHandler->logicalDeviceManager()->data(handle: devHandle);
230 if (!device->isEnabled())
231 continue;
232
233 QAspectJobPtr updateAxisActionJob(new Input::UpdateAxisActionJob(time, d->m_inputHandler.data(), devHandle));
234 jobs.push_back(x: updateAxisActionJob);
235 axisActionJobs.push_back(x: updateAxisActionJob);
236 for (const QAspectJobPtr &job : dependsOnJobs)
237 updateAxisActionJob->addDependency(dependency: job);
238 }
239
240 // Once all the axes have been updated we can step the integrations on
241 // the AxisAccumulators
242 auto accumulateJob = Input::AxisAccumulatorJobPtr::create(arguments: d->m_inputHandler->axisAccumulatorManager(),
243 arguments: d->m_inputHandler->axisManager());
244 accumulateJob->setDeltaTime(dt);
245 for (const QAspectJobPtr &job : std::as_const(t&: axisActionJobs))
246 accumulateJob->addDependency(dependency: job);
247 jobs.push_back(x: accumulateJob);
248
249 return jobs;
250}
251
252/*!
253 \internal
254 */
255void QInputAspect::onRegistered()
256{
257 Q_D(QInputAspect);
258 Qt3DCore::QEventFilterService *eventService = d->services()->eventFilterService();
259 Q_ASSERT(eventService);
260
261 // Set it on the input handler which will also handle its lifetime
262 d->m_inputHandler->setEventFilterService(eventService);
263}
264
265/*!
266 \internal
267 */
268void QInputAspect::onUnregistered()
269{
270 Q_D(QInputAspect);
271 // At this point it is too late to call removeEventFilter as the eventSource (Window)
272 // may already be destroyed
273 d->m_inputHandler.reset(other: nullptr);
274}
275
276void QInputAspect::onEngineStartup()
277{
278 Q_D(QInputAspect);
279 d->m_inputHandler->setScene(d->m_aspectManager->scene());
280}
281
282void QInputAspect::jobsDone()
283{
284 Q_D(QInputAspect);
285 d->m_inputHandler->resetMouseAxisState();
286}
287
288} // namespace Qt3DInput
289
290QT_END_NAMESPACE
291
292QT3D_REGISTER_NAMESPACED_ASPECT("input", QT_PREPEND_NAMESPACE(Qt3DInput), QInputAspect)
293
294#include "moc_qinputaspect.cpp"
295

source code of qt3d/src/input/frontend/qinputaspect.cpp