using Microsoft.Extensions.Logging; namespace Barcode.Host.Shared.KeyboardMouse; public class InputReader : IDisposable { private long _Ticks; private bool _Disposing; private readonly int _PiOffset; private const int _BufferLength = 24; private readonly FileStream? _FileStream; private readonly ILogger _Logger; private readonly byte[] _Buffer = new byte[_BufferLength]; public string Path { get; init; } public bool Faulted { get; private set; } public event RaiseKeyPress? OnKeyPress; public event RaiseMouseMove? OnMouseMove; public delegate void RaiseKeyPress(KeyPressEvent e); public delegate void RaiseMouseMove(MouseMoveEvent e); public InputReader(string path, ILogger logger) { Path = path; _Logger = logger; if (RunningOnRaspberryPi()) _PiOffset = -8; try { _FileStream = 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 && _FileStream is not null) _ = _FileStream.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(); switch (type) { case EventType.EV_SYN: _Ticks = DateTime.Now.Ticks; break; case EventType.EV_KEY: HandleKeyPressEvent(); break; case EventType.EV_REL: HandleMouseMoveEvent(); 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 = GetCode(); int value = GetValue(); DateTime dateTime = DateTime.Now; KeyState keyState = (KeyState)value; EventCode eventCode = (EventCode)code; KeyPressEvent keyPressEvent = new(dateTime, eventCode, keyState, new TimeSpan(dateTime.Ticks - _Ticks)); OnKeyPress?.Invoke(keyPressEvent); } private void HandleMouseMoveEvent() { short code = GetCode(); int value = GetValue(); MouseAxis axis = (MouseAxis)code; MouseMoveEvent e = new(axis, value); OnMouseMove?.Invoke(e); } public void Dispose() { _Disposing = true; _FileStream?.Dispose(); GC.SuppressFinalize(this); } private static bool RunningOnRaspberryPi() { string path = "/proc/cpuinfo"; string[] text = File.ReadAllLines(path); bool runningOnPi = text.Any(l => l.Contains("Raspberry Pi")); return runningOnPi; } }