// // InputSDL.cpp // konvergo // // Created by Lionel CHAZALLON on 16/10/2014. // // #include #include #include "InputSDL.h" #include #include /////////////////////////////////////////////////////////////////////////////////////////////////// bool InputSDLWorker::initialize() { // close if it was previously inited close(); // init SDL if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { qCritical() << "SDL failed to initialize : " << SDL_GetError(); return false; } SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); SDL_JoystickEventState(SDL_ENABLE); refreshJoystickList(); return true; } ////////////////////////////////////////////////////////////////////////////////////////////////// void InputSDLWorker::close() { if (SDL_WasInit(SDL_INIT_JOYSTICK)) { qInfo() << "SDL is closing."; // we need to close all the openned joysticks here and then exit the thread for (int joyid = 0; joyid < m_joysticks.size(); joyid++) SDL_JoystickClose(m_joysticks[joyid]); SDL_Event event; event.type = SDL_QUIT; SDL_PushEvent(&event); } } /////////////////////////////////////////////////////////////////////////////////////////////////// QString InputSDLWorker::nameForId(SDL_JoystickID id) { if (m_joysticks.contains(id)) return SDL_JoystickName(m_joysticks[id]); return "unknown joystick"; } /////////////////////////////////////////////////////////////////////////////////////////////////// void InputSDLWorker::run() { QElapsedTimer polltimer; while (true) { SDL_Event event; // startpoint for polling loop polltimer.restart(); while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: SDL_Quit(); return; break; case SDL_JOYBUTTONDOWN: { emit receivedInput(nameForId(event.jbutton.which), QString("KEY_BUTTON_%1").arg(event.jbutton.button), InputBase::KeyDown); break; } case SDL_JOYBUTTONUP: { emit receivedInput(nameForId(event.jbutton.which), QString("KEY_BUTTON_%1").arg(event.jbutton.button), InputBase::KeyUp); break; } case SDL_JOYDEVICEADDED: { qInfo() << "SDL detected device was added."; refreshJoystickList(); break; } case SDL_JOYDEVICEREMOVED: { qInfo() << "SDL detected device was removed."; refreshJoystickList(); break; } case SDL_JOYHATMOTION: { QString hatName("KEY_HAT_"); bool pressed = true; switch (event.jhat.value) { case SDL_HAT_CENTERED: if (!m_lastHat.isEmpty()) hatName = m_lastHat; else hatName += "CENTERED"; pressed = false; break; case SDL_HAT_UP: hatName += "UP"; break; case SDL_HAT_DOWN: hatName += "DOWN"; break; case SDL_HAT_RIGHT: hatName += "RIGHT"; break; case SDL_HAT_LEFT: hatName += "LEFT"; break; default: break; } m_lastHat = hatName; emit receivedInput(nameForId(event.jhat.which), hatName, pressed ? InputBase::KeyDown : InputBase::KeyUp); break; } case SDL_JOYAXISMOTION: { auto axis = event.jaxis.axis; auto value = event.jaxis.value; qDebug() << "JoyAxisMotion:" << axis << value; // handle the Digital conversion of the analog axis if (std::abs(value) > 32768 / 2) { bool up = value < 0; if (!m_axisState.contains(axis)) { emit receivedInput(nameForId(event.jaxis.which), QString("KEY_AXIS_%1_%2").arg(axis).arg(up ? "UP" : "DOWN"), InputBase::KeyDown); m_axisState.insert(axis, up); } else if (m_axisState.value(axis) != up) { emit receivedInput(nameForId(event.jaxis.which), QString("KEY_AXIS_%1_%2").arg(axis).arg(m_axisState.value(axis) ? "UP" : "DOWN"), InputBase::KeyUp); m_axisState.remove(axis); } } else if (std::abs(value) < 10000 && m_axisState.contains(axis)) // back to the center. { emit receivedInput(nameForId(event.jaxis.which), QString("KEY_AXIS_%1_%2").arg(axis).arg(m_axisState.value(axis) ? "UP" : "DOWN"), InputBase::KeyUp); m_axisState.remove(axis); } break; } default: { qWarning() << "Unhandled SDL event:" << event.type; break; } } } // make the poll time fixed to SDL_POLL_TIME if (polltimer.elapsed() < SDL_POLL_TIME) QThread::msleep(SDL_POLL_TIME - polltimer.elapsed()); } } ////////////////////////////////////////////////////////////////////////////////////////////////// void InputSDLWorker::refreshJoystickList() { // close all openned joysticks SDLJoystickMapIterator it = m_joysticks.constBegin(); while (it != m_joysticks.constEnd()) { if (SDL_JoystickGetAttached(m_joysticks[it.key()])) SDL_JoystickClose(m_joysticks[it.key()]); it++; } m_joysticks.clear(); // list all the joysticks and open them int numJoysticks = SDL_NumJoysticks(); qInfo() << "SDL found " << numJoysticks << " joysticks"; for (int joyid = 0; joyid < numJoysticks; joyid++) { SDL_Joystick* joystick = SDL_JoystickOpen(joyid); if (joystick) { int instanceid = SDL_JoystickInstanceID(joystick); qInfo() << "JoyStick #" << instanceid << " is " << SDL_JoystickName(joystick) << " with " << SDL_JoystickNumButtons(joystick) << " buttons and " << SDL_JoystickNumAxes(joystick) << "axes"; m_joysticks[instanceid] = joystick; m_axisState.clear(); } } } /////////////////////////////////////////////////////////////////////////////////////////////////// InputSDL::InputSDL(QObject* parent) : InputBase(parent) { m_thread = new QThread(this); m_sdlworker = new InputSDLWorker(nullptr); m_sdlworker->moveToThread(m_thread); connect(this, &InputSDL::run, m_sdlworker, &InputSDLWorker::run); connect(m_sdlworker, &InputSDLWorker::receivedInput, this, &InputBase::receivedInput); m_thread->start(); } /////////////////////////////////////////////////////////////////////////////////////////////////// InputSDL::~InputSDL() { close(); if (m_thread->isRunning()) { m_thread->exit(0); m_thread->wait(); } } ////////////////////////////////////////////////////////////////////////////////////////////////// bool InputSDL::initInput() { bool retValue; QMetaObject::invokeMethod(m_sdlworker, "initialize", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, retValue)); if (retValue) emit run(); return retValue; } ////////////////////////////////////////////////////////////////////////////////////////////////// void InputSDL::close() { QMetaObject::invokeMethod(m_sdlworker, "close", Qt::DirectConnection); }