smsvn -> ssc-hg glue: rearrange directory structure
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
#include "global.h"
|
||||
#include "InputFilter.h"
|
||||
#include "RageUtil.h"
|
||||
#include "InputHandler.h"
|
||||
#include "RageLog.h"
|
||||
#include "LocalizedString.h"
|
||||
#include "arch/arch_default.h"
|
||||
#include "InputHandler_MonkeyKeyboard.h"
|
||||
#include "Foreach.h"
|
||||
|
||||
void InputHandler::UpdateTimer()
|
||||
{
|
||||
m_LastUpdate.Touch();
|
||||
m_iInputsSinceUpdate = 0;
|
||||
}
|
||||
|
||||
void InputHandler::ButtonPressed( DeviceInput di )
|
||||
{
|
||||
if( di.ts.IsZero() )
|
||||
{
|
||||
di.ts = m_LastUpdate.Half();
|
||||
++m_iInputsSinceUpdate;
|
||||
}
|
||||
|
||||
INPUTFILTER->ButtonPressed( di );
|
||||
|
||||
if( m_iInputsSinceUpdate >= 1000 )
|
||||
{
|
||||
/*
|
||||
* We haven't received an update in a long time, so warn about it. We expect to receive
|
||||
* input events before the first UpdateTimer call only on the first update. Leave
|
||||
* m_iInputsSinceUpdate where it is, so we only warn once. Only updates that didn't provide
|
||||
* a timestamp are counted; if the driver provides its own timestamps, UpdateTimer is
|
||||
* optional.
|
||||
*/
|
||||
LOG->Warn( "InputHandler::ButtonPressed: Driver sent many updates without calling UpdateTimer" );
|
||||
FAIL_M("x");
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t InputHandler::DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers )
|
||||
{
|
||||
wchar_t c = L'\0';
|
||||
switch( button )
|
||||
{
|
||||
default:
|
||||
if( button < 127 )
|
||||
c = (wchar_t) button;
|
||||
else if( button >= KEY_KP_C0 && button <= KEY_KP_C9 )
|
||||
c =(wchar_t) (button - KEY_KP_C0) + '0';
|
||||
break;
|
||||
case KEY_KP_SLASH: c = L'/'; break;
|
||||
case KEY_KP_ASTERISK: c = L'*'; break;
|
||||
case KEY_KP_HYPHEN: c = L'-'; break;
|
||||
case KEY_KP_PLUS: c = L'+'; break;
|
||||
case KEY_KP_PERIOD: c = L'.'; break;
|
||||
case KEY_KP_EQUAL: c = L'='; break;
|
||||
}
|
||||
|
||||
// Handle some default US keyboard modifiers for derived InputHandlers that
|
||||
// don't implement DeviceButtonToChar.
|
||||
if( bUseCurrentKeyModifiers )
|
||||
{
|
||||
bool bHoldingShift =
|
||||
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT)) ||
|
||||
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT));
|
||||
|
||||
bool bHoldingCtrl =
|
||||
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL)) ||
|
||||
INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL));
|
||||
|
||||
if( bHoldingShift && !bHoldingCtrl )
|
||||
{
|
||||
MakeUpper( &c, 1 );
|
||||
|
||||
switch( c )
|
||||
{
|
||||
case L'`': c = L'~'; break;
|
||||
case L'1': c = L'!'; break;
|
||||
case L'2': c = L'@'; break;
|
||||
case L'3': c = L'#'; break;
|
||||
case L'4': c = L'$'; break;
|
||||
case L'5': c = L'%'; break;
|
||||
case L'6': c = L'^'; break;
|
||||
case L'7': c = L'&'; break;
|
||||
case L'8': c = L'*'; break;
|
||||
case L'9': c = L'('; break;
|
||||
case L'0': c = L')'; break;
|
||||
case L'-': c = L'_'; break;
|
||||
case L'=': c = L'+'; break;
|
||||
case L'[': c = L'{'; break;
|
||||
case L']': c = L'}'; break;
|
||||
case L'\'': c = L'"'; break;
|
||||
case L'\\': c = L'|'; break;
|
||||
case L';': c = L':'; break;
|
||||
case L',': c = L'<'; break;
|
||||
case L'.': c = L'>'; break;
|
||||
case L'/': c = L'?'; break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static LocalizedString HOME ( "DeviceButton", "Home" );
|
||||
static LocalizedString END ( "DeviceButton", "End" );
|
||||
static LocalizedString UP ( "DeviceButton", "Up" );
|
||||
static LocalizedString DOWN ( "DeviceButton", "Down" );
|
||||
static LocalizedString SPACE ( "DeviceButton", "Space" );
|
||||
static LocalizedString SHIFT ( "DeviceButton", "Shift" );
|
||||
static LocalizedString CTRL ( "DeviceButton", "Ctrl" );
|
||||
static LocalizedString ALT ( "DeviceButton", "Alt" );
|
||||
static LocalizedString INSERT ( "DeviceButton", "Insert" );
|
||||
static LocalizedString DEL ( "DeviceButton", "Delete" );
|
||||
static LocalizedString PGUP ( "DeviceButton", "PgUp" );
|
||||
static LocalizedString PGDN ( "DeviceButton", "PgDn" );
|
||||
static LocalizedString BACKSLASH ( "DeviceButton", "Backslash" );
|
||||
|
||||
RString InputHandler::GetDeviceSpecificInputString( const DeviceInput &di )
|
||||
{
|
||||
if( di.device == InputDevice_Invalid )
|
||||
return RString();
|
||||
|
||||
if( di.device == DEVICE_KEYBOARD )
|
||||
{
|
||||
wchar_t c = DeviceButtonToChar( di.button, false );
|
||||
if( c && c != L' ' ) // Don't show "Key " for space.
|
||||
return InputDeviceToString( di.device ) + " " + Capitalize( WStringToRString(wstring()+c) );
|
||||
}
|
||||
|
||||
RString s = DeviceButtonToString( di.button );
|
||||
if( di.device != DEVICE_KEYBOARD )
|
||||
s = InputDeviceToString( di.device ) + " " + s;
|
||||
return s;
|
||||
}
|
||||
|
||||
RString InputHandler::GetLocalizedInputString( const DeviceInput &di )
|
||||
{
|
||||
switch( di.button )
|
||||
{
|
||||
case KEY_HOME: return HOME.GetValue();
|
||||
case KEY_END: return END.GetValue();
|
||||
case KEY_UP: return UP.GetValue();
|
||||
case KEY_DOWN: return DOWN.GetValue();
|
||||
case KEY_SPACE: return SPACE.GetValue();
|
||||
case KEY_LSHIFT: case KEY_RSHIFT: return SHIFT.GetValue();
|
||||
case KEY_LCTRL: case KEY_RCTRL: return CTRL.GetValue();
|
||||
case KEY_LALT: case KEY_RALT: return ALT.GetValue();
|
||||
case KEY_INSERT: return INSERT.GetValue();
|
||||
case KEY_DEL: return DEL.GetValue();
|
||||
case KEY_PGUP: return PGUP.GetValue();
|
||||
case KEY_PGDN: return PGDN.GetValue();
|
||||
case KEY_BACKSLASH: return BACKSLASH.GetValue();
|
||||
default:
|
||||
wchar_t c = DeviceButtonToChar( di.button, false );
|
||||
if( c && c != L' ' ) // Don't show "Key " for space.
|
||||
return Capitalize( WStringToRString(wstring()+c) );
|
||||
|
||||
return DeviceButtonToString( di.button );
|
||||
}
|
||||
}
|
||||
|
||||
DriverList InputHandler::m_pDriverList;
|
||||
|
||||
static LocalizedString INPUT_HANDLERS_EMPTY( "Arch", "Input Handlers cannot be empty." );
|
||||
void InputHandler::Create( const RString &drivers_, vector<InputHandler *> &Add )
|
||||
{
|
||||
const RString drivers = drivers_.empty()? RString(DEFAULT_INPUT_DRIVER_LIST):drivers_;
|
||||
vector<RString> DriversToTry;
|
||||
split( drivers, ",", DriversToTry, true );
|
||||
|
||||
if( DriversToTry.empty() )
|
||||
RageException::Throw( "%s", INPUT_HANDLERS_EMPTY.GetValue().c_str() );
|
||||
|
||||
FOREACH_CONST( RString, DriversToTry, s )
|
||||
{
|
||||
RageDriver *pDriver = InputHandler::m_pDriverList.Create( *s );
|
||||
if( pDriver == NULL )
|
||||
{
|
||||
LOG->Trace( "Unknown Input Handler name: %s", s->c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
InputHandler *ret = dynamic_cast<InputHandler *>( pDriver );
|
||||
DEBUG_ASSERT( ret );
|
||||
Add.push_back( ret );
|
||||
}
|
||||
|
||||
// Always add
|
||||
Add.push_back( new InputHandler_MonkeyKeyboard );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,105 @@
|
||||
#ifndef INPUT_HANDLER_H
|
||||
#define INPUT_HANDLER_H
|
||||
|
||||
/*
|
||||
* This is a simple class to handle special input devices. Update()
|
||||
* will be called during the input update; the derived class should
|
||||
* send appropriate events to InputHandler.
|
||||
*
|
||||
* Note that, if the underlying device is capable of it, you're free to
|
||||
* start a blocking thread; just store inputs in your class and send them
|
||||
* off in a batch on the next Update. This gets much more accurate timestamps;
|
||||
* we get events more quickly and timestamp them, instead of having a rough timing
|
||||
* granularity due to the framerate.
|
||||
*
|
||||
* Send input events for a specific type of device. Only one driver
|
||||
* for a given set of InputDevice types should be loaded for a given
|
||||
* arch. For example, any number of drivers may produce DEVICE_PUMPn
|
||||
* events, but only one may be loaded at a time. (This will be inconvenient
|
||||
* if, for example, we have two completely distinct methods of getting
|
||||
* input for the same device; we have no method to allocate device numbers.
|
||||
* We don't need this now; I'll write it if it becomes needed.)
|
||||
*/
|
||||
#include "RageInputDevice.h" // for InputDevice
|
||||
#include "arch/RageDriver.h"
|
||||
|
||||
class InputHandler: public RageDriver
|
||||
{
|
||||
public:
|
||||
static void Create( const RString &sDrivers, vector<InputHandler *> &apAdd );
|
||||
static DriverList m_pDriverList;
|
||||
|
||||
InputHandler() { m_iInputsSinceUpdate = 0; }
|
||||
virtual ~InputHandler() { }
|
||||
virtual void Update() { }
|
||||
virtual bool DevicesChanged() { return false; }
|
||||
virtual void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut ) = 0;
|
||||
|
||||
// Override to return a pretty string that's specific to the controller type.
|
||||
virtual RString GetDeviceSpecificInputString( const DeviceInput &di );
|
||||
virtual RString GetLocalizedInputString( const DeviceInput &di );
|
||||
virtual wchar_t DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers );
|
||||
|
||||
// Override to find out whether the controller is currently plugged in.
|
||||
// Not all InputHandlers will support this. Not applicable to all InputHandlers.
|
||||
virtual InputDeviceState GetInputDeviceState( InputDevice id ) { return InputDeviceState_Connected; }
|
||||
|
||||
/* In Windows, some devices need to be recreated if we recreate our main window.
|
||||
* Override this if you need to do that. */
|
||||
virtual void WindowReset() { }
|
||||
|
||||
protected:
|
||||
/* Convenience function: Call this to queue a received event. This may be called
|
||||
* in a thread.
|
||||
*
|
||||
* Important detail: If the timestamp, di.ts, is zero, then it is assumed that
|
||||
* this is not a threaded event handler. In that case, input is being polled,
|
||||
* and the actual time the button was pressed may be any time since the last
|
||||
* poll. In this case, ButtonPressed will pretend the button was pressed at
|
||||
* the midpoint since the last update, which will smooth out the error.
|
||||
*
|
||||
* Note that timestamps are set to the current time by default, so for this to
|
||||
* happen, you need to explicitly call di.ts.SetZero().
|
||||
*
|
||||
* If the timestamp is set, it'll be left alone. */
|
||||
void ButtonPressed( DeviceInput di );
|
||||
|
||||
/* Call this at the end of polling input. */
|
||||
void UpdateTimer();
|
||||
|
||||
private:
|
||||
RageTimer m_LastUpdate;
|
||||
int m_iInputsSinceUpdate;
|
||||
};
|
||||
|
||||
#define REGISTER_INPUT_HANDLER_CLASS2( name, x ) \
|
||||
static RegisterRageDriver register_##name( &InputHandler::m_pDriverList, #name, CreateClass<InputHandler_##x, RageDriver> )
|
||||
#define REGISTER_INPUT_HANDLER_CLASS( name ) REGISTER_INPUT_HANDLER_CLASS2( name, name )
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,752 @@
|
||||
#include "global.h"
|
||||
#include "InputHandler_DirectInput.h"
|
||||
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
#include "archutils/Win32/AppInstance.h"
|
||||
#include "archutils/Win32/DirectXHelpers.h"
|
||||
#include "archutils/Win32/ErrorStrings.h"
|
||||
#include "archutils/Win32/GraphicsWindow.h"
|
||||
#include "archutils/Win32/RegistryAccess.h"
|
||||
#include "InputFilter.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "Foreach.h"
|
||||
|
||||
#include "InputHandler_DirectInputHelper.h"
|
||||
|
||||
REGISTER_INPUT_HANDLER_CLASS2( DirectInput, DInput );
|
||||
|
||||
static vector<DIDevice> Devices;
|
||||
|
||||
/* Number of joysticks found: */
|
||||
static int g_iNumJoysticks;
|
||||
|
||||
static BOOL CALLBACK EnumDevicesCallback( const DIDEVICEINSTANCE *pdidInstance, void *pContext )
|
||||
{
|
||||
DIDevice device;
|
||||
|
||||
switch( pdidInstance->dwDevType & 0xFF )
|
||||
{
|
||||
case DIDEVTYPE_KEYBOARD: device.type = device.KEYBOARD; break;
|
||||
case DIDEVTYPE_JOYSTICK: device.type = device.JOYSTICK; break;
|
||||
default: return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
device.JoystickInst = *pdidInstance;
|
||||
|
||||
switch( device.type )
|
||||
{
|
||||
case device.JOYSTICK:
|
||||
if( g_iNumJoysticks == NUM_JOYSTICKS )
|
||||
return DIENUM_CONTINUE;
|
||||
|
||||
device.dev = enum_add2( DEVICE_JOY1, g_iNumJoysticks );
|
||||
g_iNumJoysticks++;
|
||||
break;
|
||||
|
||||
case device.KEYBOARD:
|
||||
device.dev = DEVICE_KEYBOARD;
|
||||
break;
|
||||
}
|
||||
|
||||
Devices.push_back(device);
|
||||
|
||||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
static void CheckForDirectInputDebugMode()
|
||||
{
|
||||
int iVal;
|
||||
if( RegistryAccess::GetRegValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\DirectInput", "emulation", iVal) )
|
||||
{
|
||||
if( iVal & 0x8 )
|
||||
LOG->Warn("DirectInput keyboard debug mode appears to be enabled. This reduces\n"
|
||||
"input timing accuracy significantly. Disabling this is strongly recommended." );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static BOOL CALLBACK CountDevicesCallback( const DIDEVICEINSTANCE *pdidInstance, void *pContext )
|
||||
{
|
||||
(*(int*)pContext)++;
|
||||
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;
|
||||
HRESULT hr = g_dinput->EnumDevices( DIDEVTYPE_JOYSTICK, CountDevicesCallback, &iCount, DIEDFL_ATTACHEDONLY );
|
||||
if( hr != DI_OK )
|
||||
{
|
||||
LOG->Warn( hr_ssprintf(hr, "g_dinput->EnumDevices") );
|
||||
}
|
||||
return iCount;
|
||||
}
|
||||
|
||||
InputHandler_DInput::InputHandler_DInput()
|
||||
{
|
||||
LOG->Trace( "InputHandler_DInput::InputHandler_DInput()" );
|
||||
|
||||
CheckForDirectInputDebugMode();
|
||||
|
||||
m_bShutdown = false;
|
||||
g_iNumJoysticks = 0;
|
||||
|
||||
AppInstance inst;
|
||||
HRESULT hr = DirectInputCreate(inst.Get(), DIRECTINPUT_VERSION, &g_dinput, NULL);
|
||||
if( hr != DI_OK )
|
||||
RageException::Throw( hr_ssprintf(hr, "InputHandler_DInput: DirectInputCreate") );
|
||||
|
||||
LOG->Trace( "InputHandler_DInput: IDirectInput::EnumDevices(DIDEVTYPE_KEYBOARD)" );
|
||||
hr = g_dinput->EnumDevices( DIDEVTYPE_KEYBOARD, EnumDevicesCallback, NULL, DIEDFL_ATTACHEDONLY );
|
||||
if( hr != DI_OK )
|
||||
RageException::Throw( hr_ssprintf(hr, "InputHandler_DInput: IDirectInput::EnumDevices") );
|
||||
|
||||
LOG->Trace( "InputHandler_DInput: IDirectInput::EnumDevices(DIDEVTYPE_JOYSTICK)" );
|
||||
hr = g_dinput->EnumDevices( DIDEVTYPE_JOYSTICK, EnumDevicesCallback, NULL, DIEDFL_ATTACHEDONLY );
|
||||
if( hr != DI_OK )
|
||||
RageException::Throw( hr_ssprintf(hr, "InputHandler_DInput: IDirectInput::EnumDevices") );
|
||||
|
||||
for( unsigned i = 0; i < Devices.size(); ++i )
|
||||
{
|
||||
if( Devices[i].Open() )
|
||||
continue;
|
||||
|
||||
Devices.erase( Devices.begin() + i );
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG->Info( "Found %u DirectInput devices:", Devices.size() );
|
||||
for( unsigned i = 0; i < Devices.size(); ++i )
|
||||
{
|
||||
LOG->Info( " %d: '%s' axes: %d, hats: %d, buttons: %d (%s)",
|
||||
i,
|
||||
Devices[i].m_sName.c_str(),
|
||||
Devices[i].axes,
|
||||
Devices[i].hats,
|
||||
Devices[i].buttons,
|
||||
Devices[i].buffered? "buffered": "unbuffered" );
|
||||
}
|
||||
|
||||
m_iLastSeenNumHidDevices = GetNumHidDevices();
|
||||
m_iNumTimesLeftToPollForJoysticksChanged = 0;
|
||||
m_iLastSeenNumJoysticks = GetNumJoysticksSlow();
|
||||
|
||||
StartThread();
|
||||
}
|
||||
|
||||
void InputHandler_DInput::StartThread()
|
||||
{
|
||||
ASSERT( !m_InputThread.IsCreated() );
|
||||
if( PREFSMAN->m_bThreadedInput )
|
||||
{
|
||||
m_InputThread.SetName( "DirectInput thread" );
|
||||
m_InputThread.Create( InputThread_Start, this );
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler_DInput::ShutdownThread()
|
||||
{
|
||||
m_bShutdown = true;
|
||||
if( m_InputThread.IsCreated() )
|
||||
{
|
||||
LOG->Trace( "Shutting down DirectInput thread ..." );
|
||||
m_InputThread.Wait();
|
||||
LOG->Trace( "DirectInput thread shut down." );
|
||||
}
|
||||
m_bShutdown = false;
|
||||
}
|
||||
|
||||
InputHandler_DInput::~InputHandler_DInput()
|
||||
{
|
||||
ShutdownThread();
|
||||
|
||||
for( unsigned i = 0; i < Devices.size(); ++i )
|
||||
Devices[i].Close();
|
||||
|
||||
Devices.clear();
|
||||
g_dinput->Release();
|
||||
g_dinput = NULL;
|
||||
}
|
||||
|
||||
void InputHandler_DInput::WindowReset()
|
||||
{
|
||||
/* We need to reopen keyboards. */
|
||||
ShutdownThread();
|
||||
|
||||
for( unsigned i = 0; i < Devices.size(); ++i )
|
||||
{
|
||||
if( Devices[i].type != Devices[i].KEYBOARD )
|
||||
continue;
|
||||
|
||||
Devices[i].Close();
|
||||
|
||||
/* We lose buffered inputs here, so we need to clear all pressed keys. */
|
||||
INPUTFILTER->ResetDevice( Devices[i].dev );
|
||||
|
||||
bool ret = Devices[i].Open();
|
||||
|
||||
/* Reopening it should succeed. */
|
||||
ASSERT( ret );
|
||||
}
|
||||
|
||||
StartThread();
|
||||
}
|
||||
|
||||
#define HAT_UP_MASK 1
|
||||
#define HAT_DOWN_MASK 2
|
||||
#define HAT_LEFT_MASK 4
|
||||
#define HAT_RIGHT_MASK 8
|
||||
|
||||
static int TranslatePOV(DWORD value)
|
||||
{
|
||||
const int HAT_VALS[] =
|
||||
{
|
||||
HAT_UP_MASK,
|
||||
HAT_UP_MASK | HAT_RIGHT_MASK,
|
||||
HAT_RIGHT_MASK,
|
||||
HAT_DOWN_MASK | HAT_RIGHT_MASK,
|
||||
HAT_DOWN_MASK,
|
||||
HAT_DOWN_MASK | HAT_LEFT_MASK,
|
||||
HAT_LEFT_MASK,
|
||||
HAT_UP_MASK | HAT_LEFT_MASK
|
||||
};
|
||||
|
||||
if( LOWORD(value) == 0xFFFF )
|
||||
return 0;
|
||||
|
||||
/* Round the value up: */
|
||||
value += 4500 / 2;
|
||||
value %= 36000;
|
||||
value /= 4500;
|
||||
|
||||
if( value >= 8 )
|
||||
return 0; /* shouldn't happen */
|
||||
|
||||
return HAT_VALS[value];
|
||||
}
|
||||
|
||||
static HRESULT GetDeviceState( LPDIRECTINPUTDEVICE2 dev, int size, void *ptr )
|
||||
{
|
||||
HRESULT hr = dev->GetDeviceState( size, ptr );
|
||||
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
|
||||
{
|
||||
hr = dev->Acquire();
|
||||
if( hr != DI_OK )
|
||||
{
|
||||
LOG->Trace( hr_ssprintf(hr, "?") );
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = dev->GetDeviceState( size, ptr );
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* This doesn't take a timestamp; instead, we let InputHandler::ButtonPressed figure
|
||||
* it out. Be sure to call InputHandler::Update() between each poll. */
|
||||
void InputHandler_DInput::UpdatePolled( DIDevice &device, const RageTimer &tm )
|
||||
{
|
||||
switch( device.type )
|
||||
{
|
||||
default:
|
||||
ASSERT(0);
|
||||
case device.KEYBOARD:
|
||||
{
|
||||
unsigned char keys[256];
|
||||
|
||||
HRESULT hr = GetDeviceState( device.Device, 256, keys );
|
||||
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
|
||||
return;
|
||||
|
||||
if( hr != DI_OK )
|
||||
{
|
||||
LOG->MapLog( "UpdatePolled", hr_ssprintf(hr, "Failures on polled keyboard update") );
|
||||
return;
|
||||
}
|
||||
|
||||
for( int k = 0; k < 256; ++k )
|
||||
{
|
||||
const DeviceButton key = (DeviceButton) device.Inputs[k].num;
|
||||
ButtonPressed( DeviceInput(device.dev, key, !!(keys[k] & 0x80) ) );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case device.JOYSTICK:
|
||||
{
|
||||
DIJOYSTATE state;
|
||||
|
||||
HRESULT hr = GetDeviceState(device.Device, sizeof(state), &state);
|
||||
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
|
||||
return;
|
||||
|
||||
/* Set each known axis, button and POV. */
|
||||
for( unsigned i = 0; i < device.Inputs.size(); ++i )
|
||||
{
|
||||
const input_t &in = device.Inputs[i];
|
||||
const InputDevice dev = device.dev;
|
||||
|
||||
switch(in.type)
|
||||
{
|
||||
case in.BUTTON:
|
||||
{
|
||||
DeviceInput di( dev, enum_add2(JOY_BUTTON_1, in.num), !!state.rgbButtons[in.ofs - DIJOFS_BUTTON0], tm );
|
||||
ButtonPressed( di );
|
||||
break;
|
||||
}
|
||||
|
||||
case in.AXIS:
|
||||
{
|
||||
DeviceButton neg = DeviceButton_Invalid, pos = DeviceButton_Invalid;
|
||||
int val = 0;
|
||||
switch( in.ofs )
|
||||
{
|
||||
case DIJOFS_X: neg = JOY_LEFT; pos = JOY_RIGHT;
|
||||
val = state.lX;
|
||||
break;
|
||||
case DIJOFS_Y: neg = JOY_UP; pos = JOY_DOWN;
|
||||
val = state.lY;
|
||||
break;
|
||||
case DIJOFS_Z: neg = JOY_Z_UP; pos = JOY_Z_DOWN;
|
||||
val = state.lZ;
|
||||
break;
|
||||
case DIJOFS_RX: neg = JOY_ROT_LEFT; pos = JOY_ROT_RIGHT;
|
||||
val = state.lRx;
|
||||
break;
|
||||
case DIJOFS_RY: neg = JOY_ROT_UP; pos = JOY_ROT_DOWN;
|
||||
val = state.lRy;
|
||||
break;
|
||||
case DIJOFS_RZ: neg = JOY_ROT_Z_UP; pos = JOY_ROT_Z_DOWN;
|
||||
val = state.lRz;
|
||||
break;
|
||||
case DIJOFS_SLIDER(0):
|
||||
neg = JOY_AUX_1; pos = JOY_AUX_2;
|
||||
val = state.rglSlider[0];
|
||||
break;
|
||||
case DIJOFS_SLIDER(1):
|
||||
neg = JOY_AUX_3; pos = JOY_AUX_4;
|
||||
val = state.rglSlider[1];
|
||||
break;
|
||||
default: LOG->MapLog( "unknown input",
|
||||
"Controller '%s' is returning an unknown joystick offset, %i",
|
||||
device.m_sName.c_str(), in.ofs );
|
||||
continue;
|
||||
}
|
||||
if( neg != DeviceButton_Invalid )
|
||||
{
|
||||
float l = SCALE( int(val), 0.0f, 100.0f, 0.0f, 1.0f );
|
||||
ButtonPressed( DeviceInput(dev, neg, max(-l,0), tm) );
|
||||
ButtonPressed( DeviceInput(dev, pos, max(+l,0), tm) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case in.HAT:
|
||||
if( in.num == 0 )
|
||||
{
|
||||
const int pos = TranslatePOV( state.rgdwPOV[in.ofs - DIJOFS_POV(0)] );
|
||||
ButtonPressed( DeviceInput(dev, JOY_HAT_UP, !!(pos & HAT_UP_MASK), tm) );
|
||||
ButtonPressed( DeviceInput(dev, JOY_HAT_DOWN, !!(pos & HAT_DOWN_MASK), tm) );
|
||||
ButtonPressed( DeviceInput(dev, JOY_HAT_LEFT, !!(pos & HAT_LEFT_MASK), tm) );
|
||||
ButtonPressed( DeviceInput(dev, JOY_HAT_RIGHT, !!(pos & HAT_RIGHT_MASK), tm) );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler_DInput::UpdateBuffered( DIDevice &device, const RageTimer &tm )
|
||||
{
|
||||
DWORD numevents;
|
||||
DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
|
||||
|
||||
numevents = INPUT_QSIZE;
|
||||
HRESULT hr = device.Device->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), evtbuf, &numevents, 0 );
|
||||
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
|
||||
{
|
||||
INPUTFILTER->ResetDevice( device.dev );
|
||||
return;
|
||||
}
|
||||
|
||||
if( hr != DI_OK )
|
||||
{
|
||||
LOG->Trace( hr_ssprintf(hr, "UpdateBuffered: IDirectInputDevice2_GetDeviceData") );
|
||||
return;
|
||||
}
|
||||
|
||||
if( GetForegroundWindow() != GraphicsWindow::GetHwnd() )
|
||||
{
|
||||
/* Discard input when not focused, and release all keys. */
|
||||
INPUTFILTER->ResetDevice( device.dev );
|
||||
return;
|
||||
}
|
||||
|
||||
for( int i = 0; i < (int) numevents; ++i )
|
||||
{
|
||||
for(unsigned j = 0; j < device.Inputs.size(); ++j)
|
||||
{
|
||||
const input_t &in = device.Inputs[j];
|
||||
const InputDevice dev = device.dev;
|
||||
|
||||
if( evtbuf[i].dwOfs != in.ofs )
|
||||
continue;
|
||||
|
||||
switch( in.type )
|
||||
{
|
||||
case in.KEY:
|
||||
/*
|
||||
switch( in.num )
|
||||
{
|
||||
// "Joystick with Keyboard" hack
|
||||
case 115: //s
|
||||
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_UP, !!(evtbuf[i].dwData & 0x80), tm) );
|
||||
break;
|
||||
case 120: //x
|
||||
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_DOWN, !!(evtbuf[i].dwData & 0x80), tm) );
|
||||
break;
|
||||
case 122: //z
|
||||
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_LEFT, !!(evtbuf[i].dwData & 0x80), tm) );
|
||||
break;
|
||||
case 99: //c
|
||||
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_RIGHT, !!(evtbuf[i].dwData & 0x80), tm) );
|
||||
break;
|
||||
case 100: //d
|
||||
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_BUTTON_1, !!(evtbuf[i].dwData & 0x80), tm) );
|
||||
break;
|
||||
case 101: //e
|
||||
ButtonPressed( DeviceInput(DEVICE_JOY1, JOY_BUTTON_2, !!(evtbuf[i].dwData & 0x80), tm) );
|
||||
break;
|
||||
default:
|
||||
*/
|
||||
ButtonPressed( DeviceInput(dev, (DeviceButton) in.num, !!(evtbuf[i].dwData & 0x80), tm) );
|
||||
/*
|
||||
break;
|
||||
}
|
||||
*/
|
||||
break;
|
||||
|
||||
case in.BUTTON:
|
||||
ButtonPressed( DeviceInput(dev, enum_add2(JOY_BUTTON_1, in.num), !!evtbuf[i].dwData, tm) );
|
||||
break;
|
||||
|
||||
case in.AXIS:
|
||||
{
|
||||
DeviceButton up = DeviceButton_Invalid, down = DeviceButton_Invalid;
|
||||
switch(in.ofs)
|
||||
{
|
||||
case DIJOFS_X: up = JOY_LEFT; down = JOY_RIGHT; break;
|
||||
case DIJOFS_Y: up = JOY_UP; down = JOY_DOWN; break;
|
||||
case DIJOFS_Z: up = JOY_Z_UP; down = JOY_Z_DOWN; break;
|
||||
case DIJOFS_RX: up = JOY_ROT_UP; down = JOY_ROT_DOWN; break;
|
||||
case DIJOFS_RY: up = JOY_ROT_LEFT; down = JOY_ROT_RIGHT; break;
|
||||
case DIJOFS_RZ: up = JOY_ROT_Z_UP; down = JOY_ROT_Z_DOWN; break;
|
||||
case DIJOFS_SLIDER(0): up = JOY_AUX_1; down = JOY_AUX_2; break;
|
||||
case DIJOFS_SLIDER(1): up = JOY_AUX_3; down = JOY_AUX_4; break;
|
||||
default: LOG->MapLog( "unknown input",
|
||||
"Controller '%s' is returning an unknown joystick offset, %i",
|
||||
device.m_sName.c_str(), in.ofs );
|
||||
continue;
|
||||
}
|
||||
|
||||
float l = SCALE( int(evtbuf[i].dwData), 0.0f, 100.0f, 0.0f, 1.0f );
|
||||
ButtonPressed( DeviceInput(dev, up, max(-l,0), tm) );
|
||||
ButtonPressed( DeviceInput(dev, down, max(+l,0), tm) );
|
||||
break;
|
||||
}
|
||||
case in.HAT:
|
||||
{
|
||||
const int pos = TranslatePOV( evtbuf[i].dwData );
|
||||
ButtonPressed( DeviceInput(dev, JOY_HAT_UP, !!(pos & HAT_UP_MASK), tm) );
|
||||
ButtonPressed( DeviceInput(dev, JOY_HAT_DOWN, !!(pos & HAT_DOWN_MASK), tm) );
|
||||
ButtonPressed( DeviceInput(dev, JOY_HAT_LEFT, !!(pos & HAT_LEFT_MASK), tm) );
|
||||
ButtonPressed( DeviceInput(dev, JOY_HAT_RIGHT, !!(pos & HAT_RIGHT_MASK), tm) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InputHandler_DInput::PollAndAcquireDevices( bool bBuffered )
|
||||
{
|
||||
for( unsigned i = 0; i < Devices.size(); ++i )
|
||||
{
|
||||
if( Devices[i].buffered != bBuffered )
|
||||
continue;
|
||||
|
||||
HRESULT hr = Devices[i].Device->Poll();
|
||||
if( hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED )
|
||||
{
|
||||
INPUTFILTER->ResetDevice( Devices[i].dev );
|
||||
|
||||
/* This will fail with "access denied" on the keyboard if we don't
|
||||
* have focus. */
|
||||
hr = Devices[i].Device->Acquire();
|
||||
if( hr != DI_OK )
|
||||
continue;
|
||||
|
||||
Devices[i].Device->Poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler_DInput::Update()
|
||||
{
|
||||
/* Handle polled devices. Handle buffered, too, if there's no input thread to do it. */
|
||||
PollAndAcquireDevices( false );
|
||||
if( !m_InputThread.IsCreated() )
|
||||
PollAndAcquireDevices( true );
|
||||
|
||||
for( unsigned i = 0; i < Devices.size(); ++i )
|
||||
{
|
||||
if( !Devices[i].buffered )
|
||||
{
|
||||
UpdatePolled( Devices[i], RageZeroTimer );
|
||||
}
|
||||
else if( !m_InputThread.IsCreated() )
|
||||
{
|
||||
/* If we have an input thread, it'll handle buffered devices. */
|
||||
UpdateBuffered( Devices[i], RageZeroTimer );
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
int iOldNumHidDevices = m_iLastSeenNumHidDevices;
|
||||
m_iLastSeenNumHidDevices = GetNumHidDevices();
|
||||
if( iOldNumHidDevices != m_iLastSeenNumHidDevices )
|
||||
{
|
||||
LOG->Warn( "HID devices changes" );
|
||||
m_iNumTimesLeftToPollForJoysticksChanged = (int)(POLL_FOR_JOYSTICK_CHANGES_LENGTH_SECONDS / POLL_FOR_JOYSTICK_CHANGES_EVERY_SECONDS);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST))
|
||||
LOG->Warn(werr_ssprintf(GetLastError(), "Failed to set DirectInput thread priority"));
|
||||
|
||||
/* Enable priority boosting. */
|
||||
SetThreadPriorityBoost( GetCurrentThread(), FALSE );
|
||||
|
||||
vector<DIDevice*> BufferedDevices;
|
||||
HANDLE Handle = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
for( unsigned i = 0; i < Devices.size(); ++i )
|
||||
{
|
||||
if( !Devices[i].buffered )
|
||||
continue;
|
||||
|
||||
BufferedDevices.push_back( &Devices[i] );
|
||||
|
||||
Devices[i].Device->Unacquire();
|
||||
HRESULT hr = Devices[i].Device->SetEventNotification( Handle );
|
||||
if( FAILED(hr) )
|
||||
LOG->Warn( "IDirectInputDevice2_SetEventNotification failed on %i", i );
|
||||
Devices[i].Device->Acquire();
|
||||
}
|
||||
|
||||
while( !m_bShutdown )
|
||||
{
|
||||
CHECKPOINT;
|
||||
if( BufferedDevices.size() )
|
||||
{
|
||||
/* Update buffered devices. */
|
||||
PollAndAcquireDevices( true );
|
||||
|
||||
int ret = WaitForSingleObjectEx( Handle, 50, true );
|
||||
if( ret == -1 )
|
||||
{
|
||||
LOG->Trace( werr_ssprintf(GetLastError(), "WaitForSingleObjectEx failed") );
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Update devices even if no event was triggered, since this also checks for focus
|
||||
* loss. */
|
||||
RageTimer now;
|
||||
for( unsigned i = 0; i < BufferedDevices.size(); ++i )
|
||||
UpdateBuffered( *BufferedDevices[i], now );
|
||||
}
|
||||
CHECKPOINT;
|
||||
|
||||
/* If we have no buffered devices, we didn't delay at WaitForMultipleObjectsEx. */
|
||||
if( BufferedDevices.size() == 0 )
|
||||
usleep( 50000 );
|
||||
CHECKPOINT;
|
||||
}
|
||||
CHECKPOINT;
|
||||
|
||||
for( unsigned i = 0; i < Devices.size(); ++i )
|
||||
{
|
||||
if( !Devices[i].buffered )
|
||||
continue;
|
||||
|
||||
Devices[i].Device->Unacquire();
|
||||
Devices[i].Device->SetEventNotification( NULL );
|
||||
}
|
||||
|
||||
CloseHandle(Handle);
|
||||
}
|
||||
|
||||
void InputHandler_DInput::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
for( unsigned i=0; i < Devices.size(); ++i )
|
||||
vDevicesOut.push_back( InputDeviceInfo(Devices[i].dev, Devices[i].m_sName) );
|
||||
}
|
||||
|
||||
static wchar_t ScancodeAndKeysToChar( DWORD scancode, unsigned char keys[256] )
|
||||
{
|
||||
static HKL layout = GetKeyboardLayout(0); // 0 == current thread
|
||||
UINT vk = MapVirtualKeyEx( scancode, 1, layout );
|
||||
|
||||
static bool bInitialized = false;
|
||||
|
||||
typedef int (WINAPI TOUNICODEEX)( IN UINT wVirtKey, IN UINT wScanCode, IN CONST BYTE *lpKeyState, OUT LPWSTR pwszBuff, IN int cchBuff, IN UINT wFlags, IN HKL dwhkl );
|
||||
static TOUNICODEEX *pToUnicodeEx;
|
||||
|
||||
if( !bInitialized )
|
||||
{
|
||||
bInitialized = true;
|
||||
HMODULE hModule = GetModuleHandle( "user32.dll" );
|
||||
pToUnicodeEx = (TOUNICODEEX *) GetProcAddress( hModule, "ToUnicodeEx" );
|
||||
}
|
||||
|
||||
|
||||
unsigned short result[2]; // ToAscii writes a max of 2 chars
|
||||
ZERO( result );
|
||||
|
||||
if( pToUnicodeEx != NULL )
|
||||
{
|
||||
int iNum = pToUnicodeEx( vk, scancode, keys, (LPWSTR)result, 2, 0, layout );
|
||||
if( iNum == 1 )
|
||||
return result[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
int iNum = ToAsciiEx( vk, scancode, keys, result, 0, layout );
|
||||
// iNum == 2 will happen only for dead keys. See MSDN for ToAsciiEx.
|
||||
if( iNum == 1 )
|
||||
{
|
||||
RString s = RString()+(char)result[0];
|
||||
return ConvertCodepageToWString( s, CP_ACP )[0];
|
||||
}
|
||||
}
|
||||
|
||||
return '\0';
|
||||
}
|
||||
|
||||
wchar_t InputHandler_DInput::DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers )
|
||||
{
|
||||
// ToAsciiEx maps these keys to a character. They shouldn't be mapped to any character.
|
||||
switch( button )
|
||||
{
|
||||
case KEY_ESC:
|
||||
case KEY_TAB:
|
||||
case KEY_ENTER:
|
||||
case KEY_BACK:
|
||||
return '\0';
|
||||
}
|
||||
|
||||
FOREACH_CONST( DIDevice, Devices, d )
|
||||
{
|
||||
if( d->type != DIDevice::KEYBOARD )
|
||||
continue;
|
||||
|
||||
FOREACH_CONST( input_t, d->Inputs, i )
|
||||
{
|
||||
if( button != i->num )
|
||||
continue;
|
||||
|
||||
unsigned char keys[256];
|
||||
ZERO( keys );
|
||||
if( bUseCurrentKeyModifiers )
|
||||
GetKeyboardState(keys);
|
||||
wchar_t c = ScancodeAndKeysToChar( i->ofs, keys );
|
||||
if( c )
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return InputHandler::DeviceButtonToChar( button, bUseCurrentKeyModifiers );
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef INPUTHANDLER_DIRECTINPUT_H
|
||||
#define INPUTHANDLER_DIRECTINPUT_H
|
||||
|
||||
#include "InputHandler.h"
|
||||
#include "RageThreads.h"
|
||||
|
||||
struct DIDevice;
|
||||
class InputHandler_DInput: public InputHandler
|
||||
{
|
||||
public:
|
||||
InputHandler_DInput();
|
||||
~InputHandler_DInput();
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
wchar_t DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers );
|
||||
void Update();
|
||||
bool DevicesChanged();
|
||||
void WindowReset();
|
||||
|
||||
private:
|
||||
RageThread m_InputThread;
|
||||
bool m_bShutdown;
|
||||
|
||||
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; }
|
||||
void InputThreadMain();
|
||||
|
||||
void StartThread();
|
||||
void ShutdownThread();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,329 @@
|
||||
#include "global.h"
|
||||
#include "InputHandler_DirectInputHelper.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
#include "archutils/Win32/DirectXHelpers.h"
|
||||
#include "archutils/Win32/ErrorStrings.h"
|
||||
#include "archutils/Win32/GraphicsWindow.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma comment(lib, "dinput.lib")
|
||||
#if defined(_WINDOWS)
|
||||
#pragma comment(lib, "dxguid.lib")
|
||||
#endif
|
||||
#endif
|
||||
LPDIRECTINPUT g_dinput = NULL;
|
||||
|
||||
static int ConvertScancodeToKey( int scancode );
|
||||
static BOOL CALLBACK DIJoystick_EnumDevObjectsProc(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data);
|
||||
|
||||
DIDevice::DIDevice()
|
||||
{
|
||||
buttons = axes = hats = 0;
|
||||
dev = InputDevice_Invalid;
|
||||
buffered = true;
|
||||
memset(&JoystickInst, 0, sizeof(JoystickInst));
|
||||
Device = NULL;
|
||||
}
|
||||
|
||||
bool DIDevice::Open()
|
||||
{
|
||||
m_sName = ConvertACPToUTF8( JoystickInst.tszProductName );
|
||||
|
||||
LOG->Trace( "Opening device '%s'", m_sName.c_str() );
|
||||
buffered = true;
|
||||
|
||||
LPDIRECTINPUTDEVICE tmpdevice;
|
||||
HRESULT hr = g_dinput->CreateDevice( JoystickInst.guidInstance, &tmpdevice, NULL );
|
||||
if ( hr != DI_OK )
|
||||
{
|
||||
LOG->Info( hr_ssprintf(hr, "OpenDevice: IDirectInput_CreateDevice") );
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = tmpdevice->QueryInterface( IID_IDirectInputDevice2, (LPVOID *) &Device );
|
||||
tmpdevice->Release();
|
||||
if ( hr != DI_OK )
|
||||
{
|
||||
LOG->Info( hr_ssprintf(hr, "OpenDevice(%s): IDirectInputDevice::QueryInterface", m_sName.c_str()) );
|
||||
return false;
|
||||
}
|
||||
|
||||
int coop = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
|
||||
if( type == KEYBOARD )
|
||||
coop = DISCL_NONEXCLUSIVE | DISCL_FOREGROUND;
|
||||
|
||||
hr = Device->SetCooperativeLevel( GraphicsWindow::GetHwnd(), coop );
|
||||
if ( hr != DI_OK )
|
||||
{
|
||||
LOG->Info( hr_ssprintf(hr, "OpenDevice(%s): IDirectInputDevice2::SetCooperativeLevel", m_sName.c_str()) );
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = Device->SetDataFormat( type == JOYSTICK? &c_dfDIJoystick: &c_dfDIKeyboard );
|
||||
if ( hr != DI_OK )
|
||||
{
|
||||
LOG->Info( hr_ssprintf(hr, "OpenDevice(%s): IDirectInputDevice2::SetDataFormat", m_sName.c_str()) );
|
||||
return false;
|
||||
}
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case JOYSTICK:
|
||||
Device->EnumObjects( DIJoystick_EnumDevObjectsProc, this, DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
|
||||
break;
|
||||
case KEYBOARD:
|
||||
/* Always 256-button. */
|
||||
for( int b = 0; b < 256; ++b )
|
||||
{
|
||||
input_t in;
|
||||
in.type = in.KEY;
|
||||
|
||||
in.num = ConvertScancodeToKey(b);
|
||||
in.ofs = b;
|
||||
buttons++;
|
||||
Inputs.push_back(in);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
DIPROPDWORD dipdw;
|
||||
memset(&dipdw, 0, sizeof(dipdw));
|
||||
dipdw.diph.dwSize = sizeof(dipdw);
|
||||
dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
|
||||
dipdw.diph.dwObj = 0;
|
||||
dipdw.diph.dwHow = DIPH_DEVICE;
|
||||
dipdw.dwData = INPUT_QSIZE;
|
||||
hr = Device->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph );
|
||||
if ( hr == DI_POLLEDDEVICE )
|
||||
{
|
||||
/* This device doesn't support buffering, so we're forced
|
||||
* to use less reliable polling. */
|
||||
buffered = false;
|
||||
}
|
||||
else if ( hr != DI_OK )
|
||||
{
|
||||
LOG->Info( hr_ssprintf(hr, "OpenDevice(%s): IDirectInputDevice2::SetProperty", m_sName.c_str()) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DIDevice::Close()
|
||||
{
|
||||
/* Don't try to close a device that isn't open. */
|
||||
ASSERT( Device != NULL );
|
||||
|
||||
Device->Unacquire();
|
||||
Device->Release();
|
||||
|
||||
Device = NULL;
|
||||
buttons = axes = hats = NULL;
|
||||
Inputs.clear();
|
||||
}
|
||||
|
||||
static BOOL CALLBACK DIJoystick_EnumDevObjectsProc(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data)
|
||||
{
|
||||
DIDevice *device = (DIDevice *) data;
|
||||
HRESULT hr;
|
||||
|
||||
input_t in;
|
||||
const int SupportedMask = DIDFT_BUTTON | DIDFT_POV | DIDFT_AXIS;
|
||||
if(!(dev->dwType & SupportedMask))
|
||||
return DIENUM_CONTINUE; /* unsupported */
|
||||
|
||||
in.ofs = dev->dwOfs;
|
||||
|
||||
if(dev->dwType & DIDFT_BUTTON) {
|
||||
if( device->buttons == 24 )
|
||||
return DIENUM_CONTINUE; /* too many buttons */
|
||||
|
||||
in.type = in.BUTTON;
|
||||
in.num = device->buttons;
|
||||
device->buttons++;
|
||||
} else if(dev->dwType & DIDFT_POV) {
|
||||
in.type = in.HAT;
|
||||
in.num = device->hats;
|
||||
device->hats++;
|
||||
} else { /* dev->dwType & DIDFT_AXIS */
|
||||
DIPROPRANGE diprg;
|
||||
DIPROPDWORD dilong;
|
||||
|
||||
in.type = in.AXIS;
|
||||
in.num = device->axes;
|
||||
|
||||
diprg.diph.dwSize = sizeof(diprg);
|
||||
diprg.diph.dwHeaderSize = sizeof(diprg.diph);
|
||||
diprg.diph.dwObj = dev->dwOfs;
|
||||
diprg.diph.dwHow = DIPH_BYOFFSET;
|
||||
diprg.lMin = -100;
|
||||
diprg.lMax = 100;
|
||||
|
||||
hr = device->Device->SetProperty( DIPROP_RANGE, &diprg.diph );
|
||||
if ( hr != DI_OK )
|
||||
return DIENUM_CONTINUE; /* don't use this axis */
|
||||
|
||||
/* Set dead zone to 0. */
|
||||
dilong.diph.dwSize = sizeof(dilong);
|
||||
dilong.diph.dwHeaderSize = sizeof(dilong.diph);
|
||||
dilong.diph.dwObj = dev->dwOfs;
|
||||
dilong.diph.dwHow = DIPH_BYOFFSET;
|
||||
dilong.dwData = 0;
|
||||
hr = device->Device->SetProperty( DIPROP_DEADZONE, &dilong.diph );
|
||||
if ( hr != DI_OK )
|
||||
return DIENUM_CONTINUE; /* don't use this axis */
|
||||
|
||||
device->axes++;
|
||||
}
|
||||
|
||||
device->Inputs.push_back(in);
|
||||
|
||||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
static int ConvertScancodeToKey( int scancode )
|
||||
{
|
||||
switch(scancode)
|
||||
{
|
||||
case DIK_ESCAPE: return KEY_ESC;
|
||||
case DIK_1: return KEY_C1;
|
||||
case DIK_2: return KEY_C2;
|
||||
case DIK_3: return KEY_C3;
|
||||
case DIK_4: return KEY_C4;
|
||||
case DIK_5: return KEY_C5;
|
||||
case DIK_6: return KEY_C6;
|
||||
case DIK_7: return KEY_C7;
|
||||
case DIK_8: return KEY_C8;
|
||||
case DIK_9: return KEY_C9;
|
||||
case DIK_0: return KEY_C0;
|
||||
case DIK_MINUS: return KEY_HYPHEN;
|
||||
case DIK_EQUALS: return KEY_EQUAL;
|
||||
case DIK_BACK: return KEY_BACK;
|
||||
case DIK_TAB: return KEY_TAB;
|
||||
case DIK_Q: return KEY_Cq;
|
||||
case DIK_W: return KEY_Cw;
|
||||
case DIK_E: return KEY_Ce;
|
||||
case DIK_R: return KEY_Cr;
|
||||
case DIK_T: return KEY_Ct;
|
||||
case DIK_Y: return KEY_Cy;
|
||||
case DIK_U: return KEY_Cu;
|
||||
case DIK_I: return KEY_Ci;
|
||||
case DIK_O: return KEY_Co;
|
||||
case DIK_P: return KEY_Cp;
|
||||
case DIK_LBRACKET: return KEY_LBRACKET;
|
||||
case DIK_RBRACKET: return KEY_RBRACKET;
|
||||
case DIK_RETURN: return KEY_ENTER;
|
||||
case DIK_LCONTROL: return KEY_LCTRL;
|
||||
case DIK_A: return KEY_Ca;
|
||||
case DIK_S: return KEY_Cs;
|
||||
case DIK_D: return KEY_Cd;
|
||||
case DIK_F: return KEY_Cf;
|
||||
case DIK_G: return KEY_Cg;
|
||||
case DIK_H: return KEY_Ch;
|
||||
case DIK_J: return KEY_Cj;
|
||||
case DIK_K: return KEY_Ck;
|
||||
case DIK_L: return KEY_Cl;
|
||||
case DIK_SEMICOLON: return KEY_SEMICOLON;
|
||||
case DIK_APOSTROPHE: return KEY_SQUOTE;
|
||||
case DIK_GRAVE: return KEY_ACCENT;
|
||||
case DIK_LSHIFT: return KEY_LSHIFT;
|
||||
case DIK_BACKSLASH: return KEY_BACKSLASH;
|
||||
case DIK_OEM_102: return KEY_BACKSLASH;
|
||||
case DIK_Z: return KEY_Cz;
|
||||
case DIK_X: return KEY_Cx;
|
||||
case DIK_C: return KEY_Cc;
|
||||
case DIK_V: return KEY_Cv;
|
||||
case DIK_B: return KEY_Cb;
|
||||
case DIK_N: return KEY_Cn;
|
||||
case DIK_M: return KEY_Cm;
|
||||
case DIK_COMMA: return KEY_COMMA;
|
||||
case DIK_PERIOD: return KEY_PERIOD;
|
||||
case DIK_SLASH: return KEY_SLASH;
|
||||
case DIK_RSHIFT: return KEY_RSHIFT;
|
||||
case DIK_MULTIPLY: return KEY_KP_ASTERISK;
|
||||
case DIK_LMENU: return KEY_LALT;
|
||||
case DIK_SPACE: return KEY_SPACE;
|
||||
case DIK_CAPITAL: return KEY_CAPSLOCK;
|
||||
case DIK_F1: return KEY_F1;
|
||||
case DIK_F2: return KEY_F2;
|
||||
case DIK_F3: return KEY_F3;
|
||||
case DIK_F4: return KEY_F4;
|
||||
case DIK_F5: return KEY_F5;
|
||||
case DIK_F6: return KEY_F6;
|
||||
case DIK_F7: return KEY_F7;
|
||||
case DIK_F8: return KEY_F8;
|
||||
case DIK_F9: return KEY_F9;
|
||||
case DIK_F10: return KEY_F10;
|
||||
case DIK_NUMLOCK: return KEY_NUMLOCK;
|
||||
case DIK_SCROLL: return KEY_SCRLLOCK;
|
||||
case DIK_NUMPAD7: return KEY_KP_C7;
|
||||
case DIK_NUMPAD8: return KEY_KP_C8;
|
||||
case DIK_NUMPAD9: return KEY_KP_C9;
|
||||
case DIK_SUBTRACT: return KEY_KP_HYPHEN;
|
||||
case DIK_NUMPAD4: return KEY_KP_C4;
|
||||
case DIK_NUMPAD5: return KEY_KP_C5;
|
||||
case DIK_NUMPAD6: return KEY_KP_C6;
|
||||
case DIK_ADD: return KEY_KP_PLUS;
|
||||
case DIK_NUMPAD1: return KEY_KP_C1;
|
||||
case DIK_NUMPAD2: return KEY_KP_C2;
|
||||
case DIK_NUMPAD3: return KEY_KP_C3;
|
||||
case DIK_NUMPAD0: return KEY_KP_C0;
|
||||
case DIK_DECIMAL: return KEY_KP_PERIOD;
|
||||
case DIK_F11: return KEY_F11;
|
||||
case DIK_F12: return KEY_F12;
|
||||
|
||||
case DIK_F13: return KEY_F13;
|
||||
case DIK_F14: return KEY_F14;
|
||||
case DIK_F15: return KEY_F15;
|
||||
|
||||
case DIK_NUMPADEQUALS: return KEY_KP_EQUAL;
|
||||
case DIK_NUMPADENTER: return KEY_KP_ENTER;
|
||||
case DIK_RCONTROL: return KEY_RCTRL;
|
||||
case DIK_DIVIDE: return KEY_KP_SLASH;
|
||||
case DIK_SYSRQ: return KEY_PRTSC;
|
||||
case DIK_RMENU: return KEY_RALT;
|
||||
case DIK_PAUSE: return KEY_PAUSE;
|
||||
case DIK_HOME: return KEY_HOME;
|
||||
case DIK_UP: return KEY_UP;
|
||||
case DIK_PRIOR: return KEY_PGUP;
|
||||
case DIK_LEFT: return KEY_LEFT;
|
||||
case DIK_RIGHT: return KEY_RIGHT;
|
||||
case DIK_END: return KEY_END;
|
||||
case DIK_DOWN: return KEY_DOWN;
|
||||
case DIK_NEXT: return KEY_PGDN;
|
||||
case DIK_INSERT: return KEY_INSERT;
|
||||
case DIK_DELETE: return KEY_DEL;
|
||||
case DIK_LWIN: return KEY_LMETA;
|
||||
case DIK_RWIN: return KEY_RMETA;
|
||||
case DIK_APPS: return KEY_MENU;
|
||||
default: return '?';
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef INPUTHANDLER_DIRECTINPUT_HELPER_H
|
||||
#define INPUTHANDLER_DIRECTINPUT_HELPER_H
|
||||
|
||||
#include "InputFilter.h"
|
||||
|
||||
#define DIRECTINPUT_VERSION 0x0500
|
||||
#include <dinput.h>
|
||||
extern LPDIRECTINPUT g_dinput;
|
||||
|
||||
#define INPUT_QSIZE 32
|
||||
|
||||
typedef struct input_t
|
||||
{
|
||||
/* DirectInput offset for this input type: */
|
||||
DWORD ofs;
|
||||
|
||||
/* Button, axis or hat: */
|
||||
enum Type { KEY, BUTTON, AXIS, HAT } type;
|
||||
|
||||
int num;
|
||||
} input_t;
|
||||
|
||||
struct DIDevice
|
||||
{
|
||||
DIDEVICEINSTANCE JoystickInst;
|
||||
LPDIRECTINPUTDEVICE2 Device;
|
||||
RString m_sName;
|
||||
|
||||
enum { KEYBOARD, JOYSTICK } type;
|
||||
|
||||
bool buffered;
|
||||
int buttons, axes, hats;
|
||||
vector<input_t> Inputs;
|
||||
InputDevice dev;
|
||||
|
||||
DIDevice();
|
||||
|
||||
bool Open();
|
||||
void Close();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,446 @@
|
||||
#include "global.h"
|
||||
#include "InputHandler_Linux_Event.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageUtil.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
REGISTER_INPUT_HANDLER_CLASS2( Event, Linux_Event );
|
||||
|
||||
bool InputHandler_Linux_Event::m_bFoundAnyJoysticks;
|
||||
|
||||
static RString BustypeToString( int iBus )
|
||||
{
|
||||
switch( iBus )
|
||||
{
|
||||
// case BUS_ADB:
|
||||
// case BUS_AMIGA: return "amiga input";
|
||||
case BUS_BLUETOOTH: return "Bluetooth";
|
||||
case BUS_GAMEPORT: return "gameport";
|
||||
// case BUS_HIL:
|
||||
// case BUS_HOST:
|
||||
// case BUS_I2C:
|
||||
case BUS_I8042: return "keyboard";
|
||||
case BUS_ISA: return "ISA";
|
||||
case BUS_ISAPNP: return "ISAPNP";
|
||||
case BUS_PARPORT: return "parallel port";
|
||||
case BUS_PCI: return "PCI";
|
||||
case BUS_RS232: return "serial port";
|
||||
case BUS_USB: return "USB";
|
||||
case BUS_XTKBD: return "XT keyboard";
|
||||
default: return ssprintf("unknown bus %x", iBus);
|
||||
}
|
||||
}
|
||||
|
||||
struct EventDevice
|
||||
{
|
||||
EventDevice();
|
||||
~EventDevice();
|
||||
bool Open( RString sFile, InputDevice dev );
|
||||
bool IsOpen() const { return m_iFD != -1; }
|
||||
void Close()
|
||||
{
|
||||
if( m_iFD != -1 )
|
||||
close( m_iFD );
|
||||
m_iFD = -1;
|
||||
}
|
||||
|
||||
int m_iFD;
|
||||
RString m_sPath;
|
||||
RString m_sName;
|
||||
InputDevice m_Dev;
|
||||
|
||||
int aiAbsMin[ABS_MAX];
|
||||
int aiAbsMax[ABS_MAX];
|
||||
DeviceButton aiAbsMappingHigh[ABS_MAX];
|
||||
DeviceButton aiAbsMappingLow[ABS_MAX];
|
||||
};
|
||||
|
||||
static vector<EventDevice *> g_apEventDevices;
|
||||
|
||||
/* Return true if the numbered event device exists. sysfs may not always be
|
||||
* there; return false if we don't know. */
|
||||
static bool EventDeviceExists( int iNum )
|
||||
{
|
||||
RString sDir = ssprintf( "/sys/class" );
|
||||
struct stat st;
|
||||
if( stat(sDir, &st) == -1 )
|
||||
return true;
|
||||
|
||||
RString sFile = ssprintf( "/sys/class/input/event%i", iNum );
|
||||
return stat(sFile, &st) == 0;
|
||||
}
|
||||
|
||||
static bool BitIsSet( const uint8_t *pArray, uint32_t iBit )
|
||||
{
|
||||
return !!(pArray[iBit/8] & (1<<(iBit%8)));
|
||||
}
|
||||
|
||||
EventDevice::EventDevice()
|
||||
{
|
||||
m_iFD = -1;
|
||||
}
|
||||
|
||||
bool EventDevice::Open( RString sFile, InputDevice dev )
|
||||
{
|
||||
m_sPath = sFile;
|
||||
m_Dev = dev;
|
||||
m_iFD = open( sFile, O_RDWR );
|
||||
if( m_iFD == -1 )
|
||||
{
|
||||
if( errno == ENODEV )
|
||||
return false;
|
||||
|
||||
if( !EventDeviceExists(m_iFD) )
|
||||
return false;
|
||||
|
||||
LOG->Warn( "Error opening %s: %s", sFile.c_str(), strerror(errno) );
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool bLogged = false;
|
||||
if( !bLogged )
|
||||
{
|
||||
bLogged = true;
|
||||
int iVersion;
|
||||
if( ioctl(m_iFD, EVIOCGVERSION, &iVersion) == -1 )
|
||||
LOG->Warn( "ioctl(EVIOCGVERSION): %s", strerror(errno) );
|
||||
else
|
||||
LOG->Info( "Event driver: v%i.%i.%i", (iVersion >> 16) & 0xFF, (iVersion >> 8) & 0xFF, iVersion & 0xFF );
|
||||
}
|
||||
|
||||
char szName[1024];
|
||||
if( ioctl(m_iFD, EVIOCGNAME(sizeof(szName)), szName) == -1 )
|
||||
{
|
||||
LOG->Warn( "ioctl(EVIOCGNAME): %s", strerror(errno) );
|
||||
|
||||
m_sName = "(unknown)";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sName = szName;
|
||||
}
|
||||
|
||||
input_id DevInfo;
|
||||
if( ioctl(m_iFD, EVIOCGID, &DevInfo) == -1 )
|
||||
{
|
||||
LOG->Warn( "ioctl(EVIOCGID): %s", strerror(errno) );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->Info( "Input device: %s: %s device, ID %04x:%04x, version %x: %s", sFile.c_str(),
|
||||
BustypeToString(DevInfo.bustype).c_str(), DevInfo.vendor, DevInfo.product,
|
||||
DevInfo.version, m_sName.c_str() );
|
||||
}
|
||||
|
||||
uint8_t iABSMask[ABS_MAX/8 + 1];
|
||||
memset( iABSMask, 0, sizeof(iABSMask) );
|
||||
if( ioctl(m_iFD, EVIOCGBIT(EV_ABS, sizeof(iABSMask)), iABSMask) < 0 )
|
||||
LOG->Warn( "ioctl(EVIOCGBIT(EV_ABS)): %s", strerror(errno) );
|
||||
|
||||
if( !BitIsSet(iABSMask, ABS_X) && !BitIsSet(iABSMask, ABS_THROTTLE) && !BitIsSet(iABSMask, ABS_WHEEL) )
|
||||
{
|
||||
LOG->Info( " Not a joystick; ignored" );
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t iKeyMask[KEY_MAX/8 + 1];
|
||||
memset( iKeyMask, 0, sizeof(iKeyMask) );
|
||||
if( ioctl(m_iFD, EVIOCGBIT(EV_KEY, sizeof(iKeyMask)), iKeyMask) < 0 )
|
||||
LOG->Warn( "ioctl(EVIOCGBIT(EV_KEY)): %s", strerror(errno) );
|
||||
|
||||
uint8_t iEventTypes[EV_MAX/8];
|
||||
memset( iEventTypes, 0, sizeof(iEventTypes) );
|
||||
if( ioctl(m_iFD, EVIOCGBIT(0, EV_MAX), iEventTypes) == -1 )
|
||||
LOG->Warn( "ioctl(EV_MAX): %s", strerror(errno) );
|
||||
|
||||
{
|
||||
vector<RString> setEventTypes;
|
||||
|
||||
if( BitIsSet(iEventTypes, EV_SYN) ) setEventTypes.push_back( "syn" );
|
||||
if( BitIsSet(iEventTypes, EV_KEY) ) setEventTypes.push_back( "key" );
|
||||
if( BitIsSet(iEventTypes, EV_REL) ) setEventTypes.push_back( "rel" );
|
||||
if( BitIsSet(iEventTypes, EV_ABS) ) setEventTypes.push_back( "abs" );
|
||||
if( BitIsSet(iEventTypes, EV_MSC) ) setEventTypes.push_back( "misc" );
|
||||
if( BitIsSet(iEventTypes, EV_SW) ) setEventTypes.push_back( "sw" );
|
||||
if( BitIsSet(iEventTypes, EV_LED) ) setEventTypes.push_back( "led" );
|
||||
if( BitIsSet(iEventTypes, EV_SND) ) setEventTypes.push_back( "snd" );
|
||||
if( BitIsSet(iEventTypes, EV_REP) ) setEventTypes.push_back( "rep" );
|
||||
if( BitIsSet(iEventTypes, EV_FF) ) setEventTypes.push_back( "ff" );
|
||||
if( BitIsSet(iEventTypes, EV_PWR) ) setEventTypes.push_back( "pwr" );
|
||||
if( BitIsSet(iEventTypes, EV_FF_STATUS) ) setEventTypes.push_back( "ff_status" );
|
||||
|
||||
LOG->Info( " Event types: %s", join(", ", setEventTypes).c_str() );
|
||||
}
|
||||
|
||||
int iTotalKeys = 0;
|
||||
for( int i = 0; i < KEY_MAX; ++i )
|
||||
{
|
||||
if( !BitIsSet(iKeyMask, i) )
|
||||
continue;
|
||||
++iTotalKeys;
|
||||
}
|
||||
|
||||
int iTotalAxes = 0;
|
||||
const DeviceButton iExtraAxes[] = { JOY_LEFT_2, JOY_UP_2, JOY_AUX_1, JOY_AUX_3 };
|
||||
int iNextExtraAxis = 0;
|
||||
for( int i = 0; i < ABS_MAX; ++i )
|
||||
{
|
||||
if( !BitIsSet(iABSMask, i) )
|
||||
continue;
|
||||
struct input_absinfo absinfo;
|
||||
if( ioctl(m_iFD, EVIOCGABS(i), &absinfo) < 0 )
|
||||
{
|
||||
LOG->Warn( "ioctl(EVIOCGABS): %s", strerror(errno) );
|
||||
continue;
|
||||
}
|
||||
|
||||
//LOG->Info( " Axis %i: min: %i; max: %i; fuzz: %i; flat: %i",
|
||||
// i, absinfo.minimum, absinfo.maximum, absinfo.fuzz, absinfo.flat );
|
||||
aiAbsMin[i] = absinfo.minimum;
|
||||
aiAbsMax[i] = absinfo.maximum;
|
||||
aiAbsMappingHigh[i] = enum_add2(JOY_RIGHT, 2*i);
|
||||
aiAbsMappingLow[i] = enum_add2(JOY_LEFT, 2*i);
|
||||
|
||||
if( i == ABS_X )
|
||||
{
|
||||
aiAbsMappingHigh[i] = JOY_RIGHT;
|
||||
aiAbsMappingLow[i] = JOY_LEFT;
|
||||
}
|
||||
else if( i == ABS_Y )
|
||||
{
|
||||
aiAbsMappingHigh[i] = JOY_DOWN;
|
||||
aiAbsMappingLow[i] = JOY_UP;
|
||||
}
|
||||
else if( i == ABS_Z )
|
||||
{
|
||||
aiAbsMappingHigh[i] = JOY_Z_DOWN;
|
||||
aiAbsMappingLow[i] = JOY_Z_UP;
|
||||
}
|
||||
else if( i == ABS_RX )
|
||||
{
|
||||
aiAbsMappingHigh[i] = JOY_ROT_RIGHT;
|
||||
aiAbsMappingLow[i] = JOY_ROT_LEFT;
|
||||
}
|
||||
else if( i == ABS_RY )
|
||||
{
|
||||
aiAbsMappingHigh[i] = JOY_ROT_DOWN;
|
||||
aiAbsMappingLow[i] = JOY_ROT_UP;
|
||||
}
|
||||
else if( i == ABS_RZ )
|
||||
{
|
||||
aiAbsMappingHigh[i] = JOY_ROT_Z_DOWN;
|
||||
aiAbsMappingLow[i] = JOY_ROT_Z_UP;
|
||||
}
|
||||
else if( i == ABS_HAT0X )
|
||||
{
|
||||
aiAbsMappingHigh[i] = JOY_HAT_RIGHT;
|
||||
aiAbsMappingLow[i] = JOY_HAT_LEFT;
|
||||
}
|
||||
else if( i == ABS_HAT0Y )
|
||||
{
|
||||
aiAbsMappingHigh[i] = JOY_HAT_UP;
|
||||
aiAbsMappingLow[i] = JOY_HAT_DOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( iNextExtraAxis < (int) ARRAYLEN(iExtraAxes) )
|
||||
{
|
||||
aiAbsMappingLow[i] = iExtraAxes[iNextExtraAxis];
|
||||
aiAbsMappingHigh[i] = enum_add2( aiAbsMappingLow[i], 1 );
|
||||
++iNextExtraAxis;
|
||||
}
|
||||
}
|
||||
|
||||
++iTotalAxes;
|
||||
}
|
||||
LOG->Info( " Total keys: %i; total axes: %i", iTotalKeys, iTotalAxes );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EventDevice::~EventDevice()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
InputHandler_Linux_Event::InputHandler_Linux_Event()
|
||||
{
|
||||
if( InputHandler_Linux_Event::m_bFoundAnyJoysticks )
|
||||
{
|
||||
LOG->Trace( "InputHandler_Linux_Event disabled (joystick driver already loaded)" );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Permission problems are likely. We want to warn about them only if there's actually
|
||||
* an underlying device, but if we can't open the device, the only way we can tell if
|
||||
* there'd be anything there is sysfs. That won't always be there. */
|
||||
m_bFoundAnyJoysticks = false;
|
||||
InputDevice NextDevice = DEVICE_JOY1;
|
||||
for( int i = 0; i < 64; ++i )
|
||||
{
|
||||
RString sFile = ssprintf( "/dev/input/event%i", i );
|
||||
|
||||
g_apEventDevices.push_back( new EventDevice );
|
||||
EventDevice *pDev = g_apEventDevices.back();
|
||||
if( !pDev->Open(sFile, NextDevice) )
|
||||
{
|
||||
delete pDev;
|
||||
g_apEventDevices.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
NextDevice = enum_add2(NextDevice, 1);
|
||||
m_bFoundAnyJoysticks = true;
|
||||
}
|
||||
|
||||
m_bShutdown = false;
|
||||
|
||||
if( m_bFoundAnyJoysticks )
|
||||
{
|
||||
m_InputThread.SetName( "Event input thread" );
|
||||
m_InputThread.Create( InputThread_Start, this );
|
||||
|
||||
/* We loaded joysticks, so disable joydev. */
|
||||
}
|
||||
}
|
||||
|
||||
InputHandler_Linux_Event::~InputHandler_Linux_Event()
|
||||
{
|
||||
if( m_InputThread.IsCreated() )
|
||||
{
|
||||
m_bShutdown = true;
|
||||
LOG->Trace( "Shutting down joystick thread ..." );
|
||||
m_InputThread.Wait();
|
||||
LOG->Trace( "Joystick thread shut down." );
|
||||
}
|
||||
|
||||
for( int i = 0; i < (int) g_apEventDevices.size(); ++i )
|
||||
delete g_apEventDevices[i];
|
||||
g_apEventDevices.clear();
|
||||
}
|
||||
|
||||
int InputHandler_Linux_Event::InputThread_Start( void *p )
|
||||
{
|
||||
((InputHandler_Linux_Event *) p)->InputThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InputHandler_Linux_Event::InputThread()
|
||||
{
|
||||
while( !m_bShutdown )
|
||||
{
|
||||
fd_set fdset;
|
||||
FD_ZERO( &fdset );
|
||||
int iMaxFD = -1;
|
||||
|
||||
for( int i = 0; i < (int) g_apEventDevices.size(); ++i )
|
||||
{
|
||||
int iFD = g_apEventDevices[i]->m_iFD;
|
||||
if( !g_apEventDevices[i]->IsOpen() )
|
||||
continue;
|
||||
|
||||
FD_SET( iFD, &fdset );
|
||||
iMaxFD = max( iMaxFD, iFD );
|
||||
}
|
||||
|
||||
if( iMaxFD == -1 )
|
||||
break;
|
||||
|
||||
struct timeval zero = {0,100000};
|
||||
if( select(iMaxFD+1, &fdset, NULL, NULL, &zero) <= 0 )
|
||||
continue;
|
||||
RageTimer now;
|
||||
|
||||
for( int i = 0; i < (int) g_apEventDevices.size(); ++i )
|
||||
{
|
||||
if( !g_apEventDevices[i]->IsOpen() )
|
||||
continue;
|
||||
|
||||
if( !FD_ISSET(g_apEventDevices[i]->m_iFD, &fdset) )
|
||||
continue;
|
||||
|
||||
input_event event;
|
||||
int ret = read( g_apEventDevices[i]->m_iFD, &event, sizeof(event) );
|
||||
if( ret == -1 )
|
||||
{
|
||||
LOG->Warn( "Error reading from %s: %s; disabled", g_apEventDevices[i]->m_sPath.c_str(), strerror(errno) );
|
||||
g_apEventDevices[i]->Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ret != sizeof(event) )
|
||||
{
|
||||
LOG->Warn("Unexpected packet (size %i != %i) from joystick %i; disabled", ret, (int)sizeof(event), i);
|
||||
g_apEventDevices[i]->Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case EV_KEY: {
|
||||
int iNum = event.code;
|
||||
// In 2.6.11 using an EMS USB2, the event number for P1 Tri (the first button)
|
||||
// is being reported as 32 instead of 0. Correct for this.
|
||||
wrap( iNum, 32 ); // max number of joystick buttons. Make this a constant?
|
||||
ButtonPressed( DeviceInput(g_apEventDevices[i]->m_Dev, enum_add2(JOY_BUTTON_1, iNum), event.value != 0, now) );
|
||||
break;
|
||||
}
|
||||
|
||||
case EV_ABS: {
|
||||
ASSERT_M( event.code < ABS_MAX, ssprintf("%i", event.code) );
|
||||
DeviceButton neg = g_apEventDevices[i]->aiAbsMappingLow[event.code];
|
||||
DeviceButton pos = g_apEventDevices[i]->aiAbsMappingHigh[event.code];
|
||||
|
||||
float l = SCALE( int(event.value), (float) g_apEventDevices[i]->aiAbsMin[i], (float) g_apEventDevices[i]->aiAbsMax[i], -1.0f, 1.0f );
|
||||
ButtonPressed( DeviceInput(g_apEventDevices[i]->m_Dev, neg, max(-l,0), now) );
|
||||
ButtonPressed( DeviceInput(g_apEventDevices[i]->m_Dev, pos, max(+l,0), now) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
InputHandler::UpdateTimer();
|
||||
}
|
||||
|
||||
void InputHandler_Linux_Event::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
for( unsigned i = 0; i < g_apEventDevices.size(); ++i )
|
||||
{
|
||||
EventDevice *pDev = g_apEventDevices[i];
|
||||
vDevicesOut.push_back( InputDeviceInfo(pDev->m_Dev, pDev->m_sName) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2003-2008 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,55 @@
|
||||
/* InputHandler_Linux_Event - evdev-based input driver */
|
||||
|
||||
#ifndef INPUT_HANDLER_LINUX_EVENT_H
|
||||
#define INPUT_HANDLER_LINUX_EVENT_H
|
||||
|
||||
#include "InputHandler.h"
|
||||
#include "RageThreads.h"
|
||||
|
||||
|
||||
class InputHandler_Linux_Event: public InputHandler
|
||||
{
|
||||
public:
|
||||
enum { NUM_JOYSTICKS = 4 };
|
||||
InputHandler_Linux_Event();
|
||||
~InputHandler_Linux_Event();
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
|
||||
/* Shared with InputHandler_Linux_Joystick.cpp: */
|
||||
static bool m_bFoundAnyJoysticks;
|
||||
|
||||
private:
|
||||
static int InputThread_Start( void *p );
|
||||
void InputThread();
|
||||
|
||||
RageThread m_InputThread;
|
||||
bool m_bShutdown;
|
||||
};
|
||||
#define USE_INPUT_HANDLER_LINUX_JOYSTICK
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2003-2008 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,226 @@
|
||||
#include "global.h"
|
||||
#include "InputHandler_Linux_Joystick.h"
|
||||
#include "InputHandler_Linux_Event.h" // for m_bFoundAnyJoysticks
|
||||
#include "RageLog.h"
|
||||
#include "RageUtil.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/joystick.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
REGISTER_INPUT_HANDLER_CLASS2( Joystick, Linux_Joystick );
|
||||
|
||||
static const char *Paths[InputHandler_Linux_Joystick::NUM_JOYSTICKS] =
|
||||
{
|
||||
"/dev/js0",
|
||||
"/dev/js1",
|
||||
"/dev/input/js0",
|
||||
"/dev/input/js1",
|
||||
};
|
||||
|
||||
InputHandler_Linux_Joystick::InputHandler_Linux_Joystick()
|
||||
{
|
||||
LOG->Trace( "InputHandler_Linux_Joystick::InputHandler_Linux_Joystick" );
|
||||
for(int i = 0; i < NUM_JOYSTICKS; ++i)
|
||||
fds[i] = -1;
|
||||
|
||||
if( InputHandler_Linux_Event::m_bFoundAnyJoysticks )
|
||||
{
|
||||
LOG->Trace( "InputHandler_Linux_Joystick disabled (joystick driver already loaded)" );
|
||||
return;
|
||||
}
|
||||
|
||||
/* We check both eg. /dev/js0 and /dev/input/js0. If both exist, they're probably
|
||||
* the same device; keep track of device IDs so we don't open the same joystick
|
||||
* twice. */
|
||||
set< pair<int,int> > devices;
|
||||
bool bFoundAnyJoysticks = false;
|
||||
|
||||
for(int i = 0; i < NUM_JOYSTICKS; ++i)
|
||||
{
|
||||
struct stat st;
|
||||
if( stat( Paths[i], &st ) == -1 )
|
||||
{
|
||||
if( errno != ENOENT )
|
||||
LOG->Warn( "Couldn't stat %s: %s", Paths[i], strerror(errno) );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !S_ISCHR( st.st_mode ) )
|
||||
{
|
||||
LOG->Warn( "Ignoring %s: not a character device", Paths[i] );
|
||||
continue;
|
||||
}
|
||||
|
||||
pair<int,int> dev( major(st.st_rdev), minor(st.st_rdev) );
|
||||
if( devices.find(dev) != devices.end() )
|
||||
continue; /* dupe */
|
||||
devices.insert( dev );
|
||||
|
||||
fds[i] = open( Paths[i], O_RDONLY );
|
||||
|
||||
if(fds[i] != -1)
|
||||
{
|
||||
char szName[1024];
|
||||
ZERO( szName );
|
||||
if( ioctl(fds[i], JSIOCGNAME(sizeof(szName)), szName) < 0 )
|
||||
m_sDescription[i] = ssprintf( "Unknown joystick at %s", Paths[i] );
|
||||
else
|
||||
m_sDescription[i] = szName;
|
||||
|
||||
LOG->Info("Opened %s", Paths[i]);
|
||||
bFoundAnyJoysticks = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_bShutdown = false;
|
||||
|
||||
if( bFoundAnyJoysticks )
|
||||
{
|
||||
m_InputThread.SetName( "Joystick thread" );
|
||||
m_InputThread.Create( InputThread_Start, this );
|
||||
InputHandler_Linux_Event::m_bFoundAnyJoysticks = true;
|
||||
}
|
||||
}
|
||||
|
||||
InputHandler_Linux_Joystick::~InputHandler_Linux_Joystick()
|
||||
{
|
||||
if( m_InputThread.IsCreated() )
|
||||
{
|
||||
m_bShutdown = true;
|
||||
LOG->Trace( "Shutting down joystick thread ..." );
|
||||
m_InputThread.Wait();
|
||||
LOG->Trace( "Joystick thread shut down." );
|
||||
}
|
||||
|
||||
for(int i = 0; i < NUM_JOYSTICKS; ++i)
|
||||
if(fds[i] != -1) close(fds[i]);
|
||||
}
|
||||
|
||||
int InputHandler_Linux_Joystick::InputThread_Start( void *p )
|
||||
{
|
||||
((InputHandler_Linux_Joystick *) p)->InputThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InputHandler_Linux_Joystick::InputThread()
|
||||
{
|
||||
while( !m_bShutdown )
|
||||
{
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
int max_fd = -1;
|
||||
|
||||
for(int i = 0; i < NUM_JOYSTICKS; ++i)
|
||||
{
|
||||
if (fds[i] < 0)
|
||||
continue;
|
||||
|
||||
FD_SET(fds[i], &fdset);
|
||||
max_fd = max(max_fd, fds[i]);
|
||||
}
|
||||
|
||||
if(max_fd == -1)
|
||||
break;
|
||||
|
||||
struct timeval zero = {0,100000};
|
||||
if( select(max_fd+1, &fdset, NULL, NULL, &zero) <= 0 )
|
||||
continue;
|
||||
RageTimer now;
|
||||
|
||||
printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n");
|
||||
for(int i = 0; i < NUM_JOYSTICKS; ++i)
|
||||
{
|
||||
if( fds[i] == -1 )
|
||||
continue;
|
||||
|
||||
if(!FD_ISSET(fds[i], &fdset))
|
||||
continue;
|
||||
|
||||
js_event event;
|
||||
int ret = read(fds[i], &event, sizeof(event));
|
||||
if(ret != sizeof(event))
|
||||
{
|
||||
LOG->Warn("Unexpected packet (size %i != %i) from joystick %i; disabled", ret, (int)sizeof(event), i);
|
||||
close(fds[i]);
|
||||
fds[i] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
InputDevice id = InputDevice(DEVICE_JOY1 + i);
|
||||
|
||||
event.type &= ~JS_EVENT_INIT;
|
||||
switch (event.type) {
|
||||
case JS_EVENT_BUTTON: {
|
||||
int iNum = event.number;
|
||||
// In 2.6.11 using an EMS USB2, the event number for P1 Tri (the first button)
|
||||
// is being reported as 32 instead of 0. Correct for this.
|
||||
wrap( iNum, 32 ); // max number of joystick buttons. Make this a constant?
|
||||
ButtonPressed( DeviceInput(id, enum_add2(JOY_BUTTON_1, iNum), event.value, now) );
|
||||
break;
|
||||
}
|
||||
|
||||
case JS_EVENT_AXIS: {
|
||||
DeviceButton neg = enum_add2(JOY_LEFT, 2*event.number);
|
||||
DeviceButton pos = enum_add2(JOY_RIGHT, 2*event.number);
|
||||
float l = SCALE( int(event.value), 0.0f, 32767, 0.0f, 1.0f );
|
||||
ButtonPressed( DeviceInput(id, neg, max(-l,0), now) );
|
||||
ButtonPressed( DeviceInput(id, pos, max(+l,0), now) );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG->Warn("Unexpected packet (type %i) from joystick %i; disabled", event.type, i);
|
||||
close(fds[i]);
|
||||
fds[i] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
InputHandler::UpdateTimer();
|
||||
}
|
||||
|
||||
void InputHandler_Linux_Joystick::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
for(int i = 0; i < NUM_JOYSTICKS; ++i)
|
||||
{
|
||||
if (fds[i] < 0)
|
||||
continue;
|
||||
|
||||
vDevicesOut.push_back( InputDeviceInfo(InputDevice(DEVICE_JOY1+i), m_sDescription[i]) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef INPUT_HANDLER_LINUX_JOYSTICK_H
|
||||
#define INPUT_HANDLER_LINUX_JOYSTICK_H 1
|
||||
|
||||
#include "InputHandler.h"
|
||||
#include "RageThreads.h"
|
||||
|
||||
|
||||
class InputHandler_Linux_Joystick: public InputHandler
|
||||
{
|
||||
public:
|
||||
enum { NUM_JOYSTICKS = 4 };
|
||||
InputHandler_Linux_Joystick();
|
||||
~InputHandler_Linux_Joystick();
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
|
||||
private:
|
||||
static int InputThread_Start( void *p );
|
||||
void InputThread();
|
||||
|
||||
int fds[NUM_JOYSTICKS];
|
||||
RString m_sDescription[NUM_JOYSTICKS];
|
||||
RageThread m_InputThread;
|
||||
bool m_bShutdown;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,221 @@
|
||||
#include "global.h"
|
||||
|
||||
/* This handler is used for odd cases where we don't use SDL for input. */
|
||||
|
||||
#include "InputHandler_Linux_tty.h"
|
||||
#include "InputHandler_Linux_tty_keys.h"
|
||||
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageException.h"
|
||||
|
||||
#include "archutils/Unix/SignalHandler.h"
|
||||
|
||||
#include "SDL_utils.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <linux/kd.h>
|
||||
#include <linux/keyboard.h>
|
||||
#include <termios.h>
|
||||
|
||||
REGISTER_INPUT_HANDLER_CLASS2( tty, Linux_tty );
|
||||
|
||||
/* Map from keys (ignoring shifts) to SDLK values. */
|
||||
static int keys[NR_KEYS];
|
||||
static termios saved_kbd_termios;
|
||||
static int saved_kbd_mode;
|
||||
|
||||
/* This is normally a singleton. Keep track of it, so we can access it
|
||||
* from our signal handler. */
|
||||
static InputHandler_Linux_tty *handler = NULL;
|
||||
|
||||
void InputHandler_Linux_tty::OnCrash(int signo)
|
||||
{
|
||||
/* Make sure we delete the input handler if we crash, so we don't leave
|
||||
* the terminal in raw mode. */
|
||||
delete handler;
|
||||
handler = NULL;
|
||||
}
|
||||
|
||||
|
||||
InputHandler_Linux_tty::InputHandler_Linux_tty()
|
||||
{
|
||||
fd = open("/dev/tty", O_RDWR);
|
||||
if(fd == -1)
|
||||
RageException::Throw("open(\"/dev/tty\"): %s", strerror(errno));
|
||||
|
||||
if (tcgetattr(fd, &saved_kbd_termios) == -1)
|
||||
RageException::Throw("tcgetattr(%i) failed: %s", fd, strerror(errno));
|
||||
termios keyboard_termios = saved_kbd_termios;
|
||||
keyboard_termios.c_lflag &= ~(ICANON | ECHO | ISIG);
|
||||
keyboard_termios.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
|
||||
keyboard_termios.c_cc[VMIN] = 0;
|
||||
keyboard_termios.c_cc[VTIME] = 0;
|
||||
if (tcsetattr(fd, TCSAFLUSH, &keyboard_termios) == -1)
|
||||
RageException::Throw("tcsetattr(%i, TCSAFLUSH) failed: %s", fd, strerror(errno));
|
||||
|
||||
if (ioctl(fd, KDGKBMODE, &saved_kbd_mode) == -1)
|
||||
RageException::Throw("ioctl(%i, KDGKBMODE) failed: %s", fd, strerror(errno));
|
||||
if (ioctl(fd, KDSKBMODE, K_MEDIUMRAW) == -1)
|
||||
RageException::Throw("ioctl(%i, KDSKBMODE, K_MEDIUMRAW) failed: %s", fd, strerror(errno));
|
||||
|
||||
if (ioctl(fd, KDSETMODE, KD_GRAPHICS) == -1 )
|
||||
RageException::Throw("ioctl(%i, KDSETMODE, KD_GRAPHICS) failed: %s", fd, strerror(errno));
|
||||
|
||||
memset(keys, 0, sizeof(keys));
|
||||
|
||||
for (int i = 0; i < NR_KEYS; ++i)
|
||||
{
|
||||
switch(i)
|
||||
{
|
||||
case SCANCODE_PRINTSCREEN: keys[i] = SDLK_PRINT; continue;
|
||||
case SCANCODE_BREAK: keys[i] = SDLK_BREAK; continue;
|
||||
case SCANCODE_BREAK_ALTERNATIVE: keys[i] = SDLK_PAUSE; continue;
|
||||
case SCANCODE_LEFTSHIFT: keys[i] = SDLK_LSHIFT; continue;
|
||||
case SCANCODE_RIGHTSHIFT: keys[i] = SDLK_RSHIFT; continue;
|
||||
case SCANCODE_LEFTCONTROL: keys[i] = SDLK_LCTRL; continue;
|
||||
case SCANCODE_RIGHTCONTROL: keys[i] = SDLK_RCTRL; continue;
|
||||
case SCANCODE_RIGHTWIN: keys[i] = SDLK_RSUPER; continue;
|
||||
case SCANCODE_LEFTWIN: keys[i] = SDLK_LSUPER; continue;
|
||||
case 127: keys[i] = SDLK_MENU; continue;
|
||||
}
|
||||
|
||||
kbentry entry;
|
||||
|
||||
entry.kb_table = 0;
|
||||
entry.kb_index = i;
|
||||
if (ioctl(fd, KDGKBENT, &entry))
|
||||
continue; /* error */
|
||||
|
||||
const int kern_map = entry.kb_value;
|
||||
|
||||
switch(kern_map)
|
||||
{
|
||||
case K_ENTER: keys[i] = SDLK_RETURN; break;
|
||||
case K_F1: keys[i] = SDLK_F1; break;
|
||||
case K_F2: keys[i] = SDLK_F2; break;
|
||||
case K_F3: keys[i] = SDLK_F3; break;
|
||||
case K_F4: keys[i] = SDLK_F4; break;
|
||||
case K_F5: keys[i] = SDLK_F5; break;
|
||||
case K_F6: keys[i] = SDLK_F6; break;
|
||||
case K_F7: keys[i] = SDLK_F7; break;
|
||||
case K_F8: keys[i] = SDLK_F8; break;
|
||||
case K_F9: keys[i] = SDLK_F9; break;
|
||||
case K_F10: keys[i] = SDLK_F10; break;
|
||||
case K_F11: keys[i] = SDLK_F11; break;
|
||||
case K_F12: keys[i] = SDLK_F12; break;
|
||||
case K_UP: keys[i] = SDLK_UP; break;
|
||||
case K_DOWN: keys[i] = SDLK_DOWN; break;
|
||||
case K_LEFT: keys[i] = SDLK_LEFT; break;
|
||||
case K_RIGHT: keys[i] = SDLK_RIGHT; break;
|
||||
case K_P0: keys[i] = SDLK_KP0; break;
|
||||
case K_P1: keys[i] = SDLK_KP1; break;
|
||||
case K_P2: keys[i] = SDLK_KP2; break;
|
||||
case K_P3: keys[i] = SDLK_KP3; break;
|
||||
case K_P4: keys[i] = SDLK_KP4; break;
|
||||
case K_P5: keys[i] = SDLK_KP5; break;
|
||||
case K_P6: keys[i] = SDLK_KP6; break;
|
||||
case K_P7: keys[i] = SDLK_KP7; break;
|
||||
case K_P8: keys[i] = SDLK_KP8; break;
|
||||
case K_P9: keys[i] = SDLK_KP9; break;
|
||||
case K_PPLUS: keys[i] = SDLK_KP_PLUS; break;
|
||||
case K_PMINUS: keys[i] = SDLK_KP_MINUS; break;
|
||||
case K_PSTAR: keys[i] = SDLK_KP_MULTIPLY; break;
|
||||
case K_PSLASH: keys[i] = SDLK_KP_DIVIDE; break;
|
||||
case K_PENTER: keys[i] = SDLK_KP_ENTER; break;
|
||||
case K_PDOT: keys[i] = SDLK_KP_PERIOD; break;
|
||||
case K_ALT: keys[i] = SDLK_LALT; break;
|
||||
case K_ALTGR: keys[i] = SDLK_RALT; break;
|
||||
case K_INSERT: keys[i] = SDLK_INSERT; break;
|
||||
case K_REMOVE: keys[i] = SDLK_DELETE; break;
|
||||
case K_PGUP: keys[i] = SDLK_PAGEUP; break;
|
||||
case K_PGDN: keys[i] = SDLK_PAGEDOWN; break;
|
||||
case K_FIND: keys[i] = SDLK_HOME; break;
|
||||
case K_SELECT: keys[i] = SDLK_END; break;
|
||||
case K_NUM: keys[i] = SDLK_NUMLOCK; break;
|
||||
case K_CAPS: keys[i] = SDLK_CAPSLOCK; break;
|
||||
case K_F13: keys[i] = SDLK_PRINT; break;
|
||||
case K_HOLD: keys[i] = SDLK_SCROLLOCK; break;
|
||||
case K_PAUSE: keys[i] = SDLK_PAUSE; break;
|
||||
case 127: keys[i] = SDLK_BACKSPACE; break;
|
||||
default: keys[i] = KVAL(kern_map);
|
||||
}
|
||||
}
|
||||
|
||||
handler = this;
|
||||
SignalHandler::OnClose(OnCrash);
|
||||
}
|
||||
|
||||
|
||||
InputHandler_Linux_tty::~InputHandler_Linux_tty()
|
||||
{
|
||||
LOG->Trace("~InputHandler_Linux_tty");
|
||||
ioctl(fd, KDSETMODE, KD_TEXT);
|
||||
ioctl(fd, KDSKBMODE, saved_kbd_mode);
|
||||
tcsetattr(fd, TCSAFLUSH, &saved_kbd_termios);
|
||||
close(fd);
|
||||
|
||||
handler = NULL;
|
||||
}
|
||||
|
||||
void InputHandler_Linux_tty::Update()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(fd, &fdset);
|
||||
|
||||
struct timeval zero = {0,0};
|
||||
if ( select(fd+1, &fdset, NULL, NULL, &zero) <= 0 )
|
||||
return;
|
||||
|
||||
unsigned char keybuf[BUFSIZ];
|
||||
SDL_keysym keysym;
|
||||
|
||||
int ret = read(fd, keybuf, BUFSIZ);
|
||||
for ( int i=0; i < ret; ++i ) {
|
||||
const int key = keybuf[i] & 0x7F;
|
||||
const int butno = keys[key];
|
||||
const bool pressed = !(keybuf[i] & 0x80);
|
||||
|
||||
ButtonPressed( DeviceInput(DEVICE_KEYBOARD, butno, pressed) );
|
||||
}
|
||||
}
|
||||
|
||||
InputHandler::UpdateTimer();
|
||||
}
|
||||
|
||||
void InputHandler_Linux_tty::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
vDevicesOut.push_back( InputDeviceInfo(DEVICE_KEYBOARD,"Keyboard") );
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef INPUT_HANDLER_LINUX_TTY_H
|
||||
#define INPUT_HANDLER_LINUX_TTY_H 1
|
||||
|
||||
#include "InputHandler.h"
|
||||
|
||||
class InputHandler_Linux_tty: public InputHandler
|
||||
{
|
||||
int fd;
|
||||
static void OnCrash(int);
|
||||
|
||||
public:
|
||||
void Update();
|
||||
InputHandler_Linux_tty();
|
||||
~InputHandler_Linux_tty();
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2003-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,142 @@
|
||||
/* Keyboard interface for svgalib. */
|
||||
/* Can be used independently. */
|
||||
|
||||
#ifndef VGAKEYBOARD_H
|
||||
#define VGAKEYBOARD_H
|
||||
|
||||
#define SCANCODE_ESCAPE 1
|
||||
|
||||
#define SCANCODE_1 2
|
||||
#define SCANCODE_2 3
|
||||
#define SCANCODE_3 4
|
||||
#define SCANCODE_4 5
|
||||
#define SCANCODE_5 6
|
||||
#define SCANCODE_6 7
|
||||
#define SCANCODE_7 8
|
||||
#define SCANCODE_8 9
|
||||
#define SCANCODE_9 10
|
||||
#define SCANCODE_0 11
|
||||
|
||||
#define SCANCODE_MINUS 12
|
||||
#define SCANCODE_EQUAL 13
|
||||
|
||||
#define SCANCODE_BACKSPACE 14
|
||||
#define SCANCODE_TAB 15
|
||||
|
||||
#define SCANCODE_Q 16
|
||||
#define SCANCODE_W 17
|
||||
#define SCANCODE_E 18
|
||||
#define SCANCODE_R 19
|
||||
#define SCANCODE_T 20
|
||||
#define SCANCODE_Y 21
|
||||
#define SCANCODE_U 22
|
||||
#define SCANCODE_I 23
|
||||
#define SCANCODE_O 24
|
||||
#define SCANCODE_P 25
|
||||
#define SCANCODE_BRACKET_LEFT 26
|
||||
#define SCANCODE_BRACKET_RIGHT 27
|
||||
|
||||
#define SCANCODE_ENTER 28
|
||||
|
||||
#define SCANCODE_LEFTCONTROL 29
|
||||
|
||||
#define SCANCODE_A 30
|
||||
#define SCANCODE_S 31
|
||||
#define SCANCODE_D 32
|
||||
#define SCANCODE_F 33
|
||||
#define SCANCODE_G 34
|
||||
#define SCANCODE_H 35
|
||||
#define SCANCODE_J 36
|
||||
#define SCANCODE_K 37
|
||||
#define SCANCODE_L 38
|
||||
#define SCANCODE_SEMICOLON 39
|
||||
#define SCANCODE_APOSTROPHE 40
|
||||
#define SCANCODE_GRAVE 41
|
||||
|
||||
#define SCANCODE_LEFTSHIFT 42
|
||||
#define SCANCODE_BACKSLASH 43
|
||||
|
||||
#define SCANCODE_Z 44
|
||||
#define SCANCODE_X 45
|
||||
#define SCANCODE_C 46
|
||||
#define SCANCODE_V 47
|
||||
#define SCANCODE_B 48
|
||||
#define SCANCODE_N 49
|
||||
#define SCANCODE_M 50
|
||||
#define SCANCODE_COMMA 51
|
||||
#define SCANCODE_PERIOD 52
|
||||
#define SCANCODE_SLASH 53
|
||||
|
||||
#define SCANCODE_RIGHTSHIFT 54
|
||||
#define SCANCODE_KEYPADMULTIPLY 55
|
||||
|
||||
#define SCANCODE_LEFTALT 56
|
||||
#define SCANCODE_SPACE 57
|
||||
#define SCANCODE_CAPSLOCK 58
|
||||
|
||||
#define SCANCODE_F1 59
|
||||
#define SCANCODE_F2 60
|
||||
#define SCANCODE_F3 61
|
||||
#define SCANCODE_F4 62
|
||||
#define SCANCODE_F5 63
|
||||
#define SCANCODE_F6 64
|
||||
#define SCANCODE_F7 65
|
||||
#define SCANCODE_F8 66
|
||||
#define SCANCODE_F9 67
|
||||
#define SCANCODE_F10 68
|
||||
|
||||
#define SCANCODE_NUMLOCK 69
|
||||
#define SCANCODE_SCROLLLOCK 70
|
||||
|
||||
#define SCANCODE_KEYPAD7 71
|
||||
#define SCANCODE_CURSORUPLEFT 71
|
||||
#define SCANCODE_KEYPAD8 72
|
||||
#define SCANCODE_CURSORUP 72
|
||||
#define SCANCODE_KEYPAD9 73
|
||||
#define SCANCODE_CURSORUPRIGHT 73
|
||||
#define SCANCODE_KEYPADMINUS 74
|
||||
#define SCANCODE_KEYPAD4 75
|
||||
#define SCANCODE_CURSORLEFT 75
|
||||
#define SCANCODE_KEYPAD5 76
|
||||
#define SCANCODE_KEYPAD6 77
|
||||
#define SCANCODE_CURSORRIGHT 77
|
||||
#define SCANCODE_KEYPADPLUS 78
|
||||
#define SCANCODE_KEYPAD1 79
|
||||
#define SCANCODE_CURSORDOWNLEFT 79
|
||||
#define SCANCODE_KEYPAD2 80
|
||||
#define SCANCODE_CURSORDOWN 80
|
||||
#define SCANCODE_KEYPAD3 81
|
||||
#define SCANCODE_CURSORDOWNRIGHT 81
|
||||
#define SCANCODE_KEYPAD0 82
|
||||
#define SCANCODE_KEYPADPERIOD 83
|
||||
|
||||
#define SCANCODE_LESS 86
|
||||
|
||||
#define SCANCODE_F11 87
|
||||
#define SCANCODE_F12 88
|
||||
|
||||
#define SCANCODE_KEYPADENTER 96
|
||||
#define SCANCODE_RIGHTCONTROL 97
|
||||
#define SCANCODE_CONTROL 97
|
||||
#define SCANCODE_KEYPADDIVIDE 98
|
||||
#define SCANCODE_PRINTSCREEN 99
|
||||
#define SCANCODE_RIGHTALT 100
|
||||
#define SCANCODE_BREAK 101 /* Beware: is 119 */
|
||||
#define SCANCODE_BREAK_ALTERNATIVE 119 /* on some keyboards! */
|
||||
|
||||
#define SCANCODE_HOME 102
|
||||
#define SCANCODE_CURSORBLOCKUP 103 /* Cursor key block */
|
||||
#define SCANCODE_PAGEUP 104
|
||||
#define SCANCODE_CURSORBLOCKLEFT 105 /* Cursor key block */
|
||||
#define SCANCODE_CURSORBLOCKRIGHT 106 /* Cursor key block */
|
||||
#define SCANCODE_END 107
|
||||
#define SCANCODE_CURSORBLOCKDOWN 108 /* Cursor key block */
|
||||
#define SCANCODE_PAGEDOWN 109
|
||||
#define SCANCODE_INSERT 110
|
||||
#define SCANCODE_REMOVE 111
|
||||
|
||||
#define SCANCODE_RIGHTWIN 126
|
||||
#define SCANCODE_LEFTWIN 125
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,482 @@
|
||||
#include "global.h"
|
||||
#include "RageLog.h"
|
||||
#include "InputHandler_MacOSX_HID.h"
|
||||
#include "Foreach.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "InputFilter.h"
|
||||
#include "archutils/Darwin/DarwinThreadHelpers.h"
|
||||
#include "archutils/Darwin/KeyboardDevice.h"
|
||||
#include "archutils/Darwin/JoystickDevice.h"
|
||||
#include "archutils/Darwin/PumpDevice.h"
|
||||
|
||||
#include <IOKit/IOMessage.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
REGISTER_INPUT_HANDLER_CLASS2( HID, MacOSX_HID );
|
||||
|
||||
void InputHandler_MacOSX_HID::QueueCallback( void *target, int result, void *refcon, void *sender )
|
||||
{
|
||||
// The result seems useless as you can't actually return anything...
|
||||
// refcon is the Device number
|
||||
|
||||
RageTimer now;
|
||||
InputHandler_MacOSX_HID *This = (InputHandler_MacOSX_HID *)target;
|
||||
IOHIDQueueInterface **queue = (IOHIDQueueInterface **)sender;
|
||||
IOHIDEventStruct event;
|
||||
AbsoluteTime zeroTime = { 0, 0 };
|
||||
HIDDevice *dev = This->m_vDevices[int( refcon )];
|
||||
vector<DeviceInput> vPresses;
|
||||
|
||||
while( (result = CALL(queue, getNextEvent, &event, zeroTime, 0)) == kIOReturnSuccess )
|
||||
{
|
||||
if( event.longValueSize != 0 && event.longValue != NULL )
|
||||
{
|
||||
free( event.longValue );
|
||||
continue;
|
||||
}
|
||||
//LOG->Trace( "Got event with cookie %p, value %d", event.elementCookie, int(event.value) );
|
||||
dev->GetButtonPresses( vPresses, event.elementCookie, event.value, now );
|
||||
}
|
||||
FOREACH_CONST( DeviceInput, vPresses, i )
|
||||
INPUTFILTER->ButtonPressed( *i );
|
||||
}
|
||||
|
||||
static void RunLoopStarted( CFRunLoopObserverRef o, CFRunLoopActivity a, void *sem )
|
||||
{
|
||||
CFRunLoopObserverInvalidate( o );
|
||||
CFRelease( o ); // we don't need this any longer
|
||||
((RageSemaphore *)sem)->Post();
|
||||
}
|
||||
|
||||
int InputHandler_MacOSX_HID::Run( void *data )
|
||||
{
|
||||
InputHandler_MacOSX_HID *This = (InputHandler_MacOSX_HID *)data;
|
||||
|
||||
This->m_LoopRef = CFRunLoopGetCurrent();
|
||||
CFRetain( This->m_LoopRef );
|
||||
|
||||
This->StartDevices();
|
||||
{
|
||||
const RString sError = SetThreadPrecedence( 1.0f );
|
||||
if( !sError.empty() )
|
||||
LOG->Warn( "Could not set precedence of the input thread: %s", sError.c_str() );
|
||||
}
|
||||
// Add an observer for the start of the run loop
|
||||
{
|
||||
/* The function copies the information out of the structure, so the memory pointed
|
||||
* to by context does not need to persist beyond the function call. */
|
||||
CFRunLoopObserverContext context = { 0, &This->m_Sem, NULL, NULL, NULL };
|
||||
CFRunLoopObserverRef o = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopEntry,
|
||||
false, 0, RunLoopStarted, &context);
|
||||
CFRunLoopAddObserver( This->m_LoopRef, o, kCFRunLoopDefaultMode );
|
||||
}
|
||||
|
||||
/* Add a source for ending the run loop. This serves two purposes:
|
||||
* 1. it provides a way to terminate the run loop when IH_MacOSX_HID exists, and
|
||||
* 2. it ensures that CFRunLoopRun() doesn't return immediately if there are no other sources. */
|
||||
{
|
||||
/* Being a little tricky here, the perform callback takes a void* and returns nothing.
|
||||
* CFRunLoopStop takes a CFRunLoopRef (a pointer) so cast the function and pass the loop ref. */
|
||||
void *info = This->m_LoopRef;
|
||||
void (*perform)(void *) = (void (*)(void *))CFRunLoopStop;
|
||||
// { version, info, retain, release, copyDescription, equal, hash, schedule, cancel, perform }
|
||||
CFRunLoopSourceContext context = { 0, info, NULL, NULL, NULL, NULL, NULL, NULL, NULL, perform };
|
||||
|
||||
// Pass 1 so that it is called after all inputs have been handled (they will have order = 0)
|
||||
This->m_SourceRef = CFRunLoopSourceCreate( kCFAllocatorDefault, 1, &context );
|
||||
|
||||
CFRunLoopAddSource( This->m_LoopRef, This->m_SourceRef, kCFRunLoopDefaultMode );
|
||||
}
|
||||
CFRunLoopRun();
|
||||
LOG->Trace( "Shutting down input handler thread..." );
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InputHandler_MacOSX_HID::DeviceAdded( void *refCon, io_iterator_t )
|
||||
{
|
||||
InputHandler_MacOSX_HID *This = (InputHandler_MacOSX_HID *)refCon;
|
||||
|
||||
LockMut( This->m_ChangeLock );
|
||||
This->m_bChanged = true;
|
||||
}
|
||||
|
||||
void InputHandler_MacOSX_HID::DeviceChanged( void *refCon, io_service_t service, natural_t messageType, void *arg )
|
||||
{
|
||||
if( messageType == kIOMessageServiceIsTerminated )
|
||||
{
|
||||
InputHandler_MacOSX_HID *This = (InputHandler_MacOSX_HID *)refCon;
|
||||
|
||||
LockMut( This->m_ChangeLock );
|
||||
This->m_bChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// m_LoopRef needs to be set before this is called
|
||||
void InputHandler_MacOSX_HID::StartDevices()
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
ASSERT( m_LoopRef );
|
||||
FOREACH( HIDDevice *, m_vDevices, i )
|
||||
(*i)->StartQueue( m_LoopRef, InputHandler_MacOSX_HID::QueueCallback, this, n++ );
|
||||
|
||||
CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource( m_NotifyPort );
|
||||
|
||||
CFRunLoopAddSource( m_LoopRef, runLoopSource, kCFRunLoopDefaultMode );
|
||||
}
|
||||
|
||||
InputHandler_MacOSX_HID::~InputHandler_MacOSX_HID()
|
||||
{
|
||||
FOREACH( HIDDevice *, m_vDevices, i )
|
||||
delete *i;
|
||||
if( PREFSMAN->m_bThreadedInput )
|
||||
{
|
||||
CFRunLoopSourceSignal( m_SourceRef );
|
||||
CFRunLoopWakeUp( m_LoopRef );
|
||||
m_InputThread.Wait();
|
||||
CFRelease( m_SourceRef );
|
||||
CFRelease( m_LoopRef );
|
||||
LOG->Trace( "Input handler thread shut down." );
|
||||
}
|
||||
|
||||
FOREACH( io_iterator_t, m_vIters, i )
|
||||
IOObjectRelease( *i );
|
||||
IONotificationPortDestroy( m_NotifyPort );
|
||||
}
|
||||
|
||||
static CFDictionaryRef GetMatchingDictionary( int usagePage, int usage )
|
||||
{
|
||||
// Build the matching dictionary.
|
||||
CFMutableDictionaryRef dict;
|
||||
|
||||
if( (dict = IOServiceMatching(kIOHIDDeviceKey)) == NULL )
|
||||
FAIL_M( "Couldn't create a matching dictionary." );
|
||||
// Refine the search by only looking for joysticks
|
||||
CFNumberRef usagePageRef = CFInt( usagePage );
|
||||
CFNumberRef usageRef = CFInt( usage );
|
||||
|
||||
CFDictionarySetValue( dict, CFSTR(kIOHIDPrimaryUsagePageKey), usagePageRef );
|
||||
CFDictionarySetValue( dict, CFSTR(kIOHIDPrimaryUsageKey), usageRef );
|
||||
|
||||
// Cleanup after ourselves
|
||||
CFRelease( usagePageRef );
|
||||
CFRelease( usageRef );
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
// Factor this out because nothing else in IH_C::AddDevices() needs to know the type of the device.
|
||||
static HIDDevice *MakeDevice( InputDevice id )
|
||||
{
|
||||
if( id == DEVICE_KEYBOARD )
|
||||
return new KeyboardDevice;
|
||||
if( IsJoystick(id) )
|
||||
return new JoystickDevice;
|
||||
if( IsPump(id) )
|
||||
return new PumpDevice;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void InputHandler_MacOSX_HID::AddDevices( int usagePage, int usage, InputDevice &id )
|
||||
{
|
||||
io_iterator_t iter;
|
||||
CFDictionaryRef dict = GetMatchingDictionary( usagePage, usage );
|
||||
kern_return_t ret = IOServiceAddMatchingNotification( m_NotifyPort, kIOFirstMatchNotification, dict,
|
||||
InputHandler_MacOSX_HID::DeviceAdded, this, &iter );
|
||||
io_object_t device;
|
||||
|
||||
if( ret != KERN_SUCCESS )
|
||||
return;
|
||||
|
||||
m_vIters.push_back( iter );
|
||||
|
||||
// Iterate over the devices and add them
|
||||
while( (device = IOIteratorNext(iter)) )
|
||||
{
|
||||
LOG->Trace( "\tFound device %d", id );
|
||||
HIDDevice *dev = MakeDevice( id );
|
||||
int num;
|
||||
|
||||
if( !dev )
|
||||
{
|
||||
LOG->Trace( "\t\tInvalid id, deleting device" );
|
||||
IOObjectRelease( device );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !dev->Open(device) || (num = dev->AssignIDs(id)) == -1 )
|
||||
{
|
||||
LOG->Trace( "\tFailed top open or assign id, deleting device" );
|
||||
delete dev;
|
||||
IOObjectRelease( device );
|
||||
continue;
|
||||
}
|
||||
io_iterator_t i;
|
||||
|
||||
enum_add( id, num );
|
||||
m_vDevices.push_back( dev );
|
||||
|
||||
ret = IOServiceAddInterestNotification( m_NotifyPort, device, kIOGeneralInterest,
|
||||
InputHandler_MacOSX_HID::DeviceChanged, this, &i );
|
||||
|
||||
if( ret == KERN_SUCCESS )
|
||||
m_vIters.push_back( i );
|
||||
else
|
||||
LOG->Trace( "\t\tFailed to add device changed notification, deleting device" );
|
||||
IOObjectRelease( device );
|
||||
}
|
||||
}
|
||||
|
||||
InputHandler_MacOSX_HID::InputHandler_MacOSX_HID() : m_Sem( "Input thread started" ), m_ChangeLock( "Input handler change lock" )
|
||||
{
|
||||
InputDevice id = DEVICE_KEYBOARD;
|
||||
|
||||
// Set up the notify ports.
|
||||
m_NotifyPort = IONotificationPortCreate( kIOMasterPortDefault );
|
||||
|
||||
// Add devices.
|
||||
LOG->Trace( "Finding keyboards" );
|
||||
AddDevices( kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard, id );
|
||||
LOG->Trace( "Finding joysticks" );
|
||||
id = DEVICE_JOY1;
|
||||
AddDevices( kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, id );
|
||||
AddDevices( kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, id );
|
||||
LOG->Trace( "Finding pump" );
|
||||
id = DEVICE_PUMP1;
|
||||
AddDevices( kHIDPage_VendorDefinedStart, 0x0001, id ); // Pump pads use the first vendor specific usage page.
|
||||
m_bChanged = false;
|
||||
|
||||
if( PREFSMAN->m_bThreadedInput )
|
||||
{
|
||||
m_InputThread.SetName( "Input thread" );
|
||||
m_InputThread.Create( InputHandler_MacOSX_HID::Run, this );
|
||||
// Wait for the run loop to start before returning.
|
||||
m_Sem.Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LoopRef = CFRunLoopRef( GetCFRunLoopFromEventLoop(GetMainEventLoop()) );
|
||||
CFRetain( m_LoopRef );
|
||||
StartDevices();
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler_MacOSX_HID::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevices )
|
||||
{
|
||||
FOREACH_CONST( HIDDevice *, m_vDevices, i )
|
||||
(*i)->GetDevicesAndDescriptions( vDevices );
|
||||
}
|
||||
|
||||
RString InputHandler_MacOSX_HID::GetDeviceSpecificInputString( const DeviceInput &di )
|
||||
{
|
||||
if( di.device == DEVICE_KEYBOARD )
|
||||
{
|
||||
#define OTHER(n) (KEY_OTHER_0 + (n))
|
||||
switch( di.button )
|
||||
{
|
||||
case KEY_DEL: return "del";
|
||||
case KEY_BACK: return "delete";
|
||||
case KEY_ENTER: return "return";
|
||||
case KEY_LALT: return "left option";
|
||||
case KEY_RALT: return "right option";
|
||||
case KEY_LMETA: return "left cmd";
|
||||
case KEY_RMETA: return "right cmd";
|
||||
case KEY_INSERT: return "help";
|
||||
case OTHER(0): return "F17";
|
||||
case OTHER(1): return "F18";
|
||||
case OTHER(2): return "F19";
|
||||
case OTHER(3): return "F20";
|
||||
case OTHER(4): return "F21";
|
||||
case OTHER(5): return "F22";
|
||||
case OTHER(6): return "F23";
|
||||
case OTHER(7): return "F25";
|
||||
case OTHER(8): return "execute";
|
||||
case OTHER(9): return "select";
|
||||
case OTHER(10): return "stop";
|
||||
case OTHER(11): return "again";
|
||||
case OTHER(12): return "undo";
|
||||
case OTHER(13): return "cut";
|
||||
case OTHER(14): return "copy";
|
||||
case OTHER(15): return "paste";
|
||||
case OTHER(16): return "find";
|
||||
case OTHER(17): return "mute";
|
||||
case OTHER(18): return "volume up";
|
||||
case OTHER(19): return "volume down";
|
||||
case OTHER(20): return "AS/400 equal";
|
||||
case OTHER(21): return "international 1";
|
||||
case OTHER(22): return "international 2";
|
||||
case OTHER(23): return "international 3";
|
||||
case OTHER(24): return "international 4";
|
||||
case OTHER(25): return "international 5";
|
||||
case OTHER(26): return "international 6";
|
||||
case OTHER(27): return "international 7";
|
||||
case OTHER(28): return "international 8";
|
||||
case OTHER(29): return "international 9";
|
||||
case OTHER(30): return "lang 1";
|
||||
case OTHER(31): return "lang 2";
|
||||
case OTHER(32): return "lang 3";
|
||||
case OTHER(33): return "lang 4";
|
||||
case OTHER(34): return "lang 5";
|
||||
case OTHER(35): return "lang 6";
|
||||
case OTHER(36): return "lang 7";
|
||||
case OTHER(37): return "lang 8";
|
||||
case OTHER(38): return "lang 9";
|
||||
case OTHER(39): return "alt erase";
|
||||
case OTHER(40): return "sys req";
|
||||
case OTHER(41): return "cancel";
|
||||
case OTHER(42): return "separator";
|
||||
case OTHER(43): return "out";
|
||||
case OTHER(44): return "oper";
|
||||
case OTHER(45): return "clear/again"; // XXX huh?
|
||||
case OTHER(46): return "cr sel/props"; // XXX
|
||||
case OTHER(47): return "ex sel";
|
||||
case OTHER(48): return "non US backslash";
|
||||
case OTHER(49): return "application";
|
||||
case OTHER(50): return "prior";
|
||||
}
|
||||
#undef OTHER
|
||||
}
|
||||
if( di.device == DEVICE_PUMP1 || di.device == DEVICE_PUMP2 )
|
||||
{
|
||||
switch( di.button )
|
||||
{
|
||||
case JOY_BUTTON_1: return "UL";
|
||||
case JOY_BUTTON_2: return "UR";
|
||||
case JOY_BUTTON_3: return "MID";
|
||||
case JOY_BUTTON_4: return "DL";
|
||||
case JOY_BUTTON_5: return "DR";
|
||||
case JOY_BUTTON_6: return "Esc";
|
||||
case JOY_BUTTON_7: return "P2 UL";
|
||||
case JOY_BUTTON_8: return "P2 UR";
|
||||
case JOY_BUTTON_9: return "P2 MID";
|
||||
case JOY_BUTTON_10: return "P2 DL";
|
||||
case JOY_BUTTON_11: return "P2 DR";
|
||||
}
|
||||
}
|
||||
|
||||
return InputHandler::GetDeviceSpecificInputString( di );
|
||||
}
|
||||
|
||||
wchar_t InputHandler_MacOSX_HID::DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers )
|
||||
{
|
||||
// KeyTranslate maps these keys to a character. They shouldn't be mapped to any character.
|
||||
switch( button )
|
||||
{
|
||||
default:
|
||||
if( (button >= KEY_F1 && button <= KEY_F16) )
|
||||
return L'\0';
|
||||
break;
|
||||
case KEY_UP:
|
||||
case KEY_DOWN:
|
||||
case KEY_LEFT:
|
||||
case KEY_RIGHT:
|
||||
case KEY_ESC:
|
||||
case KEY_TAB:
|
||||
case KEY_ENTER:
|
||||
case KEY_PRTSC:
|
||||
case KEY_SCRLLOCK:
|
||||
case KEY_PAUSE:
|
||||
case KEY_DEL:
|
||||
case KEY_HOME:
|
||||
case KEY_END:
|
||||
case KEY_PGUP:
|
||||
case KEY_PGDN:
|
||||
case KEY_NUMLOCK:
|
||||
case KEY_KP_ENTER:
|
||||
return L'\0';
|
||||
}
|
||||
|
||||
// Find the USB key code for this DeviceButton
|
||||
UInt8 iMacVirtualKey;
|
||||
if( KeyboardDevice::DeviceButtonToMacVirtualKey( button, iMacVirtualKey ) )
|
||||
{
|
||||
UInt32 modifiers = 0;
|
||||
if( bUseCurrentKeyModifiers )
|
||||
modifiers = GetCurrentKeyModifiers();
|
||||
|
||||
SInt16 iCurrentKeyScript = GetScriptManagerVariable( smKeyScript );
|
||||
SInt16 iCurrentKeyLayoutID = GetScriptVariable( iCurrentKeyScript, smScriptKeys );
|
||||
static SInt16 iLastKeyLayoutID = !iCurrentKeyLayoutID; // Just be different.
|
||||
static UInt32 iDeadKeyState;
|
||||
static UCKeyboardLayout **KeyLayout;
|
||||
|
||||
if( iCurrentKeyLayoutID != iLastKeyLayoutID )
|
||||
{
|
||||
iDeadKeyState = 0;
|
||||
KeyLayout = (UCKeyboardLayout **)GetResource( 'uchr', iCurrentKeyLayoutID );
|
||||
iLastKeyLayoutID = iCurrentKeyLayoutID;
|
||||
}
|
||||
if( KeyLayout )
|
||||
{
|
||||
UInt32 keyboardType = LMGetKbdType();
|
||||
UInt32 modifiers = bUseCurrentKeyModifiers ? GetCurrentKeyModifiers() : 0;
|
||||
UniChar unicodeInputString[4];
|
||||
UniCharCount length;
|
||||
OSStatus status = UCKeyTranslate( *KeyLayout, iMacVirtualKey, kUCKeyActionDown, modifiers,
|
||||
keyboardType, 0, &iDeadKeyState, ARRAYLEN(unicodeInputString),
|
||||
&length, unicodeInputString );
|
||||
|
||||
if( status )
|
||||
return L'\0';
|
||||
|
||||
CFStringRef inputString = CFStringCreateWithCharacters( NULL, unicodeInputString, length );
|
||||
char utf8InputString[7]; // Max size is 6 (although really only 4 are used) + null.
|
||||
|
||||
if( !CFStringGetCString(inputString, utf8InputString, 7, kCFStringEncodingUTF8) )
|
||||
{
|
||||
CFRelease( inputString );
|
||||
return L'\0';
|
||||
}
|
||||
|
||||
wchar_t ch = utf8_get_char( utf8InputString );
|
||||
|
||||
CFRelease( inputString );
|
||||
return ch == INVALID_CHAR ? L'\0' : ch;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall back on the 'KCHR' resource.
|
||||
static unsigned long state = 0;
|
||||
static Ptr keymap = NULL;
|
||||
Ptr new_keymap;
|
||||
|
||||
new_keymap = (Ptr)GetScriptManagerVariable(smKCHRCache);
|
||||
if( new_keymap != keymap )
|
||||
{
|
||||
keymap = new_keymap;
|
||||
state = 0;
|
||||
}
|
||||
// XXX: Only returns ascii. é will be returned as e.
|
||||
return KeyTranslate( keymap, UInt16(iMacVirtualKey)|modifiers, &state ) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
return InputHandler::DeviceButtonToChar( button, bUseCurrentKeyModifiers );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (c) 2005, 2006 Steve Checkoway
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef INPUT_HANDLER_MACOSX_HID_H
|
||||
#define INPUT_HANDLER_MACOSX_HID_H
|
||||
|
||||
#include <vector>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include "InputHandler.h"
|
||||
#include "RageThreads.h"
|
||||
|
||||
class HIDDevice;
|
||||
|
||||
class InputHandler_MacOSX_HID : public InputHandler
|
||||
{
|
||||
private:
|
||||
vector<HIDDevice *> m_vDevices;
|
||||
RageThread m_InputThread;
|
||||
RageSemaphore m_Sem;
|
||||
CFRunLoopRef m_LoopRef;
|
||||
CFRunLoopSourceRef m_SourceRef;
|
||||
vector<io_iterator_t> m_vIters; // We don't really care about these but they need to stick around
|
||||
IONotificationPortRef m_NotifyPort;
|
||||
RageMutex m_ChangeLock;
|
||||
bool m_bChanged;
|
||||
|
||||
static int Run( void *data );
|
||||
static void DeviceAdded( void *refCon, io_iterator_t iter );
|
||||
static void DeviceChanged( void *refCon, io_service_t service, natural_t messageType, void *arg );
|
||||
void StartDevices();
|
||||
void AddDevices( int usagePage, int usage, InputDevice &id );
|
||||
|
||||
public:
|
||||
InputHandler_MacOSX_HID();
|
||||
~InputHandler_MacOSX_HID();
|
||||
|
||||
bool DevicesChanged() { LockMut( m_ChangeLock ); return m_bChanged; }
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
RString GetDeviceSpecificInputString( const DeviceInput &di );
|
||||
wchar_t DeviceButtonToChar( DeviceButton button, bool bUseCurrentKeyModifiers );
|
||||
static void QueueCallback( void *target, int result, void *refcon, void *sender );
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2005-2006 Steve Checkoway
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "global.h"
|
||||
#include "InputHandler_MonkeyKeyboard.h"
|
||||
#include "RageUtil.h"
|
||||
#include "PrefsManager.h"
|
||||
|
||||
|
||||
InputHandler_MonkeyKeyboard::InputHandler_MonkeyKeyboard()
|
||||
{
|
||||
m_dbLast = DeviceButton_Invalid;
|
||||
}
|
||||
|
||||
InputHandler_MonkeyKeyboard::~InputHandler_MonkeyKeyboard()
|
||||
{
|
||||
}
|
||||
|
||||
void InputHandler_MonkeyKeyboard::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
vDevicesOut.push_back( InputDeviceInfo(DEVICE_KEYBOARD, "MonkeyKeyboard") );
|
||||
}
|
||||
|
||||
static const DeviceButton g_keys[] =
|
||||
{
|
||||
// Some of the default keys for the dance game type
|
||||
KEY_LEFT, // DANCE_BUTTON_LEFT,
|
||||
KEY_RIGHT, // DANCE_BUTTON_RIGHT,
|
||||
KEY_UP, // DANCE_BUTTON_UP,
|
||||
KEY_DOWN, // DANCE_BUTTON_DOWN,
|
||||
KEY_ENTER, // DANCE_BUTTON_START,
|
||||
KEY_ENTER, // DANCE_BUTTON_START,
|
||||
KEY_ENTER, // DANCE_BUTTON_START,
|
||||
KEY_DEL, // DANCE_BUTTON_MENULEFT
|
||||
KEY_PGDN, // DANCE_BUTTON_MENURIGHT
|
||||
KEY_HOME, // DANCE_BUTTON_MENUUP
|
||||
KEY_END, // DANCE_BUTTON_MENUDOWN
|
||||
KEY_F1, // DANCE_BUTTON_COIN
|
||||
KEY_F1, // DANCE_BUTTON_COIN
|
||||
KEY_KP_C4, // DANCE_BUTTON_LEFT,
|
||||
KEY_KP_C6, // DANCE_BUTTON_RIGHT,
|
||||
KEY_KP_C8, // DANCE_BUTTON_UP,
|
||||
KEY_KP_C2, // DANCE_BUTTON_DOWN,
|
||||
KEY_KP_C7, // DANCE_BUTTON_UPLEFT,
|
||||
KEY_KP_C9, // DANCE_BUTTON_UPRIGHT,
|
||||
KEY_KP_ENTER, // DANCE_BUTTON_START,
|
||||
KEY_KP_ENTER, // DANCE_BUTTON_START,
|
||||
KEY_KP_ENTER, // DANCE_BUTTON_START,
|
||||
KEY_KP_SLASH, // DANCE_BUTTON_MENULEFT
|
||||
KEY_KP_ASTERISK, // DANCE_BUTTON_MENURIGHT
|
||||
KEY_KP_HYPHEN, // DANCE_BUTTON_MENUUP
|
||||
KEY_KP_PLUS, // DANCE_BUTTON_MENUDOWN
|
||||
};
|
||||
|
||||
static DeviceButton GetRandomKeyboardKey()
|
||||
{
|
||||
int index = RandomInt( ARRAYLEN(g_keys) );
|
||||
return g_keys[index];
|
||||
}
|
||||
|
||||
|
||||
void InputHandler_MonkeyKeyboard::Update()
|
||||
{
|
||||
if( !PREFSMAN->m_bMonkeyInput )
|
||||
{
|
||||
if( m_dbLast != DeviceButton_Invalid )
|
||||
{
|
||||
// End the previous key
|
||||
DeviceInput di = DeviceInput( DEVICE_KEYBOARD, m_dbLast, 0 );
|
||||
ButtonPressed( di );
|
||||
m_dbLast = DeviceButton_Invalid;
|
||||
}
|
||||
InputHandler::UpdateTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
float fSecsAgo = m_timerPressButton.Ago();
|
||||
|
||||
if( fSecsAgo > 0.5 )
|
||||
{
|
||||
if( m_dbLast != DeviceButton_Invalid )
|
||||
{
|
||||
// End the previous key
|
||||
DeviceInput di = DeviceInput( DEVICE_KEYBOARD, m_dbLast, 0 );
|
||||
ButtonPressed( di );
|
||||
}
|
||||
|
||||
// Choose a new key and send it.
|
||||
m_dbLast = GetRandomKeyboardKey();
|
||||
DeviceInput di = DeviceInput( DEVICE_KEYBOARD, m_dbLast, 1 );
|
||||
ButtonPressed( di );
|
||||
m_timerPressButton.Touch();
|
||||
}
|
||||
|
||||
InputHandler::UpdateTimer();
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2002-2004 Chris Danford
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef INPUT_HANDLER_MONKEY_SCRIPT
|
||||
#define INPUT_HANDLER_MONKEY_SCRIPT
|
||||
|
||||
#include "InputHandler.h"
|
||||
#include "RageTimer.h"
|
||||
#include "RageInputDevice.h"
|
||||
|
||||
class InputHandler_MonkeyKeyboard: public InputHandler
|
||||
{
|
||||
public:
|
||||
void Update();
|
||||
InputHandler_MonkeyKeyboard();
|
||||
~InputHandler_MonkeyKeyboard();
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
|
||||
private:
|
||||
RageTimer m_timerPressButton;
|
||||
DeviceButton m_dbLast; // Last input that we sent
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2002-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "global.h"
|
||||
#include "RageUtil.h"
|
||||
#include "InputHandler_Win32_MIDI.h"
|
||||
#include "RageLog.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#pragma comment (lib,"winmm.lib")
|
||||
|
||||
REGISTER_INPUT_HANDLER_CLASS2( MIDI, Win32_MIDI );
|
||||
|
||||
static HMIDIIN g_device;
|
||||
static void CALLBACK midiCallback(HMIDIIN g_device, UINT status, DWORD instancePtr, DWORD data, DWORD timestamp);
|
||||
|
||||
static RString GetMidiError( MMRESULT result )
|
||||
{
|
||||
char szError[256];
|
||||
midiOutGetErrorText( result, szError, 256 );
|
||||
return szError;
|
||||
}
|
||||
|
||||
InputHandler_Win32_MIDI::InputHandler_Win32_MIDI()
|
||||
{
|
||||
int device_id = 0;
|
||||
|
||||
g_device = NULL;
|
||||
|
||||
if( device_id >= (int) midiInGetNumDevs() )
|
||||
{
|
||||
m_bFoundDevice = false;
|
||||
return;
|
||||
}
|
||||
m_bFoundDevice = true;
|
||||
|
||||
MMRESULT result = midiInOpen( &g_device, device_id, (DWORD) &midiCallback, (DWORD) this, CALLBACK_FUNCTION );
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
LOG->Warn( "Error opening MIDI device: %s", GetMidiError(result).c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
result = midiInStart(g_device);
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
LOG->Warn( "Error starting MIDI device: %s", GetMidiError(result).c_str() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
InputHandler_Win32_MIDI::~InputHandler_Win32_MIDI()
|
||||
{
|
||||
MMRESULT result;
|
||||
|
||||
result = midiInReset( g_device );
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
LOG->Warn( "Error resetting MIDI device: %s", GetMidiError(result).c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
result = midiInClose( g_device );
|
||||
if( result != MMSYSERR_NOERROR )
|
||||
{
|
||||
LOG->Warn( "Error closing MIDI device: %s", GetMidiError(result).c_str() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler_Win32_MIDI::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
if( m_bFoundDevice )
|
||||
{
|
||||
vDevicesOut.push_back( InputDeviceInfo(DEVICE_MIDI,"Win32_MIDI") );
|
||||
}
|
||||
}
|
||||
|
||||
static void CALLBACK midiCallback( HMIDIIN device, UINT status, DWORD instancePtr, DWORD data, DWORD timestamp )
|
||||
{
|
||||
if( status == MIM_DATA )
|
||||
{
|
||||
int iType = data & 0xff;
|
||||
int iChannel = (data & 0xff00) >> 8;
|
||||
int iValue = (data & 0xff0000) >> 16;
|
||||
|
||||
// Channel 0 in midi is a special channel that generally will get triggered when too many channels are pressed.
|
||||
if( iChannel == 0 )
|
||||
return;
|
||||
|
||||
if( iType == 144 )
|
||||
{
|
||||
DeviceInput di = DeviceInput( DEVICE_MIDI, enum_add2(MIDI_FIRST, iChannel), iValue > 0 );
|
||||
di.ts.Touch();
|
||||
((InputHandler_Win32_MIDI *)instancePtr)->SetDev( di );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005 Charles Lohr
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef INPUT_HANDLER_WIN32_MIDI
|
||||
#define INPUT_HANDLER_WIN32_MIDI
|
||||
|
||||
#include "InputHandler.h"
|
||||
#include "RageInputDevice.h"
|
||||
|
||||
class InputHandler_Win32_MIDI: public InputHandler
|
||||
{
|
||||
public:
|
||||
InputHandler_Win32_MIDI();
|
||||
~InputHandler_Win32_MIDI();
|
||||
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
|
||||
void SetDev( DeviceInput key ) { ButtonPressed( key ); }
|
||||
|
||||
private:
|
||||
bool m_bFoundDevice;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* (c) 2002-2005 Charles Lohr, Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "global.h"
|
||||
#include "InputHandler_Win32_Para.h"
|
||||
|
||||
#include "RageLog.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageInputDevice.h"
|
||||
#include "archutils/Win32/USB.h"
|
||||
|
||||
// TODO: Abstract this windows-specific stuff into USBDevice.
|
||||
extern "C" {
|
||||
#include "archutils/Win32/ddk/setupapi.h"
|
||||
/* Quiet header warning: */
|
||||
#include "archutils/Win32/ddk/hidsdi.h"
|
||||
}
|
||||
|
||||
REGISTER_INPUT_HANDLER_CLASS2( Para, Win32_Para );
|
||||
|
||||
static void InitHack( HANDLE h )
|
||||
{
|
||||
UCHAR hack[] = {0, 1};
|
||||
|
||||
if( HidD_SetFeature(h, (PVOID) hack, 2) == TRUE )
|
||||
LOG->Info( "Para controller powered on successfully" );
|
||||
else
|
||||
LOG->Warn( "Para controller power-on failed" );
|
||||
}
|
||||
|
||||
InputHandler_Win32_Para::InputHandler_Win32_Para()
|
||||
{
|
||||
const int para_usb_vid = 0x0507;
|
||||
const int para_usb_pid = 0x0011;
|
||||
|
||||
USBDevice *dev = new USBDevice;
|
||||
|
||||
if( dev->Open(para_usb_vid, para_usb_pid, sizeof(long), 0, InitHack) )
|
||||
{
|
||||
LOG->Info("Para controller initialized");
|
||||
}
|
||||
SAFE_DELETE( dev );
|
||||
}
|
||||
|
||||
void InputHandler_Win32_Para::GetDevicesAndDescriptions(vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
// The device appears as a HID joystick
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2002-2004 Chris Danford, Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Initializes a USB Para controller so that it will function as a regular HID joystick. */
|
||||
|
||||
#ifndef INPUT_HANDLER_WIN32_PARA_H
|
||||
#define INPUT_HANDLER_WIN32_PARA_H
|
||||
|
||||
#include "InputHandler.h"
|
||||
|
||||
class InputHandler_Win32_Para: public InputHandler
|
||||
{
|
||||
public:
|
||||
InputHandler_Win32_Para();
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2002-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,182 @@
|
||||
#include "global.h"
|
||||
#include "InputHandler_Win32_Pump.h"
|
||||
|
||||
#include "PrefsManager.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageInputDevice.h"
|
||||
#include "archutils/Win32/ErrorStrings.h"
|
||||
#include "archutils/Win32/USB.h"
|
||||
|
||||
REGISTER_INPUT_HANDLER_CLASS2( Pump, Win32_Pump );
|
||||
|
||||
InputHandler_Win32_Pump::InputHandler_Win32_Pump()
|
||||
{
|
||||
m_bShutdown = false;
|
||||
const int pump_usb_vid = 0x0d2f, pump_usb_pid = 0x0001;
|
||||
|
||||
m_pDevice = new USBDevice[NUM_PUMPS];
|
||||
|
||||
bool bFoundOnePad = false;
|
||||
for( int i = 0; i < NUM_PUMPS; ++i )
|
||||
{
|
||||
if( m_pDevice[i].Open(pump_usb_vid, pump_usb_pid, sizeof(long), i, NULL) )
|
||||
{
|
||||
bFoundOnePad = true;
|
||||
LOG->Info( "Found Pump pad %i", i );
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't start a thread if we have no pads. */
|
||||
if( bFoundOnePad && PREFSMAN->m_bThreadedInput )
|
||||
{
|
||||
InputThread.SetName( "Pump thread" );
|
||||
InputThread.Create( InputThread_Start, this );
|
||||
}
|
||||
}
|
||||
|
||||
InputHandler_Win32_Pump::~InputHandler_Win32_Pump()
|
||||
{
|
||||
if( InputThread.IsCreated() )
|
||||
{
|
||||
m_bShutdown = true;
|
||||
LOG->Trace( "Shutting down Pump thread ..." );
|
||||
InputThread.Wait();
|
||||
LOG->Trace( "Pump thread shut down." );
|
||||
}
|
||||
|
||||
delete[] m_pDevice;
|
||||
}
|
||||
|
||||
void InputHandler_Win32_Pump::HandleInput( int iDevice, int iEvent )
|
||||
{
|
||||
static const int bits[] = {
|
||||
/* P1 */ (1<<9), (1<<12), (1<<13), (1<<11), (1<<10),
|
||||
/* ESC */ (1<<16),
|
||||
/* P1 */ (1<<17), (1<<20), (1<<21), (1<<19), (1<<18),
|
||||
};
|
||||
|
||||
InputDevice id = InputDevice( DEVICE_PUMP1 + iDevice );
|
||||
|
||||
for( int iButton = 0; iButton < ARRAYLEN(bits); ++iButton )
|
||||
{
|
||||
DeviceInput di( id, enum_add2(JOY_BUTTON_1, iButton), !(iEvent & bits[iButton]) );
|
||||
|
||||
/* If we're in a thread, our timestamp is accurate. */
|
||||
if( InputThread.IsCreated() )
|
||||
di.ts.Touch();
|
||||
|
||||
ButtonPressed( di );
|
||||
}
|
||||
}
|
||||
|
||||
RString InputHandler_Win32_Pump::GetDeviceSpecificInputString( const DeviceInput &di )
|
||||
{
|
||||
switch( di.button )
|
||||
{
|
||||
case JOY_BUTTON_1: return "UL";
|
||||
case JOY_BUTTON_2: return "UR";
|
||||
case JOY_BUTTON_3: return "MID";
|
||||
case JOY_BUTTON_4: return "DL";
|
||||
case JOY_BUTTON_5: return "DR";
|
||||
case JOY_BUTTON_6: return "Esc";
|
||||
case JOY_BUTTON_7: return "P2 UL";
|
||||
case JOY_BUTTON_8: return "P2 UR";
|
||||
case JOY_BUTTON_9: return "P2 MID";
|
||||
case JOY_BUTTON_10: return "P2 DL";
|
||||
case JOY_BUTTON_11: return "P2 DR";
|
||||
}
|
||||
|
||||
return InputHandler::GetDeviceSpecificInputString( di );
|
||||
}
|
||||
|
||||
void InputHandler_Win32_Pump::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
for(int i = 0; i < NUM_PUMPS; ++i)
|
||||
{
|
||||
if( m_pDevice[i].IsOpen() )
|
||||
{
|
||||
vDevicesOut.push_back( InputDeviceInfo(InputDevice(DEVICE_PUMP1+i),"Pump USB") );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int InputHandler_Win32_Pump::InputThread_Start( void *p )
|
||||
{
|
||||
((InputHandler_Win32_Pump *) p)->InputThreadMain();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InputHandler_Win32_Pump::InputThreadMain()
|
||||
{
|
||||
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) )
|
||||
LOG->Warn( werr_ssprintf(GetLastError(), "Failed to set Pump thread priority") );
|
||||
|
||||
/* Enable priority boosting. */
|
||||
SetThreadPriorityBoost( GetCurrentThread(), FALSE );
|
||||
|
||||
vector<WindowsFileIO *> apSources;
|
||||
for( int i = 0; i < NUM_PUMPS; ++i )
|
||||
{
|
||||
if( m_pDevice[i].m_IO.IsOpen() )
|
||||
apSources.push_back( &m_pDevice[i].m_IO );
|
||||
}
|
||||
|
||||
while( !m_bShutdown )
|
||||
{
|
||||
CHECKPOINT;
|
||||
int iActual = 0, iVal = 0;
|
||||
int iRet = WindowsFileIO::read_several( apSources, &iVal, iActual, 0.100f );
|
||||
|
||||
CHECKPOINT;
|
||||
if( iRet <= 0 )
|
||||
continue; /* no event */
|
||||
|
||||
HandleInput( iActual, iVal );
|
||||
InputHandler::UpdateTimer();
|
||||
}
|
||||
CHECKPOINT;
|
||||
}
|
||||
|
||||
void InputHandler_Win32_Pump::Update()
|
||||
{
|
||||
if( !InputThread.IsCreated() )
|
||||
{
|
||||
for( int i = 0; i < NUM_PUMPS; ++i )
|
||||
{
|
||||
int iRet = m_pDevice[i].GetPadEvent();
|
||||
|
||||
if( iRet == -1 )
|
||||
continue; /* no event */
|
||||
|
||||
HandleInput( i, iRet );
|
||||
}
|
||||
InputHandler::UpdateTimer();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2002-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef INPUT_HANDLER_WIN32_PUMP_H
|
||||
#define INPUT_HANDLER_WIN32_PUMP_H
|
||||
|
||||
#include "InputHandler.h"
|
||||
#include "RageThreads.h"
|
||||
|
||||
class USBDevice;
|
||||
class InputHandler_Win32_Pump: public InputHandler
|
||||
{
|
||||
public:
|
||||
void Update();
|
||||
InputHandler_Win32_Pump();
|
||||
~InputHandler_Win32_Pump();
|
||||
RString GetDeviceSpecificInputString( const DeviceInput &di );
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
|
||||
private:
|
||||
USBDevice *m_pDevice;
|
||||
RageThread InputThread;
|
||||
bool m_bShutdown;
|
||||
|
||||
static int InputThread_Start( void *p );
|
||||
void InputThreadMain();
|
||||
void HandleInput( int devno, int event );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2002-2004 Glenn Maynard
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
#include "global.h"
|
||||
#include "InputHandler_X11.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageDisplay.h"
|
||||
#include "InputFilter.h"
|
||||
#include "archutils/Unix/X11Helper.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
using namespace X11Helper;
|
||||
|
||||
REGISTER_INPUT_HANDLER_CLASS( X11 );
|
||||
|
||||
static DeviceButton XSymToDeviceButton( int key )
|
||||
{
|
||||
#define KEY_INV DeviceButton_Invalid
|
||||
static const DeviceButton ASCIIKeySyms[] =
|
||||
{
|
||||
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 0 - 4 */
|
||||
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 5 - 9 */
|
||||
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 10 - 14 */
|
||||
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 15 - 19 */
|
||||
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 20 - 24 */
|
||||
KEY_INV , KEY_INV , KEY_INV , KEY_INV , KEY_INV , /* 25 - 29 */
|
||||
KEY_INV , KEY_INV , KEY_SPACE , KEY_EXCL , KEY_QUOTE , /* 30 - 34 */
|
||||
KEY_HASH , KEY_DOLLAR , KEY_PERCENT , KEY_AMPER , KEY_SQUOTE , /* 35 - 39 */
|
||||
KEY_LPAREN , KEY_RPAREN , KEY_ASTERISK , KEY_PLUS , KEY_COMMA , /* 40 - 44 */
|
||||
KEY_HYPHEN , KEY_PERIOD , KEY_SLASH , KEY_C0 , KEY_C1 , /* 45 - 49 */
|
||||
KEY_C2 , KEY_C3 , KEY_C4 , KEY_C5 , KEY_C6 , /* 50 - 54 */
|
||||
KEY_C7 , KEY_C8 , KEY_C9 , KEY_COLON , KEY_SEMICOLON, /* 55 - 59 */
|
||||
KEY_LANGLE , KEY_EQUAL , KEY_RANGLE , KEY_QUESTION, KEY_AT , /* 60 - 64 */
|
||||
KEY_CA , KEY_CB , KEY_CC , KEY_CD , KEY_CE , /* 65 - 69 */
|
||||
KEY_CF , KEY_CG , KEY_CH , KEY_CI , KEY_CJ , /* 70 - 74 */
|
||||
KEY_CK , KEY_CL , KEY_CM , KEY_CN , KEY_CO , /* 75 - 79 */
|
||||
KEY_CP , KEY_CQ , KEY_CR , KEY_CS , KEY_CT , /* 80 - 84 */
|
||||
KEY_CU , KEY_CV , KEY_CW , KEY_CX , KEY_CY , /* 85 - 89 */
|
||||
KEY_CZ , KEY_LBRACKET, KEY_BACKSLASH, KEY_RBRACKET, KEY_CARAT , /* 90 - 94 */
|
||||
KEY_UNDERSCORE, KEY_ACCENT , KEY_Ca , KEY_Cb , KEY_Cc , /* 95 - 99 */
|
||||
KEY_Cd , KEY_Ce , KEY_Cf , KEY_Cg , KEY_Ch , /* 100 - 104 */
|
||||
KEY_Ci , KEY_Cj , KEY_Ck , KEY_Cl , KEY_Cm , /* 105 - 109 */
|
||||
KEY_Cn , KEY_Co , KEY_Cp , KEY_Cq , KEY_Cr , /* 110 - 114 */
|
||||
KEY_Cs , KEY_Ct , KEY_Cu , KEY_Cv , KEY_Cw , /* 115 - 119 */
|
||||
KEY_Cx , KEY_Cy , KEY_Cz , KEY_LBRACE , KEY_PIPE , /* 120 - 124 */
|
||||
KEY_RBRACE , KEY_INV , KEY_DEL /* 125 - 127 */
|
||||
};
|
||||
|
||||
/* 32...127: */
|
||||
if( key < int(ARRAYLEN(ASCIIKeySyms)))
|
||||
return ASCIIKeySyms[key];
|
||||
|
||||
/* XK_KP_0 ... XK_KP_9 to KEY_KP_C0 ... KEY_KP_C9 */
|
||||
if( key >= XK_KP_0 && key <= XK_KP_9 )
|
||||
return enum_add2(KEY_KP_C0, key - XK_KP_0);
|
||||
|
||||
switch( key )
|
||||
{
|
||||
/* These are needed because of the way X registers the keypad. */
|
||||
case XK_BackSpace: return KEY_BACK;
|
||||
case XK_Tab: return KEY_TAB;
|
||||
case XK_Pause: return KEY_PAUSE;
|
||||
case XK_Escape: return KEY_ESC;
|
||||
case XK_KP_Insert: return KEY_KP_C0;
|
||||
case XK_KP_End: return KEY_KP_C1;
|
||||
case XK_KP_Down: return KEY_KP_C2;
|
||||
case XK_KP_Page_Down: return KEY_KP_C3;
|
||||
case XK_KP_Left: return KEY_KP_C4;
|
||||
case XK_KP_Begin: return KEY_KP_C5;
|
||||
case XK_KP_Right: return KEY_KP_C6;
|
||||
case XK_KP_Home: return KEY_KP_C7;
|
||||
case XK_KP_Up: return KEY_KP_C8;
|
||||
case XK_KP_Page_Up: return KEY_KP_C9;
|
||||
case XK_KP_Decimal: return KEY_KP_PERIOD;
|
||||
case XK_KP_Divide: return KEY_KP_SLASH;
|
||||
case XK_KP_Multiply: return KEY_KP_ASTERISK;
|
||||
case XK_KP_Subtract: return KEY_KP_HYPHEN;
|
||||
case XK_KP_Add: return KEY_KP_PLUS;
|
||||
case XK_KP_Equal: return KEY_KP_EQUAL;
|
||||
case XK_KP_Enter: return KEY_KP_ENTER;
|
||||
case XK_Up: return KEY_UP;
|
||||
case XK_Down: return KEY_DOWN;
|
||||
case XK_Right: return KEY_RIGHT;
|
||||
case XK_Left: return KEY_LEFT;
|
||||
case XK_Insert: return KEY_INSERT;
|
||||
case XK_Home: return KEY_HOME;
|
||||
case XK_Delete: return KEY_DEL;
|
||||
case XK_End: return KEY_END;
|
||||
case XK_Page_Up: return KEY_PGUP;
|
||||
case XK_Page_Down: return KEY_PGDN;
|
||||
case XK_F1: return KEY_F1;
|
||||
case XK_F2: return KEY_F2;
|
||||
case XK_F3: return KEY_F3;
|
||||
case XK_F4: return KEY_F4;
|
||||
case XK_F5: return KEY_F5;
|
||||
case XK_F6: return KEY_F6;
|
||||
case XK_F7: return KEY_F7;
|
||||
case XK_F8: return KEY_F8;
|
||||
case XK_F9: return KEY_F9;
|
||||
case XK_F10: return KEY_F10;
|
||||
case XK_F11: return KEY_F11;
|
||||
case XK_F12: return KEY_F12;
|
||||
case XK_F13: return KEY_F13;
|
||||
case XK_F14: return KEY_F14;
|
||||
case XK_F15: return KEY_F15;
|
||||
|
||||
case XK_Num_Lock: return KEY_NUMLOCK;
|
||||
case XK_Caps_Lock: return KEY_CAPSLOCK;
|
||||
case XK_Scroll_Lock: return KEY_SCRLLOCK;
|
||||
case XK_Return: return KEY_ENTER;
|
||||
case XK_Sys_Req: return KEY_PRTSC;
|
||||
case XK_Print: return KEY_PRTSC;
|
||||
case XK_Shift_R: return KEY_RSHIFT;
|
||||
case XK_Shift_L: return KEY_LSHIFT;
|
||||
case XK_Control_R: return KEY_RCTRL;
|
||||
case XK_Control_L: return KEY_LCTRL;
|
||||
case XK_Alt_R: return KEY_RALT;
|
||||
case XK_Alt_L: return KEY_LALT;
|
||||
case XK_Meta_R: return KEY_RMETA;
|
||||
case XK_Meta_L: return KEY_LMETA;
|
||||
case XK_Super_L: return KEY_LSUPER;
|
||||
case XK_Super_R: return KEY_RSUPER;
|
||||
case XK_Menu: return KEY_MENU;
|
||||
}
|
||||
|
||||
return DeviceButton_Invalid;
|
||||
}
|
||||
|
||||
InputHandler_X11::InputHandler_X11()
|
||||
{
|
||||
if( Dpy == NULL || Win == None )
|
||||
return;
|
||||
XWindowAttributes winAttrib;
|
||||
|
||||
XGetWindowAttributes( Dpy, Win, &winAttrib );
|
||||
XSelectInput( Dpy, Win, winAttrib.your_event_mask | KeyPressMask | KeyReleaseMask );
|
||||
}
|
||||
|
||||
InputHandler_X11::~InputHandler_X11()
|
||||
{
|
||||
if( Dpy == NULL || Win == None )
|
||||
return;
|
||||
XWindowAttributes winAttrib;
|
||||
|
||||
XGetWindowAttributes( Dpy, Win, &winAttrib );
|
||||
XSelectInput( Dpy, Win, winAttrib.your_event_mask & ~(KeyPressMask|KeyReleaseMask) );
|
||||
}
|
||||
|
||||
void InputHandler_X11::Update()
|
||||
{
|
||||
if( Dpy == NULL || Win == None )
|
||||
{
|
||||
InputHandler::UpdateTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
XEvent event, lastEvent;
|
||||
DeviceButton lastDB = DeviceButton_Invalid;
|
||||
lastEvent.type = 0;
|
||||
|
||||
while( XCheckWindowEvent(Dpy, Win, KeyPressMask | KeyReleaseMask, &event) )
|
||||
{
|
||||
const bool bPress = event.type == KeyPress;
|
||||
if( lastEvent.type != 0 )
|
||||
{
|
||||
if( bPress && event.xkey.time == lastEvent.xkey.time &&
|
||||
event.xkey.keycode == lastEvent.xkey.keycode )
|
||||
{
|
||||
// This is a repeat event so ignore it.
|
||||
lastEvent.type = 0;
|
||||
continue;
|
||||
}
|
||||
// This is a new event so the last release was not a repeat.
|
||||
ButtonPressed( DeviceInput(DEVICE_KEYBOARD, lastDB, 0) );
|
||||
lastEvent.type = 0;
|
||||
}
|
||||
// Why only the zero index?
|
||||
lastDB = XSymToDeviceButton( XLookupKeysym(&event.xkey, 0) );
|
||||
if( lastDB == DeviceButton_Invalid )
|
||||
continue;
|
||||
if( bPress )
|
||||
ButtonPressed( DeviceInput(DEVICE_KEYBOARD, lastDB, 1) );
|
||||
else
|
||||
lastEvent = event;
|
||||
}
|
||||
// Handle any last releases.
|
||||
if( lastEvent.type != 0 )
|
||||
ButtonPressed( DeviceInput(DEVICE_KEYBOARD, lastDB, 0) );
|
||||
InputHandler::UpdateTimer();
|
||||
}
|
||||
|
||||
|
||||
void InputHandler_X11::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
if( Dpy && Win )
|
||||
vDevicesOut.push_back( InputDeviceInfo(DEVICE_KEYBOARD,"Keyboard") );
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2005, 2006 Sean Burke, Ben Anderson, Steve Checkoway
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,42 @@
|
||||
/* InputHandler_X11 - X-based keyboard input handler. */
|
||||
|
||||
#ifndef INPUT_HANDLER_X11_H
|
||||
#define INPUT_HANDLER_X11_H
|
||||
|
||||
#include "InputHandler.h"
|
||||
|
||||
class InputHandler_X11: public InputHandler
|
||||
{
|
||||
public:
|
||||
InputHandler_X11();
|
||||
~InputHandler_X11();
|
||||
void Update();
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2005 Sean Burke, Ben Anderson
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,232 @@
|
||||
#include "global.h"
|
||||
#include "InputHandler_Xbox.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageDisplay.h"
|
||||
|
||||
|
||||
#include <xtl.h>
|
||||
|
||||
struct DEVICE_STATE {
|
||||
XPP_DEVICE_TYPE *pxdt;
|
||||
DWORD dwState;
|
||||
};
|
||||
|
||||
byte buttonMasks[] = { XINPUT_GAMEPAD_DPAD_LEFT,
|
||||
XINPUT_GAMEPAD_DPAD_RIGHT,
|
||||
XINPUT_GAMEPAD_DPAD_UP,
|
||||
XINPUT_GAMEPAD_DPAD_DOWN,
|
||||
XINPUT_GAMEPAD_START,
|
||||
XINPUT_GAMEPAD_BACK,
|
||||
XINPUT_GAMEPAD_LEFT_THUMB,
|
||||
XINPUT_GAMEPAD_RIGHT_THUMB};
|
||||
|
||||
|
||||
/**
|
||||
* XBOX controller maps to the following RageInputDevice constants:
|
||||
* DPAD -> JOY_HAT_...
|
||||
* START -> JOY_AUX_1
|
||||
* BACK -> JOY_AUX_2
|
||||
* Left thumb button -> JOY_AUX_3
|
||||
* Right thumb button -> JOY_AUX_4
|
||||
* Following buttons are JOY_(index):
|
||||
* A, B, X, Y, BLACK, WHITE, Left trigger, right trigger
|
||||
*/
|
||||
|
||||
InputHandler_Xbox::InputHandler_Xbox()
|
||||
{
|
||||
//
|
||||
// Init joysticks
|
||||
//
|
||||
ZeroMemory( joysticks, sizeof(joysticks) );
|
||||
|
||||
getHandles();
|
||||
}
|
||||
|
||||
InputHandler_Xbox::~InputHandler_Xbox()
|
||||
{
|
||||
for(unsigned i = 0; i < NUM_JOYSTICKS; i++)
|
||||
{
|
||||
if(joysticks[i] != 0)
|
||||
XInputClose(joysticks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler_Xbox::Update()
|
||||
{
|
||||
// check insertions and removals
|
||||
DWORD dwInsert, dwRemove;
|
||||
DEVICE_STATE devices = {XDEVICE_TYPE_GAMEPAD, 0};
|
||||
|
||||
bool changes = false;
|
||||
// Check each device type to see if any changes have occurred.
|
||||
if( XGetDeviceChanges( devices.pxdt, &dwInsert, &dwRemove ) )
|
||||
{
|
||||
for(int j = 0; j < 4; j++)
|
||||
{
|
||||
if(1 << j & dwRemove)
|
||||
{
|
||||
changes = true;
|
||||
LOG->Trace("A joystick was removed");
|
||||
}
|
||||
if(1 << j & dwInsert)
|
||||
{
|
||||
changes = true;
|
||||
LOG->Trace("A joystick was inserted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(changes)
|
||||
{
|
||||
getHandles();
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < NUM_JOYSTICKS; i++)
|
||||
{
|
||||
if(joysticks[i] == 0)
|
||||
continue;
|
||||
|
||||
InputDevice inputDevice = InputDevice(DEVICE_JOY1 + i);
|
||||
XINPUT_STATE xis;
|
||||
// Query latest state.
|
||||
XInputGetState( joysticks[i], &xis );
|
||||
|
||||
// check buttons
|
||||
for(int j = 0; j < ARRAYLEN(buttonMasks); j++)
|
||||
{
|
||||
DWORD nowPressed = xis.Gamepad.wButtons & buttonMasks[j];
|
||||
DWORD wasPressed = lastState[i].wButtons & buttonMasks[j];
|
||||
|
||||
if(nowPressed != wasPressed)
|
||||
{
|
||||
DeviceButton Button = DeviceButton(JOY_HAT_LEFT + j);
|
||||
if(Button >= JOY_BUTTON_32)
|
||||
{
|
||||
LOG->Warn("Ignored joystick event (button too high)");
|
||||
continue;
|
||||
}
|
||||
DeviceInput di(inputDevice, Button, nowPressed != 0);
|
||||
ButtonPressed(di);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check analog buttons
|
||||
for(int j = 0; j < ARRAYLEN(xis.Gamepad.bAnalogButtons); j++)
|
||||
{
|
||||
bool nowPressed = xis.Gamepad.bAnalogButtons[j] > XINPUT_GAMEPAD_MAX_CROSSTALK;
|
||||
bool wasPressed = lastState[i].bAnalogButtons[j] > XINPUT_GAMEPAD_MAX_CROSSTALK;
|
||||
|
||||
if(nowPressed != wasPressed)
|
||||
{
|
||||
DeviceButton Button = DeviceButton(JOY_BUTTON_1 + j);
|
||||
if(Button >= JOY_BUTTON_32)
|
||||
{
|
||||
LOG->Warn("Ignored joystick event (button too high)");
|
||||
continue;
|
||||
}
|
||||
DeviceInput di(inputDevice, Button, nowPressed);
|
||||
ButtonPressed(di);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check thumbsticks
|
||||
SHORT axes[] = { xis.Gamepad.sThumbLX, xis.Gamepad.sThumbLY, xis.Gamepad.sThumbRX, xis.Gamepad.sThumbRY};
|
||||
|
||||
for(int j = 0; j < ARRAYLEN(axes); j++)
|
||||
{
|
||||
if(axes[j] != 0)
|
||||
{
|
||||
// Reverse y axis (negative values are down, not up)
|
||||
if(j == 1 || j == 3)
|
||||
axes[j] = -axes[j];
|
||||
|
||||
DeviceButton neg = (DeviceButton)(JOY_LEFT + (2 * j));
|
||||
DeviceButton pos = (DeviceButton)(JOY_RIGHT + (2 * j));
|
||||
float l = SCALE( axes[j], 0.0f, 32768.0f, 0.0f, 1.0f );
|
||||
ButtonPressed(DeviceInput(inputDevice, neg,max(-l,0),RageZeroTimer));
|
||||
ButtonPressed(DeviceInput(inputDevice, pos,max(+l,0),RageZeroTimer));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&lastState[i], &xis.Gamepad, sizeof(XINPUT_GAMEPAD));
|
||||
}
|
||||
|
||||
InputHandler::UpdateTimer();
|
||||
}
|
||||
|
||||
void InputHandler_Xbox::GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut )
|
||||
{
|
||||
for( int i=0; i<NUM_JOYSTICKS; i++ )
|
||||
{
|
||||
if( joysticks[i] != 0 )
|
||||
{
|
||||
vDevicesOut.push_back( InputDeviceInfo(InputDevice(DEVICE_JOY1+i),"XboxGameHardware") );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputHandler_Xbox::getHandles()
|
||||
{
|
||||
for(unsigned i = 0; i < NUM_JOYSTICKS; i++)
|
||||
{
|
||||
if(joysticks[i] != 0)
|
||||
XInputClose(joysticks[i]);
|
||||
}
|
||||
|
||||
ZeroMemory( joysticks, sizeof(joysticks) );
|
||||
ZeroMemory( lastState, sizeof(lastState) );
|
||||
|
||||
// Work out joystick handles
|
||||
DEVICE_STATE devices = {XDEVICE_TYPE_GAMEPAD, 0};
|
||||
devices.dwState = XGetDevices( devices.pxdt );
|
||||
|
||||
// Check the global gamepad state for a connected device.
|
||||
unsigned playersAllocated = 0;
|
||||
unsigned joysFound = 0;
|
||||
|
||||
for( unsigned i = 0; i < NUM_PORTS; i++ )
|
||||
{
|
||||
if( devices.dwState & 1 << i)
|
||||
{
|
||||
if(playersAllocated < NUM_JOYSTICKS)
|
||||
{
|
||||
XINPUT_POLLING_PARAMETERS pollingParameters = {TRUE, TRUE, 0, 8, 8, 0,};
|
||||
joysticks[playersAllocated] = XInputOpen(XDEVICE_TYPE_GAMEPAD, (DWORD)i, XDEVICE_NO_SLOT, &pollingParameters);
|
||||
playersAllocated++;
|
||||
}
|
||||
joysFound++;
|
||||
}
|
||||
}
|
||||
|
||||
LOG->Info( "Found %d connected joysticks for %d players", joysFound, playersAllocated );
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2004 Ryan Dortmans
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef INPUT_HANDLER_XBOX_H
|
||||
#define INPUT_HANDLER_XBOX_H
|
||||
|
||||
#include "InputHandler.h"
|
||||
|
||||
#include <xtl.h>
|
||||
|
||||
#define NUM_PORTS 4
|
||||
|
||||
class InputHandler_Xbox: public InputHandler
|
||||
{
|
||||
HANDLE joysticks[NUM_JOYSTICKS];
|
||||
XINPUT_GAMEPAD lastState[NUM_JOYSTICKS];
|
||||
|
||||
public:
|
||||
void Update();
|
||||
InputHandler_Xbox();
|
||||
~InputHandler_Xbox();
|
||||
void GetDevicesAndDescriptions( vector<InputDeviceInfo>& vDevicesOut );
|
||||
|
||||
private:
|
||||
void getHandles();
|
||||
};
|
||||
#define USE_INPUT_HANDLER_XBOX
|
||||
REGISTER_INPUT_HANDLER_CLASS( Xbox );
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2004 Ryan Dortmans
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* 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 OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
Reference in New Issue
Block a user