1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtFeedback module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qfeedbackplugininterfaces.h"
38#include "qfeedbackplugin_p.h"
39#include "qfeedbackeffect_p.h"
40
41#include "qfeedbackpluginsearch.h"
42
43#include <QtCore/QCoreApplication>
44#include <QtCore/QStringList>
45#include <QtCore/QDir>
46#include <QtCore/QPluginLoader>
47#include <QtCore/QHash>
48#include <QDebug>
49
50QT_BEGIN_NAMESPACE
51
52/*!
53 \class QFeedbackInterface
54 \ingroup feedback
55 \inmodule QtFeedback
56
57 \brief The QFeedbackInterface class is the base class for plugins providing feedback.
58
59 This interface gives the possibility to report errors from within a backend plugin.
60*/
61
62/*!
63 \fn QFeedbackInterface::reportError(const QFeedbackEffect *effect, QFeedbackEffect::ErrorType error)
64
65 Allows a plugin to report the specified \a error whenever necessary. Errors most likely can happen
66 trying to play or pause an effect, which should be supplied as the parameter \a effect.
67*/
68
69/*!
70 \enum QFeedbackInterface::PluginPriority
71
72 This enum describes the priority that the plugin should have in case more than one of the same type (Haptics or Theme) is found.
73 If more than one plugin has the same priority, the first one that has been loaded will be used. However, multiple
74 file effect plugins can be loaded at the same time.
75
76 \value PluginLowPriority The plugin will have a low priority. This is usually the case for
77 platform specific-APIs.
78
79 \value PluginNormalPriority The plugin will have a normal priority.
80 This is usually the case for advanced technologies.
81
82 \value PluginHighPriority The plugin will have higher priority. Use this priority if you
83 want your own plugin to be used.
84*/
85
86
87void QFeedbackInterface::reportError(const QFeedbackEffect *effect, QFeedbackEffect::ErrorType error)
88{
89 if (effect)
90 emit effect->error(error);
91}
92
93
94// These are really useless docs, so I've marked them as internal
95/*!
96 \internal
97 \fn QFeedbackThemeInterface::~QFeedbackThemeInterface()
98
99 Destroys any resources used by this interface.
100*/
101
102/*!
103 \internal
104 \fn QFeedbackFileInterface::~QFeedbackFileInterface()
105
106 Destroys any resources used by this interface.
107*/
108
109/*!
110 \internal
111 \fn QFeedbackHapticsInterface::~QFeedbackHapticsInterface()
112
113 Destroys any resources used by this interface.
114*/
115
116
117
118template <class T>
119class BackendLoader
120{
121public:
122 BackendLoader() : inst(0) { }
123 ~BackendLoader() { pl.unload(); }
124
125 void setInstance(T *newInst) { inst = newInst; }
126 T * instance() { return inst; }
127
128 void tryLoad(QPluginLoader &loader)
129 {
130 if (T *newInst = qobject_cast<T*>(loader.instance())) {
131 if (!inst || inst->pluginPriority() < newInst->pluginPriority()) {
132 inst = newInst;
133 pl.unload(); //release any reference to a previous plugin instance
134 pl.setFileName(loader.fileName());
135 pl.load(); //Adds a ref to the library
136 }
137 }
138 }
139
140
141private:
142 QPluginLoader pl;
143 T *inst;
144};
145
146
147class FileBackend : public QFeedbackFileInterface
148{
149public:
150 FileBackend()
151 {
152 }
153
154 //this class is used to redirect the calls to all the file backends available
155 virtual void setLoaded(QFeedbackFileEffect *effect, bool load)
156 {
157 if (load) {
158 //start loading
159 tryBackendLoad(effect);
160 } else {
161 //unload
162 if (QFeedbackFileInterface *subBackend = getBackend(effect))
163 subBackend->setLoaded(effect, load);
164 QFeedbackFileEffectPrivate::get(e: effect)->loadFinished(success: false); // make sure it's marked unloaded [XXX this isn't allowed to fail!]
165 }
166 }
167
168 virtual void setEffectState(QFeedbackFileEffect *effect, QFeedbackEffect::State state)
169 {
170 if (QFeedbackFileInterface *subBackend = getBackend(effect))
171 subBackend->setEffectState(effect, state);
172 else
173 QFeedbackInterface::reportError(effect, error: QFeedbackEffect::UnknownError);
174 }
175
176 virtual QFeedbackEffect::State effectState(const QFeedbackFileEffect *effect)
177 {
178 if (QFeedbackFileInterface *subBackend = getBackend(effect))
179 return subBackend->effectState(effect);
180
181 return QFeedbackEffect::Stopped;
182 }
183
184 virtual int effectDuration(const QFeedbackFileEffect *effect)
185 {
186 if (QFeedbackFileInterface *subBackend = getBackend(effect))
187 return subBackend->effectDuration(effect);
188
189 return 0;
190 }
191
192 virtual QStringList supportedMimeTypes()
193 {
194 QStringList ret;
195 for (int i = 0; i < subBackends.count(); ++i) {
196 ret += subBackends.at(i)->supportedMimeTypes();
197 }
198 return ret;
199 }
200
201 void addFileBackend(QFeedbackFileInterface *backend)
202 {
203 subBackends.append(t: backend);
204 }
205
206 void reportLoadFinished(QFeedbackFileEffect *effect, bool success)
207 {
208 if (success) {
209 //the file was loaded by the current backend
210 QFeedbackFileEffectPrivate::get(e: effect)->loadFinished(success: true);
211 return;
212 }
213
214 //let's try the next backend
215 tryBackendLoad(effect);
216 }
217
218private:
219 QList<QFeedbackFileInterface*> subBackends;
220
221 QFeedbackFileInterface *getBackend(const QFeedbackFileEffect *effect)
222 {
223 const QFeedbackFileEffectPrivate *priv = QFeedbackFileEffectPrivate::get(e: effect);
224 if (priv->backendUsed >= 0 && priv->backendUsed < subBackends.count())
225 return subBackends.at(i: priv->backendUsed);
226 return 0;
227 }
228
229 void tryBackendLoad(QFeedbackFileEffect *effect)
230 {
231 QFeedbackFileEffectPrivate *p = QFeedbackFileEffectPrivate::get(e: effect);
232 p->backendUsed++;
233
234 //let's try to load the file
235 if (p->backendUsed >= subBackends.count()) {
236 //the file couldn't be loaded
237 p->loadFinished(success: false);
238 reportError(effect, error: QFeedbackEffect::UnknownError);
239 // Do a state change as well, (to stopped)
240 QMetaObject::invokeMethod(obj: effect, member: "stateChanged");
241 return;
242 }
243
244 subBackends.at(i: p->backendUsed)->setLoaded(effect, true);
245 //now we're waiting for the reply (call to asyncLoadFinished)
246 }
247};
248
249
250class BackendManager
251{
252public:
253 BackendManager()
254 {
255 QStringList pluginPaths = getPluginPaths(plugintype: QLatin1String("feedback"));
256
257 foreach (const QString& pluginPath, pluginPaths) {
258 QPluginLoader loader(pluginPath);
259
260 hapticsBackend.tryLoad(loader);
261 themeBackend.tryLoad(loader);
262
263 if (QFeedbackFileInterface *newFile = qobject_cast<QFeedbackFileInterface*>(object: loader.instance())) {
264 fileBackend.addFileBackend(backend: newFile);
265 } else {
266 loader.unload();
267 }
268 }
269
270 if (!hapticsBackend.instance())
271 hapticsBackend.setInstance(new QDummyBackend);
272 }
273
274 QFeedbackHapticsInterface* hapticsBackendInstance()
275 {
276 return hapticsBackend.instance();
277 }
278
279 QFeedbackThemeInterface* themeBackendInstance()
280 {
281 return themeBackend.instance();
282 }
283
284 FileBackend *fileBackendInstance()
285 {
286 return &fileBackend;
287 }
288
289private:
290 BackendLoader<QFeedbackHapticsInterface> hapticsBackend;
291 BackendLoader<QFeedbackThemeInterface> themeBackend;
292 FileBackend fileBackend;
293};
294
295Q_GLOBAL_STATIC(BackendManager, backendManager)
296
297/*!
298 \class QFeedbackHapticsInterface
299 \ingroup feedback
300
301 \brief The QFeedbackHapticsInterface class is the base class for plugins providing custom haptics effects.
302
303 This interface will be used to try to play custom effects with specific duration, intensity, envelope and period.
304 An effect is always played on a specified actuator.
305*/
306
307
308/*!
309 \enum QFeedbackHapticsInterface::EffectProperty
310 This enum describes all effect properties for haptics effects.
311
312 \value Duration The effect duration (in milliseconds)
313 \value Intensity The effect intensity
314 \value AttackTime The effect attack time (in milliseconds)
315 \value AttackIntensity The effect attack intensity
316 \value FadeTime The effect fade time (in milliseconds)
317 \value FadeIntensity The effect fade intensity
318 \value Period The effect period, this is an optional effect property.
319*/
320
321/*!
322 \enum QFeedbackHapticsInterface::ActuatorProperty
323 This enum describes all actuator properties.
324
325 \value Name The actuator name.
326 \value State The actuator state.
327 \value Enabled The actuator enabled state.
328 */
329
330
331/*!
332 \fn QFeedbackHapticsInterface::actuators()
333
334 Return the available actuators provided by this plugin. The ownership of the actuator objects stays with the plugin.
335*/
336
337/*!
338 \fn QFeedbackHapticsInterface::pluginPriority()
339
340 Returns the priority for the plugin.
341 \sa QFeedbackInterface::PluginPriority
342*/
343
344
345// XXX TODO.. these should have been pointers to QFA :/
346/*!
347 \fn QFeedbackHapticsInterface::setActuatorProperty(const QFeedbackActuator& actuator, ActuatorProperty property, const QVariant & value)
348
349 Sets a \a value for \a property on the \a actuator.
350
351 \sa ActuatorProperty
352*/
353
354/*!
355 \fn QFeedbackHapticsInterface::actuatorProperty(const QFeedbackActuator & actuator, ActuatorProperty property)
356
357 Returns the value for the \a property for an \a actuator.
358
359 \sa ActuatorProperty
360*/
361
362/*!
363 \fn QFeedbackHapticsInterface::isActuatorCapabilitySupported(const QFeedbackActuator &actuator, QFeedbackActuator::Capability capability)
364
365 Returns true if the \a actuator supports the \a capability.
366*/
367
368
369/*!
370 \fn QFeedbackHapticsInterface::updateEffectProperty(const QFeedbackHapticsEffect *effect, EffectProperty property)
371
372 Tells the backend that the \a property has been updated for the supplied \a effect.
373*/
374
375/*!
376 \fn QFeedbackHapticsInterface::setEffectState(const QFeedbackHapticsEffect *effect, QFeedbackEffect::State state)
377
378 Sets the state to \a state for the effect \a effect. If that fails the backend should report an error by
379 calling reportError and \a effect will in turn emit an error signal.
380*/
381
382/*!
383 \fn QFeedbackHapticsInterface::effectState(const QFeedbackHapticsEffect *effect)
384
385 Get the current state for the effect \a effect.
386*/
387
388/*!
389 \internal
390 \fn QFeedbackHapticsInterface::instance()
391
392 Returns the instance of the object managing haptics custom effects.
393 If no backend has been loaded, this will return a null pointer.
394*/
395QFeedbackHapticsInterface *QFeedbackHapticsInterface::instance()
396{
397 return backendManager()->hapticsBackendInstance();
398}
399
400/*!
401 \fn QFeedbackHapticsInterface::createFeedbackActuator(QObject *parent, int id)
402
403 Creates an instance of QFeedbackActuator with the identifier \a id and parent \a parent. This allows
404 backends to create instances of actuators. It is then up to the each backend to manage
405 the identifiers according to its needs.
406*/
407QFeedbackActuator* QFeedbackHapticsInterface::createFeedbackActuator(QObject* parent, int id)
408{
409 return new QFeedbackActuator(parent, id);
410}
411
412/*!
413 \class QFeedbackThemeInterface
414 \ingroup feedback
415
416 \brief The QFeedbackThemeInterface class is the base class for plugins providing themed effects.
417
418 They can be of any nature (tactile, audio...).
419 This simple interface will be used to play those themed effects by a simple call to the play method.
420*/
421
422
423/*!
424 \fn QFeedbackThemeInterface::pluginPriority()
425
426 Returns the priority for the plugin.
427*/
428
429/*!
430 \fn QFeedbackThemeInterface::play(QFeedbackEffect::Effect effect)
431
432 Plays the theme effect \a effect. Returns false in case of an error.
433*/
434
435/*!
436 \internal
437 \fn QFeedbackThemeInterface::instance()
438
439 Returns the instance of the object managing theme effects.
440 If no backend has been loaded, this will return a null pointer.
441*/
442QFeedbackThemeInterface *QFeedbackThemeInterface::instance()
443{
444 return backendManager()->themeBackendInstance();
445}
446
447/*!
448 \internal
449 \class QFeedbackFileInterface
450 \ingroup feedback
451
452 \brief The QFeedbackFileInterface class is the base class for plugins providing support for effects stored in files.
453
454 They can be of any nature (tactile, audio...). As it is possible to load many different file types using
455 different technologies, all the backend plugins exposing this interface will be loaded at the same time.
456 When loading a file all the backend will be tried in order until one can load the file. It is thus very important
457 that the backends return a load status as soon as possible to not take a too long time to load a file.
458*/
459
460/*!
461 \internal
462 \fn QFeedbackFileInterface::setLoaded(QFeedbackFileEffect* effect, bool value)
463
464 Sets the state of the effect \a effect to be loaded if \a value is true, otherwise unloaded.
465 Loading a file is asynchronous. Once the backend knows if it has loaded or can't load the file, it must
466 call the reportLoadFinished function.
467*/
468
469/*!
470 \internal
471 \fn QFeedbackFileInterface::setEffectState(QFeedbackFileEffect *effect, QFeedbackEffect::State state)
472
473 Sets the state of \a effect to \a state.
474*/
475
476/*!
477 \internal
478 \fn QFeedbackFileInterface::effectState(const QFeedbackFileEffect *effect)
479
480 Returns the current state of the effect \a effect.
481*/
482
483/*!
484 \internal
485 \fn QFeedbackFileInterface::effectDuration(const QFeedbackFileEffect *effect)
486
487 Return the duration of \a effect, in milliseconds.
488 It should return \l QFeedbackEffect::Infinite in case the duration is infinite, or 0 if undefined or unknown.
489*/
490
491/*!
492 \internal
493 \fn QFeedbackFileInterface::supportedMimeTypes()
494
495 Returns a list of the MIME types supported by this plugin.
496*/
497
498/*!
499 \internal
500 \fn QFeedbackFileInterface::instance()
501
502 Returns the instance of the object managing theme effects.
503 Even if no backend has been loaded, this will never return a null pointer.
504*/
505QFeedbackFileInterface *QFeedbackFileInterface::instance()
506{
507 return backendManager()->fileBackendInstance();
508}
509
510/*!
511 \internal
512 \fn QFeedbackFileInterface::reportLoadFinished(QFeedbackFileEffect *effect, bool success)
513
514 This is the function the backend should call when it has finished trying to load the effect \a effect.
515 As loading a file is asynchronous and multiple plugins are attempted after each other, the
516 backend has to call this function in order for the process to perform smoothly.
517 The success of the operation is indicated by the \a success parameter.
518*/
519void QFeedbackFileInterface::reportLoadFinished(QFeedbackFileEffect *effect, bool success)
520{
521 backendManager()->fileBackendInstance()->reportLoadFinished(effect, success);
522}
523
524QT_END_NAMESPACE
525

source code of qtfeedback/src/feedback/qfeedbackplugin.cpp