feat: add keyboard layout support

This commit is contained in:
2026-08-13 17:57:44 +02:00
parent 86e3ebfe33
commit 821543238d
36 changed files with 1132 additions and 125 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 109
#define MONTAUK_BUILD_NUMBER 116
+3
View File
@@ -54,6 +54,9 @@ namespace montauk::abi {
outEvent->shift = k.Shift;
outEvent->ctrl = k.Ctrl;
outEvent->alt = k.Alt;
outEvent->capslock = k.CapsLock;
outEvent->altgr = k.AltGr;
outEvent->extended = k.Extended;
}
static char Sys_GetChar() {
+7
View File
@@ -458,6 +458,13 @@ namespace montauk::abi {
bool shift;
bool ctrl;
bool alt;
bool capslock;
// Right Alt held, reported separately from alt so keyboard layouts can
// use it as AltGr without swallowing left-Alt shortcuts.
bool altgr;
// Key came from an E0-prefixed scancode (keypad "/" and Enter share a
// scancode with the main-block keys). Layout translation must skip it.
bool extended;
};
struct MouseState {
+21 -4
View File
@@ -179,7 +179,9 @@ namespace Drivers::PS2::Keyboard {
.Shift = g_Modifiers.LeftShift || g_Modifiers.RightShift,
.Ctrl = g_Modifiers.LeftCtrl || g_Modifiers.RightCtrl,
.Alt = g_Modifiers.LeftAlt || g_Modifiers.RightAlt,
.CapsLock = g_Modifiers.CapsLock
.CapsLock = g_Modifiers.CapsLock,
.AltGr = g_Modifiers.RightAlt,
.Extended = true
};
PushAndNotify(event);
@@ -237,7 +239,9 @@ namespace Drivers::PS2::Keyboard {
.Shift = g_Modifiers.LeftShift || g_Modifiers.RightShift,
.Ctrl = g_Modifiers.LeftCtrl || g_Modifiers.RightCtrl,
.Alt = g_Modifiers.LeftAlt || g_Modifiers.RightAlt,
.CapsLock = g_Modifiers.CapsLock
.CapsLock = g_Modifiers.CapsLock,
.AltGr = g_Modifiers.RightAlt,
.Extended = false
};
PushAndNotify(event);
return;
@@ -261,7 +265,9 @@ namespace Drivers::PS2::Keyboard {
.Shift = g_Modifiers.LeftShift || g_Modifiers.RightShift,
.Ctrl = g_Modifiers.LeftCtrl || g_Modifiers.RightCtrl,
.Alt = g_Modifiers.LeftAlt || g_Modifiers.RightAlt,
.CapsLock = g_Modifiers.CapsLock
.CapsLock = g_Modifiers.CapsLock,
.AltGr = g_Modifiers.RightAlt,
.Extended = false
};
PushAndNotify(event);
@@ -298,7 +304,18 @@ namespace Drivers::PS2::Keyboard {
}
void InjectKeyEvent(const KeyEvent& event) {
PushAndNotify(event);
// Caps Lock is owned here rather than by each injecting driver: USB HID
// reports the key as an ordinary usage and carries no lock state, so
// without this the lock would never toggle on a USB keyboard.
KeyEvent stamped = event;
if (stamped.Scancode == ScCapsLock && stamped.Pressed)
g_Modifiers.CapsLock = !g_Modifiers.CapsLock;
stamped.CapsLock = g_Modifiers.CapsLock;
PushAndNotify(stamped);
}
bool GetCapsLock() {
return g_Modifiers.CapsLock;
}
};
+11
View File
@@ -18,6 +18,13 @@ namespace Drivers::PS2::Keyboard {
bool Ctrl;
bool Alt;
bool CapsLock;
// Right Alt held. Reported separately from Alt so keyboard layouts can
// use it as AltGr without swallowing left-Alt shortcuts.
bool AltGr;
// Key came from an E0-prefixed (extended) scancode. The prefix is not
// part of Scancode, so without this flag keypad "/" is indistinguishable
// from the main "/" key and layout translation would rewrite it.
bool Extended;
};
// Modifier key state
@@ -52,4 +59,8 @@ namespace Drivers::PS2::Keyboard {
// Inject a key event from an external source (e.g., USB HID keyboard)
void InjectKeyEvent(const KeyEvent& event);
// Current Caps Lock state. Injecting drivers (USB HID) need this to
// compute ASCII, since the lock is toggled centrally here.
bool GetCapsLock();
};
+24 -12
View File
@@ -200,20 +200,28 @@ namespace Drivers::USB::HidKeyboard {
return (hidUsage >= 0x39 && hidUsage <= 0x53);
}
// Keys that arrive E0-prefixed on a PS/2 keyboard. They share a scancode
// with a main-block key (keypad "/" with "/", keypad Enter with Enter), so
// they must be flagged or a keyboard layout would rewrite them.
static bool IsExtendedKey(uint8_t hidUsage) {
return hidUsage == 0x54 || hidUsage == 0x58; // KP /, KP Enter
}
// Build and inject a KeyEvent into the PS/2 keyboard buffer
static void InjectKey(uint8_t scancode, bool pressed, uint8_t modifiers,
bool nonChar = false) {
bool nonChar = false, bool extended = false) {
bool shift = (modifiers & (MOD_LEFT_SHIFT | MOD_RIGHT_SHIFT)) != 0;
bool ctrl = (modifiers & (MOD_LEFT_CTRL | MOD_RIGHT_CTRL)) != 0;
bool alt = (modifiers & (MOD_LEFT_ALT | MOD_RIGHT_ALT)) != 0;
bool caps = Drivers::PS2::Keyboard::GetCapsLock();
char ascii = 0;
if (!nonChar && scancode < 128 && pressed) {
if (shift) {
ascii = g_ScancodeToAsciiShifted[scancode];
} else {
ascii = g_ScancodeToAscii[scancode];
}
// Caps Lock inverts shift for letters only, matching the PS/2 path.
char base = g_ScancodeToAscii[scancode];
bool upper = shift;
if (caps && base >= 'a' && base <= 'z') upper = !shift;
ascii = upper ? g_ScancodeToAsciiShifted[scancode] : base;
}
Drivers::PS2::Keyboard::KeyEvent event = {
@@ -223,7 +231,9 @@ namespace Drivers::USB::HidKeyboard {
.Shift = shift,
.Ctrl = ctrl,
.Alt = alt,
.CapsLock = false
.CapsLock = caps, // re-stamped by InjectKeyEvent, which owns the lock
.AltGr = (modifiers & MOD_RIGHT_ALT) != 0,
.Extended = extended
};
Drivers::PS2::Keyboard::InjectKeyEvent(event);
@@ -238,7 +248,9 @@ namespace Drivers::USB::HidKeyboard {
.Shift = (modifiers & (MOD_LEFT_SHIFT | MOD_RIGHT_SHIFT)) != 0,
.Ctrl = (modifiers & (MOD_LEFT_CTRL | MOD_RIGHT_CTRL)) != 0,
.Alt = (modifiers & (MOD_LEFT_ALT | MOD_RIGHT_ALT)) != 0,
.CapsLock = false
.CapsLock = Drivers::PS2::Keyboard::GetCapsLock(),
.AltGr = (modifiers & MOD_RIGHT_ALT) != 0,
.Extended = false
};
Drivers::PS2::Keyboard::InjectKeyEvent(event);
@@ -271,7 +283,7 @@ namespace Drivers::USB::HidKeyboard {
uint8_t scancode = g_HidToScancode[key];
if (scancode != 0) {
InjectKey(scancode, false, modifiers, IsNonCharKey(key));
InjectKey(scancode, false, modifiers, IsNonCharKey(key), IsExtendedKey(key));
}
g_PrevKeys[i] = 0;
}
@@ -354,7 +366,7 @@ namespace Drivers::USB::HidKeyboard {
if (!KeyInArray(key, g_PrevKeys)) {
uint8_t scancode = g_HidToScancode[key];
if (scancode != 0) {
InjectKey(scancode, true, modifiers, IsNonCharKey(key));
InjectKey(scancode, true, modifiers, IsNonCharKey(key), IsExtendedKey(key));
g_RepeatKey = key;
g_RepeatStartMs = now;
g_LastRepeatMs = 0;
@@ -373,7 +385,7 @@ namespace Drivers::USB::HidKeyboard {
if (!KeyInArray(key, keys)) {
uint8_t scancode = g_HidToScancode[key];
if (scancode != 0) {
InjectKey(scancode, false, modifiers, IsNonCharKey(key));
InjectKey(scancode, false, modifiers, IsNonCharKey(key), IsExtendedKey(key));
}
if (key == g_RepeatKey) {
g_RepeatKey = 0;
@@ -405,7 +417,7 @@ namespace Drivers::USB::HidKeyboard {
(now - g_LastRepeatMs) >= TYPEMATIC_PERIOD_MS) {
uint8_t scancode = g_HidToScancode[g_RepeatKey];
if (scancode != 0) {
InjectKey(scancode, true, g_PrevModifiers, IsNonCharKey(g_RepeatKey));
InjectKey(scancode, true, g_PrevModifiers, IsNonCharKey(g_RepeatKey), IsExtendedKey(g_RepeatKey));
}
g_LastRepeatMs = now;
}