15#pragma warning disable CS0649
17#pragma warning restore CS0649
35 public struct KeyInfo {
36 public int GamepadIndex;
40 public KeyInfo(
int gamepadIndex,
GamePadButton button,
bool press) {
41 GamepadIndex = gamepadIndex;
47 public struct TriggerInfo {
48 public int GamepadIndex;
49 public GamePadTrigger Trigger;
52 public TriggerInfo(
int gamepadIndex, GamePadTrigger trigger,
float value) {
53 GamepadIndex = gamepadIndex;
59 public static Dictionary<int, int> m_deviceToIndex = [];
60 public static List<int> m_deviceToRemove = [];
61 public static ConcurrentQueue<KeyInfo> m_cachedKeyEvents = [];
62 public static ConcurrentQueue<TriggerInfo> m_cachedTriggerEvents = [];
63 public static readonly
bool[,] m_lastDpadStates =
new bool[4, 4];
64 static readonly
bool[,] m_dpadFromKey =
new bool[4, 4];
65 static readonly
bool[,] m_lastTriggerDown =
new bool[4, 2];
67 const float TRIGGER_DOWN_THRESHOLD = 0.5f;
68 const float TRIGGER_UP_THRESHOLD = 0.4f;
79#if !MOBILE && !BROWSER
89 m_deviceToRemove.Clear();
90 foreach (
int key
in m_deviceToIndex.Keys) {
91 if (InputDevice.GetDevice(key) ==
null) {
92 m_deviceToRemove.Add(key);
95 foreach (
int item
in m_deviceToRemove) {
99 while (m_cachedKeyEvents.TryDequeue(out KeyInfo keyInfo)) {
101 HandleKeyDown(keyInfo.GamepadIndex, keyInfo.Button);
104 HandleKeyUp(keyInfo.GamepadIndex, keyInfo.Button);
107 while (m_cachedTriggerEvents.TryDequeue(out TriggerInfo info)) {
108 int num = TranslateDeviceId(info.GamepadIndex);
111 m_states[num].Triggers[(int)info.Trigger] = info.Value;
116 public static void HandleKeyEvent(KeyEvent e) {
117 m_cachedKeyEvents.Enqueue(
new KeyInfo(TranslateDeviceId(e.DeviceId), TranslateKey(e.KeyCode), e.Action == KeyEventActions.Down));
120 internal static void HandleKeyDown(
int gamepadIndex,
GamePadButton gamePadButton) {
121 if (gamepadIndex < 0) {
127 int idx = gamePadButton
switch {
128 GamePadButton.DPadLeft => 0,
129 GamePadButton.DPadRight => 1,
130 GamePadButton.DPadUp => 2,
131 GamePadButton.DPadDown => 3,
132 _ =>
throw new ArgumentOutOfRangeException()
134 m_dpadFromKey[gamepadIndex, idx] =
true;
136 m_states[gamepadIndex].Buttons[(int)gamePadButton] =
true;
137 switch (gamePadButton) {
144 internal static void HandleKeyUp(
int gamepadIndex,
GamePadButton gamePadButton) {
145 if (gamepadIndex < 0) {
151 int idx = gamePadButton
switch {
152 GamePadButton.DPadLeft => 0,
153 GamePadButton.DPadRight => 1,
154 GamePadButton.DPadUp => 2,
155 GamePadButton.DPadDown => 3,
156 _ =>
throw new ArgumentOutOfRangeException()
158 m_dpadFromKey[gamepadIndex, idx] =
false;
160 m_states[gamepadIndex].Buttons[(int)gamePadButton] =
false;
161 switch (gamePadButton) {
168 internal static void HandleMotionEvent(MotionEvent e) {
169 int gamepadIndex = TranslateDeviceId(e.DeviceId);
170 if (gamepadIndex >= 0) {
171 m_states[gamepadIndex].Sticks[0] =
new Vector2(e.GetAxisValue(Axis.X), 0f - e.GetAxisValue(Axis.Y));
172 m_states[gamepadIndex].Sticks[1] =
new Vector2(e.GetAxisValue(Axis.Z), 0f - e.GetAxisValue(Axis.Rz));
173 float l = MathF.Max(e.GetAxisValue(Axis.Ltrigger), e.GetAxisValue(Axis.Brake));
174 float r = MathF.Max(e.GetAxisValue(Axis.Rtrigger), e.GetAxisValue(Axis.Gas));
175 m_cachedTriggerEvents.Enqueue(
new TriggerInfo(TranslateDeviceId(e.DeviceId),
GamePadTrigger.Left, l));
176 m_cachedTriggerEvents.Enqueue(
new TriggerInfo(TranslateDeviceId(e.DeviceId),
GamePadTrigger.Right, r));
177 float axisX = e.GetAxisValue(Axis.HatX);
178 float axisY = e.GetAxisValue(Axis.HatY);
179 ProcessDpad(gamepadIndex, gamepadIndex, 0, axisX < -0.5f,
GamePadButton.DPadLeft);
180 ProcessDpad(gamepadIndex, gamepadIndex, 1, axisX > 0.5f,
GamePadButton.DPadRight);
181 ProcessDpad(gamepadIndex, gamepadIndex, 2, axisY < -0.5f,
GamePadButton.DPadUp);
182 ProcessDpad(gamepadIndex, gamepadIndex, 3, axisY > 0.5f,
GamePadButton.DPadDown);
186 public static void ProcessDpad(
int gamepadIndex,
int padIndex,
int dpadIndex,
bool current,
GamePadButton button) {
187 if (m_dpadFromKey[padIndex, dpadIndex]) {
190 bool last = m_lastDpadStates[padIndex, dpadIndex];
191 if (current == last) {
194 m_lastDpadStates[padIndex, dpadIndex] = current;
195 m_cachedKeyEvents.Enqueue(
new KeyInfo(gamepadIndex, button, current));
198 public static int TranslateDeviceId(
int deviceId) {
199 if (m_deviceToIndex.TryGetValue(deviceId, out
int value)) {
202 for (
int i = 0; i < 4; i++) {
203 if (!m_deviceToIndex.Values.Contains(i)) {
204 Connect(deviceId, i);
211 public static GamePadButton TranslateKey(Keycode keyCode) => keyCode
switch {
230 public static void Connect(
int deviceId,
int index) {
231 m_deviceToIndex.Add(deviceId, index);
235 public static void Disconnect(
int deviceId) {
236 if (m_deviceToIndex.Remove(deviceId, out
int value)) {
237 m_states[value].IsConnected =
false;
244 for (
int padIndex = 0; padIndex < 4; padIndex++) {
249 if (gamepad ==
null) {
252 string name = gamepad.Name;
253 if (!name.Contains(
"Unmapped")) {
255 if (gamepad.IsConnected) {
256 state.IsConnected =
true;
258 IReadOnlyList<Thumbstick> thumbsticks = gamepad.Thumbsticks;
259 for (
int i = 0; i < 2; i++) {
262 IReadOnlyList<Trigger> triggers = gamepad.Triggers;
263 for (
int i = 0; i < 2; i++) {
264 state.
Triggers[i] = triggers[i].Position;
266 foreach (Button button
in gamepad.Buttons) {
267 switch (button.Name) {
268 case ButtonName.A: state.
Buttons[0] = button.Pressed;
break;
269 case ButtonName.B: state.
Buttons[1] = button.Pressed;
break;
270 case ButtonName.X: state.
Buttons[2] = button.Pressed;
break;
271 case ButtonName.Y: state.
Buttons[3] = button.Pressed;
break;
272 case ButtonName.Back: state.
Buttons[4] = button.Pressed;
break;
273 case ButtonName.Start: state.
Buttons[5] = button.Pressed;
break;
274 case ButtonName.LeftStick: state.
Buttons[6] = button.Pressed;
break;
275 case ButtonName.RightStick: state.
Buttons[7] = button.Pressed;
break;
276 case ButtonName.LeftBumper: state.
Buttons[8] = button.Pressed;
break;
277 case ButtonName.RightBumper: state.
Buttons[9] = button.Pressed;
break;
278 case ButtonName.DPadLeft: state.
Buttons[10] = button.Pressed;
break;
279 case ButtonName.DPadRight: state.
Buttons[12] = button.Pressed;
break;
280 case ButtonName.DPadUp: state.
Buttons[11] = button.Pressed;
break;
281 case ButtonName.DPadDown: state.
Buttons[13] = button.Pressed;
break;
287 state.IsConnected =
false;
294 public static bool IsConnected(
int gamePadIndex) => gamePadIndex < 0 || gamePadIndex >= m_states.Length
295 ?
throw new ArgumentOutOfRangeException(nameof(gamePadIndex))
301 throw new ArgumentOutOfRangeException(nameof(deadZone));
306 float num = result.
Length();
309 result *= num2 / num;
318 throw new ArgumentOutOfRangeException(nameof(deadZone)) :
324 throw new ArgumentOutOfRangeException(nameof(deadZone));
330 return value >= threshold;
336 throw new ArgumentOutOfRangeException(nameof(deadZone));
342 && trigger1 == trigger) {
347 return !current && last;
358 && button1 == button) {
362 return !
m_states[gamePadIndex].Buttons[(int)button] &&
m_states[gamePadIndex].LastButtons[(
int)button];
365 return m_states[gamePadIndex].Buttons[(int)button] && !
m_states[gamePadIndex].LastButtons[(
int)button];
370 if (
m_states[gamePadIndex].Buttons[(
int)button]
371 && !
m_states[gamePadIndex].LastButtons[(
int)button]) {
374 double num =
m_states[gamePadIndex].ButtonsRepeat[(int)button];
385 return state.
Triggers[0] >= threshold
396 m_states[gamePadIndex].ModifierKeyOfCurrentCombo = modifierKey;
427 for (
int i = 0; i <
m_states.Length; i++) {
428 for (
int j = 0; j <
m_states[i].Sticks.Length; j++) {
431 for (
int k = 0; k <
m_states[i].Triggers.Length; k++) {
434 for (
int l = 0; l <
m_states[i].Buttons.Length; l++) {
442 for (
int i = 0; i <
m_states.Length; i++) {
448 for (
int j = 0; j < state.
Buttons.Length; j++) {
462 for (
int k = 0; k < state.
Triggers.Length; k++) {
466 state.ModifierKeyOfCurrentCombo =
null;
472 MathF.Sign(value) * MathF.Max(MathF.Abs(value) - deadZone, 0f) / (1f - deadZone);
474 public static void GamepadConnectedHandler(
int index,
string name) {
475 if (index < 0 || index >=
m_states.Length) {
481 public static void GamepadDisconnectedHandler(
int index) {
482 if (index < 0 || index >=
m_states.Length) {