Don't always poll DI for joystick changes in the main thread. Instead, poll the HID registry value, then poll DI for 15 seconds to catch the joystick change.

This commit is contained in:
Chris Danford
2006-03-22 22:39:35 +00:00
parent 4a998771eb
commit c0dfb2453d
2 changed files with 57 additions and 31 deletions
@@ -67,6 +67,13 @@ static BOOL CALLBACK CountDevicesCallback( const DIDEVICEINSTANCE *pdidInstance,
return DIENUM_CONTINUE;
}
static int GetNumHidDevices()
{
int i = 0;
RegistryAccess::GetRegValue( "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\HidUsb\\Enum", "Count", i, false ); // don't warn on error
return i;
}
static int GetNumJoysticksSlow()
{
int iCount = 0;
@@ -124,8 +131,9 @@ InputHandler_DInput::InputHandler_DInput()
Devices[i].buffered? "buffered": "unbuffered" );
}
m_iCurrentNumJoysticks = GetNumJoysticksSlow();
m_iLastSeenNumJoysticks = m_iCurrentNumJoysticks;
m_iLastSeenNumHidDevices = GetNumHidDevices();
m_iNumTimesLeftToPollForJoysticksChanged = 0;
m_iLastSeenNumJoysticks = GetNumJoysticksSlow();
StartThread();
}
@@ -137,10 +145,6 @@ void InputHandler_DInput::StartThread()
{
m_InputThread.SetName( "DirectInput thread" );
m_InputThread.Create( InputThread_Start, this );
// TODO: Check for changed devices with non-threaded input?
m_DevicesChangedThread.SetName( "DI DevicesChanged thread" );
m_DevicesChangedThread.Create( DevicesChangedThread_Start, this );
}
}
@@ -153,12 +157,6 @@ void InputHandler_DInput::ShutdownThread()
m_InputThread.Wait();
LOG->Trace( "DirectInput thread shut down." );
}
if( m_DevicesChangedThread.IsCreated() )
{
LOG->Trace( "Shutting down DI DevicesChanged thread ..." );
m_DevicesChangedThread.Wait();
LOG->Trace( "DI DevicesChanged thread shut down." );
}
m_bShutdown = false;
}
@@ -524,24 +522,55 @@ void InputHandler_DInput::Update()
InputHandler::UpdateTimer();
}
const float POLL_FOR_JOYSTICK_CHANGES_LENGTH_SECONDS = 15.0f;
const float POLL_FOR_JOYSTICK_CHANGES_EVERY_SECONDS = 0.25f;
bool InputHandler_DInput::DevicesChanged()
{
int iOldNumJoysticks = m_iLastSeenNumJoysticks;
m_iLastSeenNumJoysticks = m_iCurrentNumJoysticks;
return iOldNumJoysticks != m_iLastSeenNumJoysticks;
}
//
// GetNumJoysticksSlow() blocks DirectInput for a while even if called from a
// different thread, so we can't poll with it.
// GetNumHidDevices() is fast, but sometimes the DirectInput joysticks haven't updated by
// the time the HID registry value changes.
// So, poll using GetNumHidDevices(). When that changes, poll using GetNumJoysticksSlow()
// for a little while to give DirectInput time to catch up. On this XP machine, it takes
// 2-10 DirectInput polls (0.5-2.5 seconds) to catch a newly installed device after the
// registry value changes, and catches non-new plugged/unplugged devices on the first
// DirectInputPoll.
// Note that this "poll for N seconds" method will not work if the Add New Hardware wizard
// halts device installation to wait for a driver. Most of the joysticks people would
// want to use don't prompt for a driver though and the wizard adds them pretty quickly.
//
void InputHandler_DInput::DevicesChangedThreadMain()
{
while( !m_bShutdown )
int iOldNumHidDevices = m_iLastSeenNumHidDevices;
m_iLastSeenNumHidDevices = GetNumHidDevices();
if( iOldNumHidDevices != m_iLastSeenNumHidDevices )
{
m_iCurrentNumJoysticks = GetNumJoysticksSlow();
CHECKPOINT;
// We'll miss a device if one is plugged in and removed in the same 1/10th of a second.
// This is hard to do in practice even on purpose.
usleep( 100*1000 ); // .1s
LOG->Warn( "HID devices changes" );
m_iNumTimesLeftToPollForJoysticksChanged = (int)(POLL_FOR_JOYSTICK_CHANGES_LENGTH_SECONDS / POLL_FOR_JOYSTICK_CHANGES_EVERY_SECONDS);
}
CHECKPOINT;
if( m_iNumTimesLeftToPollForJoysticksChanged > 0 )
{
static RageTimer timerPollJoysticks;
if( timerPollJoysticks.Ago() >= POLL_FOR_JOYSTICK_CHANGES_EVERY_SECONDS )
{
m_iNumTimesLeftToPollForJoysticksChanged--;
timerPollJoysticks.Touch();
LOG->Warn( "polling for joystick changes" );
int iOldNumJoysticks = m_iLastSeenNumJoysticks;
m_iLastSeenNumJoysticks = GetNumJoysticksSlow();
if( iOldNumJoysticks != m_iLastSeenNumJoysticks )
{
LOG->Warn( "joysticks changed" );
m_iNumTimesLeftToPollForJoysticksChanged = 0;
return true;
}
}
}
return false;
}
void InputHandler_DInput::InputThreadMain()
@@ -17,21 +17,18 @@ public:
private:
RageThread m_InputThread;
RageThread m_DevicesChangedThread;
bool m_bShutdown;
// Use these to figure out if a joystick was plugged/unplugged
int m_iCurrentNumJoysticks;
int m_iLastSeenNumJoysticks;
int m_iLastSeenNumHidDevices; // This changes first on plug/unplug
int m_iNumTimesLeftToPollForJoysticksChanged;
int m_iLastSeenNumJoysticks; // This changes sometime after m_iLastSeenNumHidDevices
void UpdatePolled( DIDevice &device, const RageTimer &tm );
void UpdateBuffered( DIDevice &device, const RageTimer &tm );
void PollAndAcquireDevices( bool bBuffered );
static int InputThread_Start( void *p ) { ((InputHandler_DInput *) p)->InputThreadMain(); return 0; }
static int DevicesChangedThread_Start( void *p ) { ((InputHandler_DInput *) p)->DevicesChangedThreadMain(); return 0; }
void InputThreadMain();
void DevicesChangedThreadMain();
void StartThread();
void ShutdownThread();