1.1 --- a/HidEvent.cs Sun Mar 15 16:56:31 2015 +0100
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,598 +0,0 @@
1.4 -//
1.5 -// Copyright (C) 2014-2015 Stéphane Lenclud.
1.6 -//
1.7 -// This file is part of SharpLibHid.
1.8 -//
1.9 -// SharpDisplayManager is free software: you can redistribute it and/or modify
1.10 -// it under the terms of the GNU General Public License as published by
1.11 -// the Free Software Foundation, either version 3 of the License, or
1.12 -// (at your option) any later version.
1.13 -//
1.14 -// SharpDisplayManager is distributed in the hope that it will be useful,
1.15 -// but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 -// GNU General Public License for more details.
1.18 -//
1.19 -// You should have received a copy of the GNU General Public License
1.20 -// along with SharpDisplayManager. If not, see <http://www.gnu.org/licenses/>.
1.21 -//
1.22 -
1.23 -
1.24 -using System;
1.25 -using System.Windows.Forms;
1.26 -using System.Runtime.InteropServices;
1.27 -using System.Diagnostics;
1.28 -using System.Text;
1.29 -using Microsoft.Win32.SafeHandles;
1.30 -using SharpLib.Win32;
1.31 -using System.Collections.Generic;
1.32 -using System.Timers;
1.33 -using SharpLib.Hid.Usage;
1.34 -
1.35 -
1.36 -namespace SharpLib.Hid
1.37 -{
1.38 - /// <summary>
1.39 - /// We provide utility functions to interpret gamepad dpad state.
1.40 - /// </summary>
1.41 - public enum DirectionPadState
1.42 - {
1.43 - Rest=-1,
1.44 - Up=0,
1.45 - UpRight=1,
1.46 - Right=2,
1.47 - DownRight=3,
1.48 - Down=4,
1.49 - DownLeft=5,
1.50 - Left=6,
1.51 - UpLeft=7
1.52 - }
1.53 -
1.54 - /// <summary>
1.55 - /// Represent a HID event.
1.56 - /// TODO: Rename this into HidRawInput?
1.57 - /// </summary>
1.58 - public class HidEvent : IDisposable
1.59 - {
1.60 - public bool IsValid { get; private set; }
1.61 - public bool IsForeground { get; private set; }
1.62 - public bool IsBackground { get { return !IsForeground; } }
1.63 - public bool IsMouse { get; private set; }
1.64 - public bool IsKeyboard { get; private set; }
1.65 - /// <summary>
1.66 - /// If this not a mouse or keyboard event then it's a generic HID event.
1.67 - /// </summary>
1.68 - public bool IsGeneric { get; private set; }
1.69 - public bool IsButtonDown { get { return Usages.Count == 1 && Usages[0] != 0; } }
1.70 - public bool IsButtonUp { get { return Usages.Count == 0; } }
1.71 - public bool IsRepeat { get { return RepeatCount != 0; } }
1.72 - public uint RepeatCount { get; private set; }
1.73 -
1.74 - public HidDevice Device { get; private set; }
1.75 - public RAWINPUT RawInput { get {return iRawInput;} }
1.76 - private RAWINPUT iRawInput;
1.77 -
1.78 - public ushort UsagePage { get; private set; }
1.79 - public ushort UsageCollection { get; private set; }
1.80 - public uint UsageId { get { return ((uint)UsagePage << 16 | (uint)UsageCollection); } }
1.81 - public List<ushort> Usages { get; private set; }
1.82 - /// <summary>
1.83 - /// Sorted in the same order as Device.InputValueCapabilities.
1.84 - /// </summary>
1.85 - public Dictionary<HIDP_VALUE_CAPS,uint> UsageValues { get; private set; }
1.86 - //TODO: We need a collection of input report
1.87 - public byte[] InputReport { get; private set; }
1.88 - //
1.89 - public delegate void HidEventRepeatDelegate(HidEvent aHidEvent);
1.90 - public event HidEventRepeatDelegate OnHidEventRepeat;
1.91 -
1.92 - private System.Timers.Timer Timer { get; set; }
1.93 - public DateTime Time { get; private set; }
1.94 - public DateTime OriginalTime { get; private set; }
1.95 -
1.96 - //Compute repeat delay and speed based on system settings
1.97 - //Those computations were taken from the Petzold here: ftp://ftp.charlespetzold.com/ProgWinForms/4%20Custom%20Controls/NumericScan/NumericScan/ClickmaticButton.cs
1.98 - private int iRepeatDelay = 250 * (1 + SystemInformation.KeyboardDelay);
1.99 - private int iRepeatSpeed = 405 - 12 * SystemInformation.KeyboardSpeed;
1.100 -
1.101 - /// <summary>
1.102 - /// Tells whether this event has already been disposed of.
1.103 - /// </summary>
1.104 - public bool IsStray { get { return Timer == null; } }
1.105 -
1.106 - /// <summary>
1.107 - /// We typically dispose of events as soon as we get the corresponding key up signal.
1.108 - /// </summary>
1.109 - public void Dispose()
1.110 - {
1.111 - Timer.Enabled = false;
1.112 - Timer.Dispose();
1.113 - //Mark this event as a stray
1.114 - Timer = null;
1.115 - }
1.116 -
1.117 - /// <summary>
1.118 - /// Initialize an HidEvent from a WM_INPUT message
1.119 - /// </summary>
1.120 - /// <param name="hRawInputDevice">Device Handle as provided by RAWINPUTHEADER.hDevice, typically accessed as rawinput.header.hDevice</param>
1.121 - public HidEvent(Message aMessage, HidEventRepeatDelegate aRepeatDelegate)
1.122 - {
1.123 - RepeatCount = 0;
1.124 - IsValid = false;
1.125 - IsKeyboard = false;
1.126 - IsGeneric = false;
1.127 -
1.128 - Time = DateTime.Now;
1.129 - OriginalTime = DateTime.Now;
1.130 - Timer = new System.Timers.Timer();
1.131 - Timer.Elapsed += (sender, e) => OnRepeatTimerElapsed(sender, e, this);
1.132 - Usages = new List<ushort>();
1.133 - UsageValues = new Dictionary<HIDP_VALUE_CAPS,uint>();
1.134 - OnHidEventRepeat += aRepeatDelegate;
1.135 -
1.136 - if (aMessage.Msg != Const.WM_INPUT)
1.137 - {
1.138 - //Has to be a WM_INPUT message
1.139 - return;
1.140 - }
1.141 -
1.142 - if (Macro.GET_RAWINPUT_CODE_WPARAM(aMessage.WParam) == Const.RIM_INPUT)
1.143 - {
1.144 - IsForeground = true;
1.145 - }
1.146 - else if (Macro.GET_RAWINPUT_CODE_WPARAM(aMessage.WParam) == Const.RIM_INPUTSINK)
1.147 - {
1.148 - IsForeground = false;
1.149 - }
1.150 -
1.151 - //Declare some pointers
1.152 - IntPtr rawInputBuffer = IntPtr.Zero;
1.153 -
1.154 - try
1.155 - {
1.156 - //Fetch raw input
1.157 - iRawInput = new RAWINPUT();
1.158 - if (!Win32.RawInput.GetRawInputData(aMessage.LParam, ref iRawInput, ref rawInputBuffer))
1.159 - {
1.160 - Debug.WriteLine("GetRawInputData failed!");
1.161 - return;
1.162 - }
1.163 -
1.164 - //Our device can actually be null.
1.165 - //This is notably happening for some keyboard events
1.166 - if (RawInput.header.hDevice != IntPtr.Zero)
1.167 - {
1.168 - //Get various information about this HID device
1.169 - Device = new HidDevice(RawInput.header.hDevice);
1.170 - }
1.171 -
1.172 - if (RawInput.header.dwType == Win32.RawInputDeviceType.RIM_TYPEHID) //Check that our raw input is HID
1.173 - {
1.174 - IsGeneric = true;
1.175 -
1.176 - Debug.WriteLine("WM_INPUT source device is HID.");
1.177 - //Get Usage Page and Usage
1.178 - //Debug.WriteLine("Usage Page: 0x" + deviceInfo.hid.usUsagePage.ToString("X4") + " Usage ID: 0x" + deviceInfo.hid.usUsage.ToString("X4"));
1.179 - UsagePage = Device.Info.hid.usUsagePage;
1.180 - UsageCollection = Device.Info.hid.usUsage;
1.181 -
1.182 - if (!(RawInput.hid.dwSizeHid > 1 //Make sure our HID msg size more than 1. In fact the first ushort is irrelevant to us for now
1.183 - && RawInput.hid.dwCount > 0)) //Check that we have at least one HID msg
1.184 - {
1.185 - return;
1.186 - }
1.187 -
1.188 - //Allocate a buffer for one HID input
1.189 - InputReport = new byte[RawInput.hid.dwSizeHid];
1.190 -
1.191 - Debug.WriteLine("Raw input contains " + RawInput.hid.dwCount + " HID input report(s)");
1.192 -
1.193 - //For each HID input report in our raw input
1.194 - for (int i = 0; i < RawInput.hid.dwCount; i++)
1.195 - {
1.196 - //Compute the address from which to copy our HID input
1.197 - int hidInputOffset = 0;
1.198 - unsafe
1.199 - {
1.200 - byte* source = (byte*)rawInputBuffer;
1.201 - source += sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + (RawInput.hid.dwSizeHid * i);
1.202 - hidInputOffset = (int)source;
1.203 - }
1.204 -
1.205 - //Copy HID input into our buffer
1.206 - Marshal.Copy(new IntPtr(hidInputOffset), InputReport, 0, (int)RawInput.hid.dwSizeHid);
1.207 - //
1.208 - ProcessInputReport(InputReport);
1.209 - }
1.210 - }
1.211 - else if (RawInput.header.dwType == RawInputDeviceType.RIM_TYPEMOUSE)
1.212 - {
1.213 - IsMouse = true;
1.214 -
1.215 - Debug.WriteLine("WM_INPUT source device is Mouse.");
1.216 - // do mouse handling...
1.217 - }
1.218 - else if (RawInput.header.dwType == RawInputDeviceType.RIM_TYPEKEYBOARD)
1.219 - {
1.220 - IsKeyboard = true;
1.221 -
1.222 - Debug.WriteLine("WM_INPUT source device is Keyboard.");
1.223 - // do keyboard handling...
1.224 - if (Device != null)
1.225 - {
1.226 - Debug.WriteLine("Type: " + Device.Info.keyboard.dwType.ToString());
1.227 - Debug.WriteLine("SubType: " + Device.Info.keyboard.dwSubType.ToString());
1.228 - Debug.WriteLine("Mode: " + Device.Info.keyboard.dwKeyboardMode.ToString());
1.229 - Debug.WriteLine("Number of function keys: " + Device.Info.keyboard.dwNumberOfFunctionKeys.ToString());
1.230 - Debug.WriteLine("Number of indicators: " + Device.Info.keyboard.dwNumberOfIndicators.ToString());
1.231 - Debug.WriteLine("Number of keys total: " + Device.Info.keyboard.dwNumberOfKeysTotal.ToString());
1.232 - }
1.233 - }
1.234 - }
1.235 - finally
1.236 - {
1.237 - //Always executed when leaving our try block
1.238 - Marshal.FreeHGlobal(rawInputBuffer);
1.239 - }
1.240 -
1.241 - //
1.242 - if (IsButtonDown)
1.243 - {
1.244 - //TODO: Make this optional
1.245 - //StartRepeatTimer(iRepeatDelay);
1.246 - }
1.247 -
1.248 - IsValid = true;
1.249 - }
1.250 -
1.251 - /// <summary>
1.252 - ///
1.253 - /// </summary>
1.254 - private void ProcessInputReport(byte[] aInputReport)
1.255 - {
1.256 - //Print HID input report in our debug output
1.257 - //string hidDump = "HID input report: " + InputReportString();
1.258 - //Debug.WriteLine(hidDump);
1.259 -
1.260 - //Get all our usages, those are typically the buttons currently pushed on a gamepad.
1.261 - //For a remote control it's usually just the one button that was pushed.
1.262 - GetUsages(aInputReport);
1.263 -
1.264 - //Now process direction pad (d-pad, dpad) and axes
1.265 - GetUsageValues(aInputReport);
1.266 - }
1.267 -
1.268 - /// <summary>
1.269 - /// Typically fetches values of a joystick/gamepad axis and dpad directions.
1.270 - /// </summary>
1.271 - /// <param name="aInputReport"></param>
1.272 - private void GetUsageValues(byte[] aInputReport)
1.273 - {
1.274 - if (Device.InputValueCapabilities == null)
1.275 - {
1.276 - return;
1.277 - }
1.278 -
1.279 - foreach (HIDP_VALUE_CAPS caps in Device.InputValueCapabilities)
1.280 - {
1.281 - if (caps.IsRange)
1.282 - {
1.283 - //What should we do with those guys?
1.284 - continue;
1.285 - }
1.286 -
1.287 - //Now fetch and add our usage value
1.288 - uint usageValue = 0;
1.289 - Win32.HidStatus status = Win32.Function.HidP_GetUsageValue(Win32.HIDP_REPORT_TYPE.HidP_Input, caps.UsagePage, caps.LinkCollection, caps.NotRange.Usage, ref usageValue, Device.PreParsedData, aInputReport, (uint)aInputReport.Length);
1.290 - if (status == Win32.HidStatus.HIDP_STATUS_SUCCESS)
1.291 - {
1.292 - UsageValues[caps]=usageValue;
1.293 - }
1.294 - }
1.295 - }
1.296 -
1.297 - /// <summary>
1.298 - /// Get all our usages, those are typically the buttons currently pushed on a gamepad.
1.299 - /// For a remote control it's usually just the one button that was pushed.
1.300 - /// </summary>
1.301 - private void GetUsages(byte[] aInputReport)
1.302 - {
1.303 - //Do proper parsing of our HID report
1.304 - //First query our usage count
1.305 - uint usageCount = 0;
1.306 - Win32.USAGE_AND_PAGE[] usages = null;
1.307 - Win32.HidStatus status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, aInputReport, (uint)aInputReport.Length);
1.308 - if (status == Win32.HidStatus.HIDP_STATUS_BUFFER_TOO_SMALL)
1.309 - {
1.310 - //Allocate a large enough buffer
1.311 - usages = new Win32.USAGE_AND_PAGE[usageCount];
1.312 - //...and fetch our usages
1.313 - status = Win32.Function.HidP_GetUsagesEx(Win32.HIDP_REPORT_TYPE.HidP_Input, 0, usages, ref usageCount, Device.PreParsedData, aInputReport, (uint)aInputReport.Length);
1.314 - if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
1.315 - {
1.316 - Debug.WriteLine("Second pass could not parse HID data: " + status.ToString());
1.317 - }
1.318 - }
1.319 - else if (status != Win32.HidStatus.HIDP_STATUS_SUCCESS)
1.320 - {
1.321 - Debug.WriteLine("First pass could not parse HID data: " + status.ToString());
1.322 - }
1.323 -
1.324 - Debug.WriteLine("Usage count: " + usageCount.ToString());
1.325 -
1.326 - //Copy usages into this event
1.327 - if (usages != null)
1.328 - {
1.329 - foreach (USAGE_AND_PAGE up in usages)
1.330 - {
1.331 - //Debug.WriteLine("UsagePage: 0x" + usages[0].UsagePage.ToString("X4"));
1.332 - //Debug.WriteLine("Usage: 0x" + usages[0].Usage.ToString("X4"));
1.333 - //Add this usage to our list
1.334 - Usages.Add(up.Usage);
1.335 - }
1.336 - }
1.337 - }
1.338 -
1.339 - /// <summary>
1.340 - ///
1.341 - /// </summary>
1.342 - /// <param name="aUsagePage"></param>
1.343 - /// <param name="Usage"></param>
1.344 - /// <returns></returns>
1.345 - public uint GetUsageValue(ushort aUsagePage, ushort aUsage)
1.346 - {
1.347 - foreach (HIDP_VALUE_CAPS caps in Device.InputValueCapabilities)
1.348 - {
1.349 - if (caps.IsRange)
1.350 - {
1.351 - //What should we do with those guys?
1.352 - continue;
1.353 - }
1.354 -
1.355 - //Check if we have a match
1.356 - if (caps.UsagePage == aUsagePage && caps.NotRange.Usage == aUsage)
1.357 - {
1.358 - return UsageValues[caps];
1.359 - }
1.360 - }
1.361 -
1.362 - return 0;
1.363 - }
1.364 -
1.365 - /// <summary>
1.366 - ///
1.367 - /// </summary>
1.368 - /// <param name="aUsagePage"></param>
1.369 - /// <param name="aUsage"></param>
1.370 - /// <returns></returns>
1.371 - public int GetValueCapabilitiesIndex(ushort aUsagePage, ushort aUsage)
1.372 - {
1.373 - int i = -1;
1.374 - foreach (HIDP_VALUE_CAPS caps in Device.InputValueCapabilities)
1.375 - {
1.376 - i++;
1.377 - if (caps.IsRange)
1.378 - {
1.379 - //What should we do with those guys?
1.380 - continue;
1.381 - }
1.382 -
1.383 - //Check if we have a match
1.384 - if (caps.UsagePage == aUsagePage && caps.NotRange.Usage == aUsage)
1.385 - {
1.386 - return i;
1.387 - }
1.388 - }
1.389 -
1.390 - return i;
1.391 - }
1.392 -
1.393 -
1.394 - /// <summary>
1.395 - /// TODO: Move this to another level?
1.396 - /// </summary>
1.397 - /// <param name="aInterval"></param>
1.398 - public void StartRepeatTimer(double aInterval)
1.399 - {
1.400 - if (Timer == null)
1.401 - {
1.402 - return;
1.403 - }
1.404 - Timer.Enabled = false;
1.405 - //Initial delay do not use auto reset
1.406 - //After our initial delay however we do setup our timer one more time using auto reset
1.407 - Timer.AutoReset = (RepeatCount != 0);
1.408 - Timer.Interval = aInterval;
1.409 - Timer.Enabled = true;
1.410 - }
1.411 -
1.412 - static private void OnRepeatTimerElapsed(object sender, ElapsedEventArgs e, HidEvent aHidEvent)
1.413 - {
1.414 - if (aHidEvent.IsStray)
1.415 - {
1.416 - //Skip events if canceled
1.417 - return;
1.418 - }
1.419 -
1.420 - aHidEvent.RepeatCount++;
1.421 - aHidEvent.Time = DateTime.Now;
1.422 - if (aHidEvent.RepeatCount == 1)
1.423 - {
1.424 - //Re-Start our timer only after the initial delay
1.425 - aHidEvent.StartRepeatTimer(aHidEvent.iRepeatSpeed);
1.426 - }
1.427 -
1.428 - //Broadcast our repeat event
1.429 - aHidEvent.OnHidEventRepeat(aHidEvent);
1.430 - }
1.431 -
1.432 - /// <summary>
1.433 - /// Provide the state of the dpad or hat switch if any.
1.434 - /// If no dpad is found we return 'at rest'.
1.435 - /// </summary>
1.436 - /// <returns></returns>
1.437 - public DirectionPadState GetDirectionPadState()
1.438 - {
1.439 - int index=GetValueCapabilitiesIndex((ushort)Hid.UsagePage.GenericDesktopControls, (ushort)GenericDesktop.HatSwitch);
1.440 - if (index < 0)
1.441 - {
1.442 - //No hat switch found
1.443 - return DirectionPadState.Rest;
1.444 - }
1.445 -
1.446 - HIDP_VALUE_CAPS caps=Device.InputValueCapabilities[index];
1.447 - if (caps.IsRange)
1.448 - {
1.449 - //Defensive
1.450 - return DirectionPadState.Rest;
1.451 - }
1.452 -
1.453 - uint dpadUsageValue = UsageValues[caps];
1.454 -
1.455 - if (dpadUsageValue < caps.LogicalMin || dpadUsageValue > caps.LogicalMax)
1.456 - {
1.457 - //Out of range means at rest
1.458 - return DirectionPadState.Rest;
1.459 - }
1.460 -
1.461 - //Normalize value to start at zero
1.462 - //TODO: more error check here?
1.463 - DirectionPadState res = (DirectionPadState)((int)dpadUsageValue - caps.LogicalMin);
1.464 - return res;
1.465 - }
1.466 -
1.467 - /// <summary>
1.468 - /// Print information about this device to our debug output.
1.469 - /// </summary>
1.470 - public void DebugWrite()
1.471 - {
1.472 - if (!IsValid)
1.473 - {
1.474 - Debug.WriteLine("==== Invalid HidEvent");
1.475 - return;
1.476 - }
1.477 -
1.478 - if (Device!=null)
1.479 - {
1.480 - Device.DebugWrite();
1.481 - }
1.482 -
1.483 - if (IsGeneric) Debug.WriteLine("==== Generic");
1.484 - if (IsKeyboard) Debug.WriteLine("==== Keyboard");
1.485 - if (IsMouse) Debug.WriteLine("==== Mouse");
1.486 - Debug.WriteLine("==== Foreground: " + IsForeground.ToString());
1.487 - Debug.WriteLine("==== UsagePage: 0x" + UsagePage.ToString("X4"));
1.488 - Debug.WriteLine("==== UsageCollection: 0x" + UsageCollection.ToString("X4"));
1.489 - Debug.WriteLine("==== InputReport: 0x" + InputReportString());
1.490 - foreach (ushort usage in Usages)
1.491 - {
1.492 - Debug.WriteLine("==== Usage: 0x" + usage.ToString("X4"));
1.493 - }
1.494 - }
1.495 -
1.496 - /// <summary>
1.497 - ///
1.498 - /// </summary>
1.499 - /// <returns></returns>
1.500 - public string InputReportString()
1.501 - {
1.502 - if (InputReport == null)
1.503 - {
1.504 - return "null";
1.505 - }
1.506 -
1.507 - string hidDump = "";
1.508 - foreach (byte b in InputReport)
1.509 - {
1.510 - hidDump += b.ToString("X2");
1.511 - }
1.512 - return hidDump;
1.513 - }
1.514 -
1.515 -
1.516 - /// <summary>
1.517 - /// Create a list view item describing this HidEvent
1.518 - /// </summary>
1.519 - /// <returns></returns>
1.520 - public ListViewItem ToListViewItem()
1.521 - {
1.522 - string usageText = "";
1.523 -
1.524 - foreach (ushort usage in Usages)
1.525 - {
1.526 - if (usageText != "")
1.527 - {
1.528 - //Add a separator
1.529 - usageText += ", ";
1.530 - }
1.531 -
1.532 - UsagePage usagePage = (UsagePage)UsagePage;
1.533 - switch (usagePage)
1.534 - {
1.535 - case Hid.UsagePage.Consumer:
1.536 - usageText += ((ConsumerControl)usage).ToString();
1.537 - break;
1.538 -
1.539 - case Hid.UsagePage.WindowsMediaCenterRemoteControl:
1.540 - usageText += ((WindowsMediaCenterRemoteControl)usage).ToString();
1.541 - break;
1.542 -
1.543 - default:
1.544 - usageText += usage.ToString("X2");
1.545 - break;
1.546 - }
1.547 - }
1.548 -
1.549 - //If we are a gamepad display axis and dpad values
1.550 - if (Device.IsGamePad)
1.551 - {
1.552 - //uint dpadUsageValue = GetUsageValue((ushort)Hid.UsagePage.GenericDesktopControls, (ushort)Hid.Usage.GenericDesktop.HatSwitch);
1.553 - //usageText = dpadUsageValue.ToString("X") + " (dpad), " + usageText;
1.554 -
1.555 - if (usageText != "")
1.556 - {
1.557 - //Add a separator
1.558 - usageText += " (Buttons)";
1.559 - }
1.560 -
1.561 - if (usageText != "")
1.562 - {
1.563 - //Add a separator
1.564 - usageText += ", ";
1.565 - }
1.566 -
1.567 - usageText += GetDirectionPadState().ToString();
1.568 -
1.569 - foreach (KeyValuePair<HIDP_VALUE_CAPS, uint> entry in UsageValues)
1.570 - {
1.571 - if (entry.Key.IsRange)
1.572 - {
1.573 - continue;
1.574 - }
1.575 -
1.576 - Type usageType = Utils.UsageType((UsagePage)entry.Key.UsagePage);
1.577 - if (usageType == null)
1.578 - {
1.579 - //TODO: check why this is happening on Logitech rumble gamepad 2.
1.580 - //Probably some of our axis are hiding in there.
1.581 - continue;
1.582 - }
1.583 - string name = Enum.GetName(usageType, entry.Key.NotRange.Usage);
1.584 -
1.585 - if (usageText != "")
1.586 - {
1.587 - //Add a separator
1.588 - usageText += ", ";
1.589 - }
1.590 - usageText += entry.Value.ToString("X") + " ("+ name +")";
1.591 - }
1.592 - }
1.593 -
1.594 - //Now create our list item
1.595 - ListViewItem item = new ListViewItem(new[] { usageText, InputReportString(), UsagePage.ToString("X2"), UsageCollection.ToString("X2"), RepeatCount.ToString(), Time.ToString("HH:mm:ss:fff") });
1.596 - return item;
1.597 - }
1.598 -
1.599 - }
1.600 -
1.601 -}
1.602 \ No newline at end of file