JoystickSDL.cc 6.5 KB
Newer Older
1 2 3 4 5
#include "JoystickSDL.h"

#include "QGCApplication.h"

#include <QQmlEngine>
6
#include <QTextStream>
7

8
JoystickSDL::JoystickSDL(const QString& name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController, MultiVehicleManager* multiVehicleManager)
9
    : Joystick(name,axisCount,buttonCount,hatCount,multiVehicleManager)
10
    , _isGameController(isGameController)
11 12
    , _index(index)
{
13
    if(_isGameController) _setDefaultCalibration();
14 15
}

Jacob Walser's avatar
Jacob Walser committed
16
bool JoystickSDL::init(void) {
17
    if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0) {
Jacob Walser's avatar
Jacob Walser committed
18
        SDL_JoystickEventState(SDL_ENABLE);
19
        qWarning() << "Couldn't initialize SimpleDirectMediaLayer:" << SDL_GetError();
Jacob Walser's avatar
Jacob Walser committed
20
        return false;
21
    }
22
    _loadGameControllerMappings();
Jacob Walser's avatar
Jacob Walser committed
23 24 25 26 27 28 29
    return true;
}

QMap<QString, Joystick*> JoystickSDL::discover(MultiVehicleManager* _multiVehicleManager) {
    static QMap<QString, Joystick*> ret;

    QMap<QString,Joystick*> newRet;
30

31 32 33 34 35
    // Load available joysticks

    qCDebug(JoystickLog) << "Available joysticks";

    for (int i=0; i<SDL_NumJoysticks(); i++) {
36 37
        QString name = SDL_JoystickNameForIndex(i);

38
        if (!ret.contains(name)) {
39
            int axisCount, buttonCount, hatCount;
40
            bool isGameController;
41

42

43 44 45 46 47 48 49
            if (SDL_IsGameController(i)) {
                isGameController = true;
                axisCount = SDL_CONTROLLER_AXIS_MAX;
                buttonCount = SDL_CONTROLLER_BUTTON_MAX;
                hatCount = 0;
            } else {
                isGameController = false;
50 51 52 53 54 55 56 57 58 59 60 61
                if (SDL_Joystick* sdlJoystick = SDL_JoystickOpen(i)) {
                    SDL_ClearError();
                    axisCount = SDL_JoystickNumAxes(sdlJoystick);
                    buttonCount = SDL_JoystickNumButtons(sdlJoystick);
                    hatCount = SDL_JoystickNumHats(sdlJoystick);
                    if (axisCount < 0 || buttonCount < 0 || hatCount < 0) {
                        qCWarning(JoystickLog) << "\t libsdl error parsing joystick features:" << SDL_GetError();
                    }
                    SDL_JoystickClose(sdlJoystick);
                } else {
                    qCWarning(JoystickLog) << "\t libsdl failed opening joystick" << qPrintable(name) << "error:" << SDL_GetError();
                    continue;
62
                }
63 64
            }

65
            qCDebug(JoystickLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount << "hats:" << hatCount << "isGC:" << isGameController;
66

67
            // Check for joysticks with duplicate names and differentiate the keys when necessary.
68 69 70 71 72 73 74 75 76
            // This is required when using an Xbox 360 wireless receiver that always identifies as
            // 4 individual joysticks, regardless of how many joysticks are actually connected to the
            // receiver. Using GUID does not help, all of these devices present the same GUID.
            QString originalName = name;
            uint8_t duplicateIdx = 1;
            while (newRet[name]) {
                name = QString("%1 %2").arg(originalName).arg(duplicateIdx++);
            }

Jacob Walser's avatar
Jacob Walser committed
77
            newRet[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController, _multiVehicleManager);
78
        } else {
Jacob Walser's avatar
Jacob Walser committed
79
            newRet[name] = ret[name];
80
            JoystickSDL *j = static_cast<JoystickSDL*>(newRet[name]);
81 82
            if (j->index() != i) {
                j->setIndex(i); // This joystick index has been remapped by SDL
Jacob Walser's avatar
Jacob Walser committed
83 84 85 86
            }
            // 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);
87 88 89
            qCDebug(JoystickLog) << "\tSkipping duplicate" << name;
        }
    }
Jacob Walser's avatar
Jacob Walser committed
90 91 92 93 94 95

    if (!newRet.count()) {
        qCDebug(JoystickLog) << "\tnone found";
    }

    ret = newRet;
96 97 98
    return ret;
}

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
void JoystickSDL::_loadGameControllerMappings(void) {
    QFile file(":/db/mapping/joystick/gamecontrollerdb.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qWarning() << "Couldn't load GameController mapping database.";
        return;
    }

    QTextStream s(&file);

    while (!s.atEnd()) {
        SDL_GameControllerAddMapping(s.readLine().toStdString().c_str());
    }
}

Gregory Dymarek's avatar
Gregory Dymarek committed
114
bool JoystickSDL::_open(void) {
115 116 117 118 119 120
    if ( _isGameController ) {
        sdlController = SDL_GameControllerOpen(_index);
        sdlJoystick = SDL_GameControllerGetJoystick(sdlController);
    } else {
        sdlJoystick = SDL_JoystickOpen(_index);
    }
121 122

    if (!sdlJoystick) {
Gregory Dymarek's avatar
Gregory Dymarek committed
123 124
        qCWarning(JoystickLog) << "SDL_JoystickOpen failed:" << SDL_GetError();
        return false;
125 126
    }

Jacob Walser's avatar
Jacob Walser committed
127 128
    qCDebug(JoystickLog) << "Opened joystick at" << sdlJoystick;

129 130 131
    return true;
}

Gregory Dymarek's avatar
Gregory Dymarek committed
132
void JoystickSDL::_close(void) {
133
    if (sdlJoystick == nullptr) {
Jacob Walser's avatar
Jacob Walser committed
134 135 136 137 138 139 140 141 142 143 144 145 146
        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);
Jacob Walser's avatar
Jacob Walser committed
147 148 149 150
            // This segfaults so often, and I've spent so much time trying to find the cause and fix it
            // I think this might be an SDL bug
            // We are much more stable just commenting this out
            //SDL_JoystickClose(sdlJoystick);
Jacob Walser's avatar
Jacob Walser committed
151 152 153
        }
    }

154 155
    sdlJoystick   = nullptr;
    sdlController = nullptr;
156 157
}

Gregory Dymarek's avatar
Gregory Dymarek committed
158
bool JoystickSDL::_update(void)
159 160
{
    SDL_JoystickUpdate();
161
    SDL_GameControllerUpdate();
162 163 164
    return true;
}

Gregory Dymarek's avatar
Gregory Dymarek committed
165
bool JoystickSDL::_getButton(int i) {
166 167
    if (_isGameController) {
        return SDL_GameControllerGetButton(sdlController, SDL_GameControllerButton(i)) == 1;
168
    } else {
169
        return SDL_JoystickGetButton(sdlJoystick, i) == 1;
170
    }
171 172
}

Gregory Dymarek's avatar
Gregory Dymarek committed
173
int JoystickSDL::_getAxis(int i) {
174
    if (_isGameController) {
175 176 177 178
        return SDL_GameControllerGetAxis(sdlController, SDL_GameControllerAxis(i));
    } else {
        return SDL_JoystickGetAxis(sdlJoystick, i);
    }
179 180
}

181
bool JoystickSDL::_getHat(int hat, int i) {
182
    uint8_t hatButtons[] = {SDL_HAT_UP,SDL_HAT_DOWN,SDL_HAT_LEFT,SDL_HAT_RIGHT};
183 184
    if (i < int(sizeof(hatButtons))) {
        return (SDL_JoystickGetHat(sdlJoystick, hat) & hatButtons[i]) != 0;
185
    }
186
    return false;
187
}