////TODO: return Substring and use path parser; should get rid of allocations // From the given control path, try to determine the control layout being used. // // NOTE: This function will only use information available in the path itself or // in layouts referenced by the path. It will not look at actual devices // in the system. This is to make the behavior predictable and not dependent // on whether you currently have the right device connected or not. // NOTE: Allocates! public static string TryGetControlLayout(string path) { if (path == null) { throw new ArgumentNullException(nameof(path)); } var pathLength = path.Length; var indexOfLastSlash = path.LastIndexOf('/'); if (indexOfLastSlash == -1 || indexOfLastSlash == 0) { // If there's no '/' at all in the path, it surely does not mention // a control. Same if the '/' is the first thing in the path. return(null); } // Simplest case where control layout is mentioned explicitly with '<..>'. // Note this will only catch if the control is *only* referenced by layout and not by anything else // in addition (like usage or name). if (pathLength > indexOfLastSlash + 2 && path[indexOfLastSlash + 1] == '<' && path[pathLength - 1] == '>') { var layoutNameStart = indexOfLastSlash + 2; var layoutNameLength = pathLength - layoutNameStart - 1; return(path.Substring(layoutNameStart, layoutNameLength)); } // Have to actually look at the path in detail. var parser = new PathParser(path); if (!parser.MoveToNextComponent()) { return(null); } if (parser.current.isWildcard) { throw new NotImplementedException(); } if (parser.current.layout.length == 0) { return(null); } var deviceLayoutName = parser.current.layout.ToString(); if (!parser.MoveToNextComponent()) { return(null); // No control component. } if (parser.current.isWildcard) { return(Wildcard); } return(FindControlLayoutRecursive(ref parser, deviceLayoutName)); }
/// <summary> /// From the given control path, try to determine the device layout being used. /// </summary> /// <remarks> /// This function will only use information available in the path itself or /// in layouts referenced by the path. It will not look at actual devices /// in the system. This is to make the behavior predictable and not dependent /// on whether you currently have the right device connected or not. /// </remarks> /// <param name="path">A control path (like "/<gamepad>/leftStick")</param> /// <returns>The name of the device layout used by the given control path or null /// if the path does not specify a device layout or does so in a way that is not /// supported by the function.</returns> /// <exception cref="ArgumentNullException"><paramref name="path"/> is null</exception> /// <example> /// <code> /// InputControlPath.TryGetDeviceLayout("/<gamepad>/leftStick"); // Returns "gamepad". /// InputControlPath.TryGetDeviceLayout("/*/leftStick"); // Returns "*". /// InputControlPath.TryGetDeviceLayout("/gamepad/leftStick"); // Returns null. "gamepad" is a device name here. /// </code> /// </example> public static string TryGetDeviceLayout(string path) { if (path == null) { throw new ArgumentNullException(nameof(path)); } var parser = new PathParser(path); if (!parser.MoveToNextComponent()) { return(null); } if (parser.current.layout.length > 0) { return(parser.current.layout.ToString()); } if (parser.current.isWildcard) { return(Wildcard); } return(null); }
/// <summary> // From the given control path, try to determine the device template being used. /// </summary> /// <remarks> /// This function will only use information available in the path itself or /// in templates referenced by the path. It will not look at actual devices /// in the system. This is to make the behavior predictable and not dependent /// on whether you currently have the right device connected or not. /// /// Note that this function allocates and causes GC. /// </remarks> /// <param name="path">A control path (like "/<gamepad>/leftStick")</param> /// <returns>The name of the device template used by the given control path or null /// if the path does not specify a device template or does so in a way that is not /// supported by the function.</returns> /// <exception cref="ArgumentNullException"><paramref name="path"/> is null</exception> /// <example> /// <code> /// InputControlPath.TryGetDeviceTemplate("/<gamepad>/leftStick"); // Returns "gamepad". /// InputControlPath.TryGetDeviceTemplate("/*/leftStick"); // Returns "*". /// InputControlPath.TryGetDeviceTemplate("/gamepad/leftStick"); // Returns null. "gamepad" is a device name here. /// </code> /// </example> public static string TryGetDeviceTemplate(string path) { if (path == null) { throw new ArgumentNullException("path"); } var parser = new PathParser(path); if (!parser.MoveToNextComponent()) { return(null); } if (parser.current.template.length > 0) { return(parser.current.template.ToString()); } if (parser.current.isWildcard) { return(kWildcard); } return(null); }
/// <summary> /// Create a human readable string from the given control path. /// </summary> /// <param name="path">A control path such as "<XRController>{LeftHand}/position".</param> /// <returns>A string such as "leftStick/x [Gamepad]".</returns> public static string ToHumanReadableString(string path) { if (string.IsNullOrEmpty(path)) { return(string.Empty); } var buffer = new StringBuilder(); var parser = new PathParser(path); ////REVIEW: ideally, we'd use display names of controls rather than the control paths directly from the path // First level is taken to be device. if (parser.MoveToNextComponent()) { var device = parser.current.ToHumanReadableString(); // Any additional levels (if present) are taken to form a control path on the device. var isFirstControlLevel = true; while (parser.MoveToNextComponent()) { if (!isFirstControlLevel) { buffer.Append('/'); } buffer.Append(parser.current.ToHumanReadableString()); isFirstControlLevel = false; } if (!string.IsNullOrEmpty(device)) { buffer.Append(" ["); buffer.Append(device); buffer.Append(']'); } } // If we didn't manage to figure out a display name, default to displaying // the path as is. if (buffer.Length == 0) { return(path); } return(buffer.ToString()); }
private static bool MatchesRecursive(ref PathParser parser, InputControl currentControl) { // Recurse into parent before looking at the current control. This // will advance the parser to where our control is in the path. var parent = currentControl.parent; if (parent != null && !MatchesRecursive(ref parser, parent)) { return(false); } // Fail if there's no more path left. if (!parser.MoveToNextComponent()) { return(false); } // Match current path component against current control. return(parser.current.Matches(currentControl)); }
public static string[] TryGetDeviceUsages(string path) { if (path == null) { throw new ArgumentNullException(nameof(path)); } var parser = new PathParser(path); if (!parser.MoveToNextComponent()) { return(null); } if (parser.current.usages != null && parser.current.usages.Length > 0) { return(Array.ConvertAll <Substring, string>(parser.current.usages, i => { return i.ToString(); })); } return(null); }
/// <summary> /// Create a human readable string from the given control path. /// </summary> /// <param name="path">A control path such as "<XRController>{LeftHand}/position".</param> /// <returns>A string such as "Gamepad leftStick/x".</returns> public static string ToHumanReadableString(string path) { if (string.IsNullOrEmpty(path)) { return(string.Empty); } var result = string.Empty; var parser = new PathParser(path); ////REVIEW: ideally, we'd use display names of controls rather than the control paths directly from the path // First level is taken to be device. if (parser.MoveToNextComponent()) { // If all we have is a usage, create a simple string with just that. if (parser.current.isWildcard && parser.current.layout.isEmpty && parser.current.usage.isEmpty) { var savedParser = parser; if (parser.MoveToNextComponent() && !parser.current.usage.isEmpty && parser.current.name.isEmpty && parser.current.layout.isEmpty) { var usage = parser.current.usage.ToString(); if (!parser.MoveToNextComponent()) { return(usage); } } // Reset. parser = savedParser; } result += parser.current.ToHumanReadableString(); // Any additional levels (if present) are taken to form a control path on the device. var isFirstControlLevel = true; while (parser.MoveToNextComponent()) { if (!isFirstControlLevel) { result += '/'; } else { result += ' '; } result += parser.current.ToHumanReadableString(); isFirstControlLevel = false; } } // If we didn't manage to figure out a display name, default to displaying // the path as is. if (string.IsNullOrEmpty(result)) { return(path); } return(result); }