IndieGame/client/Packages/com.unity.inputsystem@1.7.0/InputSystem/Plugins/Linux/SDLDeviceBuilder.cs

289 lines
12 KiB
C#
Raw Normal View History

2024-10-11 10:12:15 +08:00
#if UNITY_EDITOR || UNITY_STANDALONE_LINUX
using System;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
using System.Text;
using UnityEngine.InputSystem.Layouts;
namespace UnityEngine.InputSystem.Linux
{
[Serializable]
internal class SDLLayoutBuilder
{
[SerializeField] private string m_ParentLayout;
[SerializeField] private SDLDeviceDescriptor m_Descriptor;
internal static string OnFindLayoutForDevice(ref InputDeviceDescription description, string matchedLayout,
InputDeviceExecuteCommandDelegate executeCommandDelegate)
{
if (description.interfaceName != LinuxSupport.kInterfaceName)
return null;
if (string.IsNullOrEmpty(description.capabilities))
return null;
// Try to parse the SDL descriptor.
SDLDeviceDescriptor deviceDescriptor;
try
{
deviceDescriptor = SDLDeviceDescriptor.FromJson(description.capabilities);
}
catch (Exception exception)
{
Debug.LogError($"{exception} while trying to parse descriptor for SDL device: {description.capabilities}");
return null;
}
if (deviceDescriptor == null)
return null;
string layoutName;
if (string.IsNullOrEmpty(description.manufacturer))
{
layoutName = $"{SanitizeName(description.interfaceName)}::{SanitizeName(description.product)}";
}
else
{
layoutName =
$"{SanitizeName(description.interfaceName)}::{SanitizeName(description.manufacturer)}::{SanitizeName(description.product)}";
}
var layout = new SDLLayoutBuilder { m_Descriptor = deviceDescriptor, m_ParentLayout = matchedLayout };
InputSystem.RegisterLayoutBuilder(() => layout.Build(), layoutName, matchedLayout);
return layoutName;
}
private static string SanitizeName(string originalName)
{
var stringLength = originalName.Length;
var sanitizedName = new StringBuilder(stringLength);
for (var i = 0; i < stringLength; i++)
{
var letter = originalName[i];
if (char.IsUpper(letter) || char.IsLower(letter) || char.IsDigit(letter))
sanitizedName.Append(letter);
}
return sanitizedName.ToString();
}
private static bool IsAxis(SDLFeatureDescriptor feature, SDLAxisUsage axis)
{
return feature.featureType == JoystickFeatureType.Axis
&& feature.usageHint == (int)axis;
}
private static void BuildStickFeature(ref InputControlLayout.Builder builder, SDLFeatureDescriptor xFeature, SDLFeatureDescriptor yFeature)
{
int byteOffset;
if (xFeature.offset <= yFeature.offset)
byteOffset = xFeature.offset;
else
byteOffset = yFeature.offset;
const string stickName = "Stick";
builder.AddControl(stickName)
.WithLayout("Stick")
.WithByteOffset((uint)byteOffset)
.WithSizeInBits((uint)xFeature.featureSize * 8 + (uint)yFeature.featureSize * 8)
.WithUsages(CommonUsages.Primary2DMotion);
builder.AddControl(stickName + "/x")
.WithFormat(InputStateBlock.FormatInt)
.WithByteOffset(0)
.WithSizeInBits((uint)xFeature.featureSize * 8)
.WithParameters("clamp=1,clampMin=-1,clampMax=1,scale,scaleFactor=65538");
builder.AddControl(stickName + "/y")
.WithFormat(InputStateBlock.FormatInt)
.WithByteOffset(4)
.WithSizeInBits((uint)xFeature.featureSize * 8)
.WithParameters("clamp=1,clampMin=-1,clampMax=1,scale,scaleFactor=65538,invert");
builder.AddControl(stickName + "/up")
.WithParameters("clamp=1,clampMin=-1,clampMax=0,scale,scaleFactor=65538,invert");
builder.AddControl(stickName + "/down")
.WithParameters("clamp=1,clampMin=0,clampMax=1,scale,scaleFactor=65538,invert=false");
builder.AddControl(stickName + "/left")
.WithParameters("clamp=1,clampMin=-1,clampMax=0,scale,scaleFactor=65538,invert");
builder.AddControl(stickName + "/right")
.WithParameters("clamp=1,clampMin=0,clampMax=1,scale,scaleFactor=65538");
}
private static bool IsHatX(SDLFeatureDescriptor feature)
{
return feature.featureType == JoystickFeatureType.Hat
&& (feature.usageHint == (int)SDLAxisUsage.Hat0X
|| feature.usageHint == (int)SDLAxisUsage.Hat1X
|| feature.usageHint == (int)SDLAxisUsage.Hat2X
|| feature.usageHint == (int)SDLAxisUsage.Hat3X);
}
private static bool IsHatY(SDLFeatureDescriptor feature)
{
return feature.featureType == JoystickFeatureType.Hat
&& (feature.usageHint == (int)SDLAxisUsage.Hat0Y
|| feature.usageHint == (int)SDLAxisUsage.Hat1Y
|| feature.usageHint == (int)SDLAxisUsage.Hat2Y
|| feature.usageHint == (int)SDLAxisUsage.Hat3Y);
}
private static int HatNumber(SDLFeatureDescriptor feature)
{
Debug.Assert(feature.featureType == JoystickFeatureType.Hat);
return 1 + (feature.usageHint - (int)SDLAxisUsage.Hat0X) / 2;
}
private static void BuildHatFeature(ref InputControlLayout.Builder builder, SDLFeatureDescriptor xFeature, SDLFeatureDescriptor yFeature)
{
Debug.Assert(xFeature.offset < yFeature.offset, "Order of features must be X followed by Y");
var hat = HatNumber(xFeature);
var hatName = hat > 1 ? $"Hat{hat}" : "Hat";
builder.AddControl(hatName)
.WithLayout("Dpad")
.WithByteOffset((uint)xFeature.offset)
.WithSizeInBits((uint)xFeature.featureSize * 8 + (uint)yFeature.featureSize * 8)
.WithUsages(CommonUsages.Hatswitch);
builder.AddControl(hatName + "/up")
.WithFormat(InputStateBlock.FormatInt)
.WithParameters("scale,scaleFactor=2147483647,clamp,clampMin=-1,clampMax=0,invert")
.WithByteOffset(4)
.WithBitOffset(0)
.WithSizeInBits((uint)yFeature.featureSize * 8);
builder.AddControl(hatName + "/down")
.WithFormat(InputStateBlock.FormatInt)
.WithParameters("scale,scaleFactor=2147483647,clamp,clampMin=0,clampMax=1")
.WithByteOffset(4)
.WithBitOffset(0)
.WithSizeInBits((uint)yFeature.featureSize * 8);
builder.AddControl(hatName + "/left")
.WithFormat(InputStateBlock.FormatInt)
.WithParameters("scale,scaleFactor=2147483647,clamp,clampMin=-1,clampMax=0,invert")
.WithByteOffset(0)
.WithBitOffset(0)
.WithSizeInBits((uint)xFeature.featureSize * 8);
builder.AddControl(hatName + "/right")
.WithFormat(InputStateBlock.FormatInt)
.WithParameters("scale,scaleFactor=2147483647,clamp,clampMin=0,clampMax=1")
.WithByteOffset(0)
.WithBitOffset(0)
.WithSizeInBits((uint)xFeature.featureSize * 8);
}
internal InputControlLayout Build()
{
var builder = new InputControlLayout.Builder
{
stateFormat = new FourCC('L', 'J', 'O', 'Y'),
extendsLayout = m_ParentLayout
};
for (var i = 0; i < m_Descriptor.controls.LengthSafe(); i++)
{
var feature = m_Descriptor.controls[i];
switch (feature.featureType)
{
case JoystickFeatureType.Axis:
{
var usage = (SDLAxisUsage)feature.usageHint;
var featureName = LinuxSupport.GetAxisNameFromUsage(usage);
var parameters = "scale,scaleFactor=65538,clamp=1,clampMin=-1,clampMax=1";
// If X is followed by Y, build a stick out of the two.
if (IsAxis(feature, SDLAxisUsage.X) && i + 1 < m_Descriptor.controls.Length)
{
var nextFeature = m_Descriptor.controls[i + 1];
if (IsAxis(nextFeature, SDLAxisUsage.Y))
{
BuildStickFeature(ref builder, feature, nextFeature);
++i;
continue;
}
}
if (IsAxis(feature, SDLAxisUsage.Y))
parameters += ",invert";
var control = builder.AddControl(featureName)
.WithLayout("Analog")
.WithByteOffset((uint)feature.offset)
.WithFormat(InputStateBlock.FormatInt)
.WithParameters(parameters);
if (IsAxis(feature, SDLAxisUsage.RotateZ))
control.WithUsages(CommonUsages.Twist);
break;
}
case JoystickFeatureType.Ball:
{
//TODO
break;
}
case JoystickFeatureType.Button:
{
var usage = (SDLButtonUsage)feature.usageHint;
var featureName = LinuxSupport.GetButtonNameFromUsage(usage);
if (featureName != null)
{
builder.AddControl(featureName)
.WithLayout("Button")
.WithByteOffset((uint)feature.offset)
.WithBitOffset((uint)feature.bit)
.WithFormat(InputStateBlock.FormatBit);
}
break;
}
case JoystickFeatureType.Hat:
{
var usage = (SDLAxisUsage)feature.usageHint;
var featureName = LinuxSupport.GetAxisNameFromUsage(usage);
var parameters = "scale,scaleFactor=2147483647,clamp=1,clampMin=-1,clampMax=1";
if (i + 1 < m_Descriptor.controls.Length)
{
var nextFeature = m_Descriptor.controls[i + 1];
if (IsHatY(nextFeature) && HatNumber(feature) == HatNumber(nextFeature))
{
BuildHatFeature(ref builder, feature, nextFeature);
++i;
continue;
}
}
if (IsHatY(feature))
parameters += ",invert";
builder.AddControl(featureName)
.WithLayout("Analog")
.WithByteOffset((uint)feature.offset)
.WithFormat(InputStateBlock.FormatInt)
.WithParameters(parameters);
break;
}
default:
{
throw new NotImplementedException(
$"SDLLayoutBuilder.Build: Trying to build an SDL device with an unknown feature of type {feature.featureType}.");
}
}
}
return builder.Build();
}
}
}
#endif // UNITY_EDITOR || UNITY_STANDALONE_LINUX