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