From 00c737b880865e874391af5681d8247f1cd6d4ad Mon Sep 17 00:00:00 2001 From: Piotr Dziwinski Date: Sat, 30 Jun 2012 12:26:40 +0200 Subject: Joystick polling with timer - added joystick polling through timer - updated documentation on CApplication class --- src/app/app.cpp | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++----- src/app/app.h | 46 +++++++++++++++++++++--- 2 files changed, 138 insertions(+), 14 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 5612c17..2be58ff 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -29,6 +29,13 @@ #include +//! Interval of timer called to update joystick state +const int JOYSTICK_TIMER_INTERVAL = 1000/30; + +//! Function called by the timer +Uint32 JoystickTimerCallback(Uint32 interval, void *); + + /** * \struct ApplicationPrivate * \brief Private data of CApplication class @@ -45,6 +52,8 @@ struct ApplicationPrivate SDL_Joystick *joystick; //! Index of joystick device int joystickIndex; + //! Id of joystick timer + SDL_TimerID joystickTimer; ApplicationPrivate() { @@ -52,12 +61,19 @@ struct ApplicationPrivate surface = NULL; joystick = NULL; joystickIndex = 0; + joystickTimer = 0; } }; +CApplication* CApplication::m_appInstance = NULL; + + CApplication::CApplication() { + assert(m_appInstance == NULL); + m_appInstance = this; + m_private = new ApplicationPrivate(); m_exitCode = 0; @@ -80,11 +96,6 @@ CApplication::CApplication() m_time = 0.0f; - for (int i = 0; i < 32; i++) - { - m_joyButton[i] = false; - } - m_windowTitle = "COLOBOT"; m_showStats = false; @@ -150,7 +161,7 @@ bool CApplication::Create() /* SDL initialization sequence */ - Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_JOYSTICK; + Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER; if (SDL_Init(initFlags) < 0) { @@ -213,8 +224,8 @@ bool CApplication::Create() // Enable translating key codes of key press events to unicode chars SDL_EnableUNICODE(1); - // Enable joystick event generation - SDL_JoystickEventState(SDL_ENABLE); + // Don't generate joystick events + SDL_JoystickEventState(SDL_IGNORE); // For now, enable joystick for testing @@ -255,13 +266,90 @@ bool CApplication::OpenJoystick() if (m_private->joystick == NULL) return false; + // Create the vectors with joystick axis & button states to exactly the required size + m_joyAxeState = std::vector(SDL_JoystickNumAxes(m_private->joystick), 0); + m_joyButtonState = std::vector(SDL_JoystickNumButtons(m_private->joystick), false); + + // Create a timer for polling joystick state + m_private->joystickTimer = SDL_AddTimer(JOYSTICK_TIMER_INTERVAL, JoystickTimerCallback, NULL); + return true; } void CApplication::CloseJoystick() { + // Timer will remove itself automatically + SDL_JoystickClose(m_private->joystick); + m_private->joystick = NULL; +} + +Uint32 JoystickTimerCallback(Uint32 interval, void *) +{ + CApplication *app = CApplication::RetInstance(); + if ((app == NULL) || (! app->RetJoystickEnabled())) + return 0; // don't run the timer again + + app->UpdateJoystick(); + + return interval; // run for the same interval again +} + +/** Updates the state info in CApplication and on change, creates SDL events and pushes them to SDL event queue. + This way, the events get handled properly in the main event loop and besides, SDL_PushEvent() ensures thread-safety. */ +void CApplication::UpdateJoystick() +{ + if (! m_joystickEnabled) + return; + + SDL_JoystickUpdate(); + + for (int axis = 0; axis < (int) m_joyAxeState.size(); ++axis) + { + int newValue = SDL_JoystickGetAxis(m_private->joystick, axis); + + if (m_joyAxeState[axis] != newValue) + { + m_joyAxeState[axis] = newValue; + + SDL_Event joyAxisEvent; + + joyAxisEvent.jaxis.type = SDL_JOYAXISMOTION; + joyAxisEvent.jaxis.which = 0; + joyAxisEvent.jaxis.axis = axis; + joyAxisEvent.jaxis.value = newValue; + + SDL_PushEvent(&joyAxisEvent); + } + } + + for (int button = 0; button < (int) m_joyButtonState.size(); ++button) + { + bool newValue = SDL_JoystickGetButton(m_private->joystick, button) == 1; + + if (m_joyButtonState[button] != newValue) + { + m_joyButtonState[button] = newValue; + + SDL_Event joyButtonEvent; + + if (newValue) + { + joyButtonEvent.jbutton.type = SDL_JOYBUTTONDOWN; + joyButtonEvent.jbutton.state = SDL_PRESSED; + } + else + { + joyButtonEvent.jbutton.type = SDL_JOYBUTTONUP; + joyButtonEvent.jbutton.state = SDL_RELEASED; + } + joyButtonEvent.jbutton.which = 0; + joyButtonEvent.jbutton.button = button; + + SDL_PushEvent(&joyButtonEvent); + } + } } int CApplication::Run() @@ -444,7 +532,7 @@ void CApplication::ProcessEvent(Event event) case EVENT_JOY_BUTTON_UP: printf("EVENT_JOY_BUTTON_%s:\n", (event.type == EVENT_JOY_BUTTON_DOWN) ? "DOWN" : "UP"); printf(" button = %d\n", event.joyButton.button); - printf(" state = %s\n", (event.mouseButton.state == STATE_PRESSED) ? "STATE_PRESSED" : "STATE_RELEASED"); + printf(" state = %s\n", (event.joyButton.state == STATE_PRESSED) ? "STATE_PRESSED" : "STATE_RELEASED"); break; default: break; diff --git a/src/app/app.h b/src/app/app.h index 9d689e2..b2d9135 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -25,6 +25,7 @@ #include "graphics/common/engine.h" #include +#include class CInstanceManager; @@ -39,10 +40,32 @@ struct ApplicationPrivate; * \class CApplication * \brief Main application * - * This class is responsible for creating and handling main application window, - * receiving events, etc. + * This class is responsible for main application execution, including creating + * and handling main application window, receiving events, etc. + * + * It is a singleton class with only one instance that can be created. + * + * Creation of other main objects + * + * The class creates the only instance of CInstanceManager, CEventQueue, CEngine, + * CRobotMain and CSound classes. + * + * Window management + * + * The class is responsible for creating app window, setting and changing the video mode, + * setting the position of mouse and changing the cursor, grabbing and writing screenshots. + * + * Events + * + * Events are taken from SDL event queue and either handled by CApplication or translated + * to common events from src/common.h and pushed to global event queue CEventQueue. + * Joystick events are generated somewhat differently, by running a separate timer, + * polling the device for changes and synthesising events on change. It avoids flooding + * the event queue with too many joystick events and the granularity of the timer can be + * adjusted. + * + * The events are further handled in CRobotMain class. * - * ... */ class CApplication { @@ -52,6 +75,10 @@ public: //! Destructor ~CApplication(); + //! Returns the only CApplication instance + static CApplication* RetInstance() + { return m_appInstance; } + public: //! Parses commandline arguments Error ParseArguments(int argc, char *argv[]); @@ -66,6 +93,9 @@ public: //! Updates the simulation state void StepSimulation(float rTime); + //! Polls the state of joystick axes and buttons + void UpdateJoystick(); + void SetShowStat(bool show); bool RetShowStat(); @@ -124,6 +154,9 @@ protected: void OutputText(long x, long y, char* str); protected: + //! The only instance of CApplication + static CApplication* m_appInstance; + //! Instance manager CInstanceManager* m_iMan; //! Private (SDL-dependent data) ApplicationPrivate* m_private; @@ -138,7 +171,6 @@ protected: //! Main class of the proper game engine CRobotMain* m_robotMain; - //! Code to return at exit int m_exitCode; @@ -164,10 +196,14 @@ protected: int m_keyState; Math::Vector m_axeKey; Math::Vector m_axeJoy; - bool m_joyButton[32]; Math::Point m_mousePos; long m_mouseWheel; + //! Current state of joystick axes; may be updated from another thread + std::vector m_joyAxeState; + //! Current state of joystick buttons; may be updated from another thread + std::vector m_joyButtonState; + float m_time; long m_key[50][2]; }; -- cgit v1.2.3-1-g7c22