Andreas Jung
2005-07-23 22:36:01 UTC
Hello,
I am currently about to create the input-layer of a flight simulator project
in managed DirectInput using C++/CLI Express Beta 2. Due to the nature of
flight simulators, it is absolutely necesary to have fully assignable axes,
e.g. to utilize a Saitek X45 (my test-joystick) with 6 axes to full extent.
I've come up with a pretty clever approach to store the enumerated
axes/buttons in a Hashtable (err... Generic::Dictionary) with the DI-Offset
as key. (see code snippet below).
This works ... well ... for my the X/Y-Axis and the rudder plus around 6
buttons. My throttle and some other axes DO NOT WORK, neither some buttons
with ID over 6 do.
I have really no idea why, but it *feels* like an old 4-axes/few-buttons
old-school-joystick.
The actual problem is: During initialization, the throttle is stored with a
Offset of "8"; during polling, no events with an offset of "8" are found in
the buffered joystick-data. So the assert ("assert( data->Offset != 8);")
never breaks, altough it should when the throttle is moved. Other axes work
fine.
The actual throttle-value-reading in the Joystick-State-Structure looks
correct; but I cannot use the Joystick-State, as there is no connection
between the enumerated axes/buttons and the Joystick-State, other than the
offset (AFAIK I cannot simply extract an offset-integer in managed world).
My question is: is it a bug or what am I doing wrong, that I cannot receive
*all* buffered joystick-data, but only the first few axes/buttons. Is it
wrong usage of the API? Is it a missing property of the device not set during
initialization?
Please help me! This problems really becomes a serious bottleneck of my
timeframe! Documentation of Managed DirectInput is not really a
documentation, and there are even no tutorials out there, which go beyond the
question how to access the basic Joystick-State-structure...
-Andreas Jung
So as promised, below the code-snippet. It should be pretty self explaining,
even though the declarations are not fully included.
---snip---
C++/CLI Express Beta 2 code:
namespace I_DONT_TELL_THE_NAME_OF_MY_PROJECT_SORRY
{
InputDeviceHandler::InputDeviceHandler( DirectInput::DeviceInstance^
deviceInstance, Windows::Forms::Form^ cooperativeForm,
RegisterDeviceAxisEvent^ registerDeviceAxis, RegisterDeviceButtonEvent^
registerDeviceButton )
{
// Create device from given device-type-guid (temporariliy assumed to be a
joystick!)
m_Device = gcnew DirectInput::Device( deviceInstance->InstanceGuid );
// Get a pointer to a friendly name of the device
String^ joyName = m_Device->DeviceInformation.InstanceName;
// This hashtable is going to hold all device-object-instances in use
// Key: DI-offset in the state-structure
// Value:
// value class DOIHashValue
// {
// public:
// enum class DOIHashValueType
// {
// Button,
// Axis,
// Unknown
// };
// public:
// DOIHashValueType Type;
// Object^ DataPtr;
// };
m_DOIs = gcnew Generic::Dictionary<int, DOIHashValue>();
// Enumerate all device-object-instances and save in the m_DOIs-hashtable
for each( DirectInput::DeviceObjectInstance doi in m_Device->Objects )
{
if( ( doi.ObjectId & (int)DirectInput::DeviceObjectTypeFlags::Axis ) != 0 )
{
// Set range to neutral - we handle joystick-curves later on our own
m_Device->Properties->SetRange(
DirectInput::ParameterHow::ById,
doi.ObjectId,
DirectInput::InputRange( -10000, 10000 ) );
m_Device->Properties->SetSaturation(
DirectInput::ParameterHow::ById,
doi.ObjectId,
10000 );
m_Device->Properties->SetDeadZone(
DirectInput::ParameterHow::ById,
doi.ObjectId,
0 );
// Add axis
// -add to the hashtable with key=offset
// -put a float-variable into the heap, where others can point to as well
// -the name of the axis and it's pointer go to the owner
DOIHashValue newValue;
newValue.DataPtr = gcnew float();
newValue.Type = DOIHashValue::DOIHashValueType::Axis;
m_DOIs[doi.Offset] = newValue;
registerDeviceAxis( doi.Name, joyName, (float^)newValue.DataPtr );
}
else if( doi.ObjectId & (int)DirectInput::DeviceObjectTypeFlags::Button )
{
// Add button
// -add to the hashtable with key=offset
// -put a "ButtonState-Enum"-variable into the heap, where others can
point to as well
// -the name of the button and it's pointer go to the owner
DOIHashValue newValue;
newValue.DataPtr = gcnew ButtonState();
newValue.Type = DOIHashValue::DOIHashValueType::Button;
m_DOIs[doi.Offset] = newValue;
registerDeviceButton( doi.Name, joyName, (ButtonState^)newValue.DataPtr );
}
}
// Set final device properties
m_Device->Properties->AxisModeAbsolute = true;
m_Device->Properties->BufferSize = 256;
m_Device->SetCooperativeLevel( cooperativeForm,
DirectInput::CooperativeLevelFlags::NonExclusive |
DirectInput::CooperativeLevelFlags::Background );
m_Device->Acquire();
}
InputDeviceHandler::~InputDeviceHandler()
{
m_Device->Unacquire();
m_Device->Dispose();
}
void InputDeviceHandler::Poll()
{
// Just make sure everyone is awake
m_Device->Poll();
// Let DI point to the buffer of latest input-events
DirectInput::BufferedDataCollection^ bufferedDataCollection =
m_Device->GetBufferedData();
if( bufferedDataCollection != nullptr )
{
// Enumerate all input-events and handle them
for each( DirectInput::BufferedData^ data in bufferedDataCollection )
{
if( (Object^)data != nullptr )
{
// --------------------------------------------
// !!! ATTENTION !!!
// Debugger told me that 8 is the offset of my
// slider-axis (the throttle of my Saitek X45).
// The throttle is not recognized, so this
// assert never breaks!
// --------------------------------------------
Diagnostics::Debug::Assert( data->Offset != 8 );
// If the key hasn't been registered during
// intialization, the key has been classified
// as "not of any use" (=other than axis/button).
if( !m_DOIs->ContainsKey( data->Offset ) )
continue;
// Look into the bucket, wether the offset
// belongs to a button or axis.
DOIHashValue value = m_DOIs[data->Offset];
if( value.Type == DOIHashValue::DOIHashValueType::Axis )
{
// Axis-value is scaled and put into the heap, where
// others can look up the axis's value.
*(float^)value.DataPtr = data->Data / 10000.0f;
}
else if( value.Type == DOIHashValue::DOIHashValueType::Button )
{
// A button can be neutral (up), currently pressed, or
// released (one trigger-cycle: (up-)down-up -> released).
if( data->Data == 0 )
{
if( *(ButtonState^)value.DataPtr == ButtonState::Pressed )
{
// Button was down and is now up, thus it was pressed an released
*(ButtonState^)value.DataPtr = ButtonState::Released;
}
else
{
*(ButtonState^)value.DataPtr = ButtonState::Up;
}
}
else
{
*(ButtonState^)value.DataPtr = ButtonState::Pressed;
}
}
}
}
}
// Don't make the GC's life too hard...
delete bufferedDataCollection;
}
}
I am currently about to create the input-layer of a flight simulator project
in managed DirectInput using C++/CLI Express Beta 2. Due to the nature of
flight simulators, it is absolutely necesary to have fully assignable axes,
e.g. to utilize a Saitek X45 (my test-joystick) with 6 axes to full extent.
I've come up with a pretty clever approach to store the enumerated
axes/buttons in a Hashtable (err... Generic::Dictionary) with the DI-Offset
as key. (see code snippet below).
This works ... well ... for my the X/Y-Axis and the rudder plus around 6
buttons. My throttle and some other axes DO NOT WORK, neither some buttons
with ID over 6 do.
I have really no idea why, but it *feels* like an old 4-axes/few-buttons
old-school-joystick.
The actual problem is: During initialization, the throttle is stored with a
Offset of "8"; during polling, no events with an offset of "8" are found in
the buffered joystick-data. So the assert ("assert( data->Offset != 8);")
never breaks, altough it should when the throttle is moved. Other axes work
fine.
The actual throttle-value-reading in the Joystick-State-Structure looks
correct; but I cannot use the Joystick-State, as there is no connection
between the enumerated axes/buttons and the Joystick-State, other than the
offset (AFAIK I cannot simply extract an offset-integer in managed world).
My question is: is it a bug or what am I doing wrong, that I cannot receive
*all* buffered joystick-data, but only the first few axes/buttons. Is it
wrong usage of the API? Is it a missing property of the device not set during
initialization?
Please help me! This problems really becomes a serious bottleneck of my
timeframe! Documentation of Managed DirectInput is not really a
documentation, and there are even no tutorials out there, which go beyond the
question how to access the basic Joystick-State-structure...
-Andreas Jung
So as promised, below the code-snippet. It should be pretty self explaining,
even though the declarations are not fully included.
---snip---
C++/CLI Express Beta 2 code:
namespace I_DONT_TELL_THE_NAME_OF_MY_PROJECT_SORRY
{
InputDeviceHandler::InputDeviceHandler( DirectInput::DeviceInstance^
deviceInstance, Windows::Forms::Form^ cooperativeForm,
RegisterDeviceAxisEvent^ registerDeviceAxis, RegisterDeviceButtonEvent^
registerDeviceButton )
{
// Create device from given device-type-guid (temporariliy assumed to be a
joystick!)
m_Device = gcnew DirectInput::Device( deviceInstance->InstanceGuid );
// Get a pointer to a friendly name of the device
String^ joyName = m_Device->DeviceInformation.InstanceName;
// This hashtable is going to hold all device-object-instances in use
// Key: DI-offset in the state-structure
// Value:
// value class DOIHashValue
// {
// public:
// enum class DOIHashValueType
// {
// Button,
// Axis,
// Unknown
// };
// public:
// DOIHashValueType Type;
// Object^ DataPtr;
// };
m_DOIs = gcnew Generic::Dictionary<int, DOIHashValue>();
// Enumerate all device-object-instances and save in the m_DOIs-hashtable
for each( DirectInput::DeviceObjectInstance doi in m_Device->Objects )
{
if( ( doi.ObjectId & (int)DirectInput::DeviceObjectTypeFlags::Axis ) != 0 )
{
// Set range to neutral - we handle joystick-curves later on our own
m_Device->Properties->SetRange(
DirectInput::ParameterHow::ById,
doi.ObjectId,
DirectInput::InputRange( -10000, 10000 ) );
m_Device->Properties->SetSaturation(
DirectInput::ParameterHow::ById,
doi.ObjectId,
10000 );
m_Device->Properties->SetDeadZone(
DirectInput::ParameterHow::ById,
doi.ObjectId,
0 );
// Add axis
// -add to the hashtable with key=offset
// -put a float-variable into the heap, where others can point to as well
// -the name of the axis and it's pointer go to the owner
DOIHashValue newValue;
newValue.DataPtr = gcnew float();
newValue.Type = DOIHashValue::DOIHashValueType::Axis;
m_DOIs[doi.Offset] = newValue;
registerDeviceAxis( doi.Name, joyName, (float^)newValue.DataPtr );
}
else if( doi.ObjectId & (int)DirectInput::DeviceObjectTypeFlags::Button )
{
// Add button
// -add to the hashtable with key=offset
// -put a "ButtonState-Enum"-variable into the heap, where others can
point to as well
// -the name of the button and it's pointer go to the owner
DOIHashValue newValue;
newValue.DataPtr = gcnew ButtonState();
newValue.Type = DOIHashValue::DOIHashValueType::Button;
m_DOIs[doi.Offset] = newValue;
registerDeviceButton( doi.Name, joyName, (ButtonState^)newValue.DataPtr );
}
}
// Set final device properties
m_Device->Properties->AxisModeAbsolute = true;
m_Device->Properties->BufferSize = 256;
m_Device->SetCooperativeLevel( cooperativeForm,
DirectInput::CooperativeLevelFlags::NonExclusive |
DirectInput::CooperativeLevelFlags::Background );
m_Device->Acquire();
}
InputDeviceHandler::~InputDeviceHandler()
{
m_Device->Unacquire();
m_Device->Dispose();
}
void InputDeviceHandler::Poll()
{
// Just make sure everyone is awake
m_Device->Poll();
// Let DI point to the buffer of latest input-events
DirectInput::BufferedDataCollection^ bufferedDataCollection =
m_Device->GetBufferedData();
if( bufferedDataCollection != nullptr )
{
// Enumerate all input-events and handle them
for each( DirectInput::BufferedData^ data in bufferedDataCollection )
{
if( (Object^)data != nullptr )
{
// --------------------------------------------
// !!! ATTENTION !!!
// Debugger told me that 8 is the offset of my
// slider-axis (the throttle of my Saitek X45).
// The throttle is not recognized, so this
// assert never breaks!
// --------------------------------------------
Diagnostics::Debug::Assert( data->Offset != 8 );
// If the key hasn't been registered during
// intialization, the key has been classified
// as "not of any use" (=other than axis/button).
if( !m_DOIs->ContainsKey( data->Offset ) )
continue;
// Look into the bucket, wether the offset
// belongs to a button or axis.
DOIHashValue value = m_DOIs[data->Offset];
if( value.Type == DOIHashValue::DOIHashValueType::Axis )
{
// Axis-value is scaled and put into the heap, where
// others can look up the axis's value.
*(float^)value.DataPtr = data->Data / 10000.0f;
}
else if( value.Type == DOIHashValue::DOIHashValueType::Button )
{
// A button can be neutral (up), currently pressed, or
// released (one trigger-cycle: (up-)down-up -> released).
if( data->Data == 0 )
{
if( *(ButtonState^)value.DataPtr == ButtonState::Pressed )
{
// Button was down and is now up, thus it was pressed an released
*(ButtonState^)value.DataPtr = ButtonState::Released;
}
else
{
*(ButtonState^)value.DataPtr = ButtonState::Up;
}
}
else
{
*(ButtonState^)value.DataPtr = ButtonState::Pressed;
}
}
}
}
}
// Don't make the GC's life too hard...
delete bufferedDataCollection;
}
}