using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Controls; using System.Windows.Input; namespace Jellyfish.Virtu.Services { public sealed class WpfKeyboardService : KeyboardService { public WpfKeyboardService(Machine machine, UserControl page) : base(machine) { if (page == null) { throw new ArgumentNullException("page"); } page.KeyDown += OnPageKeyDown; page.KeyUp += OnPageKeyUp; page.GotKeyboardFocus += (sender, e) => _updateAnyKeyDown = true; } public override bool IsKeyDown(int key) { return IsKeyDown((Key)key); } public override void Update() // main thread { var keyboard = System.Windows.Input.Keyboard.PrimaryDevice; if (_updateAnyKeyDown) { _updateAnyKeyDown = false; IsAnyKeyDown = false; for (int i = 0; i < KeyValues.Length; i++) { var key = KeyValues[i]; bool isKeyDown = keyboard.IsKeyDown(key); _states[(int)key] = isKeyDown; if (isKeyDown) { IsAnyKeyDown = true; } } } IsControlKeyDown = ((keyboard.Modifiers & ModifierKeys.Control) != 0); IsShiftKeyDown = ((keyboard.Modifiers & ModifierKeys.Shift) != 0); IsOpenAppleKeyDown = keyboard.IsKeyDown(Key.LeftAlt) || IsKeyDown(Key.NumPad0); IsCloseAppleKeyDown = keyboard.IsKeyDown(Key.RightAlt) || IsKeyDown(Key.Decimal); IsResetKeyDown = IsControlKeyDown && keyboard.IsKeyDown(Key.Back); base.Update(); } private bool IsKeyDown(Key key) { return _states[(int)key]; } private void OnPageKeyDown(object sender, KeyEventArgs e) { //DebugService.WriteLine(string.Concat("OnPageKeyDn: Key=", e.Key)); _states[(int)((e.Key == Key.System) ? e.SystemKey : e.Key)] = true; _updateAnyKeyDown = false; IsAnyKeyDown = true; int asciiKey = GetAsciiKey(e.Key, e.KeyboardDevice); if (asciiKey >= 0) { Machine.Keyboard.Latch = asciiKey; e.Handled = true; } Update(); } private void OnPageKeyUp(object sender, KeyEventArgs e) { //DebugService.WriteLine(string.Concat("OnPageKeyUp: Key=", e.Key)); _states[(int)((e.Key == Key.System) ? e.SystemKey : e.Key)] = false; _updateAnyKeyDown = true; bool control = ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0); if (control && (e.Key == Key.Divide)) { Machine.Cpu.IsThrottled ^= true; } else if (control && (e.Key == Key.Multiply)) { Machine.Video.IsMonochrome ^= true; } else if (control && (e.Key == Key.Subtract)) { Machine.Video.IsFullScreen ^= true; } Update(); } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")] private static int GetAsciiKey(Key key, KeyboardDevice keyboard) { bool control = ((keyboard.Modifiers & ModifierKeys.Control) != 0); bool shift = ((keyboard.Modifiers & ModifierKeys.Shift) != 0); bool capsLock = shift ^ keyboard.IsKeyToggled(Key.CapsLock); switch (key) { case Key.Left: return 0x08; case Key.Tab: return 0x09; case Key.Down: return 0x0A; case Key.Up: return 0x0B; case Key.Enter: return 0x0D; case Key.Right: return 0x15; case Key.Escape: return 0x1B; case Key.Back: return control ? -1 : 0x7F; case Key.Space: return ' '; case Key.D1: return shift ? '!' : '1'; case Key.D2: return control ? 0x00 : shift ? '@' : '2'; case Key.D3: return shift ? '#' : '3'; case Key.D4: return shift ? '$' : '4'; case Key.D5: return shift ? '%' : '5'; case Key.D6: return control ? 0x1E : shift ? '^' : '6'; case Key.D7: return shift ? '&' : '7'; case Key.D8: return shift ? '*' : '8'; case Key.D9: return shift ? '(' : '9'; case Key.D0: return shift ? ')' : '0'; case Key.A: return control ? 0x01 : capsLock ? 'A' : 'a'; case Key.B: return control ? 0x02 : capsLock ? 'B' : 'b'; case Key.C: return control ? 0x03 : capsLock ? 'C' : 'c'; case Key.D: return control ? 0x04 : capsLock ? 'D' : 'd'; case Key.E: return control ? 0x05 : capsLock ? 'E' : 'e'; case Key.F: return control ? 0x06 : capsLock ? 'F' : 'f'; case Key.G: return control ? 0x07 : capsLock ? 'G' : 'g'; case Key.H: return control ? 0x08 : capsLock ? 'H' : 'h'; case Key.I: return control ? 0x09 : capsLock ? 'I' : 'i'; case Key.J: return control ? 0x0A : capsLock ? 'J' : 'j'; case Key.K: return control ? 0x0B : capsLock ? 'K' : 'k'; case Key.L: return control ? 0x0C : capsLock ? 'L' : 'l'; case Key.M: return control ? 0x0D : capsLock ? 'M' : 'm'; case Key.N: return control ? 0x0E : capsLock ? 'N' : 'n'; case Key.O: return control ? 0x0F : capsLock ? 'O' : 'o'; case Key.P: return control ? 0x10 : capsLock ? 'P' : 'p'; case Key.Q: return control ? 0x11 : capsLock ? 'Q' : 'q'; case Key.R: return control ? 0x12 : capsLock ? 'R' : 'r'; case Key.S: return control ? 0x13 : capsLock ? 'S' : 's'; case Key.T: return control ? 0x14 : capsLock ? 'T' : 't'; case Key.U: return control ? 0x15 : capsLock ? 'U' : 'u'; case Key.V: return control ? 0x16 : capsLock ? 'V' : 'v'; case Key.W: return control ? 0x17 : capsLock ? 'W' : 'w'; case Key.X: return control ? 0x18 : capsLock ? 'X' : 'x'; case Key.Y: return control ? 0x19 : capsLock ? 'Y' : 'y'; case Key.Z: return control ? 0x1A : capsLock ? 'Z' : 'z'; case Key.Oem1: return shift ? ':' : ';'; case Key.Oem2: return shift ? '?' : '/'; case Key.Oem3: return shift ? '~' : '`'; case Key.Oem4: return shift ? '{' : '['; case Key.Oem5: return control ? 0x1C : shift ? '|' : '\\'; case Key.Oem6: return control ? 0x1D : shift ? '}' : ']'; case Key.Oem7: return shift ? '"' : '\''; case Key.OemMinus: return control ? 0x1F : shift ? '_' : '-'; case Key.OemPlus: return shift ? '+' : '='; case Key.OemComma: return shift ? '<' : ','; case Key.OemPeriod: return shift ? '>' : '.'; } return -1; } private static readonly Key[] KeyValues = (from key in (Key[])Enum.GetValues(typeof(Key)) where (key != Key.None) // filter Key.None; avoids validation exception select key).ToArray(); private static readonly int KeyCount = (int)(KeyValues.Max()) + 1; private bool[] _states = new bool[KeyCount]; private bool _updateAnyKeyDown; } }