diff --git a/BizHawk.Client.Common/config/Config.cs b/BizHawk.Client.Common/config/Config.cs index b48b85a0dc..dcd0654ca4 100644 --- a/BizHawk.Client.Common/config/Config.cs +++ b/BizHawk.Client.Common/config/Config.cs @@ -105,6 +105,7 @@ namespace BizHawk.Client.Common public bool RunInBackground = true; public bool AcceptBackgroundInput = false; public bool AcceptBackgroundInputControllerOnly = false; + public bool HandleAlternateKeyboardLayouts = false; public bool SingleInstanceMode = false; public bool AllowUD_LR = false; public bool ForbidUD_LR = false; diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 02e3c5cbf7..1b30776f00 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -668,6 +668,7 @@ + diff --git a/BizHawk.Client.EmuHawk/Input/Keyboard.cs b/BizHawk.Client.EmuHawk/Input/Keyboard.cs index d42f5e6b6a..b7cda338cf 100644 --- a/BizHawk.Client.EmuHawk/Input/Keyboard.cs +++ b/BizHawk.Client.EmuHawk/Input/Keyboard.cs @@ -60,9 +60,9 @@ namespace BizHawk.Client.EmuHawk foreach (var e in events) { foreach (var k in e.PressedKeys) - _eventList.Add(new KeyEvent { Key = k, Pressed = true }); + _eventList.Add(new KeyEvent { Key = KeyboardMapping.Handle(k), Pressed = true }); foreach (var k in e.ReleasedKeys) - _eventList.Add(new KeyEvent { Key = k, Pressed = false }); + _eventList.Add(new KeyEvent { Key = KeyboardMapping.Handle(k), Pressed = false }); } } diff --git a/BizHawk.Client.EmuHawk/Input/KeyboardMapping.cs b/BizHawk.Client.EmuHawk/Input/KeyboardMapping.cs new file mode 100644 index 0000000000..270cf9eecb --- /dev/null +++ b/BizHawk.Client.EmuHawk/Input/KeyboardMapping.cs @@ -0,0 +1,416 @@ +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using BizHawk.Client.Common; +using SlimDX.DirectInput; + +namespace BizHawk.Client.EmuHawk +{ + internal static class KeyboardMapping + { + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern uint MapVirtualKey(uint uCode, uint uMapType); + + private const uint MAPVK_VSC_TO_VK_EX = 0x03; + + public static Key Handle(Key key) + { + if (!Global.Config.HandleAlternateKeyboardLayouts) return key; + ScanCode inputScanCode = SlimDXScanCodeMap[(int)key]; + Keys virtualKey = (Keys)MapVirtualKey((uint)inputScanCode, MAPVK_VSC_TO_VK_EX); + ScanCode standardScanCode = GetStandardScanCode(virtualKey); + if (standardScanCode == 0) + standardScanCode = inputScanCode; + return ScanCodeToSlimDXKey[standardScanCode]; + } + + private static ScanCode GetStandardScanCode(Keys virtualKey) + { + switch (virtualKey) + { + case Keys.Escape: return ScanCode.Escape; + case Keys.D1: return ScanCode.D1; + case Keys.D2: return ScanCode.D2; + case Keys.D3: return ScanCode.D3; + case Keys.D4: return ScanCode.D4; + case Keys.D5: return ScanCode.D5; + case Keys.D6: return ScanCode.D6; + case Keys.D7: return ScanCode.D7; + case Keys.D8: return ScanCode.D8; + case Keys.D9: return ScanCode.D9; + case Keys.D0: return ScanCode.D0; + case Keys.OemMinus: return ScanCode.Minus; + case Keys.Oemplus: return ScanCode.Equals; + case Keys.Back: return ScanCode.Back; + case Keys.Tab: return ScanCode.Tab; + case Keys.Q: return ScanCode.Q; + case Keys.W: return ScanCode.W; + case Keys.E: return ScanCode.E; + case Keys.R: return ScanCode.R; + case Keys.T: return ScanCode.T; + case Keys.Y: return ScanCode.Y; + case Keys.U: return ScanCode.U; + case Keys.I: return ScanCode.I; + case Keys.O: return ScanCode.O; + case Keys.P: return ScanCode.P; + case Keys.OemOpenBrackets: return ScanCode.LBracket; + case Keys.OemCloseBrackets: return ScanCode.RBracket; + case Keys.Return: return ScanCode.Return; + case Keys.LControlKey: return ScanCode.LControl; + case Keys.A: return ScanCode.A; + case Keys.S: return ScanCode.S; + case Keys.D: return ScanCode.D; + case Keys.F: return ScanCode.F; + case Keys.G: return ScanCode.G; + case Keys.H: return ScanCode.H; + case Keys.J: return ScanCode.J; + case Keys.K: return ScanCode.K; + case Keys.L: return ScanCode.L; + case Keys.OemSemicolon: return ScanCode.Semicolon; + case Keys.OemQuotes: return ScanCode.Apostrophe; + case Keys.Oemtilde: return ScanCode.Grave; + case Keys.LShiftKey: return ScanCode.LShift; + case Keys.OemPipe: return ScanCode.Backslash; + case Keys.Z: return ScanCode.Z; + case Keys.X: return ScanCode.X; + case Keys.C: return ScanCode.C; + case Keys.V: return ScanCode.V; + case Keys.B: return ScanCode.B; + case Keys.N: return ScanCode.N; + case Keys.M: return ScanCode.M; + case Keys.Oemcomma: return ScanCode.Comma; + case Keys.OemPeriod: return ScanCode.Period; + case Keys.OemQuestion: return ScanCode.Slash; + case Keys.RShiftKey: return ScanCode.RShift; + case Keys.Multiply: return ScanCode.Multiply; + case Keys.LMenu: return ScanCode.LMenu; + case Keys.Space: return ScanCode.Space; + case Keys.Capital: return ScanCode.Capital; + case Keys.F1: return ScanCode.F1; + case Keys.F2: return ScanCode.F2; + case Keys.F3: return ScanCode.F3; + case Keys.F4: return ScanCode.F4; + case Keys.F5: return ScanCode.F5; + case Keys.F6: return ScanCode.F6; + case Keys.F7: return ScanCode.F7; + case Keys.F8: return ScanCode.F8; + case Keys.F9: return ScanCode.F9; + case Keys.F10: return ScanCode.F10; + case Keys.NumLock: return ScanCode.NumLock; + case Keys.Scroll: return ScanCode.Scroll; + case Keys.Subtract: return ScanCode.Subtract; + case Keys.Add: return ScanCode.Add; + case Keys.OemBackslash: return ScanCode.Oem_102; + case Keys.F11: return ScanCode.F11; + case Keys.F12: return ScanCode.F12; + case Keys.F13: return ScanCode.F13; + case Keys.F14: return ScanCode.F14; + case Keys.F15: return ScanCode.F15; + } + return 0; + } + + private enum ScanCode + { + Escape = 0x01, + D1 = 0x02, + D2 = 0x03, + D3 = 0x04, + D4 = 0x05, + D5 = 0x06, + D6 = 0x07, + D7 = 0x08, + D8 = 0x09, + D9 = 0x0A, + D0 = 0x0B, + Minus = 0x0C, + Equals = 0x0D, + Back = 0x0E, + Tab = 0x0F, + Q = 0x10, + W = 0x11, + E = 0x12, + R = 0x13, + T = 0x14, + Y = 0x15, + U = 0x16, + I = 0x17, + O = 0x18, + P = 0x19, + LBracket = 0x1A, + RBracket = 0x1B, + Return = 0x1C, + LControl = 0x1D, + A = 0x1E, + S = 0x1F, + D = 0x20, + F = 0x21, + G = 0x22, + H = 0x23, + J = 0x24, + K = 0x25, + L = 0x26, + Semicolon = 0x27, + Apostrophe = 0x28, + Grave = 0x29, + LShift = 0x2A, + Backslash = 0x2B, + Z = 0x2C, + X = 0x2D, + C = 0x2E, + V = 0x2F, + B = 0x30, + N = 0x31, + M = 0x32, + Comma = 0x33, + Period = 0x34, + Slash = 0x35, + RShift = 0x36, + Multiply = 0x37, + LMenu = 0x38, + Space = 0x39, + Capital = 0x3A, + F1 = 0x3B, + F2 = 0x3C, + F3 = 0x3D, + F4 = 0x3E, + F5 = 0x3F, + F6 = 0x40, + F7 = 0x41, + F8 = 0x42, + F9 = 0x43, + F10 = 0x44, + NumLock = 0x45, + Scroll = 0x46, + NumPad7 = 0x47, + NumPad8 = 0x48, + NumPad9 = 0x49, + Subtract = 0x4A, + NumPad4 = 0x4B, + NumPad5 = 0x4C, + NumPad6 = 0x4D, + Add = 0x4E, + NumPad1 = 0x4F, + NumPad2 = 0x50, + NumPad3 = 0x51, + NumPad0 = 0x52, + Decimal = 0x53, + Oem_102 = 0x56, + F11 = 0x57, + F12 = 0x58, + F13 = 0x64, + F14 = 0x65, + F15 = 0x66, + Kana = 0x70, + Abnt_C1 = 0x73, + Convert = 0x79, + NoConvert = 0x7B, + Yen = 0x7D, + Abnt_C2 = 0x7E, + NumPadEquals = 0x8D, + PrevTrack = 0x90, + AT = 0x91, + Colon = 0x92, + Underline = 0x93, + Kanji = 0x94, + Stop = 0x95, + AX = 0x96, + Unlabeled = 0x97, + NextTrack = 0x99, + NumPadEnter = 0x9C, + RControl = 0x9D, + Mute = 0xA0, + Calculator = 0xA1, + PlayPause = 0xA2, + MediaStop = 0xA4, + VolumeDown = 0xAE, + VolumeUp = 0xB0, + WebHome = 0xB2, + NumPadComma = 0xB3, + Divide = 0xB5, + SysRq = 0xB7, + RMenu = 0xB8, + Pause = 0xC5, + Home = 0xC7, + Up = 0xC8, + Prior = 0xC9, + Left = 0xCB, + Right = 0xCD, + End = 0xCF, + Down = 0xD0, + Next = 0xD1, + Insert = 0xD2, + Delete = 0xD3, + LWin = 0xDB, + RWin = 0xDC, + Apps = 0xDD, + Power = 0xDE, + Sleep = 0xDF, + Wake = 0xE3, + WebSearch = 0xE5, + WebFavorites = 0xE6, + WebRefresh = 0xE7, + WebStop = 0xE8, + WebForward = 0xE9, + WebBack = 0xEA, + MyComputer = 0xEB, + Mail = 0xEC, + MediaSelect = 0xED + } + + private static ScanCode[] SlimDXScanCodeMap = new ScanCode[] + { + ScanCode.D0, // 0 + ScanCode.D1, // 1 + ScanCode.D2, // 2 + ScanCode.D3, // 3 + ScanCode.D4, // 4 + ScanCode.D5, // 5 + ScanCode.D6, // 6 + ScanCode.D7, // 7 + ScanCode.D8, // 8 + ScanCode.D9, // 9 + ScanCode.A, // 10 + ScanCode.B, // 11 + ScanCode.C, // 12 + ScanCode.D, // 13 + ScanCode.E, // 14 + ScanCode.F, // 15 + ScanCode.G, // 16 + ScanCode.H, // 17 + ScanCode.I, // 18 + ScanCode.J, // 19 + ScanCode.K, // 20 + ScanCode.L, // 21 + ScanCode.M, // 22 + ScanCode.N, // 23 + ScanCode.O, // 24 + ScanCode.P, // 25 + ScanCode.Q, // 26 + ScanCode.R, // 27 + ScanCode.S, // 28 + ScanCode.T, // 29 + ScanCode.U, // 30 + ScanCode.V, // 31 + ScanCode.W, // 32 + ScanCode.X, // 33 + ScanCode.Y, // 34 + ScanCode.Z, // 35 + ScanCode.Abnt_C1, // 36 + ScanCode.Abnt_C2, // 37 + ScanCode.Apostrophe, // 38 + ScanCode.Apps, // 39 + ScanCode.AT, // 40 + ScanCode.AX, // 41 + ScanCode.Back, // 42 + ScanCode.Backslash, // 43 + ScanCode.Calculator, // 44 + ScanCode.Capital, // 45 + ScanCode.Colon, // 46 + ScanCode.Comma, // 47 + ScanCode.Convert, // 48 + ScanCode.Delete, // 49 + ScanCode.Down, // 50 + ScanCode.End, // 51 + ScanCode.Equals, // 52 + ScanCode.Escape, // 53 + ScanCode.F1, // 54 + ScanCode.F2, // 55 + ScanCode.F3, // 56 + ScanCode.F4, // 57 + ScanCode.F5, // 58 + ScanCode.F6, // 59 + ScanCode.F7, // 60 + ScanCode.F8, // 61 + ScanCode.F9, // 62 + ScanCode.F10, // 63 + ScanCode.F11, // 64 + ScanCode.F12, // 65 + ScanCode.F13, // 66 + ScanCode.F14, // 67 + ScanCode.F15, // 68 + ScanCode.Grave, // 69 + ScanCode.Home, // 70 + ScanCode.Insert, // 71 + ScanCode.Kana, // 72 + ScanCode.Kanji, // 73 + ScanCode.LBracket, // 74 + ScanCode.LControl, // 75 + ScanCode.Left, // 76 + ScanCode.LMenu, // 77 + ScanCode.LShift, // 78 + ScanCode.LWin, // 79 + ScanCode.Mail, // 80 + ScanCode.MediaSelect, // 81 + ScanCode.MediaStop, // 82 + ScanCode.Minus, // 83 + ScanCode.Mute, // 84 + ScanCode.MyComputer, // 85 + ScanCode.NextTrack, // 86 + ScanCode.NoConvert, // 87 + ScanCode.NumLock, // 88 + ScanCode.NumPad0, // 89 + ScanCode.NumPad1, // 90 + ScanCode.NumPad2, // 91 + ScanCode.NumPad3, // 92 + ScanCode.NumPad4, // 93 + ScanCode.NumPad5, // 94 + ScanCode.NumPad6, // 95 + ScanCode.NumPad7, // 96 + ScanCode.NumPad8, // 97 + ScanCode.NumPad9, // 98 + ScanCode.NumPadComma, // 99 + ScanCode.NumPadEnter, // 100 + ScanCode.NumPadEquals, // 101 + ScanCode.Subtract, // 102 + ScanCode.Decimal, // 103 + ScanCode.Add, // 104 + ScanCode.Divide, // 105 + ScanCode.Multiply, // 106 + ScanCode.Oem_102, // 107 + ScanCode.Next, // 108 + ScanCode.Prior, // 109 + ScanCode.Pause, // 110 + ScanCode.Period, // 111 + ScanCode.PlayPause, // 112 + ScanCode.Power, // 113 + ScanCode.PrevTrack, // 114 + ScanCode.RBracket, // 115 + ScanCode.RControl, // 116 + ScanCode.Return, // 117 + ScanCode.Right, // 118 + ScanCode.RMenu, // 119 + ScanCode.RShift, // 120 + ScanCode.RWin, // 121 + ScanCode.Scroll, // 122 + ScanCode.Semicolon, // 123 + ScanCode.Slash, // 124 + ScanCode.Sleep, // 125 + ScanCode.Space, // 126 + ScanCode.Stop, // 127 + ScanCode.SysRq, // 128 + ScanCode.Tab, // 129 + ScanCode.Underline, // 130 + ScanCode.Unlabeled, // 131 + ScanCode.Up, // 132 + ScanCode.VolumeDown, // 133 + ScanCode.VolumeUp, // 134 + ScanCode.Wake, // 135 + ScanCode.WebBack, // 136 + ScanCode.WebFavorites, // 137 + ScanCode.WebForward, // 138 + ScanCode.WebHome, // 139 + ScanCode.WebRefresh, // 140 + ScanCode.WebSearch, // 141 + ScanCode.WebStop, // 142 + ScanCode.Yen, // 143 + 0 // 144 + }; + + private static Dictionary ScanCodeToSlimDXKey = + SlimDXScanCodeMap + .Select((n, i) => new { Value = n, Index = i }) + .ToDictionary(n => n.Value, n => (Key)n.Index); + } +} diff --git a/BizHawk.Client.EmuHawk/config/GuiOptions.Designer.cs b/BizHawk.Client.EmuHawk/config/GuiOptions.Designer.cs index 4b273ae153..038f9c74bf 100644 --- a/BizHawk.Client.EmuHawk/config/GuiOptions.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/GuiOptions.Designer.cs @@ -74,6 +74,7 @@ this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.label9 = new System.Windows.Forms.Label(); this.label10 = new System.Windows.Forms.Label(); + this.HandleAlternateKeyboardLayoutsCheckBox = new System.Windows.Forms.CheckBox(); this.tabControl1.SuspendLayout(); this.tabPage1.SuspendLayout(); this.groupBox1.SuspendLayout(); @@ -121,6 +122,7 @@ // // tabPage1 // + this.tabPage1.Controls.Add(this.HandleAlternateKeyboardLayoutsCheckBox); this.tabPage1.Controls.Add(this.groupBox1); this.tabPage1.Controls.Add(this.NeverAskSaveCheckbox); this.tabPage1.Controls.Add(this.label2); @@ -146,7 +148,7 @@ this.groupBox1.Controls.Add(this.StartFullScreenCheckbox); this.groupBox1.Controls.Add(this.label3); this.groupBox1.Controls.Add(this.SingleInstanceModeCheckbox); - this.groupBox1.Location = new System.Drawing.Point(6, 182); + this.groupBox1.Location = new System.Drawing.Point(6, 205); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(369, 140); this.groupBox1.TabIndex = 13; @@ -562,6 +564,16 @@ this.label10.TabIndex = 1; this.label10.Text = "every"; // + // HandleAlternateKeyboardLayoutsCheckBox + // + this.HandleAlternateKeyboardLayoutsCheckBox.AutoSize = true; + this.HandleAlternateKeyboardLayoutsCheckBox.Location = new System.Drawing.Point(6, 175); + this.HandleAlternateKeyboardLayoutsCheckBox.Name = "HandleAlternateKeyboardLayoutsCheckBox"; + this.HandleAlternateKeyboardLayoutsCheckBox.Size = new System.Drawing.Size(255, 17); + this.HandleAlternateKeyboardLayoutsCheckBox.TabIndex = 11; + this.HandleAlternateKeyboardLayoutsCheckBox.Text = "Handle alternate keyboard layouts (e.g. Dvorak) [experimental]"; + this.HandleAlternateKeyboardLayoutsCheckBox.UseVisualStyleBackColor = true; + // // EmuHawkOptions // this.AcceptButton = this.OkBtn; @@ -640,5 +652,6 @@ private System.Windows.Forms.NumericUpDown AutosaveSRAMtextBox; private System.Windows.Forms.Label label10; private System.Windows.Forms.Label label9; + private System.Windows.Forms.CheckBox HandleAlternateKeyboardLayoutsCheckBox; } } \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/GuiOptions.cs b/BizHawk.Client.EmuHawk/config/GuiOptions.cs index 76e29d2a03..f05e73be46 100644 --- a/BizHawk.Client.EmuHawk/config/GuiOptions.cs +++ b/BizHawk.Client.EmuHawk/config/GuiOptions.cs @@ -50,6 +50,7 @@ namespace BizHawk.Client.EmuHawk RunInBackgroundCheckbox.Checked = Global.Config.RunInBackground; AcceptBackgroundInputCheckbox.Checked = Global.Config.AcceptBackgroundInput; AcceptBackgroundInputControllerOnlyCheckBox.Checked = Global.Config.AcceptBackgroundInputControllerOnly; + HandleAlternateKeyboardLayoutsCheckBox.Checked = Global.Config.HandleAlternateKeyboardLayouts; NeverAskSaveCheckbox.Checked = Global.Config.SupressAskSave; SingleInstanceModeCheckbox.Checked = Global.Config.SingleInstanceMode; @@ -85,6 +86,7 @@ namespace BizHawk.Client.EmuHawk Global.Config.RunInBackground = RunInBackgroundCheckbox.Checked; Global.Config.AcceptBackgroundInput = AcceptBackgroundInputCheckbox.Checked; Global.Config.AcceptBackgroundInputControllerOnly = AcceptBackgroundInputControllerOnlyCheckBox.Checked; + Global.Config.HandleAlternateKeyboardLayouts = HandleAlternateKeyboardLayoutsCheckBox.Checked; Global.Config.SupressAskSave = NeverAskSaveCheckbox.Checked; Global.Config.SingleInstanceMode = SingleInstanceModeCheckbox.Checked;