From 1f365224925d05fdca8c720d17c1c653ec9d6c3f Mon Sep 17 00:00:00 2001 From: uvos Date: Tue, 1 Feb 2022 19:54:07 +0100 Subject: [PATCH] add joysick support and state feedback --- src/QJoysticks.cpp | 479 +++++++++++++++++++++++++ src/QJoysticks.h | 122 +++++++ src/items/itemstore.cpp | 5 + src/jsbackend/JoysticksCommon.h | 112 ++++++ src/jsbackend/JoysticksCommon.h.orig | 111 ++++++ src/jsbackend/SDL_Joysticks.cpp | 309 ++++++++++++++++ src/jsbackend/SDL_Joysticks.cpp.orig | 345 ++++++++++++++++++ src/jsbackend/SDL_Joysticks.h | 76 ++++ src/jsbackend/SDL_Joysticks.h.orig | 76 ++++ src/jsbackend/VirtualJoystick.cpp | 424 ++++++++++++++++++++++ src/jsbackend/VirtualJoystick.cpp.orig | 423 ++++++++++++++++++++++ src/jsbackend/VirtualJoystick.h | 112 ++++++ src/jsbackend/VirtualJoystick.h.orig | 112 ++++++ src/main.cpp | 3 + src/microcontroller.cpp | 35 +- src/trainjs.cpp | 116 ++++++ src/trainjs.h | 47 +++ src/ui/itemscrollbox.cpp | 11 + src/ui/itemwidget.cpp | 38 +- src/ui/itemwidget.h | 3 +- trainControllerUI.pro | 13 +- 21 files changed, 2940 insertions(+), 32 deletions(-) create mode 100644 src/QJoysticks.cpp create mode 100644 src/QJoysticks.h create mode 100644 src/jsbackend/JoysticksCommon.h create mode 100644 src/jsbackend/JoysticksCommon.h.orig create mode 100644 src/jsbackend/SDL_Joysticks.cpp create mode 100644 src/jsbackend/SDL_Joysticks.cpp.orig create mode 100644 src/jsbackend/SDL_Joysticks.h create mode 100644 src/jsbackend/SDL_Joysticks.h.orig create mode 100644 src/jsbackend/VirtualJoystick.cpp create mode 100644 src/jsbackend/VirtualJoystick.cpp.orig create mode 100644 src/jsbackend/VirtualJoystick.h create mode 100644 src/jsbackend/VirtualJoystick.h.orig create mode 100644 src/trainjs.cpp create mode 100644 src/trainjs.h diff --git a/src/QJoysticks.cpp b/src/QJoysticks.cpp new file mode 100644 index 0000000..2deb989 --- /dev/null +++ b/src/QJoysticks.cpp @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include "QJoysticks.h" +#include "jsbackend/SDL_Joysticks.h" +#include "jsbackend/VirtualJoystick.h" + +QJoysticks::QJoysticks() +{ + /* Initialize input methods */ + m_sdlJoysticks = new SDL_Joysticks(this); + m_virtualJoystick = new VirtualJoystick(this); + + /* Configure SDL joysticks */ + connect(sdlJoysticks(), &SDL_Joysticks::POVEvent, this, &QJoysticks::POVEvent); + connect(sdlJoysticks(), &SDL_Joysticks::axisEvent, this, &QJoysticks::axisEvent); + connect(sdlJoysticks(), &SDL_Joysticks::buttonEvent, this, &QJoysticks::buttonEvent); + connect(sdlJoysticks(), &SDL_Joysticks::countChanged, this, &QJoysticks::updateInterfaces); + + /* Configure virtual joysticks */ + connect(virtualJoystick(), &VirtualJoystick::povEvent, this, &QJoysticks::POVEvent); + connect(virtualJoystick(), &VirtualJoystick::axisEvent, this, &QJoysticks::axisEvent); + connect(virtualJoystick(), &VirtualJoystick::buttonEvent, this, &QJoysticks::buttonEvent); + connect(virtualJoystick(), &VirtualJoystick::enabledChanged, this, &QJoysticks::updateInterfaces); + + /* React to own signals to create QML signals */ + connect(this, &QJoysticks::POVEvent, this, &QJoysticks::onPOVEvent); + connect(this, &QJoysticks::axisEvent, this, &QJoysticks::onAxisEvent); + connect(this, &QJoysticks::buttonEvent, this, &QJoysticks::onButtonEvent); + + /* Configure the settings */ + m_sortJoyticks = 0; + m_settings = new QSettings(qApp->organizationName(), qApp->applicationName()); + m_settings->beginGroup("Blacklisted Joysticks"); +} + +QJoysticks::~QJoysticks() +{ + delete m_settings; + delete m_sdlJoysticks; + delete m_virtualJoystick; +} + +/** + * Returns the one and only instance of this class + */ +QJoysticks *QJoysticks::getInstance() +{ + static QJoysticks joysticks; + return &joysticks; +} + +/** + * Returns the number of joysticks that are attached to the computer and/or + * registered with the \c QJoysticks system. + * + * \note This count also includes the virtual joystick (if its enabled) + */ +int QJoysticks::count() const +{ + return inputDevices().count(); +} + +/** + * Returns the number of joysticks that are not blacklisted. + * This can be considered the "effective" number of joysticks. + */ +int QJoysticks::nonBlacklistedCount() +{ + int cnt = count(); + + for (int i = 0; i < count(); ++i) + if (isBlacklisted(i)) + --cnt; + + return cnt; +} + +/** + * Returns a list with the names of all registered joystick. + * + * \note This list also includes the blacklisted joysticks + * \note This list also includes the virtual joystick (if its enabled) + */ +QStringList QJoysticks::deviceNames() const +{ + QStringList names; + + foreach (QJoystickDevice *joystick, inputDevices()) + names.append(joystick->name); + + return names; +} + +/** + * Returns the POV value for the given joystick \a index and \a pov ID + */ +int QJoysticks::getPOV(const int index, const int pov) +{ + if (joystickExists(index)) + return getInputDevice(index)->povs.at(pov); + + return -1; +} + +/** + * Returns the axis value for the given joystick \a index and \a axis ID + */ +double QJoysticks::getAxis(const int index, const int axis) +{ + if (joystickExists(index)) + return getInputDevice(index)->axes.at(axis); + + return 0; +} + +/** + * Returns the button value for the given joystick \a index and \a button ID + */ +bool QJoysticks::getButton(const int index, const int button) +{ + if (joystickExists(index)) + return getInputDevice(index)->buttons.at(button); + + return false; +} + +/** + * Returns the number of axes that the joystick at the given \a index has. + */ +int QJoysticks::getNumAxes(const int index) +{ + if (joystickExists(index)) + return getInputDevice(index)->axes.count(); + + return -1; +} + +/** + * Returns the number of POVs that the joystick at the given \a index has. + */ +int QJoysticks::getNumPOVs(const int index) +{ + if (joystickExists(index)) + return getInputDevice(index)->povs.count(); + + return -1; +} + +/** + * Returns the number of buttons that the joystick at the given \a index has. + */ +int QJoysticks::getNumButtons(const int index) +{ + if (joystickExists(index)) + return getInputDevice(index)->buttons.count(); + + return -1; +} + +/** + * Returns \c true if the joystick at the given \a index is blacklisted. + */ +bool QJoysticks::isBlacklisted(const int index) +{ + if (joystickExists(index)) + return inputDevices().at(index)->blacklisted; + + return true; +} + +/** + * Returns \c true if the joystick at the given \a index is valid, otherwise, + * the function returns \c false and warns the user through the console. + */ +bool QJoysticks::joystickExists(const int index) +{ + return (index >= 0) && (count() > index); +} + +/** + * Returns the name of the given joystick + */ +QString QJoysticks::getName(const int index) +{ + if (joystickExists(index)) + return m_devices.at(index)->name; + + return "Invalid Joystick"; +} + +/** + * Returns a pointer to the SDL joysticks system. + * This can be used if you need to get more information regarding the joysticks + * registered and managed with SDL. + */ +SDL_Joysticks *QJoysticks::sdlJoysticks() const +{ + return m_sdlJoysticks; +} + +/** + * Returns a pointer to the virtual joystick system. + * This can be used if you need to get more information regarding the virtual + * joystick or want to change its properties directly. + * + * \note You can also change the properties of the virtual joysticks using the + * functions of the \c QJoysticks system class + */ +VirtualJoystick *QJoysticks::virtualJoystick() const +{ + return m_virtualJoystick; +} + +/** + * Returns a pointer to the device at the given \a index. + */ +QJoystickDevice *QJoysticks::getInputDevice(const int index) +{ + if (joystickExists(index)) + return inputDevices().at(index); + + return Q_NULLPTR; +} + +/** + * Returns a pointer to a list containing all registered joysticks. + * This can be used for advanced hacks or just to get all properties of each + * joystick. + */ +QList QJoysticks::inputDevices() const +{ + return m_devices; +} + +/** + * If \a sort is set to true, then the device list will put all blacklisted + * joysticks at the end of the list + */ +void QJoysticks::setSortJoysticksByBlacklistState(bool sort) +{ + if (m_sortJoyticks != sort) + { + m_sortJoyticks = sort; + updateInterfaces(); + } +} + +/** + * Blacklists or whitelists the joystick at the given \a index. + * + * \note This function does not have effect if the given joystick does not exist + * \note Once the joystick is blacklisted, the joystick list will be updated + */ +void QJoysticks::setBlacklisted(const int index, bool blacklisted) +{ + Q_ASSERT(joystickExists(index)); + + /* Netrualize the joystick */ + if (blacklisted) + { + for (int i = 0; i < getNumAxes(index); ++i) + emit axisChanged(index, i, 0); + + for (int i = 0; i < getNumButtons(index); ++i) + emit buttonChanged(index, i, false); + + for (int i = 0; i < getNumPOVs(index); ++i) + emit povChanged(index, i, 0); + } + + /* See if blacklist value was actually changed */ + bool changed = m_devices.at(index)->blacklisted != blacklisted; + + /* Save settings */ + m_devices.at(index)->blacklisted = blacklisted; + m_settings->setValue(getName(index), blacklisted); + + /* Re-scan joysticks if blacklist value has changed */ + if (changed) + updateInterfaces(); +} + +/** + * 'Rescans' for new/removed joysticks and registers them again. + */ +void QJoysticks::updateInterfaces() +{ + m_devices.clear(); + + /* Put blacklisted joysticks at the bottom of the list */ + if (m_sortJoyticks) + { + /* Register non-blacklisted SDL joysticks */ + foreach (QJoystickDevice *joystick, sdlJoysticks()->joysticks()) + { + joystick->blacklisted = m_settings->value(joystick->name, false).toBool(); + if (!joystick->blacklisted) + addInputDevice(joystick); + } + + /* Register the virtual joystick (if its not blacklisted) */ + if (virtualJoystick()->joystickEnabled()) + { + QJoystickDevice *joystick = virtualJoystick()->joystick(); + joystick->blacklisted = m_settings->value(joystick->name, false).toBool(); + + if (!joystick->blacklisted) + { + addInputDevice(joystick); + virtualJoystick()->setJoystickID(inputDevices().count() - 1); + } + } + + /* Register blacklisted SDL joysticks */ + foreach (QJoystickDevice *joystick, sdlJoysticks()->joysticks()) + { + joystick->blacklisted = m_settings->value(joystick->name, false).toBool(); + if (joystick->blacklisted) + addInputDevice(joystick); + } + + /* Register the virtual joystick (if its blacklisted) */ + if (virtualJoystick()->joystickEnabled()) + { + QJoystickDevice *joystick = virtualJoystick()->joystick(); + joystick->blacklisted = m_settings->value(joystick->name, false).toBool(); + + if (joystick->blacklisted) + { + addInputDevice(joystick); + virtualJoystick()->setJoystickID(inputDevices().count() - 1); + } + } + } + + /* Sort normally */ + else + { + /* Register SDL joysticks */ + foreach (QJoystickDevice *joystick, sdlJoysticks()->joysticks()) + { + addInputDevice(joystick); + joystick->blacklisted = m_settings->value(joystick->name, false).toBool(); + } + + /* Register virtual joystick */ + if (virtualJoystick()->joystickEnabled()) + { + QJoystickDevice *joystick = virtualJoystick()->joystick(); + joystick->blacklisted = m_settings->value(joystick->name, false).toBool(); + + addInputDevice(joystick); + virtualJoystick()->setJoystickID(inputDevices().count() - 1); + } + } + + emit countChanged(); +} + +/** + * Changes the axis value range of the virtual joystick. + * + * Take into account that maximum axis values supported by the \c QJoysticks + * system is from \c -1 to \c 1. + */ +void QJoysticks::setVirtualJoystickRange(qreal range) +{ + virtualJoystick()->setAxisRange(range); +} + +/** + * Enables or disables the virtual joystick + */ +void QJoysticks::setVirtualJoystickEnabled(bool enabled) +{ + virtualJoystick()->setJoystickEnabled(enabled); +} + +void QJoysticks::setVirtualJoystickAxisSensibility(qreal sensibility) +{ + virtualJoystick()->setAxisSensibility(sensibility); +} + +/** + * Removes all the registered joysticks and emits appropriate signals. + */ +void QJoysticks::resetJoysticks() +{ + m_devices.clear(); + emit countChanged(); +} + +/** + * Registers the given \a device to the \c QJoysticks system + */ +void QJoysticks::addInputDevice(QJoystickDevice *device) +{ + Q_ASSERT(device); + m_devices.append(device); +} + +/** + * Configures the QML-friendly signal based on the information given by the + * \a event data and updates the joystick values + */ +void QJoysticks::onPOVEvent(const QJoystickPOVEvent &e) +{ + if (e.joystick == nullptr) + return; + + if (!isBlacklisted(e.joystick->id)) + { + if (e.pov < getInputDevice(e.joystick->id)->povs.count()) + { + getInputDevice(e.joystick->id)->povs[e.pov] = e.angle; + emit povChanged(e.joystick->id, e.pov, e.angle); + } + } +} + +/** + * Configures the QML-friendly signal based on the information given by the + * \a event data and updates the joystick values + */ +void QJoysticks::onAxisEvent(const QJoystickAxisEvent &e) +{ + if (e.joystick == nullptr) + return; + + if (!isBlacklisted(e.joystick->id)) + { + if (e.axis < getInputDevice(e.joystick->id)->axes.count()) + { + getInputDevice(e.joystick->id)->axes[e.axis] = e.value; + emit axisChanged(e.joystick->id, e.axis, e.value); + } + } +} + +/** + * Configures the QML-friendly signal based on the information given by the + * \a event data and updates the joystick values + */ +void QJoysticks::onButtonEvent(const QJoystickButtonEvent &e) +{ + if (e.joystick == nullptr) + return; + + if (!isBlacklisted(e.joystick->id)) + { + if (e.button < getInputDevice(e.joystick->id)->buttons.count()) + { + getInputDevice(e.joystick->id)->buttons[e.button] = e.pressed; + emit buttonChanged(e.joystick->id, e.button, e.pressed); + } + } +} diff --git a/src/QJoysticks.h b/src/QJoysticks.h new file mode 100644 index 0000000..819e092 --- /dev/null +++ b/src/QJoysticks.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _QJOYSTICKS_MAIN_H +#define _QJOYSTICKS_MAIN_H + +#include +#include +#include "jsbackend/JoysticksCommon.h" + +class QSettings; +class SDL_Joysticks; +class VirtualJoystick; + +/** + * \brief Manages the input systems and communicates them with the application + * + * The \c QJoysticks class is the "god-object" of this system. It manages every + * input system used by the application (e.g. SDL for real joysticks and + * keyboard for virtual joystick) and communicates every module/input system + * with the rest of the application through standarized types. + * + * The joysticks are assigned a numerical ID, which the \c QJoysticks can use to + * identify them. The ID's start with \c 0 (as with a QList). The ID's are + * refreshed when a joystick is attached or removed. The first joystick that + * has been connected to the computer will have \c 0 as an ID, the second + * joystick will have \c 1 as an ID, and so on... + * + * \note the virtual joystick will ALWAYS be the last joystick to be registered, + * even if it has been enabled before any SDL joystick has been attached. + */ +class QJoysticks : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(int nonBlacklistedCount READ nonBlacklistedCount NOTIFY countChanged) + Q_PROPERTY(QStringList deviceNames READ deviceNames NOTIFY countChanged) + + friend class Test_QJoysticks; + +signals: + void countChanged(); + void enabledChanged(const bool enabled); + void POVEvent(const QJoystickPOVEvent &event); + void axisEvent(const QJoystickAxisEvent &event); + void buttonEvent(const QJoystickButtonEvent &event); + void povChanged(const int js, const int pov, const int angle); + void axisChanged(const int js, const int axis, const qreal value); + void buttonChanged(const int js, const int button, const bool pressed); + +public: + static QJoysticks *getInstance(); + + int count() const; + int nonBlacklistedCount(); + QStringList deviceNames() const; + + Q_INVOKABLE int getPOV(const int index, const int pov); + Q_INVOKABLE double getAxis(const int index, const int axis); + Q_INVOKABLE bool getButton(const int index, const int button); + + Q_INVOKABLE int getNumAxes(const int index); + Q_INVOKABLE int getNumPOVs(const int index); + Q_INVOKABLE int getNumButtons(const int index); + Q_INVOKABLE bool isBlacklisted(const int index); + Q_INVOKABLE bool joystickExists(const int index); + Q_INVOKABLE QString getName(const int index); + + SDL_Joysticks *sdlJoysticks() const; + VirtualJoystick *virtualJoystick() const; + QJoystickDevice *getInputDevice(const int index); + QList inputDevices() const; + +public slots: + void updateInterfaces(); + void setVirtualJoystickRange(qreal range); + void setVirtualJoystickEnabled(bool enabled); + void setVirtualJoystickAxisSensibility(qreal sensibility); + void setSortJoysticksByBlacklistState(bool sort); + void setBlacklisted(int index, bool blacklisted); + +protected: + explicit QJoysticks(); + ~QJoysticks(); + +private slots: + void resetJoysticks(); + void addInputDevice(QJoystickDevice *device); + void onPOVEvent(const QJoystickPOVEvent &e); + void onAxisEvent(const QJoystickAxisEvent &e); + void onButtonEvent(const QJoystickButtonEvent &e); + +private: + bool m_sortJoyticks; + + QSettings *m_settings; + SDL_Joysticks *m_sdlJoysticks; + VirtualJoystick *m_virtualJoystick; + + QList m_devices; +}; + +#endif diff --git a/src/items/itemstore.cpp b/src/items/itemstore.cpp index d0f11c9..a6f9842 100644 --- a/src/items/itemstore.cpp +++ b/src/items/itemstore.cpp @@ -7,6 +7,11 @@ ItemStore::ItemStore(QObject *parent): QObject(parent) void ItemStore::addItem(std::shared_ptr item) { + if(!item) + { + qWarning()<<"invalid item"; + return; + } bool mached = false; for(unsigned i = 0; i < items_.size(); i++ ) if(*items_[i] == *item) mached = true; diff --git a/src/jsbackend/JoysticksCommon.h b/src/jsbackend/JoysticksCommon.h new file mode 100644 index 0000000..3c27d25 --- /dev/null +++ b/src/jsbackend/JoysticksCommon.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _QJOYSTICKS_COMMON_H +#define _QJOYSTICKS_COMMON_H + +#include +#include + +/** + * @brief Represents a joystick and its properties + * + * This structure contains: + * - The numerical ID of the joystick + * - The sdl instance id of the joystick + * - The joystick display name + * - The number of axes operated by the joystick + * - The number of buttons operated by the joystick + * - The number of POVs operated by the joystick + * - A boolean value blacklisting or whitelisting the joystick + */ +struct QJoystickDevice +{ + int id; /**< Holds the ID of the joystick */ + int instanceID; /**< Holds the sdl instance id of the joystick */ + QString name; /**< Holds the name/title of the joystick */ + QList povs; /**< Holds the values for each POV */ + QList axes; /**< Holds the values for each axis */ + QList buttons; /**< Holds the values for each button */ + bool blacklisted; /**< Holds \c true if the joystick is disabled */ +}; + +/** + * @brief Represents a joystick rumble request + * + * This structure contains: + * - A pointer to the joystick that should be rumbled + * - The length (in milliseconds) of the rumble effect. + * - The strength of the effect (from 0 to 1) + */ +struct QJoystickRumble +{ + uint length; /**< The duration of the effect */ + qreal strength; /**< Strength of the effect (0 to 1) */ + QJoystickDevice *joystick; /**< The pointer to the target joystick */ +}; + +/** + * @brief Represents an POV event that can be triggered by a joystick + * + * This structure contains: + * - A pointer to the joystick that triggered the event + * - The POV number/ID + * - The current POV angle + */ +struct QJoystickPOVEvent +{ + int pov; /**< The numerical ID of the POV */ + int angle; /**< The current angle of the POV */ + QJoystickDevice *joystick; /**< Pointer to the device that caused the event */ +}; + +/** + * @brief Represents an axis event that can be triggered by a joystick + * + * This structure contains: + * - A pointer to the joystick that caused the event + * - The axis number/ID + * - The current axis value + */ +struct QJoystickAxisEvent +{ + int axis; /**< The numerical ID of the axis */ + qreal value; /**< The value (from -1 to 1) of the axis */ + QJoystickDevice *joystick; /**< Pointer to the device that caused the event */ +}; + +/** + * @brief Represents a button event that can be triggered by a joystick + * + * This structure contains: + * - A pointer to the joystick that caused the event + * - The button number/ID + * - The current button state (pressed or not pressed) + */ +struct QJoystickButtonEvent +{ + int button; /**< The numerical ID of the button */ + bool pressed; /**< Set to \c true if the button is pressed */ + QJoystickDevice *joystick; /**< Pointer to the device that caused the event */ +}; + +#endif diff --git a/src/jsbackend/JoysticksCommon.h.orig b/src/jsbackend/JoysticksCommon.h.orig new file mode 100644 index 0000000..60d3d97 --- /dev/null +++ b/src/jsbackend/JoysticksCommon.h.orig @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _QJOYSTICKS_COMMON_H +#define _QJOYSTICKS_COMMON_H + +#include + +/** + * @brief Represents a joystick and its properties + * + * This structure contains: + * - The numerical ID of the joystick + * - The sdl instance id of the joystick + * - The joystick display name + * - The number of axes operated by the joystick + * - The number of buttons operated by the joystick + * - The number of POVs operated by the joystick + * - A boolean value blacklisting or whitelisting the joystick + */ +struct QJoystickDevice +{ + int id; /**< Holds the ID of the joystick */ + int instanceID; /**< Holds the sdl instance id of the joystick */ + QString name; /**< Holds the name/title of the joystick */ + QList povs; /**< Holds the values for each POV */ + QList axes; /**< Holds the values for each axis */ + QList buttons; /**< Holds the values for each button */ + bool blacklisted; /**< Holds \c true if the joystick is disabled */ +}; + +/** + * @brief Represents a joystick rumble request + * + * This structure contains: + * - A pointer to the joystick that should be rumbled + * - The length (in milliseconds) of the rumble effect. + * - The strength of the effect (from 0 to 1) + */ +struct QJoystickRumble +{ + uint length; /**< The duration of the effect */ + qreal strength; /**< Strength of the effect (0 to 1) */ + QJoystickDevice *joystick; /**< The pointer to the target joystick */ +}; + +/** + * @brief Represents an POV event that can be triggered by a joystick + * + * This structure contains: + * - A pointer to the joystick that triggered the event + * - The POV number/ID + * - The current POV angle + */ +struct QJoystickPOVEvent +{ + int pov; /**< The numerical ID of the POV */ + int angle; /**< The current angle of the POV */ + QJoystickDevice *joystick; /**< Pointer to the device that caused the event */ +}; + +/** + * @brief Represents an axis event that can be triggered by a joystick + * + * This structure contains: + * - A pointer to the joystick that caused the event + * - The axis number/ID + * - The current axis value + */ +struct QJoystickAxisEvent +{ + int axis; /**< The numerical ID of the axis */ + qreal value; /**< The value (from -1 to 1) of the axis */ + QJoystickDevice *joystick; /**< Pointer to the device that caused the event */ +}; + +/** + * @brief Represents a button event that can be triggered by a joystick + * + * This structure contains: + * - A pointer to the joystick that caused the event + * - The button number/ID + * - The current button state (pressed or not pressed) + */ +struct QJoystickButtonEvent +{ + int button; /**< The numerical ID of the button */ + bool pressed; /**< Set to \c true if the button is pressed */ + QJoystickDevice *joystick; /**< Pointer to the device that caused the event */ +}; + +#endif diff --git a/src/jsbackend/SDL_Joysticks.cpp b/src/jsbackend/SDL_Joysticks.cpp new file mode 100644 index 0000000..4b2bdd0 --- /dev/null +++ b/src/jsbackend/SDL_Joysticks.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "SDL_Joysticks.h" + +SDL_Joysticks::SDL_Joysticks(QObject *parent) + : QObject(parent) +{ + if (SDL_Init(SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER)) + { + qDebug() << "Cannot initialize SDL:" << SDL_GetError(); + qApp->quit(); + } + + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + + SDL_JoystickEventState(SDL_ENABLE); + + int count = SDL_NumJoysticks(); + + for (int i = 0; i < count; ++i) + configureJoystick(i); + + QTimer::singleShot(100, Qt::PreciseTimer, this, SLOT(update())); +} + +SDL_Joysticks::~SDL_Joysticks() +{ + for (QMap::iterator i = m_joysticks.begin(); + i != m_joysticks.end(); ++i) + { + delete i.value(); + } + + SDL_Quit(); +} + +/** + * Returns a list with all the registered joystick devices + */ +QMap SDL_Joysticks::joysticks() +{ + int index = 0; + QMap joysticks; + for (QMap::iterator it = m_joysticks.begin(); + it != m_joysticks.end(); ++it) + { + it.value()->id = index; + joysticks[index++] = it.value(); + } + + return joysticks; +} + +/** + * Based on the data contained in the \a request, this function will instruct + * the appropriate joystick to rumble for + */ +void SDL_Joysticks::rumble(const QJoystickRumble &request) +{ + SDL_Haptic *haptic = SDL_HapticOpen(request.joystick->id); + + if (haptic) + { + SDL_HapticRumbleInit(haptic); + SDL_HapticRumblePlay(haptic, request.strength, request.length); + } +} + +/** + * Polls for new SDL events and reacts to each event accordingly. + */ +void SDL_Joysticks::update() +{ + SDL_Event event; + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_JOYDEVICEADDED: + configureJoystick(&event); + break; + case SDL_JOYDEVICEREMOVED: + { + SDL_Joystick *js = SDL_JoystickFromInstanceID(event.jdevice.which); + if (js) + { + SDL_JoystickClose(js); + } + + SDL_GameController *gc = SDL_GameControllerFromInstanceID( + event.cdevice.which); + if (gc) + { + SDL_GameControllerClose(gc); + } + } + + delete m_joysticks[event.jdevice.which]; + m_joysticks.remove(event.jdevice.which); + + emit countChanged(); + break; + case SDL_JOYAXISMOTION: + emit axisEvent(getAxisEvent(&event)); + break; + case SDL_CONTROLLERAXISMOTION: + emit axisEvent(getAxisEvent(&event)); + break; + case SDL_JOYBUTTONUP: + emit buttonEvent(getButtonEvent(&event)); + break; + case SDL_JOYBUTTONDOWN: + emit buttonEvent(getButtonEvent(&event)); + break; + case SDL_JOYHATMOTION: + emit POVEvent(getPOVEvent(&event)); + break; + } + } + + QTimer::singleShot(10, Qt::PreciseTimer, this, SLOT(update())); +} + +void SDL_Joysticks::configureJoystick(const SDL_Event *event) +{ + configureJoystick(event->jdevice.which); +} + +void SDL_Joysticks::configureJoystick(int id) +{ + QJoystickDevice *joystick = getJoystick(id); + + if (!SDL_IsGameController(id)) + { + SDL_Joystick *js = SDL_JoystickFromInstanceID(joystick->instanceID); + if (js) + { + char guid[1024]; + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(js), guid, + sizeof(guid)); + } + } + + SDL_GameControllerOpen(id); + + emit countChanged(); +} + +/** + * Returns the josytick device registered with the given \a id. + * If no joystick with the given \a id is found, then the function will warn + * the user through the console. + */ +QJoystickDevice *SDL_Joysticks::getJoystick(int id) +{ + QJoystickDevice *joystick = new QJoystickDevice; + SDL_Joystick *sdl_joystick = SDL_JoystickOpen(id); + + if (sdl_joystick) + { + joystick->id = id; + joystick->instanceID = SDL_JoystickInstanceID(sdl_joystick); + joystick->blacklisted = false; + joystick->name = SDL_JoystickName(sdl_joystick); + + /* Get joystick properties */ + int povs = SDL_JoystickNumHats(sdl_joystick); + int axes = SDL_JoystickNumAxes(sdl_joystick); + int buttons = SDL_JoystickNumButtons(sdl_joystick); + + /* Initialize POVs */ + for (int i = 0; i < povs; ++i) + joystick->povs.append(0); + + /* Initialize axes */ + for (int i = 0; i < axes; ++i) + joystick->axes.append(0); + + /* Initialize buttons */ + for (int i = 0; i < buttons; ++i) + joystick->buttons.append(false); + + m_joysticks[joystick->instanceID] = joystick; + } + else + { + qWarning() << Q_FUNC_INFO << "Cannot find joystick with id:" << id; + } + + return joystick; +} + +/** + * Reads the contents of the given \a event and constructs a new + * \c QJoystickPOVEvent to be used with the \c QJoysticks system. + */ +QJoystickPOVEvent SDL_Joysticks::getPOVEvent(const SDL_Event + *sdl_event) +{ + QJoystickPOVEvent event; + + if (!m_joysticks.contains(sdl_event->jdevice.which)) + { + return event; + } + event.pov = sdl_event->jhat.hat; + event.joystick = m_joysticks[sdl_event->jdevice.which]; + + switch (sdl_event->jhat.value) + { + case SDL_HAT_RIGHTUP: + event.angle = 45; + break; + case SDL_HAT_RIGHTDOWN: + event.angle = 135; + break; + case SDL_HAT_LEFTDOWN: + event.angle = 225; + break; + case SDL_HAT_LEFTUP: + event.angle = 315; + break; + case SDL_HAT_UP: + event.angle = 0; + break; + case SDL_HAT_RIGHT: + event.angle = 90; + break; + case SDL_HAT_DOWN: + event.angle = 180; + break; + case SDL_HAT_LEFT: + event.angle = 270; + break; + default: + event.angle = -1; + break; + } + + return event; +} + +/** + * Reads the contents of the given \a event and constructs a new + * \c QJoystickAxisEvent to be used with the \c QJoysticks system. + */ +QJoystickAxisEvent SDL_Joysticks::getAxisEvent(const SDL_Event + *sdl_event) +{ + QJoystickAxisEvent event; + + if (!m_joysticks.contains(sdl_event->cdevice.which)) + { + return event; + } + + event.axis = sdl_event->caxis.axis; + event.value = static_cast(sdl_event->caxis.value) / 32767; + event.joystick = m_joysticks[sdl_event->cdevice.which]; + + return event; +} + +/** + * Reads the contents of the given \a event and constructs a new + * \c QJoystickButtonEvent to be used with the \c QJoysticks system. + */ +QJoystickButtonEvent SDL_Joysticks::getButtonEvent( + const SDL_Event *sdl_event) +{ + QJoystickButtonEvent event; + + if (!m_joysticks.contains(sdl_event->jdevice.which)) + { + return event; + } + + event.button = sdl_event->jbutton.button; + event.pressed = sdl_event->jbutton.state == SDL_PRESSED; + event.joystick = m_joysticks[sdl_event->jdevice.which]; + event.joystick->buttons[event.button] = event.pressed; + + return event; +} diff --git a/src/jsbackend/SDL_Joysticks.cpp.orig b/src/jsbackend/SDL_Joysticks.cpp.orig new file mode 100644 index 0000000..fceff27 --- /dev/null +++ b/src/jsbackend/SDL_Joysticks.cpp.orig @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "SDL_Joysticks.h" + +SDL_Joysticks::SDL_Joysticks(QObject *parent) + : QObject(parent) +{ + +#ifdef SDL_SUPPORTED + if (SDL_Init(SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER)) + { + qDebug() << "Cannot initialize SDL:" << SDL_GetError(); + qApp->quit(); + } + + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + + SDL_JoystickEventState(SDL_ENABLE); + + int count = SDL_NumJoysticks(); + + for (int i = 0; i < count; ++i) + configureJoystick(i); + + QTimer::singleShot(100, Qt::PreciseTimer, this, SLOT(update())); +#endif +} + +SDL_Joysticks::~SDL_Joysticks() +{ + for (QMap::iterator i = m_joysticks.begin(); + i != m_joysticks.end(); ++i) + { + delete i.value(); + } + +#ifdef SDL_SUPPORTED + SDL_Quit(); +#endif +} + +/** + * Returns a list with all the registered joystick devices + */ +QMap SDL_Joysticks::joysticks() +{ +#ifdef SDL_SUPPORTED + int index = 0; + QMap joysticks; + for (QMap::iterator it = m_joysticks.begin(); + it != m_joysticks.end(); ++it) + { + it.value()->id = index; + joysticks[index++] = it.value(); + } + + return joysticks; +#endif + return QMap(); +} + +/** + * Based on the data contained in the \a request, this function will instruct + * the appropriate joystick to rumble for + */ +void SDL_Joysticks::rumble(const QJoystickRumble &request) +{ +#ifdef SDL_SUPPORTED + SDL_Haptic *haptic = SDL_HapticOpen(request.joystick->id); + + if (haptic) + { + SDL_HapticRumbleInit(haptic); + SDL_HapticRumblePlay(haptic, request.strength, request.length); + } +#else + Q_UNUSED(request); +#endif +} + +/** + * Polls for new SDL events and reacts to each event accordingly. + */ +void SDL_Joysticks::update() +{ +#ifdef SDL_SUPPORTED + SDL_Event event; + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_JOYDEVICEADDED: + configureJoystick(&event); + break; + case SDL_JOYDEVICEREMOVED: + { + SDL_Joystick *js = SDL_JoystickFromInstanceID(event.jdevice.which); + if (js) + { + SDL_JoystickClose(js); + } + + SDL_GameController *gc = SDL_GameControllerFromInstanceID( + event.cdevice.which); + if (gc) + { + SDL_GameControllerClose(gc); + } + } + + delete m_joysticks[event.jdevice.which]; + m_joysticks.remove(event.jdevice.which); + + emit countChanged(); + break; + case SDL_JOYAXISMOTION: + emit axisEvent(getAxisEvent(&event)); + break; + case SDL_CONTROLLERAXISMOTION: + emit axisEvent(getAxisEvent(&event)); + break; + case SDL_JOYBUTTONUP: + emit buttonEvent(getButtonEvent(&event)); + break; + case SDL_JOYBUTTONDOWN: + emit buttonEvent(getButtonEvent(&event)); + break; + case SDL_JOYHATMOTION: + emit POVEvent(getPOVEvent(&event)); + break; + } + } + + QTimer::singleShot(10, Qt::PreciseTimer, this, SLOT(update())); +#endif +} + +void SDL_Joysticks::configureJoystick(const SDL_Event *event) +{ + configureJoystick(event->jdevice.which); +} + +void SDL_Joysticks::configureJoystick(int id) +{ +#ifdef SDL_SUPPORTED + QJoystickDevice *joystick = getJoystick(id); + + if (!SDL_IsGameController(id)) + { + SDL_Joystick *js = SDL_JoystickFromInstanceID(joystick->instanceID); + if (js) + { + char guid[1024]; + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(js), guid, + sizeof(guid)); + } + } + + SDL_GameControllerOpen(id); + + emit countChanged(); +#else + Q_UNUSED(event); +#endif +} + +/** + * Returns the josytick device registered with the given \a id. + * If no joystick with the given \a id is found, then the function will warn + * the user through the console. + */ +QJoystickDevice *SDL_Joysticks::getJoystick(int id) +{ +#ifdef SDL_SUPPORTED + QJoystickDevice *joystick = new QJoystickDevice; + SDL_Joystick *sdl_joystick = SDL_JoystickOpen(id); + + if (sdl_joystick) + { + joystick->id = id; + joystick->instanceID = SDL_JoystickInstanceID(sdl_joystick); + joystick->blacklisted = false; + joystick->name = SDL_JoystickName(sdl_joystick); + + /* Get joystick properties */ + int povs = SDL_JoystickNumHats(sdl_joystick); + int axes = SDL_JoystickNumAxes(sdl_joystick); + int buttons = SDL_JoystickNumButtons(sdl_joystick); + + /* Initialize POVs */ + for (int i = 0; i < povs; ++i) + joystick->povs.append(0); + + /* Initialize axes */ + for (int i = 0; i < axes; ++i) + joystick->axes.append(0); + + /* Initialize buttons */ + for (int i = 0; i < buttons; ++i) + joystick->buttons.append(false); + + m_joysticks[joystick->instanceID] = joystick; + } + else + { + qWarning() << Q_FUNC_INFO << "Cannot find joystick with id:" << id; + } + + return joystick; +#else + Q_UNUSED(id); + return NULL; +#endif +} + +/** + * Reads the contents of the given \a event and constructs a new + * \c QJoystickPOVEvent to be used with the \c QJoysticks system. + */ +QJoystickPOVEvent SDL_Joysticks::getPOVEvent(const SDL_Event + *sdl_event) +{ + QJoystickPOVEvent event; + + if (!m_joysticks.contains(sdl_event->jdevice.which)) + { + return event; + } + +#ifdef SDL_SUPPORTED + event.pov = sdl_event->jhat.hat; + event.joystick = m_joysticks[sdl_event->jdevice.which]; + + switch (sdl_event->jhat.value) + { + case SDL_HAT_RIGHTUP: + event.angle = 45; + break; + case SDL_HAT_RIGHTDOWN: + event.angle = 135; + break; + case SDL_HAT_LEFTDOWN: + event.angle = 225; + break; + case SDL_HAT_LEFTUP: + event.angle = 315; + break; + case SDL_HAT_UP: + event.angle = 0; + break; + case SDL_HAT_RIGHT: + event.angle = 90; + break; + case SDL_HAT_DOWN: + event.angle = 180; + break; + case SDL_HAT_LEFT: + event.angle = 270; + break; + default: + event.angle = -1; + break; + } +#else + Q_UNUSED(sdl_event); +#endif + + return event; +} + +/** + * Reads the contents of the given \a event and constructs a new + * \c QJoystickAxisEvent to be used with the \c QJoysticks system. + */ +QJoystickAxisEvent SDL_Joysticks::getAxisEvent(const SDL_Event + *sdl_event) +{ + QJoystickAxisEvent event; + + if (!m_joysticks.contains(sdl_event->cdevice.which)) + { + return event; + } + +#ifdef SDL_SUPPORTED + event.axis = sdl_event->caxis.axis; + event.value = static_cast(sdl_event->caxis.value) / 32767; + event.joystick = m_joysticks[sdl_event->cdevice.which]; +#else + Q_UNUSED(sdl_event); +#endif + + return event; +} + +/** + * Reads the contents of the given \a event and constructs a new + * \c QJoystickButtonEvent to be used with the \c QJoysticks system. + */ +QJoystickButtonEvent SDL_Joysticks::getButtonEvent( + const SDL_Event *sdl_event) +{ + QJoystickButtonEvent event; + + if (!m_joysticks.contains(sdl_event->jdevice.which)) + { + return event; + } + +#ifdef SDL_SUPPORTED + event.button = sdl_event->jbutton.button; + event.pressed = sdl_event->jbutton.state == SDL_PRESSED; + event.joystick = m_joysticks[sdl_event->jdevice.which]; + event.joystick->buttons[event.button] = event.pressed; +#else + Q_UNUSED(sdl_event); +#endif + + return event; +} diff --git a/src/jsbackend/SDL_Joysticks.h b/src/jsbackend/SDL_Joysticks.h new file mode 100644 index 0000000..4ff91aa --- /dev/null +++ b/src/jsbackend/SDL_Joysticks.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _QJOYSTICKS_SDL_JOYSTICK_H +#define _QJOYSTICKS_SDL_JOYSTICK_H + +#include +#include +#include +#include "JoysticksCommon.h" + +/** + * \brief Translates SDL events into \c QJoysticks events + * + * This class is in charge of managing and operating real joysticks through the + * SDL API. The implementation procedure is the same for every operating system. + * + * The only thing that differs from each operating system is the backup mapping + * applied in the case that we do not know what mapping to apply to a joystick. + * + * \note The joystick values are refreshed every 20 milliseconds through a + * simple event loop. + */ +class SDL_Joysticks : public QObject +{ + Q_OBJECT + +signals: + void countChanged(); + void POVEvent(const QJoystickPOVEvent &event); + void axisEvent(const QJoystickAxisEvent &event); + void buttonEvent(const QJoystickButtonEvent &event); + +public: + SDL_Joysticks(QObject *parent = Q_NULLPTR); + ~SDL_Joysticks(); + + QMap joysticks(); + +public slots: + void rumble(const QJoystickRumble &request); + +private slots: + void update(); + void configureJoystick(const SDL_Event *event); + void configureJoystick(int id); + +private: + QJoystickDevice *getJoystick(int id); + QJoystickPOVEvent getPOVEvent(const SDL_Event *sdl_event); + QJoystickAxisEvent getAxisEvent(const SDL_Event *sdl_event); + QJoystickButtonEvent getButtonEvent(const SDL_Event *sdl_event); + + QMap m_joysticks; +}; + +#endif diff --git a/src/jsbackend/SDL_Joysticks.h.orig b/src/jsbackend/SDL_Joysticks.h.orig new file mode 100644 index 0000000..43afb42 --- /dev/null +++ b/src/jsbackend/SDL_Joysticks.h.orig @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _QJOYSTICKS_SDL_JOYSTICK_H +#define _QJOYSTICKS_SDL_JOYSTICK_H + +#include +#include +#include +#include "JoysticksCommon.h" + +/** + * \brief Translates SDL events into \c QJoysticks events + * + * This class is in charge of managing and operating real joysticks through the + * SDL API. The implementation procedure is the same for every operating system. + * + * The only thing that differs from each operating system is the backup mapping + * applied in the case that we do not know what mapping to apply to a joystick. + * + * \note The joystick values are refreshed every 20 milliseconds through a + * simple event loop. + */ +class SDL_Joysticks : public QObject +{ + Q_OBJECT + +signals: + void countChanged(); + void POVEvent(const QJoystickPOVEvent &event); + void axisEvent(const QJoystickAxisEvent &event); + void buttonEvent(const QJoystickButtonEvent &event); + +public: + SDL_Joysticks(QObject *parent = Q_NULLPTR); + ~SDL_Joysticks(); + + QMap joysticks(); + +public slots: + void rumble(const QJoystickRumble &request); + +private slots: + void update(); + void configureJoystick(const SDL_Event *event); + void configureJoystick(int id); + +private: + QJoystickDevice *getJoystick(int id); + QJoystickPOVEvent getPOVEvent(const SDL_Event *sdl_event); + QJoystickAxisEvent getAxisEvent(const SDL_Event *sdl_event); + QJoystickButtonEvent getButtonEvent(const SDL_Event *sdl_event); + + QMap m_joysticks; +}; + +#endif diff --git a/src/jsbackend/VirtualJoystick.cpp b/src/jsbackend/VirtualJoystick.cpp new file mode 100644 index 0000000..03d42be --- /dev/null +++ b/src/jsbackend/VirtualJoystick.cpp @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "VirtualJoystick.h" + +VirtualJoystick::VirtualJoystick(QObject *parent) + : QObject(parent) +{ + m_axisRange = 1; + m_joystickEnabled = false; + m_joystick.blacklisted = false; + m_joystick.name = tr("Virtual Joystick"); + + /* Initialize POVs */ + m_joystick.povs.append(0); + + /* Initialize axes */ + m_axisStatus = QVector(NUMBER_OF_AXES, AxisState::STILL); + m_axisValue = QVector(NUMBER_OF_AXES, 0); + for (int i = 0; i < NUMBER_OF_AXES; ++i) + { + m_joystick.axes.append(0); + } + + /* Initialize buttons */ + for (int i = 0; i < NUMBER_OF_BUTTONS; ++i) + m_joystick.buttons.append(false); + + m_timerUpdateAxis.reset(new QTimer()); + connect(m_timerUpdateAxis.get(), &QTimer::timeout, this, &VirtualJoystick::updateAxis); +} + +/** + * Returns the current axis range of the joystick. + * The axis range is an absolute value that represents the maximum value that + * the joystick can have. + * + * For example, the \c QJoystick system supports an axis range of 1 (-1 to 1). + * If you set an axis range of 0.8 to the virtual joystick, then it will report + * values ranging from -0.8 to 0.8. + */ +qreal VirtualJoystick::axisRange() const +{ + return m_axisRange; +} + +/** + * Returns \c true if the virtual joystick is enabled. + */ +bool VirtualJoystick::joystickEnabled() const +{ + return m_joystickEnabled; +} + +/** + * Returns a pointer to the virtual josytick device. This can be used if you + * need to customize the virtual joystick (e.g. add new axes or buttons). + */ +QJoystickDevice *VirtualJoystick::joystick() +{ + return &m_joystick; +} + +/** + * Sets the ID of the virtual joystick device. The \c QJoysticks will + * automatically change the \a ID of the virtual joystick when it scans for + * new joysticks. + * + * The virtual joystick will ALWAYS be the last joystick to be registered. + */ +void VirtualJoystick::setJoystickID(int id) +{ + m_joystick.id = id; +} + +/** + * Changes the axis range that the joystick can use. For example, if you set + * an axis range of 0.8, then axis values will be reported from -0.8 to 0.8. + * + * If you set an axis range of 1 (maximum), then the joystick will report axis + * values ranging from -1 to 1. + */ +void VirtualJoystick::setAxisRange(qreal range) +{ + range = fabs(range); + + if (range > 1) + range = 1; + + m_axisRange = range; +} + +/** + * Enables or disables the virtual joystick device. + */ +void VirtualJoystick::setJoystickEnabled(bool enabled) +{ + if (enabled) + { + // That means that the axes will be updated each 10 ms + m_timerUpdateAxis->start(10); + qApp->installEventFilter(this); + } + + else + { + m_timerUpdateAxis->stop(); + // Removing the event filter since the joystick is no longer active + qApp->removeEventFilter(this); + } + + m_joystickEnabled = enabled; + emit enabledChanged(); +} + +void VirtualJoystick::setAxisSensibility(qreal sensibility) +{ + if (sensibility > 1 || sensibility < 0) + { + qFatal("Fatal: VirtualJoystick Axis sensibility must be a value between 0 and 1"); + } + + const qint16 stepMinimum = 50; + const qint16 stepMaximum = 1000; + + m_axisStep = static_cast((-stepMaximum + stepMinimum) * sensibility) + stepMaximum; +} + +/** + * Polls the keyboard events and if required, reports a change in the axis + * values of the virtual joystick device. + */ +void VirtualJoystick::readAxes(int key, bool pressed) +{ + + /* Horizontal axis on thumb 1 */ + if (key == Qt::Key_A) + { + if (pressed) + m_axisStatus[AXIS_AD] = DECREASE; + else + m_axisStatus[AXIS_AD] = STILL; + } + else if (key == Qt::Key_D) + { + if (pressed) + m_axisStatus[AXIS_AD] = INCREASE; + else + m_axisStatus[AXIS_AD] = STILL; + } + + /* Vertical axis on thumb 1 */ + if (key == Qt::Key_S) + { + if (pressed) + m_axisStatus[AXIS_SW] = DECREASE; + else + m_axisStatus[AXIS_SW] = STILL; + } + else if (key == Qt::Key_W) + { + if (pressed) + m_axisStatus[AXIS_SW] = INCREASE; + else + m_axisStatus[AXIS_SW] = STILL; + } + + /* Trigger 1 */ + if (key == Qt::Key_Q) + { + if (pressed) + m_axisStatus[AXIS_QE] = DECREASE; + else + m_axisStatus[AXIS_QE] = STILL; + } + else if (key == Qt::Key_E) + { + if (pressed) + m_axisStatus[AXIS_QE] = INCREASE; + else + m_axisStatus[AXIS_QE] = STILL; + } + + /* Trigger 2 */ + if (key == Qt::Key_U) + { + if (pressed) + m_axisStatus[AXIS_UO] = DECREASE; + else + m_axisStatus[AXIS_UO] = STILL; + } + else if (key == Qt::Key_O) + { + if (pressed) + m_axisStatus[AXIS_UO] = INCREASE; + else + m_axisStatus[AXIS_UO] = STILL; + } + + /* Horizontal axis on thumb 2 */ + if (key == Qt::Key_J) + { + if (pressed) + m_axisStatus[AXIS_JL] = DECREASE; + else + m_axisStatus[AXIS_JL] = STILL; + } + else if (key == Qt::Key_L) + { + if (pressed) + m_axisStatus[AXIS_JL] = INCREASE; + else + m_axisStatus[AXIS_JL] = STILL; + } + + /* Vertical axis on thumb 2 */ + if (key == Qt::Key_K) + { + if (pressed) + m_axisStatus[AXIS_KI] = DECREASE; + else + m_axisStatus[AXIS_KI] = STILL; + } + else if (key == Qt::Key_I) + { + if (pressed) + m_axisStatus[AXIS_KI] = INCREASE; + else + m_axisStatus[AXIS_KI] = STILL; + } +} + +void VirtualJoystick::updateAxis() +{ + for (quint8 i = 0; i < NUMBER_OF_AXES; i++) + { + changeAxisValue(i); + } +} + +/** + * Polls the keyboard events and if required, reports a change in the POV/hat + * values of the virtual joystick device. + */ +void VirtualJoystick::readPOVs(int key, bool pressed) +{ + int angle = 0; + + if (key == Qt::Key_Up) + angle = 360; + else if (key == Qt::Key_Right) + angle = 90; + else if (key == Qt::Key_Left) + angle = 270; + else if (key == Qt::Key_Down) + angle = 180; + + if (!pressed) + angle = 0; + + if (joystickEnabled()) + { + QJoystickPOVEvent event; + event.pov = 0; + event.angle = angle; + event.joystick = joystick(); + + emit povEvent(event); + } +} + +/** + * Polls the keyboard events and if required, reports a change in the button + * values of the virtual joystick device. + */ +void VirtualJoystick::readButtons(int key, bool pressed) +{ + int button = -1; + + if (key == Qt::Key_0) + { + // Special key that reset all axes + button = 0; + resetAllAxes(); + } + else if (key == Qt::Key_1) + button = 1; + else if (key == Qt::Key_2) + button = 2; + else if (key == Qt::Key_3) + button = 3; + else if (key == Qt::Key_4) + button = 4; + else if (key == Qt::Key_5) + button = 5; + else if (key == Qt::Key_6) + button = 6; + else if (key == Qt::Key_7) + button = 7; + else if (key == Qt::Key_8) + button = 8; + else if (key == Qt::Key_9) + button = 9; + + if (button != -1 && joystickEnabled()) + { + QJoystickButtonEvent event; + event.button = button; + event.pressed = pressed; + event.joystick = joystick(); + + emit buttonEvent(event); + } +} + +/** + * Called when the event filter detects a keyboard event. + * + * This function prompts the joystick to update its axis, button and POV values + * based on the keys that have been pressed or released. + */ +void VirtualJoystick::processKeyEvent(QKeyEvent *event, bool pressed) +{ + if (joystickEnabled()) + { + readPOVs(event->key(), pressed); + readAxes(event->key(), pressed); + readButtons(event->key(), pressed); + } +} + +/** + * "Listens" for keyboard presses or releases while any window or widget of the + * application is focused. + * + * \note This function may or may not detect keyboard events when the + * application is not focused. This depends on the operating system and + * the window manager that is being used. + */ +bool VirtualJoystick::eventFilter(QObject *object, QEvent *event) +{ + Q_UNUSED(object); + + switch (event->type()) + { + case QEvent::KeyPress: + processKeyEvent(static_cast(event), true); + break; + case QEvent::KeyRelease: + processKeyEvent(static_cast(event), false); + break; + default: + break; + } + + return false; +} + +void VirtualJoystick::changeAxisValue(quint8 axis) +{ + switch (m_axisStatus[axis]) + { + case STILL: + return; + + case INCREASE: + if (m_axisValue[axis] > AXIS_MAXIMUM_VIRTUAL_JOYSTICK - m_axisStep) + m_axisValue[axis] = AXIS_MAXIMUM_VIRTUAL_JOYSTICK; + else + m_axisValue[axis] += m_axisStep; + break; + + case DECREASE: + if (m_axisValue[axis] < AXIS_MINIMUM_VIRTUAL_JOYSTICK + m_axisStep) + m_axisValue[axis] = AXIS_MINIMUM_VIRTUAL_JOYSTICK; + else + m_axisValue[axis] -= m_axisStep; + break; + } + + QJoystickAxisEvent event; + + event.axis = axis; + event.value = m_axisRange * static_cast(m_axisValue[axis]) / AXIS_MAXIMUM_VIRTUAL_JOYSTICK; + event.joystick = joystick(); + + emit axisEvent(event); +} + +void VirtualJoystick::resetAllAxes() +{ + for (quint8 i = 0; i < NUMBER_OF_AXES; i++) + { + m_axisValue[i] = 0; + + QJoystickAxisEvent event; + + event.axis = i; + event.value = 0; + event.joystick = joystick(); + + emit axisEvent(event); + } +} diff --git a/src/jsbackend/VirtualJoystick.cpp.orig b/src/jsbackend/VirtualJoystick.cpp.orig new file mode 100644 index 0000000..44f0e48 --- /dev/null +++ b/src/jsbackend/VirtualJoystick.cpp.orig @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "VirtualJoystick.h" + +VirtualJoystick::VirtualJoystick(QObject *parent) + : QObject(parent) +{ + m_axisRange = 1; + m_joystickEnabled = false; + m_joystick.blacklisted = false; + m_joystick.name = tr("Virtual Joystick"); + + /* Initialize POVs */ + m_joystick.povs.append(0); + + /* Initialize axes */ + m_axisStatus = QVector(NUMBER_OF_AXES, AxisState::STILL); + m_axisValue = QVector(NUMBER_OF_AXES, 0); + for (int i = 0; i < NUMBER_OF_AXES; ++i) + { + m_joystick.axes.append(0); + } + + /* Initialize buttons */ + for (int i = 0; i < NUMBER_OF_BUTTONS; ++i) + m_joystick.buttons.append(false); + + m_timerUpdateAxis.reset(new QTimer()); + connect(m_timerUpdateAxis.get(), &QTimer::timeout, this, &VirtualJoystick::updateAxis); +} + +/** + * Returns the current axis range of the joystick. + * The axis range is an absolute value that represents the maximum value that + * the joystick can have. + * + * For example, the \c QJoystick system supports an axis range of 1 (-1 to 1). + * If you set an axis range of 0.8 to the virtual joystick, then it will report + * values ranging from -0.8 to 0.8. + */ +qreal VirtualJoystick::axisRange() const +{ + return m_axisRange; +} + +/** + * Returns \c true if the virtual joystick is enabled. + */ +bool VirtualJoystick::joystickEnabled() const +{ + return m_joystickEnabled; +} + +/** + * Returns a pointer to the virtual josytick device. This can be used if you + * need to customize the virtual joystick (e.g. add new axes or buttons). + */ +QJoystickDevice *VirtualJoystick::joystick() +{ + return &m_joystick; +} + +/** + * Sets the ID of the virtual joystick device. The \c QJoysticks will + * automatically change the \a ID of the virtual joystick when it scans for + * new joysticks. + * + * The virtual joystick will ALWAYS be the last joystick to be registered. + */ +void VirtualJoystick::setJoystickID(int id) +{ + m_joystick.id = id; +} + +/** + * Changes the axis range that the joystick can use. For example, if you set + * an axis range of 0.8, then axis values will be reported from -0.8 to 0.8. + * + * If you set an axis range of 1 (maximum), then the joystick will report axis + * values ranging from -1 to 1. + */ +void VirtualJoystick::setAxisRange(qreal range) +{ + range = fabs(range); + + if (range > 1) + range = 1; + + m_axisRange = range; +} + +/** + * Enables or disables the virtual joystick device. + */ +void VirtualJoystick::setJoystickEnabled(bool enabled) +{ + if (enabled) + { + // That means that the axes will be updated each 10 ms + m_timerUpdateAxis->start(10); + qApp->installEventFilter(this); + } + + else + { + m_timerUpdateAxis->stop(); + // Removing the event filter since the joystick is no longer active + qApp->removeEventFilter(this); + } + + m_joystickEnabled = enabled; + emit enabledChanged(); +} + +void VirtualJoystick::setAxisSensibility(qreal sensibility) +{ + if (sensibility > 1 || sensibility < 0) + { + qFatal("Fatal: VirtualJoystick Axis sensibility must be a value between 0 and 1"); + } + + const qint16 stepMinimum = 50; + const qint16 stepMaximum = 1000; + + m_axisStep = static_cast((-stepMaximum + stepMinimum) * sensibility) + stepMaximum; +} + +/** + * Polls the keyboard events and if required, reports a change in the axis + * values of the virtual joystick device. + */ +void VirtualJoystick::readAxes(int key, bool pressed) +{ + + /* Horizontal axis on thumb 1 */ + if (key == Qt::Key_A) + { + if (pressed) + m_axisStatus[AXIS_AD] = DECREASE; + else + m_axisStatus[AXIS_AD] = STILL; + } + else if (key == Qt::Key_D) + { + if (pressed) + m_axisStatus[AXIS_AD] = INCREASE; + else + m_axisStatus[AXIS_AD] = STILL; + } + + /* Vertical axis on thumb 1 */ + if (key == Qt::Key_S) + { + if (pressed) + m_axisStatus[AXIS_SW] = DECREASE; + else + m_axisStatus[AXIS_SW] = STILL; + } + else if (key == Qt::Key_W) + { + if (pressed) + m_axisStatus[AXIS_SW] = INCREASE; + else + m_axisStatus[AXIS_SW] = STILL; + } + + /* Trigger 1 */ + if (key == Qt::Key_Q) + { + if (pressed) + m_axisStatus[AXIS_QE] = DECREASE; + else + m_axisStatus[AXIS_QE] = STILL; + } + else if (key == Qt::Key_E) + { + if (pressed) + m_axisStatus[AXIS_QE] = INCREASE; + else + m_axisStatus[AXIS_QE] = STILL; + } + + /* Trigger 2 */ + if (key == Qt::Key_U) + { + if (pressed) + m_axisStatus[AXIS_UO] = DECREASE; + else + m_axisStatus[AXIS_UO] = STILL; + } + else if (key == Qt::Key_O) + { + if (pressed) + m_axisStatus[AXIS_UO] = INCREASE; + else + m_axisStatus[AXIS_UO] = STILL; + } + + /* Horizontal axis on thumb 2 */ + if (key == Qt::Key_J) + { + if (pressed) + m_axisStatus[AXIS_JL] = DECREASE; + else + m_axisStatus[AXIS_JL] = STILL; + } + else if (key == Qt::Key_L) + { + if (pressed) + m_axisStatus[AXIS_JL] = INCREASE; + else + m_axisStatus[AXIS_JL] = STILL; + } + + /* Vertical axis on thumb 2 */ + if (key == Qt::Key_K) + { + if (pressed) + m_axisStatus[AXIS_KI] = DECREASE; + else + m_axisStatus[AXIS_KI] = STILL; + } + else if (key == Qt::Key_I) + { + if (pressed) + m_axisStatus[AXIS_KI] = INCREASE; + else + m_axisStatus[AXIS_KI] = STILL; + } +} + +void VirtualJoystick::updateAxis() +{ + for (quint8 i = 0; i < NUMBER_OF_AXES; i++) + { + changeAxisValue(i); + } +} + +/** + * Polls the keyboard events and if required, reports a change in the POV/hat + * values of the virtual joystick device. + */ +void VirtualJoystick::readPOVs(int key, bool pressed) +{ + int angle = 0; + + if (key == Qt::Key_Up) + angle = 360; + else if (key == Qt::Key_Right) + angle = 90; + else if (key == Qt::Key_Left) + angle = 270; + else if (key == Qt::Key_Down) + angle = 180; + + if (!pressed) + angle = 0; + + if (joystickEnabled()) + { + QJoystickPOVEvent event; + event.pov = 0; + event.angle = angle; + event.joystick = joystick(); + + emit povEvent(event); + } +} + +/** + * Polls the keyboard events and if required, reports a change in the button + * values of the virtual joystick device. + */ +void VirtualJoystick::readButtons(int key, bool pressed) +{ + int button = -1; + + if (key == Qt::Key_0) + { // Special key that reset all axes + button = 0; + resetAllAxes(); + } + else if (key == Qt::Key_1) + button = 1; + else if (key == Qt::Key_2) + button = 2; + else if (key == Qt::Key_3) + button = 3; + else if (key == Qt::Key_4) + button = 4; + else if (key == Qt::Key_5) + button = 5; + else if (key == Qt::Key_6) + button = 6; + else if (key == Qt::Key_7) + button = 7; + else if (key == Qt::Key_8) + button = 8; + else if (key == Qt::Key_9) + button = 9; + + if (button != -1 && joystickEnabled()) + { + QJoystickButtonEvent event; + event.button = button; + event.pressed = pressed; + event.joystick = joystick(); + + emit buttonEvent(event); + } +} + +/** + * Called when the event filter detects a keyboard event. + * + * This function prompts the joystick to update its axis, button and POV values + * based on the keys that have been pressed or released. + */ +void VirtualJoystick::processKeyEvent(QKeyEvent *event, bool pressed) +{ + if (joystickEnabled()) + { + readPOVs(event->key(), pressed); + readAxes(event->key(), pressed); + readButtons(event->key(), pressed); + } +} + +/** + * "Listens" for keyboard presses or releases while any window or widget of the + * application is focused. + * + * \note This function may or may not detect keyboard events when the + * application is not focused. This depends on the operating system and + * the window manager that is being used. + */ +bool VirtualJoystick::eventFilter(QObject *object, QEvent *event) +{ + Q_UNUSED(object); + + switch (event->type()) + { + case QEvent::KeyPress: + processKeyEvent(static_cast(event), true); + break; + case QEvent::KeyRelease: + processKeyEvent(static_cast(event), false); + break; + default: + break; + } + + return false; +} + +void VirtualJoystick::changeAxisValue(quint8 axis) +{ + switch (m_axisStatus[axis]) + { + case STILL: + return; + + case INCREASE: + if (m_axisValue[axis] > AXIS_MAXIMUM_VIRTUAL_JOYSTICK - m_axisStep) + m_axisValue[axis] = AXIS_MAXIMUM_VIRTUAL_JOYSTICK; + else + m_axisValue[axis] += m_axisStep; + break; + + case DECREASE: + if (m_axisValue[axis] < AXIS_MINIMUM_VIRTUAL_JOYSTICK + m_axisStep) + m_axisValue[axis] = AXIS_MINIMUM_VIRTUAL_JOYSTICK; + else + m_axisValue[axis] -= m_axisStep; + break; + } + + QJoystickAxisEvent event; + + event.axis = axis; + event.value = m_axisRange * static_cast(m_axisValue[axis]) / AXIS_MAXIMUM_VIRTUAL_JOYSTICK; + event.joystick = joystick(); + + emit axisEvent(event); +} + +void VirtualJoystick::resetAllAxes() +{ + for (quint8 i = 0; i < NUMBER_OF_AXES; i++) + { + m_axisValue[i] = 0; + + QJoystickAxisEvent event; + + event.axis = i; + event.value = 0; + event.joystick = joystick(); + + emit axisEvent(event); + } +} diff --git a/src/jsbackend/VirtualJoystick.h b/src/jsbackend/VirtualJoystick.h new file mode 100644 index 0000000..5596bae --- /dev/null +++ b/src/jsbackend/VirtualJoystick.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _QJOYSTICKS_JOYSTICK_H +#define _QJOYSTICKS_JOYSTICK_H + +#include +#include +#include +#include +#include + +#include "JoysticksCommon.h" + +#define AXIS_AD 0 +#define AXIS_SW 1 +#define AXIS_QE 2 +#define AXIS_UO 3 +#define AXIS_JL 4 +#define AXIS_KI 5 + +enum AxisState : qint8 +{ + + INCREASE = -1, + STILL = 0, + DECREASE = 1 + +}; + +const qint16 AXIS_MINIMUM_VIRTUAL_JOYSTICK { -32767 }; +const qint16 AXIS_MAXIMUM_VIRTUAL_JOYSTICK { 32767 }; + +const int NUMBER_OF_AXES { 6 }; +const int NUMBER_OF_BUTTONS { 10 }; + +/** + * \brief Translates keyboard input to joystick input + * + * This class implements a virtual joystick device that uses the computer's + * keyboard as means to get the axis, button and POV values of the joystick. + */ +class VirtualJoystick : public QObject +{ + Q_OBJECT + +signals: + void enabledChanged(); + void povEvent(const QJoystickPOVEvent &event); + void axisEvent(const QJoystickAxisEvent &event); + void buttonEvent(const QJoystickButtonEvent &event); + +public: + VirtualJoystick(QObject *parent = Q_NULLPTR); + + qreal axisRange() const; + bool joystickEnabled() const; + QJoystickDevice *joystick(); + +public slots: + void setJoystickID(int id); + void setAxisRange(qreal range); + void setJoystickEnabled(bool enabled); + + void setAxisSensibility(qreal sensibility); + +private slots: + void readAxes(int key, bool pressed); + void updateAxis(); + void readPOVs(int key, bool pressed); + void readButtons(int key, bool pressed); + void processKeyEvent(QKeyEvent *event, bool pressed); + +protected: + bool eventFilter(QObject *object, QEvent *event); + +private: + qreal m_axisRange; + bool m_joystickEnabled; + QJoystickDevice m_joystick; + + qint16 m_axisStep; + + QVector m_axisStatus; + QVector m_axisValue; + + QScopedPointer m_timerUpdateAxis; + + void changeAxisValue(quint8 axis); + void resetAllAxes(); +}; + +#endif diff --git a/src/jsbackend/VirtualJoystick.h.orig b/src/jsbackend/VirtualJoystick.h.orig new file mode 100644 index 0000000..581fddc --- /dev/null +++ b/src/jsbackend/VirtualJoystick.h.orig @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015-2017 Alex Spataru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _QJOYSTICKS_JOYSTICK_H +#define _QJOYSTICKS_JOYSTICK_H + +#include +#include +#include +#include +#include + +#include "JoysticksCommon.h" + +#define AXIS_AD 0 +#define AXIS_SW 1 +#define AXIS_QE 2 +#define AXIS_UO 3 +#define AXIS_JL 4 +#define AXIS_KI 5 + +enum AxisState : qint8 +{ + + INCREASE = -1, + STILL = 0, + DECREASE = 1 + +}; + +const qint16 AXIS_MINIMUM_VIRTUAL_JOYSTICK { -32767 }; +const qint16 AXIS_MAXIMUM_VIRTUAL_JOYSTICK { 32767 }; + +const int NUMBER_OF_AXES { 6 }; +const int NUMBER_OF_BUTTONS { 10 }; + +/** + * \brief Translates keyboard input to joystick input + * + * This class implements a virtual joystick device that uses the computer's + * keyboard as means to get the axis, button and POV values of the joystick. + */ +class VirtualJoystick : public QObject +{ + Q_OBJECT + +signals: + void enabledChanged(); + void povEvent(const QJoystickPOVEvent &event); + void axisEvent(const QJoystickAxisEvent &event); + void buttonEvent(const QJoystickButtonEvent &event); + +public: + VirtualJoystick(QObject *parent = Q_NULLPTR); + + qreal axisRange() const; + bool joystickEnabled() const; + QJoystickDevice *joystick(); + +public slots: + void setJoystickID(int id); + void setAxisRange(qreal range); + void setJoystickEnabled(bool enabled); + + void setAxisSensibility(qreal sensibility); + +private slots: + void readAxes(int key, bool pressed); + void updateAxis(); + void readPOVs(int key, bool pressed); + void readButtons(int key, bool pressed); + void processKeyEvent(QKeyEvent *event, bool pressed); + +protected: + bool eventFilter(QObject *object, QEvent *event); + +private: + qreal m_axisRange; + bool m_joystickEnabled; + QJoystickDevice m_joystick; + + qint16 m_axisStep; + + QVector m_axisStatus; + QVector m_axisValue; + + QScopedPointer m_timerUpdateAxis; + + void changeAxisValue(quint8 axis); + void resetAllAxes(); +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index 1954520..17392c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include #include "microcontroller.h" +#include "trainjs.h" #include "ui/mainwindow.h" #include "items/itemstore.h" #include "items/train.h" @@ -86,6 +87,8 @@ int main(int argc, char *argv[]) Microcontroller micro(masterIODevice); micro.setPower(true); + TrainJs::init(); + Train::micro = µ Turnout::micro = µ ItemStore items; diff --git a/src/microcontroller.cpp b/src/microcontroller.cpp index 1ef28fd..450bd02 100644 --- a/src/microcontroller.cpp +++ b/src/microcontroller.cpp @@ -50,9 +50,9 @@ void Microcontroller::write(const QByteArray& buffer) if(_port != nullptr) { _port->write(buffer); - _port->waitForBytesWritten(1000); + _port->waitForBytesWritten(1000); } - std::this_thread::sleep_for(std::chrono::milliseconds(40)); + std::this_thread::sleep_for(std::chrono::milliseconds(40)); } void Microcontroller::write(char* buffer, const size_t length) @@ -61,9 +61,9 @@ void Microcontroller::write(char* buffer, const size_t length) if(_port != nullptr) { _port->write(buffer, length); - _port->waitForBytesWritten(1000); + //_port->waitForBytesWritten(1000); } - std::this_thread::sleep_for(std::chrono::milliseconds(40)); + std::this_thread::sleep_for(std::chrono::milliseconds(40)); } bool Microcontroller::connected() @@ -103,20 +103,19 @@ void Microcontroller::setIODevice(QIODevice *port) std::shared_ptr Microcontroller::processTrainLine(const QString& buffer) { QStringList bufferList = buffer.split(' '); - if(bufferList.size() >= 13 && buffer.startsWith("NUMBER:")) + if(bufferList.size() >= 14 && buffer.startsWith("TRAIN NUMBER:")) { - return std::shared_ptr(new Train(bufferList[1].toInt(), bufferList[3].toInt(), bufferList[12].toInt())); + return std::shared_ptr(new Train(bufferList[2].toInt(), bufferList[4].toInt(), bufferList[13].toInt())); } return nullptr; } std::shared_ptr Microcontroller::processTurnoutLine(const QString& buffer) { - qDebug()<<__func__<<" :"<= 6 && buffer.startsWith("NUMBER:")) + if(bufferList.size() >= 7 && buffer.startsWith("TURNOUT NUMBER:")) { - return std::shared_ptr(new Turnout(bufferList[1].toInt(), bufferList[1].toInt(), bufferList[5].toInt())); + return std::shared_ptr(new Turnout(bufferList[2].toInt(), bufferList[2].toInt(), bufferList[6].toInt())); } return nullptr; } @@ -125,12 +124,15 @@ void Microcontroller::processList(const QString& buffer) { QStringList bufferList = buffer.split(' '); qDebug()<<__func__<<" :"<= 10 && buffer.startsWith("NUMBER:")) + if(bufferList.size() >= 10 && buffer.contains("NUMBER:")) { + std::shared_ptr item; if(listMode == TRAIN_LIST) - itemList.push_back(processTrainLine(buffer)); + item = processTrainLine(buffer); else if(listMode == TURNOUT_LIST) - itemList.push_back(processTurnoutLine(buffer)); + item = processTurnoutLine(buffer); + if(item) + itemList.push_back(item); } else { @@ -146,7 +148,10 @@ void Microcontroller::processList(const QString& buffer) void Microcontroller::processItemState(const QString& buffer) { - itemChanged(static_cast(*processTrainLine(buffer))); + if(_buffer.startsWith("TRAIN NUMBER:")) + itemChanged(static_cast(*processTrainLine(buffer))); + else if(_buffer.startsWith("TRAIN NUMBER:")) + itemChanged(static_cast(*processTurnoutLine(buffer))); } @@ -165,10 +170,10 @@ void Microcontroller::processMicroReturn() { listMode = TURNOUT_LIST; } - else if(_buffer.startsWith("NUMBER:")) + else if(_buffer.startsWith("TRAIN NUMBER:") || _buffer.startsWith("TRAIN NUMBER:")) { processItemState(_buffer); - } + } else if(_buffer.startsWith("TrainController")) { requestState(); diff --git a/src/trainjs.cpp b/src/trainjs.cpp new file mode 100644 index 0000000..3e69d36 --- /dev/null +++ b/src/trainjs.cpp @@ -0,0 +1,116 @@ +#include "trainjs.h" +#include + +#include "items/train.h" + +TrainJs::TrainJs(int id, int axis): id_(id), axis_(axis) +{ + longpressTimer_.setSingleShot(true); + longpressTimer_.setInterval(LONGPRESS_TIME); + + QJoysticks* jsmanager = QJoysticks::getInstance(); + connect(jsmanager, &QJoysticks::axisChanged, this, &TrainJs::axisChanged); + connect(jsmanager, &QJoysticks::buttonChanged, this, &TrainJs::buttonChanged); + connect(&longpressTimer_, &QTimer::timeout, this, &TrainJs::longpressTimeout); +} + +TrainJs::~TrainJs() +{ +} + +void TrainJs::init() +{ + QJoysticks* jsmanager = QJoysticks::getInstance(); + jsmanager->updateInterfaces(); + const QList joysticks = jsmanager->inputDevices(); + + qDebug()<<"loading joysticks number attached: "<count(); + + for(int id = 0; id < joysticks.count(); ++id) + { + QJoystickDevice *joystick = joysticks[id]; + if(joystick->name == JOYSTICK_NAME) + { + for(int axis = 0; axis < joystick->axes.count(); ++axis) + { + js_.push_back(std::shared_ptr(new TrainJs(id, axis))); + qDebug()<<"matched joystick: "<name<<" axis "<> TrainJs::getJsDevices() +{ + return js_; +} + +std::weak_ptr TrainJs::getItem() +{ + return item_; +} +void TrainJs::setItem(std::weak_ptr item) +{ + item_ = item; +} + +bool TrainJs::itemIsSet() +{ + return !item_.expired(); +} + +void TrainJs::axisChanged(const int id, const int axis, const qreal value) +{ + if(id == id_ && axis == axis_) + { + if(std::shared_ptr workitem = item_.lock()) + { + uint8_t newValue = (value+1)*7; + if(newValue != workitem->getValue()) + workitem->setValue(newValue); + } + else + { + reqNewItem(); + } + } + +} + +void TrainJs::buttonChanged(const int id, const int button, const bool pressed) +{ + if(id == id_ && button == axis_) + { + if(pressed) + { + handleRelease = true; + longpressTimer_.start(); + } + else if(handleRelease) + { + longpressTimer_.stop(); + if(std::shared_ptr workitem = item_.lock()) + { + if(Train* train = dynamic_cast(workitem.get())) + { + if(train->getValue() == 0) + train->reverse(); + } + } + else + { + reqNewItem(); + } + handleRelease = false; + } + } +} + +void TrainJs::longpressTimeout() +{ + reqNewItem(); + handleRelease = false; +} + + diff --git a/src/trainjs.h b/src/trainjs.h new file mode 100644 index 0000000..5e3efbd --- /dev/null +++ b/src/trainjs.h @@ -0,0 +1,47 @@ +#ifndef TRAINJS_H +#define TRAINJS_H + +#include +#include +#include +#include +#include "items/item.h" +#include "QJoysticks.h" + +class TrainJs: public QObject +{ + Q_OBJECT +private: + static constexpr char JOYSTICK_NAME[] = "UVOS UsbJoy"; + static constexpr int LONGPRESS_TIME = 500; + + inline static std::vector> js_; + + int id_ = -1; + int axis_ = -1; + + QTimer longpressTimer_; + bool handleRelease = false; + + std::weak_ptr item_; + TrainJs(int id, int axis); + TrainJs() = default; + +private slots: + void axisChanged(const int id, const int axis, const qreal value); + void buttonChanged(const int id, const int button, const bool pressed); + void longpressTimeout(); + +signals: + void reqNewItem(); + +public: + ~TrainJs(); + static std::vector> getJsDevices(); + static void init(); + std::weak_ptr getItem(); + void setItem(std::weak_ptr); + bool itemIsSet(); +}; + +#endif // TRAINJS_H diff --git a/src/ui/itemscrollbox.cpp b/src/ui/itemscrollbox.cpp index b1f52a5..ce94f93 100644 --- a/src/ui/itemscrollbox.cpp +++ b/src/ui/itemscrollbox.cpp @@ -3,6 +3,7 @@ #include "ui_relayscrollbox.h" #include "../items/train.h" #include "../items/turnout.h" +#include "../trainjs.h" ItemScrollBox::ItemScrollBox(QWidget *parent) : QWidget(parent), @@ -37,6 +38,16 @@ void ItemScrollBox::addItem(std::weak_ptr item) widgets_.back()->setShortcuts(QKeySequence(Qt::Key_R), QKeySequence(Qt::Key_F), QKeySequence(Qt::Key_V)); else if(train->getTrainId() == 4) widgets_.back()->setShortcuts(QKeySequence(Qt::Key_T), QKeySequence(Qt::Key_G), QKeySequence(Qt::Key_B)); + + std::vector> joysticks = TrainJs::getJsDevices(); + for(auto joystick: joysticks) + { + if(!joystick->itemIsSet()) + { + joystick->setItem(item); + break; + } + } } else if(turnout) { diff --git a/src/ui/itemwidget.cpp b/src/ui/itemwidget.cpp index 94a27e2..f6b4c09 100644 --- a/src/ui/itemwidget.cpp +++ b/src/ui/itemwidget.cpp @@ -14,21 +14,23 @@ ItemWidget::ItemWidget(std::weak_ptr item, QWidget *parent) : { ui->setupUi(this); - if(auto workingRelay = item_.lock()) + if(auto workingItem = item_.lock()) { - ui->label->setText(workingRelay->getName()); + ui->label->setText(workingItem->getName()); - connect(ui->slider, &QSlider::valueChanged, this, &ItemWidget::moveToValue); + connect(ui->slider, &QSlider::valueChanged, this, &ItemWidget::setValue); connect(ui->checkBox_f1, &QCheckBox::stateChanged, this, &ItemWidget::f1); connect(ui->checkBox_f2, &QCheckBox::stateChanged, this, &ItemWidget::f2); connect(ui->checkBox_f3, &QCheckBox::stateChanged, this, &ItemWidget::f3); connect(ui->checkBox_f4, &QCheckBox::stateChanged, this, &ItemWidget::f4); connect(ui->pushButton_reverse, &QPushButton::clicked, this, &ItemWidget::reverse); - connect(ui->radioButton_left, &QRadioButton::clicked, this, [this](){moveToValue(0);}); - connect(ui->radioButton_right, &QRadioButton::clicked, this, [this](){moveToValue(1);}); + connect(ui->radioButton_left, &QRadioButton::clicked, this, [this](){setValue(0);}); + connect(ui->radioButton_right, &QRadioButton::clicked, this, [this](){setValue(1);}); - Train* train = dynamic_cast(workingRelay.get()); - Turnout* turnout = dynamic_cast(workingRelay.get()); + connect(workingItem.get(), &Item::valueChanged, this, &ItemWidget::moveToValue); + + Train* train = dynamic_cast(workingItem.get()); + Turnout* turnout = dynamic_cast(workingItem.get()); if(turnout) { @@ -69,7 +71,16 @@ void ItemWidget::deleteItem() } } -void ItemWidget::moveToValue(int value) +void ItemWidget::setValue(uint8_t value) +{ + moveToValue(value); + if(auto workingItem = item_.lock()) + workingItem->setValue(value); + else + disable(); +} + +void ItemWidget::moveToValue(uint8_t value) { ui->slider->blockSignals(true); ui->radioButton_left->blockSignals(true); @@ -79,9 +90,6 @@ void ItemWidget::moveToValue(int value) ui->slider->setValue(value); ui->radioButton_left->setChecked(!value); ui->radioButton_right->setChecked(value); - if(auto workingItem = item_.lock()) - workingItem->setValue(value); - else disable(); ui->slider->blockSignals(false); ui->radioButton_left->blockSignals(false); @@ -127,7 +135,7 @@ void ItemWidget::reverse() else { qDebug()<<"!((bool)workingItem->getValue()) "<getValue()); - moveToValue(!((bool)workingItem->getValue())); + setValue(!((bool)workingItem->getValue())); } } else disable(); @@ -158,17 +166,17 @@ bool ItemWidget::controles(const ItemData& relay) void ItemWidget::stepUp() { - moveToValue(ui->slider->value()+1); + setValue(ui->slider->value()+1); } void ItemWidget::stepDown() { if(ui->slider->value() == 0) { - moveToValue(0); + setValue(0); return; } - moveToValue(ui->slider->value()-1); + setValue(ui->slider->value()-1); } void ItemWidget::setShortcuts(QKeySequence up, QKeySequence down, QKeySequence rev) diff --git a/src/ui/itemwidget.h b/src/ui/itemwidget.h index 5dcbcca..b1720f6 100644 --- a/src/ui/itemwidget.h +++ b/src/ui/itemwidget.h @@ -26,7 +26,8 @@ signals: void deleteRequest(const ItemData& item); private slots: - void moveToValue(int value); + void setValue(uint8_t value); + void moveToValue(uint8_t value); void deleteItem(); void stepUp(); diff --git a/trainControllerUI.pro b/trainControllerUI.pro index 3ecf230..992cae3 100644 --- a/trainControllerUI.pro +++ b/trainControllerUI.pro @@ -10,14 +10,16 @@ TARGET = traincontrollerui TEMPLATE = app INCLUDEPATH += /usr/include/libnl3/ +INCLUDEPATH += /usr/include/SDL2/ -LIBS += -lnl-3 -lnl-genl-3 +LIBS += -lnl-3 -lnl-genl-3 -lSDL2 # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += SDL_SUPPORTED QMAKE_CXXFLAGS += -std=c++17 -O2 @@ -25,6 +27,10 @@ SOURCES += \ src/items/train.cpp \ src/items/turnout.cpp \ src/mainobject.cpp \ + src/trainjs.cpp \ + src/QJoysticks.cpp \ + src/jsbackend/SDL_Joysticks.cpp \ + src/jsbackend/VirtualJoystick.cpp \ src/ui/itemwidget.cpp \ src/ui/itemscrollbox.cpp \ src/ui/mainwindow.cpp \ @@ -37,6 +43,11 @@ HEADERS += \ src/items/train.h \ src/items/turnout.h \ src/mainobject.h \ + src/trainjs.h \ + src/QJoysticks.h \ + src/jsbackend/SDL_Joysticks.h \ + src/jsbackend/VirtualJoystick.h \ + src/jsbackend/JoysticksCommon.h \ src/ui/itemwidget.h \ src/ui/itemscrollbox.h \ src/ui/mainwindow.h \