[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

@ -89,48 +89,59 @@ namespace OpenTK.Platform.Windows
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)
{ {
switch (message) try
{ {
case WindowMessage.INPUT: switch (message)
int size = 0; {
// Get the size of the input buffer case WindowMessage.INPUT:
Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, int size = 0;
IntPtr.Zero, ref size, API.RawInputHeaderSize);
void* data_ptr = stackalloc byte[size]; #if false
RawInput* data = (RawInput*)data_ptr; // Get the size of the input buffer
Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
IntPtr.Zero, ref size, API.RawInputHeaderSize);
// Read the actual raw input structure void* data_ptr = stackalloc byte[size];
if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, RawInput* data = (RawInput*)data_ptr;
(IntPtr)data_ptr, ref size, API.RawInputHeaderSize)) #endif
{ RawInput data;
switch (data->Header.Type) // Read the actual raw input structure
if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
out data, ref size, API.RawInputHeaderSize))
{ {
case RawInputDeviceType.KEYBOARD: switch (data.Header.Type)
if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(ref *data)) {
return IntPtr.Zero; case RawInputDeviceType.KEYBOARD:
break; if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(ref data))
return IntPtr.Zero;
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;
}
} }
} break;
break;
case WindowMessage.DEVICECHANGE: case WindowMessage.DEVICECHANGE:
((WinRawKeyboard)KeyboardDriver).RefreshDevices(); ((WinRawKeyboard)KeyboardDriver).RefreshDevices();
((WinRawMouse)MouseDriver).RefreshDevices(); ((WinRawMouse)MouseDriver).RefreshDevices();
((WinRawJoystick)JoystickDriver).RefreshDevices(); ((WinRawJoystick)JoystickDriver).RefreshDevices();
break; break;
}
return base.WindowProcedure(handle, message, wParam, lParam);
}
catch (Exception e)
{
Debug.Print("[WinRawInput] Caught unhandled exception {0}", e);
return IntPtr.Zero;
} }
return base.WindowProcedure(handle, message, wParam, lParam);
} }
#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
foreach (RawInputDeviceList dev in WinRawInput.GetDeviceList())
{ {
Debug.Print("Registered for raw joystick input"); 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(RawInput* rin) public unsafe bool ProcessEvent(ref RawInput rin)
{ {
IntPtr handle = rin->Header.Device; 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)
if (HidProtocol.GetData(HidProtocolReportType.Input,
(IntPtr)data, ref size, preparsed_data,
new IntPtr(&rin->Data.HID.RawData), rin->Data.HID.Size) != HidProtocolStatus.Success)
{ {
Debug.Print("[WinRawJoystick] HidProtocol.GetData() failed with {0}", Array.Resize(ref DataBuffer, size);
Marshal.GetLastWin32Error());
return false;
} }
UpdateAxes(stick, caps, axes_caps, data); fixed (void* pdata = &rin.Data.HID.RawData)
UpdateButtons(stick, caps, button_caps, data); {
if (HidProtocol.GetData(HidProtocolReportType.Input,
DataBuffer, ref size, PreparsedData,
(IntPtr)pdata, rin.Data.HID.Size) != HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetData() failed with {0}",
Marshal.GetLastWin32Error());
return false;
}
}
#if false UpdateAxes(stick, caps, AxisCaps, DataBuffer);
// Button state UpdateButtons(stick, caps, ButtonCaps, DataBuffer);
//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