123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- #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
|