mixedreality/com.microsoft.mixedreality..../Core/Providers/InputSimulation/KeyBinding.cs

374 lines
13 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
[assembly: InternalsVisibleTo("Microsoft.MixedReality.Toolkit.Services.InputSimulation.Editor")]
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Identifier of a key combination or mouse button for generic input binding.
/// </summary>
/// <remarks>
/// This encodes either a KeyCode with optional modifiers or a mouse button index.
/// </remarks>
[System.Serializable]
public struct KeyBinding
{
/// <summary>
/// The type of value encoded in the <see cref="code"/> property.
/// </summary>
public enum KeyType : int
{
None = 0,
Mouse = 1,
Key = 2,
}
/// <summary>
/// Enum for interpreting the mouse button integer index.
/// </summary>
public enum MouseButton : int
{
Left = 0,
Right = 1,
Middle = 2,
Button3 = 3,
Button4 = 4,
Button5 = 5,
Button6 = 6,
Button7 = 7,
}
// Array of names to use for a combined enum selection.
internal static readonly string[] AllCodeNames;
// Maps (KeyType, code) combination onto the contiguous index used for enums.
// Value can be used as index in AllCodeNames array.
internal static readonly Dictionary<Tuple<KeyType, int>, int> KeyBindingToEnumMap;
// Maps enum index to a KeyBinding, for assignment after selecting an enum value.
internal static readonly Dictionary<int, Tuple<KeyType, int>> EnumToKeyBindingMap;
// Static constructor to initialize static fields
static KeyBinding()
{
KeyCode[] KeyCodeValues = (KeyCode[])Enum.GetValues(typeof(KeyCode));
MouseButton[] MouseButtonValues = (MouseButton[])Enum.GetValues(typeof(MouseButton));
// Build maps for converting between int enum value and KeyBinding values
{
KeyBindingToEnumMap = new Dictionary<Tuple<KeyType, int>, int>();
EnumToKeyBindingMap = new Dictionary<int, Tuple<KeyType, int>>();
List<string> names = new List<string>();
int index = 0;
Action<KeyType, int> AddEnumValue = (bindingType, code) =>
{
var kb = new KeyBinding() { bindingType = bindingType, code = code };
names.Add(kb.ToString());
EnumToKeyBindingMap[index] = Tuple.Create(bindingType, code);
KeyBindingToEnumMap[Tuple.Create(bindingType, code)] = index;
++index;
};
AddEnumValue(KeyType.None, 0);
foreach (MouseButton mb in MouseButtonValues)
{
AddEnumValue(KeyType.Mouse, (int)mb);
}
foreach (KeyCode kc in KeyCodeValues)
{
AddEnumValue(KeyType.Key, (int)kc);
}
AllCodeNames = names.ToArray();
}
}
[SerializeField]
private KeyType bindingType;
/// <summary>
/// Type of input this binding maps to.
/// </summary>
public KeyType BindingType => bindingType;
// Internal binding code.
// This can be a KeyCode or mouse button index, depending on the bindingType;
[SerializeField]
private int code;
public override string ToString()
{
string s = "";
s += bindingType.ToString();
switch (bindingType)
{
case KeyType.Key:
s += ": " + ((KeyCode)code).ToString();
break;
case KeyType.Mouse:
s += ": " + ((MouseButton)code).ToString();
break;
}
return s;
}
/// <summary>
/// Try to convert the binding to a KeyCode.
/// </summary>
/// <returns>True if the binding is a keyboard key</returns>
public bool TryGetKeyCode(out KeyCode keyCode)
{
keyCode = (KeyCode)code;
return bindingType == KeyType.Key;
}
/// <summary>
/// Try to convert the binding to a mouse button.
/// </summary>
/// <returns>True if the binding is a mouse button</returns>
public bool TryGetMouseButton(out int mouseButton)
{
mouseButton = code;
return bindingType == KeyType.Mouse;
}
/// <summary>
/// Try to convert the binding to a mouse button.
/// </summary>
/// <returns>True if the binding is a mouse button</returns>
public bool TryGetMouseButton(out MouseButton mouseButton)
{
if (TryGetMouseButton(out int iMouseButton))
{
mouseButton = (MouseButton)iMouseButton;
return true;
}
mouseButton = MouseButton.Left;
return false;
}
/// <summary>
/// Create a default empty binding.
/// </summary>
public static KeyBinding Unbound()
{
KeyBinding kb = new KeyBinding();
kb.bindingType = KeyType.None;
kb.code = 0;
return kb;
}
/// <summary>
/// Create a binding for a keyboard key.
/// </summary>
public static KeyBinding FromKey(KeyCode keyCode)
{
KeyBinding kb = new KeyBinding();
kb.bindingType = KeyType.Key;
kb.code = (int)keyCode;
return kb;
}
/// <summary>
/// Create a binding for a mouse button.
/// </summary>
public static KeyBinding FromMouseButton(int mouseButton)
{
KeyBinding kb = new KeyBinding();
kb.bindingType = KeyType.Mouse;
kb.code = mouseButton;
return kb;
}
/// <summary>
/// Create a binding for a mouse button.
/// </summary>
public static KeyBinding FromMouseButton(MouseButton mouseButton)
{
return FromMouseButton((int)mouseButton);
}
}
/// <summary>
/// Utility class to poll input for key bindings and to simulate key presses
/// Need to add mechanisms to poll and simulate input axis: https://github.com/microsoft/MixedRealityToolkit-Unity/issues/7659
/// </summary>
public static class KeyInputSystem
{
private static bool isSimulated;
public static bool SimulatingInput => isSimulated;
private static HashSet<int> SimulatedMouseDownSet;
private static HashSet<KeyCode> SimulatedKeyDownSet;
private static HashSet<int> SimulatedMouseSet;
private static HashSet<KeyCode> SimulatedKeySet;
private static HashSet<int> SimulatedMouseUpSet;
private static HashSet<KeyCode> SimulatedKeyUpSet;
/// <summary>
/// Starts the key input simulation. Inputs can now be simulated via <see cref="PressKey(KeyBinding)"/> and <see cref="ReleaseKey(KeyBinding)"/>
/// </summary>
public static void StartKeyInputStimulation()
{
ResetKeyInputSimulation();
isSimulated = true;
}
/// <summary>
/// Stops the key input simulation
/// </summary>
public static void StopKeyInputSimulation()
{
isSimulated = false;
}
/// <summary>
/// Resets the key input simulation. All keys will not trigger <see cref="GetKeyDown(KeyBinding)"/>, <see cref="GetKey(KeyBinding)"/>, or <see cref="GetKeyUp(KeyBinding)"/>
/// </summary>
public static void ResetKeyInputSimulation()
{
SimulatedMouseDownSet = new HashSet<int>();
SimulatedKeyDownSet = new HashSet<KeyCode>();
SimulatedMouseSet = new HashSet<int>();
SimulatedKeySet = new HashSet<KeyCode>();
SimulatedMouseUpSet = new HashSet<int>();
SimulatedKeyUpSet = new HashSet<KeyCode>();
}
/// <summary>
/// Presses a key. <see cref="GetKeyDown(KeyBinding)"/> and <see cref="GetKey(KeyBinding)"/> will be true for the keybinding.
/// <see cref="GetKeyUp(KeyBinding)"/> will no longer be true for the keybinding.
/// </summary>
public static void PressKey(KeyBinding kb)
{
if (kb.TryGetMouseButton(out int mouseButton))
{
SimulatedMouseDownSet.Add(mouseButton);
SimulatedMouseSet.Add(mouseButton);
SimulatedMouseUpSet.Remove(mouseButton);
}
if (kb.TryGetKeyCode(out KeyCode keyCode))
{
SimulatedKeyDownSet.Add(keyCode);
SimulatedKeySet.Add(keyCode);
SimulatedKeyUpSet.Remove(keyCode);
}
}
/// <summary>
/// Releases a key. <see cref="GetKeyUp(KeyBinding)"/> will be true for the keybinding.
/// <see cref="GetKeyDown(KeyBinding)"/> and <see cref="GetKey(KeyBinding)"/> will no longer be true for the keybinding.
/// </summary>
public static void ReleaseKey(KeyBinding kb)
{
if (kb.TryGetMouseButton(out int mouseButton))
{
SimulatedMouseDownSet.Remove(mouseButton);
SimulatedMouseSet.Remove(mouseButton);
SimulatedMouseUpSet.Add(mouseButton);
}
if (kb.TryGetKeyCode(out KeyCode keyCode))
{
SimulatedKeyDownSet.Remove(keyCode);
SimulatedKeySet.Remove(keyCode);
SimulatedKeyUpSet.Add(keyCode);
}
}
/// <summary>
/// Advances the Key press simulation by 1 frame. Keybindings will no longer trigger <see cref="GetKeyDown(KeyBinding)"/> or <see cref="GetKeyUp(KeyBinding)"/>
/// </summary>
public static void AdvanceSimulation()
{
// keys that were just pressed are no longer trigger GetKeyDown
SimulatedMouseDownSet = new HashSet<int>();
SimulatedKeyDownSet = new HashSet<KeyCode>();
// keys that were just released are no longer trigger GetKeyUp
SimulatedMouseUpSet = new HashSet<int>();
SimulatedKeyUpSet = new HashSet<KeyCode>();
}
/// <summary>
/// Test if the key is currently pressed.
/// </summary>
/// <returns>True if the bound key is currently pressed</returns>
public static bool GetKey(KeyBinding kb)
{
if (kb.TryGetMouseButton(out int mouseButton))
{
if (isSimulated)
return SimulatedMouseSet.Contains(mouseButton);
else
return UnityEngine.Input.GetMouseButton(mouseButton);
}
if (kb.TryGetKeyCode(out KeyCode keyCode))
{
if (isSimulated)
return SimulatedKeySet.Contains(keyCode);
else
return UnityEngine.Input.GetKey(keyCode);
}
return false;
}
/// <summary>
/// Test if the key has been pressed since the last frame.
/// </summary>
/// <returns>True if the bound key was pressed since the last frame</returns>
public static bool GetKeyDown(KeyBinding kb)
{
if (kb.TryGetMouseButton(out int mouseButton))
{
if (isSimulated)
return SimulatedMouseDownSet.Contains(mouseButton);
else
return UnityEngine.Input.GetMouseButtonDown(mouseButton);
}
if (kb.TryGetKeyCode(out KeyCode keyCode))
{
if (isSimulated)
return SimulatedKeyDownSet.Contains(keyCode);
else
return UnityEngine.Input.GetKeyDown(keyCode);
}
return false;
}
/// <summary>
/// Test if the key has been released since the last frame.
/// </summary>
/// <returns>True if the bound key was released since the last frame</returns>
public static bool GetKeyUp(KeyBinding kb)
{
if (kb.TryGetMouseButton(out int mouseButton))
{
if (isSimulated)
return SimulatedMouseUpSet.Contains(mouseButton);
else
return UnityEngine.Input.GetMouseButtonUp(mouseButton);
}
if (kb.TryGetKeyCode(out KeyCode keyCode))
{
if (isSimulated)
return SimulatedKeyUpSet.Contains(keyCode);
else
return UnityEngine.Input.GetKeyUp(keyCode);
}
return false;
}
}
}