[Win] Implemented joystick button updates

Due to the way we segregate axes from buttons, the easiest approach is
to retrieve the current button state via HidP_GetUsages().

Axes, buttons and hats are now allocated sequentially based on their
order of appearance in the device capability reports.
This commit is contained in:
thefiddler 2014-07-31 20:44:30 +02:00
parent 3fee0bd8d0
commit cbb2807959
3 changed files with 106 additions and 60 deletions

View file

@ -223,6 +223,11 @@ namespace OpenTK.Input
} }
} }
internal void ClearButtons()
{
buttons = 0;
}
internal void SetButton(JoystickButton button, bool value) internal void SetButton(JoystickButton button, bool value)
{ {
int index = (int)button; int index = (int)button;

View file

@ -86,6 +86,12 @@ namespace OpenTK.Platform.Windows
[Out] HidProtocolButtonCaps[] ButtonCaps, ref ushort ButtonCapsLength, [Out] HidProtocolButtonCaps[] ButtonCaps, ref ushort ButtonCapsLength,
[In] byte[] PreparsedData); [In] byte[] PreparsedData);
[SuppressUnmanagedCodeSecurity]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetUsages")]
unsafe public static extern HidProtocolStatus GetUsages(HidProtocolReportType type,
HIDPage usage_page, short link_collection, short* usage_list, ref int usage_length,
[In] 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,

View file

@ -50,20 +50,26 @@ namespace OpenTK.Platform.Windows
new Dictionary<int,JoystickAxis>(); new Dictionary<int,JoystickAxis>();
readonly Dictionary<int, JoystickButton> buttons = readonly Dictionary<int, JoystickButton> buttons =
new Dictionary<int, JoystickButton>(); new Dictionary<int, JoystickButton>();
readonly Dictionary<int, JoystickHat> hats =
new Dictionary<int, JoystickHat>();
#region Constructors #region Constructors
public Device(IntPtr handle, Guid guid, JoystickCapabilities caps) public Device(IntPtr handle, Guid guid)
{ {
Handle = handle; Handle = handle;
Guid = guid; Guid = guid;
Capabilities = caps;
} }
#endregion #endregion
#region Public Members #region Public Members
public void ClearButtons()
{
State.ClearButtons();
}
public void SetAxis(HIDPage page, short usage, short value) public void SetAxis(HIDPage page, short usage, short value)
{ {
JoystickAxis axis = GetAxis(page, usage); JoystickAxis axis = GetAxis(page, usage);
@ -76,6 +82,12 @@ namespace OpenTK.Platform.Windows
State.SetButton(button, value); State.SetButton(button, value);
} }
public void SetHat(HIDPage page, short usage, HatPosition pos)
{
JoystickHat hat = GetHat(page, usage);
State.SetHat(hat, new JoystickHatState(pos));
}
public void SetConnected(bool value) public void SetConnected(bool value)
{ {
Capabilities.SetIsConnected(value); Capabilities.SetIsConnected(value);
@ -84,9 +96,17 @@ namespace OpenTK.Platform.Windows
public JoystickCapabilities GetCapabilities() public JoystickCapabilities GetCapabilities()
{ {
Capabilities = new JoystickCapabilities(
axes.Count, buttons.Count, hats.Count,
Capabilities.IsConnected);
return Capabilities; return Capabilities;
} }
internal void SetCapabilities(JoystickCapabilities caps)
{
Capabilities = caps;
}
public Guid GetGuid() public Guid GetGuid()
{ {
return Guid; return Guid;
@ -97,7 +117,6 @@ namespace OpenTK.Platform.Windows
return State; return State;
} }
#endregion #endregion
#region Private Members #region Private Members
@ -127,6 +146,16 @@ namespace OpenTK.Platform.Windows
return buttons[key]; return buttons[key];
} }
JoystickHat GetHat(HIDPage page, short usage)
{
int key = MakeKey(page, usage);
if (!hats.ContainsKey(key))
{
hats.Add(key, JoystickHat.Hat0 + hats.Count);
}
return hats[key];
}
#endregion #endregion
} }
@ -204,15 +233,17 @@ namespace OpenTK.Platform.Windows
} }
else else
{ {
device = new Device(handle, guid);
// This is a new device, query its capabilities and add it // This is a new device, query its capabilities and add it
// to the device list // to the device list
JoystickCapabilities caps = GetDeviceCaps(handle); QueryDeviceCaps(device);
device = new Device(handle, guid, caps);
device.SetConnected(true); device.SetConnected(true);
Devices.Add(hardware_id, device); Devices.Add(hardware_id, device);
Debug.Print("[{0}] Connected joystick {1} ({2})", GetType().Name, guid, caps); Debug.Print("[{0}] Connected joystick {1} ({2})",
GetType().Name, device.GetGuid(), device.GetCapabilities());
} }
} }
} }
@ -274,6 +305,7 @@ namespace OpenTK.Platform.Windows
Array.Resize(ref DataBuffer, report_count); Array.Resize(ref DataBuffer, report_count);
} }
/*
if (HidProtocol.GetData(HidProtocolReportType.Input, if (HidProtocol.GetData(HidProtocolReportType.Input,
DataBuffer, ref size, PreparsedData, DataBuffer, ref size, PreparsedData,
new IntPtr((void*)&rin->Data.HID.RawData), new IntPtr((void*)&rin->Data.HID.RawData),
@ -283,9 +315,11 @@ namespace OpenTK.Platform.Windows
Marshal.GetLastWin32Error()); Marshal.GetLastWin32Error());
return false; return false;
} }
*/
UpdateAxes(stick, caps, AxisCaps, axis_caps_count, DataBuffer); UpdateButtons(rin, stick, button_caps_count);
UpdateButtons(stick, caps, ButtonCaps, button_caps_count, DataBuffer);
//UpdateAxes(stick, caps, AxisCaps, axis_caps_count, DataBuffer, size);
return true; return true;
} }
} }
@ -293,6 +327,37 @@ namespace OpenTK.Platform.Windows
return false; return false;
} }
unsafe void UpdateButtons(RawInput* rin, Device stick, int button_caps_count)
{
stick.ClearButtons();
for (int i = 0; i < button_caps_count; i++)
{
short* usage_list = stackalloc short[(int)JoystickButton.Last];
int usage_length = (int)JoystickButton.Last;
HIDPage page = ButtonCaps[i].UsagePage;
HidProtocolStatus status = HidProtocol.GetUsages(
HidProtocolReportType.Input,
page, 0, usage_list, ref usage_length,
PreparsedData,
new IntPtr((void*)&rin->Data.HID.RawData),
rin->Data.HID.Size);
if (status != HidProtocolStatus.Success)
{
Debug.Print("[WinRawJoystick] HidProtocol.GetUsages() failed with {0}",
Marshal.GetLastWin32Error());
continue;
}
for (int j = 0; j < usage_length; j++)
{
stick.SetButton(page, *(usage_list + j), true);
}
}
}
#endregion #endregion
#region Private Members #region Private Members
@ -328,21 +393,22 @@ namespace OpenTK.Platform.Windows
return true; return true;
} }
JoystickCapabilities GetDeviceCaps(IntPtr handle) void QueryDeviceCaps(Device stick)
{ {
HidProtocolCaps caps; HidProtocolCaps caps;
int axis_count; int axis_count;
int button_count; int button_count;
if (GetPreparsedData(handle, ref PreparsedData) && // Discovered elements
int axes = 0;
int dpads = 0;
int buttons = 0;
if (GetPreparsedData(stick.Handle, ref PreparsedData) &&
GetDeviceCaps(PreparsedData, out caps, GetDeviceCaps(PreparsedData, out caps,
ref AxisCaps, out axis_count, ref AxisCaps, out axis_count,
ref ButtonCaps, out button_count)) ref ButtonCaps, out button_count))
{ {
int axes = 0;
int dpads = 0;
int buttons = 0;
for (int i = 0; i < axis_count; i++) for (int i = 0; i < axis_count; i++)
{ {
if (AxisCaps[i].IsRange) if (AxisCaps[i].IsRange)
@ -351,7 +417,8 @@ namespace OpenTK.Platform.Windows
continue; continue;
} }
switch (AxisCaps[i].UsagePage) HIDPage page = AxisCaps[i].UsagePage;
switch (page)
{ {
case HIDPage.GenericDesktop: case HIDPage.GenericDesktop:
switch ((HIDUsageGD)AxisCaps[i].NotRange.Usage) switch ((HIDUsageGD)AxisCaps[i].NotRange.Usage)
@ -365,11 +432,11 @@ namespace OpenTK.Platform.Windows
case HIDUsageGD.Slider: case HIDUsageGD.Slider:
case HIDUsageGD.Dial: case HIDUsageGD.Dial:
case HIDUsageGD.Wheel: case HIDUsageGD.Wheel:
axes++; stick.SetAxis(page, AxisCaps[i].NotRange.Usage, 0);
break; break;
case HIDUsageGD.Hatswitch: case HIDUsageGD.Hatswitch:
dpads++; stick.SetHat(page, AxisCaps[i].NotRange.Usage, HatPosition.Centered);
break; break;
} }
break; break;
@ -379,14 +446,10 @@ namespace OpenTK.Platform.Windows
{ {
case HIDUsageSim.Rudder: case HIDUsageSim.Rudder:
case HIDUsageSim.Throttle: case HIDUsageSim.Throttle:
axes++; stick.SetAxis(page, AxisCaps[i].NotRange.Usage, 0);
break; break;
} }
break; break;
case HIDPage.Button:
buttons++;
break;
} }
} }
@ -399,11 +462,16 @@ namespace OpenTK.Platform.Windows
case HIDPage.Button: case HIDPage.Button:
if (is_range) if (is_range)
{ {
buttons += ButtonCaps[i].Range.UsageMax - ButtonCaps[i].Range.UsageMin + 1; for (short usage = ButtonCaps[i].Range.UsageMin; usage < ButtonCaps[i].Range.UsageMax; usage++)
{
buttons++;
stick.SetButton(page, usage, false);
}
} }
else else
{ {
buttons++; buttons++;
stick.SetButton(page, ButtonCaps[i].NotRange.Usage, false);
} }
break; break;
@ -412,10 +480,9 @@ namespace OpenTK.Platform.Windows
break; break;
} }
} }
return new JoystickCapabilities(axes, buttons, dpads, true);
} }
return new JoystickCapabilities();
stick.SetCapabilities(new JoystickCapabilities(axes, buttons, dpads, true));
} }
static bool GetDeviceCaps(byte[] preparsed_data, out HidProtocolCaps caps, static bool GetDeviceCaps(byte[] preparsed_data, out HidProtocolCaps caps,
@ -533,11 +600,11 @@ namespace OpenTK.Platform.Windows
continue; continue;
} }
if (data[i].DataIndex != index) if (data[index].DataIndex != i)
{ {
// Should also never happen // Should also never happen
Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})", Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})",
data[i].DataIndex, index); data[index].DataIndex, i);
continue; continue;
} }
@ -554,38 +621,6 @@ namespace OpenTK.Platform.Windows
} }
} }
unsafe static void UpdateButtons(Device stick, HidProtocolCaps caps, HidProtocolButtonCaps[] buttons, int buttons_count, HidProtocolData[] data)
{
for (int i = 0; i < buttons_count; i++)
{
if (!buttons[i].IsRange)
{
int index = buttons[i].NotRange.DataIndex;
if (index < 0 || index >= caps.NumberInputButtonCaps)
{
// Should never happen
Debug.Print("[WinRawJoystick] Error: DataIndex out of range");
continue;
}
if (data[i].DataIndex != index)
{
// Should also never happen
Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})",
data[i].DataIndex, index);
continue;
}
bool value = data[i].On;
stick.SetButton(buttons[i].UsagePage, buttons[i].NotRange.Usage, value);
}
else
{
// Todo: fall back to HidProtocol.GetLinkCollectionNodes
}
}
}
Device GetDevice(IntPtr handle) Device GetDevice(IntPtr handle)
{ {
long hardware_id = handle.ToInt64(); long hardware_id = handle.ToInt64();