[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)
{
int index = (int)button;

View file

@ -86,6 +86,12 @@ namespace OpenTK.Platform.Windows
[Out] HidProtocolButtonCaps[] ButtonCaps, ref ushort ButtonCapsLength,
[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]
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetUsageValue")]
public static extern HidProtocolStatus GetUsageValue(HidProtocolReportType type,

View file

@ -50,20 +50,26 @@ namespace OpenTK.Platform.Windows
new Dictionary<int,JoystickAxis>();
readonly Dictionary<int, JoystickButton> buttons =
new Dictionary<int, JoystickButton>();
readonly Dictionary<int, JoystickHat> hats =
new Dictionary<int, JoystickHat>();
#region Constructors
public Device(IntPtr handle, Guid guid, JoystickCapabilities caps)
public Device(IntPtr handle, Guid guid)
{
Handle = handle;
Guid = guid;
Capabilities = caps;
}
#endregion
#region Public Members
public void ClearButtons()
{
State.ClearButtons();
}
public void SetAxis(HIDPage page, short usage, short value)
{
JoystickAxis axis = GetAxis(page, usage);
@ -76,6 +82,12 @@ namespace OpenTK.Platform.Windows
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)
{
Capabilities.SetIsConnected(value);
@ -84,9 +96,17 @@ namespace OpenTK.Platform.Windows
public JoystickCapabilities GetCapabilities()
{
Capabilities = new JoystickCapabilities(
axes.Count, buttons.Count, hats.Count,
Capabilities.IsConnected);
return Capabilities;
}
internal void SetCapabilities(JoystickCapabilities caps)
{
Capabilities = caps;
}
public Guid GetGuid()
{
return Guid;
@ -97,7 +117,6 @@ namespace OpenTK.Platform.Windows
return State;
}
#endregion
#region Private Members
@ -127,6 +146,16 @@ namespace OpenTK.Platform.Windows
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
}
@ -204,15 +233,17 @@ namespace OpenTK.Platform.Windows
}
else
{
device = new Device(handle, guid);
// This is a new device, query its capabilities and add it
// to the device list
JoystickCapabilities caps = GetDeviceCaps(handle);
QueryDeviceCaps(device);
device = new Device(handle, guid, caps);
device.SetConnected(true);
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);
}
/*
if (HidProtocol.GetData(HidProtocolReportType.Input,
DataBuffer, ref size, PreparsedData,
new IntPtr((void*)&rin->Data.HID.RawData),
@ -283,9 +315,11 @@ namespace OpenTK.Platform.Windows
Marshal.GetLastWin32Error());
return false;
}
*/
UpdateAxes(stick, caps, AxisCaps, axis_caps_count, DataBuffer);
UpdateButtons(stick, caps, ButtonCaps, button_caps_count, DataBuffer);
UpdateButtons(rin, stick, button_caps_count);
//UpdateAxes(stick, caps, AxisCaps, axis_caps_count, DataBuffer, size);
return true;
}
}
@ -293,6 +327,37 @@ namespace OpenTK.Platform.Windows
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
#region Private Members
@ -328,21 +393,22 @@ namespace OpenTK.Platform.Windows
return true;
}
JoystickCapabilities GetDeviceCaps(IntPtr handle)
void QueryDeviceCaps(Device stick)
{
HidProtocolCaps caps;
int axis_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,
ref AxisCaps, out axis_count,
ref ButtonCaps, out button_count))
{
int axes = 0;
int dpads = 0;
int buttons = 0;
for (int i = 0; i < axis_count; i++)
{
if (AxisCaps[i].IsRange)
@ -351,7 +417,8 @@ namespace OpenTK.Platform.Windows
continue;
}
switch (AxisCaps[i].UsagePage)
HIDPage page = AxisCaps[i].UsagePage;
switch (page)
{
case HIDPage.GenericDesktop:
switch ((HIDUsageGD)AxisCaps[i].NotRange.Usage)
@ -365,11 +432,11 @@ namespace OpenTK.Platform.Windows
case HIDUsageGD.Slider:
case HIDUsageGD.Dial:
case HIDUsageGD.Wheel:
axes++;
stick.SetAxis(page, AxisCaps[i].NotRange.Usage, 0);
break;
case HIDUsageGD.Hatswitch:
dpads++;
stick.SetHat(page, AxisCaps[i].NotRange.Usage, HatPosition.Centered);
break;
}
break;
@ -379,14 +446,10 @@ namespace OpenTK.Platform.Windows
{
case HIDUsageSim.Rudder:
case HIDUsageSim.Throttle:
axes++;
stick.SetAxis(page, AxisCaps[i].NotRange.Usage, 0);
break;
}
break;
case HIDPage.Button:
buttons++;
break;
}
}
@ -399,11 +462,16 @@ namespace OpenTK.Platform.Windows
case HIDPage.Button:
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
{
buttons++;
stick.SetButton(page, ButtonCaps[i].NotRange.Usage, false);
}
break;
@ -412,10 +480,9 @@ namespace OpenTK.Platform.Windows
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,
@ -533,11 +600,11 @@ namespace OpenTK.Platform.Windows
continue;
}
if (data[i].DataIndex != index)
if (data[index].DataIndex != i)
{
// Should also never happen
Debug.Print("[WinRawJoystick] DataIndex != index ({0} != {1})",
data[i].DataIndex, index);
data[index].DataIndex, i);
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)
{
long hardware_id = handle.ToInt64();