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;