-
-
Notifications
You must be signed in to change notification settings - Fork 104
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
Refactor joystick handling #1811
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,54 +96,22 @@ protected virtual void EnumerateDevices() | |
{ | ||
this.DIJoystick.GetObjectInfoById(device.ObjectId); | ||
|
||
int offset = device.Offset; | ||
int usage = device.Usage; | ||
ObjectAspect aspect = device.Aspect; | ||
String name = device.Name; | ||
|
||
bool IsAxis = (device.ObjectId.Flags & DeviceObjectTypeFlags.AbsoluteAxis) > 0; | ||
bool IsButton = (device.ObjectId.Flags & DeviceObjectTypeFlags.Button) > 0; | ||
bool IsPOV = (device.ObjectId.Flags & DeviceObjectTypeFlags.PointOfViewController) > 0; | ||
|
||
|
||
if (IsAxis && Axes.Count < DIJoystick.Capabilities.AxeCount) | ||
{ | ||
String axisName; | ||
string axisLabel = "Unknown"; | ||
try | ||
{ | ||
var OffsetAxisName = GetAxisNameForUsage(usage); | ||
axisName = $"{AxisPrefix} {OffsetAxisName}"; | ||
axisLabel = MapDeviceNameToLabel($"{AxisPrefix} {OffsetAxisName}"); | ||
|
||
} catch (ArgumentOutOfRangeException) | ||
{ | ||
Log.Instance.log($"Axis can't be mapped: {DIJoystick.Information.InstanceName} Aspect: {aspect} Offset: {offset} Usage: {usage} Axis: {name} Label: {axisLabel}.", LogSeverity.Error); | ||
continue; | ||
} | ||
Axes.Add(new JoystickDevice() { Name = axisName, Label = axisLabel, Type = DeviceType.AnalogInput, JoystickDeviceType = JoystickDeviceType.Axis}); | ||
Log.Instance.log($"Added {DIJoystick.Information.InstanceName} Aspect {aspect} + Offset: {offset} Usage: {usage} Axis: {name} Label: {axisLabel}.", LogSeverity.Debug); | ||
|
||
RegisterAxis(device); | ||
} | ||
else if (IsButton) | ||
{ | ||
// Use the device.Usage value so this is consistent with how Axes are referenced and avoid ID collisions | ||
// when looking up names in the the .joystick.json file. | ||
var buttonName = $"{ButtonPrefix} {device.Usage}"; | ||
var buttonLabel = MapDeviceNameToLabel(buttonName); | ||
Buttons.Add(new JoystickDevice() { Name = buttonName, Label = buttonLabel, Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.Button }); | ||
Log.Instance.log($"Added {DIJoystick.Information.InstanceName} Aspect: {aspect} Offset: {offset} Usage: {usage} Button: {name} Label: {buttonLabel}.", LogSeverity.Debug); | ||
RegisterButton(device); | ||
} | ||
else if (IsPOV) | ||
{ | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}U", Label = $"{name} (↑)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}UR", Label = $"{name} (↗)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}R", Label = $"{name} (→)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}DR", Label = $"{name} (↘)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}D", Label = $"{name} (↓)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}DL", Label = $"{name} (↙)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}L", Label = $"{name} (←)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}UL", Label = $"{name} (↖)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
RegisterPOV(device); | ||
} | ||
else | ||
{ | ||
|
@@ -152,6 +120,69 @@ protected virtual void EnumerateDevices() | |
} | ||
} | ||
|
||
protected virtual void RegisterAxis(DeviceObjectInstance device) | ||
{ | ||
String axisName; | ||
string axisLabel = "Unknown"; | ||
try | ||
{ | ||
var OffsetAxisName = GetAxisNameForUsage(device.Usage); | ||
axisName = $"{AxisPrefix} {OffsetAxisName}"; | ||
axisLabel = MapDeviceNameToLabel($"{AxisPrefix} {OffsetAxisName}"); | ||
|
||
} | ||
catch (ArgumentOutOfRangeException) | ||
{ | ||
LogAddInputError(device, axisLabel, "Axis can't be mapped"); | ||
return; | ||
} | ||
Axes.Add(new JoystickDevice() { Name = axisName, Label = axisLabel, Type = DeviceType.AnalogInput, JoystickDeviceType = JoystickDeviceType.Axis }); | ||
LogInputAdded(device, axisLabel); | ||
} | ||
|
||
protected virtual void RegisterButton(DeviceObjectInstance device) | ||
{ | ||
// Use the device.Usage value so this is consistent with how Axes are referenced and avoid ID collisions | ||
// when looking up names in the the .joystick.json file. | ||
var buttonName = $"{ButtonPrefix} {device.Usage}"; | ||
var buttonLabel = MapDeviceNameToLabel(buttonName); | ||
Buttons.Add(new JoystickDevice() { Name = buttonName, Label = buttonLabel, Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.Button }); | ||
LogInputAdded(device, buttonLabel); | ||
} | ||
|
||
protected virtual void RegisterPOV(DeviceObjectInstance device) | ||
{ | ||
String name = device.Name; | ||
|
||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}U", Label = $"{name} (↑)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}UR", Label = $"{name} (↗)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}R", Label = $"{name} (→)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}DR", Label = $"{name} (↘)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}D", Label = $"{name} (↓)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}DL", Label = $"{name} (↙)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}L", Label = $"{name} (←)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
POV.Add(new JoystickDevice() { Name = $"{PovPrefix} {name}UL", Label = $"{name} (↖)", Type = DeviceType.Button, JoystickDeviceType = JoystickDeviceType.POV }); | ||
LogInputAdded(device, name); | ||
} | ||
|
||
protected void LogInputAdded(DeviceObjectInstance device, string label) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trading debug information (calling function name) for DRYness here. If having the log point to dedicated logging methods is unwanted, I have a different compromise in mind as an alternative option (generate the string in a method and call Log.Instance.log with that). |
||
{ | ||
int offset = device.Offset; | ||
int usage = device.Usage; | ||
ObjectAspect aspect = device.Aspect; | ||
String name = device.Name; | ||
Log.Instance.log($"Added {DIJoystick.Information.InstanceName} Aspect: {aspect} Offset: {offset} Usage: {usage} Button: {name} Label: {label}.", LogSeverity.Debug); | ||
} | ||
|
||
protected void LogAddInputError(DeviceObjectInstance device, string label, string errmsg) | ||
{ | ||
int offset = device.Offset; | ||
int usage = device.Usage; | ||
ObjectAspect aspect = device.Aspect; | ||
String name = device.Name; | ||
Log.Instance.log($"{errmsg}: {DIJoystick.Information.InstanceName} Aspect: {aspect} Offset: {offset} Usage: {usage} Axis: {name} Label: {label}.", LogSeverity.Error); | ||
} | ||
|
||
public string MapDeviceNameToLabel(string deviceName) | ||
{ | ||
// First try and look for a custom label. | ||
|
@@ -160,7 +191,6 @@ public string MapDeviceNameToLabel(string deviceName) | |
{ | ||
return input.Label; | ||
} | ||
|
||
string result = string.Empty; | ||
|
||
if (deviceName.StartsWith(ButtonPrefix)) | ||
|
@@ -216,32 +246,33 @@ public List<ListItem<IBaseDevice>> GetAvailableDevicesAsListItems() | |
|
||
private List<JoystickDevice> GetButtonsSorted() | ||
{ | ||
var buttons = Buttons.ToArray().ToList(); | ||
buttons.Sort(SortByPositionInDefintion); | ||
return Buttons; | ||
return Buttons.OrderBy(button => GetIndexForJoystickDevice(button)).ToList(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This addresses two nuisances (cf. issue #1808):
The Lambda could also be a function pointer, writing it this way mainly makes it more explicit that it is acting on individual buttons. |
||
} | ||
|
||
private List<JoystickDevice> GetAxisSorted() | ||
{ | ||
var axes = Axes.ToArray().ToList(); | ||
Axes.Sort(SortByPositionInDefintion); | ||
|
||
return Axes; | ||
return Axes.OrderBy(axis => GetIndexForJoystickDevice(axis)).ToList(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as the buttons, with the added fun that the sort function was run on the member axes, not the local axes. |
||
} | ||
|
||
public int GetIndexForKey(string key) | ||
public virtual int GetIndexForKey(string key) | ||
{ | ||
return Definition?.Inputs?.FindIndex(input => input.Name == key) ?? 0; | ||
// -2 = definition not found / no inputs in definition | ||
// -1 = index not found | ||
// 0 or greater: valid index | ||
return Definition?.Inputs?.FindIndex(input => input.Name == key) ?? -2; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documented some outputs and changed the return value in case of null pointers from a valid index to something the caller can distinguish. |
||
} | ||
|
||
int SortByPositionInDefintion(JoystickDevice b1, JoystickDevice b2) | ||
public int GetIndexForJoystickDevice(JoystickDevice button) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enumerable.OrderBy, unlike List.Sort, allows us to define what property to sort by. Therefore, the approach to integrate the index retrieval into the function is somewhat different. |
||
{ | ||
if (GetIndexForKey(b1.Name) == GetIndexForKey(b2.Name)) return 0; | ||
if (GetIndexForKey(b1.Name) > GetIndexForKey(b2.Name)) return 1; | ||
return -1; | ||
var index = GetIndexForKey(button.Name); | ||
// Ensure items in definition appear first | ||
if(index < 0) | ||
{ | ||
index = Int32.MaxValue; // force undefined stuff to the end of the list | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The inputs most relevant to MobiFlight users are probably spelled out in the definition. Entries not in the definition are likely to be less relevant, so let us move them after the named ones. |
||
} | ||
return index; | ||
} | ||
|
||
|
||
private void Connect() | ||
{ | ||
if (Device == null) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method has three sub-methods abstracted for more specific overrides in derived classes and to keep function length down - derived classes do not need to copy the full size of the method if all they want to change is a line or two.