Skip to content

Commit 7c48c55

Browse files
author
neil
committed
fix shift key for web vnc
1 parent e26056f commit 7c48c55

File tree

1 file changed

+43
-25
lines changed

1 file changed

+43
-25
lines changed

anyvm.py

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ def fatal(msg):
441441
</div>
442442
<div class="toolbar">
443443
<div class="toolbar-group">
444+
<button id="btn-sticky-shift" onclick="toggleSticky('ShiftLeft', 0xffe1, this)" title="Sticky Shift">Shift</button>
444445
<button id="btn-sticky-ctrl" onclick="toggleSticky('ControlLeft', 0xffe3, this)" title="Sticky Ctrl">Ctrl</button>
445446
<button id="btn-sticky-alt" onclick="toggleSticky('AltLeft', 0xffe9, this)" title="Sticky Alt">Alt</button>
446447
<button id="btn-sticky-meta" onclick="toggleSticky('MetaLeft', 0xffeb, this)" title="Sticky Meta">
@@ -920,16 +921,17 @@ def fatal(msg):
920921
document.addEventListener('keydown', e => sendKey(e, true));
921922
document.addEventListener('keyup', e => sendKey(e, false));
922923
924+
// Track which keysym was sent for each physical code to ensure consistent keyup
925+
const pressedKeysyms = {};
926+
923927
function sendKey(e, down) {
924928
if (!ws) return;
925929
926-
// Captured keys that we handle via code-to-keysym mapping
927930
const code = e.code;
928931
const key = e.key;
929932
930933
// Support Ctrl+V (Windows/Linux) or Cmd+V (Mac) for pasting
931934
if ((e.ctrlKey || e.metaKey) && (key === 'v' || key === 'V' || code === 'KeyV')) {
932-
// We let the 'paste' event handle this to avoid permission prompts where possible
933935
return;
934936
}
935937
@@ -948,47 +950,63 @@ def fatal(msg):
948950
}
949951
950952
const keyMap = {
951-
// Special keys
952953
'Backspace': 0xff08, 'Tab': 0xff09, 'Enter': 0xff0d, 'Escape': 0xff1b, 'Delete': 0xffff,
953954
'Home': 0xff50, 'End': 0xff57, 'PageUp': 0xff55, 'PageDown': 0xff56,
954955
'ArrowLeft': 0xff51, 'ArrowUp': 0xff52, 'ArrowRight': 0xff53, 'ArrowDown': 0xff54, 'Insert': 0xff63,
955956
'F1': 0xffbe, 'F2': 0xffbf, 'F3': 0xffc0, 'F4': 0xffc1, 'F5': 0xffc2, 'F6': 0xffc3,
956957
'F7': 0xffc4, 'F8': 0xffc5, 'F9': 0xffc6, 'F10': 0xffc7, 'F11': 0xffc8, 'F12': 0xffc9,
957958
'ShiftLeft': 0xffe1, 'ShiftRight': 0xffe2, 'ControlLeft': 0xffe3, 'ControlRight': 0xffe4,
958959
'AltLeft': 0xffe9, 'AltRight': 0xffea, 'MetaLeft': 0xffeb, 'MetaRight': 0xffec, 'Space': 0x0020,
959-
// Map Digit keys to their base ASCII (prevents Shift+1 sending '!')
960-
'Digit1': 0x31, 'Digit2': 0x32, 'Digit3': 0x33, 'Digit4': 0x34, 'Digit5': 0x35,
961-
'Digit6': 0x36, 'Digit7': 0x37, 'Digit8': 0x38, 'Digit9': 0x39, 'Digit0': 0x30,
962-
// Map alphabet keys
963-
'KeyA': 0x61, 'KeyB': 0x62, 'KeyC': 0x63, 'KeyD': 0x64, 'KeyE': 0x65, 'KeyF': 0x66, 'KeyG': 0x67,
964-
'KeyH': 0x68, 'KeyI': 0x69, 'KeyJ': 0x6a, 'KeyK': 0x6b, 'KeyL': 0x6c, 'KeyM': 0x6d, 'KeyN': 0x6e,
965-
'KeyO': 0x6f, 'KeyP': 0x70, 'KeyQ': 0x71, 'KeyR': 0x72, 'KeyS': 0x73, 'KeyT': 0x74, 'KeyU': 0x75,
966-
'KeyV': 0x76, 'KeyW': 0x77, 'KeyX': 0x78, 'KeyY': 0x79, 'KeyZ': 0x7a,
967-
// Punctuations (using e.code ensures we send the base keysym regardless of Shift)
968-
'Semicolon': 0x3b, 'Equal': 0x3d, 'Comma': 0x2c, 'Minus': 0x2d, 'Period': 0x2e, 'Slash': 0x2f,
969-
'Backquote': 0x60, 'BracketLeft': 0x5b, 'Backslash': 0x5c, 'BracketRight': 0x5d, 'Quote': 0x27
960+
'Shift': 0xffe1, 'Control': 0xffe3, 'Alt': 0xffe9, 'Meta': 0xffeb
970961
};
971962
972963
let keysym = 0;
973-
if (keyMap[code]) {
974-
keysym = keyMap[code];
975-
} else if (keyMap[key]) {
976-
keysym = keyMap[key];
977-
} else if (key.length === 1) {
978-
keysym = key.charCodeAt(0);
964+
if (down) {
965+
// Prioritize specific control keys
966+
if (keyMap[code]) {
967+
keysym = keyMap[code];
968+
} else if (keyMap[key]) {
969+
keysym = keyMap[key];
970+
} else if (key.length === 1) {
971+
let char = key;
972+
const softShift = stickyStates['ShiftLeft'] || stickyStates['ShiftRight'];
973+
if (softShift && !e.shiftKey) {
974+
// If software Shift is on but physical is not, escalate letters to uppercase.
975+
// This is necessary because VNC servers often interpret keysyms literally.
976+
if (char >= 'a' && char <= 'z') char = char.toUpperCase();
977+
else if (char >= 'A' && char <= 'Z') char = char.toLowerCase(); // Caps lock inverse? No, stick to shift logic.
978+
}
979+
980+
keysym = char.charCodeAt(0);
981+
// If Ctrl or Alt is down, we want the base keysym (e.g. 'c' for Ctrl+C)
982+
if ((e.ctrlKey || e.altKey || e.metaKey) && keysym < 32) {
983+
if (keysym >= 1 && keysym <= 26) keysym += 96;
984+
}
985+
}
986+
987+
if (keysym) {
988+
pressedKeysyms[code] = keysym;
989+
}
979990
} else {
980-
return;
991+
keysym = pressedKeysyms[code];
992+
delete pressedKeysyms[code];
993+
994+
// Fallback for keyup if keydown was missed
995+
if (!keysym) {
996+
if (keyMap[code]) keysym = keyMap[code];
997+
else if (keyMap[key]) keysym = keyMap[key];
998+
else if (key.length === 1) keysym = key.charCodeAt(0);
999+
}
9811000
}
9821001
1002+
if (!keysym) return;
1003+
9831004
e.preventDefault();
9841005
9851006
try {
9861007
ws.send(new Uint8Array([
9871008
4, down ? 1 : 0, 0, 0,
988-
(keysym >> 24) & 0xff,
989-
(keysym >> 16) & 0xff,
990-
(keysym >> 8) & 0xff,
991-
keysym & 0xff
1009+
(keysym >> 24) & 0xff, (keysym >> 16) & 0xff, (keysym >> 8) & 0xff, keysym & 0xff
9921010
]));
9931011
} catch (err) {
9941012
console.error("Failed to send key:", err);

0 commit comments

Comments
 (0)