mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-07-07 23:20:40 +00:00
Merge pull request #161 from thefiddler/win_xinput
[Win] HID-based IJoystickDriver2; improve XInput2 IGamePadDriver
This commit is contained in:
commit
41f1f92cdf
|
@ -101,6 +101,8 @@ namespace Examples
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Trace.Listeners.Add(new ConsoleTraceListener());
|
Trace.Listeners.Add(new ConsoleTraceListener());
|
||||||
|
Tests.GameWindowStates.Main();
|
||||||
|
return;
|
||||||
|
|
||||||
if (args.Length > 0)
|
if (args.Length > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -160,7 +160,31 @@ namespace OpenTK.Input
|
||||||
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadButtons"/>.</returns>
|
/// <returns>A <see cref="System.String"/> that represents the current <see cref="OpenTK.Input.GamePadButtons"/>.</returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Convert.ToString((int)buttons, 2).PadLeft(10, '0');
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||||
|
if (A == ButtonState.Pressed)
|
||||||
|
sb.Append("A");
|
||||||
|
if (B == ButtonState.Pressed)
|
||||||
|
sb.Append("B");
|
||||||
|
if (X == ButtonState.Pressed)
|
||||||
|
sb.Append("X");
|
||||||
|
if (Y == ButtonState.Pressed)
|
||||||
|
sb.Append("Y");
|
||||||
|
if (Back == ButtonState.Pressed)
|
||||||
|
sb.Append("Bk");
|
||||||
|
if (Start == ButtonState.Pressed)
|
||||||
|
sb.Append("St");
|
||||||
|
if (BigButton == ButtonState.Pressed)
|
||||||
|
sb.Append("Gd");
|
||||||
|
if (LeftShoulder == ButtonState.Pressed)
|
||||||
|
sb.Append("L");
|
||||||
|
if (RightShoulder == ButtonState.Pressed)
|
||||||
|
sb.Append("R");
|
||||||
|
if (LeftStick == ButtonState.Pressed)
|
||||||
|
sb.Append("Ls");
|
||||||
|
if (RightStick == ButtonState.Pressed)
|
||||||
|
sb.Append("Rs");
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -40,16 +40,18 @@ namespace OpenTK.Input
|
||||||
GamePadAxes axes;
|
GamePadAxes axes;
|
||||||
byte gamepad_type;
|
byte gamepad_type;
|
||||||
bool is_connected;
|
bool is_connected;
|
||||||
|
bool is_mapped;
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
internal GamePadCapabilities(GamePadType type, GamePadAxes axes, Buttons buttons, bool is_connected)
|
internal GamePadCapabilities(GamePadType type, GamePadAxes axes, Buttons buttons, bool is_connected, bool is_mapped)
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
gamepad_type = (byte)type;
|
gamepad_type = (byte)type;
|
||||||
this.axes = axes;
|
this.axes = axes;
|
||||||
this.buttons = buttons;
|
this.buttons = buttons;
|
||||||
this.is_connected = is_connected;
|
this.is_connected = is_connected;
|
||||||
|
this.is_mapped = is_mapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -317,6 +319,15 @@ namespace OpenTK.Input
|
||||||
get { return is_connected; }
|
get { return is_connected; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="System.Boolean"/> value describing whether a valid button configuration
|
||||||
|
/// exists for this <c>GamePad</c> in the GamePad configuration database.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsMapped
|
||||||
|
{
|
||||||
|
get { return is_mapped; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <param name="left">A <see cref="GamePadCapabilities"/> structure to test for equality.</param>
|
/// <param name="left">A <see cref="GamePadCapabilities"/> structure to test for equality.</param>
|
||||||
/// <param name="right">A <see cref="GamePadCapabilities"/> structure to test for equality.</param>
|
/// <param name="right">A <see cref="GamePadCapabilities"/> structure to test for equality.</param>
|
||||||
public static bool operator ==(GamePadCapabilities left, GamePadCapabilities right)
|
public static bool operator ==(GamePadCapabilities left, GamePadCapabilities right)
|
||||||
|
@ -338,11 +349,12 @@ namespace OpenTK.Input
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return String.Format(
|
return String.Format(
|
||||||
"{{Type: {0}; Axes: {1}; Buttons: {2}; Connected: {3}}}",
|
"{{Type: {0}; Axes: {1}; Buttons: {2}; {3}; {4}}}",
|
||||||
GamePadType,
|
GamePadType,
|
||||||
Convert.ToString((int)axes, 2),
|
Convert.ToString((int)axes, 2),
|
||||||
Convert.ToString((int)buttons, 2),
|
Convert.ToString((int)buttons, 2),
|
||||||
IsConnected);
|
IsMapped ? "Mapped" : "Unmapped",
|
||||||
|
IsConnected ? "Connected" : "Disconnected");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -355,6 +367,7 @@ namespace OpenTK.Input
|
||||||
return
|
return
|
||||||
buttons.GetHashCode() ^
|
buttons.GetHashCode() ^
|
||||||
is_connected.GetHashCode() ^
|
is_connected.GetHashCode() ^
|
||||||
|
is_mapped.GetHashCode() ^
|
||||||
gamepad_type.GetHashCode();
|
gamepad_type.GetHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,6 +399,7 @@ namespace OpenTK.Input
|
||||||
return
|
return
|
||||||
buttons == other.buttons &&
|
buttons == other.buttons &&
|
||||||
is_connected == other.is_connected &&
|
is_connected == other.is_connected &&
|
||||||
|
is_mapped == other.is_mapped &&
|
||||||
gamepad_type == other.gamepad_type;
|
gamepad_type == other.gamepad_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ namespace OpenTK.Input
|
||||||
{
|
{
|
||||||
class GamePadConfigurationDatabase
|
class GamePadConfigurationDatabase
|
||||||
{
|
{
|
||||||
|
internal const string UnmappedName = "Unmapped Controller";
|
||||||
|
|
||||||
readonly Dictionary<Guid, string> Configurations = new Dictionary<Guid, string>();
|
readonly Dictionary<Guid, string> Configurations = new Dictionary<Guid, string>();
|
||||||
|
|
||||||
internal GamePadConfigurationDatabase()
|
internal GamePadConfigurationDatabase()
|
||||||
|
@ -61,8 +63,19 @@ namespace OpenTK.Input
|
||||||
// 3. This notice may not be removed or altered from any source distribution.
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
// Default (unknown) configuration
|
||||||
|
if (!Configuration.RunningOnSdl2)
|
||||||
|
{
|
||||||
|
Add("00000000000000000000000000000000,Unmapped Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Old SDL2 mapping for XInput devices (pre SDL-2.0.4)
|
||||||
|
Add("00000000000000000000000000000000,XInput Controller,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a3,righty:a2,start:b4,x:b12,y:b13,");
|
||||||
|
}
|
||||||
|
|
||||||
// Windows - XInput
|
// Windows - XInput
|
||||||
Add("00000000000000000000000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
|
Add("78696e70757400000000000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,");
|
||||||
|
|
||||||
// Windows
|
// Windows
|
||||||
Add("341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,");
|
Add("341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,");
|
||||||
|
@ -70,6 +83,7 @@ namespace OpenTK.Input
|
||||||
Add("6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,");
|
Add("6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,");
|
||||||
Add("6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); // Guide button doesn't seem to be sent in DInput mode.
|
Add("6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,"); // Guide button doesn't seem to be sent in DInput mode.
|
||||||
Add("4d6963726f736f66742050432d6a6f79,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a5,righty:a4,x:b1,y:b2,");
|
Add("4d6963726f736f66742050432d6a6f79,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a5,righty:a4,x:b1,y:b2,");
|
||||||
|
Add("010906a3000000000000504944564944,P880 Controller,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b5,x:b0,y:b1,");
|
||||||
Add("88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,");
|
Add("88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,");
|
||||||
Add("4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,");
|
Add("4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,");
|
||||||
Add("25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,");
|
Add("25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,");
|
||||||
|
|
|
@ -110,8 +110,8 @@ namespace OpenTK.Input
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return String.Format(
|
return String.Format(
|
||||||
"{{Sticks: {0}; Buttons: {1}; DPad: {2}; IsConnected: {3}}}",
|
"{{Sticks: {0}; Triggers: {1}; Buttons: {2}; DPad: {3}; IsConnected: {4}}}",
|
||||||
ThumbSticks, Buttons, DPad, IsConnected);
|
ThumbSticks, Triggers, Buttons, DPad, IsConnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -86,7 +86,7 @@ namespace OpenTK.Input
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return String.Format(
|
return String.Format(
|
||||||
"{{Left: {0:f2}; Right: {1:f2}}}",
|
"({0:f2}; {1:f2})",
|
||||||
Left, Right);
|
Left, Right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace OpenTK.Input
|
namespace OpenTK.Input
|
||||||
|
@ -48,11 +49,18 @@ namespace OpenTK.Input
|
||||||
internal JoystickCapabilities(int axis_count, int button_count, int hat_count, bool is_connected)
|
internal JoystickCapabilities(int axis_count, int button_count, int hat_count, bool is_connected)
|
||||||
{
|
{
|
||||||
if (axis_count < 0 || axis_count > JoystickState.MaxAxes)
|
if (axis_count < 0 || axis_count > JoystickState.MaxAxes)
|
||||||
throw new ArgumentOutOfRangeException("axis_count");
|
Debug.Print("[{0}] Axis count {1} out of range (0, {2})",
|
||||||
|
typeof(JoystickCapabilities).Name, axis_count, JoystickState.MaxAxes);
|
||||||
if (button_count < 0 || button_count > JoystickState.MaxButtons)
|
if (button_count < 0 || button_count > JoystickState.MaxButtons)
|
||||||
throw new ArgumentOutOfRangeException("axis_count");
|
Debug.Print("[{0}] Button count {1} out of range (0, {2})",
|
||||||
|
typeof(JoystickCapabilities).Name, button_count, JoystickState.MaxButtons);
|
||||||
if (hat_count < 0 || hat_count > JoystickState.MaxHats)
|
if (hat_count < 0 || hat_count > JoystickState.MaxHats)
|
||||||
throw new ArgumentOutOfRangeException("hat_count");
|
Debug.Print("[{0}] Hat count {1} out of range (0, {2})",
|
||||||
|
typeof(JoystickCapabilities).Name, hat_count, JoystickState.MaxHats);
|
||||||
|
|
||||||
|
axis_count = MathHelper.Clamp(axis_count, 0, JoystickState.MaxAxes);
|
||||||
|
button_count = MathHelper.Clamp(button_count, 0, JoystickState.MaxButtons);
|
||||||
|
hat_count = MathHelper.Clamp(hat_count, 0, JoystickState.MaxHats);
|
||||||
|
|
||||||
this.axis_count = (byte)axis_count;
|
this.axis_count = (byte)axis_count;
|
||||||
this.button_count = (byte)button_count;
|
this.button_count = (byte)button_count;
|
||||||
|
@ -62,6 +70,15 @@ namespace OpenTK.Input
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Internal Members
|
||||||
|
|
||||||
|
internal void SetIsConnected(bool value)
|
||||||
|
{
|
||||||
|
is_connected = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Public Members
|
#region Public Members
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -95,6 +112,7 @@ namespace OpenTK.Input
|
||||||
public bool IsConnected
|
public bool IsConnected
|
||||||
{
|
{
|
||||||
get { return is_connected; }
|
get { return is_connected; }
|
||||||
|
private set { is_connected = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -104,7 +122,7 @@ namespace OpenTK.Input
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return String.Format(
|
return String.Format(
|
||||||
"{{Axes: {0}; Buttons: {1}; Hats: {2}; IsConnected: {2}}}",
|
"{{Axes: {0}; Buttons: {1}; Hats: {2}; IsConnected: {3}}}",
|
||||||
AxisCount, ButtonCount, HatCount, IsConnected);
|
AxisCount, ButtonCount, HatCount, IsConnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -162,11 +162,14 @@
|
||||||
<Compile Include="Math\Matrix4x2d.cs" />
|
<Compile Include="Math\Matrix4x2d.cs" />
|
||||||
<Compile Include="Math\Matrix4x3.cs" />
|
<Compile Include="Math\Matrix4x3.cs" />
|
||||||
<Compile Include="Math\Matrix4x3d.cs" />
|
<Compile Include="Math\Matrix4x3d.cs" />
|
||||||
|
<Compile Include="Platform\Common\Hid.cs" />
|
||||||
<Compile Include="Platform\DisplayDeviceBase.cs" />
|
<Compile Include="Platform\DisplayDeviceBase.cs" />
|
||||||
<Compile Include="Platform\Egl\EglUnixContext.cs" />
|
<Compile Include="Platform\Egl\EglUnixContext.cs" />
|
||||||
<Compile Include="Platform\Egl\EglWinContext.cs" />
|
<Compile Include="Platform\Egl\EglWinContext.cs" />
|
||||||
<Compile Include="Platform\MappedGamePadDriver.cs" />
|
<Compile Include="Platform\MappedGamePadDriver.cs" />
|
||||||
|
<Compile Include="Platform\Windows\Bindings\HidProtocol.cs" />
|
||||||
<Compile Include="Platform\Windows\WinInputBase.cs" />
|
<Compile Include="Platform\Windows\WinInputBase.cs" />
|
||||||
|
<Compile Include="Platform\Windows\WinRawJoystick.cs" />
|
||||||
<Compile Include="Platform\Windows\XInputJoystick.cs" />
|
<Compile Include="Platform\Windows\XInputJoystick.cs" />
|
||||||
<Compile Include="Platform\X11\Bindings\DL.cs" />
|
<Compile Include="Platform\X11\Bindings\DL.cs" />
|
||||||
<Compile Include="Platform\X11\Bindings\INotify.cs" />
|
<Compile Include="Platform\X11\Bindings\INotify.cs" />
|
||||||
|
@ -325,9 +328,6 @@
|
||||||
<Compile Include="Platform\Windows\WinGLContext.cs">
|
<Compile Include="Platform\Windows\WinGLContext.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Platform\Windows\WinMMJoystick.cs">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Platform\Windows\WglHelper.cs">
|
<Compile Include="Platform\Windows\WglHelper.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
270
Source/OpenTK/Platform/Common/Hid.cs
Normal file
270
Source/OpenTK/Platform/Common/Hid.cs
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
#region License
|
||||||
|
//
|
||||||
|
// Hid.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;
|
||||||
|
using System.Text;
|
||||||
|
using OpenTK.Input;
|
||||||
|
|
||||||
|
namespace OpenTK.Platform.Common
|
||||||
|
{
|
||||||
|
class HidHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Scales the specified value linearly between min and max.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to scale</param>
|
||||||
|
/// <param name="value_min">The minimum expected value (inclusive)</param>
|
||||||
|
/// <param name="value_max">The maximum expected value (inclusive)</param>
|
||||||
|
/// <param name="result_min">The minimum output value (inclusive)</param>
|
||||||
|
/// <param name="result_max">The maximum output value (inclusive)</param>
|
||||||
|
/// <returns>The value, scaled linearly between min and max</returns>
|
||||||
|
public static int ScaleValue(int value, int value_min, int value_max,
|
||||||
|
int result_min, int result_max)
|
||||||
|
{
|
||||||
|
if (value_min >= value_max || result_min >= result_max)
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
MathHelper.Clamp(value, value_min, value_max);
|
||||||
|
|
||||||
|
int range = result_max - result_min;
|
||||||
|
long temp = (value - value_min) * range; // need long to avoid overflow
|
||||||
|
return (int)(temp / (value_max - value_min) + result_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JoystickAxis TranslateJoystickAxis(HIDPage page, int usage)
|
||||||
|
{
|
||||||
|
switch (page)
|
||||||
|
{
|
||||||
|
case HIDPage.GenericDesktop:
|
||||||
|
switch ((HIDUsageGD)usage)
|
||||||
|
{
|
||||||
|
case HIDUsageGD.X:
|
||||||
|
return JoystickAxis.Axis0;
|
||||||
|
case HIDUsageGD.Y:
|
||||||
|
return JoystickAxis.Axis1;
|
||||||
|
|
||||||
|
case HIDUsageGD.Z:
|
||||||
|
return JoystickAxis.Axis2;
|
||||||
|
case HIDUsageGD.Rz:
|
||||||
|
return JoystickAxis.Axis3;
|
||||||
|
|
||||||
|
case HIDUsageGD.Rx:
|
||||||
|
return JoystickAxis.Axis4;
|
||||||
|
case HIDUsageGD.Ry:
|
||||||
|
return JoystickAxis.Axis5;
|
||||||
|
|
||||||
|
case HIDUsageGD.Slider:
|
||||||
|
return JoystickAxis.Axis6;
|
||||||
|
case HIDUsageGD.Dial:
|
||||||
|
return JoystickAxis.Axis7;
|
||||||
|
case HIDUsageGD.Wheel:
|
||||||
|
return JoystickAxis.Axis8;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HIDPage.Simulation:
|
||||||
|
switch ((HIDUsageSim)usage)
|
||||||
|
{
|
||||||
|
case HIDUsageSim.Rudder:
|
||||||
|
return JoystickAxis.Axis9;
|
||||||
|
case HIDUsageSim.Throttle:
|
||||||
|
return JoystickAxis.Axis10;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Print("[Input] Unknown axis with HID page/usage {0}/{1}", page, usage);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum HIDPage : ushort
|
||||||
|
{
|
||||||
|
Undefined = 0x00,
|
||||||
|
GenericDesktop = 0x01,
|
||||||
|
Simulation = 0x02,
|
||||||
|
VR = 0x03,
|
||||||
|
Sport = 0x04,
|
||||||
|
Game = 0x05,
|
||||||
|
// Reserved 0x06
|
||||||
|
KeyboardOrKeypad = 0x07, // USB Device Class Definition for Human Interface Devices (HID). Note: the usage type for all key codes is Selector (Sel).
|
||||||
|
LEDs = 0x08,
|
||||||
|
Button = 0x09,
|
||||||
|
Ordinal = 0x0A,
|
||||||
|
Telephony = 0x0B,
|
||||||
|
Consumer = 0x0C,
|
||||||
|
Digitizer = 0x0D,
|
||||||
|
// Reserved 0x0E
|
||||||
|
PID = 0x0F, // USB Physical Interface Device definitions for force feedback and related devices.
|
||||||
|
Unicode = 0x10,
|
||||||
|
// Reserved 0x11 - 0x13
|
||||||
|
AlphanumericDisplay = 0x14,
|
||||||
|
// Reserved 0x15 - 0x7F
|
||||||
|
// Monitor 0x80 - 0x83 USB Device Class Definition for Monitor Devices
|
||||||
|
// Power 0x84 - 0x87 USB Device Class Definition for Power Devices
|
||||||
|
PowerDevice = 0x84, // Power Device Page
|
||||||
|
BatterySystem = 0x85, // Battery System Page
|
||||||
|
// Reserved 0x88 - 0x8B
|
||||||
|
BarCodeScanner = 0x8C, // (Point of Sale) USB Device Class Definition for Bar Code Scanner Devices
|
||||||
|
WeighingDevice = 0x8D, // (Point of Sale) USB Device Class Definition for Weighing Devices
|
||||||
|
Scale = 0x8D, // (Point of Sale) USB Device Class Definition for Scale Devices
|
||||||
|
MagneticStripeReader = 0x8E,
|
||||||
|
// ReservedPointofSalepages 0x8F
|
||||||
|
CameraControl = 0x90, // USB Device Class Definition for Image Class Devices
|
||||||
|
Arcade = 0x91, // OAAF Definitions for arcade and coinop related Devices
|
||||||
|
// Reserved 0x92 - 0xFEFF
|
||||||
|
// VendorDefined 0xFF00 - 0xFFFF
|
||||||
|
VendorDefinedStart = 0xFF00
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consumer electronic devices
|
||||||
|
enum HIDUsageCD
|
||||||
|
{
|
||||||
|
ACPan = 0x0238
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic desktop usage
|
||||||
|
enum HIDUsageGD : ushort
|
||||||
|
{
|
||||||
|
Pointer = 0x01, // Physical Collection
|
||||||
|
Mouse = 0x02, // Application Collection
|
||||||
|
// 0x03 Reserved
|
||||||
|
Joystick = 0x04, // Application Collection
|
||||||
|
GamePad = 0x05, // Application Collection
|
||||||
|
Keyboard = 0x06, // Application Collection
|
||||||
|
Keypad = 0x07, // Application Collection
|
||||||
|
MultiAxisController = 0x08, // Application Collection
|
||||||
|
// 0x09 - 0x2F Reserved
|
||||||
|
X = 0x30, // Dynamic Value
|
||||||
|
Y = 0x31, // Dynamic Value
|
||||||
|
Z = 0x32, // Dynamic Value
|
||||||
|
Rx = 0x33, // Dynamic Value
|
||||||
|
Ry = 0x34, // Dynamic Value
|
||||||
|
Rz = 0x35, // Dynamic Value
|
||||||
|
Slider = 0x36, // Dynamic Value
|
||||||
|
Dial = 0x37, // Dynamic Value
|
||||||
|
Wheel = 0x38, // Dynamic Value
|
||||||
|
Hatswitch = 0x39, // Dynamic Value
|
||||||
|
CountedBuffer = 0x3A, // Logical Collection
|
||||||
|
ByteCount = 0x3B, // Dynamic Value
|
||||||
|
MotionWakeup = 0x3C, // One-Shot Control
|
||||||
|
Start = 0x3D, // On/Off Control
|
||||||
|
Select = 0x3E, // On/Off Control
|
||||||
|
// 0x3F Reserved
|
||||||
|
Vx = 0x40, // Dynamic Value
|
||||||
|
Vy = 0x41, // Dynamic Value
|
||||||
|
Vz = 0x42, // Dynamic Value
|
||||||
|
Vbrx = 0x43, // Dynamic Value
|
||||||
|
Vbry = 0x44, // Dynamic Value
|
||||||
|
Vbrz = 0x45, // Dynamic Value
|
||||||
|
Vno = 0x46, // Dynamic Value
|
||||||
|
// 0x47 - 0x7F Reserved
|
||||||
|
SystemControl = 0x80, // Application Collection
|
||||||
|
SystemPowerDown = 0x81, // One-Shot Control
|
||||||
|
SystemSleep = 0x82, // One-Shot Control
|
||||||
|
SystemWakeUp = 0x83, // One-Shot Control
|
||||||
|
SystemContextMenu = 0x84, // One-Shot Control
|
||||||
|
SystemMainMenu = 0x85, // One-Shot Control
|
||||||
|
SystemAppMenu = 0x86, // One-Shot Control
|
||||||
|
SystemMenuHelp = 0x87, // One-Shot Control
|
||||||
|
SystemMenuExit = 0x88, // One-Shot Control
|
||||||
|
SystemMenu = 0x89, // Selector
|
||||||
|
SystemMenuRight = 0x8A, // Re-Trigger Control
|
||||||
|
SystemMenuLeft = 0x8B, // Re-Trigger Control
|
||||||
|
SystemMenuUp = 0x8C, // Re-Trigger Control
|
||||||
|
SystemMenuDown = 0x8D, // Re-Trigger Control
|
||||||
|
// 0x8E - 0x8F Reserved
|
||||||
|
DPadUp = 0x90, // On/Off Control
|
||||||
|
DPadDown = 0x91, // On/Off Control
|
||||||
|
DPadRight = 0x92, // On/Off Control
|
||||||
|
DPadLeft = 0x93, // On/Off Control
|
||||||
|
// 0x94 - 0xFFFF Reserved
|
||||||
|
Reserved = 0xFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
enum HIDUsageSim : ushort
|
||||||
|
{
|
||||||
|
FlightSimulationDevice = 0x01, // Application Collection
|
||||||
|
AutomobileSimulationDevice = 0x02, // Application Collection
|
||||||
|
TankSimulationDevice = 0x03, // Application Collection
|
||||||
|
SpaceshipSimulationDevice = 0x04, // Application Collection
|
||||||
|
SubmarineSimulationDevice = 0x05, // Application Collection
|
||||||
|
SailingSimulationDevice = 0x06, // Application Collection
|
||||||
|
MotorcycleSimulationDevice = 0x07, // Application Collection
|
||||||
|
SportsSimulationDevice = 0x08, // Application Collection
|
||||||
|
AirplaneSimulationDevice = 0x09, // Application Collection
|
||||||
|
HelicopterSimulationDevice = 0x0A, // Application Collection
|
||||||
|
MagicCarpetSimulationDevice = 0x0B, // Application Collection
|
||||||
|
BicycleSimulationDevice = 0x0C, // Application Collection
|
||||||
|
// 0x0D - 0x1F Reserved
|
||||||
|
FlightControlStick = 0x20, // Application Collection
|
||||||
|
FlightStick = 0x21, // Application Collection
|
||||||
|
CyclicControl = 0x22, // Physical Collection
|
||||||
|
CyclicTrim = 0x23, // Physical Collection
|
||||||
|
FlightYoke = 0x24, // Application Collection
|
||||||
|
TrackControl = 0x25, // Physical Collection
|
||||||
|
// 0x26 - 0xAF Reserved
|
||||||
|
Aileron = 0xB0, // Dynamic Value
|
||||||
|
AileronTrim = 0xB1, // Dynamic Value
|
||||||
|
AntiTorqueControl = 0xB2, // Dynamic Value
|
||||||
|
AutopilotEnable = 0xB3, // On/Off Control
|
||||||
|
ChaffRelease = 0xB4, // One-Shot Control
|
||||||
|
CollectiveControl = 0xB5, // Dynamic Value
|
||||||
|
DiveBrake = 0xB6, // Dynamic Value
|
||||||
|
ElectronicCountermeasures = 0xB7, // On/Off Control
|
||||||
|
Elevator = 0xB8, // Dynamic Value
|
||||||
|
ElevatorTrim = 0xB9, // Dynamic Value
|
||||||
|
Rudder = 0xBA, // Dynamic Value
|
||||||
|
Throttle = 0xBB, // Dynamic Value
|
||||||
|
FlightCommunications = 0xBC, // On/Off Control
|
||||||
|
FlareRelease = 0xBD, // One-Shot Control
|
||||||
|
LandingGear = 0xBE, // On/Off Control
|
||||||
|
ToeBrake = 0xBF, // Dynamic Value
|
||||||
|
Trigger = 0xC0, // Momentary Control
|
||||||
|
WeaponsArm = 0xC1, // On/Off Control
|
||||||
|
Weapons = 0xC2, // Selector
|
||||||
|
WingFlaps = 0xC3, // Dynamic Value
|
||||||
|
Accelerator = 0xC4, // Dynamic Value
|
||||||
|
Brake = 0xC5, // Dynamic Value
|
||||||
|
Clutch = 0xC6, // Dynamic Value
|
||||||
|
Shifter = 0xC7, // Dynamic Value
|
||||||
|
Steering = 0xC8, // Dynamic Value
|
||||||
|
TurretDirection = 0xC9, // Dynamic Value
|
||||||
|
BarrelElevation = 0xCA, // Dynamic Value
|
||||||
|
DivePlane = 0xCB, // Dynamic Value
|
||||||
|
Ballast = 0xCC, // Dynamic Value
|
||||||
|
BicycleCrank = 0xCD, // Dynamic Value
|
||||||
|
HandleBars = 0xCE, // Dynamic Value
|
||||||
|
FrontBrake = 0xCF, // Dynamic Value
|
||||||
|
RearBrake = 0xD0, // Dynamic Value
|
||||||
|
// 0xD1 - 0xFFFF Reserved
|
||||||
|
Reserved = 0xFFFF
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,7 +41,7 @@ namespace OpenTK.Platform
|
||||||
// that is added.
|
// that is added.
|
||||||
class DeviceCollection<T> : IEnumerable<T>
|
class DeviceCollection<T> : IEnumerable<T>
|
||||||
{
|
{
|
||||||
readonly Dictionary<int, int> Map = new Dictionary<int, int>();
|
readonly Dictionary<long, int> Map = new Dictionary<long, int>();
|
||||||
readonly List<T> Devices = new List<T>();
|
readonly List<T> Devices = new List<T>();
|
||||||
|
|
||||||
#region IEnumerable<T> Members
|
#region IEnumerable<T> Members
|
||||||
|
@ -64,7 +64,15 @@ namespace OpenTK.Platform
|
||||||
|
|
||||||
#region Public Members
|
#region Public Members
|
||||||
|
|
||||||
public void Add(int id, T device)
|
/// \internal
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or replaces a device based on its hardware id.
|
||||||
|
/// A zero-based device index will be generated automatically
|
||||||
|
/// for the first available device slot.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The hardware id for the device.</param>
|
||||||
|
/// <param name="device">The device instance.</param>
|
||||||
|
public void Add(long id, T device)
|
||||||
{
|
{
|
||||||
if (!Map.ContainsKey(id))
|
if (!Map.ContainsKey(id))
|
||||||
{
|
{
|
||||||
|
@ -75,7 +83,7 @@ namespace OpenTK.Platform
|
||||||
Devices[Map[id]] = device;
|
Devices[Map[id]] = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(int id)
|
public void Remove(long id)
|
||||||
{
|
{
|
||||||
if (!TryRemove(id))
|
if (!TryRemove(id))
|
||||||
{
|
{
|
||||||
|
@ -83,7 +91,7 @@ namespace OpenTK.Platform
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryRemove(int id)
|
public bool TryRemove(long id)
|
||||||
{
|
{
|
||||||
if (!Map.ContainsKey(id))
|
if (!Map.ContainsKey(id))
|
||||||
{
|
{
|
||||||
|
@ -107,7 +115,7 @@ namespace OpenTK.Platform
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public T FromHardwareId(int id)
|
public T FromHardwareId(long id)
|
||||||
{
|
{
|
||||||
if (Map.ContainsKey(id))
|
if (Map.ContainsKey(id))
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace OpenTK.Platform
|
||||||
IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags);
|
IGraphicsContext CreateGLContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, bool directRendering, int major, int minor, GraphicsContextFlags flags);
|
||||||
|
|
||||||
GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext();
|
GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext();
|
||||||
|
|
||||||
OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver();
|
OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver();
|
||||||
|
|
||||||
OpenTK.Input.IMouseDriver2 CreateMouseDriver();
|
OpenTK.Input.IMouseDriver2 CreateMouseDriver();
|
||||||
|
|
|
@ -30,6 +30,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
|
using OpenTK.Platform.Common;
|
||||||
|
|
||||||
namespace OpenTK.Platform.MacOS
|
namespace OpenTK.Platform.MacOS
|
||||||
{
|
{
|
||||||
|
@ -755,7 +756,7 @@ namespace OpenTK.Platform.MacOS
|
||||||
case HIDUsageGD.Dial:
|
case HIDUsageGD.Dial:
|
||||||
case HIDUsageGD.Wheel:
|
case HIDUsageGD.Wheel:
|
||||||
short offset = GetJoystickAxis(val, elem);
|
short offset = GetJoystickAxis(val, elem);
|
||||||
JoystickAxis axis = TranslateJoystickAxis(usage);
|
JoystickAxis axis = HidHelper.TranslateJoystickAxis(page, usage);
|
||||||
if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
|
if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
|
||||||
{
|
{
|
||||||
joy.State.SetAxis(axis, offset);
|
joy.State.SetAxis(axis, offset);
|
||||||
|
@ -779,7 +780,7 @@ namespace OpenTK.Platform.MacOS
|
||||||
case HIDUsageSim.Rudder:
|
case HIDUsageSim.Rudder:
|
||||||
case HIDUsageSim.Throttle:
|
case HIDUsageSim.Throttle:
|
||||||
short offset = GetJoystickAxis(val, elem);
|
short offset = GetJoystickAxis(val, elem);
|
||||||
JoystickAxis axis = TranslateJoystickAxis(usage);
|
JoystickAxis axis = HidHelper.TranslateJoystickAxis(page, usage);
|
||||||
if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
|
if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
|
||||||
{
|
{
|
||||||
joy.State.SetAxis(axis, offset);
|
joy.State.SetAxis(axis, offset);
|
||||||
|
@ -806,51 +807,7 @@ namespace OpenTK.Platform.MacOS
|
||||||
int max = NativeMethods.IOHIDElementGetLogicalMax(element).ToInt32();
|
int max = NativeMethods.IOHIDElementGetLogicalMax(element).ToInt32();
|
||||||
int min = NativeMethods.IOHIDElementGetLogicalMin(element).ToInt32();
|
int min = NativeMethods.IOHIDElementGetLogicalMin(element).ToInt32();
|
||||||
int offset = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
|
int offset = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
|
||||||
if (offset < min)
|
return (short)HidHelper.ScaleValue(offset, min, max, short.MinValue, short.MaxValue);
|
||||||
offset = min;
|
|
||||||
if (offset > max)
|
|
||||||
offset = max;
|
|
||||||
|
|
||||||
const int range = short.MaxValue - short.MinValue + 1;
|
|
||||||
const int half_range = short.MaxValue + 1;
|
|
||||||
return (short)((offset - min) * range / (max - min) + half_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
static JoystickAxis TranslateJoystickAxis(int usage)
|
|
||||||
{
|
|
||||||
switch (usage)
|
|
||||||
{
|
|
||||||
case (int)HIDUsageGD.X:
|
|
||||||
return JoystickAxis.Axis0;
|
|
||||||
case (int)HIDUsageGD.Y:
|
|
||||||
return JoystickAxis.Axis1;
|
|
||||||
|
|
||||||
case (int)HIDUsageGD.Z:
|
|
||||||
return JoystickAxis.Axis2;
|
|
||||||
case (int)HIDUsageGD.Rz:
|
|
||||||
return JoystickAxis.Axis3;
|
|
||||||
|
|
||||||
case (int)HIDUsageGD.Rx:
|
|
||||||
return JoystickAxis.Axis4;
|
|
||||||
case (int)HIDUsageGD.Ry:
|
|
||||||
return JoystickAxis.Axis5;
|
|
||||||
|
|
||||||
case (int)HIDUsageGD.Slider:
|
|
||||||
return JoystickAxis.Axis6;
|
|
||||||
case (int)HIDUsageGD.Dial:
|
|
||||||
return JoystickAxis.Axis7;
|
|
||||||
case (int)HIDUsageGD.Wheel:
|
|
||||||
return JoystickAxis.Axis8;
|
|
||||||
|
|
||||||
case (int)HIDUsageSim.Rudder:
|
|
||||||
return JoystickAxis.Axis9;
|
|
||||||
case (int)HIDUsageSim.Throttle:
|
|
||||||
return JoystickAxis.Axis10;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Debug.Print("[Mac] Unknown axis with HID usage {0}", usage);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GetJoystickButton(IOHIDValueRef val, IOHIDElementRef element)
|
static bool GetJoystickButton(IOHIDValueRef val, IOHIDElementRef element)
|
||||||
|
@ -1209,169 +1166,6 @@ namespace OpenTK.Platform.MacOS
|
||||||
Calibrated // [-1, +1]
|
Calibrated // [-1, +1]
|
||||||
}
|
}
|
||||||
|
|
||||||
enum HIDPage
|
|
||||||
{
|
|
||||||
Undefined = 0x00,
|
|
||||||
GenericDesktop = 0x01,
|
|
||||||
Simulation = 0x02,
|
|
||||||
VR = 0x03,
|
|
||||||
Sport = 0x04,
|
|
||||||
Game = 0x05,
|
|
||||||
/* Reserved 0x06 */
|
|
||||||
KeyboardOrKeypad = 0x07, /* USB Device Class Definition for Human Interface Devices (HID). Note: the usage type for all key codes is Selector (Sel). */
|
|
||||||
LEDs = 0x08,
|
|
||||||
Button = 0x09,
|
|
||||||
Ordinal = 0x0A,
|
|
||||||
Telephony = 0x0B,
|
|
||||||
Consumer = 0x0C,
|
|
||||||
Digitizer = 0x0D,
|
|
||||||
/* Reserved 0x0E */
|
|
||||||
PID = 0x0F, /* USB Physical Interface Device definitions for force feedback and related devices. */
|
|
||||||
Unicode = 0x10,
|
|
||||||
/* Reserved 0x11 - 0x13 */
|
|
||||||
AlphanumericDisplay = 0x14,
|
|
||||||
/* Reserved 0x15 - 0x7F */
|
|
||||||
/* Monitor 0x80 - 0x83 USB Device Class Definition for Monitor Devices */
|
|
||||||
/* Power 0x84 - 0x87 USB Device Class Definition for Power Devices */
|
|
||||||
PowerDevice = 0x84, /* Power Device Page */
|
|
||||||
BatterySystem = 0x85, /* Battery System Page */
|
|
||||||
/* Reserved 0x88 - 0x8B */
|
|
||||||
BarCodeScanner = 0x8C, /* (Point of Sale) USB Device Class Definition for Bar Code Scanner Devices */
|
|
||||||
WeighingDevice = 0x8D, /* (Point of Sale) USB Device Class Definition for Weighing Devices */
|
|
||||||
Scale = 0x8D, /* (Point of Sale) USB Device Class Definition for Scale Devices */
|
|
||||||
MagneticStripeReader = 0x8E,
|
|
||||||
/* ReservedPointofSalepages 0x8F */
|
|
||||||
CameraControl = 0x90, /* USB Device Class Definition for Image Class Devices */
|
|
||||||
Arcade = 0x91, /* OAAF Definitions for arcade and coinop related Devices */
|
|
||||||
/* Reserved 0x92 - 0xFEFF */
|
|
||||||
/* VendorDefined 0xFF00 - 0xFFFF */
|
|
||||||
VendorDefinedStart = 0xFF00
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consumer electronic devices
|
|
||||||
enum HIDUsageCD
|
|
||||||
{
|
|
||||||
ACPan = 0x0238
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic desktop usage
|
|
||||||
enum HIDUsageGD
|
|
||||||
{
|
|
||||||
Pointer = 0x01, /* Physical Collection */
|
|
||||||
Mouse = 0x02, /* Application Collection */
|
|
||||||
/* 0x03 Reserved */
|
|
||||||
Joystick = 0x04, /* Application Collection */
|
|
||||||
GamePad = 0x05, /* Application Collection */
|
|
||||||
Keyboard = 0x06, /* Application Collection */
|
|
||||||
Keypad = 0x07, /* Application Collection */
|
|
||||||
MultiAxisController = 0x08, /* Application Collection */
|
|
||||||
/* 0x09 - 0x2F Reserved */
|
|
||||||
X = 0x30, /* Dynamic Value */
|
|
||||||
Y = 0x31, /* Dynamic Value */
|
|
||||||
Z = 0x32, /* Dynamic Value */
|
|
||||||
Rx = 0x33, /* Dynamic Value */
|
|
||||||
Ry = 0x34, /* Dynamic Value */
|
|
||||||
Rz = 0x35, /* Dynamic Value */
|
|
||||||
Slider = 0x36, /* Dynamic Value */
|
|
||||||
Dial = 0x37, /* Dynamic Value */
|
|
||||||
Wheel = 0x38, /* Dynamic Value */
|
|
||||||
Hatswitch = 0x39, /* Dynamic Value */
|
|
||||||
CountedBuffer = 0x3A, /* Logical Collection */
|
|
||||||
ByteCount = 0x3B, /* Dynamic Value */
|
|
||||||
MotionWakeup = 0x3C, /* One-Shot Control */
|
|
||||||
Start = 0x3D, /* On/Off Control */
|
|
||||||
Select = 0x3E, /* On/Off Control */
|
|
||||||
/* 0x3F Reserved */
|
|
||||||
Vx = 0x40, /* Dynamic Value */
|
|
||||||
Vy = 0x41, /* Dynamic Value */
|
|
||||||
Vz = 0x42, /* Dynamic Value */
|
|
||||||
Vbrx = 0x43, /* Dynamic Value */
|
|
||||||
Vbry = 0x44, /* Dynamic Value */
|
|
||||||
Vbrz = 0x45, /* Dynamic Value */
|
|
||||||
Vno = 0x46, /* Dynamic Value */
|
|
||||||
/* 0x47 - 0x7F Reserved */
|
|
||||||
SystemControl = 0x80, /* Application Collection */
|
|
||||||
SystemPowerDown = 0x81, /* One-Shot Control */
|
|
||||||
SystemSleep = 0x82, /* One-Shot Control */
|
|
||||||
SystemWakeUp = 0x83, /* One-Shot Control */
|
|
||||||
SystemContextMenu = 0x84, /* One-Shot Control */
|
|
||||||
SystemMainMenu = 0x85, /* One-Shot Control */
|
|
||||||
SystemAppMenu = 0x86, /* One-Shot Control */
|
|
||||||
SystemMenuHelp = 0x87, /* One-Shot Control */
|
|
||||||
SystemMenuExit = 0x88, /* One-Shot Control */
|
|
||||||
SystemMenu = 0x89, /* Selector */
|
|
||||||
SystemMenuRight = 0x8A, /* Re-Trigger Control */
|
|
||||||
SystemMenuLeft = 0x8B, /* Re-Trigger Control */
|
|
||||||
SystemMenuUp = 0x8C, /* Re-Trigger Control */
|
|
||||||
SystemMenuDown = 0x8D, /* Re-Trigger Control */
|
|
||||||
/* 0x8E - 0x8F Reserved */
|
|
||||||
DPadUp = 0x90, /* On/Off Control */
|
|
||||||
DPadDown = 0x91, /* On/Off Control */
|
|
||||||
DPadRight = 0x92, /* On/Off Control */
|
|
||||||
DPadLeft = 0x93, /* On/Off Control */
|
|
||||||
/* 0x94 - 0xFFFF Reserved */
|
|
||||||
Reserved = 0xFFFF
|
|
||||||
}
|
|
||||||
|
|
||||||
enum HIDUsageSim
|
|
||||||
{
|
|
||||||
FlightSimulationDevice = 0x01, /* Application Collection */
|
|
||||||
AutomobileSimulationDevice = 0x02, /* Application Collection */
|
|
||||||
TankSimulationDevice = 0x03, /* Application Collection */
|
|
||||||
SpaceshipSimulationDevice = 0x04, /* Application Collection */
|
|
||||||
SubmarineSimulationDevice = 0x05, /* Application Collection */
|
|
||||||
SailingSimulationDevice = 0x06, /* Application Collection */
|
|
||||||
MotorcycleSimulationDevice = 0x07, /* Application Collection */
|
|
||||||
SportsSimulationDevice = 0x08, /* Application Collection */
|
|
||||||
AirplaneSimulationDevice = 0x09, /* Application Collection */
|
|
||||||
HelicopterSimulationDevice = 0x0A, /* Application Collection */
|
|
||||||
MagicCarpetSimulationDevice = 0x0B, /* Application Collection */
|
|
||||||
BicycleSimulationDevice = 0x0C, /* Application Collection */
|
|
||||||
/* 0x0D - 0x1F Reserved */
|
|
||||||
FlightControlStick = 0x20, /* Application Collection */
|
|
||||||
FlightStick = 0x21, /* Application Collection */
|
|
||||||
CyclicControl = 0x22, /* Physical Collection */
|
|
||||||
CyclicTrim = 0x23, /* Physical Collection */
|
|
||||||
FlightYoke = 0x24, /* Application Collection */
|
|
||||||
TrackControl = 0x25, /* Physical Collection */
|
|
||||||
/* 0x26 - 0xAF Reserved */
|
|
||||||
Aileron = 0xB0, /* Dynamic Value */
|
|
||||||
AileronTrim = 0xB1, /* Dynamic Value */
|
|
||||||
AntiTorqueControl = 0xB2, /* Dynamic Value */
|
|
||||||
AutopilotEnable = 0xB3, /* On/Off Control */
|
|
||||||
ChaffRelease = 0xB4, /* One-Shot Control */
|
|
||||||
CollectiveControl = 0xB5, /* Dynamic Value */
|
|
||||||
DiveBrake = 0xB6, /* Dynamic Value */
|
|
||||||
ElectronicCountermeasures = 0xB7, /* On/Off Control */
|
|
||||||
Elevator = 0xB8, /* Dynamic Value */
|
|
||||||
ElevatorTrim = 0xB9, /* Dynamic Value */
|
|
||||||
Rudder = 0xBA, /* Dynamic Value */
|
|
||||||
Throttle = 0xBB, /* Dynamic Value */
|
|
||||||
FlightCommunications = 0xBC, /* On/Off Control */
|
|
||||||
FlareRelease = 0xBD, /* One-Shot Control */
|
|
||||||
LandingGear = 0xBE, /* On/Off Control */
|
|
||||||
ToeBrake = 0xBF, /* Dynamic Value */
|
|
||||||
Trigger = 0xC0, /* Momentary Control */
|
|
||||||
WeaponsArm = 0xC1, /* On/Off Control */
|
|
||||||
Weapons = 0xC2, /* Selector */
|
|
||||||
WingFlaps = 0xC3, /* Dynamic Value */
|
|
||||||
Accelerator = 0xC4, /* Dynamic Value */
|
|
||||||
Brake = 0xC5, /* Dynamic Value */
|
|
||||||
Clutch = 0xC6, /* Dynamic Value */
|
|
||||||
Shifter = 0xC7, /* Dynamic Value */
|
|
||||||
Steering = 0xC8, /* Dynamic Value */
|
|
||||||
TurretDirection = 0xC9, /* Dynamic Value */
|
|
||||||
BarrelElevation = 0xCA, /* Dynamic Value */
|
|
||||||
DivePlane = 0xCB, /* Dynamic Value */
|
|
||||||
Ballast = 0xCC, /* Dynamic Value */
|
|
||||||
BicycleCrank = 0xCD, /* Dynamic Value */
|
|
||||||
HandleBars = 0xCE, /* Dynamic Value */
|
|
||||||
FrontBrake = 0xCF, /* Dynamic Value */
|
|
||||||
RearBrake = 0xD0, /* Dynamic Value */
|
|
||||||
/* 0xD1 - 0xFFFF Reserved */
|
|
||||||
Reserved = 0xFFFF
|
|
||||||
}
|
|
||||||
|
|
||||||
enum HIDButton
|
enum HIDButton
|
||||||
{
|
{
|
||||||
Button_1 = 0x01, /* (primary/trigger) */
|
Button_1 = 0x01, /* (primary/trigger) */
|
||||||
|
|
|
@ -108,7 +108,12 @@ namespace OpenTK.Platform
|
||||||
// Todo: if SDL2 GameController config is ever updated to
|
// Todo: if SDL2 GameController config is ever updated to
|
||||||
// distinguish between negative/positive axes, then update
|
// distinguish between negative/positive axes, then update
|
||||||
// the following line to support both.
|
// the following line to support both.
|
||||||
pad.SetAxis(map.Target.Axis, pressed ? short.MaxValue : (short)0);
|
short value = pressed ?
|
||||||
|
short.MaxValue :
|
||||||
|
(map.Target.Axis & (GamePadAxes.LeftTrigger | GamePadAxes.RightTrigger)) != 0 ?
|
||||||
|
short.MinValue :
|
||||||
|
(short)0;
|
||||||
|
pad.SetAxis(map.Target.Axis, value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigurationType.Button:
|
case ConfigurationType.Button:
|
||||||
|
@ -150,7 +155,12 @@ namespace OpenTK.Platform
|
||||||
// Todo: if SDL2 GameController config is ever updated to
|
// Todo: if SDL2 GameController config is ever updated to
|
||||||
// distinguish between negative/positive axes, then update
|
// distinguish between negative/positive axes, then update
|
||||||
// the following line to support both.
|
// the following line to support both.
|
||||||
pad.SetAxis(map.Target.Axis, pressed ? short.MaxValue : (short)0);
|
short value = pressed ?
|
||||||
|
short.MaxValue :
|
||||||
|
(map.Target.Axis & (GamePadAxes.LeftTrigger | GamePadAxes.RightTrigger)) != 0 ?
|
||||||
|
short.MinValue :
|
||||||
|
(short)0;
|
||||||
|
pad.SetAxis(map.Target.Axis, value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigurationType.Button:
|
case ConfigurationType.Button:
|
||||||
|
@ -194,7 +204,8 @@ namespace OpenTK.Platform
|
||||||
GamePadType.GamePad, // Todo: detect different types
|
GamePadType.GamePad, // Todo: detect different types
|
||||||
mapped_axes,
|
mapped_axes,
|
||||||
mapped_buttons,
|
mapped_buttons,
|
||||||
true);
|
joy.IsConnected,
|
||||||
|
configuration.Name != GamePadConfigurationDatabase.UnmappedName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1542,6 +1542,16 @@ namespace OpenTK.Platform.SDL2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Guid(byte[]) constructor swaps the first 4+2+2 bytes.
|
||||||
|
// Compensate for that, otherwise we will not be able to match
|
||||||
|
// the Guids in the configuration database.
|
||||||
|
if (BitConverter.IsLittleEndian)
|
||||||
|
{
|
||||||
|
Array.Reverse(data, 0, 4);
|
||||||
|
Array.Reverse(data, 4, 2);
|
||||||
|
Array.Reverse(data, 6, 2);
|
||||||
|
}
|
||||||
|
|
||||||
return new Guid(data);
|
return new Guid(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,33 @@
|
||||||
#region --- License ---
|
#region License
|
||||||
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
|
//
|
||||||
* Contributions from Erik Ylvisaker
|
// WinRawJoystick.cs
|
||||||
* See license.txt for license info
|
//
|
||||||
*/
|
// Author:
|
||||||
|
// Stefanos A. <stapostol@gmail.com>
|
||||||
|
//
|
||||||
|
// Copyright (c) 2006 Stefanos Apostolopoulos
|
||||||
|
// Copyright (c) 2007 Erik Ylvisaker
|
||||||
|
//
|
||||||
|
// 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
|
#endregion
|
||||||
|
|
||||||
#region --- Using Directives ---
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
#if !MINIMAL
|
#if !MINIMAL
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
@ -14,8 +35,7 @@ using System.Drawing;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
|
using OpenTK.Platform.Common;
|
||||||
#endregion
|
|
||||||
|
|
||||||
/* TODO: Update the description of TimeBeginPeriod and other native methods. Update Timer. */
|
/* TODO: Update the description of TimeBeginPeriod and other native methods. Update Timer. */
|
||||||
|
|
||||||
|
@ -74,6 +94,7 @@ namespace OpenTK.Platform.Windows
|
||||||
using TIMERPROC = Functions.TimerProc;
|
using TIMERPROC = Functions.TimerProc;
|
||||||
|
|
||||||
using REGSAM = System.UInt32;
|
using REGSAM = System.UInt32;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -1480,16 +1501,6 @@ namespace OpenTK.Platform.Windows
|
||||||
/// <para>If Data is not large enough for the data, the function returns -1. If Data is NULL, the function returns a value of zero. In both of these cases, Size is set to the minimum size required for the Data buffer.</para>
|
/// <para>If Data is not large enough for the data, the function returns -1. If Data is NULL, the function returns a value of zero. In both of these cases, Size is set to the minimum size required for the Data buffer.</para>
|
||||||
/// <para>Call GetLastError to identify any other errors.</para>
|
/// <para>Call GetLastError to identify any other errors.</para>
|
||||||
/// </returns>
|
/// </returns>
|
||||||
[CLSCompliant(false)]
|
|
||||||
[System.Security.SuppressUnmanagedCodeSecurity]
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
|
||||||
internal static extern UINT GetRawInputDeviceInfo(
|
|
||||||
HANDLE Device,
|
|
||||||
[MarshalAs(UnmanagedType.U4)] RawInputDeviceInfoEnum Command,
|
|
||||||
[In, Out] LPVOID Data,
|
|
||||||
[In, Out] ref UINT Size
|
|
||||||
);
|
|
||||||
|
|
||||||
[System.Security.SuppressUnmanagedCodeSecurity]
|
[System.Security.SuppressUnmanagedCodeSecurity]
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
internal static extern INT GetRawInputDeviceInfo(
|
internal static extern INT GetRawInputDeviceInfo(
|
||||||
|
@ -1499,6 +1510,16 @@ namespace OpenTK.Platform.Windows
|
||||||
[In, Out] ref INT Size
|
[In, Out] ref INT Size
|
||||||
);
|
);
|
||||||
|
|
||||||
|
[CLSCompliant(false)]
|
||||||
|
[System.Security.SuppressUnmanagedCodeSecurity]
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
internal static extern INT GetRawInputDeviceInfo(
|
||||||
|
HANDLE Device,
|
||||||
|
[MarshalAs(UnmanagedType.U4)] RawInputDeviceInfoEnum Command,
|
||||||
|
[In, Out] byte[] Data,
|
||||||
|
[In, Out] ref INT Size
|
||||||
|
);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets information about the raw input device.
|
/// Gets information about the raw input device.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1626,6 +1647,52 @@ namespace OpenTK.Platform.Windows
|
||||||
INT SizeHeader
|
INT SizeHeader
|
||||||
);
|
);
|
||||||
|
|
||||||
|
internal static int GetRawInputData(IntPtr raw, out RawInputHeader header)
|
||||||
|
{
|
||||||
|
int size = RawInputHeader.SizeInBytes;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (RawInputHeader* pheader = &header)
|
||||||
|
{
|
||||||
|
if (GetRawInputData(raw, GetRawInputDataEnum.HEADER,
|
||||||
|
(IntPtr)pheader, ref size, API.RawInputHeaderSize) != RawInputHeader.SizeInBytes)
|
||||||
|
{
|
||||||
|
Debug.Print("[Error] Failed to retrieve raw input header. Error: {0}",
|
||||||
|
Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static int GetRawInputData(IntPtr raw, out RawInput data)
|
||||||
|
{
|
||||||
|
int size = RawInput.SizeInBytes;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (RawInput* pdata = &data)
|
||||||
|
{
|
||||||
|
GetRawInputData(raw, GetRawInputDataEnum.INPUT,
|
||||||
|
(IntPtr)pdata, ref size, API.RawInputHeaderSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static int GetRawInputData(IntPtr raw, byte[] data)
|
||||||
|
{
|
||||||
|
int size = data.Length;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* pdata = data)
|
||||||
|
{
|
||||||
|
GetRawInputData(raw, GetRawInputDataEnum.INPUT,
|
||||||
|
(IntPtr)pdata, ref size, API.RawInputHeaderSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IntPtr NextRawInputStructure(IntPtr data)
|
#region IntPtr NextRawInputStructure(IntPtr data)
|
||||||
|
@ -2424,8 +2491,7 @@ namespace OpenTK.Platform.Windows
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Top level collection Usage page for the raw input device.
|
/// Top level collection Usage page for the raw input device.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
//internal USHORT UsagePage;
|
internal HIDPage UsagePage;
|
||||||
internal SHORT UsagePage;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Top level collection Usage for the raw input device.
|
/// Top level collection Usage for the raw input device.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2443,6 +2509,22 @@ namespace OpenTK.Platform.Windows
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal HWND Target;
|
internal HWND Target;
|
||||||
|
|
||||||
|
public RawInputDevice(HIDUsageGD usage, RawInputDeviceFlags flags, HWND target)
|
||||||
|
{
|
||||||
|
UsagePage = HIDPage.GenericDesktop;
|
||||||
|
Usage = (short)usage;
|
||||||
|
Flags = flags;
|
||||||
|
Target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RawInputDevice(HIDUsageSim usage, RawInputDeviceFlags flags, HWND target)
|
||||||
|
{
|
||||||
|
UsagePage = HIDPage.Simulation;
|
||||||
|
Usage = (short)usage;
|
||||||
|
Flags = flags;
|
||||||
|
Target = target;
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return String.Format("{0}/{1}, flags: {2}, window: {3}", UsagePage, Usage, Flags, Target);
|
return String.Format("{0}/{1}, flags: {2}, window: {3}", UsagePage, Usage, Flags, Target);
|
||||||
|
@ -2494,6 +2576,9 @@ namespace OpenTK.Platform.Windows
|
||||||
{
|
{
|
||||||
public RawInputHeader Header;
|
public RawInputHeader Header;
|
||||||
public RawInputData Data;
|
public RawInputData Data;
|
||||||
|
|
||||||
|
public static readonly int SizeInBytes =
|
||||||
|
BlittableValueType<RawInput>.Stride;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
@ -2505,6 +2590,9 @@ namespace OpenTK.Platform.Windows
|
||||||
internal RawKeyboard Keyboard;
|
internal RawKeyboard Keyboard;
|
||||||
[FieldOffset(0)]
|
[FieldOffset(0)]
|
||||||
internal RawHID HID;
|
internal RawHID HID;
|
||||||
|
|
||||||
|
public static readonly int SizeInBytes =
|
||||||
|
BlittableValueType<RawInputData>.Stride;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -2537,6 +2625,9 @@ namespace OpenTK.Platform.Windows
|
||||||
/// Value passed in the wParam parameter of the WM_INPUT message.
|
/// Value passed in the wParam parameter of the WM_INPUT message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal WPARAM Param;
|
internal WPARAM Param;
|
||||||
|
|
||||||
|
public static readonly int SizeInBytes =
|
||||||
|
BlittableValueType<RawInputHeader>.Stride;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -2715,16 +2806,31 @@ namespace OpenTK.Platform.Windows
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Size, in bytes, of each HID input in bRawData.
|
/// Size, in bytes, of each HID input in bRawData.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal DWORD SizeHid;
|
internal DWORD Size;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of HID inputs in bRawData.
|
/// Number of HID inputs in bRawData.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal DWORD Count;
|
internal DWORD Count;
|
||||||
// The RawData field must be marshalled manually.
|
/// <summary>
|
||||||
///// <summary>
|
/// Raw input data as an array of bytes.
|
||||||
///// Raw input data as an array of bytes.
|
/// </summary>
|
||||||
///// </summary>
|
internal byte RawData;
|
||||||
//internal IntPtr RawData;
|
|
||||||
|
internal byte this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (index < 0 || index > Size * Count)
|
||||||
|
throw new ArgumentOutOfRangeException("index");
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* data = &RawData)
|
||||||
|
{
|
||||||
|
return *(data + index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -2741,7 +2847,7 @@ namespace OpenTK.Platform.Windows
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Size, in bytes, of the RawInputDeviceInfo structure.
|
/// Size, in bytes, of the RawInputDeviceInfo structure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo));
|
internal DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo));
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Type of raw input data.
|
/// Type of raw input data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
288
Source/OpenTK/Platform/Windows/Bindings/HidProtocol.cs
Normal file
288
Source/OpenTK/Platform/Windows/Bindings/HidProtocol.cs
Normal file
|
@ -0,0 +1,288 @@
|
||||||
|
#region License
|
||||||
|
//
|
||||||
|
// HidProtocol.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.Runtime.InteropServices;
|
||||||
|
using System.Security;
|
||||||
|
using System.Text;
|
||||||
|
using OpenTK.Platform.Common;
|
||||||
|
|
||||||
|
namespace OpenTK.Platform.Windows
|
||||||
|
{
|
||||||
|
class HidProtocol
|
||||||
|
{
|
||||||
|
const string lib = "hid.dll";
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetButtonCaps")]
|
||||||
|
public static extern HidProtocolStatus GetButtonCaps(HidProtocolReportType hidProtocolReportType,
|
||||||
|
IntPtr button_caps, ref ushort p, [In] byte[] preparsed_data);
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetButtonCaps")]
|
||||||
|
public static extern HidProtocolStatus GetButtonCaps(HidProtocolReportType hidProtocolReportType,
|
||||||
|
[Out] HidProtocolButtonCaps[] button_caps, ref ushort p, [In] byte[] preparsed_data);
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetCaps")]
|
||||||
|
public static extern HidProtocolStatus GetCaps([In] 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,
|
||||||
|
[In] byte[] preparsed_data, IntPtr report, int report_length);
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetData")]
|
||||||
|
public static extern HidProtocolStatus GetData(HidProtocolReportType type,
|
||||||
|
[Out] HidProtocolData[] data, ref int data_length,
|
||||||
|
[In] byte[] preparsed_data, IntPtr report, int report_length);
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetLinkCollectionNodes")]
|
||||||
|
public static extern HidProtocolStatus GetLinkCollectionNodes(
|
||||||
|
[Out] HidProtocolLinkCollectionNode[] LinkCollectionNodes,
|
||||||
|
ref int LinkCollectionNodesLength,
|
||||||
|
[In] byte[] PreparsedData);
|
||||||
|
|
||||||
|
[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,
|
||||||
|
[In] byte[] preparsed_data, IntPtr report, int report_length);
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetSpecificButtonCaps")]
|
||||||
|
public static extern HidProtocolStatus GetSpecificButtonCaps(HidProtocolReportType ReportType,
|
||||||
|
HIDPage UsagePage, ushort LinkCollection, ushort Usage,
|
||||||
|
[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,
|
||||||
|
HIDPage usage_page, short link_collection, short usage, ref uint usage_value,
|
||||||
|
[In] 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, [In] byte[] preparsed_data);
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_GetValueCaps")]
|
||||||
|
public static extern HidProtocolStatus GetValueCaps(HidProtocolReportType type,
|
||||||
|
[Out] HidProtocolValueCaps[] caps, ref ushort caps_length, [In] byte[] preparsed_data);
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
[DllImport(lib, SetLastError = true, EntryPoint = "HidP_MaxDataListLength")]
|
||||||
|
public static extern int MaxDataListLength(HidProtocolReportType type, [In] byte[] preparsed_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum HidProtocolCollectionType : byte
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum HidProtocolReportType : ushort
|
||||||
|
{
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
Feature
|
||||||
|
}
|
||||||
|
|
||||||
|
enum HidProtocolStatus : uint
|
||||||
|
{
|
||||||
|
Success = 0x00110000,
|
||||||
|
Null = 0x80110001,
|
||||||
|
InvalidPreparsedData = 0xc0110001,
|
||||||
|
InvalidReportType = 0xc0110002,
|
||||||
|
InvalidReportLength = 0xc0110003,
|
||||||
|
UsageNotFound = 0xc0110004,
|
||||||
|
ValueOutOfRange = 0xc0110005,
|
||||||
|
BadLogPhyValues = 0xc0110006,
|
||||||
|
BufferTooSmall = 0xc0110007,
|
||||||
|
InternallError = 0xc0110008,
|
||||||
|
I8042TransNotKnown = 0xc0110009,
|
||||||
|
IncompatibleReportId = 0xc011000a,
|
||||||
|
NotValueArray = 0xc011000b,
|
||||||
|
IsValueArray = 0xc011000c,
|
||||||
|
DataIndexNotFound = 0xc011000d,
|
||||||
|
DataIndexOutOfRange = 0xc011000e,
|
||||||
|
ButtonNotPressed = 0xc011000f,
|
||||||
|
ReportDoesNotExist = 0xc0110010,
|
||||||
|
NotImplemented = 0xc0110020,
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
struct HidProtocolButtonCaps
|
||||||
|
{
|
||||||
|
[FieldOffset(0)] public HIDPage UsagePage;
|
||||||
|
[FieldOffset(2)] public byte ReportID;
|
||||||
|
[FieldOffset(3), MarshalAs(UnmanagedType.U1)] public bool IsAlias;
|
||||||
|
[FieldOffset(4)] public short BitField;
|
||||||
|
[FieldOffset(6)] public short LinkCollection;
|
||||||
|
[FieldOffset(8)] public short LinkUsage;
|
||||||
|
[FieldOffset(10)] public short LinkUsagePage;
|
||||||
|
[FieldOffset(12), MarshalAs(UnmanagedType.U1)] public bool IsRange;
|
||||||
|
[FieldOffset(13), MarshalAs(UnmanagedType.U1)] public bool IsStringRange;
|
||||||
|
[FieldOffset(14), MarshalAs(UnmanagedType.U1)] public bool IsDesignatorRange;
|
||||||
|
[FieldOffset(15), MarshalAs(UnmanagedType.U1)] public bool IsAbsolute;
|
||||||
|
//[FieldOffset(16)] unsafe fixed int Reserved[10]; // no need when LayoutKind.Explicit
|
||||||
|
[FieldOffset(56)] public HidProtocolRange Range;
|
||||||
|
[FieldOffset(56)] public HidProtocolNotRange NotRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct HidProtocolCaps
|
||||||
|
{
|
||||||
|
public short Usage;
|
||||||
|
public short UsagePage;
|
||||||
|
public ushort InputReportByteLength;
|
||||||
|
public ushort OutputReportByteLength;
|
||||||
|
public ushort FeatureReportByteLength;
|
||||||
|
unsafe fixed ushort Reserved[17];
|
||||||
|
public ushort NumberLinkCollectionNodes;
|
||||||
|
public ushort NumberInputButtonCaps;
|
||||||
|
public ushort NumberInputValueCaps;
|
||||||
|
public ushort NumberInputDataIndices;
|
||||||
|
public ushort NumberOutputButtonCaps;
|
||||||
|
public ushort NumberOutputValueCaps;
|
||||||
|
public ushort NumberOutputDataIndices;
|
||||||
|
public ushort NumberFeatureButtonCaps;
|
||||||
|
public ushort NumberFeatureValueCaps;
|
||||||
|
public ushort NumberFeatureDataIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
struct HidProtocolData
|
||||||
|
{
|
||||||
|
[FieldOffset(0)] public short DataIndex;
|
||||||
|
//[FieldOffset(2)] public short Reserved;
|
||||||
|
[FieldOffset(4)] public int RawValue;
|
||||||
|
[FieldOffset(4), MarshalAs(UnmanagedType.U1)] public bool On;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct HidProtocolNotRange
|
||||||
|
{
|
||||||
|
public short Usage;
|
||||||
|
short Reserved1;
|
||||||
|
public short StringIndex;
|
||||||
|
short Reserved2;
|
||||||
|
public short DesignatorIndex;
|
||||||
|
short Reserved3;
|
||||||
|
public short DataIndex;
|
||||||
|
short Reserved4;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
struct HidProtocolLinkCollectionNode
|
||||||
|
{
|
||||||
|
public ushort LinkUsage;
|
||||||
|
public HIDPage LinkUsagePage;
|
||||||
|
public ushort Parent;
|
||||||
|
public ushort NumberOfChildren;
|
||||||
|
public ushort NextSibling;
|
||||||
|
public ushort FirstChild;
|
||||||
|
int bitfield;
|
||||||
|
public IntPtr UserContext;
|
||||||
|
|
||||||
|
public HidProtocolCollectionType CollectionType
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (HidProtocolCollectionType)(bitfield & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsAlias
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (bitfield & 0x100) == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HidProtocolRange
|
||||||
|
{
|
||||||
|
#pragma warning disable 0649
|
||||||
|
public short UsageMin;
|
||||||
|
public short UsageMax;
|
||||||
|
public short StringMin;
|
||||||
|
public short StringMax;
|
||||||
|
public short DesignatorMin;
|
||||||
|
public short DesignatorMax;
|
||||||
|
public short DataIndexMin;
|
||||||
|
public short DataIndexMax;
|
||||||
|
#pragma warning restore 0649
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
struct HidProtocolValueCaps
|
||||||
|
{
|
||||||
|
[FieldOffset(0)] public HIDPage UsagePage;
|
||||||
|
[FieldOffset(2)] public byte ReportID;
|
||||||
|
[FieldOffset(3), MarshalAs(UnmanagedType.U1)] public bool IsAlias;
|
||||||
|
[FieldOffset(4)] public ushort BitField;
|
||||||
|
[FieldOffset(6)] public short LinkCollection;
|
||||||
|
[FieldOffset(8)] public ushort LinkUsage;
|
||||||
|
[FieldOffset(10)] public ushort LinkUsagePage;
|
||||||
|
[FieldOffset(12), MarshalAs(UnmanagedType.U1)] public bool IsRange;
|
||||||
|
[FieldOffset(13), MarshalAs(UnmanagedType.U1)] public bool IsStringRange;
|
||||||
|
[FieldOffset(14), MarshalAs(UnmanagedType.U1)] public bool IsDesignatorRange;
|
||||||
|
[FieldOffset(15), MarshalAs(UnmanagedType.U1)] public bool IsAbsolute;
|
||||||
|
[FieldOffset(16), MarshalAs(UnmanagedType.U1)] public bool HasNull;
|
||||||
|
[FieldOffset(17)] byte Reserved;
|
||||||
|
[FieldOffset(18)] public short BitSize;
|
||||||
|
[FieldOffset(20)] public short ReportCount;
|
||||||
|
//[FieldOffset(22)] ushort Reserved2a;
|
||||||
|
//[FieldOffset(24)] ushort Reserved2b;
|
||||||
|
//[FieldOffset(26)] ushort Reserved2c;
|
||||||
|
//[FieldOffset(28)] ushort Reserved2d;
|
||||||
|
//[FieldOffset(30)] ushort Reserved2e;
|
||||||
|
[FieldOffset(32)] public int UnitsExp;
|
||||||
|
[FieldOffset(36)] public int Units;
|
||||||
|
[FieldOffset(40)] public int LogicalMin;
|
||||||
|
[FieldOffset(44)] public int LogicalMax;
|
||||||
|
[FieldOffset(48)] public int PhysicalMin;
|
||||||
|
[FieldOffset(52)] public int PhysicalMax;
|
||||||
|
[FieldOffset(56)] public HidProtocolRange Range;
|
||||||
|
[FieldOffset(56)] public HidProtocolNotRange NotRange;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,7 +40,11 @@ namespace OpenTK.Platform.Windows
|
||||||
class WinFactory : PlatformFactoryBase
|
class WinFactory : PlatformFactoryBase
|
||||||
{
|
{
|
||||||
readonly object SyncRoot = new object();
|
readonly object SyncRoot = new object();
|
||||||
IInputDriver2 inputDriver;
|
|
||||||
|
// The input drivers must be constructed lazily, *after* the
|
||||||
|
// WinFactory constructor has finished running. The reason is
|
||||||
|
// that they call WinFactory methods internally.
|
||||||
|
WinRawInput rawinput_driver; // For keyboard and mouse input
|
||||||
|
|
||||||
internal static IntPtr OpenGLHandle { get; private set; }
|
internal static IntPtr OpenGLHandle { get; private set; }
|
||||||
const string OpenGLName = "OPENGL32.DLL";
|
const string OpenGLName = "OPENGL32.DLL";
|
||||||
|
@ -112,41 +116,45 @@ namespace OpenTK.Platform.Windows
|
||||||
|
|
||||||
public override OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver()
|
public override OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver()
|
||||||
{
|
{
|
||||||
return InputDriver.KeyboardDriver;
|
return RawInputDriver.KeyboardDriver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override OpenTK.Input.IMouseDriver2 CreateMouseDriver()
|
public override OpenTK.Input.IMouseDriver2 CreateMouseDriver()
|
||||||
{
|
{
|
||||||
return InputDriver.MouseDriver;
|
return RawInputDriver.MouseDriver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override OpenTK.Input.IGamePadDriver CreateGamePadDriver()
|
public override OpenTK.Input.IGamePadDriver CreateGamePadDriver()
|
||||||
{
|
{
|
||||||
return InputDriver.GamePadDriver;
|
return new MappedGamePadDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IJoystickDriver2 CreateJoystickDriver()
|
public override IJoystickDriver2 CreateJoystickDriver()
|
||||||
{
|
{
|
||||||
return InputDriver.JoystickDriver;
|
return RawInputDriver.JoystickDriver;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
IInputDriver2 InputDriver
|
#region Private Members
|
||||||
|
|
||||||
|
WinRawInput RawInputDriver
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
lock (SyncRoot)
|
||||||
{
|
{
|
||||||
if (inputDriver == null)
|
if (rawinput_driver == null)
|
||||||
{
|
{
|
||||||
inputDriver = new WinRawInput();
|
rawinput_driver = new WinRawInput();
|
||||||
}
|
}
|
||||||
return inputDriver;
|
return rawinput_driver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region IDisposable Members
|
#region IDisposable Members
|
||||||
|
|
||||||
protected override void Dispose(bool manual)
|
protected override void Dispose(bool manual)
|
||||||
|
@ -155,7 +163,7 @@ namespace OpenTK.Platform.Windows
|
||||||
{
|
{
|
||||||
if (manual)
|
if (manual)
|
||||||
{
|
{
|
||||||
InputDriver.Dispose();
|
rawinput_driver.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(manual);
|
base.Dispose(manual);
|
||||||
|
|
|
@ -83,7 +83,7 @@ namespace OpenTK.Platform.Windows
|
||||||
const ClassStyle DefaultClassStyle = ClassStyle.OwnDC;
|
const ClassStyle DefaultClassStyle = ClassStyle.OwnDC;
|
||||||
|
|
||||||
const long ExtendedBit = 1 << 24; // Used to distinguish left and right control, alt and enter keys.
|
const long ExtendedBit = 1 << 24; // Used to distinguish left and right control, alt and enter keys.
|
||||||
|
|
||||||
public static readonly uint ShiftLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LSHIFT, 0);
|
public static readonly uint ShiftLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LSHIFT, 0);
|
||||||
public static readonly uint ShiftRightScanCode = Functions.MapVirtualKey(VirtualKeys.RSHIFT, 0);
|
public static readonly uint ShiftRightScanCode = Functions.MapVirtualKey(VirtualKeys.RSHIFT, 0);
|
||||||
public static readonly uint ControlLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LCONTROL, 0);
|
public static readonly uint ControlLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LCONTROL, 0);
|
||||||
|
|
|
@ -33,7 +33,7 @@ using OpenTK.Input;
|
||||||
|
|
||||||
namespace OpenTK.Platform.Windows
|
namespace OpenTK.Platform.Windows
|
||||||
{
|
{
|
||||||
abstract class WinInputBase : IInputDriver2
|
abstract class WinInputBase
|
||||||
{
|
{
|
||||||
#region Fields
|
#region Fields
|
||||||
|
|
||||||
|
@ -158,11 +158,11 @@ namespace OpenTK.Platform.Windows
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IInputDriver2 Members
|
#region Public Members
|
||||||
|
|
||||||
public abstract IMouseDriver2 MouseDriver { get; }
|
public abstract IMouseDriver2 MouseDriver { get; }
|
||||||
|
|
||||||
public abstract IKeyboardDriver2 KeyboardDriver { get; }
|
public abstract IKeyboardDriver2 KeyboardDriver { get; }
|
||||||
public abstract IGamePadDriver GamePadDriver { get; }
|
|
||||||
|
|
||||||
public abstract IJoystickDriver2 JoystickDriver { get; }
|
public abstract IJoystickDriver2 JoystickDriver { get; }
|
||||||
|
|
||||||
|
|
|
@ -1,622 +0,0 @@
|
||||||
#region License
|
|
||||||
//
|
|
||||||
// The Open Toolkit Library License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2006 - 2008 the Open Toolkit library, except where noted.
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Security;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
using OpenTK.Input;
|
|
||||||
|
|
||||||
namespace OpenTK.Platform.Windows
|
|
||||||
{
|
|
||||||
sealed class WinMMJoystick : IJoystickDriver2
|
|
||||||
{
|
|
||||||
#region Fields
|
|
||||||
|
|
||||||
readonly object sync = new object();
|
|
||||||
|
|
||||||
List<JoystickDevice> sticks = new List<JoystickDevice>();
|
|
||||||
|
|
||||||
// Matches a WinMM device index to a specific stick above
|
|
||||||
readonly Dictionary<int, int> index_to_stick =
|
|
||||||
new Dictionary<int, int>();
|
|
||||||
// Matches a player index to a WinMM device index
|
|
||||||
readonly Dictionary<int, int> player_to_index =
|
|
||||||
new Dictionary<int, int>();
|
|
||||||
|
|
||||||
// Todo: Read the joystick name from the registry.
|
|
||||||
//static readonly string RegistryJoyConfig = @"Joystick%dConfiguration";
|
|
||||||
//static readonly string RegistryJoyName = @"Joystick%dOEMName";
|
|
||||||
//static readonly string RegstryJoyCurrent = @"CurrentJoystickSettings";
|
|
||||||
|
|
||||||
bool disposed;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public WinMMJoystick()
|
|
||||||
{
|
|
||||||
RefreshDevices();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Internal Members
|
|
||||||
|
|
||||||
internal void RefreshDevices()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < sticks.Count; i++)
|
|
||||||
{
|
|
||||||
CloseJoystick(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WinMM supports up to 16 joysticks.
|
|
||||||
for (int i = 0; i < UnsafeNativeMethods.joyGetNumDevs(); i++)
|
|
||||||
{
|
|
||||||
OpenJoystick(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Members
|
|
||||||
|
|
||||||
JoystickDevice<WinMMJoyDetails> OpenJoystick(int number)
|
|
||||||
{
|
|
||||||
lock (sync)
|
|
||||||
{
|
|
||||||
JoystickDevice<WinMMJoyDetails> stick = null;
|
|
||||||
|
|
||||||
JoyCaps caps;
|
|
||||||
JoystickError result = UnsafeNativeMethods.joyGetDevCaps(number, out caps, JoyCaps.SizeInBytes);
|
|
||||||
if (result == JoystickError.NoError)
|
|
||||||
{
|
|
||||||
if (caps.NumAxes > JoystickState.MaxAxes)
|
|
||||||
{
|
|
||||||
Debug.Print("[Input] Device has {0} axes, which is higher than OpenTK maximum {1}. Please report a bug at http://www.opentk.com",
|
|
||||||
caps.NumAxes, JoystickState.MaxAxes);
|
|
||||||
caps.NumAxes = JoystickState.MaxAxes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caps.NumAxes > JoystickState.MaxButtons)
|
|
||||||
{
|
|
||||||
Debug.Print("[Input] Device has {0} buttons, which is higher than OpenTK maximum {1}. Please report a bug at http://www.opentk.com",
|
|
||||||
caps.NumButtons, JoystickState.MaxButtons);
|
|
||||||
caps.NumButtons = JoystickState.MaxButtons;
|
|
||||||
}
|
|
||||||
|
|
||||||
JoystickCapabilities joycaps = new JoystickCapabilities(
|
|
||||||
caps.NumAxes,
|
|
||||||
caps.NumButtons,
|
|
||||||
(caps.Capabilities & JoystCapsFlags.HasPov) != 0 ? 1 : 0,
|
|
||||||
true);
|
|
||||||
|
|
||||||
int num_axes = caps.NumAxes;
|
|
||||||
if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0)
|
|
||||||
num_axes += 2;
|
|
||||||
|
|
||||||
stick = new JoystickDevice<WinMMJoyDetails>(number, num_axes, caps.NumButtons);
|
|
||||||
stick.Details = new WinMMJoyDetails(joycaps);
|
|
||||||
|
|
||||||
// Make sure to reverse the vertical axes, so that +1 points up and -1 points down.
|
|
||||||
for (int axis = 0; axis < caps.NumAxes; axis++)
|
|
||||||
{
|
|
||||||
if (axis % 2 == 1)
|
|
||||||
{
|
|
||||||
stick.Details.Min[axis] = caps.GetMax(axis);
|
|
||||||
stick.Details.Max[axis] = caps.GetMin(axis);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stick.Details.Min[axis] = caps.GetMin(axis);
|
|
||||||
stick.Details.Max[axis] = caps.GetMax(axis);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0)
|
|
||||||
{
|
|
||||||
stick.Details.PovType = PovType.Exists;
|
|
||||||
if ((caps.Capabilities & JoystCapsFlags.HasPov4Dir) != 0)
|
|
||||||
stick.Details.PovType |= PovType.Discrete;
|
|
||||||
if ((caps.Capabilities & JoystCapsFlags.HasPovContinuous) != 0)
|
|
||||||
stick.Details.PovType |= PovType.Continuous;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Todo: Implement joystick name detection for WinMM.
|
|
||||||
stick.Description = String.Format("Joystick/Joystick #{0} ({1} axes, {2} buttons)", number, stick.Axis.Count, stick.Button.Count);
|
|
||||||
// Todo: Try to get the device name from the registry. Oh joy!
|
|
||||||
//string key_path = String.Format("{0}\\{1}\\{2}", RegistryJoyConfig, caps.RegKey, RegstryJoyCurrent);
|
|
||||||
//RegistryKey key = Registry.LocalMachine.OpenSubKey(key_path, false);
|
|
||||||
//if (key == null)
|
|
||||||
// key = Registry.CurrentUser.OpenSubKey(key_path, false);
|
|
||||||
//if (key == null)
|
|
||||||
// stick.Description = String.Format("USB Joystick {0} ({1} axes, {2} buttons)", number, stick.Axis.Count, stick.Button.Count);
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// key.Close();
|
|
||||||
//}
|
|
||||||
|
|
||||||
Debug.Print("Found joystick on device number {0}", number);
|
|
||||||
index_to_stick.Add(number, sticks.Count);
|
|
||||||
player_to_index.Add(player_to_index.Count, number);
|
|
||||||
sticks.Add(stick);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stick;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnplugJoystick(int player_index)
|
|
||||||
{
|
|
||||||
// Reset the system configuration. Without this,
|
|
||||||
// joysticks that are reconnected on different
|
|
||||||
// ports are given different ids, making it
|
|
||||||
// impossible to reconnect a disconnected user.
|
|
||||||
UnsafeNativeMethods.joyConfigChanged(0);
|
|
||||||
Debug.Print("[Win] WinMM joystick {0} unplugged", player_index);
|
|
||||||
CloseJoystick(player_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CloseJoystick(int player_index)
|
|
||||||
{
|
|
||||||
lock (sync)
|
|
||||||
{
|
|
||||||
if (IsValid(player_index))
|
|
||||||
{
|
|
||||||
int device_index = player_to_index[player_index];
|
|
||||||
|
|
||||||
JoystickDevice<WinMMJoyDetails> stick =
|
|
||||||
sticks[index_to_stick[device_index]] as JoystickDevice<WinMMJoyDetails>;
|
|
||||||
|
|
||||||
if (stick != null)
|
|
||||||
{
|
|
||||||
index_to_stick.Remove(device_index);
|
|
||||||
player_to_index.Remove(player_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsValid(int player_index)
|
|
||||||
{
|
|
||||||
if (player_to_index.ContainsKey(player_index))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//else if (index >= 0 && index < UnsafeNativeMethods.joyGetNumDevs())
|
|
||||||
//{
|
|
||||||
// return OpenJoystick(index) != null;
|
|
||||||
//}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static short CalculateOffset(int pos, int min, int max)
|
|
||||||
{
|
|
||||||
int offset = (ushort.MaxValue * (pos - min)) / (max - min) - short.MaxValue;
|
|
||||||
return (short)offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region IJoystickDriver2 Members
|
|
||||||
|
|
||||||
public JoystickCapabilities GetCapabilities(int player_index)
|
|
||||||
{
|
|
||||||
lock (sync)
|
|
||||||
{
|
|
||||||
if (IsValid(player_index))
|
|
||||||
{
|
|
||||||
int device_index = player_to_index[player_index];
|
|
||||||
JoystickDevice<WinMMJoyDetails> stick =
|
|
||||||
sticks[index_to_stick[device_index]] as JoystickDevice<WinMMJoyDetails>;
|
|
||||||
|
|
||||||
return stick.Details.Capabilities;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new JoystickCapabilities();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public JoystickState GetState(int player_index)
|
|
||||||
{
|
|
||||||
lock (sync)
|
|
||||||
{
|
|
||||||
JoystickState state = new JoystickState();
|
|
||||||
|
|
||||||
if (IsValid(player_index))
|
|
||||||
{
|
|
||||||
int device_index = player_to_index[player_index];
|
|
||||||
int index = index_to_stick[device_index];
|
|
||||||
JoystickDevice<WinMMJoyDetails> stick =
|
|
||||||
sticks[index] as JoystickDevice<WinMMJoyDetails>;
|
|
||||||
|
|
||||||
// For joysticks with fewer than three axes or four buttons, we must
|
|
||||||
// use joyGetPos; otherwise, joyGetPosEx. This is not just a cosmetic
|
|
||||||
// difference, simple devices will return incorrect results if we use
|
|
||||||
// joyGetPosEx on them.
|
|
||||||
if (stick.Details.Capabilities.AxisCount <= 3 || stick.Details.Capabilities.ButtonCount <= 4)
|
|
||||||
{
|
|
||||||
// Use joyGetPos
|
|
||||||
JoyInfo info = new JoyInfo();
|
|
||||||
|
|
||||||
JoystickError result = UnsafeNativeMethods.joyGetPos(device_index, ref info);
|
|
||||||
if (result == JoystickError.NoError)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < stick.Details.Capabilities.AxisCount; i++)
|
|
||||||
{
|
|
||||||
state.SetAxis(JoystickAxis.Axis0 + i, CalculateOffset(info.GetAxis(i), stick.Details.Min[i], stick.Details.Max[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < stick.Details.Capabilities.ButtonCount; i++)
|
|
||||||
{
|
|
||||||
state.SetButton(JoystickButton.Button0 + i, (info.Buttons & 1 << i) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.SetIsConnected(true);
|
|
||||||
}
|
|
||||||
else if (result == JoystickError.Unplugged)
|
|
||||||
{
|
|
||||||
UnplugJoystick(player_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Use joyGetPosEx
|
|
||||||
JoyInfoEx info_ex = new JoyInfoEx();
|
|
||||||
info_ex.Size = JoyInfoEx.SizeInBytes;
|
|
||||||
info_ex.Flags = JoystickFlags.All;
|
|
||||||
|
|
||||||
JoystickError result = UnsafeNativeMethods.joyGetPosEx(device_index, ref info_ex);
|
|
||||||
if (result == JoystickError.NoError)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < stick.Details.Capabilities.AxisCount; i++)
|
|
||||||
{
|
|
||||||
state.SetAxis(JoystickAxis.Axis0 + i, CalculateOffset(info_ex.GetAxis(i), stick.Details.Min[i], stick.Details.Max[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < stick.Details.Capabilities.ButtonCount; i++)
|
|
||||||
{
|
|
||||||
state.SetButton(JoystickButton.Button0 + i, (info_ex.Buttons & 1 << i) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < stick.Details.Capabilities.HatCount; i++)
|
|
||||||
{
|
|
||||||
// A discrete POV returns specific values for left, right, etc.
|
|
||||||
// A continuous POV returns an integer indicating an angle in degrees * 100, e.g. 18000 == 180.00 degrees.
|
|
||||||
// The vast majority of joysticks have discrete POVs, so we'll treat all of them as discrete for simplicity.
|
|
||||||
if ((JoystickPovPosition)info_ex.Pov != JoystickPovPosition.Centered)
|
|
||||||
{
|
|
||||||
HatPosition hatpos = HatPosition.Centered;
|
|
||||||
if (info_ex.Pov < 4500 || info_ex.Pov >= 31500)
|
|
||||||
hatpos |= HatPosition.Up;
|
|
||||||
if (info_ex.Pov >= 4500 && info_ex.Pov < 13500)
|
|
||||||
hatpos |= HatPosition.Right;
|
|
||||||
if (info_ex.Pov >= 13500 && info_ex.Pov < 22500)
|
|
||||||
hatpos |= HatPosition.Down;
|
|
||||||
if (info_ex.Pov >= 22500 && info_ex.Pov < 31500)
|
|
||||||
hatpos |= HatPosition.Left;
|
|
||||||
|
|
||||||
state.SetHat(JoystickHat.Hat0 + i, new JoystickHatState(hatpos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.SetIsConnected(true);
|
|
||||||
}
|
|
||||||
else if (result == JoystickError.Unplugged)
|
|
||||||
{
|
|
||||||
UnplugJoystick(player_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid GetGuid(int index)
|
|
||||||
{
|
|
||||||
lock (sync)
|
|
||||||
{
|
|
||||||
Guid guid = new Guid();
|
|
||||||
|
|
||||||
if (IsValid(index))
|
|
||||||
{
|
|
||||||
// Todo: implement WinMM Guid retrieval
|
|
||||||
}
|
|
||||||
|
|
||||||
return guid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region IDisposable
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dispose(bool manual)
|
|
||||||
{
|
|
||||||
if (!disposed)
|
|
||||||
{
|
|
||||||
if (manual)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~WinMMJoystick()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region UnsafeNativeMethods
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
enum JoystickFlags
|
|
||||||
{
|
|
||||||
X = 0x1,
|
|
||||||
Y = 0x2,
|
|
||||||
Z = 0x4,
|
|
||||||
R = 0x8,
|
|
||||||
U = 0x10,
|
|
||||||
V = 0x20,
|
|
||||||
Pov = 0x40,
|
|
||||||
Buttons = 0x80,
|
|
||||||
All = X | Y | Z | R | U | V | Pov | Buttons
|
|
||||||
}
|
|
||||||
|
|
||||||
enum JoystickError : uint
|
|
||||||
{
|
|
||||||
NoError = 0,
|
|
||||||
InvalidParameters = 165,
|
|
||||||
NoCanDo = 166,
|
|
||||||
Unplugged = 167
|
|
||||||
//MM_NoDriver = 6,
|
|
||||||
//MM_InvalidParameter = 11
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
enum JoystCapsFlags
|
|
||||||
{
|
|
||||||
HasZ = 0x1,
|
|
||||||
HasR = 0x2,
|
|
||||||
HasU = 0x4,
|
|
||||||
HasV = 0x8,
|
|
||||||
HasPov = 0x16,
|
|
||||||
HasPov4Dir = 0x32,
|
|
||||||
HasPovContinuous = 0x64
|
|
||||||
}
|
|
||||||
|
|
||||||
enum JoystickPovPosition : ushort
|
|
||||||
{
|
|
||||||
Centered = 0xFFFF,
|
|
||||||
Forward = 0,
|
|
||||||
Right = 9000,
|
|
||||||
Backward = 18000,
|
|
||||||
Left = 27000
|
|
||||||
}
|
|
||||||
|
|
||||||
struct JoyCaps
|
|
||||||
{
|
|
||||||
public ushort Mid;
|
|
||||||
public ushort ProductId;
|
|
||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
|
||||||
public string ProductName;
|
|
||||||
public int XMin;
|
|
||||||
public int XMax;
|
|
||||||
public int YMin;
|
|
||||||
public int YMax;
|
|
||||||
public int ZMin;
|
|
||||||
public int ZMax;
|
|
||||||
public int NumButtons;
|
|
||||||
public int PeriodMin;
|
|
||||||
public int PeriodMax;
|
|
||||||
public int RMin;
|
|
||||||
public int RMax;
|
|
||||||
public int UMin;
|
|
||||||
public int UMax;
|
|
||||||
public int VMin;
|
|
||||||
public int VMax;
|
|
||||||
public JoystCapsFlags Capabilities;
|
|
||||||
public int MaxAxes;
|
|
||||||
public int NumAxes;
|
|
||||||
public int MaxButtons;
|
|
||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
|
||||||
public string RegKey;
|
|
||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
|
||||||
public string OemVxD;
|
|
||||||
|
|
||||||
public static readonly int SizeInBytes;
|
|
||||||
|
|
||||||
static JoyCaps()
|
|
||||||
{
|
|
||||||
SizeInBytes = Marshal.SizeOf(default(JoyCaps));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetMin(int i)
|
|
||||||
{
|
|
||||||
switch (i)
|
|
||||||
{
|
|
||||||
case 0: return XMin;
|
|
||||||
case 1: return YMin;
|
|
||||||
case 2: return ZMin;
|
|
||||||
case 3: return RMin;
|
|
||||||
case 4: return UMin;
|
|
||||||
case 5: return VMin;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetMax(int i)
|
|
||||||
{
|
|
||||||
switch (i)
|
|
||||||
{
|
|
||||||
case 0: return XMax;
|
|
||||||
case 1: return YMax;
|
|
||||||
case 2: return ZMax;
|
|
||||||
case 3: return RMax;
|
|
||||||
case 4: return UMax;
|
|
||||||
case 5: return VMax;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct JoyInfo
|
|
||||||
{
|
|
||||||
public int XPos;
|
|
||||||
public int YPos;
|
|
||||||
public int ZPos;
|
|
||||||
public uint Buttons;
|
|
||||||
|
|
||||||
public int GetAxis(int i)
|
|
||||||
{
|
|
||||||
switch (i)
|
|
||||||
{
|
|
||||||
case 0: return XPos;
|
|
||||||
case 1: return YPos;
|
|
||||||
case 2: return ZPos;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct JoyInfoEx
|
|
||||||
{
|
|
||||||
public int Size;
|
|
||||||
[MarshalAs(UnmanagedType.I4)]
|
|
||||||
public JoystickFlags Flags;
|
|
||||||
public int XPos;
|
|
||||||
public int YPos;
|
|
||||||
public int ZPos;
|
|
||||||
public int RPos;
|
|
||||||
public int UPos;
|
|
||||||
public int VPos;
|
|
||||||
public uint Buttons;
|
|
||||||
public uint ButtonNumber;
|
|
||||||
public int Pov;
|
|
||||||
#pragma warning disable 0169
|
|
||||||
uint Reserved1;
|
|
||||||
uint Reserved2;
|
|
||||||
#pragma warning restore 0169
|
|
||||||
|
|
||||||
public static readonly int SizeInBytes;
|
|
||||||
|
|
||||||
static JoyInfoEx()
|
|
||||||
{
|
|
||||||
SizeInBytes = Marshal.SizeOf(default(JoyInfoEx));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetAxis(int i)
|
|
||||||
{
|
|
||||||
switch (i)
|
|
||||||
{
|
|
||||||
case 0: return XPos;
|
|
||||||
case 1: return YPos;
|
|
||||||
case 2: return ZPos;
|
|
||||||
case 3: return RPos;
|
|
||||||
case 4: return UPos;
|
|
||||||
case 5: return VPos;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class UnsafeNativeMethods
|
|
||||||
{
|
|
||||||
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern JoystickError joyGetDevCaps(int uJoyID, out JoyCaps pjc, int cbjc);
|
|
||||||
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern JoystickError joyGetPos(int uJoyID, ref JoyInfo pji);
|
|
||||||
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern JoystickError joyGetPosEx(int uJoyID, ref JoyInfoEx pji);
|
|
||||||
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern int joyGetNumDevs();
|
|
||||||
[DllImport("Winmm.dll"), SuppressUnmanagedCodeSecurity]
|
|
||||||
public static extern JoystickError joyConfigChanged(int flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region enum PovType
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
enum PovType
|
|
||||||
{
|
|
||||||
None = 0x0,
|
|
||||||
Exists = 0x1,
|
|
||||||
Discrete = 0x2,
|
|
||||||
Continuous = 0x4
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region struct WinMMJoyDetails
|
|
||||||
|
|
||||||
struct WinMMJoyDetails
|
|
||||||
{
|
|
||||||
public readonly int[] Min, Max; // Minimum and maximum offset of each axis.
|
|
||||||
public PovType PovType;
|
|
||||||
public JoystickCapabilities Capabilities;
|
|
||||||
|
|
||||||
public WinMMJoyDetails(JoystickCapabilities caps)
|
|
||||||
: this()
|
|
||||||
{
|
|
||||||
Min = new int[caps.AxisCount];
|
|
||||||
Max = new int[caps.AxisCount];
|
|
||||||
Capabilities = caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float CalculateOffset(float pos, int axis)
|
|
||||||
{
|
|
||||||
float offset = (2 * (pos - Min[axis])) / (Max[axis] - Min[axis]) - 1;
|
|
||||||
if (offset > 1)
|
|
||||||
return 1;
|
|
||||||
else if (offset < -1)
|
|
||||||
return -1;
|
|
||||||
else if (offset < 0.001f && offset > -0.001f)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,12 +38,10 @@ namespace OpenTK.Platform.Windows
|
||||||
#region Fields
|
#region Fields
|
||||||
|
|
||||||
// Input event data.
|
// Input event data.
|
||||||
static RawInput data = new RawInput();
|
|
||||||
|
|
||||||
WinRawKeyboard keyboard_driver;
|
WinRawKeyboard keyboard_driver;
|
||||||
WinRawMouse mouse_driver;
|
WinRawMouse mouse_driver;
|
||||||
IGamePadDriver gamepad_driver;
|
WinRawJoystick joystick_driver;
|
||||||
IJoystickDriver2 joystick_driver;
|
|
||||||
|
|
||||||
IntPtr DevNotifyHandle;
|
IntPtr DevNotifyHandle;
|
||||||
static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");
|
static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");
|
||||||
|
@ -88,46 +86,53 @@ namespace OpenTK.Platform.Windows
|
||||||
#region WindowProcedure
|
#region WindowProcedure
|
||||||
|
|
||||||
// Processes the input Windows Message, routing the buffer to the correct Keyboard, Mouse or HID.
|
// Processes the input Windows Message, routing the buffer to the correct Keyboard, Mouse or HID.
|
||||||
protected override IntPtr WindowProcedure(
|
protected unsafe override IntPtr WindowProcedure(
|
||||||
IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
|
IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
|
||||||
{
|
{
|
||||||
switch (message)
|
try
|
||||||
{
|
{
|
||||||
case WindowMessage.INPUT:
|
switch (message)
|
||||||
int size = 0;
|
{
|
||||||
// Get the size of the input buffer
|
case WindowMessage.INPUT:
|
||||||
Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
|
|
||||||
IntPtr.Zero, ref size, API.RawInputHeaderSize);
|
|
||||||
|
|
||||||
// Read the actual raw input structure
|
|
||||||
if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
|
|
||||||
out data, ref size, API.RawInputHeaderSize))
|
|
||||||
{
|
|
||||||
switch (data.Header.Type)
|
|
||||||
{
|
{
|
||||||
case RawInputDeviceType.KEYBOARD:
|
// Retrieve the raw input data buffer
|
||||||
if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(data))
|
RawInputHeader header;
|
||||||
return IntPtr.Zero;
|
if (Functions.GetRawInputData(lParam, out header) == RawInputHeader.SizeInBytes)
|
||||||
break;
|
{
|
||||||
|
switch (header.Type)
|
||||||
|
{
|
||||||
|
case RawInputDeviceType.KEYBOARD:
|
||||||
|
if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(lParam))
|
||||||
|
return IntPtr.Zero;
|
||||||
|
break;
|
||||||
|
|
||||||
case RawInputDeviceType.MOUSE:
|
case RawInputDeviceType.MOUSE:
|
||||||
if (((WinRawMouse)MouseDriver).ProcessMouseEvent(data))
|
if (((WinRawMouse)MouseDriver).ProcessMouseEvent(lParam))
|
||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RawInputDeviceType.HID:
|
case RawInputDeviceType.HID:
|
||||||
break;
|
if (((WinRawJoystick)JoystickDriver).ProcessEvent(lParam))
|
||||||
|
return IntPtr.Zero;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case WindowMessage.DEVICECHANGE:
|
case WindowMessage.DEVICECHANGE:
|
||||||
((WinRawKeyboard)KeyboardDriver).RefreshDevices();
|
((WinRawKeyboard)KeyboardDriver).RefreshDevices();
|
||||||
((WinRawMouse)MouseDriver).RefreshDevices();
|
((WinRawMouse)MouseDriver).RefreshDevices();
|
||||||
((WinMMJoystick)JoystickDriver).RefreshDevices();
|
((WinRawJoystick)JoystickDriver).RefreshDevices();
|
||||||
break;
|
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
|
#endregion
|
||||||
|
@ -138,17 +143,7 @@ namespace OpenTK.Platform.Windows
|
||||||
{
|
{
|
||||||
keyboard_driver = new WinRawKeyboard(Parent.Handle);
|
keyboard_driver = new WinRawKeyboard(Parent.Handle);
|
||||||
mouse_driver = new WinRawMouse(Parent.Handle);
|
mouse_driver = new WinRawMouse(Parent.Handle);
|
||||||
joystick_driver = new WinMMJoystick();
|
joystick_driver = new WinRawJoystick(Parent.Handle);
|
||||||
try
|
|
||||||
{
|
|
||||||
gamepad_driver = new XInputJoystick();
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
Debug.Print("[Win] XInput driver not supported, falling back to WinMM");
|
|
||||||
gamepad_driver = new MappedGamePadDriver();
|
|
||||||
}
|
|
||||||
|
|
||||||
DevNotifyHandle = RegisterForDeviceNotifications(Parent);
|
DevNotifyHandle = RegisterForDeviceNotifications(Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,9 +176,19 @@ namespace OpenTK.Platform.Windows
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#endregion
|
#region GetDeviceList
|
||||||
|
|
||||||
#region IInputDriver2 Members
|
public static RawInputDeviceList[] GetDeviceList()
|
||||||
|
{
|
||||||
|
int count = WinRawInput.DeviceCount;
|
||||||
|
RawInputDeviceList[] ridl = new RawInputDeviceList[count];
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
ridl[i] = new RawInputDeviceList();
|
||||||
|
Functions.GetRawInputDeviceList(ridl, ref count, API.RawInputDeviceListSize);
|
||||||
|
return ridl;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public override IKeyboardDriver2 KeyboardDriver
|
public override IKeyboardDriver2 KeyboardDriver
|
||||||
{
|
{
|
||||||
|
@ -195,11 +200,6 @@ namespace OpenTK.Platform.Windows
|
||||||
get { return mouse_driver; }
|
get { return mouse_driver; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IGamePadDriver GamePadDriver
|
|
||||||
{
|
|
||||||
get { return gamepad_driver; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IJoystickDriver2 JoystickDriver
|
public override IJoystickDriver2 JoystickDriver
|
||||||
{
|
{
|
||||||
get { return joystick_driver; }
|
get { return joystick_driver; }
|
||||||
|
|
830
Source/OpenTK/Platform/Windows/WinRawJoystick.cs
Normal file
830
Source/OpenTK/Platform/Windows/WinRawJoystick.cs
Normal file
|
@ -0,0 +1,830 @@
|
||||||
|
#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;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using OpenTK.Input;
|
||||||
|
using OpenTK.Platform.Common;
|
||||||
|
|
||||||
|
namespace OpenTK.Platform.Windows
|
||||||
|
{
|
||||||
|
class WinRawJoystick : IJoystickDriver2
|
||||||
|
{
|
||||||
|
class Device
|
||||||
|
{
|
||||||
|
public IntPtr Handle;
|
||||||
|
JoystickCapabilities Capabilities;
|
||||||
|
JoystickState State;
|
||||||
|
Guid Guid;
|
||||||
|
|
||||||
|
internal readonly List<HidProtocolValueCaps> AxisCaps =
|
||||||
|
new List<HidProtocolValueCaps>();
|
||||||
|
internal readonly List<HidProtocolButtonCaps> ButtonCaps =
|
||||||
|
new List<HidProtocolButtonCaps>();
|
||||||
|
internal readonly bool IsXInput;
|
||||||
|
internal readonly int XInputIndex;
|
||||||
|
|
||||||
|
readonly Dictionary<int, JoystickAxis> axes =
|
||||||
|
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, bool is_xinput, int xinput_index)
|
||||||
|
{
|
||||||
|
Handle = handle;
|
||||||
|
Guid = guid;
|
||||||
|
IsXInput = is_xinput;
|
||||||
|
XInputIndex = xinput_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Members
|
||||||
|
|
||||||
|
public void ClearButtons()
|
||||||
|
{
|
||||||
|
State.ClearButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAxis(short collection, HIDPage page, short usage, short value)
|
||||||
|
{
|
||||||
|
JoystickAxis axis = GetAxis(collection, page, usage);
|
||||||
|
State.SetAxis(axis, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetButton(short collection, HIDPage page, short usage, bool value)
|
||||||
|
{
|
||||||
|
JoystickButton button = GetButton(collection, page, usage);
|
||||||
|
State.SetButton(button, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetHat(short collection, HIDPage page, short usage, HatPosition pos)
|
||||||
|
{
|
||||||
|
JoystickHat hat = GetHat(collection, page, usage);
|
||||||
|
State.SetHat(hat, new JoystickHatState(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConnected(bool value)
|
||||||
|
{
|
||||||
|
Capabilities.SetIsConnected(value);
|
||||||
|
State.SetIsConnected(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JoystickState GetState()
|
||||||
|
{
|
||||||
|
return State;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Members
|
||||||
|
|
||||||
|
static int MakeKey(short collection, HIDPage page, short usage)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
JoystickAxis GetAxis(short collection, HIDPage page, short usage)
|
||||||
|
{
|
||||||
|
int key = MakeKey(collection, page, usage);
|
||||||
|
if (!axes.ContainsKey(key))
|
||||||
|
{
|
||||||
|
JoystickAxis axis = HidHelper.TranslateJoystickAxis(page, usage);
|
||||||
|
axes.Add(key, axis);
|
||||||
|
}
|
||||||
|
return axes[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
JoystickButton GetButton(short collection, HIDPage page, short usage)
|
||||||
|
{
|
||||||
|
int key = MakeKey(collection, page, usage);
|
||||||
|
if (!buttons.ContainsKey(key))
|
||||||
|
{
|
||||||
|
buttons.Add(key, JoystickButton.Button0 + buttons.Count);
|
||||||
|
}
|
||||||
|
return buttons[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
JoystickHat GetHat(short collection, HIDPage page, short usage)
|
||||||
|
{
|
||||||
|
int key = MakeKey(collection, page, usage);
|
||||||
|
if (!hats.ContainsKey(key))
|
||||||
|
{
|
||||||
|
hats.Add(key, JoystickHat.Hat0 + hats.Count);
|
||||||
|
}
|
||||||
|
return hats[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly string TypeName = typeof(WinRawJoystick).Name;
|
||||||
|
|
||||||
|
XInputJoystick XInput = new XInputJoystick();
|
||||||
|
|
||||||
|
// Defines which types of HID devices we are interested in
|
||||||
|
readonly RawInputDevice[] DeviceTypes;
|
||||||
|
|
||||||
|
readonly IntPtr Window;
|
||||||
|
readonly object UpdateLock = new object();
|
||||||
|
readonly DeviceCollection<Device> Devices = new DeviceCollection<Device>();
|
||||||
|
|
||||||
|
byte[] HIDData = new byte[1024];
|
||||||
|
byte[] PreparsedData = new byte[1024];
|
||||||
|
HidProtocolData[] DataBuffer = new HidProtocolData[16];
|
||||||
|
|
||||||
|
public WinRawJoystick(IntPtr window)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Using WinRawJoystick.");
|
||||||
|
Debug.Indent();
|
||||||
|
|
||||||
|
if (window == IntPtr.Zero)
|
||||||
|
throw new ArgumentNullException("window");
|
||||||
|
|
||||||
|
Window = window;
|
||||||
|
DeviceTypes = new RawInputDevice[]
|
||||||
|
{
|
||||||
|
new RawInputDevice(HIDUsageGD.Joystick, RawInputDeviceFlags.DEVNOTIFY | RawInputDeviceFlags.INPUTSINK, window),
|
||||||
|
new RawInputDevice(HIDUsageGD.GamePad, RawInputDeviceFlags.DEVNOTIFY | 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Public Members
|
||||||
|
|
||||||
|
public void RefreshDevices()
|
||||||
|
{
|
||||||
|
// Mark all devices as disconnected. We will check which of those
|
||||||
|
// are connected below.
|
||||||
|
foreach (var device in Devices)
|
||||||
|
{
|
||||||
|
device.SetConnected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover joystick devices
|
||||||
|
int xinput_device_count = 0;
|
||||||
|
foreach (RawInputDeviceList dev in WinRawInput.GetDeviceList())
|
||||||
|
{
|
||||||
|
// Skip non-joystick devices
|
||||||
|
if (dev.Type != RawInputDeviceType.HID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 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.)
|
||||||
|
IntPtr handle = dev.Device;
|
||||||
|
bool is_xinput = IsXInput(handle);
|
||||||
|
Guid guid = GetDeviceGuid(handle);
|
||||||
|
long hardware_id = handle.ToInt64();
|
||||||
|
|
||||||
|
Device device = Devices.FromHardwareId(hardware_id);
|
||||||
|
if (device != null)
|
||||||
|
{
|
||||||
|
// We have already opened this device, mark it as connected
|
||||||
|
device.SetConnected(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
device = new Device(handle, guid, is_xinput,
|
||||||
|
is_xinput ? xinput_device_count++ : 0);
|
||||||
|
|
||||||
|
// This is a new device, query its capabilities and add it
|
||||||
|
// to the device list
|
||||||
|
if (!QueryDeviceCaps(device))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
device.SetConnected(true);
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
Devices.Add(hardware_id, device);
|
||||||
|
|
||||||
|
Debug.Print("[{0}] Connected joystick {1} ({2})",
|
||||||
|
GetType().Name, device.GetGuid(), device.GetCapabilities());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe bool ProcessEvent(IntPtr raw)
|
||||||
|
{
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
Array.Resize(ref HIDData, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the raw HID data buffer
|
||||||
|
if (Functions.GetRawInputData(raw, HIDData) > 0)
|
||||||
|
{
|
||||||
|
fixed (byte* pdata = HIDData)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stick.IsXInput)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAxes(rin, stick);
|
||||||
|
UpdateButtons(rin, stick);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HatPosition GetHatPosition(uint value, HidProtocolValueCaps caps)
|
||||||
|
{
|
||||||
|
if (caps.LogicalMax == 8)
|
||||||
|
return (HatPosition)value;
|
||||||
|
else
|
||||||
|
return HatPosition.Centered;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
short collection = stick.AxisCaps[i].LinkCollection;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
stick.SetHat(collection, page, usage, GetHatPosition(value, stick.AxisCaps[i]));
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
stick.SetAxis(collection, page, usage, scaled_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe void UpdateButtons(RawInput* rin, Device stick)
|
||||||
|
{
|
||||||
|
stick.ClearButtons();
|
||||||
|
|
||||||
|
for (int i = 0; i < stick.ButtonCaps.Count; i++)
|
||||||
|
{
|
||||||
|
short* usage_list = stackalloc short[(int)JoystickButton.Last + 1];
|
||||||
|
int usage_length = (int)JoystickButton.Last;
|
||||||
|
HIDPage page = stick.ButtonCaps[i].UsagePage;
|
||||||
|
short collection = stick.ButtonCaps[i].LinkCollection;
|
||||||
|
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
short usage = *(usage_list + j);
|
||||||
|
stick.SetButton(collection, page, usage, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Members
|
||||||
|
|
||||||
|
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(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.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QueryDeviceCaps(Device stick)
|
||||||
|
{
|
||||||
|
Debug.Print("[{0}] Querying joystick {1}",
|
||||||
|
TypeName, stick.GetGuid());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Debug.Indent();
|
||||||
|
HidProtocolCaps caps;
|
||||||
|
|
||||||
|
if (GetPreparsedData(stick.Handle, ref PreparsedData) &&
|
||||||
|
GetDeviceCaps(stick, PreparsedData, out caps))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < stick.AxisCaps.Count; i++)
|
||||||
|
{
|
||||||
|
Debug.Print("Analyzing value collection {0} {1} {2}",
|
||||||
|
i,
|
||||||
|
stick.AxisCaps[i].IsRange ? "range" : "",
|
||||||
|
stick.AxisCaps[i].IsAlias ? "alias" : "");
|
||||||
|
|
||||||
|
if (stick.AxisCaps[i].IsRange || stick.AxisCaps[i].IsAlias)
|
||||||
|
{
|
||||||
|
Debug.Print("Skipping value collection {0}", i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
HIDPage page = stick.AxisCaps[i].UsagePage;
|
||||||
|
short collection = stick.AxisCaps[i].LinkCollection;
|
||||||
|
switch (page)
|
||||||
|
{
|
||||||
|
case HIDPage.GenericDesktop:
|
||||||
|
HIDUsageGD gd_usage = (HIDUsageGD)stick.AxisCaps[i].NotRange.Usage;
|
||||||
|
switch (gd_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:
|
||||||
|
Debug.Print("Found axis {0} ({1} / {2})",
|
||||||
|
JoystickAxis.Axis0 + stick.GetCapabilities().AxisCount,
|
||||||
|
page, (HIDUsageGD)stick.AxisCaps[i].NotRange.Usage);
|
||||||
|
stick.SetAxis(collection, page, stick.AxisCaps[i].NotRange.Usage, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HIDUsageGD.Hatswitch:
|
||||||
|
Debug.Print("Found hat {0} ({1} / {2})",
|
||||||
|
JoystickHat.Hat0 + stick.GetCapabilities().HatCount,
|
||||||
|
page, (HIDUsageGD)stick.AxisCaps[i].NotRange.Usage);
|
||||||
|
stick.SetHat(collection, page, stick.AxisCaps[i].NotRange.Usage, HatPosition.Centered);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Debug.Print("Unknown usage {0} for page {1}",
|
||||||
|
gd_usage, page);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
stick.SetAxis(collection, page, stick.AxisCaps[i].NotRange.Usage, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Debug.Print("Unknown page {0}", page);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < stick.ButtonCaps.Count; i++)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
short collection = stick.ButtonCaps[i].LinkCollection;
|
||||||
|
switch (page)
|
||||||
|
{
|
||||||
|
case HIDPage.Button:
|
||||||
|
if (is_range)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
stick.SetButton(collection, page, usage, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Print("Found button {0} ({1} / {2})",
|
||||||
|
JoystickButton.Button0 + stick.GetCapabilities().ButtonCount,
|
||||||
|
page, stick.ButtonCaps[i].NotRange.Usage);
|
||||||
|
stick.SetButton(collection, page, stick.ButtonCaps[i].NotRange.Usage, false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Debug.Print("Unknown page {0} for button.", page);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Debug.Unindent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetDeviceCaps(Device stick, byte[] preparsed_data, out HidProtocolCaps caps)
|
||||||
|
{
|
||||||
|
int axis_caps_count = 0;
|
||||||
|
int button_caps_count = 0;
|
||||||
|
|
||||||
|
// Query joystick capabilities
|
||||||
|
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
|
||||||
|
HidProtocolValueCaps[] axis_caps = new HidProtocolValueCaps[caps.NumberInputValueCaps];
|
||||||
|
HidProtocolButtonCaps[] button_caps = new HidProtocolButtonCaps[caps.NumberInputButtonCaps];
|
||||||
|
|
||||||
|
// Axis capabilities
|
||||||
|
ushort axis_count = (ushort)axis_caps.Length;
|
||||||
|
if (HidProtocol.GetValueCaps(HidProtocolReportType.Input,
|
||||||
|
axis_caps, ref axis_count, preparsed_data) !=
|
||||||
|
HidProtocolStatus.Success)
|
||||||
|
{
|
||||||
|
Debug.Print("[WinRawJoystick] HidProtocol.GetValueCaps() failed with {0}",
|
||||||
|
Marshal.GetLastWin32Error());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
axis_caps_count = (int)axis_count;
|
||||||
|
|
||||||
|
// Button capabilities
|
||||||
|
ushort button_count = (ushort)button_caps.Length;
|
||||||
|
if (HidProtocol.GetButtonCaps(HidProtocolReportType.Input,
|
||||||
|
button_caps, ref button_count, preparsed_data) !=
|
||||||
|
HidProtocolStatus.Success)
|
||||||
|
{
|
||||||
|
Debug.Print("[WinRawJoystick] HidProtocol.GetButtonCaps() failed with {0}",
|
||||||
|
Marshal.GetLastWin32Error());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
button_caps_count = (int)button_count;
|
||||||
|
|
||||||
|
stick.AxisCaps.Clear();
|
||||||
|
stick.AxisCaps.AddRange(axis_caps);
|
||||||
|
|
||||||
|
stick.ButtonCaps.Clear();
|
||||||
|
stick.ButtonCaps.AddRange(button_caps);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a DirectInput-compatible Guid
|
||||||
|
// (equivalent to DIDEVICEINSTANCE guidProduct field)
|
||||||
|
Guid GetDeviceGuid(IntPtr handle)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
// 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 is_xinput;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate memory and retrieve the DEVICENAME string
|
||||||
|
sbyte* pname = stackalloc sbyte[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 is_xinput;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 is_xinput;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_xinput = name.Contains("IG_");
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_xinput;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device GetDevice(IntPtr handle)
|
||||||
|
{
|
||||||
|
long hardware_id = handle.ToInt64();
|
||||||
|
bool is_device_known = false;
|
||||||
|
|
||||||
|
lock (UpdateLock)
|
||||||
|
{
|
||||||
|
is_device_known = Devices.FromHardwareId(hardware_id) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_device_known)
|
||||||
|
{
|
||||||
|
RefreshDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (UpdateLock)
|
||||||
|
{
|
||||||
|
return Devices.FromHardwareId(hardware_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid(int index)
|
||||||
|
{
|
||||||
|
return Devices.FromIndex(index) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IJoystickDriver2 Members
|
||||||
|
|
||||||
|
public JoystickState GetState(int index)
|
||||||
|
{
|
||||||
|
lock (UpdateLock)
|
||||||
|
{
|
||||||
|
if (IsValid(index))
|
||||||
|
{
|
||||||
|
Device dev = Devices.FromIndex(index);
|
||||||
|
if (dev.IsXInput)
|
||||||
|
{
|
||||||
|
return XInput.GetState(dev.XInputIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return dev.GetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new JoystickState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JoystickCapabilities GetCapabilities(int index)
|
||||||
|
{
|
||||||
|
lock (UpdateLock)
|
||||||
|
{
|
||||||
|
if (IsValid(index))
|
||||||
|
{
|
||||||
|
Device dev = Devices.FromIndex(index);
|
||||||
|
if (dev.IsXInput)
|
||||||
|
{
|
||||||
|
return XInput.GetCapabilities(dev.XInputIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return dev.GetCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new JoystickCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid GetGuid(int index)
|
||||||
|
{
|
||||||
|
lock (UpdateLock)
|
||||||
|
{
|
||||||
|
if (IsValid(index))
|
||||||
|
{
|
||||||
|
Device dev = Devices.FromIndex(index);
|
||||||
|
if (dev.IsXInput)
|
||||||
|
{
|
||||||
|
return XInput.GetGuid(dev.XInputIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return dev.GetGuid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Guid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ using System.Runtime.InteropServices;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
#endif
|
#endif
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
|
using OpenTK.Platform.Common;
|
||||||
|
|
||||||
namespace OpenTK.Platform.Windows
|
namespace OpenTK.Platform.Windows
|
||||||
{
|
{
|
||||||
|
@ -152,51 +153,57 @@ namespace OpenTK.Platform.Windows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ProcessKeyboardEvent(RawInput rin)
|
public bool ProcessKeyboardEvent(IntPtr raw)
|
||||||
{
|
{
|
||||||
bool processed = false;
|
bool processed = false;
|
||||||
|
|
||||||
bool pressed =
|
RawInput rin;
|
||||||
rin.Data.Keyboard.Message == (int)WindowMessage.KEYDOWN ||
|
if (Functions.GetRawInputData(raw, out rin) > 0)
|
||||||
rin.Data.Keyboard.Message == (int)WindowMessage.SYSKEYDOWN;
|
|
||||||
var scancode = rin.Data.Keyboard.MakeCode;
|
|
||||||
var vkey = rin.Data.Keyboard.VKey;
|
|
||||||
|
|
||||||
bool extended0 = (int)(rin.Data.Keyboard.Flags & RawInputKeyboardDataFlags.E0) != 0;
|
|
||||||
bool extended1 = (int)(rin.Data.Keyboard.Flags & RawInputKeyboardDataFlags.E1) != 0;
|
|
||||||
|
|
||||||
bool is_valid = true;
|
|
||||||
|
|
||||||
ContextHandle handle = new ContextHandle(rin.Header.Device);
|
|
||||||
KeyboardState keyboard;
|
|
||||||
if (!rawids.ContainsKey(handle))
|
|
||||||
{
|
{
|
||||||
RefreshDevices();
|
bool pressed =
|
||||||
|
rin.Data.Keyboard.Message == (int)WindowMessage.KEYDOWN ||
|
||||||
|
rin.Data.Keyboard.Message == (int)WindowMessage.SYSKEYDOWN;
|
||||||
|
var scancode = rin.Data.Keyboard.MakeCode;
|
||||||
|
var vkey = rin.Data.Keyboard.VKey;
|
||||||
|
|
||||||
|
bool extended0 = (int)(rin.Data.Keyboard.Flags & RawInputKeyboardDataFlags.E0) != 0;
|
||||||
|
bool extended1 = (int)(rin.Data.Keyboard.Flags & RawInputKeyboardDataFlags.E1) != 0;
|
||||||
|
|
||||||
|
bool is_valid = true;
|
||||||
|
|
||||||
|
ContextHandle handle = new ContextHandle(rin.Header.Device);
|
||||||
|
KeyboardState keyboard;
|
||||||
|
if (!rawids.ContainsKey(handle))
|
||||||
|
{
|
||||||
|
RefreshDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyboards.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Note:For some reason, my Microsoft Digital 3000 keyboard reports 0
|
||||||
|
// as rin.Header.Device for the "zoom-in/zoom-out" buttons.
|
||||||
|
// That's problematic, because no device has a "0" id.
|
||||||
|
// As a workaround, we'll add those buttons to the first device (if any).
|
||||||
|
int keyboard_handle = rawids.ContainsKey(handle) ? rawids[handle] : 0;
|
||||||
|
keyboard = keyboards[keyboard_handle];
|
||||||
|
|
||||||
|
Key key = WinKeyMap.TranslateKey(scancode, vkey, extended0, extended1, out is_valid);
|
||||||
|
|
||||||
|
if (is_valid)
|
||||||
|
{
|
||||||
|
keyboard.SetKeyState(key, pressed);
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (UpdateLock)
|
||||||
|
{
|
||||||
|
keyboards[keyboard_handle] = keyboard;
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyboards.Count == 0)
|
return processed;
|
||||||
return false;
|
|
||||||
|
|
||||||
// Note:For some reason, my Microsoft Digital 3000 keyboard reports 0
|
|
||||||
// as rin.Header.Device for the "zoom-in/zoom-out" buttons.
|
|
||||||
// That's problematic, because no device has a "0" id.
|
|
||||||
// As a workaround, we'll add those buttons to the first device (if any).
|
|
||||||
int keyboard_handle = rawids.ContainsKey(handle) ? rawids[handle] : 0;
|
|
||||||
keyboard = keyboards[keyboard_handle];
|
|
||||||
|
|
||||||
Key key = WinKeyMap.TranslateKey(scancode, vkey, extended0, extended1, out is_valid);
|
|
||||||
|
|
||||||
if (is_valid)
|
|
||||||
{
|
|
||||||
keyboard.SetKeyState(key, pressed);
|
|
||||||
processed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (UpdateLock)
|
|
||||||
{
|
|
||||||
keyboards[keyboard_handle] = keyboard;
|
|
||||||
return processed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -230,7 +237,7 @@ namespace OpenTK.Platform.Windows
|
||||||
|
|
||||||
static string GetDeviceName(RawInputDeviceList dev)
|
static string GetDeviceName(RawInputDeviceList dev)
|
||||||
{
|
{
|
||||||
uint size = 0;
|
int size = 0;
|
||||||
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
|
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
|
||||||
IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size);
|
IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size);
|
||||||
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size);
|
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size);
|
||||||
|
@ -241,13 +248,10 @@ namespace OpenTK.Platform.Windows
|
||||||
|
|
||||||
static void RegisterKeyboardDevice(IntPtr window, string name)
|
static void RegisterKeyboardDevice(IntPtr window, string name)
|
||||||
{
|
{
|
||||||
RawInputDevice[] rid = new RawInputDevice[1];
|
RawInputDevice[] rid = new RawInputDevice[]
|
||||||
// Keyboard is 1/6 (page/id). See http://www.microsoft.com/whdc/device/input/HID_HWID.mspx
|
{
|
||||||
rid[0] = new RawInputDevice();
|
new RawInputDevice(HIDUsageGD.Keyboard, RawInputDeviceFlags.INPUTSINK, window)
|
||||||
rid[0].UsagePage = 1;
|
};
|
||||||
rid[0].Usage = 6;
|
|
||||||
rid[0].Flags = RawInputDeviceFlags.INPUTSINK;
|
|
||||||
rid[0].Target = window;
|
|
||||||
|
|
||||||
if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize))
|
if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize))
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,6 +31,7 @@ using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
|
using OpenTK.Platform.Common;
|
||||||
|
|
||||||
namespace OpenTK.Platform.Windows
|
namespace OpenTK.Platform.Windows
|
||||||
{
|
{
|
||||||
|
@ -79,14 +80,8 @@ namespace OpenTK.Platform.Windows
|
||||||
mice[i] = state;
|
mice[i] = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = WinRawInput.DeviceCount;
|
|
||||||
RawInputDeviceList[] ridl = new RawInputDeviceList[count];
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
ridl[i] = new RawInputDeviceList();
|
|
||||||
Functions.GetRawInputDeviceList(ridl, ref count, API.RawInputDeviceListSize);
|
|
||||||
|
|
||||||
// Discover mouse devices
|
// Discover mouse devices
|
||||||
foreach (RawInputDeviceList dev in ridl)
|
foreach (RawInputDeviceList dev in WinRawInput.GetDeviceList())
|
||||||
{
|
{
|
||||||
ContextHandle id = new ContextHandle(dev.Device);
|
ContextHandle id = new ContextHandle(dev.Device);
|
||||||
if (rawids.ContainsKey(id))
|
if (rawids.ContainsKey(id))
|
||||||
|
@ -154,100 +149,109 @@ namespace OpenTK.Platform.Windows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ProcessMouseEvent(RawInput rin)
|
public bool ProcessMouseEvent(IntPtr raw_buffer)
|
||||||
{
|
{
|
||||||
RawMouse raw = rin.Data.Mouse;
|
bool processed = false;
|
||||||
ContextHandle handle = new ContextHandle(rin.Header.Device);
|
|
||||||
|
|
||||||
MouseState mouse;
|
RawInput rin;
|
||||||
if (!rawids.ContainsKey(handle))
|
if (Functions.GetRawInputData(raw_buffer, out rin) > 0)
|
||||||
{
|
{
|
||||||
RefreshDevices();
|
RawMouse raw = rin.Data.Mouse;
|
||||||
|
ContextHandle handle = new ContextHandle(rin.Header.Device);
|
||||||
|
|
||||||
|
MouseState mouse;
|
||||||
|
if (!rawids.ContainsKey(handle))
|
||||||
|
{
|
||||||
|
RefreshDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mice.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Note:For some reason, my Microsoft Digital 3000 keyboard reports 0
|
||||||
|
// as rin.Header.Device for the "zoom-in/zoom-out" buttons.
|
||||||
|
// That's problematic, because no device has a "0" id.
|
||||||
|
// As a workaround, we'll add those buttons to the first device (if any).
|
||||||
|
int mouse_handle = rawids.ContainsKey(handle) ? rawids[handle] : 0;
|
||||||
|
mouse = mice[mouse_handle];
|
||||||
|
|
||||||
|
// Set and release capture of the mouse to fix http://www.opentk.com/node/2133, Patch by Artfunkel
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_DOWN) != 0)
|
||||||
|
{
|
||||||
|
mouse.EnableBit((int)MouseButton.Left);
|
||||||
|
Functions.SetCapture(Window);
|
||||||
|
}
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_UP) != 0)
|
||||||
|
{
|
||||||
|
mouse.DisableBit((int)MouseButton.Left);
|
||||||
|
Functions.ReleaseCapture();
|
||||||
|
}
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_DOWN) != 0)
|
||||||
|
{
|
||||||
|
mouse.EnableBit((int)MouseButton.Right);
|
||||||
|
Functions.SetCapture(Window);
|
||||||
|
}
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_UP) != 0)
|
||||||
|
{
|
||||||
|
mouse.DisableBit((int)MouseButton.Right);
|
||||||
|
Functions.ReleaseCapture();
|
||||||
|
}
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_DOWN) != 0)
|
||||||
|
{
|
||||||
|
mouse.EnableBit((int)MouseButton.Middle);
|
||||||
|
Functions.SetCapture(Window);
|
||||||
|
}
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_UP) != 0)
|
||||||
|
{
|
||||||
|
mouse.DisableBit((int)MouseButton.Middle);
|
||||||
|
Functions.ReleaseCapture();
|
||||||
|
}
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_DOWN) != 0)
|
||||||
|
{
|
||||||
|
mouse.EnableBit((int)MouseButton.Button1);
|
||||||
|
Functions.SetCapture(Window);
|
||||||
|
}
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_UP) != 0)
|
||||||
|
{
|
||||||
|
mouse.DisableBit((int)MouseButton.Button1);
|
||||||
|
Functions.ReleaseCapture();
|
||||||
|
}
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_DOWN) != 0)
|
||||||
|
{
|
||||||
|
mouse.EnableBit((int)MouseButton.Button2);
|
||||||
|
Functions.SetCapture(Window);
|
||||||
|
}
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_UP) != 0)
|
||||||
|
{
|
||||||
|
mouse.DisableBit((int)MouseButton.Button2);
|
||||||
|
Functions.ReleaseCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.WHEEL) != 0)
|
||||||
|
mouse.SetScrollRelative(0, (short)raw.ButtonData / 120.0f);
|
||||||
|
|
||||||
|
if ((raw.ButtonFlags & RawInputMouseState.HWHEEL) != 0)
|
||||||
|
mouse.SetScrollRelative((short)raw.ButtonData / 120.0f, 0);
|
||||||
|
|
||||||
|
if ((raw.Flags & RawMouseFlags.MOUSE_MOVE_ABSOLUTE) != 0)
|
||||||
|
{
|
||||||
|
mouse.X = raw.LastX;
|
||||||
|
mouse.Y = raw.LastY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // Seems like MOUSE_MOVE_RELATIVE is the default, unless otherwise noted.
|
||||||
|
mouse.X += raw.LastX;
|
||||||
|
mouse.Y += raw.LastY;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (UpdateLock)
|
||||||
|
{
|
||||||
|
mice[mouse_handle] = mouse;
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mice.Count == 0)
|
return processed;
|
||||||
return false;
|
|
||||||
|
|
||||||
// Note:For some reason, my Microsoft Digital 3000 keyboard reports 0
|
|
||||||
// as rin.Header.Device for the "zoom-in/zoom-out" buttons.
|
|
||||||
// That's problematic, because no device has a "0" id.
|
|
||||||
// As a workaround, we'll add those buttons to the first device (if any).
|
|
||||||
int mouse_handle = rawids.ContainsKey(handle) ? rawids[handle] : 0;
|
|
||||||
mouse = mice[mouse_handle];
|
|
||||||
|
|
||||||
// Set and release capture of the mouse to fix http://www.opentk.com/node/2133, Patch by Artfunkel
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_DOWN) != 0){
|
|
||||||
mouse.EnableBit((int)MouseButton.Left);
|
|
||||||
Functions.SetCapture(Window);
|
|
||||||
}
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_UP) != 0)
|
|
||||||
{
|
|
||||||
mouse.DisableBit((int)MouseButton.Left);
|
|
||||||
Functions.ReleaseCapture();
|
|
||||||
}
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_DOWN) != 0)
|
|
||||||
{
|
|
||||||
mouse.EnableBit((int)MouseButton.Right);
|
|
||||||
Functions.SetCapture(Window);
|
|
||||||
}
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_UP) != 0)
|
|
||||||
{
|
|
||||||
mouse.DisableBit((int)MouseButton.Right);
|
|
||||||
Functions.ReleaseCapture();
|
|
||||||
}
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_DOWN) != 0)
|
|
||||||
{
|
|
||||||
mouse.EnableBit((int)MouseButton.Middle);
|
|
||||||
Functions.SetCapture(Window);
|
|
||||||
}
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_UP) != 0)
|
|
||||||
{
|
|
||||||
mouse.DisableBit((int)MouseButton.Middle);
|
|
||||||
Functions.ReleaseCapture();
|
|
||||||
}
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_DOWN) != 0)
|
|
||||||
{
|
|
||||||
mouse.EnableBit((int)MouseButton.Button1);
|
|
||||||
Functions.SetCapture(Window);
|
|
||||||
}
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_UP) != 0)
|
|
||||||
{
|
|
||||||
mouse.DisableBit((int)MouseButton.Button1);
|
|
||||||
Functions.ReleaseCapture();
|
|
||||||
}
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_DOWN) != 0)
|
|
||||||
{
|
|
||||||
mouse.EnableBit((int)MouseButton.Button2);
|
|
||||||
Functions.SetCapture(Window);
|
|
||||||
}
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_UP) != 0)
|
|
||||||
{
|
|
||||||
mouse.DisableBit((int)MouseButton.Button2);
|
|
||||||
Functions.ReleaseCapture();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.WHEEL) != 0)
|
|
||||||
mouse.SetScrollRelative(0, (short)raw.ButtonData / 120.0f);
|
|
||||||
|
|
||||||
if ((raw.ButtonFlags & RawInputMouseState.HWHEEL) != 0)
|
|
||||||
mouse.SetScrollRelative((short)raw.ButtonData / 120.0f, 0);
|
|
||||||
|
|
||||||
if ((raw.Flags & RawMouseFlags.MOUSE_MOVE_ABSOLUTE) != 0)
|
|
||||||
{
|
|
||||||
mouse.X = raw.LastX;
|
|
||||||
mouse.Y = raw.LastY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // Seems like MOUSE_MOVE_RELATIVE is the default, unless otherwise noted.
|
|
||||||
mouse.X += raw.LastX;
|
|
||||||
mouse.Y += raw.LastY;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (UpdateLock)
|
|
||||||
{
|
|
||||||
mice[mouse_handle] = mouse;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -257,7 +261,7 @@ namespace OpenTK.Platform.Windows
|
||||||
static string GetDeviceName(RawInputDeviceList dev)
|
static string GetDeviceName(RawInputDeviceList dev)
|
||||||
{
|
{
|
||||||
// get name size
|
// get name size
|
||||||
uint size = 0;
|
int size = 0;
|
||||||
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
|
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
|
||||||
|
|
||||||
// get actual name
|
// get actual name
|
||||||
|
@ -296,13 +300,10 @@ namespace OpenTK.Platform.Windows
|
||||||
|
|
||||||
static void RegisterRawDevice(IntPtr window, string device)
|
static void RegisterRawDevice(IntPtr window, string device)
|
||||||
{
|
{
|
||||||
RawInputDevice[] rid = new RawInputDevice[1];
|
RawInputDevice[] rid = new RawInputDevice[]
|
||||||
// Mouse is 1/2 (page/id). See http://www.microsoft.com/whdc/device/input/HID_HWID.mspx
|
{
|
||||||
rid[0] = new RawInputDevice();
|
new RawInputDevice(HIDUsageGD.Mouse, RawInputDeviceFlags.INPUTSINK, window)
|
||||||
rid[0].UsagePage = 1;
|
};
|
||||||
rid[0].Usage = 2;
|
|
||||||
rid[0].Flags = RawInputDeviceFlags.INPUTSINK;
|
|
||||||
rid[0].Target = window;
|
|
||||||
|
|
||||||
if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize))
|
if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize))
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,36 +37,86 @@ using System.Diagnostics;
|
||||||
|
|
||||||
namespace OpenTK.Platform.Windows
|
namespace OpenTK.Platform.Windows
|
||||||
{
|
{
|
||||||
class XInputJoystick : IGamePadDriver, IDisposable
|
class XInputJoystick : IJoystickDriver2, IDisposable
|
||||||
{
|
{
|
||||||
|
// All XInput devices use the same Guid
|
||||||
|
// (only one GamePadConfiguration entry required)
|
||||||
|
static readonly Guid guid =
|
||||||
|
new Guid("78696e70757400000000000000000000"); // equiv. to "xinput"
|
||||||
|
|
||||||
XInput xinput = new XInput();
|
XInput xinput = new XInput();
|
||||||
|
|
||||||
#region IGamePadDriver Members
|
#region IJoystickDriver2 Members
|
||||||
|
|
||||||
public GamePadState GetState(int index)
|
public JoystickState GetState(int index)
|
||||||
{
|
{
|
||||||
XInputState xstate;
|
XInputState xstate;
|
||||||
XInputErrorCode error = xinput.GetState((XInputUserIndex)index, out xstate);
|
XInputErrorCode error = xinput.GetState((XInputUserIndex)index, out xstate);
|
||||||
|
|
||||||
GamePadState state = new GamePadState();
|
JoystickState state = new JoystickState();
|
||||||
if (error == XInputErrorCode.Success)
|
if (error == XInputErrorCode.Success)
|
||||||
{
|
{
|
||||||
state.SetConnected(true);
|
state.SetIsConnected(true);
|
||||||
|
|
||||||
state.SetAxis(GamePadAxes.LeftX, xstate.GamePad.ThumbLX);
|
state.SetAxis(JoystickAxis.Axis0, (short)xstate.GamePad.ThumbLX);
|
||||||
state.SetAxis(GamePadAxes.LeftY, xstate.GamePad.ThumbLY);
|
state.SetAxis(JoystickAxis.Axis1, (short)(-xstate.GamePad.ThumbLY));
|
||||||
state.SetAxis(GamePadAxes.RightX, xstate.GamePad.ThumbRX);
|
state.SetAxis(JoystickAxis.Axis2, (short)Common.HidHelper.ScaleValue(xstate.GamePad.LeftTrigger, 0, byte.MaxValue, short.MinValue, short.MaxValue));
|
||||||
state.SetAxis(GamePadAxes.RightY, xstate.GamePad.ThumbRY);
|
state.SetAxis(JoystickAxis.Axis3, (short)xstate.GamePad.ThumbRX);
|
||||||
|
state.SetAxis(JoystickAxis.Axis4, (short)(-xstate.GamePad.ThumbRY));
|
||||||
|
state.SetAxis(JoystickAxis.Axis5, (short)Common.HidHelper.ScaleValue(xstate.GamePad.RightTrigger, 0, byte.MaxValue, short.MinValue, short.MaxValue));
|
||||||
|
|
||||||
state.SetTriggers(xstate.GamePad.LeftTrigger, xstate.GamePad.RightTrigger);
|
state.SetButton(JoystickButton.Button0, (xstate.GamePad.Buttons & XInputButtons.A) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button1, (xstate.GamePad.Buttons & XInputButtons.B) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button2, (xstate.GamePad.Buttons & XInputButtons.X) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button3, (xstate.GamePad.Buttons & XInputButtons.Y) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button4, (xstate.GamePad.Buttons & XInputButtons.LeftShoulder) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button5, (xstate.GamePad.Buttons & XInputButtons.RightShoulder) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button6, (xstate.GamePad.Buttons & XInputButtons.Back) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button7, (xstate.GamePad.Buttons & XInputButtons.Start) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button8, (xstate.GamePad.Buttons & XInputButtons.LeftThumb) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button9, (xstate.GamePad.Buttons & XInputButtons.RightThumb) != 0);
|
||||||
|
state.SetButton(JoystickButton.Button10, (xstate.GamePad.Buttons & XInputButtons.Guide) != 0);
|
||||||
|
|
||||||
state.SetButton(TranslateButtons(xstate.GamePad.Buttons), true);
|
state.SetHat(JoystickHat.Hat0, new JoystickHatState(TranslateHat(xstate.GamePad.Buttons)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GamePadCapabilities GetCapabilities(int index)
|
private HatPosition TranslateHat(XInputButtons buttons)
|
||||||
|
{
|
||||||
|
XInputButtons dir = 0;
|
||||||
|
|
||||||
|
dir =XInputButtons.DPadUp | XInputButtons.DPadLeft;
|
||||||
|
if ((buttons & dir) == dir)
|
||||||
|
return HatPosition.UpLeft;
|
||||||
|
dir = XInputButtons.DPadUp | XInputButtons.DPadRight;
|
||||||
|
if ((buttons & dir) == dir)
|
||||||
|
return HatPosition.UpRight;
|
||||||
|
dir = XInputButtons.DPadDown | XInputButtons.DPadLeft;
|
||||||
|
if ((buttons & dir) == dir)
|
||||||
|
return HatPosition.DownLeft;
|
||||||
|
dir = XInputButtons.DPadDown | XInputButtons.DPadRight;
|
||||||
|
if ((buttons & dir) == dir)
|
||||||
|
return HatPosition.DownRight;
|
||||||
|
|
||||||
|
dir = XInputButtons.DPadUp;
|
||||||
|
if ((buttons & dir) == dir)
|
||||||
|
return HatPosition.Up;
|
||||||
|
dir = XInputButtons.DPadRight;
|
||||||
|
if ((buttons & dir) == dir)
|
||||||
|
return HatPosition.Right;
|
||||||
|
dir = XInputButtons.DPadDown;
|
||||||
|
if ((buttons & dir) == dir)
|
||||||
|
return HatPosition.Down;
|
||||||
|
dir = XInputButtons.DPadLeft;
|
||||||
|
if ((buttons & dir) == dir)
|
||||||
|
return HatPosition.Left;
|
||||||
|
|
||||||
|
return HatPosition.Centered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JoystickCapabilities GetCapabilities(int index)
|
||||||
{
|
{
|
||||||
XInputDeviceCapabilities xcaps;
|
XInputDeviceCapabilities xcaps;
|
||||||
XInputErrorCode error = xinput.GetCapabilities(
|
XInputErrorCode error = xinput.GetCapabilities(
|
||||||
|
@ -76,13 +126,13 @@ namespace OpenTK.Platform.Windows
|
||||||
|
|
||||||
if (error == XInputErrorCode.Success)
|
if (error == XInputErrorCode.Success)
|
||||||
{
|
{
|
||||||
GamePadType type = TranslateSubType(xcaps.SubType);
|
//GamePadType type = TranslateSubType(xcaps.SubType);
|
||||||
Buttons buttons = TranslateButtons(xcaps.GamePad.Buttons);
|
int buttons = TranslateButtons(xcaps.GamePad.Buttons);
|
||||||
GamePadAxes axes = TranslateAxes(ref xcaps.GamePad);
|
int axes = TranslateAxes(ref xcaps.GamePad);
|
||||||
|
|
||||||
return new GamePadCapabilities(type, axes, buttons, true);
|
return new JoystickCapabilities(axes, buttons, 0, true);
|
||||||
}
|
}
|
||||||
return new GamePadCapabilities();
|
return new JoystickCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetName(int index)
|
public string GetName(int index)
|
||||||
|
@ -90,47 +140,53 @@ namespace OpenTK.Platform.Windows
|
||||||
return String.Empty;
|
return String.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid GetGuid(int index)
|
||||||
|
{
|
||||||
|
return guid;
|
||||||
|
}
|
||||||
|
|
||||||
public bool SetVibration(int index, float left, float right)
|
public bool SetVibration(int index, float left, float right)
|
||||||
{
|
{
|
||||||
return false;
|
left = MathHelper.Clamp(left, 0.0f, 1.0f);
|
||||||
|
right = MathHelper.Clamp(right, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
XInputVibration vibration = new XInputVibration(
|
||||||
|
(ushort)(left * UInt16.MaxValue),
|
||||||
|
(ushort)(right * UInt16.MaxValue));
|
||||||
|
|
||||||
|
return xinput.SetState((XInputUserIndex)index, ref vibration) == XInputErrorCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Members
|
#region Private Members
|
||||||
GamePadAxes TranslateAxes(ref XInputGamePad pad)
|
|
||||||
|
int TranslateAxes(ref XInputGamePad pad)
|
||||||
{
|
{
|
||||||
GamePadAxes axes = 0;
|
int count = 0;
|
||||||
axes |= pad.ThumbLX != 0 ? GamePadAxes.LeftX : 0;
|
count += pad.ThumbLX != 0 ? 1 : 0;
|
||||||
axes |= pad.ThumbLY != 0 ? GamePadAxes.LeftY : 0;
|
count += pad.ThumbLY != 0 ? 1 : 0;
|
||||||
axes |= pad.LeftTrigger != 0 ? GamePadAxes.LeftTrigger : 0;
|
count += pad.ThumbRX != 0 ? 1 : 0;
|
||||||
axes |= pad.ThumbRX != 0 ? GamePadAxes.RightX : 0;
|
count += pad.ThumbRY != 0 ? 1 : 0;
|
||||||
axes |= pad.ThumbRY != 0 ? GamePadAxes.RightY : 0;
|
count += pad.LeftTrigger != 0 ? 1 : 0;
|
||||||
axes |= pad.RightTrigger != 0 ? GamePadAxes.RightTrigger : 0;
|
count += pad.RightTrigger != 0 ? 1 : 0;
|
||||||
return axes;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
Buttons TranslateButtons(XInputButtons xbuttons)
|
int NumberOfSetBits(int i)
|
||||||
{
|
{
|
||||||
Buttons buttons = 0;
|
i = i - ((i >> 1) & 0x55555555);
|
||||||
buttons |= (xbuttons & XInputButtons.A) != 0 ? Buttons.A : 0;
|
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
|
||||||
buttons |= (xbuttons & XInputButtons.B) != 0 ? Buttons.B : 0;
|
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
|
||||||
buttons |= (xbuttons & XInputButtons.X) != 0 ? Buttons.X : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.Y) != 0 ? Buttons.Y : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.Start) != 0 ? Buttons.Start : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.Back) != 0 ? Buttons.Back : 0;
|
|
||||||
//buttons |= (xbuttons & XInputButtons.BigButton) != 0 ? Buttons.BigButton : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.LeftShoulder) != 0 ? Buttons.LeftShoulder : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.RightShoulder) != 0 ? Buttons.RightShoulder : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.LeftThumb) != 0 ? Buttons.LeftStick : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.RightThumb) != 0 ? Buttons.RightStick : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.DPadDown) != 0 ? Buttons.DPadDown : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.DPadUp) != 0 ? Buttons.DPadUp : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.DPadLeft) != 0 ? Buttons.DPadLeft : 0;
|
|
||||||
buttons |= (xbuttons & XInputButtons.DPadRight) != 0 ? Buttons.DPadRight : 0;
|
|
||||||
return buttons;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TranslateButtons(XInputButtons xbuttons)
|
||||||
|
{
|
||||||
|
return NumberOfSetBits((int)xbuttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if false
|
||||||
|
// Todo: Implement JoystickType enumeration
|
||||||
GamePadType TranslateSubType(XInputDeviceSubType xtype)
|
GamePadType TranslateSubType(XInputDeviceSubType xtype)
|
||||||
{
|
{
|
||||||
switch (xtype)
|
switch (xtype)
|
||||||
|
@ -150,6 +206,7 @@ namespace OpenTK.Platform.Windows
|
||||||
return GamePadType.Unknown;
|
return GamePadType.Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
enum XInputErrorCode
|
enum XInputErrorCode
|
||||||
{
|
{
|
||||||
|
@ -198,6 +255,7 @@ namespace OpenTK.Platform.Windows
|
||||||
RightThumb = 0x0080,
|
RightThumb = 0x0080,
|
||||||
LeftShoulder = 0x0100,
|
LeftShoulder = 0x0100,
|
||||||
RightShoulder = 0x0200,
|
RightShoulder = 0x0200,
|
||||||
|
Guide = 0x0400, // Undocumented, requires XInputGetStateEx + XINPUT_1_3.dll or higher
|
||||||
A = 0x1000,
|
A = 0x1000,
|
||||||
B = 0x2000,
|
B = 0x2000,
|
||||||
X = 0x4000,
|
X = 0x4000,
|
||||||
|
@ -265,8 +323,14 @@ namespace OpenTK.Platform.Windows
|
||||||
|
|
||||||
struct XInputVibration
|
struct XInputVibration
|
||||||
{
|
{
|
||||||
public short LeftMotorSpeed;
|
public ushort LeftMotorSpeed;
|
||||||
public short RightMotorSpeed;
|
public ushort RightMotorSpeed;
|
||||||
|
|
||||||
|
public XInputVibration(ushort left, ushort right)
|
||||||
|
{
|
||||||
|
LeftMotorSpeed = left;
|
||||||
|
RightMotorSpeed = right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct XInputDeviceCapabilities
|
struct XInputDeviceCapabilities
|
||||||
|
@ -306,7 +370,11 @@ namespace OpenTK.Platform.Windows
|
||||||
|
|
||||||
// Load the entry points we are interested in from that dll
|
// Load the entry points we are interested in from that dll
|
||||||
GetCapabilities = (XInputGetCapabilities)Load("XInputGetCapabilities", typeof(XInputGetCapabilities));
|
GetCapabilities = (XInputGetCapabilities)Load("XInputGetCapabilities", typeof(XInputGetCapabilities));
|
||||||
GetState = (XInputGetState)Load("XInputGetState", typeof(XInputGetState));
|
GetState =
|
||||||
|
// undocumented XInputGetStateEx with support for the "Guide" button (requires XINPUT_1_3+)
|
||||||
|
(XInputGetState)Load("XInputGetStateEx", typeof(XInputGetState)) ??
|
||||||
|
// documented XInputGetState (no support for the "Guide" button)
|
||||||
|
(XInputGetState)Load("XInputGetState", typeof(XInputGetState));
|
||||||
SetState = (XInputSetState)Load("XInputSetState", typeof(XInputSetState));
|
SetState = (XInputSetState)Load("XInputSetState", typeof(XInputSetState));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue