integrated joysticks with userinputmapper, enabling plug-in-and-play controllers and input mapping with js

This commit is contained in:
Sam Gondelman 2015-06-11 15:51:21 -07:00
parent 24fda9a733
commit b8b61e7c62
7 changed files with 278 additions and 145 deletions

View file

@ -113,6 +113,7 @@
#include "devices/Faceshift.h"
#include "devices/Leapmotion.h"
#include "devices/RealSense.h"
#include "devices/SDL2Manager.h"
#include "devices/MIDIManager.h"
#include "devices/OculusManager.h"
#include "devices/TV3DManager.h"
@ -127,7 +128,6 @@
#include "scripting/AudioDeviceScriptingInterface.h"
#include "scripting/ClipboardScriptingInterface.h"
#include "scripting/HMDScriptingInterface.h"
#include "scripting/JoystickScriptingInterface.h"
#include "scripting/GlobalServicesScriptingInterface.h"
#include "scripting/LocationScriptingInterface.h"
#include "scripting/MenuScriptingInterface.h"
@ -1455,6 +1455,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
void Application::focusOutEvent(QFocusEvent* event) {
_keyboardMouseDevice.focusOutEvent(event);
SDL2Manager::getInstance()->focusOutEvent();
// synthesize events for keys currently pressed, since we may not get their release events
foreach (int key, _keysPressed) {
@ -2450,7 +2451,7 @@ void Application::update(float deltaTime) {
}
SixenseManager::getInstance().update(deltaTime);
JoystickScriptingInterface::getInstance().update();
SDL2Manager::getInstance()->update();
}
_userInputMapper.update(deltaTime);
@ -4045,7 +4046,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());
scriptEngine->registerGlobalObject("Joysticks", &JoystickScriptingInterface::getInstance());
qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue);
scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface);

View file

@ -13,8 +13,11 @@
#include <glm/glm.hpp>
#include "Application.h"
#include "Joystick.h"
const float CONTROLLER_THRESHOLD = .25f;
#ifdef HAVE_SDL2
const float MAX_AXIS = 32768.0f;
@ -22,10 +25,7 @@ const float MAX_AXIS = 32768.0f;
Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) :
_sdlGameController(sdlGameController),
_sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
_instanceId(instanceId),
_name(name),
_axes(QVector<float>(SDL_JoystickNumAxes(_sdlJoystick))),
_buttons(QVector<bool>(SDL_JoystickNumButtons(_sdlJoystick)))
_instanceId(instanceId)
{
}
@ -42,24 +42,171 @@ void Joystick::closeJoystick() {
#endif
}
void Joystick::update() {
for (auto axisState : _axisStateMap) {
if (axisState.second < CONTROLLER_THRESHOLD && axisState.second > -CONTROLLER_THRESHOLD) {
_axisStateMap[axisState.first] = 0;
}
}
}
void Joystick::focusOutEvent() {
_axisStateMap.clear();
_buttonPressedMap.clear();
};
#ifdef HAVE_SDL2
void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) {
if (_axes.size() <= event.axis) {
_axes.resize(event.axis + 1);
SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis;
if (axis == SDL_CONTROLLER_AXIS_LEFTX) {
_axisStateMap[makeInput(LEFT_AXIS_X_POS).getChannel()] = (event.value > 0) ? event.value / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(LEFT_AXIS_X_NEG).getChannel()] = (event.value < 0) ? -event.value / MAX_AXIS : 0.0f;
} else if (axis == SDL_CONTROLLER_AXIS_LEFTY) {
_axisStateMap[makeInput(LEFT_AXIS_Y_POS).getChannel()] = (event.value > 0) ? event.value / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(LEFT_AXIS_Y_NEG).getChannel()] = (event.value < 0) ? -event.value / MAX_AXIS : 0.0f;
} else if (axis == SDL_CONTROLLER_AXIS_RIGHTX) {
_axisStateMap[makeInput(RIGHT_AXIS_X_POS).getChannel()] = (event.value > 0) ? event.value / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(RIGHT_AXIS_X_NEG).getChannel()] = (event.value < 0) ? -event.value / MAX_AXIS : 0.0f;
} else if (axis == SDL_CONTROLLER_AXIS_RIGHTY) {
_axisStateMap[makeInput(RIGHT_AXIS_Y_POS).getChannel()] = (event.value > 0) ? event.value / MAX_AXIS : 0.0f;
_axisStateMap[makeInput(RIGHT_AXIS_Y_NEG).getChannel()] = (event.value < 0) ? -event.value / MAX_AXIS : 0.0f;
}
float oldValue = _axes[event.axis];
float newValue = event.value / MAX_AXIS;
_axes[event.axis] = newValue;
emit axisValueChanged(event.axis, newValue, oldValue);
}
void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
bool oldValue = _buttons[event.button];
auto input = makeInput((SDL_GameControllerButton) event.button);
bool newValue = event.state == SDL_PRESSED;
_buttons[event.button] = newValue;
emit buttonStateChanged(event.button, newValue, oldValue);
if (newValue) {
_buttonPressedMap.insert(input.getChannel());
} else {
_buttonPressedMap.erase(input.getChannel());
}
}
#endif
void Joystick::registerToUserInputMapper(UserInputMapper& mapper) {
// Grab the current free device ID
_deviceID = mapper.getFreeDeviceID();
auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name));
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); };
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); };
proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
QVector<UserInputMapper::InputPair> availableInputs;
#ifdef HAVE_SDL2
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_A), "Bottom Button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_B), "Right Button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_X), "Left Button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_Y), "Top Button"));
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), "DPad Up"));
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down"));
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left"));
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right"));
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER), "L1"));
availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1"));
#endif
return availableInputs;
};
proxy->resetDeviceBindings = [this, &mapper] () -> bool {
mapper.removeAllInputChannelsForDevice(_deviceID);
this->assignDefaultInputMapping(mapper);
return true;
};
mapper.registerDevice(_deviceID, proxy);
}
void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {
#ifdef HAVE_SDL2
const float JOYSTICK_MOVE_SPEED = 1.0f;
const float DPAD_MOVE_SPEED = .5f;
const float JOYSTICK_YAW_SPEED = 0.5f;
const float JOYSTICK_PITCH_SPEED = 0.25f;
// Y axes are flipped (up is negative)
// Left Joystick: Movement, strafing
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
// Right Joystick: Camera orientation
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
// Dpad movement
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED);
// Button controls
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_A), DPAD_MOVE_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(SDL_CONTROLLER_BUTTON_X), JOYSTICK_YAW_SPEED);
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_B), JOYSTICK_YAW_SPEED);
// Hold front right shoulder button for precision controls
// Left Joystick: Movement, strafing
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.);
// Right Joystick: Camera orientation
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.);
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.);
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.);
// Dpad movement
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.);
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.);
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.);
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.);
// Button controls
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.);
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_A), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.);
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.);
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_B), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.);
#endif
}
float Joystick::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
return 1.0f;
} else {
return 0.0f;
}
}
return 0.0f;
}
float Joystick::getAxis(int channel) const {
auto axis = _axisStateMap.find(channel);
if (axis != _axisStateMap.end()) {
return (*axis).second;
} else {
return 0.0f;
}
}
#ifdef HAVE_SDL2
UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) {
return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
}
#endif
UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) {
return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
}

View file

@ -20,6 +20,8 @@
#undef main
#endif
#include "ui/UserInputMapper.h"
class Joystick : public QObject {
Q_OBJECT
@ -29,12 +31,38 @@ class Joystick : public QObject {
Q_PROPERTY(int instanceId READ getInstanceId)
#endif
Q_PROPERTY(int numAxes READ getNumAxes)
Q_PROPERTY(int numButtons READ getNumButtons)
public:
enum JoystickAxisChannel {
LEFT_AXIS_X_POS = 0,
LEFT_AXIS_X_NEG,
LEFT_AXIS_Y_POS,
LEFT_AXIS_Y_NEG,
RIGHT_AXIS_X_POS,
RIGHT_AXIS_X_NEG,
RIGHT_AXIS_Y_POS,
RIGHT_AXIS_Y_NEG,
};
Joystick();
~Joystick();
typedef std::unordered_set<int> ButtonPressedMap;
typedef std::map<int, float> AxisStateMap;
float getButton(int channel) const;
float getAxis(int channel) const;
#ifdef HAVE_SDL2
UserInputMapper::Input makeInput(SDL_GameControllerButton button);
#endif
UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis);
void registerToUserInputMapper(UserInputMapper& mapper);
void assignDefaultInputMapping(UserInputMapper& mapper);
void update();
void focusOutEvent();
#ifdef HAVE_SDL2
Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController);
#endif
@ -51,15 +79,8 @@ public:
int getInstanceId() const { return _instanceId; }
#endif
const QVector<float>& getAxes() const { return _axes; }
const QVector<bool>& getButtons() const { return _buttons; }
int getDeviceID() { return _deviceID; }
int getNumAxes() const { return _axes.size(); }
int getNumButtons() const { return _buttons.size(); }
signals:
void axisValueChanged(int axis, float newValue, float oldValue);
void buttonStateChanged(int button, float newValue, float oldValue);
private:
#ifdef HAVE_SDL2
SDL_GameController* _sdlGameController;
@ -68,8 +89,12 @@ private:
#endif
QString _name;
QVector<float> _axes;
QVector<bool> _buttons;
protected:
int _deviceID = 0;
ButtonPressedMap _buttonPressedMap;
AxisStateMap _axisStateMap;
};
#endif // hifi_JoystickTracker_h
#endif // hifi_Joystick_h

View file

@ -1,73 +1,66 @@
//
// JoystickScriptingInterface.cpp
// SDL2Manager.cpp
// interface/src/devices
//
// Created by Andrzej Kapolka on 5/15/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Sam Gondelman on 6/5/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <qapplication.h>
#include <QtDebug>
#include <QScriptValue>
#ifdef HAVE_SDL2
#include <SDL.h>
#undef main
#endif
#include <HFActionEvent.h>
#include <HFBackEvent.h>
#include <PerfStat.h>
#include "Application.h"
#include "Application.h"
#include "JoystickScriptingInterface.h"
#include "SDL2Manager.h"
#ifdef HAVE_SDL2
SDL_JoystickID getInstanceId(SDL_GameController* controller) {
SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) {
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
return SDL_JoystickInstanceID(joystick);
}
#endif
JoystickScriptingInterface& JoystickScriptingInterface::getInstance() {
static JoystickScriptingInterface sharedInstance;
return sharedInstance;
}
JoystickScriptingInterface::JoystickScriptingInterface() :
SDL2Manager::SDL2Manager() :
#ifdef HAVE_SDL2
_openJoysticks(),
_openJoysticks(),
#endif
_isInitialized(false)
_isInitialized(false)
{
#ifdef HAVE_SDL2
bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER) == 0);
if (initSuccess) {
int joystickCount = SDL_NumJoysticks();
for (int i = 0; i < joystickCount; i++) {
SDL_GameController* controller = SDL_GameControllerOpen(i);
if (controller) {
SDL_JoystickID id = getInstanceId(controller);
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
_openJoysticks[id] = joystick;
if (!_openJoysticks.contains(id)) {
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
_openJoysticks[id] = joystick;
joystick->registerToUserInputMapper(*Application::getUserInputMapper());
joystick->assignDefaultInputMapping(*Application::getUserInputMapper());
emit joystickAdded(joystick);
}
}
}
_isInitialized = true;
} else {
qDebug() << "Error initializing SDL";
qDebug() << "Error initializing SDL2 Manager";
}
#endif
}
JoystickScriptingInterface::~JoystickScriptingInterface() {
SDL2Manager::~SDL2Manager() {
#ifdef HAVE_SDL2
qDeleteAll(_openJoysticks);
@ -75,34 +68,25 @@ JoystickScriptingInterface::~JoystickScriptingInterface() {
#endif
}
const QObjectList JoystickScriptingInterface::getAllJoysticks() const {
QObjectList objectList;
#ifdef HAVE_SDL2
const QList<Joystick*> joystickList = _openJoysticks.values();
for (int i = 0; i < joystickList.length(); i++) {
objectList << joystickList[i];
}
#endif
return objectList;
SDL2Manager* SDL2Manager::getInstance() {
static SDL2Manager sharedInstance;
return &sharedInstance;
}
Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) {
#ifdef HAVE_SDL2
QMap<SDL_JoystickID, Joystick*>::iterator iter = _openJoysticks.begin();
while (iter != _openJoysticks.end()) {
if (iter.value()->getName() == name) {
return iter.value();
}
iter++;
void SDL2Manager::focusOutEvent() {
for (auto joystick : _openJoysticks) {
joystick->focusOutEvent();
}
#endif
return NULL;
}
void JoystickScriptingInterface::update() {
void SDL2Manager::update() {
#ifdef HAVE_SDL2
if (_isInitialized) {
PerformanceTimer perfTimer("JoystickScriptingInterface::update");
for (auto joystick : _openJoysticks) {
joystick->update();
}
PerformanceTimer perfTimer("SDL2Manager::update");
SDL_GameControllerUpdate();
SDL_Event event;
while (SDL_PollEvent(&event)) {
@ -120,16 +104,16 @@ void JoystickScriptingInterface::update() {
if (event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK) {
// this will either start or stop a global back event
QEvent::Type backType = (event.type == SDL_CONTROLLERBUTTONDOWN)
? HFBackEvent::startType()
: HFBackEvent::endType();
? HFBackEvent::startType()
: HFBackEvent::endType();
HFBackEvent backEvent(backType);
qApp->sendEvent(qApp, &backEvent);
} else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_A) {
// this will either start or stop a global action event
QEvent::Type actionType = (event.type == SDL_CONTROLLERBUTTONDOWN)
? HFActionEvent::startType()
: HFActionEvent::endType();
? HFActionEvent::startType()
: HFActionEvent::endType();
// global action events fire in the center of the screen
Application* app = Application::getInstance();
@ -141,14 +125,19 @@ void JoystickScriptingInterface::update() {
} else if (event.type == SDL_CONTROLLERDEVICEADDED) {
SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which);
SDL_JoystickID id = getInstanceId(controller);
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
_openJoysticks[id] = joystick;
emit joystickAdded(joystick);
if (!_openJoysticks.contains(id)) {
Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
_openJoysticks[id] = joystick;
joystick->registerToUserInputMapper(*Application::getUserInputMapper());
joystick->assignDefaultInputMapping(*Application::getUserInputMapper());
emit joystickAdded(joystick);
}
} else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
Joystick* joystick = _openJoysticks[event.cdevice.which];
_openJoysticks.remove(event.cdevice.which);
Application::getUserInputMapper()->removeDevice(joystick->getDeviceID());
emit joystickRemoved(joystick);
}
}

View file

@ -1,77 +1,46 @@
//
// JoystickScriptingInterface.h
// SDL2Manager.h
// interface/src/devices
//
// Created by Andrzej Kapolka on 5/15/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Sam Gondelman on 6/5/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_JoystickScriptingInterface_h
#define hifi_JoystickScriptingInterface_h
#include <QObject>
#include <QVector>
#ifndef hifi__SDL2Manager_h
#define hifi__SDL2Manager_h
#ifdef HAVE_SDL2
#include <SDL.h>
#endif
#include "ui/UserInputMapper.h"
#include "devices/Joystick.h"
/// Handles joystick input through SDL.
class JoystickScriptingInterface : public QObject {
class SDL2Manager : public QObject {
Q_OBJECT
#ifdef HAVE_SDL2
Q_PROPERTY(int AXIS_INVALID READ axisInvalid)
Q_PROPERTY(int AXIS_LEFT_X READ axisLeftX)
Q_PROPERTY(int AXIS_LEFT_Y READ axisLeftY)
Q_PROPERTY(int AXIS_RIGHT_X READ axisRightX)
Q_PROPERTY(int AXIS_RIGHT_Y READ axisRightY)
Q_PROPERTY(int AXIS_TRIGGER_LEFT READ axisTriggerLeft)
Q_PROPERTY(int AXIS_TRIGGER_RIGHT READ axisTriggerRight)
Q_PROPERTY(int AXIS_MAX READ axisMax)
Q_PROPERTY(int BUTTON_INVALID READ buttonInvalid)
Q_PROPERTY(int BUTTON_FACE_BOTTOM READ buttonFaceBottom)
Q_PROPERTY(int BUTTON_FACE_RIGHT READ buttonFaceRight)
Q_PROPERTY(int BUTTON_FACE_LEFT READ buttonFaceLeft)
Q_PROPERTY(int BUTTON_FACE_TOP READ buttonFaceTop)
Q_PROPERTY(int BUTTON_BACK READ buttonBack)
Q_PROPERTY(int BUTTON_GUIDE READ buttonGuide)
Q_PROPERTY(int BUTTON_START READ buttonStart)
Q_PROPERTY(int BUTTON_LEFT_STICK READ buttonLeftStick)
Q_PROPERTY(int BUTTON_RIGHT_STICK READ buttonRightStick)
Q_PROPERTY(int BUTTON_LEFT_SHOULDER READ buttonLeftShoulder)
Q_PROPERTY(int BUTTON_RIGHT_SHOULDER READ buttonRightShoulder)
Q_PROPERTY(int BUTTON_DPAD_UP READ buttonDpadUp)
Q_PROPERTY(int BUTTON_DPAD_DOWN READ buttonDpadDown)
Q_PROPERTY(int BUTTON_DPAD_LEFT READ buttonDpadLeft)
Q_PROPERTY(int BUTTON_DPAD_RIGHT READ buttonDpadRight)
Q_PROPERTY(int BUTTON_MAX READ buttonMax)
Q_PROPERTY(int BUTTON_PRESSED READ buttonPressed)
Q_PROPERTY(int BUTTON_RELEASED READ buttonRelease)
#endif
public:
static JoystickScriptingInterface& getInstance();
SDL2Manager();
~SDL2Manager();
void focusOutEvent();
void update();
public slots:
Joystick* joystickWithName(const QString& name);
const QObjectList getAllJoysticks() const;
static SDL2Manager* getInstance();
signals:
void joystickAdded(Joystick* joystick);
void joystickRemoved(Joystick* joystick);
private:
#ifdef HAVE_SDL2
SDL_JoystickID getInstanceId(SDL_GameController* controller);
int axisInvalid() const { return SDL_CONTROLLER_AXIS_INVALID; }
int axisLeftX() const { return SDL_CONTROLLER_AXIS_LEFTX; }
int axisLeftY() const { return SDL_CONTROLLER_AXIS_LEFTY; }
@ -80,7 +49,7 @@ private:
int axisTriggerLeft() const { return SDL_CONTROLLER_AXIS_TRIGGERLEFT; }
int axisTriggerRight() const { return SDL_CONTROLLER_AXIS_TRIGGERRIGHT; }
int axisMax() const { return SDL_CONTROLLER_AXIS_MAX; }
int buttonInvalid() const { return SDL_CONTROLLER_BUTTON_INVALID; }
int buttonFaceBottom() const { return SDL_CONTROLLER_BUTTON_A; }
int buttonFaceRight() const { return SDL_CONTROLLER_BUTTON_B; }
@ -98,18 +67,15 @@ private:
int buttonDpadLeft() const { return SDL_CONTROLLER_BUTTON_DPAD_LEFT; }
int buttonDpadRight() const { return SDL_CONTROLLER_BUTTON_DPAD_RIGHT; }
int buttonMax() const { return SDL_CONTROLLER_BUTTON_MAX; }
int buttonPressed() const { return SDL_PRESSED; }
int buttonRelease() const { return SDL_RELEASED; }
#endif
JoystickScriptingInterface();
~JoystickScriptingInterface();
#ifdef HAVE_SDL2
QMap<SDL_JoystickID, Joystick*> _openJoysticks;
#endif
bool _isInitialized;
};
#endif // hifi_JoystickScriptingInterface_h
#endif // hifi__SDL2Manager_h

View file

@ -106,6 +106,11 @@ void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) {
}
}
void UserInputMapper::removeDevice(int device) {
removeAllInputChannelsForDevice((uint16) device);
_registeredDevices.erase(device);
}
int UserInputMapper::getInputChannels(InputChannels& channels) const {
for (auto& channel : _actionToInputsMap) {
channels.push_back(channel.second);

View file

@ -189,6 +189,7 @@ public:
bool removeInputChannel(InputChannel channel);
void removeAllInputChannels();
void removeAllInputChannelsForDevice(uint16 device);
void removeDevice(int device);
//Grab all the input channels currently in use, return the number
int getInputChannels(InputChannels& channels) const;
QVector<InputChannel> getAllInputsForDevice(uint16 device);