Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Device: add API for capability labels #594

Merged
merged 1 commit into from
Feb 24, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Snowflake.Input.Device
{
/// <summary>
/// Represents a mapping from <see cref="DeviceCapability"/> to a label on the real device.
/// When enumerated,
/// </summary>
public interface IDeviceCapabilityLabels : IEnumerable<KeyValuePair<DeviceCapability, string>>
{
/// <summary>
/// Gets a friendly string representation of the provided device capability.
/// </summary>
/// <param name="capability">The <see cref="DeviceCapability"/> to get a name for.</param>
/// <returns>A string representation of the device capbility</returns>
string this[DeviceCapability capability] { get; }
}
}
Original file line number Diff line number Diff line change
@@ -78,5 +78,11 @@ public interface IInputDeviceInstance
/// the natural mapping from controller element to a device capability.
/// </summary>
IDeviceLayoutMapping DefaultLayout { get; }

/// <summary>
/// Gets the friendly label names for capabilities this device instance supports.
/// This collection must at least have labels for all supported capabilities in <see cref="Capabilities"/>.
/// </summary>
IDeviceCapabilityLabels CapabilityLabels { get; }
}
}
35 changes: 35 additions & 0 deletions src/Snowflake.Framework.Tests/Input/LabelsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Snowflake.Input.Device;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace Snowflake.Input.Tests
{
public class LabelsTests
{
[Fact]
public void DefaultLabels_Test()
{
Assert.Equal("Button0", DefaultDeviceCapabilityLabels.DefaultLabels[DeviceCapability.Button0]);
foreach(var (c, l) in DefaultDeviceCapabilityLabels.DefaultLabels)
{
Assert.Equal(l, c.ToString());
}
}

[Fact]
public void DictLabels_Test()
{
var dictLabels = new Dictionary<DeviceCapability, string>()
{
{ DeviceCapability.Button0, "Button A" }
};

IDeviceCapabilityLabels labels = new DictionaryDeviceCapabilityLabels(dictLabels);
Assert.Equal("Button A", labels[DeviceCapability.Button0]);
Assert.Single(labels);
Assert.Equal(string.Empty, labels[DeviceCapability.Axis0Negative]);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using EnumsNET;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Snowflake.Input.Device
{
/// <summary>
/// Implements a default label mapping using the name of the capability enum element.
/// </summary>
public sealed class DefaultDeviceCapabilityLabels : IDeviceCapabilityLabels
{
/// <summary>
/// Default labels using the name of the capability enum element.
/// </summary>
public static IDeviceCapabilityLabels DefaultLabels => _DefaultLabels;

private static readonly DefaultDeviceCapabilityLabels _DefaultLabels = new DefaultDeviceCapabilityLabels();

public string this[DeviceCapability capability] => Enums.AsString(capability);

public IEnumerator<KeyValuePair<DeviceCapability, string>> GetEnumerator()
{
return Enums.GetMembers<DeviceCapability>()
.Select(e => KeyValuePair.Create(e.Value, e.AsString())).GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using EnumsNET;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Snowflake.Input.Device
{
/// <summary>
/// Implements a label mapping using a backing dictionary. Missing labels are replaced with
/// the empty string.
/// </summary>
public sealed class DictionaryDeviceCapabilityLabels : IDeviceCapabilityLabels
{
/// <summary>
/// Create a new <see cref="IDeviceCapabilityLabels"/> using a backing dictionary.
/// </summary>
/// <param name="labels">The backing dictionary to use.</param>
public DictionaryDeviceCapabilityLabels(IDictionary<DeviceCapability, string> labels)
{
this.Labels = labels;
}

private IDictionary<DeviceCapability, string> Labels { get; }

public string this[DeviceCapability capability] =>
this.Labels.TryGetValue(capability, out string? value) ? value : string.Empty;

public IEnumerator<KeyValuePair<DeviceCapability, string>> GetEnumerator()
{
return this.Labels.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
}
Original file line number Diff line number Diff line change
@@ -9,15 +9,17 @@ public sealed class DirectInputDeviceInstance : IInputDeviceInstance
internal DirectInputDeviceInstance(int enumerationIndex,
int classEnumerationIndex, int uniqueNameEnumerationIndex,
int productEnumerationIndex,
IReadOnlyDictionary<DeviceCapability, (int offset, int rawId)> capabilities,
IDeviceLayoutMapping defaultLayout)
IReadOnlyDictionary<DeviceCapability, (int offset, int rawId)> capabilities,
IDeviceLayoutMapping defaultLayout,
IDeviceCapabilityLabels labels)
{
this.EnumerationIndex = enumerationIndex;
this.ClassEnumerationIndex = classEnumerationIndex;
this.NameEnumerationIndex = uniqueNameEnumerationIndex;
this.ProductEnumerationIndex = productEnumerationIndex;
this.DefaultLayout = defaultLayout;
this.CapabilityMap = capabilities;
this.CapabilityLabels = labels;
}

private IReadOnlyDictionary<DeviceCapability, (int offset, int rawId)> CapabilityMap { get; }
@@ -35,6 +37,8 @@ internal DirectInputDeviceInstance(int enumerationIndex,

public int ProductEnumerationIndex { get; }

public IDeviceCapabilityLabels CapabilityLabels { get; }

public int GetObjectOffset(DeviceCapability capability) =>
this.CapabilityMap.TryGetValue(capability, out (int offset, int _) val) ? val.offset : -1;

Original file line number Diff line number Diff line change
@@ -49,8 +49,8 @@ public KeyboardDeviceInstance()
this.Capabilities = DeviceCapabilityClasses.Keyboard
.Concat(DeviceCapabilityClasses.MouseButtons)
.Concat(DeviceCapabilityClasses.MouseCursor)
.Concat(new[] { DeviceCapability.Axis0,
DeviceCapability.Axis0Negative,
.Concat(new[] { DeviceCapability.Axis0,
DeviceCapability.Axis0Negative,
DeviceCapability.Axis0Positive })
.ToList();

@@ -69,5 +69,7 @@ public KeyboardDeviceInstance()
public int NameEnumerationIndex => 0;

public int ProductEnumerationIndex => 0;

public IDeviceCapabilityLabels CapabilityLabels => DefaultDeviceCapabilityLabels.DefaultLabels;
}
}
Original file line number Diff line number Diff line change
@@ -20,5 +20,7 @@ public sealed class PassthroughDeviceInstance : IInputDeviceInstance
public int NameEnumerationIndex => 0;

public int ProductEnumerationIndex => 0;

public IDeviceCapabilityLabels CapabilityLabels => DefaultDeviceCapabilityLabels.DefaultLabels;
}
}
3 changes: 3 additions & 0 deletions src/Snowflake.Framework/Input/Device/XInputDeviceInstance.cs
Original file line number Diff line number Diff line change
@@ -106,5 +106,8 @@ public XInputDeviceInstance(int enumerationIndex)
public IDeviceLayoutMapping DefaultLayout { get; }

public int ProductEnumerationIndex => this.EnumerationIndex;

// todo: make XInput specific labels!
public IDeviceCapabilityLabels CapabilityLabels => DefaultDeviceCapabilityLabels.DefaultLabels;
}
}
22 changes: 22 additions & 0 deletions src/Snowflake.Framework/Snowflake.Framework.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using GraphQL.Types;
using Snowflake.Input.Device;
using System;
using System.Collections.Generic;
using System.Text;

namespace Snowflake.Support.GraphQLFrameworkQueries.Types.InputDevice
{
public class DeviceCapabilityLabelKeyValuePairGraphType : ObjectGraphType<KeyValuePair<DeviceCapability, string>>
{
public DeviceCapabilityLabelKeyValuePairGraphType()
{
Name = "DeviceCapabilityLabelKeyValuePair";
Description = "A label of a device capability";
Field<DeviceCapabilityEnum>("capability", description: "The capability this label describes.", resolve: c => c.Source.Key);
Field<StringGraphType>("label", description: "The label of the capability.", resolve: c => c.Source.Value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using GraphQL.Types;
using Snowflake.Input.Device;
using System;
using System.Collections.Generic;
using System.Text;

namespace Snowflake.Support.GraphQLFrameworkQueries.Types.InputDevice
{
public class DeviceCapabilityLabelsGraphType : ObjectGraphType<IDeviceCapabilityLabels>
{
public DeviceCapabilityLabelsGraphType()
{
Name = "DeviceCapabilityLabels";
Description = "A mapping of device capabilities to friendly string labels.";
Field<ListGraphType<DeviceCapabilityLabelKeyValuePairGraphType>>("labels", description: "A list of labels in this device instance",
resolve: c => c.Source);
foreach (var (key, _) in DefaultDeviceCapabilityLabels.DefaultLabels)
{
Field<StringGraphType>(key.ToString(), resolve: c => c.Source[key] ?? key.ToString());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ public InputDeviceInstanceGraphType()
resolve: context => context.Source.Capabilities);
Field<ListGraphType<MappedControllerElementGraphType>>("defaultLayout",
description: "The default, assumed natural mapping from capability to virtual element without regard for any specific layout.",
resolve: context => (IEnumerable<MappedControllerElement>)context.Source.DefaultLayout);
resolve: context => context.Source.DefaultLayout);
Field<IntGraphType>("enumerationIndex",
description: "When enumerating devices with a given driver, the index of enumeration for this driver.",
resolve: context => context.Source.EnumerationIndex);
@@ -41,6 +41,9 @@ public InputDeviceInstanceGraphType()
"with regards to the specific type of device, as determined by unique VID/product name," +
"if and only if the driver disambiguates between different devices.",
resolve: context => context.Source.ProductEnumerationIndex);
Field<DeviceCapabilityLabelsGraphType>("capabilityLabels",
description: "Friendly labels for the device capabilities of this instance",
resolve: context => context.Source.CapabilityLabels);
}
}
}
Original file line number Diff line number Diff line change
@@ -70,18 +70,16 @@ public IEnumerable<IInputDevice> QueryConnectedDevices()

// todo add support for mapping overrides
instances.Add(new DirectInputDeviceInstance(allOrder, classOrder, nameOrder, prodOrder, capabilities,
GenerateDefaultMapping(capabilities.Keys)));
GenerateDefaultMapping(capabilities.Keys), DefaultDeviceCapabilityLabels.DefaultLabels));

yield return new InputDevice(vid, pid,
name, name, path, device.Information.InstanceGuid, instances.AsReadOnly());
}

}

private static readonly IDictionary<DeviceCapability, ControllerElement> _hidMappings =
new Dictionary<DeviceCapability, ControllerElement>()
{

{DeviceCapability.Button0, ControllerElement.ButtonA},
{DeviceCapability.Button1, ControllerElement.ButtonB},
{DeviceCapability.Button2, ControllerElement.ButtonX},