metaforce/Runtime/Input/CInputGenerator.hpp

185 lines
6.1 KiB
C++
Raw Normal View History

2015-08-18 05:54:43 +00:00
#ifndef __RETRO_CINPUTGENERATOR_HPP__
#define __RETRO_CINPUTGENERATOR_HPP__
2015-08-29 01:30:47 +00:00
#include <atomic>
2015-08-28 00:11:31 +00:00
#include <boo/boo.hpp>
2015-08-29 01:30:47 +00:00
#include "CFinalInput.hpp"
#include "CKeyboardMouseController.hpp"
2015-08-28 00:11:31 +00:00
2015-08-18 05:54:43 +00:00
namespace Retro
{
2015-08-29 01:30:47 +00:00
class CArchitectureQueue;
2015-08-18 05:54:43 +00:00
2015-08-29 01:30:47 +00:00
class CInputGenerator : public boo::DeviceFinder
2015-08-18 05:54:43 +00:00
{
2015-08-29 01:30:47 +00:00
enum EStatusChange
{
StatusNoChange = 0,
StatusConnected = 1,
StatusDisconnected = 2
};
/* When the sticks are used as logical (digital) input,
* these thresholds determine the vector magnitude indicating
* the logical state */
float m_leftDiv;
float m_rightDiv;
public:
CInputGenerator(float leftDiv, float rightDiv)
: boo::DeviceFinder({typeid(boo::DolphinSmashAdapter)}),
m_leftDiv(leftDiv),
m_rightDiv(rightDiv) {}
/* Keyboard and mouse events are delivered on the main game
* thread as part of the app's main event loop. The OS is responsible
* for buffering events in its own way, then boo flushes the buffer
* at the start of each frame, invoking these methods. No atomic locking
* is necessary, only absolute state tracking. */
2015-08-28 00:11:31 +00:00
struct WindowCallback : boo::IWindowCallback
{
2015-08-29 01:30:47 +00:00
CKeyboardMouseControllerData m_data;
void mouseDown(const SWindowCoord&, EMouseButton button, EModifierKey)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
m_data.m_mouseButtons[button] = true;
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
void mouseUp(const SWindowCoord&, EMouseButton button, EModifierKey)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
m_data.m_mouseButtons[button] = false;
2015-08-28 00:11:31 +00:00
}
void mouseMove(const SWindowCoord& coord)
{
2015-08-29 01:30:47 +00:00
m_data.m_mouseCoord = coord;
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
void scroll(const SWindowCoord&, const SScrollDelta& scroll)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
m_data.m_accumScroll += scroll;
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
void charKeyDown(unsigned long charCode, EModifierKey, bool)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
charCode = tolower(charCode);
if (charCode > 255)
return;
m_data.m_charKeys[charCode] = true;
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
void charKeyUp(unsigned long charCode, EModifierKey mods)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
charCode = tolower(charCode);
if (charCode > 255)
return;
m_data.m_charKeys[charCode] = false;
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
void specialKeyDown(ESpecialKey key, EModifierKey, bool)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
m_data.m_specialKeys[key] = true;
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
void specialKeyUp(ESpecialKey key, EModifierKey)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
m_data.m_specialKeys[key] = false;
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
void modKeyDown(EModifierKey mod, bool)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
m_data.m_modMask = EModifierKey(m_data.m_modMask | mod);
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
void modKeyUp(EModifierKey mod)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
m_data.m_modMask = EModifierKey(m_data.m_modMask & ~mod);
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
void reset()
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
m_data.m_accumScroll.zeroOut();
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
CFinalInput m_lastUpdate;
const CFinalInput& getFinalInput(unsigned idx, float dt)
{
m_lastUpdate = CFinalInput(idx, dt, m_data, m_lastUpdate);
return m_lastUpdate;
2015-08-28 00:11:31 +00:00
}
} m_windowCb;
2015-08-29 01:30:47 +00:00
/* Input via the smash adapter is received asynchronously on a USB
* report thread. This class atomically exchanges that data to the
* game thread as needed */
2015-08-28 00:11:31 +00:00
struct DolphinSmashAdapterCallback : boo::IDolphinSmashAdapterCallback
{
2015-08-29 01:30:47 +00:00
std::atomic<EStatusChange> m_statusChanges[4];
bool m_connected[4] = {};
boo::DolphinControllerState m_states[4];
std::mutex m_stateLock;
2015-08-28 00:11:31 +00:00
void controllerConnected(unsigned idx, boo::EDolphinControllerType)
{
2015-08-29 01:30:47 +00:00
/* Controller thread */
m_statusChanges[idx].store(StatusConnected);
2015-08-28 00:11:31 +00:00
}
void controllerDisconnected(unsigned idx, boo::EDolphinControllerType)
{
2015-08-29 01:30:47 +00:00
/* Controller thread */
std::unique_lock<std::mutex> lk(m_stateLock);
m_statusChanges[idx].store(StatusDisconnected);
m_states[idx].reset();
2015-08-28 00:11:31 +00:00
}
void controllerUpdate(unsigned idx, boo::EDolphinControllerType,
const boo::DolphinControllerState& state)
{
2015-08-29 01:30:47 +00:00
/* Controller thread */
std::unique_lock<std::mutex> lk(m_stateLock);
m_states[idx] = state;
}
CFinalInput m_lastUpdates[4];
const CFinalInput& getFinalInput(unsigned idx, float dt,
float leftDiv, float rightDiv)
{
/* Game thread */
std::unique_lock<std::mutex> lk(m_stateLock);
boo::DolphinControllerState state = m_states[idx];
lk.unlock();
state.clamp(); /* PADClamp equivalent */
m_lastUpdates[idx] = CFinalInput(idx, dt, state, m_lastUpdates[idx], leftDiv, rightDiv);
return m_lastUpdates[idx];
}
EStatusChange getStatusChange(unsigned idx, bool& connected)
{
/* Game thread */
EStatusChange ch = m_statusChanges[idx].exchange(StatusNoChange);
if (ch == StatusConnected)
m_connected[idx] = true;
else if (ch == StatusDisconnected)
m_connected[idx] = false;
connected = m_connected[idx];
return ch;
}
} m_dolphinCb;
/* Device connection/disconnection events are handled on a separate thread
* using the relevant OS API. This thread blocks in a loop until an event is
* received. Device pointers should only be manipulated by this thread using
* the deviceConnected() and deviceDisconnected() callbacks. */
std::unique_ptr<boo::DolphinSmashAdapter> smashAdapter = NULL;
void deviceConnected(boo::DeviceToken& tok)
2015-08-28 00:11:31 +00:00
{
2015-08-29 01:30:47 +00:00
/* Device listener thread */
if (!smashAdapter)
{
smashAdapter.reset(dynamic_cast<boo::DolphinSmashAdapter*>(tok.openAndGetDevice()));
smashAdapter->setCallback(&m_dolphinCb);
2015-08-28 00:11:31 +00:00
}
2015-08-29 01:30:47 +00:00
}
void deviceDisconnected(boo::DeviceToken&, boo::DeviceBase* device)
{
if (smashAdapter.get() == device)
smashAdapter.reset(nullptr);
}
/* This is where the game thread enters */
void Update(float dt, CArchitectureQueue& queue);
2015-08-28 00:11:31 +00:00
2015-08-18 05:54:43 +00:00
};
}
#endif // __RETRO_CINPUTGENERATOR_HPP__