diff --git a/Source/Examples/Main.cs b/Source/Examples/Main.cs
index 84dba6d7..8e68e09f 100644
--- a/Source/Examples/Main.cs
+++ b/Source/Examples/Main.cs
@@ -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)
{
diff --git a/Source/OpenTK/Input/GamePadButtons.cs b/Source/OpenTK/Input/GamePadButtons.cs
index 676fb1f9..3f866e89 100644
--- a/Source/OpenTK/Input/GamePadButtons.cs
+++ b/Source/OpenTK/Input/GamePadButtons.cs
@@ -160,7 +160,31 @@ namespace OpenTK.Input
/// A that represents the current .
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();
}
///
diff --git a/Source/OpenTK/Input/GamePadCapabilities.cs b/Source/OpenTK/Input/GamePadCapabilities.cs
index 269081e8..9947d835 100644
--- a/Source/OpenTK/Input/GamePadCapabilities.cs
+++ b/Source/OpenTK/Input/GamePadCapabilities.cs
@@ -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; }
}
+ ///
+ /// Gets a value describing whether a valid button configuration
+ /// exists for this GamePad in the GamePad configuration database.
+ ///
+ public bool IsMapped
+ {
+ get { return is_mapped; }
+ }
+
/// A structure to test for equality.
/// A structure to test for equality.
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");
}
///
@@ -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;
}
diff --git a/Source/OpenTK/Input/GamePadConfigurationDatabase.cs b/Source/OpenTK/Input/GamePadConfigurationDatabase.cs
index 292cd10b..098435b9 100644
--- a/Source/OpenTK/Input/GamePadConfigurationDatabase.cs
+++ b/Source/OpenTK/Input/GamePadConfigurationDatabase.cs
@@ -34,6 +34,8 @@ namespace OpenTK.Input
{
class GamePadConfigurationDatabase
{
+ internal const string UnmappedName = "Unmapped Controller";
+
readonly Dictionary Configurations = new Dictionary();
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,");
diff --git a/Source/OpenTK/Input/GamePadState.cs b/Source/OpenTK/Input/GamePadState.cs
index 7d0f0630..2ab66fe0 100644
--- a/Source/OpenTK/Input/GamePadState.cs
+++ b/Source/OpenTK/Input/GamePadState.cs
@@ -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);
}
///
diff --git a/Source/OpenTK/Input/GamePadTriggers.cs b/Source/OpenTK/Input/GamePadTriggers.cs
index adbc1d16..da93c4e6 100644
--- a/Source/OpenTK/Input/GamePadTriggers.cs
+++ b/Source/OpenTK/Input/GamePadTriggers.cs
@@ -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);
}
diff --git a/Source/OpenTK/Input/JoystickCapabilities.cs b/Source/OpenTK/Input/JoystickCapabilities.cs
index 1e5f88ab..89b027d2 100644
--- a/Source/OpenTK/Input/JoystickCapabilities.cs
+++ b/Source/OpenTK/Input/JoystickCapabilities.cs
@@ -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
///
@@ -95,6 +112,7 @@ namespace OpenTK.Input
public bool IsConnected
{
get { return is_connected; }
+ private set { is_connected = value; }
}
///
@@ -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);
}
diff --git a/Source/OpenTK/Input/JoystickState.cs b/Source/OpenTK/Input/JoystickState.cs
index a2dd64cc..1ecb562b 100644
--- a/Source/OpenTK/Input/JoystickState.cs
+++ b/Source/OpenTK/Input/JoystickState.cs
@@ -223,6 +223,11 @@ namespace OpenTK.Input
}
}
+ internal void ClearButtons()
+ {
+ buttons = 0;
+ }
+
internal void SetButton(JoystickButton button, bool value)
{
int index = (int)button;
diff --git a/Source/OpenTK/OpenTK.csproj b/Source/OpenTK/OpenTK.csproj
index cd3df5fe..ef234f47 100644
--- a/Source/OpenTK/OpenTK.csproj
+++ b/Source/OpenTK/OpenTK.csproj
@@ -162,11 +162,14 @@
+
+
+
@@ -325,9 +328,6 @@
Code
-
- Code
-
Code
diff --git a/Source/OpenTK/Platform/Common/Hid.cs b/Source/OpenTK/Platform/Common/Hid.cs
new file mode 100644
index 00000000..bf33d463
--- /dev/null
+++ b/Source/OpenTK/Platform/Common/Hid.cs
@@ -0,0 +1,270 @@
+#region License
+//
+// Hid.cs
+//
+// Author:
+// Stefanos A.
+//
+// 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
+ {
+ ///
+ /// Scales the specified value linearly between min and max.
+ ///
+ /// The value to scale
+ /// The minimum expected value (inclusive)
+ /// The maximum expected value (inclusive)
+ /// The minimum output value (inclusive)
+ /// The maximum output value (inclusive)
+ /// The value, scaled linearly between min and max
+ 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
+ }
+}
diff --git a/Source/OpenTK/Platform/DeviceCollection.cs b/Source/OpenTK/Platform/DeviceCollection.cs
index 0c70f356..04ac1015 100644
--- a/Source/OpenTK/Platform/DeviceCollection.cs
+++ b/Source/OpenTK/Platform/DeviceCollection.cs
@@ -41,7 +41,7 @@ namespace OpenTK.Platform
// that is added.
class DeviceCollection : IEnumerable
{
- readonly Dictionary Map = new Dictionary();
+ readonly Dictionary Map = new Dictionary();
readonly List Devices = new List();
#region IEnumerable Members
@@ -64,7 +64,15 @@ namespace OpenTK.Platform
#region Public Members
- public void Add(int id, T device)
+ /// \internal
+ ///
+ /// 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.
+ ///
+ /// The hardware id for the device.
+ /// The device instance.
+ 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))
{
diff --git a/Source/OpenTK/Platform/IPlatformFactory.cs b/Source/OpenTK/Platform/IPlatformFactory.cs
index a126c13d..b42e1ab4 100644
--- a/Source/OpenTK/Platform/IPlatformFactory.cs
+++ b/Source/OpenTK/Platform/IPlatformFactory.cs
@@ -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();
diff --git a/Source/OpenTK/Platform/MacOS/HIDInput.cs b/Source/OpenTK/Platform/MacOS/HIDInput.cs
index 4c3e4541..e4a4614d 100755
--- a/Source/OpenTK/Platform/MacOS/HIDInput.cs
+++ b/Source/OpenTK/Platform/MacOS/HIDInput.cs
@@ -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) */
diff --git a/Source/OpenTK/Platform/MappedGamePadDriver.cs b/Source/OpenTK/Platform/MappedGamePadDriver.cs
index 5d55b779..aab0f3e5 100644
--- a/Source/OpenTK/Platform/MappedGamePadDriver.cs
+++ b/Source/OpenTK/Platform/MappedGamePadDriver.cs
@@ -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
{
diff --git a/Source/OpenTK/Platform/SDL2/Sdl2.cs b/Source/OpenTK/Platform/SDL2/Sdl2.cs
index a987c3bf..4690e7d9 100644
--- a/Source/OpenTK/Platform/SDL2/Sdl2.cs
+++ b/Source/OpenTK/Platform/SDL2/Sdl2.cs
@@ -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);
}
}
diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs
index d8fa2955..05d5e73c 100644
--- a/Source/OpenTK/Platform/Windows/API.cs
+++ b/Source/OpenTK/Platform/Windows/API.cs
@@ -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.
+//
+// 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
/// 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.
/// Call GetLastError to identify any other errors.
///
- [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
+ );
+
///
/// Gets information about the raw input device.
///
@@ -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
///
/// Top level collection Usage page for the raw input device.
///
- //internal USHORT UsagePage;
- internal SHORT UsagePage;
+ internal HIDPage UsagePage;
///
/// Top level collection Usage for the raw input device.
///
@@ -2443,6 +2509,22 @@ namespace OpenTK.Platform.Windows
///
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.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.Stride;
}
#endregion
@@ -2537,6 +2625,9 @@ namespace OpenTK.Platform.Windows
/// Value passed in the wParam parameter of the WM_INPUT message.
///
internal WPARAM Param;
+
+ public static readonly int SizeInBytes =
+ BlittableValueType.Stride;
}
#endregion
@@ -2715,16 +2806,31 @@ namespace OpenTK.Platform.Windows
///
/// Size, in bytes, of each HID input in bRawData.
///
- internal DWORD SizeHid;
+ internal DWORD Size;
///
/// Number of HID inputs in bRawData.
///
internal DWORD Count;
- // The RawData field must be marshalled manually.
- /////
- ///// Raw input data as an array of bytes.
- /////
- //internal IntPtr RawData;
+ ///
+ /// Raw input data as an array of bytes.
+ ///
+ 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
///
/// Size, in bytes, of the RawInputDeviceInfo structure.
///
- DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo));
+ internal DWORD Size = Marshal.SizeOf(typeof(RawInputDeviceInfo));
///
/// Type of raw input data.
///
diff --git a/Source/OpenTK/Platform/Windows/Bindings/HidProtocol.cs b/Source/OpenTK/Platform/Windows/Bindings/HidProtocol.cs
new file mode 100644
index 00000000..a5418ad5
--- /dev/null
+++ b/Source/OpenTK/Platform/Windows/Bindings/HidProtocol.cs
@@ -0,0 +1,288 @@
+#region License
+//
+// HidProtocol.cs
+//
+// Author:
+// Stefanos A.
+//
+// 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;
+ }
+}
diff --git a/Source/OpenTK/Platform/Windows/WinFactory.cs b/Source/OpenTK/Platform/Windows/WinFactory.cs
index 877377ab..d09b4de2 100644
--- a/Source/OpenTK/Platform/Windows/WinFactory.cs
+++ b/Source/OpenTK/Platform/Windows/WinFactory.cs
@@ -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);
diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs
index eb348c89..10460dd0 100644
--- a/Source/OpenTK/Platform/Windows/WinGLNative.cs
+++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs
@@ -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);
diff --git a/Source/OpenTK/Platform/Windows/WinInputBase.cs b/Source/OpenTK/Platform/Windows/WinInputBase.cs
index 7d0865f1..701abd02 100644
--- a/Source/OpenTK/Platform/Windows/WinInputBase.cs
+++ b/Source/OpenTK/Platform/Windows/WinInputBase.cs
@@ -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; }
diff --git a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs b/Source/OpenTK/Platform/Windows/WinMMJoystick.cs
deleted file mode 100644
index ff3e6351..00000000
--- a/Source/OpenTK/Platform/Windows/WinMMJoystick.cs
+++ /dev/null
@@ -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 sticks = new List();
-
- // Matches a WinMM device index to a specific stick above
- readonly Dictionary index_to_stick =
- new Dictionary();
- // Matches a player index to a WinMM device index
- readonly Dictionary player_to_index =
- new Dictionary();
-
- // 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 OpenJoystick(int number)
- {
- lock (sync)
- {
- JoystickDevice 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(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 stick =
- sticks[index_to_stick[device_index]] as JoystickDevice;
-
- 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 stick =
- sticks[index_to_stick[device_index]] as JoystickDevice;
-
- 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 stick =
- sticks[index] as JoystickDevice;
-
- // 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
- }
-}
diff --git a/Source/OpenTK/Platform/Windows/WinRawInput.cs b/Source/OpenTK/Platform/Windows/WinRawInput.cs
index 0e99bc96..4575153e 100644
--- a/Source/OpenTK/Platform/Windows/WinRawInput.cs
+++ b/Source/OpenTK/Platform/Windows/WinRawInput.cs
@@ -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; }
diff --git a/Source/OpenTK/Platform/Windows/WinRawJoystick.cs b/Source/OpenTK/Platform/Windows/WinRawJoystick.cs
new file mode 100644
index 00000000..9d8bb48a
--- /dev/null
+++ b/Source/OpenTK/Platform/Windows/WinRawJoystick.cs
@@ -0,0 +1,830 @@
+#region License
+//
+// WinRawJoystick.cs
+//
+// Author:
+// Stefanos A.
+//
+// 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 AxisCaps =
+ new List();
+ internal readonly List ButtonCaps =
+ new List();
+ internal readonly bool IsXInput;
+ internal readonly int XInputIndex;
+
+ readonly Dictionary axes =
+ new Dictionary();
+ readonly Dictionary buttons =
+ new Dictionary();
+ readonly Dictionary hats =
+ new Dictionary();
+
+ #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 Devices = new DeviceCollection();
+
+ 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
+ }
+}
diff --git a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs
index fcfc4a6b..223f68be 100644
--- a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs
+++ b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs
@@ -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))
{
diff --git a/Source/OpenTK/Platform/Windows/WinRawMouse.cs b/Source/OpenTK/Platform/Windows/WinRawMouse.cs
index c942a36f..b1e0d447 100644
--- a/Source/OpenTK/Platform/Windows/WinRawMouse.cs
+++ b/Source/OpenTK/Platform/Windows/WinRawMouse.cs
@@ -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))
{
diff --git a/Source/OpenTK/Platform/Windows/XInputJoystick.cs b/Source/OpenTK/Platform/Windows/XInputJoystick.cs
index ef38e087..2e21adfe 100644
--- a/Source/OpenTK/Platform/Windows/XInputJoystick.cs
+++ b/Source/OpenTK/Platform/Windows/XInputJoystick.cs
@@ -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));
}