package rdp // RDP mouse event flags — these match the MS-RDPBCGR specification. const ( MouseFlagMove uint32 = 0x0800 // Mouse moved (no button change) MouseFlagButton1 uint32 = 0x1000 // Left button MouseFlagButton2 uint32 = 0x2000 // Right button MouseFlagButton3 uint32 = 0x4000 // Middle button MouseFlagDown uint32 = 0x8000 // Button pressed (absence = released) // Extended mouse flags for wheel events MouseFlagWheel uint32 = 0x0200 // Vertical wheel rotation MouseFlagWheelNeg uint32 = 0x0100 // Negative wheel direction (scroll down) MouseFlagHWheel uint32 = 0x0400 // Horizontal wheel rotation ) // ScancodeMap maps JavaScript KeyboardEvent.code strings to RDP hardware // scancodes (Set 1 / XT scan codes). This covers the standard US 104-key // layout. Extended keys (those with a 0xE0 prefix on the wire) have the // high byte set to 0xE0. // // Reference: USB HID to PS/2 scancode mapping + MS-RDPBCGR 2.2.8.1.1.3.1.1.1 var ScancodeMap = map[string]uint32{ // ── Row 0: Escape + Function keys ────────────────────────────── "Escape": 0x0001, "F1": 0x003B, "F2": 0x003C, "F3": 0x003D, "F4": 0x003E, "F5": 0x003F, "F6": 0x0040, "F7": 0x0041, "F8": 0x0042, "F9": 0x0043, "F10": 0x0044, "F11": 0x0057, "F12": 0x0058, // ── Row 1: Number row ────────────────────────────────────────── "Backquote": 0x0029, // ` ~ "Digit1": 0x0002, "Digit2": 0x0003, "Digit3": 0x0004, "Digit4": 0x0005, "Digit5": 0x0006, "Digit6": 0x0007, "Digit7": 0x0008, "Digit8": 0x0009, "Digit9": 0x000A, "Digit0": 0x000B, "Minus": 0x000C, // - _ "Equal": 0x000D, // = + "Backspace": 0x000E, // ── Row 2: QWERTY row ───────────────────────────────────────── "Tab": 0x000F, "KeyQ": 0x0010, "KeyW": 0x0011, "KeyE": 0x0012, "KeyR": 0x0013, "KeyT": 0x0014, "KeyY": 0x0015, "KeyU": 0x0016, "KeyI": 0x0017, "KeyO": 0x0018, "KeyP": 0x0019, "BracketLeft": 0x001A, // [ { "BracketRight":0x001B, // ] } "Backslash": 0x002B, // \ | // ── Row 3: Home row ─────────────────────────────────────────── "CapsLock": 0x003A, "KeyA": 0x001E, "KeyS": 0x001F, "KeyD": 0x0020, "KeyF": 0x0021, "KeyG": 0x0022, "KeyH": 0x0023, "KeyJ": 0x0024, "KeyK": 0x0025, "KeyL": 0x0026, "Semicolon": 0x0027, // ; : "Quote": 0x0028, // ' " "Enter": 0x001C, // ── Row 4: Bottom row ───────────────────────────────────────── "ShiftLeft": 0x002A, "KeyZ": 0x002C, "KeyX": 0x002D, "KeyC": 0x002E, "KeyV": 0x002F, "KeyB": 0x0030, "KeyN": 0x0031, "KeyM": 0x0032, "Comma": 0x0033, // , < "Period": 0x0034, // . > "Slash": 0x0035, // / ? "ShiftRight": 0x0036, // ── Row 5: Bottom modifiers + space ─────────────────────────── "ControlLeft": 0x001D, "MetaLeft": 0xE05B, // Left Windows / Super "AltLeft": 0x0038, "Space": 0x0039, "AltRight": 0xE038, // Right Alt (extended) "MetaRight": 0xE05C, // Right Windows / Super "ContextMenu": 0xE05D, // Application / Menu key "ControlRight": 0xE01D, // Right Ctrl (extended) // ── Navigation cluster ──────────────────────────────────────── "PrintScreen": 0xE037, "ScrollLock": 0x0046, "Pause": 0x0045, // Note: Pause has special handling on wire "Insert": 0xE052, "Home": 0xE047, "PageUp": 0xE049, "Delete": 0xE053, "End": 0xE04F, "PageDown": 0xE051, // ── Arrow keys ──────────────────────────────────────────────── "ArrowUp": 0xE048, "ArrowLeft": 0xE04B, "ArrowDown": 0xE050, "ArrowRight": 0xE04D, // ── Numpad ──────────────────────────────────────────────────── "NumLock": 0x0045, "NumpadDivide": 0xE035, "NumpadMultiply":0x0037, "NumpadSubtract":0x004A, "Numpad7": 0x0047, "Numpad8": 0x0048, "Numpad9": 0x0049, "NumpadAdd": 0x004E, "Numpad4": 0x004B, "Numpad5": 0x004C, "Numpad6": 0x004D, "Numpad1": 0x004F, "Numpad2": 0x0050, "Numpad3": 0x0051, "NumpadEnter": 0xE01C, "Numpad0": 0x0052, "NumpadDecimal": 0x0053, // ── Multimedia / browser keys (common on 104+ key layouts) ─── "BrowserBack": 0xE06A, "BrowserForward": 0xE069, "BrowserRefresh": 0xE067, "BrowserStop": 0xE068, "BrowserSearch": 0xE065, "BrowserFavorites":0xE066, "BrowserHome": 0xE032, "VolumeMute": 0xE020, "VolumeDown": 0xE02E, "VolumeUp": 0xE030, "MediaTrackNext": 0xE019, "MediaTrackPrevious":0xE010, "MediaStop": 0xE024, "MediaPlayPause": 0xE022, "LaunchMail": 0xE06C, "LaunchApp1": 0xE06B, "LaunchApp2": 0xE021, // ── International keys ──────────────────────────────────────── "IntlBackslash": 0x0056, // key between left Shift and Z on ISO keyboards "IntlYen": 0x007D, // Yen key on Japanese keyboards "IntlRo": 0x0073, // Ro key on Japanese keyboards } // JSKeyToScancode translates a JavaScript KeyboardEvent.code string to an // RDP hardware scancode. Returns the scancode and true if a mapping exists, // or 0 and false for unmapped keys. func JSKeyToScancode(jsCode string) (uint32, bool) { sc, ok := ScancodeMap[jsCode] return sc, ok } // IsExtendedKey returns true if the scancode has the 0xE0 extended prefix. // Extended keys require a two-byte sequence on the RDP wire. func IsExtendedKey(scancode uint32) bool { return (scancode & 0xFF00) == 0xE000 } // ScancodeValue returns the low byte of the scancode (the actual scan code // value without the extended prefix). func ScancodeValue(scancode uint32) uint8 { return uint8(scancode & 0xFF) }