Skip to content

Commit

Permalink
Input actions and em. head impl end
Browse files Browse the repository at this point in the history
  • Loading branch information
KimihikoAkayasaki committed Oct 25, 2024
1 parent c5a6cf2 commit 90c218a
Show file tree
Hide file tree
Showing 11 changed files with 576 additions and 162 deletions.
76 changes: 52 additions & 24 deletions Amethyst.Plugins.Contract/Actions.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
using System;
using System.Diagnostics.CodeAnalysis;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace Amethyst.Plugins.Contract;

// Input action declaration for key events
public abstract class KeyInputAction : IComparable<KeyInputAction>, IEquatable<KeyInputAction>
public interface IKeyInputAction : IComparable<IKeyInputAction>, IEquatable<IKeyInputAction>
{
/// <summary>
/// Identifies the action
/// </summary>
public Guid Guid { get; init; } = Guid.Empty;
public string Guid { get; init; }

/// <summary>
/// Friendly name of the action
/// </summary>
public string Name { get; set; } = string.Empty;
public string Name { get; set; }

/// <summary>
/// Action description for binding UI
/// </summary>
public string Description { get; set; } = string.Empty;
public string Description { get; set; }

/// <summary>
/// Action image for binding UI to be shown in info
Expand All @@ -37,23 +36,57 @@ public abstract class KeyInputAction : IComparable<KeyInputAction>, IEquatable<K
/// </summary>
public Action<object?> Invoke => _ => { };

/// <summary>
/// Checks whether the action is used for anything
/// </summary>
public bool IsUsed => false;

/// <summary>
/// Action data type (shortcut)
/// </summary>
public Type DataType => typeof(object);
}

// Input action declaration for key events
public class KeyInputAction<T> : IKeyInputAction
{
/// <summary>
/// Identifies the action
/// </summary>
public string Guid { get; init; } = System.Guid.NewGuid().ToString();

/// <summary>
/// Friendly name of the action
/// </summary>
public string Name { get; set; } = "INVALID";

/// <summary>
/// Action description for binding UI
/// </summary>
public string Description { get; set; } = string.Empty;

/// <summary>
/// Action image for binding UI to be shown in info
/// MUST BE OF TYPE Microsoft.UI.Xaml.Controls.Image
/// </summary>
/// <remarks>
/// Make this a getter and return an Image constructed
/// during OnLoad, expect COM-related crashes otherwise
/// </remarks>
public object? Image { get; set; }

/// <summary>
/// Implement comparator with other actions (by Guid)
/// </summary>
public int CompareTo(KeyInputAction? other)
public int CompareTo(IKeyInputAction? other)
{
return Guid.CompareTo(other?.Guid);
return string.Compare(Guid, other?.Guid, StringComparison.Ordinal);
}

/// <summary>
/// Implement comparator with other actions (by Guid)
/// </summary>
public bool Equals(KeyInputAction? other)
public bool Equals(IKeyInputAction? other)
{
return Guid.Equals(other?.Guid);
}
Expand All @@ -63,7 +96,7 @@ public bool Equals(KeyInputAction? other)
/// </summary>
public override bool Equals(object? obj)
{
return Equals(obj as KeyInputAction);
return Equals(obj as IKeyInputAction);
}

/// <summary>
Expand All @@ -73,30 +106,25 @@ public override int GetHashCode()
{
return Guid.GetHashCode();
}
}

// Input action declaration for key events
public class KeyInputAction<T> : KeyInputAction
{
[SetsRequiredMembers]
public KeyInputAction()
{
Guid = Guid.NewGuid();
Name = "INVALID";
}

/// <summary>
/// Host import for Invoke() calls
/// Host import for Invoke() calls and stuff
/// Func so you can use it in static context
/// </summary>
public IAmethystHost? Host { get; set; }
public Func<IAmethystHost?> GetHost { get; set; } = () => null;

/// <summary>
/// Invoke the action (shortcut)
/// </summary>
public new Action<T?> Invoke => data => Host?.ReceiveKeyInput(this, data);
public Action<T?> Invoke => data => GetHost()?.ReceiveKeyInput(this, data);

/// <summary>
/// Checks whether the action is used for anything
/// </summary>
public bool IsUsed => GetHost()?.CheckInputActionIsUsed(this) ?? false;

/// <summary>
/// Action data type (shortcut)
/// </summary>
public new Type DataType => typeof(T);
public Type DataType => typeof(T);
}
2 changes: 1 addition & 1 deletion Amethyst.Plugins.Contract/Amethyst.Plugins.Contract.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>Amethyst Device Plugin API (Contract)</Title>
<FileVersion></FileVersion>
<Version>0.3.27-alpha</Version>
<Version>0.3.32-alpha</Version>
<Platforms>x64</Platforms>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion Amethyst.Plugins.Contract/Classes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public Quaternion Orientation
/// </summary>
[JsonIgnore]
[IgnoreDataMember]
public SortedSet<KeyInputAction> SupportedInputActions { get; init; } = new();
public SortedSet<IKeyInputAction> SupportedInputActions { get; init; } = new();
}

[DataContract]
Expand Down
22 changes: 18 additions & 4 deletions Amethyst.Plugins.Contract/Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ public interface IServiceEndpoint
/// You will not be able to receive actions from unsupported TrackerType either
/// </summary>
[DefaultValue(null)]
public Dictionary<TrackerType, SortedSet<KeyInputAction>> SupportedInputActions { get; }
public Dictionary<TrackerType, SortedSet<IKeyInputAction>> SupportedInputActions { get; }

/// <summary>
/// Get the absolute pose of the HMD, calibrated against the play space
Expand Down Expand Up @@ -352,7 +352,7 @@ public interface IServiceEndpoint
/// <summary>
/// Process a key input event sent by a device, that was assigned and found
/// </summary>
public Task ProcessKeyInput<T>(KeyInputAction<T> action, T? data,
public Task ProcessKeyInput(IKeyInputAction action, object? data,
TrackerType? receiver, CancellationToken? token = null);
}

Expand Down Expand Up @@ -425,6 +425,11 @@ public interface IAmethystHost
/// </summary>
bool IsTrackedJointValid(TrackedJointType jointType);

/// <summary>
/// Check if a tracker with the specified role is enabled and active
/// </summary>
bool IsTrackerEnabled(TrackerType trackerType);

/// <summary>
/// Lock the main update loop while in scope with [lock (UpdateThreadLock) { }]
/// This will block AME from updating while locked, and also wait for when the/
Expand Down Expand Up @@ -490,15 +495,24 @@ void Log(object message, LogSeverity severity = LogSeverity.Info, [CallerLineNum
/// <param name="data">
/// Data to be sent, involved with the action
/// </param>
void ReceiveKeyInput<T>(KeyInputAction<T> action, T? data);
void ReceiveKeyInput(IKeyInputAction action, object? data);

/// <summary>
/// Check whether a KeyInputAction is used for anything
/// Devices may use this to skip updating unused actions
/// </summary>
/// <param name="action">
/// Definition of the input action to validate
/// </param>
bool CheckInputActionIsUsed(IKeyInputAction action);
}

/// <summary>
/// Implement this interface and put its type inside your plugin metadata
/// under "DependencyInstaller" for dependency installation functionality
/// Note: you should only use basic/local functionality, as it is unknown
/// whether the load context will contain any external libraries you may
/// be relying on - like framework or device proprietary SDKs and such, etc
/// be relying on - like framework or device proprietary SDKs and such, etc.
/// </summary>
public interface IDependencyInstaller
{
Expand Down
33 changes: 30 additions & 3 deletions Amethyst/Classes/AppTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public class AppTracker : INotifyPropertyChanged
// Input actions: < SERVICE : < SERVICE ACTION DATA : DEVICE ACTION DATA > >
// For "disabled" actions InputActionSource is null, for "hidden" - nothing
// Use InputActionsMap for faster and easier data access, along with bindings
public SortedDictionary<string, Dictionary<InputActionEndpoint, InputActionSource>> InputActions = [];
public SortedDictionary<string, JsonDictionary<InputActionEndpoint, InputActionSource>> InputActions = [];
public Vector3 OrientationOffset = new(0, 0, 0);

// Internal data offset
Expand Down Expand Up @@ -220,17 +220,39 @@ public bool IsActiveEnabled
Role is TrackerType.TrackerWaist or TrackerType.TrackerLeftFoot or TrackerType.TrackerRightFoot ||
(AppPlugins.CurrentServiceEndpoint?.AdditionalSupportedTrackerTypes.Contains(Role) ?? false);

// Returns all input actions available from the currently selected service,
// paired with their selected sources that will trigger the action if updated
// Note: use InputActionSource.IsValid to check whether the source exists
[JsonIgnore]
public Dictionary<InputActionEndpoint, InputActionSource> InputActionsMap =>
InputActions.TryGetValue(AppData.Settings.ServiceEndpointGuid, out var map) ? map : [];

// Returns all input actions available from the currently selected service,
// paired with their current selection state: true for used, false for hidden
// Note: "used" can also mean that the action is shown & disabled by the user
[JsonIgnore]
public Dictionary<InputActionEndpoint, bool> AvailableInputActions =>
AppPlugins.CurrentServiceEndpoint?.SupportedInputActions?.TryGetValue(Role, out var actions) ?? false
? actions.ToDictionary(x => new InputActionEndpoint { Tracker = Role, Action = x.Guid },
x => InputActionsMap.Keys.Any(y => y.Tracker == Role && y.Action == x.Guid && y.IsValid))
? actions.ToDictionary(x => new InputActionEndpoint { Tracker = Role, Guid = x.Guid },
x => InputActionsMap.Keys.Any(y => y.Tracker == Role && y.Guid == x.Guid && y.IsValid))
: [];

[JsonIgnore]
public IEnumerable<InputActionEntry> InputActionEntries =>
AvailableInputActions.Select(x => new InputActionEntry
{
Action = x.Key,
IsEnabled = x.Value
}).OrderBy(x => x.Name);

[JsonIgnore]
public IEnumerable<InputActionBindingEntry> InputActionBindingEntries =>
InputActionsMap.Select(x => new InputActionBindingEntry
{
Action = x.Key,
Source = x.Value
}).OrderBy(x => x.ActionName);

[JsonIgnore] public bool OverridePhysics { get; set; }

[JsonIgnore] public string TrackerName => Interfacing.LocalizedJsonString($"/SharedStrings/Joints/Enum/{(int)Role}");
Expand Down Expand Up @@ -757,4 +779,9 @@ public bool IsManagedBy(string guid)
{
return guid == ManagingDeviceGuid;
}
}

[JsonArray]
public class JsonDictionary<T, TU> : Dictionary<T, TU>
{
}
Loading

0 comments on commit 90c218a

Please sign in to comment.