Maze / Library /PackageCache /com.unity.inputsystem@1.6.1 /InputSystem /Plugins /Linux /SDLDeviceBuilder.cs
| using System; | |
| using UnityEngine.InputSystem.LowLevel; | |
| using UnityEngine.InputSystem.Utilities; | |
| using System.Text; | |
| using UnityEngine.InputSystem.Layouts; | |
| namespace UnityEngine.InputSystem.Linux | |
| { | |
| [] | |
| internal class SDLLayoutBuilder | |
| { | |
| [] private string m_ParentLayout; | |
| [] 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(); | |
| } | |
| } | |
| } | |