Commit 400d6d2c authored by Jacob Walser's avatar Jacob Walser

Joystick Hotplugging

parent a8240d22
...@@ -71,9 +71,9 @@ Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatC ...@@ -71,9 +71,9 @@ Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatC
Joystick::~Joystick() Joystick::~Joystick()
{ {
delete _rgAxisValues; delete[] _rgAxisValues;
delete _rgCalibration; delete[] _rgCalibration;
delete _rgButtonValues; delete[] _rgButtonValues;
} }
void Joystick::_loadSettings(void) void Joystick::_loadSettings(void)
...@@ -286,7 +286,7 @@ void Joystick::run(void) ...@@ -286,7 +286,7 @@ void Joystick::run(void)
} }
} }
if (_calibrationMode != CalibrationModeCalibrating) { if (_calibrationMode != CalibrationModeCalibrating && _calibrated) {
int axis = _rgFunctionAxis[rollFunction]; int axis = _rgFunctionAxis[rollFunction];
float roll = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis], _deadband); float roll = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis], _deadband);
...@@ -406,6 +406,9 @@ void Joystick::startPolling(Vehicle* vehicle) ...@@ -406,6 +406,9 @@ void Joystick::startPolling(Vehicle* vehicle)
vehicle->setJoystickEnabled(false); vehicle->setJoystickEnabled(false);
} }
// Update qml in case of joystick transition
emit calibratedChanged(_calibrated);
// Only connect the new vehicle if it wants joystick data // Only connect the new vehicle if it wants joystick data
if (vehicle->joystickEnabled()) { if (vehicle->joystickEnabled()) {
_pollingStartedForCalibration = false; _pollingStartedForCalibration = false;
...@@ -430,7 +433,8 @@ void Joystick::stopPolling(void) ...@@ -430,7 +433,8 @@ void Joystick::stopPolling(void)
if (_activeVehicle && _activeVehicle->joystickEnabled()) { if (_activeVehicle && _activeVehicle->joystickEnabled()) {
UAS* uas = _activeVehicle->uas(); UAS* uas = _activeVehicle->uas();
// Neutral attitude controls
// emit manualControl(0, 0, 0, 0.5, 0, _activeVehicle->joystickMode());
disconnect(this, &Joystick::manualControl, uas, &UAS::setExternalControlSetpoint); disconnect(this, &Joystick::manualControl, uas, &UAS::setExternalControlSetpoint);
} }
// FIXME: **** // FIXME: ****
......
...@@ -89,6 +89,11 @@ public: ...@@ -89,6 +89,11 @@ public:
QString name(void) { return _name; } QString name(void) { return _name; }
// Joystick index used by sdl library
// Settable because sdl library remaps indicies after certain events
virtual int index(void) = 0;
virtual void setIndex(int index) = 0;
int throttleMode(void); int throttleMode(void);
void setThrottleMode(int mode); void setThrottleMode(int mode);
......
...@@ -55,10 +55,22 @@ void JoystickManager::setToolbox(QGCToolbox *toolbox) ...@@ -55,10 +55,22 @@ void JoystickManager::setToolbox(QGCToolbox *toolbox)
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
} }
void JoystickManager::discoverJoysticks() void JoystickManager::init()
{ {
if (JoystickSDL::init()) {
_setActiveJoystickFromSettings();
connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks);
_joystickCheckTimer.start(250);
}
}
void JoystickManager::_setActiveJoystickFromSettings(void)
{
QMap<QString,Joystick*> newMap;
#ifdef __sdljoystick__ #ifdef __sdljoystick__
_name2JoystickMap = JoystickSDL::discover(_multiVehicleManager); // Get the latest joystick mapping
newMap = JoystickSDL::discover(_multiVehicleManager);
#elif defined(__android__) #elif defined(__android__)
/* /*
* Android Joystick not yet supported * Android Joystick not yet supported
...@@ -66,16 +78,30 @@ void JoystickManager::discoverJoysticks() ...@@ -66,16 +78,30 @@ void JoystickManager::discoverJoysticks()
*/ */
#endif #endif
// Check to see if our current mapping contains any joysticks that are not in the new mapping
// If so, those joysticks have been unplugged, and need to be cleaned up
QMap<QString, Joystick*>::iterator i;
for (i = _name2JoystickMap.begin(); i != _name2JoystickMap.end(); ++i) {
if (!newMap.contains(i.key())) {
qCDebug(JoystickManagerLog) << "Releasing joystick:" << i.key();
if (_activeJoystick && !newMap.contains(_activeJoystick->name())) {
qCDebug(JoystickManagerLog) << "\twas active";
_activeJoystick->stopPolling();
_activeJoystick = NULL;
}
// FIXME: Don't leave this hanging! We need to free the object.
//i.value()->deleteLater();
}
}
_name2JoystickMap = newMap;
emit availableJoysticksChanged();
if (!_name2JoystickMap.count()) { if (!_name2JoystickMap.count()) {
qCDebug(JoystickManagerLog) << "\tnone found"; setActiveJoystick(NULL);
return; return;
} }
_setActiveJoystickFromSettings();
}
void JoystickManager::_setActiveJoystickFromSettings(void)
{
QSettings settings; QSettings settings;
settings.beginGroup(_settingsGroup); settings.beginGroup(_settingsGroup);
...@@ -98,22 +124,29 @@ void JoystickManager::setActiveJoystick(Joystick* joystick) ...@@ -98,22 +124,29 @@ void JoystickManager::setActiveJoystick(Joystick* joystick)
{ {
QSettings settings; QSettings settings;
if (!_name2JoystickMap.contains(joystick->name())) { if (joystick != NULL && !_name2JoystickMap.contains(joystick->name())) {
qCWarning(JoystickManagerLog) << "Set active not in map" << joystick->name(); qCWarning(JoystickManagerLog) << "Set active not in map" << joystick->name();
return; return;
} }
if (_activeJoystick == joystick) {
return;
}
if (_activeJoystick) { if (_activeJoystick) {
_activeJoystick->stopPolling(); _activeJoystick->stopPolling();
} }
_activeJoystick = joystick; _activeJoystick = joystick;
if (_activeJoystick != NULL) {
qCDebug(JoystickManagerLog) << "Set active:" << _activeJoystick->name();
settings.beginGroup(_settingsGroup); settings.beginGroup(_settingsGroup);
settings.setValue(_settingsKeyActiveJoystick, _activeJoystick->name()); settings.setValue(_settingsKeyActiveJoystick, _activeJoystick->name());
emit activeJoystickChanged(_activeJoystick); emit activeJoystickChanged(_activeJoystick);
emit activeJoystickNameChanged(_activeJoystick->name()); emit activeJoystickNameChanged(_activeJoystick->name());
}
} }
QVariantList JoystickManager::joysticks(void) QVariantList JoystickManager::joysticks(void)
...@@ -146,3 +179,25 @@ void JoystickManager::setActiveJoystickName(const QString& name) ...@@ -146,3 +179,25 @@ void JoystickManager::setActiveJoystickName(const QString& name)
setActiveJoystick(_name2JoystickMap[name]); setActiveJoystick(_name2JoystickMap[name]);
} }
void JoystickManager::_updateAvailableJoysticks(void)
{
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_QUIT:
qCDebug(JoystickManagerLog) << "SDL ERROR:" << SDL_GetError();
break;
case SDL_JOYDEVICEADDED:
qCDebug(JoystickManagerLog) << "Joystick added:" << event.jdevice.which;
_setActiveJoystickFromSettings();
break;
case SDL_JOYDEVICEREMOVED:
qCDebug(JoystickManagerLog) << "Joystick removed:" << event.jdevice.which;
_setActiveJoystickFromSettings();
break;
default:
break;
}
}
}
...@@ -29,8 +29,8 @@ public: ...@@ -29,8 +29,8 @@ public:
~JoystickManager(); ~JoystickManager();
/// List of available joysticks /// List of available joysticks
Q_PROPERTY(QVariantList joysticks READ joysticks CONSTANT) Q_PROPERTY(QVariantList joysticks READ joysticks NOTIFY availableJoysticksChanged)
Q_PROPERTY(QStringList joystickNames READ joystickNames CONSTANT) Q_PROPERTY(QStringList joystickNames READ joystickNames NOTIFY availableJoysticksChanged)
/// Active joystick /// Active joystick
Q_PROPERTY(Joystick* activeJoystick READ activeJoystick WRITE setActiveJoystick NOTIFY activeJoystickChanged) Q_PROPERTY(Joystick* activeJoystick READ activeJoystick WRITE setActiveJoystick NOTIFY activeJoystickChanged)
...@@ -49,13 +49,15 @@ public: ...@@ -49,13 +49,15 @@ public:
virtual void setToolbox(QGCToolbox *toolbox); virtual void setToolbox(QGCToolbox *toolbox);
public slots: public slots:
void discoverJoysticks(); void init();
signals: signals:
void activeJoystickChanged(Joystick* joystick); void activeJoystickChanged(Joystick* joystick);
void activeJoystickNameChanged(const QString& name); void activeJoystickNameChanged(const QString& name);
void availableJoysticksChanged(void);
private slots: private slots:
void _updateAvailableJoysticks(void);
private: private:
void _setActiveJoystickFromSettings(void); void _setActiveJoystickFromSettings(void);
...@@ -67,6 +69,8 @@ private: ...@@ -67,6 +69,8 @@ private:
static const char * _settingsGroup; static const char * _settingsGroup;
static const char * _settingsKeyActiveJoystick; static const char * _settingsKeyActiveJoystick;
QTimer _joystickCheckTimer;
}; };
#endif #endif
...@@ -12,15 +12,21 @@ JoystickSDL::JoystickSDL(const QString& name, int axisCount, int buttonCount, in ...@@ -12,15 +12,21 @@ JoystickSDL::JoystickSDL(const QString& name, int axisCount, int buttonCount, in
{ {
} }
QMap<QString, Joystick*> JoystickSDL::discover(MultiVehicleManager* _multiVehicleManager) { bool JoystickSDL::init(void) {
static QMap<QString, Joystick*> ret;
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0) { if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0) {
SDL_JoystickEventState(SDL_ENABLE);
qWarning() << "Couldn't initialize SimpleDirectMediaLayer:" << SDL_GetError(); qWarning() << "Couldn't initialize SimpleDirectMediaLayer:" << SDL_GetError();
return ret; return false;
} }
_loadGameControllerMappings(); _loadGameControllerMappings();
return true;
}
QMap<QString, Joystick*> JoystickSDL::discover(MultiVehicleManager* _multiVehicleManager) {
static QMap<QString, Joystick*> ret;
QMap<QString,Joystick*> newRet;
// Load available joysticks // Load available joysticks
...@@ -29,6 +35,7 @@ QMap<QString, Joystick*> JoystickSDL::discover(MultiVehicleManager* _multiVehicl ...@@ -29,6 +35,7 @@ QMap<QString, Joystick*> JoystickSDL::discover(MultiVehicleManager* _multiVehicl
for (int i=0; i<SDL_NumJoysticks(); i++) { for (int i=0; i<SDL_NumJoysticks(); i++) {
QString name = SDL_JoystickNameForIndex(i); QString name = SDL_JoystickNameForIndex(i);
// TODO use GUID instead of name in case of two joysticks with same name
if (!ret.contains(name)) { if (!ret.contains(name)) {
int axisCount, buttonCount, hatCount; int axisCount, buttonCount, hatCount;
bool isGameController; bool isGameController;
...@@ -57,11 +64,24 @@ QMap<QString, Joystick*> JoystickSDL::discover(MultiVehicleManager* _multiVehicl ...@@ -57,11 +64,24 @@ QMap<QString, Joystick*> JoystickSDL::discover(MultiVehicleManager* _multiVehicl
} }
qCDebug(JoystickLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount << "hats:" << hatCount << "isGC:" << isGameController; qCDebug(JoystickLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount << "hats:" << hatCount << "isGC:" << isGameController;
ret[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController, _multiVehicleManager); newRet[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController, _multiVehicleManager);
} else { } else {
newRet[name] = ret[name];
if (newRet[name]->index() != i) {
newRet[name]->setIndex(i); // This joystick index has been remapped by SDL
}
// Anything left in ret after we exit the loop has been removed (unplugged) and needs to be cleaned up.
// We will handle that in JoystickManager in case the removed joystick was in use.
ret.remove(name);
qCDebug(JoystickLog) << "\tSkipping duplicate" << name; qCDebug(JoystickLog) << "\tSkipping duplicate" << name;
} }
} }
if (!newRet.count()) {
qCDebug(JoystickLog) << "\tnone found";
}
ret = newRet;
return ret; return ret;
} }
...@@ -93,11 +113,32 @@ bool JoystickSDL::_open(void) { ...@@ -93,11 +113,32 @@ bool JoystickSDL::_open(void) {
return false; return false;
} }
qCDebug(JoystickLog) << "Opened joystick at" << sdlJoystick;
return true; return true;
} }
void JoystickSDL::_close(void) { void JoystickSDL::_close(void) {
if (sdlJoystick == NULL) {
qCDebug(JoystickLog) << "Attempt to close null joystick!";
return;
}
qCDebug(JoystickLog) << "Closing" << SDL_JoystickName(sdlJoystick) << "at" << sdlJoystick;
// We get a segfault if we try to close a joystick that has been detached
if (SDL_JoystickGetAttached(sdlJoystick) == SDL_FALSE) {
qCDebug(JoystickLog) << "\tJoystick is not attached!";
} else {
if (SDL_JoystickInstanceID(sdlJoystick) != -1) {
qCDebug(JoystickLog) << "\tID:" << SDL_JoystickInstanceID(sdlJoystick);
SDL_JoystickClose(sdlJoystick); SDL_JoystickClose(sdlJoystick);
}
}
sdlJoystick = NULL;
sdlController = NULL;
} }
bool JoystickSDL::_update(void) bool JoystickSDL::_update(void)
...@@ -131,4 +172,3 @@ uint8_t JoystickSDL::_getHat(int hat,int i) { ...@@ -131,4 +172,3 @@ uint8_t JoystickSDL::_getHat(int hat,int i) {
} }
return 0; return 0;
} }
...@@ -13,6 +13,10 @@ public: ...@@ -13,6 +13,10 @@ public:
JoystickSDL(const QString& name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController, MultiVehicleManager* multiVehicleManager); JoystickSDL(const QString& name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController, MultiVehicleManager* multiVehicleManager);
static QMap<QString, Joystick*> discover(MultiVehicleManager* _multiVehicleManager); static QMap<QString, Joystick*> discover(MultiVehicleManager* _multiVehicleManager);
static bool init(void);
int index(void) { return _index; }
void setIndex(int index) { _index = index; }
private: private:
static void _loadGameControllerMappings(); static void _loadGameControllerMappings();
......
...@@ -429,8 +429,8 @@ bool QGCApplication::_initForNormalAppBoot(void) ...@@ -429,8 +429,8 @@ bool QGCApplication::_initForNormalAppBoot(void)
// Load known link configurations // Load known link configurations
toolbox()->linkManager()->loadLinkConfigurationList(); toolbox()->linkManager()->loadLinkConfigurationList();
// Probe for joysticks - TODO: manage on a timer or use events to deal with hotplug // Probe for joysticks
toolbox()->joystickManager()->discoverJoysticks(); toolbox()->joystickManager()->init();
if (_settingsUpgraded) { if (_settingsUpgraded) {
settings.clear(); settings.clear();
......
...@@ -141,6 +141,8 @@ Vehicle::Vehicle(LinkInterface* link, ...@@ -141,6 +141,8 @@ Vehicle::Vehicle(LinkInterface* link,
{ {
_addLink(link); _addLink(link);
connect(_joystickManager, &JoystickManager::activeJoystickChanged, this, &Vehicle::_activeJoystickChanged);
_mavlink = qgcApp()->toolbox()->mavlinkProtocol(); _mavlink = qgcApp()->toolbox()->mavlinkProtocol();
connect(_mavlink, &MAVLinkProtocol::messageReceived, this, &Vehicle::_mavlinkMessageReceived); connect(_mavlink, &MAVLinkProtocol::messageReceived, this, &Vehicle::_mavlinkMessageReceived);
...@@ -1295,6 +1297,12 @@ QStringList Vehicle::joystickModes(void) ...@@ -1295,6 +1297,12 @@ QStringList Vehicle::joystickModes(void)
return list; return list;
} }
void Vehicle::_activeJoystickChanged(void)
{
_loadSettings();
_startJoystick(true);
}
bool Vehicle::joystickEnabled(void) bool Vehicle::joystickEnabled(void)
{ {
return _joystickEnabled; return _joystickEnabled;
......
...@@ -690,6 +690,8 @@ private slots: ...@@ -690,6 +690,8 @@ private slots:
void _newGeoFenceAvailable(void); void _newGeoFenceAvailable(void);
void _sendMavCommandAgain(void); void _sendMavCommandAgain(void);
void _activeJoystickChanged(void);
private: private:
bool _containsLink(LinkInterface* link); bool _containsLink(LinkInterface* link);
void _addLink(LinkInterface* link); void _addLink(LinkInterface* link);
......
...@@ -27,6 +27,16 @@ SetupPage { ...@@ -27,6 +27,16 @@ SetupPage {
pageName: qsTr("Joystick") pageName: qsTr("Joystick")
pageDescription: qsTr("Joystick Setup is used to configure a calibrate joysticks.") pageDescription: qsTr("Joystick Setup is used to configure a calibrate joysticks.")
Connections {
target: joystickManager
onAvailableJoysticksChanged: {
if( joystickManager.joysticks.length == 0 ) {
summaryButton.checked = true
setupView.showSummaryPanel()
}
}
}
Component { Component {
id: pageComponent id: pageComponent
...@@ -358,11 +368,20 @@ SetupPage { ...@@ -358,11 +368,20 @@ SetupPage {
QGCCheckBox { QGCCheckBox {
enabled: checked || _activeJoystick.calibrated id: enabledCheckBox
enabled: _activeJoystick.calibrated
text: _activeJoystick.calibrated ? qsTr("Enable joystick input") : qsTr("Enable not allowed (Calibrate First)") text: _activeJoystick.calibrated ? qsTr("Enable joystick input") : qsTr("Enable not allowed (Calibrate First)")
checked: _activeVehicle.joystickEnabled checked: _activeVehicle.joystickEnabled
onClicked: _activeVehicle.joystickEnabled = checked onClicked: _activeVehicle.joystickEnabled = checked
Connections {
target: joystickManager
onActiveJoystickChanged: {
enabledCheckBox.checked = Qt.binding(function() { return _activeJoystick.calibrated && _activeVehicle.joystickEnabled })
}
}
} }
Row { Row {
...@@ -390,6 +409,18 @@ SetupPage { ...@@ -390,6 +409,18 @@ SetupPage {
joystickCombo.currentIndex = index joystickCombo.currentIndex = index
} }
} }
Connections {
target: joystickManager
onAvailableJoysticksChanged: {
var index = joystickCombo.find(joystickManager.activeJoystickName)
if (index == -1) {
console.warn(qsTr("Active joystick name not in combo"), joystickManager.activeJoystickName)
} else {
joystickCombo.currentIndex = index
}
}
}
} }
} }
......
...@@ -69,7 +69,9 @@ void JoystickConfigController::start(void) ...@@ -69,7 +69,9 @@ void JoystickConfigController::start(void)
JoystickConfigController::~JoystickConfigController() JoystickConfigController::~JoystickConfigController()
{ {
if(_activeJoystick) {
_activeJoystick->stopCalibrationMode(Joystick::CalibrationModeMonitor); _activeJoystick->stopCalibrationMode(Joystick::CalibrationModeMonitor);
}
} }
/// @brief Returns the state machine entry for the specified state. /// @brief Returns the state machine entry for the specified state.
...@@ -491,10 +493,11 @@ void JoystickConfigController::_setInternalCalibrationValuesFromSettings(void) ...@@ -491,10 +493,11 @@ void JoystickConfigController::_setInternalCalibrationValuesFromSettings(void)
int paramAxis; int paramAxis;
paramAxis = joystick->getFunctionAxis((Joystick::AxisFunction_t)function); paramAxis = joystick->getFunctionAxis((Joystick::AxisFunction_t)function);
if(paramAxis >= 0) {
_rgFunctionAxisMapping[function] = paramAxis; _rgFunctionAxisMapping[function] = paramAxis;
_rgAxisInfo[paramAxis].function = (Joystick::AxisFunction_t)function; _rgAxisInfo[paramAxis].function = (Joystick::AxisFunction_t)function;
} }
}
_signalAllAttiudeValueChanges(); _signalAllAttiudeValueChanges();
} }
...@@ -809,9 +812,11 @@ void JoystickConfigController::_activeJoystickChanged(Joystick* joystick) ...@@ -809,9 +812,11 @@ void JoystickConfigController::_activeJoystickChanged(Joystick* joystick)
if (_activeJoystick) { if (_activeJoystick) {
joystickTransition = true; joystickTransition = true;
disconnect(_activeJoystick, &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged); disconnect(_activeJoystick, &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged);
delete _rgAxisInfo; // This will reset _rgFunctionAxis values to -1 to prevent out-of-bounds accesses
delete _axisValueSave; _resetInternalCalibrationValues();
delete _axisRawValue; delete[] _rgAxisInfo;
delete[] _axisValueSave;
delete[] _axisRawValue;
_axisCount = 0; _axisCount = 0;
_activeJoystick = NULL; _activeJoystick = NULL;
} }
...@@ -826,6 +831,7 @@ void JoystickConfigController::_activeJoystickChanged(Joystick* joystick) ...@@ -826,6 +831,7 @@ void JoystickConfigController::_activeJoystickChanged(Joystick* joystick)
_rgAxisInfo = new struct AxisInfo[_axisCount]; _rgAxisInfo = new struct AxisInfo[_axisCount];
_axisValueSave = new int[_axisCount]; _axisValueSave = new int[_axisCount];
_axisRawValue = new int[_axisCount]; _axisRawValue = new int[_axisCount];
_setInternalCalibrationValuesFromSettings();
connect(_activeJoystick, &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged); connect(_activeJoystick, &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment