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

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