Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'mac_joystick' into develop

  • Loading branch information...
commit e984ddd783c044126e5c432cc2152b38f9fa06cd 2 parents 3a63496 + fda2d32
@thefiddler thefiddler authored
View
4 Source/Examples/OpenTK/Test/GameWindowStates.cs
@@ -236,7 +236,7 @@ int DrawGamePads(Graphics gfx, int line)
GamePadState state = GamePad.GetState(i);
if (state.IsConnected)
{
- DrawString(gfx, caps.ToString(), line++);
+ DrawString(gfx, String.Format("{0}: {1}", i, caps), line++);
DrawString(gfx, state.ToString(), line++);
}
}
@@ -248,7 +248,7 @@ int DrawGamePads(Graphics gfx, int line)
JoystickState state = Joystick.GetState(i);
if (state.IsConnected)
{
- DrawString(gfx, caps.ToString(), line++);
+ DrawString(gfx, String.Format("{0}: {1}", i, caps), line++);
DrawString(gfx, state.ToString(), line++);
}
}
View
6 Source/OpenTK/Input/JoystickAxis.cs
@@ -5,7 +5,7 @@
// Author:
// Stefanos A. <stapostol@gmail.com>
//
-// Copyright (c) 2006-2013 Stefanos Apostolopoulos
+// 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
@@ -58,5 +58,9 @@ public enum JoystickAxis
Axis8,
/// <summary>The tenth axis of the JoystickDevice.</summary>
Axis9,
+ /// <summary>The eleventh axis of the JoystickDevice.</summary>
+ Axis10,
+ /// <summary>The highest supported axis of the JoystickDevice.</summary>
+ Last = Axis10,
}
}
View
2  Source/OpenTK/Input/JoystickButton.cs
@@ -70,5 +70,7 @@ public enum JoystickButton
Button14,
/// <summary>The sixteenth button of the JoystickDevice.</summary>
Button15,
+ /// <summary>The last supported button of the JoystickDevice.</summary>
+ Last = Button15,
}
}
View
2  Source/OpenTK/Input/JoystickDevice.cs
@@ -155,7 +155,7 @@ internal void SetButton(JoystickButton button, bool @value)
#region JoystickDevice<TDetail> : JoystickDevice
// Provides platform-specific information about the relevant JoystickDevice.
- internal sealed class JoystickDevice<TDetail> : JoystickDevice
+ internal class JoystickDevice<TDetail> : JoystickDevice
where TDetail : new()
{
internal JoystickDevice(int id, int axes, int buttons)
View
4 Source/OpenTK/Input/JoystickState.cs
@@ -40,8 +40,8 @@ public struct JoystickState : IEquatable<JoystickState>
{
// If we ever add more values to JoystickAxis or JoystickButton
// then we'll need to increase these limits.
- internal const int MaxAxes = 10;
- internal const int MaxButtons = 32;
+ internal const int MaxAxes = (int)JoystickAxis.Last;
+ internal const int MaxButtons = (int)JoystickButton.Last;
const float ConversionFactor = 1.0f / (short.MaxValue + 0.5f);
View
73 Source/OpenTK/Platform/MacOS/CarbonBindings/CoreFoundation.cs
@@ -32,7 +32,10 @@
namespace OpenTK.Platform.MacOS.Carbon
{
+ using CFIndex = System.IntPtr;
using CFRunLoop = System.IntPtr;
+ using CFStringRef = System.IntPtr;
+ using CFTypeRef = System.IntPtr;
struct CFArray
{
@@ -59,6 +62,7 @@ public int Count
}
}
}
+
struct CFDictionary
{
public CFDictionary(IntPtr reference)
@@ -76,18 +80,16 @@ public int Count
return CF.CFDictionaryGetCount(dictionaryRef);
}
}
+
public double GetNumberValue(string key)
{
- unsafe
- {
- double retval;
- IntPtr cfnum = CF.CFDictionaryGetValue(dictionaryRef,
- CF.CFSTR(key));
+ double retval;
+ IntPtr cfnum = CF.CFDictionaryGetValue(dictionaryRef,
+ CF.CFSTR(key));
- CF.CFNumberGetValue(cfnum, CF.CFNumberType.kCFNumberDoubleType, &retval);
+ CF.CFNumberGetValue(cfnum, CF.CFNumberType.kCFNumberDoubleType, out retval);
- return retval;
- }
+ return retval;
}
}
class CF
@@ -106,6 +108,9 @@ class CF
[DllImport(appServices)]
internal static extern IntPtr CFDictionaryGetValue(IntPtr theDictionary, IntPtr theKey);
+ [DllImport(appServices)]
+ internal static extern void CFRelease(CFTypeRef cf);
+
// this mirrors the definition in CFString.h.
// I don't know why, but __CFStringMakeConstantString is marked as "private and should not be used directly"
// even though the CFSTR macro just calls it.
@@ -117,9 +122,38 @@ internal static IntPtr CFSTR(string cStr)
}
[DllImport(appServices)]
- internal unsafe static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, int* valuePtr);
+ internal static extern Boolean CFStringGetCString(
+ CFStringRef theString,
+ byte[] buffer,
+ CFIndex bufferSize,
+ CFStringEncoding encoding
+ );
+
+ internal static string CFStringGetCString(IntPtr cfstr)
+ {
+ CFIndex length = CFStringGetLength(cfstr);
+ if (length != IntPtr.Zero)
+ {
+ byte[] utf8_chars = new byte[length.ToInt32() + 1];
+ if (CFStringGetCString(cfstr, utf8_chars, new IntPtr(utf8_chars.Length), CFStringEncoding.UTF8))
+ {
+ return Encoding.UTF8.GetString(utf8_chars);
+ }
+ }
+ return String.Empty;
+ }
+
+ [DllImport(appServices)]
+ internal static extern CFIndex CFStringGetLength(
+ CFStringRef theString
+ );
+
+ [DllImport(appServices)]
+ internal static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, out int valuePtr);
[DllImport(appServices)]
- internal unsafe static extern bool CFNumberGetValue(IntPtr number, CFNumberType theType, double* valuePtr);
+ internal static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, out long valuePtr);
+ [DllImport(appServices)]
+ internal static extern bool CFNumberGetValue(IntPtr number, CFNumberType theType, out double valuePtr);
internal enum CFNumberType
{
@@ -150,6 +184,25 @@ public enum CFRunLoopExitReason
HandledSource = 4
}
+ public enum CFStringEncoding
+ {
+ MacRoman = 0,
+ WindowsLatin1 = 0x0500,
+ ISOLatin1 = 0x0201,
+ NextStepLatin = 0x0B01,
+ ASCII = 0x0600,
+ Unicode = 0x0100,
+ UTF8 = 0x08000100,
+ NonLossyASCII = 0x0BFF,
+
+ UTF16 = 0x0100,
+ UTF16BE = 0x10000100,
+ UTF16LE = 0x14000100,
+ UTF32 = 0x0c000100,
+ UTF32BE = 0x18000100,
+ UTF32LE = 0x1c000100
+ }
+
public static readonly IntPtr RunLoopModeDefault = CF.CFSTR("kCFRunLoopDefaultMode");
[DllImport(appServices)]
View
699 Source/OpenTK/Platform/MacOS/HIDInput.cs
@@ -35,6 +35,7 @@ namespace OpenTK.Platform.MacOS
{
using Carbon;
using CFAllocatorRef = System.IntPtr;
+ using CFArrayRef = System.IntPtr;
using CFDictionaryRef = System.IntPtr;
using CFIndex = System.IntPtr;
using CFRunLoop = System.IntPtr;
@@ -54,17 +55,40 @@ class HIDInput : IInputDriver2, IMouseDriver2, IKeyboardDriver2, IJoystickDriver
{
#region Fields
+ class JoystickDetails
+ {
+ public string Name;
+ public Guid Guid;
+ public JoystickState State;
+ public JoystickCapabilities Capabilities;
+ readonly public Dictionary<int, JoystickButton> ElementUsageToButton =
+ new Dictionary<int, JoystickButton>();
+ }
+
+ class MacJoystick : JoystickDevice<JoystickDetails>
+ {
+ internal MacJoystick(int id, int axes, int buttons)
+ : base(id, axes, buttons)
+ { }
+ }
+
readonly IOHIDManagerRef hidmanager;
readonly Dictionary<IntPtr, MouseState> MouseDevices =
new Dictionary<IntPtr, MouseState>(new IntPtrEqualityComparer());
readonly Dictionary<int, IntPtr> MouseIndexToDevice =
new Dictionary<int, IntPtr>();
+
readonly Dictionary<IntPtr, KeyboardState> KeyboardDevices =
new Dictionary<IntPtr, KeyboardState>(new IntPtrEqualityComparer());
readonly Dictionary<int, IntPtr> KeyboardIndexToDevice =
new Dictionary<int, IntPtr>();
+ readonly Dictionary<IntPtr, MacJoystick> JoystickDevices =
+ new Dictionary<IntPtr, MacJoystick>(new IntPtrEqualityComparer());
+ readonly Dictionary<int, IntPtr> JoystickIndexToDevice =
+ new Dictionary<int, IntPtr>();
+
readonly CFRunLoop RunLoop = CF.CFRunLoopGetMain();
readonly CFString InputLoopMode = CF.RunLoopModeDefault;
readonly CFDictionary DeviceTypes = new CFDictionary();
@@ -121,109 +145,163 @@ void RegisterHIDCallbacks(IOHIDManagerRef hidmanager)
void DeviceAdded(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device)
{
- if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero)
+ try
{
- if (NativeMethods.IOHIDDeviceConformsTo(device,
- HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse))
+ bool recognized = false;
+
+ if (NativeMethods.IOHIDDeviceOpen(device, IOOptionBits.Zero) == IOReturn.Zero)
{
- if (!MouseDevices.ContainsKey(device))
+ if (NativeMethods.IOHIDDeviceConformsTo(device,
+ HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse))
{
- Debug.Print("Mouse device {0:x} discovered, sender is {1:x}", device, sender);
- MouseState state = new MouseState();
- state.IsConnected = true;
- MouseIndexToDevice.Add(MouseDevices.Count, device);
- MouseDevices.Add(device, state);
+ AddMouse(sender, device);
+ recognized = true;
}
- else
+
+ if (NativeMethods.IOHIDDeviceConformsTo(device,
+ HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard))
{
- Debug.Print("Mouse device {0:x} reconnected, sender is {1:x}", device, sender);
- MouseState state = MouseDevices[device];
- state.IsConnected = true;
- MouseDevices[device] = state;
+ AddKeyboard(sender, device);
+ recognized = true;
}
- }
- if (NativeMethods.IOHIDDeviceConformsTo(device,
- HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard))
- {
- if (!KeyboardDevices.ContainsKey(device))
+ bool is_joystick = false;
+ is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device,
+ HIDPage.GenericDesktop, (int)HIDUsageGD.Joystick);
+ is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device,
+ HIDPage.GenericDesktop, (int)HIDUsageGD.GamePad);
+ is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device,
+ HIDPage.GenericDesktop, (int)HIDUsageGD.MultiAxisController);
+ is_joystick |= NativeMethods.IOHIDDeviceConformsTo(device,
+ HIDPage.GenericDesktop, (int)HIDUsageGD.Wheel);
+ // Todo: any other interesting devices under HIDPage.Simulation + HIDUsageSim?
+ if (is_joystick)
{
- Debug.Print("Keyboard device {0:x} discovered, sender is {1:x}", device, sender);
- KeyboardState state = new KeyboardState();
- state.IsConnected = true;
- KeyboardIndexToDevice.Add(KeyboardDevices.Count, device);
- KeyboardDevices.Add(device, state);
+ AddJoystick(sender, device);
+ recognized = true;
}
- else
+
+ if (recognized)
{
- Debug.Print("Keyboard device {0:x} reconnected, sender is {1:x}", device, sender);
- KeyboardState state = KeyboardDevices[device];
- state.IsConnected = true;
- KeyboardDevices[device] = state;
+ // The device is not normally available in the InputValueCallback (HandleDeviceValueReceived), so we include
+ // the device identifier as the context variable, so we can identify it and figure out the device later.
+ // Thanks to Jase: http://www.opentk.com/node/2800
+ NativeMethods.IOHIDDeviceRegisterInputValueCallback(device,
+ HandleDeviceValueReceived, device);
+
+ NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode);
}
}
-
- // The device is not normally available in the InputValueCallback (HandleDeviceValueReceived), so we include
- // the device identifier as the context variable, so we can identify it and figure out the device later.
- // Thanks to Jase: http://www.opentk.com/node/2800
- NativeMethods.IOHIDDeviceRegisterInputValueCallback(device,
- HandleDeviceValueReceived, device);
-
- NativeMethods.IOHIDDeviceScheduleWithRunLoop(device, RunLoop, InputLoopMode);
+ }
+ catch (Exception e)
+ {
+ Debug.Print("[Mac] Exception in managed callback: {0}", e);
}
}
void DeviceRemoved(IntPtr context, IOReturn res, IntPtr sender, IOHIDDeviceRef device)
{
- if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Mouse) &&
- MouseDevices.ContainsKey(device))
+ try
{
- Debug.Print("Mouse device {0:x} disconnected, sender is {1:x}", device, sender);
+ bool recognized = false;
- // Keep the device in case it comes back later on
- MouseState state = MouseDevices[device];
- state.IsConnected = false;
- MouseDevices[device] = state;
- }
+ if (MouseDevices.ContainsKey(device))
+ {
+ RemoveMouse(sender, device);
+ recognized = true;
+ }
- if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) &&
- KeyboardDevices.ContainsKey(device))
- {
- Debug.Print("Keyboard device {0:x} disconnected, sender is {1:x}", device, sender);
+ if (KeyboardDevices.ContainsKey(device))
+ {
+ RemoveKeyboard(sender, device);
+ recognized = true;
+ }
- // Keep the device in case it comes back later on
- KeyboardState state = KeyboardDevices[device];
- state.IsConnected = false;
- KeyboardDevices[device] = state;
- }
+ if (JoystickDevices.ContainsKey(device))
+ {
+ RemoveJoystick(sender, device);
+ recognized = true;
+ }
- NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero);
- NativeMethods.IOHIDDeviceUnscheduleWithRunLoop(device, RunLoop, InputLoopMode);
+ if (recognized)
+ {
+ NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero);
+ NativeMethods.IOHIDDeviceUnscheduleFromRunLoop(device, RunLoop, InputLoopMode);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Print("[Mac] Exception in managed callback: {0}", e);
+ }
}
void DeviceValueReceived(IntPtr context, IOReturn res, IntPtr sender, IOHIDValueRef val)
{
- if (disposed)
+ try
+ {
+ if (disposed)
+ {
+ Debug.Print("DeviceValueReceived({0}, {1}, {2}, {3}) called on disposed {4}",
+ context, res, sender, val, GetType());
+ return;
+ }
+
+ MouseState mouse;
+ KeyboardState keyboard;
+ MacJoystick joystick;
+ if (MouseDevices.TryGetValue(context, out mouse))
+ {
+ MouseDevices[context] = UpdateMouse(mouse, val);
+ }
+ else if (KeyboardDevices.TryGetValue(context, out keyboard))
+ {
+ KeyboardDevices[context] = UpdateKeyboard(keyboard, val);
+ }
+ else if (JoystickDevices.TryGetValue(context, out joystick))
+ {
+ JoystickDevices[context] = UpdateJoystick(joystick, val);
+ }
+ else
+ {
+ //Debug.Print ("Device {0:x} not found in list of keyboards or mice", sender);
+ }
+ }
+ catch (Exception e)
{
- Debug.Print("DeviceValueReceived({0}, {1}, {2}, {3}) called on disposed {4}",
- context, res, sender, val, GetType());
- return;
+ Debug.Print("[Mac] Exception in managed callback: {0}", e);
}
+ }
- MouseState mouse;
- KeyboardState keyboard;
- if (MouseDevices.TryGetValue(context, out mouse))
+ #region Mouse
+
+ void AddMouse(CFAllocatorRef sender, CFAllocatorRef device)
+ {
+ if (!MouseDevices.ContainsKey(device))
{
- MouseDevices[context] = UpdateMouse(mouse, val);
+ Debug.Print("Mouse device {0:x} discovered, sender is {1:x}", device, sender);
+ MouseState state = new MouseState();
+ state.IsConnected = true;
+ MouseIndexToDevice.Add(MouseDevices.Count, device);
+ MouseDevices.Add(device, state);
}
- else if (KeyboardDevices.TryGetValue(context, out keyboard))
+ else
{
- KeyboardDevices[context] = UpdateKeyboard(keyboard, val);
- }else{
- //Debug.Print ("Device {0:x} not found in list of keyboards or mice", sender);
+ Debug.Print("Mouse device {0:x} reconnected, sender is {1:x}", device, sender);
+ MouseState state = MouseDevices[device];
+ state.IsConnected = true;
+ MouseDevices[device] = state;
}
}
+ void RemoveMouse(CFAllocatorRef sender, CFAllocatorRef device)
+ {
+ Debug.Print("Mouse device {0:x} disconnected, sender is {1:x}", device, sender);
+ // Keep the device in case it comes back later on
+ MouseState state = MouseDevices[device];
+ state.IsConnected = false;
+ MouseDevices[device] = state;
+ }
+
static MouseState UpdateMouse(MouseState state, IOHIDValueRef val)
{
IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val);
@@ -260,6 +338,38 @@ static MouseState UpdateMouse(MouseState state, IOHIDValueRef val)
return state;
}
+ #endregion
+
+ #region Keyboard
+
+ void AddKeyboard(CFAllocatorRef sender, CFAllocatorRef device)
+ {
+ if (!KeyboardDevices.ContainsKey(device))
+ {
+ Debug.Print("Keyboard device {0:x} discovered, sender is {1:x}", device, sender);
+ KeyboardState state = new KeyboardState();
+ state.IsConnected = true;
+ KeyboardIndexToDevice.Add(KeyboardDevices.Count, device);
+ KeyboardDevices.Add(device, state);
+ }
+ else
+ {
+ Debug.Print("Keyboard device {0:x} reconnected, sender is {1:x}", device, sender);
+ KeyboardState state = KeyboardDevices[device];
+ state.IsConnected = true;
+ KeyboardDevices[device] = state;
+ }
+ }
+
+ void RemoveKeyboard(CFAllocatorRef sender, CFAllocatorRef device)
+ {
+ Debug.Print("Keyboard device {0:x} disconnected, sender is {1:x}", device, sender);
+ // Keep the device in case it comes back later on
+ KeyboardState state = KeyboardDevices[device];
+ state.IsConnected = false;
+ KeyboardDevices[device] = state;
+ }
+
static KeyboardState UpdateKeyboard(KeyboardState state, IOHIDValueRef val)
{
IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val);
@@ -289,6 +399,334 @@ static KeyboardState UpdateKeyboard(KeyboardState state, IOHIDValueRef val)
#endregion
+ #region Joystick
+
+ Guid CreateJoystickGuid(IntPtr device, string name)
+ {
+ // Create a device guid from the product and vendor id keys
+ List<byte> guid_bytes = new List<byte>();
+ long vendor_id = 0;
+ long product_id = 0;
+
+ IntPtr vendor_id_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDVendorIDKey);
+ IntPtr product_id_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductIDKey);
+ if (vendor_id_ref != IntPtr.Zero)
+ {
+ CF.CFNumberGetValue(vendor_id_ref, CF.CFNumberType.kCFNumberLongType, out vendor_id);
+ }
+ if (product_id_ref != IntPtr.Zero)
+ {
+ CF.CFNumberGetValue(product_id_ref, CF.CFNumberType.kCFNumberLongType, out product_id);
+ }
+
+ if (vendor_id != 0 && product_id != 0)
+ {
+ guid_bytes.AddRange(BitConverter.GetBytes(vendor_id));
+ guid_bytes.AddRange(BitConverter.GetBytes(product_id));
+ }
+ else
+ {
+ // Bluetooth devices don't have USB vendor/product id keys.
+ // Match SDL2 algorithm for compatibility.
+ guid_bytes.Add(0x05); // BUS_BLUETOOTH
+ guid_bytes.Add(0x00);
+ guid_bytes.Add(0x00);
+ guid_bytes.Add(0x00);
+
+ // Copy the first 12 bytes of the product name
+ byte[] name_bytes = new byte[12];
+ Array.Copy(System.Text.Encoding.UTF8.GetBytes(name), name_bytes, name_bytes.Length);
+ guid_bytes.AddRange(name_bytes);
+ }
+
+ return new Guid(guid_bytes.ToArray());
+ }
+
+ MacJoystick CreateJoystick(IntPtr sender, IntPtr device)
+ {
+ MacJoystick joy = null;
+
+ // Retrieve all elements of this device
+ CFArrayRef element_array_ref = NativeMethods.IOHIDDeviceCopyMatchingElements(device, IntPtr.Zero, IntPtr.Zero);
+ if (element_array_ref != IntPtr.Zero)
+ {
+ int axes = 0;
+ int buttons = 0;
+ int dpads = 0;
+
+ CFStringRef name_ref = NativeMethods.IOHIDDeviceGetProperty(device, NativeMethods.IOHIDProductKey);
+ string name = CF.CFStringGetCString(name_ref);
+
+ Guid guid = CreateJoystickGuid(device, name);
+
+ List<int> button_elements = new List<int>();
+ CFArray element_array = new CFArray(element_array_ref);
+ for (int i = 0; i < element_array.Count; i++)
+ {
+ IOHIDElementRef element_ref = element_array[i];
+ IOHIDElementType type = NativeMethods.IOHIDElementGetType(element_ref);
+ HIDPage page = NativeMethods.IOHIDElementGetUsagePage(element_ref);
+ int usage = NativeMethods.IOHIDElementGetUsage(element_ref);
+
+ switch (page)
+ {
+ case HIDPage.GenericDesktop:
+ switch ((HIDUsageGD)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:
+ axes++;
+ break;
+
+ case HIDUsageGD.Hatswitch:
+ dpads++;
+ break;
+ }
+ break;
+
+ case HIDPage.Simulation:
+ switch ((HIDUsageSim)usage)
+ {
+ case HIDUsageSim.Rudder:
+ case HIDUsageSim.Throttle:
+ axes++;
+ break;
+ }
+ break;
+
+ case HIDPage.Button:
+ button_elements.Add(usage);
+ break;
+ }
+ }
+
+ joy = new MacJoystick(-1, axes, buttons);
+ joy.Details.Name = name;
+ joy.Details.Guid = guid;
+ joy.Details.State.SetIsConnected(true);
+ joy.Details.Capabilities = new JoystickCapabilities(axes, buttons, true);
+
+ // Map button elements to JoystickButtons
+ for (int button = 0; button < button_elements.Count; button++)
+ {
+ joy.Details.ElementUsageToButton.Add(button_elements[button], JoystickButton.Button0 + button);
+ }
+ }
+ CF.CFRelease(element_array_ref);
+
+ return joy;
+ }
+
+ MacJoystick GetJoystick(int index)
+ {
+ IntPtr device;
+ if (JoystickIndexToDevice.TryGetValue(index, out device))
+ {
+ MacJoystick joystick;
+ if (JoystickDevices.TryGetValue(device, out joystick))
+ {
+ return joystick;
+ }
+ }
+ return null;
+ }
+
+ void AddJoystick(CFAllocatorRef sender, CFAllocatorRef device)
+ {
+ Debug.Print("Joystick device {0:x} discovered, sender is {1:x}", device, sender);
+ MacJoystick joy = CreateJoystick(sender, device);
+ if (joy != null)
+ {
+ // Add a device->joy lookup entry for this device.
+ if (!JoystickDevices.ContainsKey(device))
+ {
+ // First time we've seen this device.
+ JoystickDevices.Add(device, joy);
+ }
+ else
+ {
+ // This is an old device that is replugged.
+ // This branch does not appear to be executed, ever.
+ JoystickDevices[device] = joy;
+ }
+
+ // Add an index->device lookup entry for this device.
+ // Use the first free (i.e. disconnected) index.
+ // If all indices are connected, append a new one.
+ int i;
+ for (i = 0; i < JoystickIndexToDevice.Count; i++)
+ {
+ IntPtr candidate = JoystickIndexToDevice[i];
+ if (!JoystickDevices[candidate].Details.State.IsConnected)
+ {
+ break;
+ }
+ }
+
+ if (i == JoystickDevices.Count)
+ {
+ // All indices connected, append a new one.
+ JoystickIndexToDevice.Add(JoystickDevices.Count, device);
+ }
+ else
+ {
+ // Replace joystick at that index
+ JoystickIndexToDevice[i] = device;
+ }
+ }
+ }
+
+ void RemoveJoystick(CFAllocatorRef sender, CFAllocatorRef device)
+ {
+ Debug.Print("Joystick device {0:x} disconnected, sender is {1:x}", device, sender);
+ // Keep the device in case it comes back later on
+ JoystickDevices[device].Details.State = new JoystickState();
+ JoystickDevices[device].Details.Capabilities = new JoystickCapabilities();
+ }
+
+ static MacJoystick UpdateJoystick(MacJoystick joy, IOHIDValueRef val)
+ {
+ IOHIDElementRef elem = NativeMethods.IOHIDValueGetElement(val);
+ HIDPage page = NativeMethods.IOHIDElementGetUsagePage(elem);
+ int usage = NativeMethods.IOHIDElementGetUsage(elem);
+
+ switch (page)
+ {
+ case HIDPage.GenericDesktop:
+ switch ((HIDUsageGD)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:
+ short offset = GetJoystickAxis(val, elem);
+ JoystickAxis axis = TranslateJoystickAxis(usage);
+ if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
+ {
+ joy.Details.State.SetAxis(axis, offset);
+ }
+ break;
+
+ case HIDUsageGD.Hatswitch:
+ break;
+ }
+ break;
+
+ case HIDPage.Simulation:
+ switch ((HIDUsageSim)usage)
+ {
+ case HIDUsageSim.Rudder:
+ case HIDUsageSim.Throttle:
+ short offset = GetJoystickAxis(val, elem);
+ JoystickAxis axis = TranslateJoystickAxis(usage);
+ if (axis >= JoystickAxis.Axis0 && axis <= JoystickAxis.Last)
+ {
+ joy.Details.State.SetAxis(axis, offset);
+ }
+ break;
+ }
+ break;
+
+ case HIDPage.Button:
+ {
+ bool pressed = GetJoystickButton(val, elem);
+ JoystickButton button = TranslateJoystickButton(joy, usage);
+ if (button >= JoystickButton.Button0 && button <= JoystickButton.Last)
+ {
+ joy.Details.State.SetButton(button, pressed);
+ }
+ }
+ break;
+ }
+
+ return joy;
+ }
+
+ static short GetJoystickAxis(IOHIDValueRef val, IOHIDElementRef element)
+ {
+ 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;
+ }
+ }
+
+ static bool GetJoystickButton(IOHIDValueRef val, IOHIDElementRef element)
+ {
+ // Todo: analogue buttons are transformed to digital
+ int value = NativeMethods.IOHIDValueGetIntegerValue(val).ToInt32();
+ return value >= 1;
+ }
+
+ static JoystickButton TranslateJoystickButton(MacJoystick joy, int usage)
+ {
+ JoystickButton button;
+ if (joy.Details.ElementUsageToButton.TryGetValue(usage, out button))
+ {
+ return button;
+ }
+ return JoystickButton.Last + 1;
+ }
+
+ #endregion
+
+ #endregion
+
#region IInputDriver2 Members
public IMouseDriver2 MouseDriver { get { return this; } }
@@ -373,16 +811,31 @@ string IKeyboardDriver2.GetDeviceName(int index)
JoystickState IJoystickDriver2.GetState(int index)
{
+ MacJoystick joystick = GetJoystick(index);
+ if (joystick != null)
+ {
+ return joystick.Details.State;
+ }
return new JoystickState();
}
JoystickCapabilities IJoystickDriver2.GetCapabilities(int index)
{
+ MacJoystick joystick = GetJoystick(index);
+ if (joystick != null)
+ {
+ return joystick.Details.Capabilities;
+ }
return new JoystickCapabilities();
}
Guid IJoystickDriver2.GetGuid(int index)
{
+ MacJoystick joystick = GetJoystick(index);
+ if (joystick != null)
+ {
+ return joystick.Details.Guid;
+ }
return new Guid();
}
@@ -441,6 +894,12 @@ class NativeMethods
CFString inCFRunLoopMode);
[DllImport(hid)]
+ public static extern void IOHIDManagerUnscheduleFromRunLoop(
+ IOHIDManagerRef inIOHIDManagerRef,
+ CFRunLoop inCFRunLoop,
+ CFString inCFRunLoopMode);
+
+ [DllImport(hid)]
public static extern void IOHIDManagerSetDeviceMatching(
IOHIDManagerRef manager,
CFDictionaryRef matching) ;
@@ -466,6 +925,13 @@ class NativeMethods
HIDPage inUsagePage, // the usage page to test conformance with
int inUsage); // the usage to test conformance with
+ // return the HID elements that match the criteria contained in the matching dictionary
+ [DllImport(hid)]
+ public static extern CFArrayRef IOHIDDeviceCopyMatchingElements(
+ IOHIDDeviceRef inIOHIDDeviceRef, // IOHIDDeviceRef for the HID device
+ CFDictionaryRef inMatchingCFDictRef, // the matching dictionary
+ IOOptionBits inOptions); // Option bits
+
[DllImport(hid)]
public static extern void IOHIDDeviceRegisterInputValueCallback(
IOHIDDeviceRef device,
@@ -485,7 +951,7 @@ class NativeMethods
CFString inCFRunLoopMode);
[DllImport(hid)]
- public static extern void IOHIDDeviceUnscheduleWithRunLoop(
+ public static extern void IOHIDDeviceUnscheduleFromRunLoop(
IOHIDDeviceRef device,
CFRunLoop inCFRunLoop,
CFString inCFRunLoopMode);
@@ -502,15 +968,36 @@ class NativeMethods
IOHIDValueScaleType type) ;
[DllImport(hid)]
+ public static extern IOHIDElementType IOHIDElementGetType(
+ IOHIDElementRef element);
+
+ [DllImport(hid)]
public static extern int IOHIDElementGetUsage(IOHIDElementRef elem);
[DllImport(hid)]
public static extern HIDPage IOHIDElementGetUsagePage(IOHIDElementRef elem);
+ [DllImport(hid)]
+ public static extern CFIndex IOHIDElementGetLogicalMax(IOHIDElementRef element);
+
+ [DllImport(hid)]
+ public static extern CFIndex IOHIDElementGetLogicalMin(IOHIDElementRef element);
+
public delegate void IOHIDDeviceCallback(IntPtr ctx, IOReturn res, IntPtr sender, IOHIDDeviceRef device);
public delegate void IOHIDValueCallback(IntPtr ctx, IOReturn res, IntPtr sender, IOHIDValueRef val);
}
+ enum IOHIDElementType
+ {
+ Input_Misc = 1,
+ Input_Button = 2,
+ Input_Axis = 3,
+ Input_ScanCodes = 4,
+ Output = 129,
+ Feature = 257,
+ Collection = 513
+ }
+
enum IOHIDValueScaleType
{
Physical, // [device min, device max]
@@ -615,6 +1102,65 @@ enum HIDUsageGD
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) */
@@ -1003,19 +1549,22 @@ void Dispose(bool manual)
hidmanager, IntPtr.Zero, IntPtr.Zero);
NativeMethods.IOHIDManagerRegisterDeviceRemovalCallback(
hidmanager, IntPtr.Zero, IntPtr.Zero);
- NativeMethods.IOHIDManagerScheduleWithRunLoop(
- hidmanager, IntPtr.Zero, IntPtr.Zero);
+ NativeMethods.IOHIDManagerUnscheduleFromRunLoop(
+ hidmanager, RunLoop, InputLoopMode);
foreach (var device in MouseDevices.Keys)
{
- NativeMethods.IOHIDDeviceRegisterInputValueCallback(
- device, IntPtr.Zero, IntPtr.Zero);
+ DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
}
foreach (var device in KeyboardDevices.Keys)
{
- NativeMethods.IOHIDDeviceRegisterInputValueCallback(
- device, IntPtr.Zero, IntPtr.Zero);
+ DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
+ }
+
+ foreach (var device in JoystickDevices.Keys)
+ {
+ DeviceRemoved(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, device);
}
HandleDeviceAdded = null;
Please sign in to comment.
Something went wrong with that request. Please try again.