[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetButtonCaps")]
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]
[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]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetData")]
public static extern HidProtocolStatus GetData(HidProtocolReportType type,
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]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetScaledUsageValue")]
static public extern HidProtocolStatus GetScaledUsageValue(HidProtocolReportType type,
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]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetUsageValue")]
public static extern HidProtocolStatus GetUsageValue(HidProtocolReportType type,
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]
+ [DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetValueCaps")]
+ public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type,
+ IntPtr caps, ref ushort caps_length, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetValueCaps")]
- public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type, IntPtr caps,
- ref ushort caps_length, IntPtr preparsed_data);
+ public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type,
+ HidProtocolValueCaps[] caps, ref ushort caps_length, byte[] preparsed_data);
[SuppressUnmanagedCodeSecurity]
[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
protected unsafe override IntPtr WindowProcedure(
IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
- switch (message)
+ try
{
- case WindowMessage.INPUT:
- int size = 0;
- // Get the size of the input buffer
- Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
- IntPtr.Zero, ref size, API.RawInputHeaderSize);
-
- void* data_ptr = stackalloc byte[size];
- RawInput* data = (RawInput*)data_ptr;
-
- // Read the actual raw input structure
- if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
- (IntPtr)data_ptr, ref size, API.RawInputHeaderSize))
- {
- switch (data->Header.Type)
+ switch (message)
+ {
+ case WindowMessage.INPUT:
+ int size = 0;
+
+#if false
+ // Get the size of the input buffer
+ Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
+ IntPtr.Zero, ref size, API.RawInputHeaderSize);
+
+ void* data_ptr = stackalloc byte[size];
+ RawInput* data = (RawInput*)data_ptr;
+#endif
+ RawInput data;
+ // Read the actual raw input structure
+ if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
+ out data, ref size, API.RawInputHeaderSize))
{
- case RawInputDeviceType.KEYBOARD:
- if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(ref *data))
- return IntPtr.Zero;
- break;
-
- case RawInputDeviceType.MOUSE:
- if (((WinRawMouse)MouseDriver).ProcessMouseEvent(ref *data))
- return IntPtr.Zero;
- break;
-
- case RawInputDeviceType.HID:
- if (((WinRawJoystick)JoystickDriver).ProcessEvent(data))
- return IntPtr.Zero;
- break;
+ switch (data.Header.Type)
+ {
+ case RawInputDeviceType.KEYBOARD:
+ if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(ref data))
+ return IntPtr.Zero;
+ break;
+
+ case RawInputDeviceType.MOUSE:
+ if (((WinRawMouse)MouseDriver).ProcessMouseEvent(ref data))
+ return IntPtr.Zero;
+ break;
+
+ case RawInputDeviceType.HID:
+ if (((WinRawJoystick)JoystickDriver).ProcessEvent(ref data))
+ return IntPtr.Zero;
+ break;
+ }
}
- }
- break;
-
- case WindowMessage.DEVICECHANGE:
- ((WinRawKeyboard)KeyboardDriver).RefreshDevices();
- ((WinRawMouse)MouseDriver).RefreshDevices();
- ((WinRawJoystick)JoystickDriver).RefreshDevices();
- break;
+ break;
+
+ case WindowMessage.DEVICECHANGE:
+ ((WinRawKeyboard)KeyboardDriver).RefreshDevices();
+ ((WinRawMouse)MouseDriver).RefreshDevices();
+ ((WinRawJoystick)JoystickDriver).RefreshDevices();
+ 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
State.SetButton(button, value);
}
+ public void SetConnected(bool value)
+ {
+ Capabilities.SetIsConnected(value);
+ State.SetIsConnected(value);
+ }
+
public JoystickCapabilities GetCapabilities()
{
return Capabilities;
readonly Dictionary<int, IntPtr> IndexToDevice =
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)
{
Debug.WriteLine("Using WinRawJoystick.");
new RawInputDevice(HIDUsageGD.Joystick, 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();
Debug.Unindent();
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}.",
- Marshal.GetLastWin32Error());
+ Devices[IndexToDevice[i]].SetConnected(false);
}
- 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);
if (stick == null)
{
return false;
}
+ if (!GetPreparsedData(handle, ref PreparsedData))
+ {
+ return false;
+ }
+
+ HidProtocolCaps caps;
+ if (!GetDeviceCaps(PreparsedData, out caps, ref AxisCaps, ref ButtonCaps))
+ {
+ return false;
+ }
+
+ // Query current state
+ // Allocate enough storage to hold the data of the current report
+ int size = HidProtocol.MaxDataListLength(HidProtocolReportType.Input, PreparsedData);
+ if (size == 0)
+ {
+ Debug.Print("[WinRawJoystick] HidProtocol.MaxDataListLength() failed with {0}",
+ Marshal.GetLastWin32Error());
+ return false;
+ }
+
+ // Fill the data buffer
+ if (DataBuffer.Length < size)
+ {
+ Array.Resize(ref DataBuffer, size);
+ }
+
+ fixed (void* pdata = &rin.Data.HID.RawData)
+ {
+ 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;
+ }
+ }
+
+ UpdateAxes(stick, caps, AxisCaps, DataBuffer);
+ UpdateButtons(stick, caps, ButtonCaps, DataBuffer);
+
+ return true;
+ }
+
+ 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(rin->Header.Device, RawInputDeviceInfoEnum.PREPARSEDDATA,
+ 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.
- 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)
+ 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
- HidProtocolCaps caps = new HidProtocolCaps();
+ 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
- HidProtocolValueCaps* axes_caps = stackalloc HidProtocolValueCaps[caps.NumberInputValueCaps];
if (HidProtocol.GetValueCaps(HidProtocolReportType.Input,
- (IntPtr)axes_caps, ref caps.NumberInputValueCaps, preparsed_data) !=
+ axes, ref caps.NumberInputValueCaps, preparsed_data) !=
HidProtocolStatus.Success)
{
+ Debug.Print("[WinRawJoystick] HidProtocol.GetValueCaps() failed with {0}",
+ Marshal.GetLastWin32Error());
return false;
}
// Button capabilities
- HidProtocolButtonCaps* button_caps = stackalloc HidProtocolButtonCaps[caps.NumberInputButtonCaps];
if (HidProtocol.GetButtonCaps(HidProtocolReportType.Input,
- (IntPtr)button_caps, ref caps.NumberInputButtonCaps, preparsed_data) !=
+ buttons, ref caps.NumberInputButtonCaps, preparsed_data) !=
HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetButtonCaps() failed with {0}",
return false;
}
- // Query current state
- // Allocate enough storage to hold the data of the current report
- int size = HidProtocol.MaxDataListLength(HidProtocolReportType.Input, preparsed_data);
- if (size == 0)
- {
- Debug.Print("[WinRawJoystick] HidProtocol.MaxDataListLength() failed with {0}",
- Marshal.GetLastWin32Error());
- return false;
- }
+ return true;
+ }
- // Fill the data buffer
- HidProtocolData* data = stackalloc HidProtocolData[size];
- if (HidProtocol.GetData(HidProtocolReportType.Input,
- (IntPtr)data, ref size, preparsed_data,
- new IntPtr(&rin->Data.HID.RawData), rin->Data.HID.Size) != HidProtocolStatus.Success)
+ // 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
{
- Debug.Print("[WinRawJoystick] HidProtocol.GetData() failed with {0}",
- Marshal.GetLastWin32Error());
- return false;
- }
+ // 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;
+ }
- UpdateAxes(stick, caps, axes_caps, data);
- UpdateButtons(stick, caps, button_caps, data);
+ // 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;
+ }
-#if false
- // Button state
- //g_NumberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1;
-#endif
+ // 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 true;
+ return guid;
}
- unsafe static void UpdateAxes(Device stick, HidProtocolCaps caps, HidProtocolValueCaps* axes_caps, HidProtocolData* data)
+ static void UpdateAxes(Device stick, HidProtocolCaps caps, HidProtocolValueCaps[] axes, HidProtocolData[] data)
{
// Use the data indices in the axis and button caps arrays to
// access the data buffer we just filled.
for (int i = 0; i < caps.NumberInputValueCaps; i++)
{
- HidProtocolValueCaps* axis = axes_caps + i;
- if (!axis->IsRange)
+ if (!axes[i].IsRange)
{
- int index = axis->NotRange.DataIndex;
+ int index = axes[i].NotRange.DataIndex;
if (index < 0 || index >= caps.NumberInputValueCaps)
{
// Should never happen
continue;
}
- HidProtocolData* d = (data + index);
- if (d->DataIndex != index)
+ if (data[i].DataIndex != index)
{
// Should also never happen
Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})",
- d->DataIndex, index);
+ data[i].DataIndex, index);
continue;
}
- short value = (short)HidHelper.ScaleValue(d->RawValue,
- axis->LogicalMin, axis->LogicalMax,
+ short value = (short)HidHelper.ScaleValue(data[i].RawValue,
+ axes[i].LogicalMin, axes[i].LogicalMax,
short.MinValue, short.MaxValue);
- stick.SetAxis(axis->UsagePage, axis->NotRange.Usage, value);
+ stick.SetAxis(axes[i].UsagePage, axes[i].NotRange.Usage, value);
}
else
{
}
}
- 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++)
{
- HidProtocolButtonCaps* button = button_caps + i;
- if (!button->IsRange)
+ if (!buttons[i].IsRange)
{
- int index = button->NotRange.DataIndex;
+ int index = buttons[i].NotRange.DataIndex;
if (index < 0 || index >= caps.NumberInputButtonCaps)
{
// Should never happen
continue;
}
- HidProtocolData* d = (data + index);
- if (d->DataIndex != index)
+ if (data[i].DataIndex != index)
{
// Should also never happen
Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})",
- d->DataIndex, index);
+ data[i].DataIndex, index);
continue;
}
- bool value = d->On;
-
- stick.SetButton(button->UsagePage, button->NotRange.Usage, value);
+ bool value = data[i].On;
+ stick.SetButton(buttons[i].UsagePage, buttons[i].NotRange.Usage, value);
}
else
{