mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-01-11 07:15:28 +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)
|
||||
{
|
||||
Trace.Listeners.Add(new ConsoleTraceListener());
|
||||
Tests.GameWindowStates.Main();
|
||||
return;
|
||||
|
||||
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>
|
||||
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>
|
||||
|
|
|
@ -40,16 +40,18 @@ namespace OpenTK.Input
|
|||
GamePadAxes axes;
|
||||
byte gamepad_type;
|
||||
bool is_connected;
|
||||
bool is_mapped;
|
||||
|
||||
#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()
|
||||
{
|
||||
gamepad_type = (byte)type;
|
||||
this.axes = axes;
|
||||
this.buttons = buttons;
|
||||
this.is_connected = is_connected;
|
||||
this.is_mapped = is_mapped;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -317,6 +319,15 @@ namespace OpenTK.Input
|
|||
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="right">A <see cref="GamePadCapabilities"/> structure to test for equality.</param>
|
||||
public static bool operator ==(GamePadCapabilities left, GamePadCapabilities right)
|
||||
|
@ -338,11 +349,12 @@ namespace OpenTK.Input
|
|||
public override string ToString()
|
||||
{
|
||||
return String.Format(
|
||||
"{{Type: {0}; Axes: {1}; Buttons: {2}; Connected: {3}}}",
|
||||
"{{Type: {0}; Axes: {1}; Buttons: {2}; {3}; {4}}}",
|
||||
GamePadType,
|
||||
Convert.ToString((int)axes, 2),
|
||||
Convert.ToString((int)buttons, 2),
|
||||
IsConnected);
|
||||
IsMapped ? "Mapped" : "Unmapped",
|
||||
IsConnected ? "Connected" : "Disconnected");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -355,6 +367,7 @@ namespace OpenTK.Input
|
|||
return
|
||||
buttons.GetHashCode() ^
|
||||
is_connected.GetHashCode() ^
|
||||
is_mapped.GetHashCode() ^
|
||||
gamepad_type.GetHashCode();
|
||||
}
|
||||
|
||||
|
@ -386,6 +399,7 @@ namespace OpenTK.Input
|
|||
return
|
||||
buttons == other.buttons &&
|
||||
is_connected == other.is_connected &&
|
||||
is_mapped == other.is_mapped &&
|
||||
gamepad_type == other.gamepad_type;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ namespace OpenTK.Input
|
|||
{
|
||||
class GamePadConfigurationDatabase
|
||||
{
|
||||
internal const string UnmappedName = "Unmapped Controller";
|
||||
|
||||
readonly Dictionary<Guid, string> Configurations = new Dictionary<Guid, string>();
|
||||
|
||||
internal GamePadConfigurationDatabase()
|
||||
|
@ -61,8 +63,19 @@ namespace OpenTK.Input
|
|||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
#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
|
||||
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
|
||||
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("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("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("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,");
|
||||
|
|
|
@ -110,8 +110,8 @@ namespace OpenTK.Input
|
|||
public override string ToString()
|
||||
{
|
||||
return String.Format(
|
||||
"{{Sticks: {0}; Buttons: {1}; DPad: {2}; IsConnected: {3}}}",
|
||||
ThumbSticks, Buttons, DPad, IsConnected);
|
||||
"{{Sticks: {0}; Triggers: {1}; Buttons: {2}; DPad: {3}; IsConnected: {4}}}",
|
||||
ThumbSticks, Triggers, Buttons, DPad, IsConnected);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -86,7 +86,7 @@ namespace OpenTK.Input
|
|||
public override string ToString()
|
||||
{
|
||||
return String.Format(
|
||||
"{{Left: {0:f2}; Right: {1:f2}}}",
|
||||
"({0:f2}; {1:f2})",
|
||||
Left, Right);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenTK.Input
|
||||
|
@ -48,11 +49,18 @@ namespace OpenTK.Input
|
|||
internal JoystickCapabilities(int axis_count, int button_count, int hat_count, bool is_connected)
|
||||
{
|
||||
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)
|
||||
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)
|
||||
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.button_count = (byte)button_count;
|
||||
|
@ -62,6 +70,15 @@ namespace OpenTK.Input
|
|||
|
||||
#endregion
|
||||
|
||||
#region Internal Members
|
||||
|
||||
internal void SetIsConnected(bool value)
|
||||
{
|
||||
is_connected = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Members
|
||||
|
||||
/// <summary>
|
||||
|
@ -95,6 +112,7 @@ namespace OpenTK.Input
|
|||
public bool IsConnected
|
||||
{
|
||||
get { return is_connected; }
|
||||
private set { is_connected = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -104,7 +122,7 @@ namespace OpenTK.Input
|
|||
public override string ToString()
|
||||
{
|
||||
return String.Format(
|
||||
"{{Axes: {0}; Buttons: {1}; Hats: {2}; IsConnected: {2}}}",
|
||||
"{{Axes: {0}; Buttons: {1}; Hats: {2}; IsConnected: {3}}}",
|
||||
AxisCount, ButtonCount, HatCount, IsConnected);
|
||||
}
|
||||
|
||||
|
|
|
@ -223,6 +223,11 @@ namespace OpenTK.Input
|
|||
}
|
||||
}
|
||||
|
||||
internal void ClearButtons()
|
||||
{
|
||||
buttons = 0;
|
||||
}
|
||||
|
||||
internal void SetButton(JoystickButton button, bool value)
|
||||
{
|
||||
int index = (int)button;
|
||||
|
|
|
@ -162,11 +162,14 @@
|
|||
<Compile Include="Math\Matrix4x2d.cs" />
|
||||
<Compile Include="Math\Matrix4x3.cs" />
|
||||
<Compile Include="Math\Matrix4x3d.cs" />
|
||||
<Compile Include="Platform\Common\Hid.cs" />
|
||||
<Compile Include="Platform\DisplayDeviceBase.cs" />
|
||||
<Compile Include="Platform\Egl\EglUnixContext.cs" />
|
||||
<Compile Include="Platform\Egl\EglWinContext.cs" />
|
||||
<Compile Include="Platform\MappedGamePadDriver.cs" />
|
||||
<Compile Include="Platform\Windows\Bindings\HidProtocol.cs" />
|
||||
<Compile Include="Platform\Windows\WinInputBase.cs" />
|
||||
<Compile Include="Platform\Windows\WinRawJoystick.cs" />
|
||||
<Compile Include="Platform\Windows\XInputJoystick.cs" />
|
||||
<Compile Include="Platform\X11\Bindings\DL.cs" />
|
||||
<Compile Include="Platform\X11\Bindings\INotify.cs" />
|
||||
|
@ -325,9 +328,6 @@
|
|||
<Compile Include="Platform\Windows\WinGLContext.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Platform\Windows\WinMMJoystick.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Platform\Windows\WglHelper.cs">
|
||||
<SubType>Code</SubType>
|
||||
</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.
|
||||
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>();
|
||||
|
||||
#region IEnumerable<T> Members
|
||||
|
@ -64,7 +64,15 @@ namespace OpenTK.Platform
|
|||
|
||||
#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))
|
||||
{
|
||||
|
@ -75,7 +83,7 @@ namespace OpenTK.Platform
|
|||
Devices[Map[id]] = device;
|
||||
}
|
||||
|
||||
public void Remove(int id)
|
||||
public void Remove(long 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))
|
||||
{
|
||||
|
@ -107,7 +115,7 @@ namespace OpenTK.Platform
|
|||
}
|
||||
}
|
||||
|
||||
public T FromHardwareId(int id)
|
||||
public T FromHardwareId(long 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);
|
||||
|
||||
GraphicsContext.GetCurrentContextDelegate CreateGetCurrentGraphicsContext();
|
||||
|
||||
|
||||
OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver();
|
||||
|
||||
OpenTK.Input.IMouseDriver2 CreateMouseDriver();
|
||||
|
|
|
@ -30,6 +30,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenTK.Input;
|
||||
using OpenTK.Platform.Common;
|
||||
|
||||
namespace OpenTK.Platform.MacOS
|
||||
{
|
||||
|
@ -755,7 +756,7 @@ namespace OpenTK.Platform.MacOS
|
|||
case HIDUsageGD.Dial:
|
||||
case HIDUsageGD.Wheel:
|
||||
short offset = GetJoystickAxis(val, elem);
|
||||
JoystickAxis axis = TranslateJoystickAxis(usage);
|
||||
JoystickAxis axis = HidHelper.TranslateJoystickAxis(page, usage);
|
||||
if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
|
||||
{
|
||||
joy.State.SetAxis(axis, offset);
|
||||
|
@ -779,7 +780,7 @@ namespace OpenTK.Platform.MacOS
|
|||
case HIDUsageSim.Rudder:
|
||||
case HIDUsageSim.Throttle:
|
||||
short offset = GetJoystickAxis(val, elem);
|
||||
JoystickAxis axis = TranslateJoystickAxis(usage);
|
||||
JoystickAxis axis = HidHelper.TranslateJoystickAxis(page, usage);
|
||||
if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
|
||||
{
|
||||
joy.State.SetAxis(axis, offset);
|
||||
|
@ -806,51 +807,7 @@ namespace OpenTK.Platform.MacOS
|
|||
int max = NativeMethods.IOHIDElementGetLogicalMax(element).ToInt32();
|
||||
int min = NativeMethods.IOHIDElementGetLogicalMin(element).ToInt32();
|
||||
int offset = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
|
||||
if (offset < min)
|
||||
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;
|
||||
}
|
||||
return (short)HidHelper.ScaleValue(offset, min, max, short.MinValue, short.MaxValue);
|
||||
}
|
||||
|
||||
static bool GetJoystickButton(IOHIDValueRef val, IOHIDElementRef element)
|
||||
|
@ -1209,169 +1166,6 @@ namespace OpenTK.Platform.MacOS
|
|||
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
|
||||
{
|
||||
Button_1 = 0x01, /* (primary/trigger) */
|
||||
|
|
|
@ -108,7 +108,12 @@ namespace OpenTK.Platform
|
|||
// Todo: if SDL2 GameController config is ever updated to
|
||||
// distinguish between negative/positive axes, then update
|
||||
// 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;
|
||||
|
||||
case ConfigurationType.Button:
|
||||
|
@ -150,7 +155,12 @@ namespace OpenTK.Platform
|
|||
// Todo: if SDL2 GameController config is ever updated to
|
||||
// distinguish between negative/positive axes, then update
|
||||
// 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;
|
||||
|
||||
case ConfigurationType.Button:
|
||||
|
@ -194,7 +204,8 @@ namespace OpenTK.Platform
|
|||
GamePadType.GamePad, // Todo: detect different types
|
||||
mapped_axes,
|
||||
mapped_buttons,
|
||||
true);
|
||||
joy.IsConnected,
|
||||
configuration.Name != GamePadConfigurationDatabase.UnmappedName);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,33 @@
|
|||
#region --- License ---
|
||||
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
|
||||
* Contributions from Erik Ylvisaker
|
||||
* See license.txt for license info
|
||||
*/
|
||||
#region License
|
||||
//
|
||||
// WinRawJoystick.cs
|
||||
//
|
||||
// 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
|
||||
|
||||
#region --- Using Directives ---
|
||||
|
||||
using System;
|
||||
#if !MINIMAL
|
||||
using System.Drawing;
|
||||
|
@ -14,8 +35,7 @@ using System.Drawing;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Security;
|
||||
|
||||
#endregion
|
||||
using OpenTK.Platform.Common;
|
||||
|
||||
/* 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 REGSAM = System.UInt32;
|
||||
using System.Diagnostics;
|
||||
|
||||
#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>Call GetLastError to identify any other errors.</para>
|
||||
/// </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]
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern INT GetRawInputDeviceInfo(
|
||||
|
@ -1499,6 +1510,16 @@ namespace OpenTK.Platform.Windows
|
|||
[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>
|
||||
/// Gets information about the raw input device.
|
||||
/// </summary>
|
||||
|
@ -1626,6 +1647,52 @@ namespace OpenTK.Platform.Windows
|
|||
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
|
||||
|
||||
#region IntPtr NextRawInputStructure(IntPtr data)
|
||||
|
@ -2424,8 +2491,7 @@ namespace OpenTK.Platform.Windows
|
|||
/// <summary>
|
||||
/// Top level collection Usage page for the raw input device.
|
||||
/// </summary>
|
||||
//internal USHORT UsagePage;
|
||||
internal SHORT UsagePage;
|
||||
internal HIDPage UsagePage;
|
||||
/// <summary>
|
||||
/// Top level collection Usage for the raw input device.
|
||||
/// </summary>
|
||||
|
@ -2443,6 +2509,22 @@ namespace OpenTK.Platform.Windows
|
|||
/// </summary>
|
||||
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()
|
||||
{
|
||||
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 RawInputData Data;
|
||||
|
||||
public static readonly int SizeInBytes =
|
||||
BlittableValueType<RawInput>.Stride;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
|
@ -2505,6 +2590,9 @@ namespace OpenTK.Platform.Windows
|
|||
internal RawKeyboard Keyboard;
|
||||
[FieldOffset(0)]
|
||||
internal RawHID HID;
|
||||
|
||||
public static readonly int SizeInBytes =
|
||||
BlittableValueType<RawInputData>.Stride;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -2537,6 +2625,9 @@ namespace OpenTK.Platform.Windows
|
|||
/// Value passed in the wParam parameter of the WM_INPUT message.
|
||||
/// </summary>
|
||||
internal WPARAM Param;
|
||||
|
||||
public static readonly int SizeInBytes =
|
||||
BlittableValueType<RawInputHeader>.Stride;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -2715,16 +2806,31 @@ namespace OpenTK.Platform.Windows
|
|||
/// <summary>
|
||||
/// Size, in bytes, of each HID input in bRawData.
|
||||
/// </summary>
|
||||
internal DWORD SizeHid;
|
||||
internal DWORD Size;
|
||||
/// <summary>
|
||||
/// Number of HID inputs in bRawData.
|
||||
/// </summary>
|
||||
internal DWORD Count;
|
||||
// The RawData field must be marshalled manually.
|
||||
///// <summary>
|
||||
///// Raw input data as an array of bytes.
|
||||
///// </summary>
|
||||
//internal IntPtr RawData;
|
||||
/// <summary>
|
||||
/// Raw input data as an array of bytes.
|
||||
/// </summary>
|
||||
internal byte 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
|
||||
|
@ -2741,7 +2847,7 @@ namespace OpenTK.Platform.Windows
|
|||
/// <summary>
|
||||
/// Size, in bytes, of the RawInputDeviceInfo structure.
|
||||
/// </summary>
|
||||
DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo));
|
||||
internal DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo));
|
||||
/// <summary>
|
||||
/// Type of raw input data.
|
||||
/// </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
|
||||
{
|
||||
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; }
|
||||
const string OpenGLName = "OPENGL32.DLL";
|
||||
|
@ -112,41 +116,45 @@ namespace OpenTK.Platform.Windows
|
|||
|
||||
public override OpenTK.Input.IKeyboardDriver2 CreateKeyboardDriver()
|
||||
{
|
||||
return InputDriver.KeyboardDriver;
|
||||
return RawInputDriver.KeyboardDriver;
|
||||
}
|
||||
|
||||
public override OpenTK.Input.IMouseDriver2 CreateMouseDriver()
|
||||
{
|
||||
return InputDriver.MouseDriver;
|
||||
return RawInputDriver.MouseDriver;
|
||||
}
|
||||
|
||||
public override OpenTK.Input.IGamePadDriver CreateGamePadDriver()
|
||||
{
|
||||
return InputDriver.GamePadDriver;
|
||||
return new MappedGamePadDriver();
|
||||
}
|
||||
|
||||
public override IJoystickDriver2 CreateJoystickDriver()
|
||||
{
|
||||
return InputDriver.JoystickDriver;
|
||||
return RawInputDriver.JoystickDriver;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
IInputDriver2 InputDriver
|
||||
#region Private Members
|
||||
|
||||
WinRawInput RawInputDriver
|
||||
{
|
||||
get
|
||||
{
|
||||
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
|
||||
|
||||
protected override void Dispose(bool manual)
|
||||
|
@ -155,7 +163,7 @@ namespace OpenTK.Platform.Windows
|
|||
{
|
||||
if (manual)
|
||||
{
|
||||
InputDriver.Dispose();
|
||||
rawinput_driver.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(manual);
|
||||
|
|
|
@ -83,7 +83,7 @@ namespace OpenTK.Platform.Windows
|
|||
const ClassStyle DefaultClassStyle = ClassStyle.OwnDC;
|
||||
|
||||
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 ShiftRightScanCode = Functions.MapVirtualKey(VirtualKeys.RSHIFT, 0);
|
||||
public static readonly uint ControlLeftScanCode = Functions.MapVirtualKey(VirtualKeys.LCONTROL, 0);
|
||||
|
|
|
@ -33,7 +33,7 @@ using OpenTK.Input;
|
|||
|
||||
namespace OpenTK.Platform.Windows
|
||||
{
|
||||
abstract class WinInputBase : IInputDriver2
|
||||
abstract class WinInputBase
|
||||
{
|
||||
#region Fields
|
||||
|
||||
|
@ -158,11 +158,11 @@ namespace OpenTK.Platform.Windows
|
|||
|
||||
#endregion
|
||||
|
||||
#region IInputDriver2 Members
|
||||
#region Public Members
|
||||
|
||||
public abstract IMouseDriver2 MouseDriver { get; }
|
||||
|
||||
public abstract IKeyboardDriver2 KeyboardDriver { get; }
|
||||
public abstract IGamePadDriver GamePadDriver { 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
|
||||
|
||||
// Input event data.
|
||||
static RawInput data = new RawInput();
|
||||
|
||||
WinRawKeyboard keyboard_driver;
|
||||
WinRawMouse mouse_driver;
|
||||
IGamePadDriver gamepad_driver;
|
||||
IJoystickDriver2 joystick_driver;
|
||||
WinRawJoystick joystick_driver;
|
||||
|
||||
IntPtr DevNotifyHandle;
|
||||
static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");
|
||||
|
@ -88,46 +86,53 @@ namespace OpenTK.Platform.Windows
|
|||
#region WindowProcedure
|
||||
|
||||
// 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)
|
||||
{
|
||||
switch (message)
|
||||
try
|
||||
{
|
||||
case WindowMessage.INPUT:
|
||||
int size = 0;
|
||||
// Get the size of the input buffer
|
||||
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)
|
||||
switch (message)
|
||||
{
|
||||
case WindowMessage.INPUT:
|
||||
{
|
||||
case RawInputDeviceType.KEYBOARD:
|
||||
if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(data))
|
||||
return IntPtr.Zero;
|
||||
break;
|
||||
// Retrieve the raw input data buffer
|
||||
RawInputHeader header;
|
||||
if (Functions.GetRawInputData(lParam, out header) == RawInputHeader.SizeInBytes)
|
||||
{
|
||||
switch (header.Type)
|
||||
{
|
||||
case RawInputDeviceType.KEYBOARD:
|
||||
if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(lParam))
|
||||
return IntPtr.Zero;
|
||||
break;
|
||||
|
||||
case RawInputDeviceType.MOUSE:
|
||||
if (((WinRawMouse)MouseDriver).ProcessMouseEvent(data))
|
||||
return IntPtr.Zero;
|
||||
break;
|
||||
case RawInputDeviceType.MOUSE:
|
||||
if (((WinRawMouse)MouseDriver).ProcessMouseEvent(lParam))
|
||||
return IntPtr.Zero;
|
||||
break;
|
||||
|
||||
case RawInputDeviceType.HID:
|
||||
break;
|
||||
case RawInputDeviceType.HID:
|
||||
if (((WinRawJoystick)JoystickDriver).ProcessEvent(lParam))
|
||||
return IntPtr.Zero;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case WindowMessage.DEVICECHANGE:
|
||||
((WinRawKeyboard)KeyboardDriver).RefreshDevices();
|
||||
((WinRawMouse)MouseDriver).RefreshDevices();
|
||||
((WinMMJoystick)JoystickDriver).RefreshDevices();
|
||||
break;
|
||||
case WindowMessage.DEVICECHANGE:
|
||||
((WinRawKeyboard)KeyboardDriver).RefreshDevices();
|
||||
((WinRawMouse)MouseDriver).RefreshDevices();
|
||||
((WinRawJoystick)JoystickDriver).RefreshDevices();
|
||||
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
|
||||
|
@ -138,17 +143,7 @@ namespace OpenTK.Platform.Windows
|
|||
{
|
||||
keyboard_driver = new WinRawKeyboard(Parent.Handle);
|
||||
mouse_driver = new WinRawMouse(Parent.Handle);
|
||||
joystick_driver = new WinMMJoystick();
|
||||
try
|
||||
{
|
||||
gamepad_driver = new XInputJoystick();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Debug.Print("[Win] XInput driver not supported, falling back to WinMM");
|
||||
gamepad_driver = new MappedGamePadDriver();
|
||||
}
|
||||
|
||||
joystick_driver = new WinRawJoystick(Parent.Handle);
|
||||
DevNotifyHandle = RegisterForDeviceNotifications(Parent);
|
||||
}
|
||||
|
||||
|
@ -181,9 +176,19 @@ namespace OpenTK.Platform.Windows
|
|||
|
||||
#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
|
||||
{
|
||||
|
@ -195,11 +200,6 @@ namespace OpenTK.Platform.Windows
|
|||
get { return mouse_driver; }
|
||||
}
|
||||
|
||||
public override IGamePadDriver GamePadDriver
|
||||
{
|
||||
get { return gamepad_driver; }
|
||||
}
|
||||
|
||||
public override IJoystickDriver2 JoystickDriver
|
||||
{
|
||||
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;
|
||||
#endif
|
||||
using OpenTK.Input;
|
||||
using OpenTK.Platform.Common;
|
||||
|
||||
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 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))
|
||||
RawInput rin;
|
||||
if (Functions.GetRawInputData(raw, out rin) > 0)
|
||||
{
|
||||
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 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;
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -230,7 +237,7 @@ namespace OpenTK.Platform.Windows
|
|||
|
||||
static string GetDeviceName(RawInputDeviceList dev)
|
||||
{
|
||||
uint size = 0;
|
||||
int size = 0;
|
||||
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
|
||||
IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)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)
|
||||
{
|
||||
RawInputDevice[] rid = new RawInputDevice[1];
|
||||
// Keyboard is 1/6 (page/id). See http://www.microsoft.com/whdc/device/input/HID_HWID.mspx
|
||||
rid[0] = new RawInputDevice();
|
||||
rid[0].UsagePage = 1;
|
||||
rid[0].Usage = 6;
|
||||
rid[0].Flags = RawInputDeviceFlags.INPUTSINK;
|
||||
rid[0].Target = window;
|
||||
RawInputDevice[] rid = new RawInputDevice[]
|
||||
{
|
||||
new RawInputDevice(HIDUsageGD.Keyboard, RawInputDeviceFlags.INPUTSINK, window)
|
||||
};
|
||||
|
||||
if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize))
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@ using System.Diagnostics;
|
|||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32;
|
||||
using OpenTK.Input;
|
||||
using OpenTK.Platform.Common;
|
||||
|
||||
namespace OpenTK.Platform.Windows
|
||||
{
|
||||
|
@ -79,14 +80,8 @@ namespace OpenTK.Platform.Windows
|
|||
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
|
||||
foreach (RawInputDeviceList dev in ridl)
|
||||
foreach (RawInputDeviceList dev in WinRawInput.GetDeviceList())
|
||||
{
|
||||
ContextHandle id = new ContextHandle(dev.Device);
|
||||
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;
|
||||
ContextHandle handle = new ContextHandle(rin.Header.Device);
|
||||
bool processed = false;
|
||||
|
||||
MouseState mouse;
|
||||
if (!rawids.ContainsKey(handle))
|
||||
RawInput rin;
|
||||
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 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;
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -257,7 +261,7 @@ namespace OpenTK.Platform.Windows
|
|||
static string GetDeviceName(RawInputDeviceList dev)
|
||||
{
|
||||
// get name size
|
||||
uint size = 0;
|
||||
int size = 0;
|
||||
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
|
||||
|
||||
// get actual name
|
||||
|
@ -296,13 +300,10 @@ namespace OpenTK.Platform.Windows
|
|||
|
||||
static void RegisterRawDevice(IntPtr window, string device)
|
||||
{
|
||||
RawInputDevice[] rid = new RawInputDevice[1];
|
||||
// Mouse is 1/2 (page/id). See http://www.microsoft.com/whdc/device/input/HID_HWID.mspx
|
||||
rid[0] = new RawInputDevice();
|
||||
rid[0].UsagePage = 1;
|
||||
rid[0].Usage = 2;
|
||||
rid[0].Flags = RawInputDeviceFlags.INPUTSINK;
|
||||
rid[0].Target = window;
|
||||
RawInputDevice[] rid = new RawInputDevice[]
|
||||
{
|
||||
new RawInputDevice(HIDUsageGD.Mouse, RawInputDeviceFlags.INPUTSINK, window)
|
||||
};
|
||||
|
||||
if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize))
|
||||
{
|
||||
|
|
|
@ -37,36 +37,86 @@ using System.Diagnostics;
|
|||
|
||||
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();
|
||||
|
||||
#region IGamePadDriver Members
|
||||
#region IJoystickDriver2 Members
|
||||
|
||||
public GamePadState GetState(int index)
|
||||
public JoystickState GetState(int index)
|
||||
{
|
||||
XInputState xstate;
|
||||
XInputErrorCode error = xinput.GetState((XInputUserIndex)index, out xstate);
|
||||
|
||||
GamePadState state = new GamePadState();
|
||||
JoystickState state = new JoystickState();
|
||||
if (error == XInputErrorCode.Success)
|
||||
{
|
||||
state.SetConnected(true);
|
||||
state.SetIsConnected(true);
|
||||
|
||||
state.SetAxis(GamePadAxes.LeftX, xstate.GamePad.ThumbLX);
|
||||
state.SetAxis(GamePadAxes.LeftY, xstate.GamePad.ThumbLY);
|
||||
state.SetAxis(GamePadAxes.RightX, xstate.GamePad.ThumbRX);
|
||||
state.SetAxis(GamePadAxes.RightY, xstate.GamePad.ThumbRY);
|
||||
state.SetAxis(JoystickAxis.Axis0, (short)xstate.GamePad.ThumbLX);
|
||||
state.SetAxis(JoystickAxis.Axis1, (short)(-xstate.GamePad.ThumbLY));
|
||||
state.SetAxis(JoystickAxis.Axis2, (short)Common.HidHelper.ScaleValue(xstate.GamePad.LeftTrigger, 0, byte.MaxValue, short.MinValue, short.MaxValue));
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
XInputErrorCode error = xinput.GetCapabilities(
|
||||
|
@ -76,13 +126,13 @@ namespace OpenTK.Platform.Windows
|
|||
|
||||
if (error == XInputErrorCode.Success)
|
||||
{
|
||||
GamePadType type = TranslateSubType(xcaps.SubType);
|
||||
Buttons buttons = TranslateButtons(xcaps.GamePad.Buttons);
|
||||
GamePadAxes axes = TranslateAxes(ref xcaps.GamePad);
|
||||
//GamePadType type = TranslateSubType(xcaps.SubType);
|
||||
int buttons = TranslateButtons(xcaps.GamePad.Buttons);
|
||||
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)
|
||||
|
@ -90,47 +140,53 @@ namespace OpenTK.Platform.Windows
|
|||
return String.Empty;
|
||||
}
|
||||
|
||||
public Guid GetGuid(int index)
|
||||
{
|
||||
return guid;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#region Private Members
|
||||
GamePadAxes TranslateAxes(ref XInputGamePad pad)
|
||||
|
||||
int TranslateAxes(ref XInputGamePad pad)
|
||||
{
|
||||
GamePadAxes axes = 0;
|
||||
axes |= pad.ThumbLX != 0 ? GamePadAxes.LeftX : 0;
|
||||
axes |= pad.ThumbLY != 0 ? GamePadAxes.LeftY : 0;
|
||||
axes |= pad.LeftTrigger != 0 ? GamePadAxes.LeftTrigger : 0;
|
||||
axes |= pad.ThumbRX != 0 ? GamePadAxes.RightX : 0;
|
||||
axes |= pad.ThumbRY != 0 ? GamePadAxes.RightY : 0;
|
||||
axes |= pad.RightTrigger != 0 ? GamePadAxes.RightTrigger : 0;
|
||||
return axes;
|
||||
int count = 0;
|
||||
count += pad.ThumbLX != 0 ? 1 : 0;
|
||||
count += pad.ThumbLY != 0 ? 1 : 0;
|
||||
count += pad.ThumbRX != 0 ? 1 : 0;
|
||||
count += pad.ThumbRY != 0 ? 1 : 0;
|
||||
count += pad.LeftTrigger != 0 ? 1 : 0;
|
||||
count += pad.RightTrigger != 0 ? 1 : 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
Buttons TranslateButtons(XInputButtons xbuttons)
|
||||
int NumberOfSetBits(int i)
|
||||
{
|
||||
Buttons buttons = 0;
|
||||
buttons |= (xbuttons & XInputButtons.A) != 0 ? Buttons.A : 0;
|
||||
buttons |= (xbuttons & XInputButtons.B) != 0 ? Buttons.B : 0;
|
||||
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;
|
||||
i = i - ((i >> 1) & 0x55555555);
|
||||
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
|
||||
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
|
||||
}
|
||||
|
||||
int TranslateButtons(XInputButtons xbuttons)
|
||||
{
|
||||
return NumberOfSetBits((int)xbuttons);
|
||||
}
|
||||
|
||||
#if false
|
||||
// Todo: Implement JoystickType enumeration
|
||||
GamePadType TranslateSubType(XInputDeviceSubType xtype)
|
||||
{
|
||||
switch (xtype)
|
||||
|
@ -150,6 +206,7 @@ namespace OpenTK.Platform.Windows
|
|||
return GamePadType.Unknown;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
enum XInputErrorCode
|
||||
{
|
||||
|
@ -198,6 +255,7 @@ namespace OpenTK.Platform.Windows
|
|||
RightThumb = 0x0080,
|
||||
LeftShoulder = 0x0100,
|
||||
RightShoulder = 0x0200,
|
||||
Guide = 0x0400, // Undocumented, requires XInputGetStateEx + XINPUT_1_3.dll or higher
|
||||
A = 0x1000,
|
||||
B = 0x2000,
|
||||
X = 0x4000,
|
||||
|
@ -265,8 +323,14 @@ namespace OpenTK.Platform.Windows
|
|||
|
||||
struct XInputVibration
|
||||
{
|
||||
public short LeftMotorSpeed;
|
||||
public short RightMotorSpeed;
|
||||
public ushort LeftMotorSpeed;
|
||||
public ushort RightMotorSpeed;
|
||||
|
||||
public XInputVibration(ushort left, ushort right)
|
||||
{
|
||||
LeftMotorSpeed = left;
|
||||
RightMotorSpeed = right;
|
||||
}
|
||||
}
|
||||
|
||||
struct XInputDeviceCapabilities
|
||||
|
@ -306,7 +370,11 @@ namespace OpenTK.Platform.Windows
|
|||
|
||||
// Load the entry points we are interested in from that dll
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue