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

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