/* * Copyright (c) 2015-2017 Alex Spataru * * 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 #include #include #include #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::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 SDL_Joysticks::joysticks() { int index = 0; QMap joysticks; for (QMap::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(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; }