/* * 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); } } }