1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Linux - Fixed input provider warnings

This commit is contained in:
Diogo Trindade 2022-08-20 16:48:02 +01:00
parent 090d5b76e8
commit 6fe93ab2f7
10 changed files with 176 additions and 107 deletions

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Artemis.UI.Linux.Providers.Input
{
public class ArtemisLinuxInputProviderException : Exception
{
public ArtemisLinuxInputProviderException() : base()
{
}
public ArtemisLinuxInputProviderException(string? message) : base(message)
{
}
public ArtemisLinuxInputProviderException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Artemis.UI.Linux.Providers.Input
{
public enum LinuxDeviceType
{
Keyboard,
Mouse,
Gamepad
}
}

View File

@ -9,57 +9,57 @@ namespace Artemis.UI.Linux.Providers.Input
/// Used as markers to separate events. Events may be separated in time or in space, such as with the multitouch protocol.
/// </summary>
SYN = 0x00,
/// <summary>
/// Used to describe state changes of keyboards, buttons, or other key-like devices.
/// </summary>
KEY = 0x01,
/// <summary>
/// Used to describe relative axis value changes, e.g. moving the mouse 5 units to the left.
/// </summary>
REL = 0x02,
/// <summary>
/// Used to describe absolute axis value changes, e.g. describing the coordinates of a touch on a touchscreen.
/// </summary>
ABS = 0x03,
/// <summary>
/// Used to describe miscellaneous input data that do not fit into other types.
/// </summary>
MSC = 0x04,
/// <summary>
/// Used to describe binary state input switches.
/// </summary>
SW = 0x05,
/// <summary>
/// Used to turn LEDs on devices on and off.
/// </summary>
LED = 0x11,
/// <summary>
/// Used to output sound to devices.
/// </summary>
SND = 0x12,
/// <summary>
/// Used for autorepeating devices.
/// </summary>
REP = 0x14,
/// <summary>
/// Used to send force feedback commands to an input device.
/// </summary>
FF = 0x15,
/// <summary>
/// A special type for power button and switch input.
/// </summary>
PWR = 0x16,
/// <summary>
/// Used to receive force feedback device status.
/// </summary>

View File

@ -1,9 +1,6 @@
using System;
using System.Collections;
using Artemis.Core;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using Humanizer;
namespace Artemis.UI.Linux.Providers.Input
{
@ -12,82 +9,81 @@ namespace Artemis.UI.Linux.Providers.Input
/// </summary>
public class LinuxInputDevice
{
public string InputId { get; }
public string? Bus { get; }
public string? Vendor { get; }
public string? Product { get; }
public string? Version { get; }
public string? Name { get; }
public string? Phys { get; }
public string? Sysfs { get; }
public string? Uniq { get; }
public string[]? Handlers { get; }
public bool IsMouse => Handlers.Any(h => h.Contains("mouse"));
public bool IsKeyboard => Handlers.Any(h => h.Contains("kbd"));
public bool IsGamePad => Handlers.Any(h => h.Contains("js"));
public string EventPath => $"/dev/input/{Handlers.First(h => h.Contains("event"))}";
public LinuxInputId InputId { get; }
public string Name { get; }
public string[] Handlers { get; }
public string EventPath { get; }
public LinuxDeviceType DeviceType { get; }
public LinuxInputDevice(IEnumerable<string> lines)
{
foreach (string line in lines)
{
char dataType = line.First();
string data = line.Substring(3);
//get the first character in each line and set the according property with relevant data
char dataType = line[0];
string data = line[3..];
switch (dataType)
{
case 'I':
InputId = data;
foreach (string component in data.Split(" "))
{
string?[] parts = component.Split('=');
switch (parts[0])
{
case "Bus":
Bus = parts[1];
break;
case "Vendor":
Vendor = parts[1];
break;
case "Product":
Product = parts[1];
break;
case "Version":
Version = parts[1];
break;
default:
break;
}
}
InputId = new LinuxInputId(data);
break;
case 'N':
Name = data.Replace("\"", "")
.Replace("Name=", "");
break;
case 'P':
Phys = data.Replace("Phys=", "");
break;
case 'S':
Sysfs = data.Replace("Sysfs=", "");
Name = data.Replace("\"", "").Replace("Name=", "");
break;
case 'H':
Handlers = data.Replace("Handlers=", "").Split(" ");
break;
case 'U':
Uniq = data.Replace("Uniq=", "");
if (Handlers?.Any(h => h.Contains("mouse")) == true)
DeviceType = LinuxDeviceType.Mouse;
else if (Handlers?.Any(h => h.Contains("kbd")) == true)
DeviceType = LinuxDeviceType.Keyboard;
else if (Handlers?.Any(h => h.Contains("js")) == true)
DeviceType = LinuxDeviceType.Gamepad;
var evt = Handlers!.First(h => h.Contains("event"));
EventPath = $"/dev/input/{evt}";
break;
default:
//do we need any more of this data?
break;
}
}
if (InputId is null || Name is null || Handlers is null || EventPath is null)
{
throw new ArtemisLinuxInputProviderException("Linux device definition did not contain necessary data");
}
}
#region Overrides of Object
/// <inheritdoc />
public override string ToString() => $"{Name} - {EventPath}";
#endregion
public class LinuxInputId
{
public string Bus { get; }
public string Vendor { get; }
public string Product { get; }
public string Version { get; }
public LinuxInputId(string line)
{
var components = line.Split(" ")
.Select(c => c.Split('='))
.ToDictionary(c => c[0], c => c[1]);
Bus = components["Bus"];
Vendor = components["Vendor"];
Product = components["Product"];
Version = components["Version"];
}
public override string ToString() => $"Bus={Bus} Vendor={Vendor} Product={Product} Version={Version}";
}
}
}

View File

@ -8,11 +8,11 @@ namespace Artemis.UI.Linux.Providers.Input
public static class LinuxInputDeviceFinder
{
private const string DEVICES_FILE = "/proc/bus/input/devices";
public static IEnumerable<LinuxInputDevice> Find()
{
return File.ReadAllLines(DEVICES_FILE)
.PartitionBy(s => s == "") //split on empty lines
.PartitionBy(s => s?.Length == 0) //split on empty lines
.Select(lineGroup => new LinuxInputDevice(lineGroup));
}
@ -33,10 +33,10 @@ namespace Artemis.UI.Linux.Providers.Input
return groupNumber;
};
return a
.Select(x => new { Value = x, GroupNumber = getGroupNumber(predicate(x))} )
.Select(x => new { Value = x, GroupNumber = getGroupNumber(predicate(x)) })
.Where(x => x.GroupNumber != null)
.GroupBy(x => x.GroupNumber)
.Select(g => g.Select(x => x.Value));
.Select(g => g.Select(x => x.Value));
}
}
}

View File

@ -3,7 +3,6 @@ using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Artemis.UI.Linux.Utilities;
namespace Artemis.UI.Linux.Providers.Input
{

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Linux.Utilities;
using Serilog;
@ -20,7 +21,7 @@ namespace Artemis.UI.Linux.Providers.Input
foreach (LinuxInputDevice deviceDefinition in LinuxInputDeviceFinder.Find())
{
LinuxInputDeviceReader? reader = new LinuxInputDeviceReader(deviceDefinition);
LinuxInputDeviceReader? reader = new(deviceDefinition);
reader.InputEvent += OnInputEvent;
_readers.Add(reader);
}
@ -30,68 +31,103 @@ namespace Artemis.UI.Linux.Providers.Input
{
if (sender is not LinuxInputDeviceReader reader)
return;
if (reader.InputDevice.IsKeyboard)
switch (reader.InputDevice.DeviceType)
{
HandleKeyboardData(reader.InputDevice, e);
}
else if (reader.InputDevice.IsMouse)
{
HandleMouseData(reader.InputDevice, e);
}
else if (reader.InputDevice.IsGamePad)
{
//TODO: handle game pad input?
case LinuxDeviceType.Keyboard:
HandleKeyboardData(reader.InputDevice, e);
break;
case LinuxDeviceType.Mouse:
HandleMouseData(reader.InputDevice, e);
break;
case LinuxDeviceType.Gamepad:
break;
}
}
private void HandleKeyboardData(LinuxInputDevice keyboard, LinuxInputEventArgs e)
private void HandleKeyboardData(LinuxInputDevice keyboard, LinuxInputEventArgs args)
{
switch (e.Type)
switch (args.Type)
{
case LinuxInputEventType.KEY:
KeyboardKey key = InputUtilities.KeyFromKeyCode((LinuxKeyboardKeyCodes) e.Code);
bool isDown = e.Value != 0;
KeyboardKey key = InputUtilities.KeyFromKeyCode((LinuxKeyboardKeyCodes)args.Code);
bool isDown = args.Value != 0;
_logger.Verbose($"Keyboard Key: {(LinuxKeyboardKeyCodes) e.Code} | Down: {isDown}");
//TODO: identify
_logger.Verbose($"Keyboard Key: {(LinuxKeyboardKeyCodes)args.Code} | Down: {isDown}");
OnKeyboardDataReceived(null, key, isDown);
var identifier = keyboard.InputId;
OnIdentifierReceived(identifier, InputDeviceType.Keyboard);
ArtemisDevice? device = null;
try
{
device = _inputService.GetDeviceByIdentifier(this, identifier, InputDeviceType.Keyboard);
}
catch (Exception e)
{
_logger.Warning(e, "Failed to retrieve input device by its identifier");
}
OnKeyboardDataReceived(device, key, isDown);
break;
default:
_logger.Verbose($"Unknown keyboard event type: {e.Type}");
_logger.Verbose($"Unknown keyboard event type: {args.Type}");
break;
}
}
private void HandleMouseData(LinuxInputDevice mouse, LinuxInputEventArgs e)
private void HandleMouseData(LinuxInputDevice mouse, LinuxInputEventArgs args)
{
switch (e.Type)
var identifier = mouse.InputId;
OnIdentifierReceived(identifier, InputDeviceType.Mouse);
ArtemisDevice? device = null;
try
{
device = _inputService.GetDeviceByIdentifier(this, identifier, InputDeviceType.Mouse);
}
catch (Exception e)
{
_logger.Warning(e, "Failed to retrieve input device by its identifier");
}
switch (args.Type)
{
case LinuxInputEventType.KEY:
bool isDown = e.Value != 0;
MouseButton button = InputUtilities.MouseButtonFromButtonCode((LinuxKeyboardKeyCodes)e.Code);
bool isDown = args.Value != 0;
MouseButton button = InputUtilities.MouseButtonFromButtonCode((LinuxKeyboardKeyCodes)args.Code);
_logger.Verbose($"Mouse Button: {(LinuxKeyboardKeyCodes) e.Code} | Down: {isDown}");
_logger.Verbose($"Mouse Button: {(LinuxKeyboardKeyCodes)args.Code} | Down: {isDown}");
//TODO: identify
OnMouseButtonDataReceived(null, button, isDown);
OnMouseButtonDataReceived(device, button, isDown);
break;
case LinuxInputEventType.ABS:
LinuxAbsoluteAxis absoluteAxis = (LinuxAbsoluteAxis) e.Code;
_logger.Verbose($"Absolute mouse: axis={absoluteAxis} | value={e.Value}");
break;
case LinuxInputEventType.REL:
LinuxRelativeAxis relativeAxis = (LinuxRelativeAxis) e.Code;
_logger.Verbose($"Relative mouse: axis={relativeAxis} | value={e.Value}");
LinuxRelativeAxis relativeAxis = (LinuxRelativeAxis)args.Code;
//TODO: handle mouse movement
_logger.Verbose($"Relative mouse: axis={relativeAxis} | value={args.Value}");
switch (relativeAxis)
{
case LinuxRelativeAxis.REL_WHEEL:
OnMouseScrollDataReceived(device, MouseScrollDirection.Vertical, args.Value);
break;
case LinuxRelativeAxis.REL_HWHEEL:
OnMouseScrollDataReceived(device, MouseScrollDirection.Horizontal, args.Value);
break;
case LinuxRelativeAxis.REL_X:
OnMouseMoveDataReceived(device, 0, 0, args.Value, 0);
break;
case LinuxRelativeAxis.REL_Y:
OnMouseMoveDataReceived(device, 0, 0, 0, args.Value);
break;
}
break;
default:
_logger.Verbose($"Unknown mouse event type: {e.Type}");
_logger.Verbose($"Unknown mouse event type: {args.Type}");
break;
}
}