[Win] Completed raw input IJoystickDriver2

For improved safety, we are now using managed memory buffers instead of
stack allocations and pointers.
This commit is contained in:
Stefanos A 2014-01-18 16:00:27 +01:00 committed by thefiddler
parent f6b382c929
commit cec48ab20a
7 changed files with 339 additions and 146 deletions

View file

@ -1500,16 +1500,6 @@ namespace OpenTK.Platform.Windows
/// <para>If Data is not large enough for the data, the function returns -1. If Data is NULL, the function returns a value of zero. In both of these cases, Size is set to the minimum size required for the Data buffer.</para> /// <para>If Data is not large enough for the data, the function returns -1. If Data is NULL, the function returns a value of zero. In both of these cases, Size is set to the minimum size required for the Data buffer.</para>
/// <para>Call GetLastError to identify any other errors.</para> /// <para>Call GetLastError to identify any other errors.</para>
/// </returns> /// </returns>
[CLSCompliant(false)]
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
internal static extern UINT GetRawInputDeviceInfo(
HANDLE Device,
[MarshalAs(UnmanagedType.U4)] RawInputDeviceInfoEnum Command,
[In, Out] LPVOID Data,
[In, Out] ref UINT Size
);
[System.Security.SuppressUnmanagedCodeSecurity] [System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
internal static extern INT GetRawInputDeviceInfo( internal static extern INT GetRawInputDeviceInfo(
@ -1519,6 +1509,16 @@ namespace OpenTK.Platform.Windows
[In, Out] ref INT Size [In, Out] ref INT Size
); );
[CLSCompliant(false)]
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
internal static extern INT GetRawInputDeviceInfo(
HANDLE Device,
[MarshalAs(UnmanagedType.U4)] RawInputDeviceInfoEnum Command,
[In, Out] byte[] Data,
[In, Out] ref INT Size
);
/// <summary> /// <summary>
/// Gets information about the raw input device. /// Gets information about the raw input device.
/// </summary> /// </summary>
@ -2791,7 +2791,7 @@ namespace OpenTK.Platform.Windows
/// <summary> /// <summary>
/// Size, in bytes, of the RawInputDeviceInfo structure. /// Size, in bytes, of the RawInputDeviceInfo structure.
/// </summary> /// </summary>
DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo)); internal DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo));
/// <summary> /// <summary>
/// Type of raw input data. /// Type of raw input data.
/// </summary> /// </summary>

View file

@ -43,38 +43,54 @@ namespace OpenTK.Platform.Windows
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetButtonCaps")] [DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetButtonCaps")]
public static extern HidProtocolStatus GetButtonCaps(HidProtocolReportType hidProtocolReportType, public static extern HidProtocolStatus GetButtonCaps(HidProtocolReportType hidProtocolReportType,
IntPtr button_caps, ref ushort p, IntPtr preparsed_data); IntPtr button_caps, ref ushort p, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetButtonCaps")]
public static extern HidProtocolStatus GetButtonCaps(HidProtocolReportType hidProtocolReportType,
HidProtocolButtonCaps[] button_caps, ref ushort p, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetCaps")] [DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetCaps")]
public static extern HidProtocolStatus GetCaps(IntPtr preparsed_data, ref HidProtocolCaps capabilities); public static extern HidProtocolStatus GetCaps(byte[] preparsed_data, ref HidProtocolCaps capabilities);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetData")] [DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetData")]
public static extern HidProtocolStatus GetData(HidProtocolReportType type, public static extern HidProtocolStatus GetData(HidProtocolReportType type,
IntPtr data, ref int data_length, IntPtr data, ref int data_length,
IntPtr preparsed_data, IntPtr report, int report_length); byte[] preparsed_data, IntPtr report, int report_length);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetData")]
public static extern HidProtocolStatus GetData(HidProtocolReportType type,
HidProtocolData[] data, ref int data_length,
byte[] preparsed_data, IntPtr report, int report_length);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetScaledUsageValue")] [DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetScaledUsageValue")]
static public extern HidProtocolStatus GetScaledUsageValue(HidProtocolReportType type, static public extern HidProtocolStatus GetScaledUsageValue(HidProtocolReportType type,
HIDPage usage_page, short link_collection, short usage, ref int usage_value, HIDPage usage_page, short link_collection, short usage, ref int usage_value,
IntPtr preparsed_data, IntPtr report, int report_length); byte[] preparsed_data, IntPtr report, int report_length);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetUsageValue")] [DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetUsageValue")]
public static extern HidProtocolStatus GetUsageValue(HidProtocolReportType type, public static extern HidProtocolStatus GetUsageValue(HidProtocolReportType type,
HIDPage usage_page, short link_collection, short usage, ref uint usage_value, HIDPage usage_page, short link_collection, short usage, ref uint usage_value,
IntPtr preparsed_data, IntPtr report, int report_length); byte[] preparsed_data, IntPtr report, int report_length);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetValueCaps")] [DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetValueCaps")]
public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type, IntPtr caps, public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type,
ref ushort caps_length, IntPtr preparsed_data); IntPtr caps, ref ushort caps_length, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetValueCaps")]
public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type,
HidProtocolValueCaps[] caps, ref ushort caps_length, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_MaxDataListLength")] [DllImport(lib, SetLastError = true, EntryPoint = "HidP_MaxDataListLength")]
public static extern int MaxDataListLength(HidProtocolReportType type, IntPtr preparsed_data); public static extern int MaxDataListLength(HidProtocolReportType type, byte[] preparsed_data);
} }
enum HidProtocolReportType : ushort enum HidProtocolReportType : ushort

View file

@ -45,7 +45,6 @@ namespace OpenTK.Platform.Windows
// WinFactory constructor has finished running. The reason is // WinFactory constructor has finished running. The reason is
// that they call WinFactory methods internally. // that they call WinFactory methods internally.
WinRawInput rawinput_driver; // For keyboard and mouse input WinRawInput rawinput_driver; // For keyboard and mouse input
WinCombinedJoystick joystick_driver; // For joystick input
internal static IntPtr OpenGLHandle { get; private set; } internal static IntPtr OpenGLHandle { get; private set; }
const string OpenGLName = "OPENGL32.DLL"; const string OpenGLName = "OPENGL32.DLL";
@ -132,7 +131,7 @@ namespace OpenTK.Platform.Windows
public override IJoystickDriver2 CreateJoystickDriver() public override IJoystickDriver2 CreateJoystickDriver()
{ {
return CombinedJoystickDriver; return RawInputDriver.JoystickDriver;
} }
#endregion #endregion
@ -154,22 +153,6 @@ namespace OpenTK.Platform.Windows
} }
} }
WinCombinedJoystick CombinedJoystickDriver
{
get
{
lock (SyncRoot)
{
if (joystick_driver == null)
{
joystick_driver = new WinCombinedJoystick(
new XInputJoystick(), new WinMMJoystick());
}
return joystick_driver;
}
}
}
#endregion #endregion
#region IDisposable Members #region IDisposable Members
@ -181,7 +164,6 @@ namespace OpenTK.Platform.Windows
if (manual) if (manual)
{ {
rawinput_driver.Dispose(); rawinput_driver.Dispose();
joystick_driver.Dispose();
} }
base.Dispose(manual); base.Dispose(manual);

View file

@ -88,36 +88,41 @@ namespace OpenTK.Platform.Windows
// Processes the input Windows Message, routing the buffer to the correct Keyboard, Mouse or HID. // Processes the input Windows Message, routing the buffer to the correct Keyboard, Mouse or HID.
protected unsafe override IntPtr WindowProcedure( protected unsafe override IntPtr WindowProcedure(
IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
try
{ {
switch (message) switch (message)
{ {
case WindowMessage.INPUT: case WindowMessage.INPUT:
int size = 0; int size = 0;
#if false
// Get the size of the input buffer // Get the size of the input buffer
Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
IntPtr.Zero, ref size, API.RawInputHeaderSize); IntPtr.Zero, ref size, API.RawInputHeaderSize);
void* data_ptr = stackalloc byte[size]; void* data_ptr = stackalloc byte[size];
RawInput* data = (RawInput*)data_ptr; RawInput* data = (RawInput*)data_ptr;
#endif
RawInput data;
// Read the actual raw input structure // Read the actual raw input structure
if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
(IntPtr)data_ptr, ref size, API.RawInputHeaderSize)) out data, ref size, API.RawInputHeaderSize))
{ {
switch (data->Header.Type) switch (data.Header.Type)
{ {
case RawInputDeviceType.KEYBOARD: case RawInputDeviceType.KEYBOARD:
if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(ref *data)) if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(ref data))
return IntPtr.Zero; return IntPtr.Zero;
break; break;
case RawInputDeviceType.MOUSE: case RawInputDeviceType.MOUSE:
if (((WinRawMouse)MouseDriver).ProcessMouseEvent(ref *data)) if (((WinRawMouse)MouseDriver).ProcessMouseEvent(ref data))
return IntPtr.Zero; return IntPtr.Zero;
break; break;
case RawInputDeviceType.HID: case RawInputDeviceType.HID:
if (((WinRawJoystick)JoystickDriver).ProcessEvent(data)) if (((WinRawJoystick)JoystickDriver).ProcessEvent(ref data))
return IntPtr.Zero; return IntPtr.Zero;
break; break;
} }
@ -132,6 +137,12 @@ namespace OpenTK.Platform.Windows
} }
return base.WindowProcedure(handle, message, wParam, lParam); return base.WindowProcedure(handle, message, wParam, lParam);
} }
catch (Exception e)
{
Debug.Print("[WinRawInput] Caught unhandled exception {0}", e);
return IntPtr.Zero;
}
}
#endregion #endregion

View file

@ -76,6 +76,12 @@ namespace OpenTK.Platform.Windows
State.SetButton(button, value); State.SetButton(button, value);
} }
public void SetConnected(bool value)
{
Capabilities.SetIsConnected(value);
State.SetIsConnected(value);
}
public JoystickCapabilities GetCapabilities() public JoystickCapabilities GetCapabilities()
{ {
return Capabilities; return Capabilities;
@ -134,6 +140,11 @@ namespace OpenTK.Platform.Windows
readonly Dictionary<int, IntPtr> IndexToDevice = readonly Dictionary<int, IntPtr> IndexToDevice =
new Dictionary<int, IntPtr>(); new Dictionary<int, IntPtr>();
byte[] PreparsedData = new byte[1024];
HidProtocolValueCaps[] AxisCaps = new HidProtocolValueCaps[4];
HidProtocolButtonCaps[] ButtonCaps = new HidProtocolButtonCaps[4];
HidProtocolData[] DataBuffer = new HidProtocolData[16];
public WinRawJoystick(IntPtr window) public WinRawJoystick(IntPtr window)
{ {
Debug.WriteLine("Using WinRawJoystick."); Debug.WriteLine("Using WinRawJoystick.");
@ -148,6 +159,17 @@ namespace OpenTK.Platform.Windows
new RawInputDevice(HIDUsageGD.Joystick, RawInputDeviceFlags.INPUTSINK, window), new RawInputDevice(HIDUsageGD.Joystick, RawInputDeviceFlags.INPUTSINK, window),
new RawInputDevice(HIDUsageGD.GamePad, RawInputDeviceFlags.INPUTSINK, window), new RawInputDevice(HIDUsageGD.GamePad, RawInputDeviceFlags.INPUTSINK, window),
}; };
if (!Functions.RegisterRawInputDevices(DeviceTypes, DeviceTypes.Length, API.RawInputDeviceSize))
{
Debug.Print("[Warning] Raw input registration failed with error: {0}.",
Marshal.GetLastWin32Error());
}
else
{
Debug.Print("[WinRawJoystick] Registered for raw input");
}
RefreshDevices(); RefreshDevices();
Debug.Unindent(); Debug.Unindent();
@ -157,20 +179,34 @@ namespace OpenTK.Platform.Windows
public void RefreshDevices() public void RefreshDevices()
{ {
if (!Functions.RegisterRawInputDevices(DeviceTypes, DeviceTypes.Length, API.RawInputDeviceSize)) // Mark all devices as disconnected. We will check which of those
// are connected later on.
for (int i = 0; i < IndexToDevice.Count; i++)
{ {
Debug.Print("[Warning] Raw input registration failed with error: {0}.", Devices[IndexToDevice[i]].SetConnected(false);
Marshal.GetLastWin32Error());
}
else
{
Debug.Print("Registered for raw joystick input");
}
} }
public unsafe bool ProcessEvent(RawInput* rin) foreach (RawInputDeviceList dev in WinRawInput.GetDeviceList())
{ {
IntPtr handle = rin->Header.Device; if (dev.Type != RawInputDeviceType.HID)
continue;
IntPtr handle = dev.Device;
Guid guid = GetDeviceGuid(handle);
JoystickCapabilities caps = GetDeviceCaps(handle);
if (!Devices.ContainsKey(handle))
Devices.Add(handle, new Device(handle, guid, caps));
Device stick = Devices[handle];
stick.SetConnected(true);
}
}
public unsafe bool ProcessEvent(ref RawInput rin)
{
IntPtr handle = rin.Header.Device;
Device stick = GetDevice(handle); Device stick = GetDevice(handle);
if (stick == null) if (stick == null)
{ {
@ -178,49 +214,20 @@ namespace OpenTK.Platform.Windows
return false; return false;
} }
// Query the size of the _HIDP_PREPARSED_DATA structure for this event. if (!GetPreparsedData(handle, ref PreparsedData))
int preparsed_size = 0;
Functions.GetRawInputDeviceInfo(rin->Header.Device, RawInputDeviceInfoEnum.PREPARSEDDATA,
IntPtr.Zero, ref preparsed_size);
if (preparsed_size == 0)
return false;
// Allocate space for _HIDP_PREPARSED_DATA.
// This is an untyped blob of data.
void* preparsed_data_ptr = stackalloc byte[preparsed_size];
IntPtr preparsed_data = (IntPtr)preparsed_data_ptr;
if (Functions.GetRawInputDeviceInfo(rin->Header.Device, RawInputDeviceInfoEnum.PREPARSEDDATA,
preparsed_data, ref preparsed_size) < 0)
return false;
// Query joystick capabilities
HidProtocolCaps caps = new HidProtocolCaps();
if (HidProtocol.GetCaps(preparsed_data, ref caps) != HidProtocolStatus.Success)
return false;
// Axis capabilities
HidProtocolValueCaps* axes_caps = stackalloc HidProtocolValueCaps[caps.NumberInputValueCaps];
if (HidProtocol.GetValueCaps(HidProtocolReportType.Input,
(IntPtr)axes_caps, ref caps.NumberInputValueCaps, preparsed_data) !=
HidProtocolStatus.Success)
{ {
return false; return false;
} }
// Button capabilities HidProtocolCaps caps;
HidProtocolButtonCaps* button_caps = stackalloc HidProtocolButtonCaps[caps.NumberInputButtonCaps]; if (!GetDeviceCaps(PreparsedData, out caps, ref AxisCaps, ref ButtonCaps))
if (HidProtocol.GetButtonCaps(HidProtocolReportType.Input,
(IntPtr)button_caps, ref caps.NumberInputButtonCaps, preparsed_data) !=
HidProtocolStatus.Success)
{ {
Debug.Print("[WinRawJoystick] HidProtocol.GetButtonCaps() failed with {0}",
Marshal.GetLastWin32Error());
return false; return false;
} }
// Query current state // Query current state
// Allocate enough storage to hold the data of the current report // Allocate enough storage to hold the data of the current report
int size = HidProtocol.MaxDataListLength(HidProtocolReportType.Input, preparsed_data); int size = HidProtocol.MaxDataListLength(HidProtocolReportType.Input, PreparsedData);
if (size == 0) if (size == 0)
{ {
Debug.Print("[WinRawJoystick] HidProtocol.MaxDataListLength() failed with {0}", Debug.Print("[WinRawJoystick] HidProtocol.MaxDataListLength() failed with {0}",
@ -229,37 +236,218 @@ namespace OpenTK.Platform.Windows
} }
// Fill the data buffer // Fill the data buffer
HidProtocolData* data = stackalloc HidProtocolData[size]; if (DataBuffer.Length < size)
{
Array.Resize(ref DataBuffer, size);
}
fixed (void* pdata = &rin.Data.HID.RawData)
{
if (HidProtocol.GetData(HidProtocolReportType.Input, if (HidProtocol.GetData(HidProtocolReportType.Input,
(IntPtr)data, ref size, preparsed_data, DataBuffer, ref size, PreparsedData,
new IntPtr(&rin->Data.HID.RawData), rin->Data.HID.Size) != HidProtocolStatus.Success) (IntPtr)pdata, rin.Data.HID.Size) != HidProtocolStatus.Success)
{ {
Debug.Print("[WinRawJoystick] HidProtocol.GetData() failed with {0}", Debug.Print("[WinRawJoystick] HidProtocol.GetData() failed with {0}",
Marshal.GetLastWin32Error()); Marshal.GetLastWin32Error());
return false; return false;
} }
}
UpdateAxes(stick, caps, axes_caps, data); UpdateAxes(stick, caps, AxisCaps, DataBuffer);
UpdateButtons(stick, caps, button_caps, data); UpdateButtons(stick, caps, ButtonCaps, DataBuffer);
#if false
// Button state
//g_NumberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1;
#endif
return true; return true;
} }
unsafe static void UpdateAxes(Device stick, HidProtocolCaps caps, HidProtocolValueCaps* axes_caps, HidProtocolData* data) static bool GetPreparsedData(IntPtr handle, ref byte[] prepared_data)
{
// Query the size of the _HIDP_PREPARSED_DATA structure for this event.
int preparsed_size = 0;
Functions.GetRawInputDeviceInfo(handle, RawInputDeviceInfoEnum.PREPARSEDDATA,
IntPtr.Zero, ref preparsed_size);
if (preparsed_size == 0)
{
Debug.Print("[WinRawJoystick] Functions.GetRawInputDeviceInfo(PARSEDDATA) failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
// Allocate space for _HIDP_PREPARSED_DATA.
// This is an untyped blob of data.
if (prepared_data.Length < preparsed_size)
{
Array.Resize(ref prepared_data, preparsed_size);
}
if (Functions.GetRawInputDeviceInfo(handle, RawInputDeviceInfoEnum.PREPARSEDDATA,
prepared_data, ref preparsed_size) < 0)
{
Debug.Print("[WinRawJoystick] Functions.GetRawInputDeviceInfo(PARSEDDATA) failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
return true;
}
JoystickCapabilities GetDeviceCaps(IntPtr handle)
{
HidProtocolCaps caps;
if (GetPreparsedData(handle, ref PreparsedData) &&
GetDeviceCaps(PreparsedData, out caps, ref AxisCaps, ref ButtonCaps))
{
int axes = 0;
int dpads = 0;
int buttons = 0;
for (int i = 0; i < caps.NumberInputValueCaps; i++)
{
if (AxisCaps[i].IsRange)
continue; // Todo: range values not currently supported
switch (AxisCaps[i].UsagePage)
{
case HIDPage.GenericDesktop:
switch ((HIDUsageGD)AxisCaps[i].NotRange.Usage)
{
case HIDUsageGD.X:
case HIDUsageGD.Y:
case HIDUsageGD.Z:
case HIDUsageGD.Rx:
case HIDUsageGD.Ry:
case HIDUsageGD.Rz:
case HIDUsageGD.Slider:
case HIDUsageGD.Dial:
case HIDUsageGD.Wheel:
axes++;
break;
case HIDUsageGD.Hatswitch:
dpads++;
break;
}
break;
case HIDPage.Simulation:
switch ((HIDUsageSim)AxisCaps[i].NotRange.Usage)
{
case HIDUsageSim.Rudder:
case HIDUsageSim.Throttle:
axes++;
break;
}
break;
case HIDPage.Button:
buttons++;
break;
}
}
return new JoystickCapabilities(axes, buttons, true);
}
return new JoystickCapabilities();
}
static bool GetDeviceCaps(byte[] preparsed_data, out HidProtocolCaps caps,
ref HidProtocolValueCaps[] axes, ref HidProtocolButtonCaps[] buttons)
{
// Query joystick capabilities
caps = new HidProtocolCaps();
if (HidProtocol.GetCaps(preparsed_data, ref caps) != HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetCaps() failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
// Make sure our caps arrays are big enough
if (axes.Length < caps.NumberInputValueCaps)
{
Array.Resize(ref axes, caps.NumberInputValueCaps);
}
if (buttons.Length < caps.NumberInputButtonCaps)
{
Array.Resize(ref buttons, caps.NumberInputButtonCaps);
}
// Axis capabilities
if (HidProtocol.GetValueCaps(HidProtocolReportType.Input,
axes, ref caps.NumberInputValueCaps, preparsed_data) !=
HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetValueCaps() failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
// Button capabilities
if (HidProtocol.GetButtonCaps(HidProtocolReportType.Input,
buttons, ref caps.NumberInputButtonCaps, preparsed_data) !=
HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetButtonCaps() failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
return true;
}
// Retrieves the GUID of a device, which is stored
// in the last part of the DEVICENAME string
Guid GetDeviceGuid(IntPtr handle)
{
Guid guid = new Guid();
unsafe
{
// Find out how much memory we need to allocate
// for the DEVICENAME string
int size = 0;
if (Functions.GetRawInputDeviceInfo(handle, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size) < 0 || size == 0)
{
Debug.Print("[WinRawJoystick] Functions.GetRawInputDeviceInfo(DEVICENAME) failed with error {0}",
Marshal.GetLastWin32Error());
return guid;
}
// Allocate memory and retrieve the DEVICENAME string
char* pname = stackalloc char[size + 1];
if (Functions.GetRawInputDeviceInfo(handle, RawInputDeviceInfoEnum.DEVICENAME, (IntPtr)pname, ref size) < 0)
{
Debug.Print("[WinRawJoystick] Functions.GetRawInputDeviceInfo(DEVICENAME) failed with error {0}",
Marshal.GetLastWin32Error());
return guid;
}
// Convert the buffer to a .Net string, and split it into parts
string name = new string(pname);
if (String.IsNullOrEmpty(name))
{
Debug.Print("[WinRawJoystick] Failed to construct device name");
return guid;
}
// The GUID is stored in the last part of the string
string[] parts = name.Split('#');
if (parts.Length >= 3)
{
guid = new Guid(parts[2]);
}
}
return guid;
}
static void UpdateAxes(Device stick, HidProtocolCaps caps, HidProtocolValueCaps[] axes, HidProtocolData[] data)
{ {
// Use the data indices in the axis and button caps arrays to // Use the data indices in the axis and button caps arrays to
// access the data buffer we just filled. // access the data buffer we just filled.
for (int i = 0; i < caps.NumberInputValueCaps; i++) for (int i = 0; i < caps.NumberInputValueCaps; i++)
{ {
HidProtocolValueCaps* axis = axes_caps + i; if (!axes[i].IsRange)
if (!axis->IsRange)
{ {
int index = axis->NotRange.DataIndex; int index = axes[i].NotRange.DataIndex;
if (index < 0 || index >= caps.NumberInputValueCaps) if (index < 0 || index >= caps.NumberInputValueCaps)
{ {
// Should never happen // Should never happen
@ -267,20 +455,19 @@ namespace OpenTK.Platform.Windows
continue; continue;
} }
HidProtocolData* d = (data + index); if (data[i].DataIndex != index)
if (d->DataIndex != index)
{ {
// Should also never happen // Should also never happen
Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})", Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})",
d->DataIndex, index); data[i].DataIndex, index);
continue; continue;
} }
short value = (short)HidHelper.ScaleValue(d->RawValue, short value = (short)HidHelper.ScaleValue(data[i].RawValue,
axis->LogicalMin, axis->LogicalMax, axes[i].LogicalMin, axes[i].LogicalMax,
short.MinValue, short.MaxValue); short.MinValue, short.MaxValue);
stick.SetAxis(axis->UsagePage, axis->NotRange.Usage, value); stick.SetAxis(axes[i].UsagePage, axes[i].NotRange.Usage, value);
} }
else else
{ {
@ -289,14 +476,13 @@ namespace OpenTK.Platform.Windows
} }
} }
unsafe static void UpdateButtons(Device stick, HidProtocolCaps caps, HidProtocolButtonCaps* button_caps, HidProtocolData* data) unsafe static void UpdateButtons(Device stick, HidProtocolCaps caps, HidProtocolButtonCaps[] buttons, HidProtocolData[] data)
{ {
for (int i = 0; i < caps.NumberInputButtonCaps; i++) for (int i = 0; i < caps.NumberInputButtonCaps; i++)
{ {
HidProtocolButtonCaps* button = button_caps + i; if (!buttons[i].IsRange)
if (!button->IsRange)
{ {
int index = button->NotRange.DataIndex; int index = buttons[i].NotRange.DataIndex;
if (index < 0 || index >= caps.NumberInputButtonCaps) if (index < 0 || index >= caps.NumberInputButtonCaps)
{ {
// Should never happen // Should never happen
@ -304,18 +490,16 @@ namespace OpenTK.Platform.Windows
continue; continue;
} }
HidProtocolData* d = (data + index); if (data[i].DataIndex != index)
if (d->DataIndex != index)
{ {
// Should also never happen // Should also never happen
Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})", Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})",
d->DataIndex, index); data[i].DataIndex, index);
continue; continue;
} }
bool value = d->On; bool value = data[i].On;
stick.SetButton(buttons[i].UsagePage, buttons[i].NotRange.Usage, value);
stick.SetButton(button->UsagePage, button->NotRange.Usage, value);
} }
else else
{ {

View file

@ -231,7 +231,7 @@ namespace OpenTK.Platform.Windows
static string GetDeviceName(RawInputDeviceList dev) static string GetDeviceName(RawInputDeviceList dev)
{ {
uint size = 0; int size = 0;
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size); Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size); IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size);
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size); Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size);

View file

@ -252,7 +252,7 @@ namespace OpenTK.Platform.Windows
static string GetDeviceName(RawInputDeviceList dev) static string GetDeviceName(RawInputDeviceList dev)
{ {
// get name size // get name size
uint size = 0; int size = 0;
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size); Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
// get actual name // get actual name