Files
netbird/client/vnc/server/scancodes.go
2026-05-17 15:48:15 +02:00

275 lines
6.6 KiB
Go

//go:build !js && !ios && !android
package server
// QEMU Extended Key Event carries hardware scancodes encoded as PC AT Set 1.
// Single-byte codes cover the standard keys; the "extended" prefix 0xE0 is
// merged into the high byte (so 0xE048 is the extended-Up arrow). This file
// translates those scancodes into the per-platform identifiers each input
// backend wants:
//
// - Linux uinput wants Linux KEY_* codes (defined in
// linux/input-event-codes.h). uinput is what we use for virtual Xvfb
// sessions on Linux.
// - X11 XTest wants XKB keycodes, which on a standard layout equal
// Linux KEY_* + 8 (the per-server offset between the Linux event code
// and the X server's keycode space).
// - Windows SendInput accepts the PC AT scancode directly via
// KEYEVENTF_SCANCODE, so no mapping table is needed there; the
// extended-key bit is set when the QEMU scancode high byte is 0xE0.
// - macOS CGEventCreateKeyboardEvent takes a "virtual keycode" from
// Apple's HID set, which is unrelated to PC AT and needs its own
// table (see qemuToMacVK in input_darwin.go).
//
// Linux KEY_* codes. Only the ones we reference, since the full
// linux/input-event-codes.h list isn't useful here. Naming mirrors the
// existing constants in input_uinput_unix.go (mixed case, no underscores).
const (
keyEsc = 1
key1 = 2
key2 = 3
key3 = 4
key4 = 5
key5 = 6
key6 = 7
key7 = 8
key8 = 9
key9 = 10
key0 = 11
keyMinus = 12
keyEqual = 13
keyBackspace = 14
keyTab = 15
keyQ = 16
keyW = 17
keyE = 18
keyR = 19
keyT = 20
keyY = 21
keyU = 22
keyI = 23
keyO = 24
keyP = 25
keyLeftBracket = 26
keyRightBracket = 27
keyEnter = 28
keyLeftCtrl = 29
keyA = 30
keyS = 31
keyD = 32
keyF = 33
keyG = 34
keyH = 35
keyJ = 36
keyK = 37
keyL = 38
keySemicolon = 39
keyApostrophe = 40
keyGrave = 41
keyLeftShift = 42
keyBackslash = 43
keyZ = 44
keyX = 45
keyC = 46
keyV = 47
keyB = 48
keyN = 49
keyM = 50
keyComma = 51
keyDot = 52
keySlash = 53
keyRightShift = 54
keyKPAsterisk = 55
keyLeftAlt = 56
keySpace = 57
keyCapsLock = 58
keyF1 = 59
keyF2 = 60
keyF3 = 61
keyF4 = 62
keyF5 = 63
keyF6 = 64
keyF7 = 65
keyF8 = 66
keyF9 = 67
keyF10 = 68
keyNumLock = 69
keyScrollLock = 70
keyKP7 = 71
keyKP8 = 72
keyKP9 = 73
keyKPMinus = 74
keyKP4 = 75
keyKP5 = 76
keyKP6 = 77
keyKPPlus = 78
keyKP1 = 79
keyKP2 = 80
keyKP3 = 81
keyKP0 = 82
keyKPDot = 83
key102nd = 86
keyF11 = 87
keyF12 = 88
keyKPEnter = 96
keyRightCtrl = 97
keyKPSlash = 98
keySysRq = 99
keyRightAlt = 100
keyHome = 102
keyUp = 103
keyPageUp = 104
keyLeft = 105
keyRight = 106
keyEnd = 107
keyDown = 108
keyPageDown = 109
keyInsert = 110
keyDelete = 111
keyMute = 113
keyVolumeDown = 114
keyVolumeUp = 115
keyLeftMeta = 125
keyRightMeta = 126
keyCompose = 127
)
// qemuToLinuxKey maps the PC AT Set 1 scancode QEMU sends to a Linux KEY_*
// code. The high byte 0xE0 marks "extended" scancodes (arrows, the right-
// side modifier keys, keypad enter/divide, browser keys, etc.).
//
// Keep this table dense so a reviewer sees the whole keyboard at a glance,
// and so adding a new key is a single line.
var qemuToLinuxKey = map[uint32]int{
// Single-byte (non-extended) scancodes.
0x01: keyEsc,
0x02: key1,
0x03: key2,
0x04: key3,
0x05: key4,
0x06: key5,
0x07: key6,
0x08: key7,
0x09: key8,
0x0A: key9,
0x0B: key0,
0x0C: keyMinus,
0x0D: keyEqual,
0x0E: keyBackspace,
0x0F: keyTab,
0x10: keyQ,
0x11: keyW,
0x12: keyE,
0x13: keyR,
0x14: keyT,
0x15: keyY,
0x16: keyU,
0x17: keyI,
0x18: keyO,
0x19: keyP,
0x1A: keyLeftBracket,
0x1B: keyRightBracket,
0x1C: keyEnter,
0x1D: keyLeftCtrl,
0x1E: keyA,
0x1F: keyS,
0x20: keyD,
0x21: keyF,
0x22: keyG,
0x23: keyH,
0x24: keyJ,
0x25: keyK,
0x26: keyL,
0x27: keySemicolon,
0x28: keyApostrophe,
0x29: keyGrave,
0x2A: keyLeftShift,
0x2B: keyBackslash,
0x2C: keyZ,
0x2D: keyX,
0x2E: keyC,
0x2F: keyV,
0x30: keyB,
0x31: keyN,
0x32: keyM,
0x33: keyComma,
0x34: keyDot,
0x35: keySlash,
0x36: keyRightShift,
0x37: keyKPAsterisk,
0x38: keyLeftAlt,
0x39: keySpace,
0x3A: keyCapsLock,
0x3B: keyF1,
0x3C: keyF2,
0x3D: keyF3,
0x3E: keyF4,
0x3F: keyF5,
0x40: keyF6,
0x41: keyF7,
0x42: keyF8,
0x43: keyF9,
0x44: keyF10,
0x45: keyNumLock,
0x46: keyScrollLock,
0x47: keyKP7,
0x48: keyKP8,
0x49: keyKP9,
0x4A: keyKPMinus,
0x4B: keyKP4,
0x4C: keyKP5,
0x4D: keyKP6,
0x4E: keyKPPlus,
0x4F: keyKP1,
0x50: keyKP2,
0x51: keyKP3,
0x52: keyKP0,
0x53: keyKPDot,
0x56: key102nd,
0x57: keyF11,
0x58: keyF12,
// Extended (0xE0-prefixed) scancodes.
0xE01C: keyKPEnter,
0xE01D: keyRightCtrl,
0xE020: keyMute,
0xE02E: keyVolumeDown,
0xE030: keyVolumeUp,
0xE035: keyKPSlash,
0xE037: keySysRq, // PrintScreen
0xE038: keyRightAlt,
0xE047: keyHome,
0xE048: keyUp,
0xE049: keyPageUp,
0xE04B: keyLeft,
0xE04D: keyRight,
0xE04F: keyEnd,
0xE050: keyDown,
0xE051: keyPageDown,
0xE052: keyInsert,
0xE053: keyDelete,
0xE05B: keyLeftMeta,
0xE05C: keyRightMeta,
0xE05D: keyCompose,
}
// qemuScancodeToLinuxKey is the lookup the uinput and X11 paths use.
// Returns 0 (which Linux treats as KEY_RESERVED) when the scancode has no
// mapping, signalling "fall back to the keysym path".
func qemuScancodeToLinuxKey(scancode uint32) int {
return qemuToLinuxKey[scancode]
}
// qemuScancodeIsExtended reports whether a QEMU scancode is in the
// 0xE0-prefixed extended range. Used by Windows SendInput to set the
// KEYEVENTF_EXTENDEDKEY flag.
func qemuScancodeIsExtended(scancode uint32) bool {
return scancode&0xFF00 == 0xE000
}
// qemuScancodeLowByte returns the byte SendInput's wScan field actually
// stores: the low byte of the scancode regardless of any extended prefix.
func qemuScancodeLowByte(scancode uint32) uint16 {
return uint16(scancode & 0xFF)
}