Merge branch 'joyhat' into develop

This commit is contained in:
Stefanos A 2014-02-10 10:54:22 +01:00
commit 61f0ca89b4
12 changed files with 833 additions and 237 deletions

View file

@ -0,0 +1,77 @@
#region License
//
// HatPosition.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-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;
namespace OpenTK.Input
{
/// <summary>
/// Enumerates discrete positions for a joystick hat.
/// </summary>
public enum HatPosition : byte
{
/// <summary>
/// The hat is in its centered (neutral) position
/// </summary>
Centered = 0,
/// <summary>
/// The hat is in its top position.
/// </summary>
Up,
/// <summary>
/// The hat is in its top-right position.
/// </summary>
UpRight,
/// <summary>
/// The hat is in its right position.
/// </summary>
Right,
/// <summary>
/// The hat is in its bottom-right position.
/// </summary>
DownRight,
/// <summary>
/// The hat is in its bottom position.
/// </summary>
Down,
/// <summary>
/// The hat is in its bottom-left position.
/// </summary>
DownLeft,
/// <summary>
/// The hat is in its left position.
/// </summary>
Left,
/// <summary>
/// The hat is in its top-left position.
/// </summary>
UpLeft,
}
}

View file

@ -40,21 +40,23 @@ namespace OpenTK.Input
{
byte axis_count;
byte button_count;
byte dpad_count;
byte hat_count;
bool is_connected;
#region Constructors
internal JoystickCapabilities(int axis_count, int button_count, bool is_connected)
internal JoystickCapabilities(int axis_count, int button_count, int hat_count, bool is_connected)
{
if (axis_count < 0 || axis_count >= JoystickState.MaxAxes)
if (axis_count < 0 || axis_count > JoystickState.MaxAxes)
throw new ArgumentOutOfRangeException("axis_count");
if (button_count < 0 || button_count >= JoystickState.MaxButtons)
if (button_count < 0 || button_count > JoystickState.MaxButtons)
throw new ArgumentOutOfRangeException("axis_count");
if (hat_count < 0 || hat_count > JoystickState.MaxHats)
throw new ArgumentOutOfRangeException("hat_count");
this.axis_count = (byte)axis_count;
this.button_count = (byte)button_count;
this.dpad_count = 0; // Todo: either remove dpad_count or add it as a parameter
this.hat_count = (byte)hat_count;
this.is_connected = is_connected;
}
@ -78,6 +80,14 @@ namespace OpenTK.Input
get { return button_count; }
}
/// <summary>
/// Gets the number of hats supported by this <see cref="JoystickDevice"/>.
/// </summary>
public int HatCount
{
get { return hat_count; }
}
/// <summary>
/// Gets a value indicating whether this <see cref="JoystickDevice"/> is connected.
/// </summary>
@ -94,8 +104,8 @@ namespace OpenTK.Input
public override string ToString()
{
return String.Format(
"{{Axes: {0}; Buttons: {1}; IsConnected: {2}}}",
AxisCount, ButtonCount, IsConnected);
"{{Axes: {0}; Buttons: {1}; Hats: {2}; IsConnected: {2}}}",
AxisCount, ButtonCount, HatCount, IsConnected);
}
/// <summary>
@ -108,6 +118,7 @@ namespace OpenTK.Input
return
AxisCount.GetHashCode() ^
ButtonCount.GetHashCode() ^
HatCount.GetHashCode() ^
IsConnected.GetHashCode();
}
@ -126,15 +137,6 @@ namespace OpenTK.Input
#endregion
#region Private Members
int DPadCount
{
get { return dpad_count; }
}
#endregion
#region IEquatable<JoystickCapabilities> Members
/// <summary>
@ -148,6 +150,7 @@ namespace OpenTK.Input
return
AxisCount == other.AxisCount &&
ButtonCount == other.ButtonCount &&
HatCount == other.HatCount &&
IsConnected == other.IsConnected;
}

View file

@ -0,0 +1,60 @@
#region License
//
// JoystickHat.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-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;
namespace OpenTK.Input
{
/// <summary>
/// Defines available Joystick hats.
/// </summary>
public enum JoystickHat
{
/// <summary>
/// The first hat of the Joystick device.
/// </summary>
Hat0,
/// <summary>
/// The second hat of the Joystick device.
/// </summary>
Hat1,
/// <summary>
/// The third hat of the Joystick device.
/// </summary>
Hat2,
/// <summary>
/// The fourth hat of the Joystick device.
/// </summary>
Hat3,
/// <summary>
/// The last hat of the Joystick device.
/// </summary>
Last = Hat3
}
}

View file

@ -0,0 +1,118 @@
#region License
//
// JoystickHatState.cs
//
// Author:
// Stefanos A. <stapostol@gmail.com>
//
// Copyright (c) 2006-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;
namespace OpenTK.Input
{
/// <summary>
/// Describes the state of a joystick hat.
/// </summary>
public struct JoystickHatState
{
HatPosition position;
internal JoystickHatState(HatPosition pos)
{
position = pos;
}
/// <summary>
/// Gets a <see cref="HatPosition"/> value indicating
/// the position of this hat.
/// </summary>
/// <value>The position.</value>
public HatPosition Position { get { return position; } }
/// <summary>
/// Gets a <see cref="System.Boolean"/> indicating
/// whether this hat lies in the top hemicircle.
/// </summary>
/// <value><c>true</c> if this hat lies in the top hemicircle; otherwise, <c>false</c>.</value>
public bool IsUp
{
get
{
return
Position == HatPosition.Up ||
Position == HatPosition.UpLeft ||
Position == HatPosition.UpRight;
}
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> indicating
/// whether this hat lies in the bottom hemicircle.
/// </summary>
/// <value><c>true</c> if this hat lies in the bottom hemicircle; otherwise, <c>false</c>.</value>
public bool IsDown
{
get
{
return
Position == HatPosition.Down ||
Position == HatPosition.DownLeft ||
Position == HatPosition.DownRight;
}
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> indicating
/// whether this hat lies in the left hemicircle.
/// </summary>
/// <value><c>true</c> if this hat lies in the left hemicircle; otherwise, <c>false</c>.</value>
public bool IsLeft
{
get
{
return
Position == HatPosition.Left ||
Position == HatPosition.UpLeft ||
Position == HatPosition.DownLeft;
}
}
/// <summary>
/// Gets a <see cref="System.Boolean"/> indicating
/// whether this hat lies in the right hemicircle.
/// </summary>
/// <value><c>true</c> if this hat lies in the right hemicircle; otherwise, <c>false</c>.</value>
public bool IsRight
{
get
{
return
Position == HatPosition.Right ||
Position == HatPosition.UpRight ||
Position == HatPosition.DownRight;
}
}
}
}

View file

@ -42,12 +42,17 @@ namespace OpenTK.Input
// then we'll need to increase these limits.
internal const int MaxAxes = (int)JoystickAxis.Last + 1;
internal const int MaxButtons = (int)JoystickButton.Last + 1;
internal const int MaxHats = (int)JoystickHat.Last + 1;
const float ConversionFactor = 1.0f / (short.MaxValue + 0.5f);
unsafe fixed short axes[MaxAxes];
int buttons;
int packet_number;
int buttons;
unsafe fixed short axes[MaxAxes];
JoystickHatState hat0;
JoystickHatState hat1;
JoystickHatState hat2;
JoystickHatState hat3;
bool is_connected;
#region Public Members
@ -76,6 +81,28 @@ namespace OpenTK.Input
return (buttons & (1 << (int)button)) != 0 ? ButtonState.Pressed : ButtonState.Released;
}
/// <summary>
/// Gets the hat.
/// </summary>
/// <returns>The hat.</returns>
/// <param name="hat">Hat.</param>
public JoystickHatState GetHat(JoystickHat hat)
{
switch (hat)
{
case JoystickHat.Hat0:
return hat0;
case JoystickHat.Hat1:
return hat1;
case JoystickHat.Hat2:
return hat2;
case JoystickHat.Hat3:
return hat3;
default:
return new JoystickHatState();
}
}
/// <summary>
/// Gets a value indicating whether the specified <see cref="JoystickButton"/> is currently pressed.
/// </summary>
@ -197,14 +224,38 @@ namespace OpenTK.Input
internal void SetButton(JoystickButton button, bool value)
{
int index = 1 << (int)button;
int index = (int)button;
if (index < 0 || index >= MaxButtons)
throw new ArgumentOutOfRangeException("button");
if (value)
{
buttons |= index;
buttons |= 1 << index;
}
else
{
buttons &= ~index;
buttons &= ~(1 << index);
}
}
internal void SetHat(JoystickHat hat, JoystickHatState value)
{
switch (hat)
{
case JoystickHat.Hat0:
hat0 = value;
break;
case JoystickHat.Hat1:
hat1 = value;
break;
case JoystickHat.Hat2:
hat2 = value;
break;
case JoystickHat.Hat3:
hat3 = value;
break;
default:
throw new ArgumentOutOfRangeException("hat");
}
}

View file

@ -798,6 +798,9 @@
<Compile Include="Input\GamePadConfigurationItem.cs" />
<Compile Include="Platform\LegacyJoystickDriver.cs" />
<Compile Include="Platform\PlatformFactoryBase.cs" />
<Compile Include="Input\JoystickHat.cs" />
<Compile Include="Input\HatPosition.cs" />
<Compile Include="Input\JoystickHatState.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View file

@ -73,6 +73,8 @@ namespace OpenTK.Platform.MacOS
public JoystickCapabilities Capabilities;
readonly public Dictionary<int, JoystickButton> ElementUsageToButton =
new Dictionary<int, JoystickButton>();
readonly public Dictionary<IOHIDElementRef, JoystickHat> ElementToHat =
new Dictionary<IOHIDElementRef, JoystickHat>(new IntPtrEqualityComparer());
}
readonly IOHIDManagerRef hidmanager;
@ -441,7 +443,7 @@ namespace OpenTK.Platform.MacOS
joy = new JoystickData();
int axes = 0;
int buttons = 0;
int dpads = 0;
int hats = 0;
CFStringRef name_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductKey);
string name = CF.CFStringGetCString(name_ref);
@ -449,6 +451,7 @@ namespace OpenTK.Platform.MacOS
Guid guid = CreateJoystickGuid(device, name);
List<int> button_elements = new List<int>();
List<IOHIDElementRef> hat_elements = new List<CFAllocatorRef>();
CFArray element_array = new CFArray(element_array_ref);
for (int i = 0; i < element_array.Count; i++)
{
@ -475,7 +478,8 @@ namespace OpenTK.Platform.MacOS
break;
case HIDUsageGD.Hatswitch:
dpads++;
hats++;
hat_elements.Add(element_ref);
break;
}
break;
@ -496,16 +500,40 @@ namespace OpenTK.Platform.MacOS
}
}
if (axes > JoystickState.MaxAxes)
{
Debug.Print("[Mac] JoystickAxis limit reached ({0} > {1}), please report a bug at http://www.opentk.com",
axes, JoystickState.MaxAxes);
axes = JoystickState.MaxAxes;
}
if (buttons > JoystickState.MaxButtons)
{
Debug.Print("[Mac] JoystickButton limit reached ({0} > {1}), please report a bug at http://www.opentk.com",
buttons, JoystickState.MaxButtons);
buttons = JoystickState.MaxButtons;
}
if (hats > JoystickState.MaxHats)
{
Debug.Print("[Mac] JoystickHat limit reached ({0} > {1}), please report a bug at http://www.opentk.com",
hats, JoystickState.MaxHats);
hats = JoystickState.MaxHats;
}
joy.Name = name;
joy.Guid = guid;
joy.State.SetIsConnected(true);
joy.Capabilities = new JoystickCapabilities(axes, buttons, true);
joy.Capabilities = new JoystickCapabilities(axes, buttons, hats, true);
// Map button elements to JoystickButtons
for (int button = 0; button < button_elements.Count; button++)
{
joy.ElementUsageToButton.Add(button_elements[button], JoystickButton.Button0 + button);
}
for (int hat = 0; hat < hat_elements.Count; hat++)
{
joy.ElementToHat.Add(hat_elements[hat], JoystickHat.Hat0 + hat);
}
}
CF.CFRelease(element_array_ref);
@ -608,6 +636,12 @@ namespace OpenTK.Platform.MacOS
break;
case HIDUsageGD.Hatswitch:
HatPosition position = GetJoystickHat(val, elem);
JoystickHat hat = TranslateJoystickHat(joy, elem);
if (hat >= JoystickHat.Hat0 && hat <= JoystickHat.Last)
{
joy.State.SetHat(hat, new JoystickHatState(position));
}
break;
}
break;
@ -709,6 +743,50 @@ namespace OpenTK.Platform.MacOS
return JoystickButton.Last + 1;
}
static HatPosition GetJoystickHat(IOHIDValueRef val, IOHIDElementRef element)
{
HatPosition position = HatPosition.Centered;
int max = NativeMethods.IOHIDElementGetLogicalMax(element).ToInt32();
int min = NativeMethods.IOHIDElementGetLogicalMin(element).ToInt32();
int value = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32() - min;
int range = Math.Abs(max - min + 1);
if (value >= 0)
{
if (range == 4)
{
// 4-position hat (no diagonals)
// 0 = up; 1 = right; 2 = down; 3 = left
// map to a 8-position hat (processed below)
value *= 2;
}
if (range == 8)
{
// 0 = up; 1 = up-right; 2 = right; 3 = right-down;
// 4 = down; 5 = down-left; 6 = left; 7 = up-left
// Our HatPosition enum
position = (HatPosition)value;
}
else
{
// Todo: implement support for continuous hats
}
}
return position;
}
static JoystickHat TranslateJoystickHat(JoystickData joy, IOHIDElementRef elem)
{
JoystickHat hat;
if (joy.ElementToHat.TryGetValue(elem, out hat))
{
return hat;
}
return JoystickHat.Last + 1;
}
#endregion
#endregion

View file

@ -46,6 +46,8 @@ namespace OpenTK.Platform.SDL2
public int HatCount { get; set; }
public int BallCount { get; set; }
public bool IsConnected { get; set; }
public readonly JoystickHatState[] Hat =
new JoystickHatState[JoystickState.MaxHats];
}
// For IJoystickDriver2 implementation
@ -127,6 +129,35 @@ namespace OpenTK.Platform.SDL2
return sdl_instanceid_to_joysticks.ContainsKey(instance_id);
}
OpenTK.Input.HatPosition TranslateHat(HatPosition value)
{
if ((value & HatPosition.LeftUp) == value)
return OpenTK.Input.HatPosition.UpLeft;
if ((value & HatPosition.RightUp) == value)
return OpenTK.Input.HatPosition.UpRight;
if ((value & HatPosition.LeftDown) == value)
return OpenTK.Input.HatPosition.DownLeft;
if ((value & HatPosition.RightDown) == value)
return OpenTK.Input.HatPosition.DownRight;
if ((value & HatPosition.Up) == value)
return OpenTK.Input.HatPosition.Up;
if ((value & HatPosition.Right) == value)
return OpenTK.Input.HatPosition.Right;
if ((value & HatPosition.Down) == value)
return OpenTK.Input.HatPosition.Down;
if ((value & HatPosition.Left) == value)
return OpenTK.Input.HatPosition.Left;
return OpenTK.Input.HatPosition.Centered;
}
#if USE_SDL2_GAMECONTROLLER
bool IsControllerValid(int id)
{
@ -386,7 +417,14 @@ namespace OpenTK.Platform.SDL2
{
int index = sdl_instanceid_to_joysticks[id];
JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
// Todo: map hat to an extra axis
if (ev.Hat >= 0 && ev.Hat < JoystickState.MaxHats)
{
joystick.Details.Hat[ev.Hat] = new JoystickHatState(TranslateHat(ev.Value));
}
else
{
Debug.Print("[SDL2] Hat {0} out of range [0, {1}]", ev.Hat, JoystickState.MaxHats);
}
joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1));
}
else
@ -596,6 +634,11 @@ namespace OpenTK.Platform.SDL2
state.SetButton(JoystickButton.Button0 + i, joystick.Button[i]);
}
for (int i = 0; i < joystick.Details.HatCount; i++)
{
state.SetHat(JoystickHat.Hat0 + i, joystick.Details.Hat[i]);
}
state.SetIsConnected(joystick.Details.IsConnected);
state.SetPacketNumber(joystick.Details.PacketNumber);
}
@ -613,6 +656,7 @@ namespace OpenTK.Platform.SDL2
return new JoystickCapabilities(
joystick.Axis.Count,
joystick.Button.Count,
joystick.Details.HatCount,
joystick.Details.IsConnected);
}
return new JoystickCapabilities();

View file

@ -84,7 +84,7 @@ namespace OpenTK.Platform.Windows
const ClassStyle DefaultClassStyle = ClassStyle.OwnDC;
// Used for IInputDriver implementation
WinMMJoystick joystick_driver = new WinMMJoystick();
IJoystickDriver joystick_driver = Factory.Default.CreateLegacyJoystickDriver();
KeyboardDevice keyboard = new KeyboardDevice();
MouseDevice mouse = new MouseDevice();
IList<KeyboardDevice> keyboards = new List<KeyboardDevice>(1);
@ -1403,7 +1403,8 @@ namespace OpenTK.Platform.Windows
public void Poll()
{
joystick_driver.Poll();
if (joystick_driver is WinMMJoystick)
(joystick_driver as WinMMJoystick).Poll();
}
#endregion

View file

@ -27,12 +27,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using OpenTK.Input;
using System.Security;
using Microsoft.Win32;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using Microsoft.Win32;
using OpenTK.Input;
namespace OpenTK.Platform.Windows
{
@ -40,9 +40,18 @@ namespace OpenTK.Platform.Windows
{
#region Fields
readonly object sync = new object();
List<JoystickDevice> sticks = new List<JoystickDevice>();
IList<JoystickDevice> sticks_readonly;
// Matches a WinMM device index to a specific stick above
readonly Dictionary<int, int> index_to_stick =
new Dictionary<int, int>();
// Matches a player index to a WinMM device index
readonly Dictionary<int, int> player_to_index =
new Dictionary<int, int>();
// Todo: Read the joystick name from the registry.
//static readonly string RegistryJoyConfig = @"Joystick%dConfiguration";
//static readonly string RegistryJoyName = @"Joystick%dOEMName";
@ -57,16 +66,24 @@ namespace OpenTK.Platform.Windows
public WinMMJoystick()
{
sticks_readonly = sticks.AsReadOnly();
RefreshDevices();
}
#endregion
#region Internal Members
internal void RefreshDevices()
{
for (int i = 0; i < sticks.Count; i++)
{
CloseJoystick(i);
}
// WinMM supports up to 16 joysticks.
int number = 0;
while (number < UnsafeNativeMethods.joyGetNumDevs())
for (int i = 0; i < UnsafeNativeMethods.joyGetNumDevs(); i++)
{
JoystickDevice<WinMMJoyDetails> stick = OpenJoystick(number++);
if (stick != null)
{
sticks.Add(stick);
}
OpenJoystick(i);
}
}
@ -76,76 +93,123 @@ namespace OpenTK.Platform.Windows
JoystickDevice<WinMMJoyDetails> OpenJoystick(int number)
{
JoystickDevice<WinMMJoyDetails> stick = null;
JoyCaps caps;
JoystickError result = UnsafeNativeMethods.joyGetDevCaps(number, out caps, JoyCaps.SizeInBytes);
if (result != JoystickError.NoError)
return null;
int num_axes = caps.NumAxes;
if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0)
num_axes += 2;
stick = new JoystickDevice<WinMMJoyDetails>(number, num_axes, caps.NumButtons);
stick.Details = new WinMMJoyDetails(num_axes);
// Make sure to reverse the vertical axes, so that +1 points up and -1 points down.
int axis = 0;
if (axis < caps.NumAxes)
{ stick.Details.Min[axis] = caps.XMin; stick.Details.Max[axis] = caps.XMax; axis++; }
if (axis < caps.NumAxes)
{ stick.Details.Min[axis] = caps.YMax; stick.Details.Max[axis] = caps.YMin; axis++; }
if (axis < caps.NumAxes)
{ stick.Details.Min[axis] = caps.ZMax; stick.Details.Max[axis] = caps.ZMin; axis++; }
if (axis < caps.NumAxes)
{ stick.Details.Min[axis] = caps.RMin; stick.Details.Max[axis] = caps.RMax; axis++; }
if (axis < caps.NumAxes)
{ stick.Details.Min[axis] = caps.UMin; stick.Details.Max[axis] = caps.UMax; axis++; }
if (axis < caps.NumAxes)
{ stick.Details.Min[axis] = caps.VMax; stick.Details.Max[axis] = caps.VMin; axis++; }
if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0)
lock (sync)
{
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;
JoystickDevice<WinMMJoyDetails> stick = null;
JoyCaps caps;
JoystickError result = UnsafeNativeMethods.joyGetDevCaps(number, out caps, JoyCaps.SizeInBytes);
if (result == JoystickError.NoError)
{
if (caps.NumAxes > JoystickState.MaxAxes)
{
Debug.Print("[Input] Device has {0} axes, which is higher than OpenTK maximum {1}. Please report a bug at http://www.opentk.com",
caps.NumAxes, JoystickState.MaxAxes);
caps.NumAxes = JoystickState.MaxAxes;
}
if (caps.NumAxes > JoystickState.MaxButtons)
{
Debug.Print("[Input] Device has {0} buttons, which is higher than OpenTK maximum {1}. Please report a bug at http://www.opentk.com",
caps.NumButtons, JoystickState.MaxButtons);
caps.NumButtons = JoystickState.MaxButtons;
}
JoystickCapabilities joycaps = new JoystickCapabilities(
caps.NumAxes,
caps.NumButtons,
(caps.Capabilities & JoystCapsFlags.HasPov) != 0 ? 1 : 0,
true);
int num_axes = caps.NumAxes;
if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0)
num_axes += 2;
stick = new JoystickDevice<WinMMJoyDetails>(number, num_axes, caps.NumButtons);
stick.Details = new WinMMJoyDetails(joycaps);
// Make sure to reverse the vertical axes, so that +1 points up and -1 points down.
for (int axis = 0; axis < caps.NumAxes; axis++)
{
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;
}
#warning "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();
//}
if (stick != null)
Debug.Print("Found joystick on device number {0}", number);
return stick;
}
void UnplugJoystick(int index)
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", index);
Debug.Print("[Win] WinMM joystick {0} unplugged", player_index);
CloseJoystick(player_index);
}
bool IsValid(int index)
void CloseJoystick(int player_index)
{
return index >= 0 && index < UnsafeNativeMethods.joyGetNumDevs();
lock (sync)
{
if (IsValid(player_index))
{
int device_index = player_to_index[player_index];
JoystickDevice<WinMMJoyDetails> stick =
sticks[index_to_stick[device_index]] as JoystickDevice<WinMMJoyDetails>;
if (stick != null)
{
index_to_stick.Remove(device_index);
player_to_index.Remove(player_index);
}
}
}
}
bool IsValid(int player_index)
{
if (player_to_index.ContainsKey(player_index))
{
return true;
}
//else if (index >= 0 && index < UnsafeNativeMethods.joyGetNumDevs())
//{
// return OpenJoystick(index) != null;
//}
return false;
}
static short CalculateOffset(int pos, int min, int max)
@ -170,88 +234,91 @@ namespace OpenTK.Platform.Windows
public void Poll()
{
foreach (JoystickDevice<WinMMJoyDetails> js in sticks)
lock (sync)
{
JoyInfoEx info = new JoyInfoEx();
info.Size = JoyInfoEx.SizeInBytes;
info.Flags = JoystickFlags.All;
UnsafeNativeMethods.joyGetPosEx(js.Id, ref info);
int num_axes = js.Axis.Count;
if ((js.Details.PovType & PovType.Exists) != 0)
num_axes -= 2; // Because the last two axis are used for POV input.
int axis = 0;
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.XPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.YPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.ZPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.RPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.UPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.VPos, axis)); axis++; }
if ((js.Details.PovType & PovType.Exists) != 0)
foreach (JoystickDevice<WinMMJoyDetails> js in sticks)
{
float x = 0, y = 0;
JoyInfoEx info = new JoyInfoEx();
info.Size = JoyInfoEx.SizeInBytes;
info.Flags = JoystickFlags.All;
UnsafeNativeMethods.joyGetPosEx(js.Id, ref info);
// 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.Pov != JoystickPovPosition.Centered)
int num_axes = js.Axis.Count;
if ((js.Details.PovType & PovType.Exists) != 0)
num_axes -= 2; // Because the last two axis are used for POV input.
int axis = 0;
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.XPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.YPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.ZPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.RPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.UPos, axis)); axis++; }
if (axis < num_axes)
{ js.SetAxis((JoystickAxis)axis, js.Details.CalculateOffset((float)info.VPos, axis)); axis++; }
if ((js.Details.PovType & PovType.Exists) != 0)
{
if (info.Pov > (int)JoystickPovPosition.Left || info.Pov < (int)JoystickPovPosition.Right)
{ y = 1; }
if ((info.Pov > (int)JoystickPovPosition.Forward) && (info.Pov < (int)JoystickPovPosition.Backward))
{ x = 1; }
if ((info.Pov > (int)JoystickPovPosition.Right) && (info.Pov < (int)JoystickPovPosition.Left))
{ y = -1; }
if (info.Pov > (int)JoystickPovPosition.Backward)
{ x = -1; }
}
//if ((js.Details.PovType & PovType.Discrete) != 0)
//{
// if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Centered)
// { x = 0; y = 0; }
// else if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Forward)
// { x = 0; y = -1; }
// else if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Left)
// { x = -1; y = 0; }
// else if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Backward)
// { x = 0; y = 1; }
// else if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Right)
// { x = 1; y = 0; }
//}
//else if ((js.Details.PovType & PovType.Continuous) != 0)
//{
// if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Centered)
// {
// // This approach moves the hat on a circle, not a square as it should.
// float angle = (float)(System.Math.PI * info.Pov / 18000.0 + System.Math.PI / 2);
// x = (float)System.Math.Cos(angle);
// y = (float)System.Math.Sin(angle);
// if (x < 0.001)
// x = 0;
// if (y < 0.001)
// y = 0;
// }
//}
//else
// throw new NotImplementedException("Please post an issue report at http://www.opentk.com/issues");
float x = 0, y = 0;
js.SetAxis((JoystickAxis)axis++, x);
js.SetAxis((JoystickAxis)axis++, y);
}
// 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.Pov != JoystickPovPosition.Centered)
{
if (info.Pov > (int)JoystickPovPosition.Left || info.Pov < (int)JoystickPovPosition.Right)
{ y = 1; }
if ((info.Pov > (int)JoystickPovPosition.Forward) && (info.Pov < (int)JoystickPovPosition.Backward))
{ x = 1; }
if ((info.Pov > (int)JoystickPovPosition.Right) && (info.Pov < (int)JoystickPovPosition.Left))
{ y = -1; }
if (info.Pov > (int)JoystickPovPosition.Backward)
{ x = -1; }
}
//if ((js.Details.PovType & PovType.Discrete) != 0)
//{
// if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Centered)
// { x = 0; y = 0; }
// else if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Forward)
// { x = 0; y = -1; }
// else if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Left)
// { x = -1; y = 0; }
// else if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Backward)
// { x = 0; y = 1; }
// else if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Right)
// { x = 1; y = 0; }
//}
//else if ((js.Details.PovType & PovType.Continuous) != 0)
//{
// if ((JoystickPovPosition)info.Pov == JoystickPovPosition.Centered)
// {
// // This approach moves the hat on a circle, not a square as it should.
// float angle = (float)(System.Math.PI * info.Pov / 18000.0 + System.Math.PI / 2);
// x = (float)System.Math.Cos(angle);
// y = (float)System.Math.Sin(angle);
// if (x < 0.001)
// x = 0;
// if (y < 0.001)
// y = 0;
// }
//}
//else
// throw new NotImplementedException("Please post an issue report at http://www.opentk.com/issues");
int button = 0;
while (button < js.Button.Count)
{
js.SetButton((JoystickButton)button, (info.Buttons & (1 << button)) != 0);
button++;
js.SetAxis((JoystickAxis)axis++, x);
js.SetAxis((JoystickAxis)axis++, y);
}
int button = 0;
while (button < js.Button.Count)
{
js.SetButton((JoystickButton)button, (info.Buttons & (1 << button)) != 0);
button++;
}
}
}
}
@ -260,89 +327,111 @@ namespace OpenTK.Platform.Windows
#region IJoystickDriver2 Members
public JoystickCapabilities GetCapabilities(int index)
public JoystickCapabilities GetCapabilities(int player_index)
{
if (IsValid(index))
lock (sync)
{
JoyCaps mmcaps;
JoystickError result = UnsafeNativeMethods.joyGetDevCaps(index, out mmcaps, JoyCaps.SizeInBytes);
if (result == JoystickError.NoError)
if (IsValid(player_index))
{
JoystickCapabilities caps = new JoystickCapabilities(
mmcaps.NumAxes, mmcaps.NumButtons, true);
//if ((caps.Capabilities & JoystCapsFlags.HasPov) != 0)
// gpcaps.DPadCount++;
return caps;
}
else if (result == JoystickError.Unplugged)
{
UnplugJoystick(index);
}
}
else
{
Debug.Print("[Win] Invalid WinMM joystick device {0}", index);
}
int device_index = player_to_index[player_index];
JoystickDevice<WinMMJoyDetails> stick =
sticks[index_to_stick[device_index]] as JoystickDevice<WinMMJoyDetails>;
return new JoystickCapabilities();
return stick.Details.Capabilities;
}
return new JoystickCapabilities();
}
}
public JoystickState GetState(int index)
public JoystickState GetState(int player_index)
{
JoystickState state = new JoystickState();
if (IsValid(index))
lock (sync)
{
JoyInfoEx info = new JoyInfoEx();
info.Size = JoyInfoEx.SizeInBytes;
info.Flags = JoystickFlags.All;
JoystickState state = new JoystickState();
JoystickError result = UnsafeNativeMethods.joyGetPosEx(index, ref info);
if (result == JoystickError.NoError)
if (IsValid(player_index))
{
JoyCaps caps;
result = UnsafeNativeMethods.joyGetDevCaps(index, out caps, JoyCaps.SizeInBytes);
if (result == JoystickError.NoError)
int device_index = player_to_index[player_index];
int index = index_to_stick[device_index];
JoystickDevice<WinMMJoyDetails> stick =
sticks[index] as JoystickDevice<WinMMJoyDetails>;
// For joysticks with fewer than three axes or four buttons, we must
// use joyGetPos; otherwise, joyGetPosEx. This is not just a cosmetic
// difference, simple devices will return incorrect results if we use
// joyGetPosEx on them.
if (stick.Details.Capabilities.AxisCount <= 3 || stick.Details.Capabilities.ButtonCount <= 4)
{
state.SetAxis(JoystickAxis.Axis0, CalculateOffset(info.XPos, caps.XMin, caps.XMax));
state.SetAxis(JoystickAxis.Axis1, CalculateOffset(info.YPos, caps.YMin, caps.YMax));
state.SetAxis(JoystickAxis.Axis2, CalculateOffset(info.ZPos, caps.ZMin, caps.ZMax));
state.SetAxis(JoystickAxis.Axis3, CalculateOffset(info.RPos, caps.RMin, caps.RMax));
state.SetAxis(JoystickAxis.Axis4, CalculateOffset(info.UPos, caps.UMin, caps.UMax));
state.SetAxis(JoystickAxis.Axis5, CalculateOffset(info.VPos, caps.VMin, caps.VMax));
// Use joyGetPos
JoyInfo info = new JoyInfo();
for (int i = 0; i < 16; i++)
JoystickError result = UnsafeNativeMethods.joyGetPos(device_index, ref info);
if (result == JoystickError.NoError)
{
state.SetButton(JoystickButton.Button0 + i, (info.Buttons & 1 << i) != 0);
}
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]));
}
state.SetIsConnected(true);
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 = new JoyInfoEx();
info.Size = JoyInfoEx.SizeInBytes;
info.Flags = JoystickFlags.All;
JoystickError result = UnsafeNativeMethods.joyGetPosEx(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);
}
}
}
if (result == JoystickError.Unplugged)
{
UnplugJoystick(index);
}
return state;
}
else
{
Debug.Print("[Win] Invalid WinMM joystick device {0}", index);
}
return state;
}
public Guid GetGuid(int index)
{
Guid guid = new Guid();
if (IsValid(index))
lock (sync)
{
// Todo: implement WinMM Guid retrieval
}
Guid guid = new Guid();
return guid;
if (IsValid(index))
{
// Todo: implement WinMM Guid retrieval
}
return guid;
}
}
#endregion
@ -420,6 +509,7 @@ namespace OpenTK.Platform.Windows
Backward = 18000,
Left = 27000
}
struct JoyCaps
{
public ushort Mid;
@ -456,6 +546,53 @@ namespace OpenTK.Platform.Windows
{
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
@ -472,8 +609,10 @@ namespace OpenTK.Platform.Windows
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;
@ -481,6 +620,20 @@ namespace OpenTK.Platform.Windows
{
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
@ -488,6 +641,8 @@ namespace OpenTK.Platform.Windows
[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();
@ -514,14 +669,16 @@ namespace OpenTK.Platform.Windows
struct WinMMJoyDetails
{
public readonly float[] Min, Max; // Minimum and maximum offset of each axis.
public readonly int[] Min, Max; // Minimum and maximum offset of each axis.
public PovType PovType;
public JoystickCapabilities Capabilities;
public WinMMJoyDetails(int num_axes)
public WinMMJoyDetails(JoystickCapabilities caps)
: this()
{
Min = new float[num_axes];
Max = new float[num_axes];
PovType = PovType.None;
Min = new int[caps.AxisCount];
Max = new int[caps.AxisCount];
Capabilities = caps;
}
public float CalculateOffset(float pos, int axis)

View file

@ -125,6 +125,7 @@ namespace OpenTK.Platform.Windows
case WindowMessage.DEVICECHANGE:
((WinRawKeyboard)KeyboardDriver).RefreshDevices();
((WinRawMouse)MouseDriver).RefreshDevices();
((WinMMJoystick)JoystickDriver).RefreshDevices();
break;
}
return base.WindowProcedure(handle, message, wParam, lParam);

View file

@ -484,7 +484,10 @@ namespace OpenTK.Platform.X11
{
JoystickDevice<X11JoyDetails> js = sticks[index_to_stick[index]];
caps = new JoystickCapabilities(
js.Axis.Count, js.Button.Count, js.Details.State.IsConnected);
js.Axis.Count,
js.Button.Count,
0, // hats not supported by /dev/js
js.Details.State.IsConnected);
}
return caps;
}