using PIEHidNetCore; using System.IO; using System.Windows.Media; namespace PamhagenSysCtrl.Helpers { public class Xbe24Keyboard : IDisposable, PIEDataHandler, PIEErrorHandler { public enum LedBank { UPPER = 0, LOWER = 1 } public record struct KeyState(byte[] Rows) { public readonly bool GetKey(int n) => (Rows[n / 6] & (1 << (n % 6))) != 0; } protected readonly PIEDevice XbeDevice; private KeyState LastKeyState = new(new byte[4]); private readonly (Color Color, bool Flashing)[] LastStates = new (Color, bool)[48]; public bool IsKeyPressed(int keyIndex) => LastKeyState.GetKey(keyIndex); public event EventHandler? KeyDown; public event EventHandler? KeyUp; public event EventHandler? KeysChanged; public Xbe24Keyboard(int pid = 0x0555) { foreach (var device in PIEDevice.EnumeratePIE()) { if (device.Pid == pid && device.HidUsagePage == 0x0C && device.HidUsage == 1 && device.WriteLength > 1) { XbeDevice = device; break; } } if (XbeDevice == null) throw new ArgumentException($"No XBE keyboard with id 05f3:{pid:x4}"); int res; if ((res = XbeDevice.SetupInterface()) != 0) throw new IOException($"Unable to SetupInterface for XBE keyboard: {res}"); if ((res = XbeDevice.SetDataCallback(this)) != 0) throw new IOException($"Unable to SetDataCallback for XBE keyboard: {res}"); if ((res = XbeDevice.SetErrorCallback(this)) != 0) throw new IOException($"Unable to SetErrorCallback for XBE keyboard: {res}"); } public void Dispose() { XbeDevice?.CloseInterface(); } public void HandlePIEHidData(byte[] data, PIEDevice sourceDevice, int error) { if (sourceDevice != XbeDevice) return; var current = new KeyState(data[3..8]); var last = LastKeyState; LastKeyState = current; HandleKeyStateChange(current, last); } public void HandlePIEHidError(PIEDevice sourceDevice, int error) { if (sourceDevice != XbeDevice) return; // TODO } protected void HandleKeyStateChange(KeyState current, KeyState last) { for (int i = 0; i < 24; i++) { if (!last.GetKey(i) && current.GetKey(i)) { KeyDown?.Invoke(this, i); } else if (last.GetKey(i) && !current.GetKey(i)) { KeyUp?.Invoke(this, i); } } KeysChanged?.Invoke(this, current); } protected void SendCommand(byte command, byte[] payload) { byte[] data = new byte[XbeDevice.WriteLength]; data[1] = command; Array.Copy(payload, 0, data, 2, Math.Min(payload.Length, data.Length - 2)); int res; do { res = XbeDevice.WriteData(data); } while (res == 404); if (res != 0) throw new IOException($"Unable to WriteData for XBE keyboard: {res}"); } public void SetBacklight(int keyIndex, Color color, bool flashing = false) { SetBacklight(keyIndex, LedBank.UPPER, color, flashing); SetBacklight(keyIndex, LedBank.LOWER, color, flashing); } public void SetBacklight(int keyIndex, LedBank bank, Color color, bool flashing = false) { if (LastStates[keyIndex * 2 + (int)bank] == (color, flashing)) return; SendCommand(165, [(byte)keyIndex, (byte)bank, color.R, color.G, color.B, (byte)(flashing ? 1 : 0)]); LastStates[keyIndex * 2 + (int)bank] = (color, flashing); } public void SetBacklight(Color color) { SetBacklight(LedBank.UPPER, color); SetBacklight(LedBank.LOWER, color); } public void SetBacklight(LedBank bank, Color color) { SendCommand(166, [(byte)bank, color.R, color.G, color.B]); for (int i = 0; i < LastStates.Length; i++) LastStates[i] = (color, false); } public void SetFashFrequency(int ms) { SendCommand(180, [(byte)(ms / 16)]); } } }