Skip to content

Commit

Permalink
[udev] Refactor logic to avoid rapid motor events
Browse files Browse the repository at this point in the history
  • Loading branch information
garbear committed Aug 30, 2016
1 parent ff08865 commit 5ab8d4a
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 62 deletions.
8 changes: 6 additions & 2 deletions src/addon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,19 @@ PERIPHERAL_ERROR GetEvents(unsigned int* event_count, PERIPHERAL_EVENT** events)
if (!event_count || !events)
return PERIPHERAL_ERROR_INVALID_PARAMETERS;

PERIPHERAL_ERROR result = PERIPHERAL_ERROR_FAILED;

std::vector<ADDON::PeripheralEvent> peripheralEvents;
if (CJoystickManager::Get().GetEvents(peripheralEvents))
{
*event_count = peripheralEvents.size();
ADDON::PeripheralEvents::ToStructs(peripheralEvents, events);
return PERIPHERAL_NO_ERROR;
result = PERIPHERAL_NO_ERROR;
}

return PERIPHERAL_ERROR_FAILED;
CJoystickManager::Get().ProcessEvents();

return result;
}

void FreeEvents(unsigned int event_count, PERIPHERAL_EVENT* events)
Expand Down
5 changes: 5 additions & 0 deletions src/api/Joystick.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ namespace JOYSTICK
*/
virtual bool SendEvent(const ADDON::PeripheralEvent& event);

/*!
* Process events sent to the joystick
*/
virtual void ProcessEvents() { }

/*!
* Tries to power off the joystick.
*/
Expand Down
8 changes: 8 additions & 0 deletions src/api/JoystickManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,14 @@ bool CJoystickManager::SendEvent(const ADDON::PeripheralEvent& event)
return bHandled;
}

void CJoystickManager::ProcessEvents()
{
CLockObject lock(m_joystickMutex);

for (const JoystickPtr& joystick : m_joysticks)
joystick->ProcessEvents();
}

void CJoystickManager::TriggerScan(void)
{
if (m_scanner)
Expand Down
5 changes: 5 additions & 0 deletions src/api/JoystickManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ namespace JOYSTICK
*/
bool SendEvent(const ADDON::PeripheralEvent& event);

/*!
* \brief Process events that have arrived since the last call to ProcessEvents()
*/
void ProcessEvents();

/*!
* \brief Trigger a scan for joysticks through the callback
*/
Expand Down
131 changes: 72 additions & 59 deletions src/api/udev/JoystickUdev.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ CJoystickUdev::CJoystickUdev(udev_device* dev, const char* path)
m_fd(INVALID_FD),
m_bInitialized(false),
m_has_set_ff(false),
m_effect(-1)
m_effect(-1),
m_motors(),
m_previousMotors()
{
for (unsigned int i = 0; i < MOTOR_COUNT; i++)
m_motors[i] = 0;
Expand Down Expand Up @@ -99,6 +101,75 @@ void CJoystickUdev::Deinitialize(void)
CJoystick::Deinitialize();
}

void CJoystickUdev::ProcessEvents(void)
{
uint32_t oldStrength = static_cast<uint32_t>(m_previousMotors[MOTOR_STRONG]) +
static_cast<uint32_t>(m_previousMotors[MOTOR_WEAK]);
uint32_t newStrength = static_cast<uint32_t>(m_motors[MOTOR_STRONG]) +
static_cast<uint32_t>(m_motors[MOTOR_WEAK]);

bool bWasPlaying = (oldStrength > 0);
bool bIsPlaying = (newStrength > 0);

if (!bWasPlaying && !bIsPlaying)
{
// Nothing to do
}
else if (!bWasPlaying && bIsPlaying)
{
UpdateMotorState();

// Play effect
Play(true);
}
else if (bWasPlaying && !bIsPlaying)
{
// Stop the effect
Play(false);
}
else
{
if (oldStrength != newStrength)
UpdateMotorState();
}

m_previousMotors = m_motors;
}

void CJoystickUdev::Play(bool bPlayStop)
{
struct input_event play = { { } };

play.type = EV_FF;
play.code = m_effect;
play.value = bPlayStop;

if (write(m_fd, &play, sizeof(play)) < (ssize_t)sizeof(play))
esyslog("[udev]: Failed to play rumble effect on \"%s\"", Name().c_str());
}

void CJoystickUdev::UpdateMotorState()
{
struct ff_effect e = { };

int old_effect = m_has_set_ff ? m_effect : -1;

e.type = FF_RUMBLE;
e.id = old_effect;
e.u.rumble.strong_magnitude = m_motors[MOTOR_STRONG];
e.u.rumble.weak_magnitude = m_motors[MOTOR_WEAK];

if (ioctl(m_fd, EVIOCSFF, &e) < 0)
{
esyslog("Failed to set rumble effect on \"%s\"", Name().c_str());
}
else
{
m_effect = e.id;
m_has_set_ff = true;
}
}

bool CJoystickUdev::ScanEvents(void)
{
input_event events[32];
Expand Down Expand Up @@ -272,65 +343,7 @@ bool CJoystickUdev::SetMotor(unsigned int motorIndex, float magnitude)

uint16_t strength = std::min(0xffff, static_cast<int>(magnitude * 0xffff));

bool bChanged = false;

if (strength != m_motors[motorIndex])
bChanged = true;

if (!bChanged)
return true;

uint32_t oldStrength = static_cast<uint32_t>(m_motors[MOTOR_STRONG]) +
static_cast<uint32_t>(m_motors[MOTOR_WEAK]);

m_motors[motorIndex] = strength;

uint32_t newStrength = static_cast<uint32_t>(m_motors[MOTOR_STRONG]) +
static_cast<uint32_t>(m_motors[MOTOR_WEAK]);

if (newStrength > 0)
{
// Create new or update old playing state
struct ff_effect e = { };

int old_effect = m_has_set_ff ? m_effect : -1;

e.type = FF_RUMBLE;
e.id = old_effect;
e.u.rumble.strong_magnitude = m_motors[MOTOR_STRONG];
e.u.rumble.weak_magnitude = m_motors[MOTOR_WEAK];

if (ioctl(m_fd, EVIOCSFF, &e) < 0)
{
esyslog("Failed to set rumble effect on \"%s\"", Name().c_str());
return false;
}

m_effect = e.id;
m_has_set_ff = true;
}

bool bWasPlaying = !!oldStrength;
bool bIsPlaying = !!newStrength;

if (bWasPlaying != bIsPlaying)
{
struct input_event play = { { } };

play.type = EV_FF;
play.code = m_effect;
play.value = bIsPlaying;

// udev fails to stop playing if event is written too soon after last ioctl
if (!bIsPlaying)
usleep(2500);

if (write(m_fd, &play, sizeof(play)) < (ssize_t)sizeof(play))
{
esyslog("[udev]: Failed to play rumble effect on \"%s\"", Name().c_str());
return false;
}
}

return true;
}
8 changes: 7 additions & 1 deletion src/api/udev/JoystickUdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

#include "api/Joystick.h"

#include <array>
#include <linux/input.h>
#include <sys/types.h>

Expand All @@ -65,13 +66,17 @@ namespace JOYSTICK
virtual bool Equals(const CJoystick* rhs) const override;
virtual bool Initialize(void) override;
virtual void Deinitialize(void) override;
virtual void ProcessEvents(void) override;

protected:
// implementation of CJoystick
virtual bool ScanEvents(void) override;
bool SetMotor(unsigned int motorIndex, float magnitude);

private:
void UpdateMotorState();
void Play(bool bPlayStop);

struct Axis
{
unsigned int axisIndex;
Expand All @@ -93,6 +98,7 @@ namespace JOYSTICK
// Joystick properties
std::map<unsigned int, unsigned int> m_button_bind; // Maps keycodes -> button
std::map<unsigned int, Axis> m_axes_bind; // Maps keycodes -> axis and axis info
uint16_t m_motors[MOTOR_COUNT];
std::array<uint16_t, MOTOR_COUNT> m_motors;
std::array<uint16_t, MOTOR_COUNT> m_previousMotors;
};
}

0 comments on commit 5ab8d4a

Please sign in to comment.