310 lines
7.6 KiB
C++
310 lines
7.6 KiB
C++
/*
|
|
* 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(10, 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;
|
|
}
|