mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-03-28 13:26:57 +00:00
Merge branch 'mac_joystick' into develop
This commit is contained in:
commit
e984ddd783
|
@ -236,7 +236,7 @@ namespace Examples.Tests
|
|||
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 @@ namespace Examples.Tests
|
|||
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++);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ namespace OpenTK.Input
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,5 +70,7 @@ namespace OpenTK.Input
|
|||
Button14,
|
||||
/// <summary>The sixteenth button of the JoystickDevice.</summary>
|
||||
Button15,
|
||||
/// <summary>The last supported button of the JoystickDevice.</summary>
|
||||
Last = Button15,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ namespace OpenTK.Input
|
|||
#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)
|
||||
|
|
|
@ -40,8 +40,8 @@ namespace OpenTK.Input
|
|||
{
|
||||
// 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);
|
||||
|
||||
|
|
|
@ -32,7 +32,10 @@ using System.Text;
|
|||
|
||||
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 @@ namespace OpenTK.Platform.MacOS.Carbon
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CFDictionary
|
||||
{
|
||||
public CFDictionary(IntPtr reference)
|
||||
|
@ -76,18 +80,16 @@ namespace OpenTK.Platform.MacOS.Carbon
|
|||
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 @@ namespace OpenTK.Platform.MacOS.Carbon
|
|||
[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 @@ namespace OpenTK.Platform.MacOS.Carbon
|
|||
}
|
||||
|
||||
[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 unsafe static extern bool CFNumberGetValue(IntPtr number, CFNumberType theType, double* valuePtr);
|
||||
internal static extern CFIndex CFStringGetLength(
|
||||
CFStringRef theString
|
||||
);
|
||||
|
||||
[DllImport(appServices)]
|
||||
internal static extern bool CFNumberGetValue (IntPtr number, CFNumberType theType, out int valuePtr);
|
||||
[DllImport(appServices)]
|
||||
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 @@ namespace OpenTK.Platform.MacOS.Carbon
|
|||
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)]
|
||||
|
|
|
@ -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 @@ namespace OpenTK.Platform.MacOS
|
|||
{
|
||||
#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 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
AddJoystick(sender, device);
|
||||
recognized = true;
|
||||
}
|
||||
|
||||
if (recognized)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (NativeMethods.IOHIDDeviceConformsTo(device,
|
||||
HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (KeyboardDevices.ContainsKey(device))
|
||||
{
|
||||
RemoveKeyboard(sender, device);
|
||||
recognized = true;
|
||||
}
|
||||
|
||||
if (JoystickDevices.ContainsKey(device))
|
||||
{
|
||||
RemoveJoystick(sender, device);
|
||||
recognized = true;
|
||||
}
|
||||
|
||||
if (recognized)
|
||||
{
|
||||
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero);
|
||||
NativeMethods.IOHIDDeviceUnscheduleFromRunLoop(device, RunLoop, InputLoopMode);
|
||||
}
|
||||
}
|
||||
|
||||
if (NativeMethods.IOHIDDeviceConformsTo(device, HIDPage.GenericDesktop, (int)HIDUsageGD.Keyboard) &&
|
||||
KeyboardDevices.ContainsKey(device))
|
||||
catch (Exception e)
|
||||
{
|
||||
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;
|
||||
Debug.Print("[Mac] Exception in managed callback: {0}", e);
|
||||
}
|
||||
|
||||
NativeMethods.IOHIDDeviceRegisterInputValueCallback(device, IntPtr.Zero, IntPtr.Zero);
|
||||
NativeMethods.IOHIDDeviceUnscheduleWithRunLoop(device, RunLoop, InputLoopMode);
|
||||
}
|
||||
|
||||
void DeviceValueReceived(IntPtr context, IOReturn res, IntPtr sender, IOHIDValueRef val)
|
||||
{
|
||||
if (disposed)
|
||||
try
|
||||
{
|
||||
Debug.Print("DeviceValueReceived({0}, {1}, {2}, {3}) called on disposed {4}",
|
||||
context, res, sender, val, GetType());
|
||||
return;
|
||||
}
|
||||
if (disposed)
|
||||
{
|
||||
Debug.Print("DeviceValueReceived({0}, {1}, {2}, {3}) called on disposed {4}",
|
||||
context, res, sender, val, GetType());
|
||||
return;
|
||||
}
|
||||
|
||||
MouseState mouse;
|
||||
KeyboardState keyboard;
|
||||
if (MouseDevices.TryGetValue(context, out mouse))
|
||||
{
|
||||
MouseDevices[context] = UpdateMouse(mouse, val);
|
||||
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);
|
||||
}
|
||||
}
|
||||
else if (KeyboardDevices.TryGetValue(context, out keyboard))
|
||||
catch (Exception e)
|
||||
{
|
||||
KeyboardDevices[context] = UpdateKeyboard(keyboard, val);
|
||||
}else{
|
||||
//Debug.Print ("Device {0:x} not found in list of keyboards or mice", sender);
|
||||
Debug.Print("[Mac] Exception in managed callback: {0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
#region Mouse
|
||||
|
||||
void AddMouse(CFAllocatorRef sender, CFAllocatorRef device)
|
||||
{
|
||||
if (!MouseDevices.ContainsKey(device))
|
||||
{
|
||||
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
|
||||
{
|
||||
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 @@ namespace OpenTK.Platform.MacOS
|
|||
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 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
#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 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -440,6 +893,12 @@ namespace OpenTK.Platform.MacOS
|
|||
CFRunLoop inCFRunLoop,
|
||||
CFString inCFRunLoopMode);
|
||||
|
||||
[DllImport(hid)]
|
||||
public static extern void IOHIDManagerUnscheduleFromRunLoop(
|
||||
IOHIDManagerRef inIOHIDManagerRef,
|
||||
CFRunLoop inCFRunLoop,
|
||||
CFString inCFRunLoopMode);
|
||||
|
||||
[DllImport(hid)]
|
||||
public static extern void IOHIDManagerSetDeviceMatching(
|
||||
IOHIDManagerRef manager,
|
||||
|
@ -466,6 +925,13 @@ namespace OpenTK.Platform.MacOS
|
|||
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 @@ namespace OpenTK.Platform.MacOS
|
|||
CFString inCFRunLoopMode);
|
||||
|
||||
[DllImport(hid)]
|
||||
public static extern void IOHIDDeviceUnscheduleWithRunLoop(
|
||||
public static extern void IOHIDDeviceUnscheduleFromRunLoop(
|
||||
IOHIDDeviceRef device,
|
||||
CFRunLoop inCFRunLoop,
|
||||
CFString inCFRunLoopMode);
|
||||
|
@ -501,16 +967,37 @@ namespace OpenTK.Platform.MacOS
|
|||
IOHIDValueRef @value,
|
||||
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 @@ namespace OpenTK.Platform.MacOS
|
|||
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 @@ namespace OpenTK.Platform.MacOS
|
|||
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;
|
||||
|
|
Loading…
Reference in a new issue