using Microsoft.Extensions.Logging; namespace Barcode.Host.Shared.KeyboardMouse; public class InputReader : IDisposable { private readonly ILogger _Logger; private const int _BufferLength = 24; private static readonly int _PiOffset; private readonly byte[] _Buffer = new byte[_BufferLength]; private FileStream? _Stream; private bool _Disposing; public delegate void RaiseKeyPress(KeyPressEvent e); public delegate void RaiseMouseMove(MouseMoveEvent e); public event RaiseKeyPress? OnKeyPress; public event RaiseMouseMove? OnMouseMove; public string Path { get; } public bool Faulted { get; private set; } static InputReader() { if (RunningOnRaspberryPi()) { _PiOffset = -8; } } public InputReader( string path, ILogger logger) { _Logger = logger; Path = path; try { _Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } catch (UnauthorizedAccessException ex) { _Logger.LogError(ex, "Current user doesn't have permissions to access input data. Add user to input group to correct this error"); Faulted = true; } catch (IOException ex) { _Logger.LogWarning(ex, $"Error occurred while trying to build stream for {path}"); Faulted = true; } _ = Task.Run(Run); } private void Run() { while (true) { if (_Disposing) break; try { if (!Faulted && _Stream is not null) { _ = _Stream.Read(_Buffer, 0, _BufferLength); } } catch (IOException ex) { _Logger.LogInformation(ex, $"Error occured while trying to read from the stream for {Path}"); Faulted = true; } EventType type = GetEventType(); short code = GetCode(); int value = GetValue(); switch (type) { case EventType.EV_KEY: HandleKeyPressEvent(code, value); break; case EventType.EV_REL: MouseAxis axis = (MouseAxis)code; MouseMoveEvent e = new(axis, value); OnMouseMove?.Invoke(e); break; } } } private int GetValue() { byte[] valueBits = new[] { _Buffer[20 + _PiOffset], _Buffer[21 + _PiOffset], _Buffer[22 + _PiOffset], _Buffer[23 + _PiOffset] }; int value = BitConverter.ToInt32(valueBits, 0); return value; } private short GetCode() { byte[] codeBits = new[] { _Buffer[18 + _PiOffset], _Buffer[19 + _PiOffset] }; short code = BitConverter.ToInt16(codeBits, 0); return code; } private EventType GetEventType() { byte[] typeBits = new[] { _Buffer[16 + _PiOffset], _Buffer[17 + _PiOffset] }; short type = BitConverter.ToInt16(typeBits, 0); EventType eventType = (EventType)type; return eventType; } private void HandleKeyPressEvent(short code, int value) { EventCode c = (EventCode)code; KeyState s = (KeyState)value; KeyPressEvent e = new(c, s); OnKeyPress?.Invoke(e); } public void Dispose() { _Disposing = true; _Stream?.Dispose(); _Stream = null; } private static bool RunningOnRaspberryPi() { string path = "/proc/cpuinfo"; string[] text = File.ReadAllLines(path); bool runningOnPi = text.Any(l => l.Contains("Raspberry Pi")); return runningOnPi; } }