2014-01-17 00:27:07 +00:00
#region License
//
// WinRawJoystick.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2014 Stefanos Apostolopoulos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#endregion
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
2014-01-17 18:11:16 +00:00
using System.Runtime.InteropServices ;
2014-01-17 00:27:07 +00:00
using System.Text ;
using OpenTK.Input ;
2014-01-17 18:11:16 +00:00
using OpenTK.Platform.Common ;
2014-01-17 00:27:07 +00:00
namespace OpenTK.Platform.Windows
{
class WinRawJoystick : IJoystickDriver2
{
2014-01-17 18:11:16 +00:00
class Device
{
public IntPtr Handle ;
JoystickCapabilities Capabilities ;
JoystickState State ;
Guid Guid ;
2014-07-31 20:50:33 +00:00
internal readonly List < HidProtocolValueCaps > AxisCaps =
new List < HidProtocolValueCaps > ( ) ;
internal readonly List < HidProtocolButtonCaps > ButtonCaps =
new List < HidProtocolButtonCaps > ( ) ;
2014-07-31 22:41:49 +00:00
internal readonly bool IsXInput ;
internal readonly int XInputIndex ;
2014-07-31 20:50:33 +00:00
2014-01-17 18:11:16 +00:00
readonly Dictionary < int , JoystickAxis > axes =
new Dictionary < int , JoystickAxis > ( ) ;
readonly Dictionary < int , JoystickButton > buttons =
new Dictionary < int , JoystickButton > ( ) ;
2014-07-31 18:44:30 +00:00
readonly Dictionary < int , JoystickHat > hats =
new Dictionary < int , JoystickHat > ( ) ;
2014-01-17 18:11:16 +00:00
#region Constructors
2014-07-31 22:41:49 +00:00
public Device ( IntPtr handle , Guid guid , bool is_xinput , int xinput_index )
2014-01-17 18:11:16 +00:00
{
Handle = handle ;
Guid = guid ;
2014-07-31 22:41:49 +00:00
IsXInput = is_xinput ;
XInputIndex = xinput_index ;
2014-01-17 18:11:16 +00:00
}
#endregion
#region Public Members
2014-07-31 18:44:30 +00:00
public void ClearButtons ( )
{
State . ClearButtons ( ) ;
}
2014-08-02 21:10:33 +00:00
public void SetAxis ( short collection , HIDPage page , short usage , short value )
2014-01-17 18:11:16 +00:00
{
2016-01-08 10:43:39 +00:00
if ( page = = HIDPage . GenericDesktop | | page = = HIDPage . Simulation ) // set axis only when HIDPage is known by HidHelper.TranslateJoystickAxis() to avoid axis0 to be overwritten by unknown HIDPage
{
JoystickAxis axis = GetAxis ( collection , page , usage ) ;
State . SetAxis ( axis , value ) ;
}
2014-01-17 18:11:16 +00:00
}
2014-08-02 21:10:33 +00:00
public void SetButton ( short collection , HIDPage page , short usage , bool value )
2014-01-17 18:11:16 +00:00
{
2014-08-02 21:10:33 +00:00
JoystickButton button = GetButton ( collection , page , usage ) ;
2014-01-17 18:11:16 +00:00
State . SetButton ( button , value ) ;
}
2014-08-02 21:10:33 +00:00
public void SetHat ( short collection , HIDPage page , short usage , HatPosition pos )
2014-07-31 18:44:30 +00:00
{
2014-08-02 21:10:33 +00:00
JoystickHat hat = GetHat ( collection , page , usage ) ;
2014-07-31 18:44:30 +00:00
State . SetHat ( hat , new JoystickHatState ( pos ) ) ;
}
2014-01-18 15:00:27 +00:00
public void SetConnected ( bool value )
{
Capabilities . SetIsConnected ( value ) ;
State . SetIsConnected ( value ) ;
}
2014-01-17 18:11:16 +00:00
public JoystickCapabilities GetCapabilities ( )
{
2014-07-31 18:44:30 +00:00
Capabilities = new JoystickCapabilities (
axes . Count , buttons . Count , hats . Count ,
Capabilities . IsConnected ) ;
2014-01-17 18:11:16 +00:00
return Capabilities ;
}
2014-07-31 18:44:30 +00:00
internal void SetCapabilities ( JoystickCapabilities caps )
{
Capabilities = caps ;
}
2014-01-17 18:11:16 +00:00
public Guid GetGuid ( )
{
return Guid ;
}
public JoystickState GetState ( )
{
return State ;
}
#endregion
#region Private Members
2014-08-02 21:10:33 +00:00
static int MakeKey ( short collection , HIDPage page , short usage )
2014-01-17 18:11:16 +00:00
{
2014-08-02 21:10:33 +00:00
byte coll_byte = unchecked ( ( byte ) collection ) ;
byte page_byte = unchecked ( ( byte ) ( ( ( ushort ) page & 0xff00 ) > > 8 | ( ( ushort ) page & 0xff ) ) ) ;
return ( coll_byte < < 24 ) | ( page_byte < < 16 ) | unchecked ( ( ushort ) usage ) ;
2014-01-17 18:11:16 +00:00
}
2014-08-02 21:10:33 +00:00
JoystickAxis GetAxis ( short collection , HIDPage page , short usage )
2014-01-17 18:11:16 +00:00
{
2014-08-02 21:10:33 +00:00
int key = MakeKey ( collection , page , usage ) ;
2014-01-17 18:11:16 +00:00
if ( ! axes . ContainsKey ( key ) )
{
2014-07-31 20:28:23 +00:00
JoystickAxis axis = HidHelper . TranslateJoystickAxis ( page , usage ) ;
axes . Add ( key , axis ) ;
2014-01-17 18:11:16 +00:00
}
return axes [ key ] ;
}
2014-08-02 21:10:33 +00:00
JoystickButton GetButton ( short collection , HIDPage page , short usage )
2014-01-17 18:11:16 +00:00
{
2014-08-02 21:10:33 +00:00
int key = MakeKey ( collection , page , usage ) ;
2014-01-17 18:11:16 +00:00
if ( ! buttons . ContainsKey ( key ) )
{
buttons . Add ( key , JoystickButton . Button0 + buttons . Count ) ;
}
return buttons [ key ] ;
}
2014-08-02 21:10:33 +00:00
JoystickHat GetHat ( short collection , HIDPage page , short usage )
2014-07-31 18:44:30 +00:00
{
2014-08-02 21:10:33 +00:00
int key = MakeKey ( collection , page , usage ) ;
2014-07-31 18:44:30 +00:00
if ( ! hats . ContainsKey ( key ) )
{
hats . Add ( key , JoystickHat . Hat0 + hats . Count ) ;
}
return hats [ key ] ;
}
2014-01-17 18:11:16 +00:00
#endregion
}
2014-08-02 20:57:42 +00:00
static readonly string TypeName = typeof ( WinRawJoystick ) . Name ;
2014-07-31 22:41:49 +00:00
XInputJoystick XInput = new XInputJoystick ( ) ;
2014-01-17 18:11:16 +00:00
// Defines which types of HID devices we are interested in
readonly RawInputDevice [ ] DeviceTypes ;
2014-01-17 00:27:07 +00:00
readonly object UpdateLock = new object ( ) ;
2014-07-31 12:02:51 +00:00
readonly DeviceCollection < Device > Devices = new DeviceCollection < Device > ( ) ;
2014-01-17 00:27:07 +00:00
2014-07-31 13:44:11 +00:00
byte [ ] HIDData = new byte [ 1024 ] ;
2014-01-18 15:00:27 +00:00
byte [ ] PreparsedData = new byte [ 1024 ] ;
HidProtocolData [ ] DataBuffer = new HidProtocolData [ 16 ] ;
2014-01-17 00:27:07 +00:00
public WinRawJoystick ( IntPtr window )
{
2014-01-17 18:11:16 +00:00
Debug . WriteLine ( "Using WinRawJoystick." ) ;
2014-01-17 00:27:07 +00:00
Debug . Indent ( ) ;
if ( window = = IntPtr . Zero )
throw new ArgumentNullException ( "window" ) ;
2014-01-17 18:11:16 +00:00
DeviceTypes = new RawInputDevice [ ]
{
2014-07-31 20:52:50 +00:00
new RawInputDevice ( HIDUsageGD . Joystick , RawInputDeviceFlags . DEVNOTIFY | RawInputDeviceFlags . INPUTSINK , window ) ,
new RawInputDevice ( HIDUsageGD . GamePad , RawInputDeviceFlags . DEVNOTIFY | RawInputDeviceFlags . INPUTSINK , window ) ,
2014-01-17 18:11:16 +00:00
} ;
2014-01-18 15:00:27 +00:00
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" ) ;
}
2014-01-17 00:27:07 +00:00
RefreshDevices ( ) ;
Debug . Unindent ( ) ;
}
2014-01-17 18:11:16 +00:00
#region Public Members
2014-01-17 00:27:07 +00:00
public void RefreshDevices ( )
{
2014-01-18 15:00:27 +00:00
// Mark all devices as disconnected. We will check which of those
2014-07-31 12:02:51 +00:00
// are connected below.
foreach ( var device in Devices )
2014-01-17 18:11:16 +00:00
{
2014-07-31 12:02:51 +00:00
device . SetConnected ( false ) ;
2014-01-17 18:11:16 +00:00
}
2014-01-18 15:00:27 +00:00
2014-07-31 12:02:51 +00:00
// Discover joystick devices
2014-07-31 22:41:49 +00:00
int xinput_device_count = 0 ;
2014-01-18 15:00:27 +00:00
foreach ( RawInputDeviceList dev in WinRawInput . GetDeviceList ( ) )
2014-01-17 18:11:16 +00:00
{
2014-07-31 12:02:51 +00:00
// Skip non-joystick devices
2014-01-18 15:00:27 +00:00
if ( dev . Type ! = RawInputDeviceType . HID )
continue ;
2014-07-31 21:03:24 +00:00
// We use the device handle as the hardware id.
// This works, but the handle will change whenever the
// device is unplugged/replugged. We compensate for this
// by checking device GUIDs, below.
// Note: we cannot use the GUID as the hardware id,
// because it is costly to query (and we need to query
// that every time we process a device event.)
2014-01-18 15:00:27 +00:00
IntPtr handle = dev . Device ;
2014-09-02 20:12:13 +00:00
bool is_xinput = IsXInput ( handle ) ;
Guid guid = GetDeviceGuid ( handle ) ;
2014-07-31 13:44:11 +00:00
long hardware_id = handle . ToInt64 ( ) ;
2014-01-18 15:00:27 +00:00
2014-07-31 12:02:51 +00:00
Device device = Devices . FromHardwareId ( hardware_id ) ;
if ( device ! = null )
{
// We have already opened this device, mark it as connected
device . SetConnected ( true ) ;
}
else
{
2014-07-31 22:41:49 +00:00
device = new Device ( handle , guid , is_xinput ,
is_xinput ? xinput_device_count + + : 0 ) ;
2014-07-31 18:44:30 +00:00
2014-07-31 12:02:51 +00:00
// This is a new device, query its capabilities and add it
// to the device list
2016-01-08 12:16:38 +00:00
if ( ! QueryDeviceCaps ( device ) & & ! is_xinput )
2014-09-02 21:32:50 +00:00
{
continue ;
}
2014-07-31 12:02:51 +00:00
device . SetConnected ( true ) ;
2014-07-31 21:03:24 +00:00
// Check if a disconnected device with identical GUID already exists.
// If so, replace that device with this instance.
Device match = null ;
foreach ( Device candidate in Devices )
{
if ( candidate . GetGuid ( ) = = guid & & ! candidate . GetCapabilities ( ) . IsConnected )
{
match = candidate ;
}
}
if ( match ! = null )
{
Devices . Remove ( match . Handle . ToInt64 ( ) ) ;
}
2014-07-31 12:02:51 +00:00
Devices . Add ( hardware_id , device ) ;
2014-01-18 15:00:27 +00:00
2014-07-31 18:44:30 +00:00
Debug . Print ( "[{0}] Connected joystick {1} ({2})" ,
GetType ( ) . Name , device . GetGuid ( ) , device . GetCapabilities ( ) ) ;
2014-07-31 12:02:51 +00:00
}
}
2014-01-17 18:11:16 +00:00
}
2014-07-31 13:44:11 +00:00
public unsafe bool ProcessEvent ( IntPtr raw )
2014-01-17 18:11:16 +00:00
{
2014-07-31 13:44:11 +00:00
// Query the size of the raw HID data buffer
int size = 0 ;
Functions . GetRawInputData ( raw , GetRawInputDataEnum . INPUT , IntPtr . Zero , ref size , RawInputHeader . SizeInBytes ) ;
if ( size > HIDData . Length )
2014-01-17 18:11:16 +00:00
{
2014-07-31 13:44:11 +00:00
Array . Resize ( ref HIDData , size ) ;
2014-01-18 15:00:27 +00:00
}
2014-07-31 13:44:11 +00:00
// Retrieve the raw HID data buffer
if ( Functions . GetRawInputData ( raw , HIDData ) > 0 )
2014-01-18 15:00:27 +00:00
{
2014-07-31 13:44:11 +00:00
fixed ( byte * pdata = HIDData )
2014-01-18 15:00:27 +00:00
{
2014-07-31 13:44:11 +00:00
RawInput * rin = ( RawInput * ) pdata ;
IntPtr handle = rin - > Header . Device ;
Device stick = GetDevice ( handle ) ;
if ( stick = = null )
{
Debug . Print ( "[WinRawJoystick] Unknown device {0}" , handle ) ;
return false ;
}
2014-07-31 22:41:49 +00:00
if ( stick . IsXInput )
{
return true ;
}
2014-07-31 13:44:11 +00:00
if ( ! GetPreparsedData ( handle , ref PreparsedData ) )
{
return false ;
}
// Query current state
// Allocate enough storage to hold the data of the current report
int report_count = HidProtocol . MaxDataListLength ( HidProtocolReportType . Input , PreparsedData ) ;
if ( report_count = = 0 )
{
Debug . Print ( "[WinRawJoystick] HidProtocol.MaxDataListLength() failed with {0}" ,
Marshal . GetLastWin32Error ( ) ) ;
return false ;
}
// Fill the data buffer
if ( DataBuffer . Length < report_count )
{
Array . Resize ( ref DataBuffer , report_count ) ;
}
2014-07-31 21:47:18 +00:00
UpdateAxes ( rin , stick ) ;
2014-07-31 20:50:33 +00:00
UpdateButtons ( rin , stick ) ;
2014-07-31 13:44:11 +00:00
return true ;
2014-01-18 15:00:27 +00:00
}
}
2014-07-31 13:44:11 +00:00
return false ;
2014-01-18 15:00:27 +00:00
}
2014-07-31 21:47:18 +00:00
HatPosition GetHatPosition ( uint value , HidProtocolValueCaps caps )
2014-07-31 20:28:23 +00:00
{
if ( caps . LogicalMax = = 8 )
return ( HatPosition ) value ;
2016-01-08 10:43:39 +00:00
else if ( caps . LogicalMax = = 7 )
{
value + + ;
value % = 9 ;
return ( HatPosition ) value ;
}
2014-07-31 20:28:23 +00:00
else
return HatPosition . Centered ;
}
2014-07-31 21:47:18 +00:00
unsafe void UpdateAxes ( RawInput * rin , Device stick )
{
for ( int i = 0 ; i < stick . AxisCaps . Count ; i + + )
{
if ( stick . AxisCaps [ i ] . IsRange )
{
Debug . Print ( "[{0}] Axis range collections not implemented. Please report your controller type at http://www.opentk.com" ,
GetType ( ) . Name ) ;
continue ;
}
HIDPage page = stick . AxisCaps [ i ] . UsagePage ;
short usage = stick . AxisCaps [ i ] . NotRange . Usage ;
uint value = 0 ;
2014-08-02 21:10:33 +00:00
short collection = stick . AxisCaps [ i ] . LinkCollection ;
2014-07-31 21:47:18 +00:00
HidProtocolStatus status = HidProtocol . GetUsageValue (
HidProtocolReportType . Input ,
page , 0 , usage , ref value ,
PreparsedData ,
new IntPtr ( ( void * ) & rin - > Data . HID . RawData ) ,
rin - > Data . HID . Size ) ;
if ( status ! = HidProtocolStatus . Success )
{
Debug . Print ( "[{0}] HidProtocol.GetScaledUsageValue() failed. Error: {1}" ,
GetType ( ) . Name , status ) ;
continue ;
}
if ( page = = HIDPage . GenericDesktop & & ( HIDUsageGD ) usage = = HIDUsageGD . Hatswitch )
{
2014-08-02 21:10:33 +00:00
stick . SetHat ( collection , page , usage , GetHatPosition ( value , stick . AxisCaps [ i ] ) ) ;
2014-07-31 21:47:18 +00:00
}
else
{
short scaled_value = ( short ) HidHelper . ScaleValue (
( int ) ( ( long ) value + stick . AxisCaps [ i ] . LogicalMin ) ,
stick . AxisCaps [ i ] . LogicalMin , stick . AxisCaps [ i ] . LogicalMax ,
Int16 . MinValue , Int16 . MaxValue ) ;
2014-08-02 21:10:33 +00:00
stick . SetAxis ( collection , page , usage , scaled_value ) ;
2014-07-31 21:47:18 +00:00
}
}
}
2014-07-31 20:50:33 +00:00
unsafe void UpdateButtons ( RawInput * rin , Device stick )
2014-07-31 18:44:30 +00:00
{
stick . ClearButtons ( ) ;
2014-07-31 20:50:33 +00:00
for ( int i = 0 ; i < stick . ButtonCaps . Count ; i + + )
2014-07-31 18:44:30 +00:00
{
2014-07-31 20:28:23 +00:00
short * usage_list = stackalloc short [ ( int ) JoystickButton . Last + 1 ] ;
2014-07-31 18:44:30 +00:00
int usage_length = ( int ) JoystickButton . Last ;
2014-07-31 20:50:33 +00:00
HIDPage page = stick . ButtonCaps [ i ] . UsagePage ;
2014-08-02 21:10:33 +00:00
short collection = stick . ButtonCaps [ i ] . LinkCollection ;
2014-07-31 18:44:30 +00:00
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 + + )
{
2014-07-31 20:28:23 +00:00
short usage = * ( usage_list + j ) ;
2014-08-02 21:10:33 +00:00
stick . SetButton ( collection , page , usage , true ) ;
2014-07-31 18:44:30 +00:00
}
}
}
2014-01-18 15:53:30 +00:00
#endregion
#region Private Members
2014-01-18 15:00:27 +00:00
static bool GetPreparsedData ( IntPtr handle , ref byte [ ] prepared_data )
{
2014-01-17 18:11:16 +00:00
// Query the size of the _HIDP_PREPARSED_DATA structure for this event.
int preparsed_size = 0 ;
2014-01-18 15:00:27 +00:00
Functions . GetRawInputDeviceInfo ( handle , RawInputDeviceInfoEnum . PREPARSEDDATA ,
2014-01-17 18:11:16 +00:00
IntPtr . Zero , ref preparsed_size ) ;
if ( preparsed_size = = 0 )
2014-01-18 15:00:27 +00:00
{
Debug . Print ( "[WinRawJoystick] Functions.GetRawInputDeviceInfo(PARSEDDATA) failed with {0}" ,
Marshal . GetLastWin32Error ( ) ) ;
2014-01-17 18:11:16 +00:00
return false ;
2014-01-18 15:00:27 +00:00
}
2014-01-17 18:11:16 +00:00
// Allocate space for _HIDP_PREPARSED_DATA.
// This is an untyped blob of data.
2014-01-18 15:00:27 +00:00
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 ( ) ) ;
2014-01-17 18:11:16 +00:00
return false ;
2014-01-18 15:00:27 +00:00
}
return true ;
}
2014-09-02 21:32:50 +00:00
bool QueryDeviceCaps ( Device stick )
2014-01-18 15:00:27 +00:00
{
2014-08-02 20:57:42 +00:00
Debug . Print ( "[{0}] Querying joystick {1}" ,
TypeName , stick . GetGuid ( ) ) ;
2014-07-31 18:44:30 +00:00
2014-08-02 20:57:42 +00:00
try
2014-01-18 15:00:27 +00:00
{
2014-08-02 20:57:42 +00:00
Debug . Indent ( ) ;
HidProtocolCaps caps ;
if ( GetPreparsedData ( stick . Handle , ref PreparsedData ) & &
GetDeviceCaps ( stick , PreparsedData , out caps ) )
2014-01-18 15:00:27 +00:00
{
2014-09-02 21:32:50 +00:00
if ( stick . AxisCaps . Count > = JoystickState . MaxAxes | |
stick . ButtonCaps . Count > = JoystickState . MaxButtons )
{
Debug . Print ( "Device {0} has {1} and {2} buttons. This might be a touch device - skipping." ,
stick . Handle , stick . AxisCaps . Count , stick . ButtonCaps . Count ) ;
return false ;
}
2014-08-02 20:57:42 +00:00
for ( int i = 0 ; i < stick . AxisCaps . Count ; i + + )
2014-07-31 07:49:43 +00:00
{
2014-08-02 20:57:42 +00:00
Debug . Print ( "Analyzing value collection {0} {1} {2}" ,
i ,
stick . AxisCaps [ i ] . IsRange ? "range" : "" ,
stick . AxisCaps [ i ] . IsAlias ? "alias" : "" ) ;
2014-01-18 15:00:27 +00:00
2014-08-02 20:57:42 +00:00
if ( stick . AxisCaps [ i ] . IsRange | | stick . AxisCaps [ i ] . IsAlias )
{
Debug . Print ( "Skipping value collection {0}" , i ) ;
continue ;
}
HIDPage page = stick . AxisCaps [ i ] . UsagePage ;
2014-08-02 21:10:33 +00:00
short collection = stick . AxisCaps [ i ] . LinkCollection ;
2014-08-02 20:57:42 +00:00
switch ( page )
{
case HIDPage . GenericDesktop :
2014-09-02 20:12:13 +00:00
HIDUsageGD gd_usage = ( HIDUsageGD ) stick . AxisCaps [ i ] . NotRange . Usage ;
switch ( gd_usage )
2014-08-02 20:57:42 +00:00
{
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 :
Debug . Print ( "Found axis {0} ({1} / {2})" ,
JoystickAxis . Axis0 + stick . GetCapabilities ( ) . AxisCount ,
page , ( HIDUsageGD ) stick . AxisCaps [ i ] . NotRange . Usage ) ;
2014-08-02 21:10:33 +00:00
stick . SetAxis ( collection , page , stick . AxisCaps [ i ] . NotRange . Usage , 0 ) ;
2014-08-02 20:57:42 +00:00
break ;
case HIDUsageGD . Hatswitch :
Debug . Print ( "Found hat {0} ({1} / {2})" ,
JoystickHat . Hat0 + stick . GetCapabilities ( ) . HatCount ,
page , ( HIDUsageGD ) stick . AxisCaps [ i ] . NotRange . Usage ) ;
2014-08-02 21:10:33 +00:00
stick . SetHat ( collection , page , stick . AxisCaps [ i ] . NotRange . Usage , HatPosition . Centered ) ;
2014-08-02 20:57:42 +00:00
break ;
2014-09-02 20:12:13 +00:00
default :
Debug . Print ( "Unknown usage {0} for page {1}" ,
gd_usage , page ) ;
break ;
2014-08-02 20:57:42 +00:00
}
break ;
case HIDPage . Simulation :
switch ( ( HIDUsageSim ) stick . AxisCaps [ i ] . NotRange . Usage )
{
case HIDUsageSim . Rudder :
case HIDUsageSim . Throttle :
Debug . Print ( "Found simulation axis {0} ({1} / {2})" ,
JoystickAxis . Axis0 + stick . GetCapabilities ( ) . AxisCount ,
page , ( HIDUsageSim ) stick . AxisCaps [ i ] . NotRange . Usage ) ;
2014-08-02 21:10:33 +00:00
stick . SetAxis ( collection , page , stick . AxisCaps [ i ] . NotRange . Usage , 0 ) ;
2014-08-02 20:57:42 +00:00
break ;
}
break ;
2014-09-02 20:12:13 +00:00
default :
Debug . Print ( "Unknown page {0}" , page ) ;
break ;
2014-08-02 20:57:42 +00:00
}
2014-01-18 15:00:27 +00:00
}
2014-08-02 20:57:42 +00:00
for ( int i = 0 ; i < stick . ButtonCaps . Count ; i + + )
2014-07-31 07:49:43 +00:00
{
2014-08-02 20:57:42 +00:00
Debug . Print ( "Analyzing button collection {0} {1} {2}" ,
i ,
stick . ButtonCaps [ i ] . IsRange ? "range" : "" ,
stick . ButtonCaps [ i ] . IsAlias ? "alias" : "" ) ;
if ( stick . ButtonCaps [ i ] . IsAlias )
{
Debug . Print ( "Skipping button collection {0}" , i ) ;
continue ;
}
bool is_range = stick . ButtonCaps [ i ] . IsRange ;
HIDPage page = stick . ButtonCaps [ i ] . UsagePage ;
2014-08-02 21:10:33 +00:00
short collection = stick . ButtonCaps [ i ] . LinkCollection ;
2014-08-02 20:57:42 +00:00
switch ( page )
{
case HIDPage . Button :
if ( is_range )
2014-07-31 18:44:30 +00:00
{
2014-08-02 20:57:42 +00:00
for ( short usage = stick . ButtonCaps [ i ] . Range . UsageMin ; usage < = stick . ButtonCaps [ i ] . Range . UsageMax ; usage + + )
{
Debug . Print ( "Found button {0} ({1} / {2})" ,
JoystickButton . Button0 + stick . GetCapabilities ( ) . ButtonCount ,
page , usage ) ;
2014-08-02 21:10:33 +00:00
stick . SetButton ( collection , page , usage , false ) ;
2014-08-02 20:57:42 +00:00
}
2014-07-31 18:44:30 +00:00
}
2014-08-02 20:57:42 +00:00
else
{
Debug . Print ( "Found button {0} ({1} / {2})" ,
JoystickButton . Button0 + stick . GetCapabilities ( ) . ButtonCount ,
page , stick . ButtonCaps [ i ] . NotRange . Usage ) ;
2014-08-02 21:10:33 +00:00
stick . SetButton ( collection , page , stick . ButtonCaps [ i ] . NotRange . Usage , false ) ;
2014-08-02 20:57:42 +00:00
}
break ;
default :
Debug . Print ( "Unknown page {0} for button." , page ) ;
break ;
}
2014-07-31 07:49:43 +00:00
}
}
2016-01-08 12:16:38 +00:00
else
return false ;
2014-01-18 15:00:27 +00:00
}
2014-08-02 20:57:42 +00:00
finally
{
Debug . Unindent ( ) ;
}
2014-09-02 21:32:50 +00:00
return true ;
2014-01-18 15:00:27 +00:00
}
2014-01-17 18:11:16 +00:00
2014-07-31 20:50:33 +00:00
static bool GetDeviceCaps ( Device stick , byte [ ] preparsed_data , out HidProtocolCaps caps )
2014-01-18 15:00:27 +00:00
{
2014-01-17 18:11:16 +00:00
// Query joystick capabilities
2014-01-18 15:00:27 +00:00
caps = new HidProtocolCaps ( ) ;
2014-01-17 18:11:16 +00:00
if ( HidProtocol . GetCaps ( preparsed_data , ref caps ) ! = HidProtocolStatus . Success )
2014-01-18 15:00:27 +00:00
{
Debug . Print ( "[WinRawJoystick] HidProtocol.GetCaps() failed with {0}" ,
Marshal . GetLastWin32Error ( ) ) ;
2014-01-17 18:11:16 +00:00
return false ;
2014-01-18 15:00:27 +00:00
}
// Make sure our caps arrays are big enough
2014-07-31 20:50:33 +00:00
HidProtocolValueCaps [ ] axis_caps = new HidProtocolValueCaps [ caps . NumberInputValueCaps ] ;
HidProtocolButtonCaps [ ] button_caps = new HidProtocolButtonCaps [ caps . NumberInputButtonCaps ] ;
2014-01-17 18:11:16 +00:00
// Axis capabilities
2014-07-31 07:49:43 +00:00
ushort axis_count = ( ushort ) axis_caps . Length ;
2014-01-17 18:11:16 +00:00
if ( HidProtocol . GetValueCaps ( HidProtocolReportType . Input ,
2014-07-31 07:49:43 +00:00
axis_caps , ref axis_count , preparsed_data ) ! =
2014-01-17 18:11:16 +00:00
HidProtocolStatus . Success )
{
2014-01-18 15:00:27 +00:00
Debug . Print ( "[WinRawJoystick] HidProtocol.GetValueCaps() failed with {0}" ,
Marshal . GetLastWin32Error ( ) ) ;
2014-01-17 18:11:16 +00:00
return false ;
}
// Button capabilities
2014-07-31 07:49:43 +00:00
ushort button_count = ( ushort ) button_caps . Length ;
2014-01-17 18:11:16 +00:00
if ( HidProtocol . GetButtonCaps ( HidProtocolReportType . Input ,
2014-07-31 07:49:43 +00:00
button_caps , ref button_count , preparsed_data ) ! =
2014-01-17 18:11:16 +00:00
HidProtocolStatus . Success )
{
Debug . Print ( "[WinRawJoystick] HidProtocol.GetButtonCaps() failed with {0}" ,
Marshal . GetLastWin32Error ( ) ) ;
return false ;
}
2014-07-31 20:50:33 +00:00
stick . AxisCaps . Clear ( ) ;
stick . AxisCaps . AddRange ( axis_caps ) ;
stick . ButtonCaps . Clear ( ) ;
stick . ButtonCaps . AddRange ( button_caps ) ;
2014-01-18 15:00:27 +00:00
return true ;
}
2014-01-17 18:11:16 +00:00
2014-09-02 20:12:13 +00:00
// Get a DirectInput-compatible Guid
// (equivalent to DIDEVICEINSTANCE guidProduct field)
Guid GetDeviceGuid ( IntPtr handle )
2014-01-18 15:00:27 +00:00
{
2014-09-02 20:12:13 +00:00
// Retrieve a RID_DEVICE_INFO struct which contains the VID and PID
RawInputDeviceInfo info = new RawInputDeviceInfo ( ) ;
int size = info . Size ;
if ( Functions . GetRawInputDeviceInfo ( handle , RawInputDeviceInfoEnum . DEVICEINFO , info , ref size ) < 0 )
{
Debug . Print ( "[WinRawJoystick] Functions.GetRawInputDeviceInfo(DEVICEINFO) failed with error {0}" ,
Marshal . GetLastWin32Error ( ) ) ;
return Guid . Empty ;
}
// Todo: this Guid format is only valid for USB joysticks.
// Bluetooth devices, such as OUYA controllers, have a totally
// different PID/VID format in DirectInput.
// Do we need to use the same guid or could we simply use PID/VID
// there too? (Test with an OUYA controller.)
int vid = info . Device . HID . VendorId ;
int pid = info . Device . HID . ProductId ;
return new Guid (
( pid < < 16 ) | vid ,
0 , 0 ,
0 , 0 ,
( byte ) 'P' , ( byte ) 'I' , ( byte ) 'D' ,
( byte ) 'V' , ( byte ) 'I' , ( byte ) 'D' ) ;
}
// Checks whether this is an XInput device.
// XInput devices should be handled through
// the XInput API.
bool IsXInput ( IntPtr handle )
{
bool is_xinput = false ;
2014-01-18 15:00:27 +00:00
unsafe
2014-01-17 18:11:16 +00:00
{
2014-01-18 15:00:27 +00:00
// 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 ( ) ) ;
2014-09-02 20:12:13 +00:00
return is_xinput ;
2014-01-18 15:00:27 +00:00
}
// Allocate memory and retrieve the DEVICENAME string
2014-07-31 12:02:51 +00:00
sbyte * pname = stackalloc sbyte [ size + 1 ] ;
2014-01-18 15:00:27 +00:00
if ( Functions . GetRawInputDeviceInfo ( handle , RawInputDeviceInfoEnum . DEVICENAME , ( IntPtr ) pname , ref size ) < 0 )
{
Debug . Print ( "[WinRawJoystick] Functions.GetRawInputDeviceInfo(DEVICENAME) failed with error {0}" ,
Marshal . GetLastWin32Error ( ) ) ;
2014-09-02 20:12:13 +00:00
return is_xinput ;
2014-01-18 15:00:27 +00:00
}
2014-01-17 18:11:16 +00:00
2014-01-18 15:00:27 +00:00
// 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" ) ;
2014-09-02 20:12:13 +00:00
return is_xinput ;
2014-01-18 15:00:27 +00:00
}
2014-07-31 22:41:49 +00:00
is_xinput = name . Contains ( "IG_" ) ;
2014-01-18 15:00:27 +00:00
}
2014-01-17 18:11:16 +00:00
2014-09-02 20:12:13 +00:00
return is_xinput ;
2014-01-17 18:11:16 +00:00
}
Device GetDevice ( IntPtr handle )
{
2014-07-31 12:02:51 +00:00
long hardware_id = handle . ToInt64 ( ) ;
2014-01-17 18:11:16 +00:00
bool is_device_known = false ;
lock ( UpdateLock )
{
2014-07-31 12:02:51 +00:00
is_device_known = Devices . FromHardwareId ( hardware_id ) ! = null ;
2014-01-17 18:11:16 +00:00
}
if ( ! is_device_known )
{
RefreshDevices ( ) ;
}
lock ( UpdateLock )
{
2014-07-31 12:02:51 +00:00
return Devices . FromHardwareId ( hardware_id ) ;
2014-01-17 18:11:16 +00:00
}
}
bool IsValid ( int index )
{
2014-07-31 12:02:51 +00:00
return Devices . FromIndex ( index ) ! = null ;
2014-01-17 18:11:16 +00:00
}
#endregion
#region IJoystickDriver2 Members
2014-01-17 00:27:07 +00:00
public JoystickState GetState ( int index )
{
2014-01-17 18:11:16 +00:00
lock ( UpdateLock )
{
if ( IsValid ( index ) )
{
2014-07-31 22:41:49 +00:00
Device dev = Devices . FromIndex ( index ) ;
if ( dev . IsXInput )
{
return XInput . GetState ( dev . XInputIndex ) ;
}
else
{
return dev . GetState ( ) ;
}
2014-01-17 18:11:16 +00:00
}
return new JoystickState ( ) ;
}
2014-01-17 00:27:07 +00:00
}
public JoystickCapabilities GetCapabilities ( int index )
{
2014-01-17 18:11:16 +00:00
lock ( UpdateLock )
{
if ( IsValid ( index ) )
{
2014-07-31 22:41:49 +00:00
Device dev = Devices . FromIndex ( index ) ;
if ( dev . IsXInput )
{
return XInput . GetCapabilities ( dev . XInputIndex ) ;
}
else
{
return dev . GetCapabilities ( ) ;
}
2014-01-17 18:11:16 +00:00
}
return new JoystickCapabilities ( ) ;
}
2014-01-17 00:27:07 +00:00
}
public Guid GetGuid ( int index )
{
2014-01-17 18:11:16 +00:00
lock ( UpdateLock )
{
if ( IsValid ( index ) )
{
2014-07-31 22:41:49 +00:00
Device dev = Devices . FromIndex ( index ) ;
if ( dev . IsXInput )
{
return XInput . GetGuid ( dev . XInputIndex ) ;
}
else
{
return dev . GetGuid ( ) ;
}
2014-01-17 18:11:16 +00:00
}
return new Guid ( ) ;
}
2014-01-17 00:27:07 +00:00
}
2014-01-17 18:11:16 +00:00
#endregion
2014-01-17 00:27:07 +00:00
}
}