add joysick support and state feedback

This commit is contained in:
uvos 2022-02-01 19:54:07 +01:00
parent 840cec975d
commit 1f36522492
21 changed files with 2940 additions and 32 deletions

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <QString>
#include <QList>
/**
* @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<int> povs; /**< Holds the values for each POV */
QList<double> axes; /**< Holds the values for each axis */
QList<bool> 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

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <QString>
/**
* @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<int> povs; /**< Holds the values for each POV */
QList<double> axes; /**< Holds the values for each axis */
QList<bool> 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

View file

@ -0,0 +1,309 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <QFile>
#include <QDebug>
#include <QTimer>
#include <QApplication>
#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<int, QJoystickDevice *>::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<int, QJoystickDevice *> SDL_Joysticks::joysticks()
{
int index = 0;
QMap<int, QJoystickDevice *> joysticks;
for (QMap<int, QJoystickDevice *>::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<qreal>(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;
}

View file

@ -0,0 +1,345 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <QFile>
#include <QDebug>
#include <QTimer>
#include <QApplication>
#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<int, QJoystickDevice *>::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<int, QJoystickDevice *> SDL_Joysticks::joysticks()
{
#ifdef SDL_SUPPORTED
int index = 0;
QMap<int, QJoystickDevice *> joysticks;
for (QMap<int, QJoystickDevice *>::iterator it = m_joysticks.begin();
it != m_joysticks.end(); ++it)
{
it.value()->id = index;
joysticks[index++] = it.value();
}
return joysticks;
#endif
return QMap<int, QJoystickDevice *>();
}
/**
* 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<qreal>(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;
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <SDL.h>
#include <QObject>
#include <QMap>
#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<int, QJoystickDevice *> 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<int, QJoystickDevice *> m_joysticks;
};
#endif

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <SDL.h>
#include <QObject>
#include <QMap>
#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<int, QJoystickDevice *> 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<int, QJoystickDevice *> m_joysticks;
};
#endif

View file

@ -0,0 +1,424 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <math.h>
#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<AxisState>(NUMBER_OF_AXES, AxisState::STILL);
m_axisValue = QVector<qint16>(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<qint16>((-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<QKeyEvent *>(event), true);
break;
case QEvent::KeyRelease:
processKeyEvent(static_cast<QKeyEvent *>(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<qreal>(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);
}
}

View file

@ -0,0 +1,423 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <math.h>
#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<AxisState>(NUMBER_OF_AXES, AxisState::STILL);
m_axisValue = QVector<qint16>(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<qint16>((-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<QKeyEvent *>(event), true);
break;
case QEvent::KeyRelease:
processKeyEvent(static_cast<QKeyEvent *>(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<qreal>(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);
}
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <QWidget>
#include <QKeyEvent>
#include <QApplication>
#include <QTimer>
#include <QScopedPointer>
#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<AxisState> m_axisStatus;
QVector<qint16> m_axisValue;
QScopedPointer<QTimer> m_timerUpdateAxis;
void changeAxisValue(quint8 axis);
void resetAllAxes();
};
#endif

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2015-2017 Alex Spataru <alex_spataru@outlook.com>
*
* 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 <QWidget>
#include <QKeyEvent>
#include <QApplication>
#include <QTimer>
#include <QScopedPointer>
#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<AxisState> m_axisStatus;
QVector<qint16> m_axisValue;
QScopedPointer<QTimer> m_timerUpdateAxis;
void changeAxisValue(quint8 axis);
void resetAllAxes();
};
#endif