Input模块增加缓存
This commit is contained in:
parent
843cd5e38e
commit
86a110aacc
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
|
|
||||||
@ -9,6 +10,8 @@ public static class GlyphService
|
|||||||
private static readonly string[] PlayStationGroupHints = { "playstation", "dualshock", "dualsense", "gamepad", "controller" };
|
private static readonly string[] PlayStationGroupHints = { "playstation", "dualshock", "dualsense", "gamepad", "controller" };
|
||||||
private static readonly string[] OtherGamepadGroupHints = { "gamepad", "controller", "joystick" };
|
private static readonly string[] OtherGamepadGroupHints = { "gamepad", "controller", "joystick" };
|
||||||
private static readonly char[] TrimChars = { '{', '}', '<', '>', '\'', '"' };
|
private static readonly char[] TrimChars = { '{', '}', '<', '>', '\'', '"' };
|
||||||
|
private static readonly Dictionary<string, string> DisplayNameCache = new(StringComparer.Ordinal);
|
||||||
|
private static readonly Dictionary<int, string> SpriteTagCache = new();
|
||||||
|
|
||||||
private static InputGlyphDatabase _database;
|
private static InputGlyphDatabase _database;
|
||||||
|
|
||||||
@ -102,7 +105,7 @@ public static class GlyphService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = $"<sprite name=\"{sprite.name}\">";
|
tag = GetSpriteTag(sprite);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,14 +141,21 @@ public static class GlyphService
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DisplayNameCache.TryGetValue(controlPath, out string cachedDisplayName))
|
||||||
|
{
|
||||||
|
return cachedDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
string humanReadable = InputControlPath.ToHumanReadableString(controlPath, InputControlPath.HumanReadableStringOptions.OmitDevice);
|
string humanReadable = InputControlPath.ToHumanReadableString(controlPath, InputControlPath.HumanReadableStringOptions.OmitDevice);
|
||||||
if (!string.IsNullOrWhiteSpace(humanReadable))
|
if (!string.IsNullOrWhiteSpace(humanReadable))
|
||||||
{
|
{
|
||||||
|
DisplayNameCache[controlPath] = humanReadable;
|
||||||
return humanReadable;
|
return humanReadable;
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] parts = controlPath.Split('/');
|
int separatorIndex = controlPath.LastIndexOf('/');
|
||||||
string last = parts[parts.Length - 1].Trim(TrimChars);
|
string last = (separatorIndex >= 0 ? controlPath.Substring(separatorIndex + 1) : controlPath).Trim(TrimChars);
|
||||||
|
DisplayNameCache[controlPath] = last;
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,11 +247,69 @@ public static class GlyphService
|
|||||||
}
|
}
|
||||||
|
|
||||||
string[] hints = GetGroupHints(category);
|
string[] hints = GetGroupHints(category);
|
||||||
string[] tokens = groups.Split(InputBinding.Separator);
|
int tokenStart = 0;
|
||||||
for (int i = 0; i < tokens.Length; i++)
|
for (int i = 0; i <= groups.Length; i++)
|
||||||
{
|
{
|
||||||
string token = tokens[i].Trim();
|
if (i < groups.Length && groups[i] != InputBinding.Separator)
|
||||||
if (ContainsAny(token, hints))
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tokenLength = i - tokenStart;
|
||||||
|
while (tokenLength > 0 && char.IsWhiteSpace(groups[tokenStart]))
|
||||||
|
{
|
||||||
|
tokenStart++;
|
||||||
|
tokenLength--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (tokenLength > 0 && char.IsWhiteSpace(groups[tokenStart + tokenLength - 1]))
|
||||||
|
{
|
||||||
|
tokenLength--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenLength > 0)
|
||||||
|
{
|
||||||
|
string token = groups.Substring(tokenStart, tokenLength);
|
||||||
|
if (ContainsAny(token, hints))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenStart = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetSpriteTag(Sprite sprite)
|
||||||
|
{
|
||||||
|
if (sprite == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int instanceId = sprite.GetInstanceID();
|
||||||
|
if (SpriteTagCache.TryGetValue(instanceId, out string cachedTag))
|
||||||
|
{
|
||||||
|
return cachedTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedTag = $"<sprite name=\"{sprite.name}\">";
|
||||||
|
SpriteTagCache[instanceId] = cachedTag;
|
||||||
|
return cachedTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsAny(string source, string[] hints)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(source) || hints == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < hints.Length; i++)
|
||||||
|
{
|
||||||
|
if (source.IndexOf(hints[i], StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -250,26 +318,6 @@ public static class GlyphService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool MatchesControlPath(string path, InputDeviceWatcher.InputDeviceCategory category)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (category)
|
|
||||||
{
|
|
||||||
case InputDeviceWatcher.InputDeviceCategory.Keyboard:
|
|
||||||
return StartsWithDevice(path, "<Keyboard>") || StartsWithDevice(path, "<Mouse>");
|
|
||||||
case InputDeviceWatcher.InputDeviceCategory.Xbox:
|
|
||||||
return StartsWithDevice(path, "<Gamepad>") || StartsWithDevice(path, "<Joystick>") || ContainsAny(path, XboxGroupHints);
|
|
||||||
case InputDeviceWatcher.InputDeviceCategory.PlayStation:
|
|
||||||
return StartsWithDevice(path, "<Gamepad>") || StartsWithDevice(path, "<Joystick>") || ContainsAny(path, PlayStationGroupHints);
|
|
||||||
default:
|
|
||||||
return StartsWithDevice(path, "<Gamepad>") || StartsWithDevice(path, "<Joystick>") || ContainsAny(path, OtherGamepadGroupHints);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool StartsWithDevice(string path, string deviceTag)
|
private static bool StartsWithDevice(string path, string deviceTag)
|
||||||
{
|
{
|
||||||
return path.StartsWith(deviceTag, StringComparison.OrdinalIgnoreCase);
|
return path.StartsWith(deviceTag, StringComparison.OrdinalIgnoreCase);
|
||||||
@ -290,26 +338,29 @@ public static class GlyphService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ContainsAny(string source, string[] hints)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(source) || hints == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < hints.Length; i++)
|
|
||||||
{
|
|
||||||
if (source.IndexOf(hints[i], StringComparison.OrdinalIgnoreCase) >= 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetEffectivePath(InputBinding binding)
|
private static string GetEffectivePath(InputBinding binding)
|
||||||
{
|
{
|
||||||
return string.IsNullOrWhiteSpace(binding.effectivePath) ? binding.path : binding.effectivePath;
|
return string.IsNullOrWhiteSpace(binding.effectivePath) ? binding.path : binding.effectivePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool MatchesControlPath(string path, InputDeviceWatcher.InputDeviceCategory category)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (category)
|
||||||
|
{
|
||||||
|
case InputDeviceWatcher.InputDeviceCategory.Keyboard:
|
||||||
|
return StartsWithDevice(path, "<Keyboard>") || StartsWithDevice(path, "<Mouse>");
|
||||||
|
case InputDeviceWatcher.InputDeviceCategory.Xbox:
|
||||||
|
return StartsWithDevice(path, "<Gamepad>") || StartsWithDevice(path, "<Joystick>") || ContainsAny(path, XboxGroupHints);
|
||||||
|
case InputDeviceWatcher.InputDeviceCategory.PlayStation:
|
||||||
|
return StartsWithDevice(path, "<Gamepad>") || StartsWithDevice(path, "<Joystick>") || ContainsAny(path, PlayStationGroupHints);
|
||||||
|
default:
|
||||||
|
return StartsWithDevice(path, "<Gamepad>") || StartsWithDevice(path, "<Joystick>") || ContainsAny(path, OtherGamepadGroupHints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -268,7 +268,6 @@ public class InputBindingManager : MonoSingleton<InputBindingManager>
|
|||||||
action.name,
|
action.name,
|
||||||
binding.name,
|
binding.name,
|
||||||
bindingIndex,
|
bindingIndex,
|
||||||
binding.groups?.Split(InputBinding.Separator) ?? Array.Empty<string>(),
|
|
||||||
new BindingPath(binding.path, binding.overridePath),
|
new BindingPath(binding.path, binding.overridePath),
|
||||||
binding
|
binding
|
||||||
));
|
));
|
||||||
@ -281,18 +280,16 @@ public class InputBindingManager : MonoSingleton<InputBindingManager>
|
|||||||
public readonly string parentAction;
|
public readonly string parentAction;
|
||||||
public readonly string compositePart;
|
public readonly string compositePart;
|
||||||
public readonly int bindingIndex;
|
public readonly int bindingIndex;
|
||||||
public readonly string[] group;
|
|
||||||
public readonly BindingPath bindingPath;
|
public readonly BindingPath bindingPath;
|
||||||
public readonly InputBinding inputBinding;
|
public readonly InputBinding inputBinding;
|
||||||
|
|
||||||
public Binding(string name, string parentAction, string compositePart, int bindingIndex,
|
public Binding(string name, string parentAction, string compositePart, int bindingIndex,
|
||||||
string[] group, BindingPath bindingPath, InputBinding inputBinding)
|
BindingPath bindingPath, InputBinding inputBinding)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.parentAction = parentAction;
|
this.parentAction = parentAction;
|
||||||
this.compositePart = compositePart;
|
this.compositePart = compositePart;
|
||||||
this.bindingIndex = bindingIndex;
|
this.bindingIndex = bindingIndex;
|
||||||
this.group = group;
|
|
||||||
this.bindingPath = bindingPath;
|
this.bindingPath = bindingPath;
|
||||||
this.inputBinding = inputBinding;
|
this.inputBinding = inputBinding;
|
||||||
}
|
}
|
||||||
@ -472,7 +469,9 @@ public class InputBindingManager : MonoSingleton<InputBindingManager>
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 在清除之前创建准备好的重绑定的副本
|
// 在清除之前创建准备好的重绑定的副本
|
||||||
var appliedContexts = new HashSet<RebindContext>(Instance.preparedRebinds);
|
HashSet<RebindContext> appliedContexts = Instance.OnApply != null
|
||||||
|
? new HashSet<RebindContext>(Instance.preparedRebinds)
|
||||||
|
: null;
|
||||||
|
|
||||||
foreach (var ctx in Instance.preparedRebinds)
|
foreach (var ctx in Instance.preparedRebinds)
|
||||||
{
|
{
|
||||||
@ -510,7 +509,7 @@ public class InputBindingManager : MonoSingleton<InputBindingManager>
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogError("[InputBindingManager] Failed to apply binds: " + ex);
|
Debug.LogError("[InputBindingManager] Failed to apply binds: " + ex);
|
||||||
Instance.OnApply?.Invoke(false, new HashSet<RebindContext>());
|
Instance.OnApply?.Invoke(false, null);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -523,7 +522,9 @@ public class InputBindingManager : MonoSingleton<InputBindingManager>
|
|||||||
if (!Instance.isApplyPending) return;
|
if (!Instance.isApplyPending) return;
|
||||||
|
|
||||||
// 在清除之前创建准备好的重绑定的副本(用于事件通知)
|
// 在清除之前创建准备好的重绑定的副本(用于事件通知)
|
||||||
var discardedContexts = new HashSet<RebindContext>(Instance.preparedRebinds);
|
HashSet<RebindContext> discardedContexts = Instance.OnApply != null
|
||||||
|
? new HashSet<RebindContext>(Instance.preparedRebinds)
|
||||||
|
: null;
|
||||||
|
|
||||||
Instance.preparedRebinds.Clear();
|
Instance.preparedRebinds.Clear();
|
||||||
Instance.isApplyPending = false;
|
Instance.isApplyPending = false;
|
||||||
@ -554,21 +555,23 @@ public class InputBindingManager : MonoSingleton<InputBindingManager>
|
|||||||
rebindOperation = op
|
rebindOperation = op
|
||||||
.OnApplyBinding((o, path) =>
|
.OnApplyBinding((o, path) =>
|
||||||
{
|
{
|
||||||
|
RebindContext preparedContext = new RebindContext(action, bindingIndex, path);
|
||||||
if (AnyPreparedRebind(path, action, bindingIndex, out var existing))
|
if (AnyPreparedRebind(path, action, bindingIndex, out var existing))
|
||||||
{
|
{
|
||||||
PrepareRebind(new RebindContext(action, bindingIndex, path));
|
PrepareRebind(preparedContext);
|
||||||
PrepareRebind(new RebindContext(existing.action, existing.bindingIndex, NULL_BINDING));
|
PrepareRebind(new RebindContext(existing.action, existing.bindingIndex, NULL_BINDING));
|
||||||
OnRebindConflict?.Invoke(new RebindContext(action, bindingIndex, path), existing);
|
OnRebindConflict?.Invoke(preparedContext, existing);
|
||||||
}
|
}
|
||||||
else if (AnyBindingPath(path, action, bindingIndex, out var dup))
|
else if (AnyBindingPath(path, action, bindingIndex, out var dup))
|
||||||
{
|
{
|
||||||
PrepareRebind(new RebindContext(action, bindingIndex, path));
|
RebindContext conflictingContext = new RebindContext(dup.action, dup.bindingIndex, dup.action.bindings[dup.bindingIndex].path);
|
||||||
|
PrepareRebind(preparedContext);
|
||||||
PrepareRebind(new RebindContext(dup.action, dup.bindingIndex, NULL_BINDING));
|
PrepareRebind(new RebindContext(dup.action, dup.bindingIndex, NULL_BINDING));
|
||||||
OnRebindConflict?.Invoke(new RebindContext(action, bindingIndex, path), new RebindContext(dup.action, dup.bindingIndex, dup.action.bindings[dup.bindingIndex].path));
|
OnRebindConflict?.Invoke(preparedContext, conflictingContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PrepareRebind(new RebindContext(action, bindingIndex, path));
|
PrepareRebind(preparedContext);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.OnComplete(opc =>
|
.OnComplete(opc =>
|
||||||
@ -647,18 +650,17 @@ public class InputBindingManager : MonoSingleton<InputBindingManager>
|
|||||||
|
|
||||||
private void PrepareRebind(RebindContext context)
|
private void PrepareRebind(RebindContext context)
|
||||||
{
|
{
|
||||||
// 如果存在相同操作/绑定的现有重绑定,则移除
|
// Remove any existing prepared state for the same action/binding pair.
|
||||||
preparedRebinds.Remove(context);
|
preparedRebinds.Remove(context);
|
||||||
|
|
||||||
|
BindingPath bindingPath = GetBindingPath(context.action, context.bindingIndex);
|
||||||
|
if (bindingPath == null) return;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(context.overridePath))
|
if (string.IsNullOrEmpty(context.overridePath))
|
||||||
{
|
{
|
||||||
var bp = GetBindingPath(context.action, context.bindingIndex);
|
context.overridePath = bindingPath.bindingPath;
|
||||||
if (bp != null) context.overridePath = bp.bindingPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var bindingPath = GetBindingPath(context.action, context.bindingIndex);
|
|
||||||
if (bindingPath == null) return;
|
|
||||||
|
|
||||||
if (bindingPath.EffectivePath != context.overridePath)
|
if (bindingPath.EffectivePath != context.overridePath)
|
||||||
{
|
{
|
||||||
preparedRebinds.Add(context);
|
preparedRebinds.Add(context);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
#endif
|
#endif
|
||||||
@ -93,6 +94,7 @@ public static class InputDeviceWatcher
|
|||||||
private static InputAction _anyInputAction;
|
private static InputAction _anyInputAction;
|
||||||
private static float _lastSwitchTime = -Mathf.Infinity;
|
private static float _lastSwitchTime = -Mathf.Infinity;
|
||||||
private static DeviceContext _lastEmittedContext = CreateDefaultContext();
|
private static DeviceContext _lastEmittedContext = CreateDefaultContext();
|
||||||
|
private static readonly Dictionary<int, DeviceContext> DeviceContextCache = new();
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
|
|
||||||
public static event Action<InputDeviceCategory> OnDeviceChanged;
|
public static event Action<InputDeviceCategory> OnDeviceChanged;
|
||||||
@ -154,6 +156,7 @@ public static class InputDeviceWatcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
InputSystem.onDeviceChange -= OnDeviceChange;
|
InputSystem.onDeviceChange -= OnDeviceChange;
|
||||||
|
DeviceContextCache.Clear();
|
||||||
|
|
||||||
ApplyContext(CreateDefaultContext(), false);
|
ApplyContext(CreateDefaultContext(), false);
|
||||||
_lastEmittedContext = CurrentContext;
|
_lastEmittedContext = CurrentContext;
|
||||||
@ -171,7 +174,13 @@ public static class InputDeviceWatcher
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceContext deviceContext = BuildContext(control.device);
|
InputDevice device = control.device;
|
||||||
|
if (device == null || device.deviceId == CurrentDeviceId)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceContext deviceContext = BuildContext(device);
|
||||||
if (deviceContext.DeviceId == CurrentDeviceId)
|
if (deviceContext.DeviceId == CurrentDeviceId)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -198,6 +207,7 @@ public static class InputDeviceWatcher
|
|||||||
{
|
{
|
||||||
case InputDeviceChange.Removed:
|
case InputDeviceChange.Removed:
|
||||||
case InputDeviceChange.Disconnected:
|
case InputDeviceChange.Disconnected:
|
||||||
|
DeviceContextCache.Remove(device.deviceId);
|
||||||
if (device.deviceId == CurrentDeviceId)
|
if (device.deviceId == CurrentDeviceId)
|
||||||
{
|
{
|
||||||
PromoteFallbackDevice(device.deviceId);
|
PromoteFallbackDevice(device.deviceId);
|
||||||
@ -205,6 +215,7 @@ public static class InputDeviceWatcher
|
|||||||
break;
|
break;
|
||||||
case InputDeviceChange.Reconnected:
|
case InputDeviceChange.Reconnected:
|
||||||
case InputDeviceChange.Added:
|
case InputDeviceChange.Added:
|
||||||
|
DeviceContextCache.Remove(device.deviceId);
|
||||||
if (CurrentDeviceId < 0 && IsRelevantDevice(device))
|
if (CurrentDeviceId < 0 && IsRelevantDevice(device))
|
||||||
{
|
{
|
||||||
SetCurrentContext(BuildContext(device));
|
SetCurrentContext(BuildContext(device));
|
||||||
@ -271,15 +282,22 @@ public static class InputDeviceWatcher
|
|||||||
return CreateDefaultContext();
|
return CreateDefaultContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DeviceContextCache.TryGetValue(device.deviceId, out DeviceContext cachedContext))
|
||||||
|
{
|
||||||
|
return cachedContext;
|
||||||
|
}
|
||||||
|
|
||||||
TryParseVendorProductIds(device.description.capabilities, out int vendorId, out int productId);
|
TryParseVendorProductIds(device.description.capabilities, out int vendorId, out int productId);
|
||||||
string deviceName = string.IsNullOrWhiteSpace(device.displayName) ? device.name : device.displayName;
|
string deviceName = string.IsNullOrWhiteSpace(device.displayName) ? device.name : device.displayName;
|
||||||
return new DeviceContext(
|
DeviceContext context = new DeviceContext(
|
||||||
DetermineCategoryFromDevice(device),
|
DetermineCategoryFromDevice(device, vendorId),
|
||||||
device.deviceId,
|
device.deviceId,
|
||||||
vendorId,
|
vendorId,
|
||||||
productId,
|
productId,
|
||||||
deviceName,
|
deviceName,
|
||||||
device.layout);
|
device.layout);
|
||||||
|
DeviceContextCache[device.deviceId] = context;
|
||||||
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DeviceContext CreateDefaultContext()
|
private static DeviceContext CreateDefaultContext()
|
||||||
@ -287,7 +305,7 @@ public static class InputDeviceWatcher
|
|||||||
return new DeviceContext(InputDeviceCategory.Keyboard, -1, 0, 0, DefaultKeyboardDeviceName, Keyboard.current != null ? Keyboard.current.layout : string.Empty);
|
return new DeviceContext(InputDeviceCategory.Keyboard, -1, 0, 0, DefaultKeyboardDeviceName, Keyboard.current != null ? Keyboard.current.layout : string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InputDeviceCategory DetermineCategoryFromDevice(InputDevice device)
|
private static InputDeviceCategory DetermineCategoryFromDevice(InputDevice device, int vendorId = 0)
|
||||||
{
|
{
|
||||||
if (device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
@ -301,18 +319,17 @@ public static class InputDeviceWatcher
|
|||||||
|
|
||||||
if (IsGamepadLike(device))
|
if (IsGamepadLike(device))
|
||||||
{
|
{
|
||||||
return GetGamepadCategory(device);
|
return GetGamepadCategory(device, vendorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
string combined = CombineDeviceDescription(device);
|
if (DescriptionContains(device, "xbox") || DescriptionContains(device, "xinput"))
|
||||||
if (ContainsIgnoreCase(combined, "xbox") || ContainsIgnoreCase(combined, "xinput"))
|
|
||||||
{
|
{
|
||||||
return InputDeviceCategory.Xbox;
|
return InputDeviceCategory.Xbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ContainsIgnoreCase(combined, "dualshock")
|
if (DescriptionContains(device, "dualshock")
|
||||||
|| ContainsIgnoreCase(combined, "dualsense")
|
|| DescriptionContains(device, "dualsense")
|
||||||
|| ContainsIgnoreCase(combined, "playstation"))
|
|| DescriptionContains(device, "playstation"))
|
||||||
{
|
{
|
||||||
return InputDeviceCategory.PlayStation;
|
return InputDeviceCategory.PlayStation;
|
||||||
}
|
}
|
||||||
@ -367,7 +384,7 @@ public static class InputDeviceWatcher
|
|||||||
|| ContainsIgnoreCase(layout, "Joystick");
|
|| ContainsIgnoreCase(layout, "Joystick");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InputDeviceCategory GetGamepadCategory(InputDevice device)
|
private static InputDeviceCategory GetGamepadCategory(InputDevice device, int vendorId = 0)
|
||||||
{
|
{
|
||||||
if (device == null)
|
if (device == null)
|
||||||
{
|
{
|
||||||
@ -380,28 +397,29 @@ public static class InputDeviceWatcher
|
|||||||
return InputDeviceCategory.Xbox;
|
return InputDeviceCategory.Xbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryParseVendorProductIds(device.description.capabilities, out int vendorId, out _))
|
if (vendorId == 0 && TryParseVendorProductIds(device.description.capabilities, out int parsedVendorId, out _))
|
||||||
{
|
{
|
||||||
if (vendorId == 0x045E || vendorId == 1118)
|
vendorId = parsedVendorId;
|
||||||
{
|
|
||||||
return InputDeviceCategory.Xbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vendorId == 0x054C || vendorId == 1356)
|
|
||||||
{
|
|
||||||
return InputDeviceCategory.PlayStation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string combined = CombineDeviceDescription(device);
|
if (vendorId == 0x045E || vendorId == 1118)
|
||||||
if (ContainsIgnoreCase(combined, "xbox"))
|
|
||||||
{
|
{
|
||||||
return InputDeviceCategory.Xbox;
|
return InputDeviceCategory.Xbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ContainsIgnoreCase(combined, "dualshock")
|
if (vendorId == 0x054C || vendorId == 1356)
|
||||||
|| ContainsIgnoreCase(combined, "dualsense")
|
{
|
||||||
|| ContainsIgnoreCase(combined, "playstation"))
|
return InputDeviceCategory.PlayStation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DescriptionContains(device, "xbox"))
|
||||||
|
{
|
||||||
|
return InputDeviceCategory.Xbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DescriptionContains(device, "dualshock")
|
||||||
|
|| DescriptionContains(device, "dualsense")
|
||||||
|
|| DescriptionContains(device, "playstation"))
|
||||||
{
|
{
|
||||||
return InputDeviceCategory.PlayStation;
|
return InputDeviceCategory.PlayStation;
|
||||||
}
|
}
|
||||||
@ -409,14 +427,20 @@ public static class InputDeviceWatcher
|
|||||||
return InputDeviceCategory.Other;
|
return InputDeviceCategory.Other;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string CombineDeviceDescription(InputDevice device)
|
private static bool DescriptionContains(InputDevice device, string value)
|
||||||
{
|
{
|
||||||
return string.Concat(
|
if (device == null)
|
||||||
device.description.interfaceName, " ",
|
{
|
||||||
device.layout, " ",
|
return false;
|
||||||
device.description.product, " ",
|
}
|
||||||
device.description.manufacturer, " ",
|
|
||||||
device.displayName);
|
var description = device.description;
|
||||||
|
return ContainsIgnoreCase(description.interfaceName, value)
|
||||||
|
|| ContainsIgnoreCase(device.layout, value)
|
||||||
|
|| ContainsIgnoreCase(description.product, value)
|
||||||
|
|| ContainsIgnoreCase(description.manufacturer, value)
|
||||||
|
|| ContainsIgnoreCase(device.displayName, value)
|
||||||
|
|| ContainsIgnoreCase(device.name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryParseVendorProductIds(string capabilities, out int vendorId, out int productId)
|
private static bool TryParseVendorProductIds(string capabilities, out int vendorId, out int productId)
|
||||||
|
|||||||
@ -45,6 +45,7 @@ public sealed class InputGlyphDatabase : ScriptableObject
|
|||||||
InputDeviceWatcher.InputDeviceCategory.Xbox,
|
InputDeviceWatcher.InputDeviceCategory.Xbox,
|
||||||
InputDeviceWatcher.InputDeviceCategory.Keyboard,
|
InputDeviceWatcher.InputDeviceCategory.Keyboard,
|
||||||
};
|
};
|
||||||
|
private static readonly Dictionary<string, string> NormalizedPathCache = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
public List<DeviceGlyphTable> tables = new List<DeviceGlyphTable>();
|
public List<DeviceGlyphTable> tables = new List<DeviceGlyphTable>();
|
||||||
public Sprite placeholderSprite;
|
public Sprite placeholderSprite;
|
||||||
@ -253,7 +254,14 @@ public sealed class InputGlyphDatabase : ScriptableObject
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CanonicalizeDeviceLayout(controlPath.Trim().ToLowerInvariant());
|
if (NormalizedPathCache.TryGetValue(controlPath, out string normalizedPath))
|
||||||
|
{
|
||||||
|
return normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedPath = CanonicalizeDeviceLayout(controlPath.Trim().ToLowerInvariant());
|
||||||
|
NormalizedPathCache[controlPath] = normalizedPath;
|
||||||
|
return normalizedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string CanonicalizeDeviceLayout(string controlPath)
|
private static string CanonicalizeDeviceLayout(string controlPath)
|
||||||
|
|||||||
@ -12,6 +12,7 @@ public sealed class InputGlyphText : InputGlyphBehaviourBase
|
|||||||
private TMP_Text _textField;
|
private TMP_Text _textField;
|
||||||
private string _templateText;
|
private string _templateText;
|
||||||
private string _cachedFormattedText;
|
private string _cachedFormattedText;
|
||||||
|
private string _cachedReplacementToken;
|
||||||
|
|
||||||
protected override void OnEnable()
|
protected override void OnEnable()
|
||||||
{
|
{
|
||||||
@ -35,16 +36,23 @@ public sealed class InputGlyphText : InputGlyphBehaviourBase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string formattedText;
|
string replacementToken;
|
||||||
if (GlyphService.TryGetTMPTagForActionPath(actionReference, compositePartName, CurrentCategory, out string tag, out string displayFallback))
|
if (GlyphService.TryGetTMPTagForActionPath(actionReference, compositePartName, CurrentCategory, out string tag, out string displayFallback))
|
||||||
{
|
{
|
||||||
formattedText = Utility.Text.Format(_templateText, tag);
|
replacementToken = tag;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
formattedText = Utility.Text.Format(_templateText, displayFallback);
|
replacementToken = displayFallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_cachedReplacementToken == replacementToken)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cachedReplacementToken = replacementToken;
|
||||||
|
string formattedText = Utility.Text.Format(_templateText, replacementToken);
|
||||||
if (_cachedFormattedText != formattedText)
|
if (_cachedFormattedText != formattedText)
|
||||||
{
|
{
|
||||||
_cachedFormattedText = formattedText;
|
_cachedFormattedText = formattedText;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user