fix(aten-kvm): send USB HID keycodes instead of X11 keysyms
The ATEN BMC expects USB HID keycodes in the key event packet, not X11 keysyms. Sending keysyms caused wrong keys (e.g. Q→Down Arrow, C→F10) because the BMC interpreted the keysym value as a HID keycode. Rewrote keymap to use HID keycodes from Java KeyMap.initHidKeyMap(), renamed keysym→keycode throughout, and added toolbar width compensation to window sizing so the framebuffer image gets its full width. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4e3d24064e
commit
4f6c577ea7
6 changed files with 225 additions and 220 deletions
|
|
@ -28,7 +28,7 @@ Rust workspace with four crates:
|
|||
|
||||
### Module responsibilities (aten-gui)
|
||||
|
||||
- `keymap.rs` — Physical key (winit KeyCode) to X11 keysym mapping (US QWERTY)
|
||||
- `keymap.rs` — Physical key (winit KeyCode) to USB HID keycode mapping
|
||||
- `ui/main.slint` — Window layout: left toolbar (Ctrl/Alt toggles, Ctrl+Alt+Del), framebuffer display, error dialog overlay
|
||||
|
||||
### Module responsibilities (aten-mount)
|
||||
|
|
|
|||
|
|
@ -756,7 +756,7 @@ never calls it. Only mouse events support AES-128-CBC encryption.
|
|||
│ padding: u8 = 0x00 │ Always zero (no encryption flag)
|
||||
│ down_flag: u8 │ 0 = up, 1 = down
|
||||
│ padding: 2 bytes (zero) │
|
||||
│ keysym: u32 │ X11 keysym (translated from VK code)
|
||||
│ keycode: u32 │ USB HID keycode
|
||||
│ padding: 9 bytes (zero) │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
|
@ -767,11 +767,11 @@ The `down_flag` values:
|
|||
- `0` = key released (up)
|
||||
- `1` = key pressed (down)
|
||||
|
||||
The `keysym` field contains **X11 keysyms** (not HID usage codes as previously
|
||||
documented). The native `processVK()` function translates Java VK codes through
|
||||
three lookup tables to produce X11 keysyms. The Java `KeyMap.VKtoHID()` provides
|
||||
the initial VK-to-HID mapping, but the native side further translates through
|
||||
its own tables. See the processVK section below for the complete mapping.
|
||||
The `keycode` field contains **USB HID keycodes**. The Java client's
|
||||
`KeyMap.VKtoHID()` maps Java VK codes to HID keycodes before passing them
|
||||
to the native layer. The native `processVK()` tables documented below show
|
||||
the intermediate X11 keysym representation used internally, but the BMC
|
||||
firmware ultimately interprets the value field as a HID keycode.
|
||||
|
||||
**Keyboard send flow** (`RFBKeyboard::Sendkey` at 0x0011a670):
|
||||
1. `StreamWriteStart()` - begin write batch
|
||||
|
|
@ -780,7 +780,7 @@ its own tables. See the processVK section below for the complete mapping.
|
|||
4. `StreamWrite8(0x00)` - padding byte (always 0, no encryption flag)
|
||||
5. `StreamWrite8(down_flag)` - key state
|
||||
6. `StreamWriteSkip(2)` - 2 zero bytes padding
|
||||
7. `StreamWrite32(keysym)` - the translated keysym value
|
||||
7. `StreamWrite32(keycode)` - the HID keycode value
|
||||
8. `StreamWriteSkip(9)` - 9 zero bytes padding
|
||||
9. `StreamWriteFlush()` - send
|
||||
|
||||
|
|
@ -1964,7 +1964,7 @@ Parameters: `(this, mode, keysize, input, num_blocks, key, output, iv)`
|
|||
|
||||
Keyboard events are always sent unencrypted. Despite having an `RFBKMCryto*` at
|
||||
RFBKeyboard offset 0x20, the `Sendkey()` function never references it. The
|
||||
message is simply: type 0x04, padding 0x00, down_flag, 2 zero bytes, keysym (u32),
|
||||
message is simply: type 0x04, padding 0x00, down_flag, 2 zero bytes, keycode (u32),
|
||||
9 zero bytes.
|
||||
|
||||
## Connection Flow (Java Side)
|
||||
|
|
@ -2131,7 +2131,7 @@ All multi-byte values are big-endian (network byte order).
|
|||
|
||||
```
|
||||
KeyEvent (18 bytes, always cleartext):
|
||||
04 00 [down] 00 00 [keysym:4] 00 00 00 00 00 00 00 00 00
|
||||
04 00 [down] 00 00 [keycode:4] 00 00 00 00 00 00 00 00 00
|
||||
|
||||
PointerEvent unencrypted (18 bytes):
|
||||
05 00 [buttons] [x:2] [y:2] 00 00 00 00 00 00 00 00 00 00 00
|
||||
|
|
@ -2692,16 +2692,16 @@ For most modern ATEN/ASPEED BMCs, expect encoding 0x58. See the
|
|||
Keyboard events are always sent in cleartext (never encrypted):
|
||||
|
||||
```
|
||||
send: 04 00 [down_flag:1] 00 00 [keysym:4] 00 00 00 00 00 00 00 00 00
|
||||
send: 04 00 [down_flag:1] 00 00 [keycode:4] 00 00 00 00 00 00 00 00 00
|
||||
```
|
||||
|
||||
Total: 18 bytes. The `keysym` is an X11 keysym value (e.g., 0xFF0D for Enter,
|
||||
0x0041 for 'A'). The `down_flag` is 0 for key-up and 1 for key-down.
|
||||
Total: 18 bytes. The `keycode` is a USB HID keycode (e.g., 0x28 for Enter,
|
||||
0x04 for 'A'). The `down_flag` is 0 for key-up and 1 for key-down.
|
||||
|
||||
Example -- pressing and releasing the 'a' key (uppercase keysym per Table 1):
|
||||
Example -- pressing and releasing the 'a' key (HID keycode 0x04):
|
||||
```
|
||||
send: 04 00 01 00 00 00 00 00 41 00 00 00 00 00 00 00 00 00 (key down, keysym=0x41)
|
||||
send: 04 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 00 00 (key up, keysym=0x41)
|
||||
send: 04 00 01 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 (key down, keycode=0x04)
|
||||
send: 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 (key up, keycode=0x04)
|
||||
```
|
||||
|
||||
### (d) Sending Mouse Input
|
||||
|
|
|
|||
|
|
@ -1,130 +1,131 @@
|
|||
use slint::winit_030::winit::keyboard::KeyCode;
|
||||
|
||||
pub fn physical_key_to_keysym(key: KeyCode) -> Option<u32> {
|
||||
// The ATEN BMC firmware maps keysyms to USB HID keycodes using tables
|
||||
// derived from the original Java client's processVK() function. Letters
|
||||
// must be uppercase (0x41-0x5A) to match those lookup tables.
|
||||
let keysym = match key {
|
||||
KeyCode::KeyA => 0x0041,
|
||||
KeyCode::KeyB => 0x0042,
|
||||
KeyCode::KeyC => 0x0043,
|
||||
KeyCode::KeyD => 0x0044,
|
||||
KeyCode::KeyE => 0x0045,
|
||||
KeyCode::KeyF => 0x0046,
|
||||
KeyCode::KeyG => 0x0047,
|
||||
KeyCode::KeyH => 0x0048,
|
||||
KeyCode::KeyI => 0x0049,
|
||||
KeyCode::KeyJ => 0x004A,
|
||||
KeyCode::KeyK => 0x004B,
|
||||
KeyCode::KeyL => 0x004C,
|
||||
KeyCode::KeyM => 0x004D,
|
||||
KeyCode::KeyN => 0x004E,
|
||||
KeyCode::KeyO => 0x004F,
|
||||
KeyCode::KeyP => 0x0050,
|
||||
KeyCode::KeyQ => 0x0051,
|
||||
KeyCode::KeyR => 0x0052,
|
||||
KeyCode::KeyS => 0x0053,
|
||||
KeyCode::KeyT => 0x0054,
|
||||
KeyCode::KeyU => 0x0055,
|
||||
KeyCode::KeyV => 0x0056,
|
||||
KeyCode::KeyW => 0x0057,
|
||||
KeyCode::KeyX => 0x0058,
|
||||
KeyCode::KeyY => 0x0059,
|
||||
KeyCode::KeyZ => 0x005A,
|
||||
KeyCode::Digit1 => 0x0031,
|
||||
KeyCode::Digit2 => 0x0032,
|
||||
KeyCode::Digit3 => 0x0033,
|
||||
KeyCode::Digit4 => 0x0034,
|
||||
KeyCode::Digit5 => 0x0035,
|
||||
KeyCode::Digit6 => 0x0036,
|
||||
KeyCode::Digit7 => 0x0037,
|
||||
KeyCode::Digit8 => 0x0038,
|
||||
KeyCode::Digit9 => 0x0039,
|
||||
KeyCode::Digit0 => 0x0030,
|
||||
KeyCode::F1 => 0xFFBE,
|
||||
KeyCode::F2 => 0xFFBF,
|
||||
KeyCode::F3 => 0xFFC0,
|
||||
KeyCode::F4 => 0xFFC1,
|
||||
KeyCode::F5 => 0xFFC2,
|
||||
KeyCode::F6 => 0xFFC3,
|
||||
KeyCode::F7 => 0xFFC4,
|
||||
KeyCode::F8 => 0xFFC5,
|
||||
KeyCode::F9 => 0xFFC6,
|
||||
KeyCode::F10 => 0xFFC7,
|
||||
KeyCode::F11 => 0xFFC8,
|
||||
KeyCode::F12 => 0xFFC9,
|
||||
KeyCode::F13 => 0xFFCA,
|
||||
KeyCode::F14 => 0xFFCB,
|
||||
KeyCode::F15 => 0xFFCC,
|
||||
KeyCode::F16 => 0xFFCD,
|
||||
KeyCode::F17 => 0xFFCE,
|
||||
KeyCode::F18 => 0xFFCF,
|
||||
KeyCode::F19 => 0xFFD0,
|
||||
KeyCode::F20 => 0xFFD1,
|
||||
KeyCode::F21 => 0xFFD2,
|
||||
KeyCode::F22 => 0xFFD3,
|
||||
KeyCode::F23 => 0xFFD4,
|
||||
KeyCode::F24 => 0xFFD5,
|
||||
KeyCode::ShiftLeft => 0xFFE1,
|
||||
KeyCode::ShiftRight => 0xFFE2,
|
||||
KeyCode::ControlLeft => 0xFFE3,
|
||||
KeyCode::ControlRight => 0xFFE4,
|
||||
KeyCode::AltLeft => 0xFFE9,
|
||||
KeyCode::AltRight => 0xFFEA,
|
||||
KeyCode::SuperLeft => 0xFFEB,
|
||||
KeyCode::SuperRight => 0xFFEC,
|
||||
KeyCode::CapsLock => 0xFFE5,
|
||||
KeyCode::NumLock => 0xFF7F,
|
||||
KeyCode::ScrollLock => 0xFF14,
|
||||
KeyCode::Home => 0xFF50,
|
||||
KeyCode::ArrowLeft => 0xFF51,
|
||||
KeyCode::ArrowUp => 0xFF52,
|
||||
KeyCode::ArrowRight => 0xFF53,
|
||||
KeyCode::ArrowDown => 0xFF54,
|
||||
KeyCode::PageUp => 0xFF55,
|
||||
KeyCode::PageDown => 0xFF56,
|
||||
KeyCode::End => 0xFF57,
|
||||
KeyCode::Backspace => 0xFF08,
|
||||
KeyCode::Tab => 0xFF09,
|
||||
KeyCode::Enter => 0xFF0D,
|
||||
KeyCode::Escape => 0xFF1B,
|
||||
KeyCode::Space => 0x0020,
|
||||
KeyCode::Insert => 0xFF63,
|
||||
KeyCode::Delete => 0xFFFF,
|
||||
KeyCode::PrintScreen => 0xFF61,
|
||||
KeyCode::Pause => 0xFF13,
|
||||
KeyCode::ContextMenu => 0xFF67,
|
||||
KeyCode::Numpad0 => 0xFFB0,
|
||||
KeyCode::Numpad1 => 0xFFB1,
|
||||
KeyCode::Numpad2 => 0xFFB2,
|
||||
KeyCode::Numpad3 => 0xFFB3,
|
||||
KeyCode::Numpad4 => 0xFFB4,
|
||||
KeyCode::Numpad5 => 0xFFB5,
|
||||
KeyCode::Numpad6 => 0xFFB6,
|
||||
KeyCode::Numpad7 => 0xFFB7,
|
||||
KeyCode::Numpad8 => 0xFFB8,
|
||||
KeyCode::Numpad9 => 0xFFB9,
|
||||
KeyCode::NumpadMultiply => 0xFFAA,
|
||||
KeyCode::NumpadAdd => 0xFFAB,
|
||||
KeyCode::NumpadSubtract => 0xFFAD,
|
||||
KeyCode::NumpadDecimal => 0xFFAE,
|
||||
KeyCode::NumpadDivide => 0xFFAF,
|
||||
KeyCode::NumpadEnter => 0xFF8D,
|
||||
KeyCode::Minus => 0x005F,
|
||||
KeyCode::Equal => 0x002B,
|
||||
KeyCode::BracketLeft => 0x005b,
|
||||
KeyCode::BracketRight => 0x005d,
|
||||
KeyCode::Backslash => 0x005c,
|
||||
KeyCode::Semicolon => 0x003A,
|
||||
KeyCode::Quote => 0x0022,
|
||||
KeyCode::Backquote => 0x0060,
|
||||
KeyCode::Comma => 0x002c,
|
||||
KeyCode::Period => 0x002e,
|
||||
KeyCode::Slash => 0x002f,
|
||||
KeyCode::IntlBackslash => 0x003c,
|
||||
/// Maps a physical key to a USB HID keycode.
|
||||
///
|
||||
/// The ATEN BMC expects USB HID keycodes in the key event packet.
|
||||
/// This table is derived from the original Java client's `KeyMap.initHidKeyMap()`.
|
||||
pub fn physical_key_to_hid(key: KeyCode) -> Option<u32> {
|
||||
let hid = match key {
|
||||
KeyCode::KeyA => 0x04,
|
||||
KeyCode::KeyB => 0x05,
|
||||
KeyCode::KeyC => 0x06,
|
||||
KeyCode::KeyD => 0x07,
|
||||
KeyCode::KeyE => 0x08,
|
||||
KeyCode::KeyF => 0x09,
|
||||
KeyCode::KeyG => 0x0A,
|
||||
KeyCode::KeyH => 0x0B,
|
||||
KeyCode::KeyI => 0x0C,
|
||||
KeyCode::KeyJ => 0x0D,
|
||||
KeyCode::KeyK => 0x0E,
|
||||
KeyCode::KeyL => 0x0F,
|
||||
KeyCode::KeyM => 0x10,
|
||||
KeyCode::KeyN => 0x11,
|
||||
KeyCode::KeyO => 0x12,
|
||||
KeyCode::KeyP => 0x13,
|
||||
KeyCode::KeyQ => 0x14,
|
||||
KeyCode::KeyR => 0x15,
|
||||
KeyCode::KeyS => 0x16,
|
||||
KeyCode::KeyT => 0x17,
|
||||
KeyCode::KeyU => 0x18,
|
||||
KeyCode::KeyV => 0x19,
|
||||
KeyCode::KeyW => 0x1A,
|
||||
KeyCode::KeyX => 0x1B,
|
||||
KeyCode::KeyY => 0x1C,
|
||||
KeyCode::KeyZ => 0x1D,
|
||||
KeyCode::Digit1 => 0x1E,
|
||||
KeyCode::Digit2 => 0x1F,
|
||||
KeyCode::Digit3 => 0x20,
|
||||
KeyCode::Digit4 => 0x21,
|
||||
KeyCode::Digit5 => 0x22,
|
||||
KeyCode::Digit6 => 0x23,
|
||||
KeyCode::Digit7 => 0x24,
|
||||
KeyCode::Digit8 => 0x25,
|
||||
KeyCode::Digit9 => 0x26,
|
||||
KeyCode::Digit0 => 0x27,
|
||||
KeyCode::Enter => 0x28,
|
||||
KeyCode::Escape => 0x29,
|
||||
KeyCode::Backspace => 0x2A,
|
||||
KeyCode::Tab => 0x2B,
|
||||
KeyCode::Space => 0x2C,
|
||||
KeyCode::Minus => 0x2D,
|
||||
KeyCode::Equal => 0x2E,
|
||||
KeyCode::BracketLeft => 0x2F,
|
||||
KeyCode::BracketRight => 0x30,
|
||||
KeyCode::Backslash => 0x31,
|
||||
KeyCode::Semicolon => 0x33,
|
||||
KeyCode::Quote => 0x34,
|
||||
KeyCode::Backquote => 0x35,
|
||||
KeyCode::Comma => 0x36,
|
||||
KeyCode::Period => 0x37,
|
||||
KeyCode::Slash => 0x38,
|
||||
KeyCode::CapsLock => 0x39,
|
||||
KeyCode::F1 => 0x3A,
|
||||
KeyCode::F2 => 0x3B,
|
||||
KeyCode::F3 => 0x3C,
|
||||
KeyCode::F4 => 0x3D,
|
||||
KeyCode::F5 => 0x3E,
|
||||
KeyCode::F6 => 0x3F,
|
||||
KeyCode::F7 => 0x40,
|
||||
KeyCode::F8 => 0x41,
|
||||
KeyCode::F9 => 0x42,
|
||||
KeyCode::F10 => 0x43,
|
||||
KeyCode::F11 => 0x44,
|
||||
KeyCode::F12 => 0x45,
|
||||
KeyCode::PrintScreen => 0x46,
|
||||
KeyCode::ScrollLock => 0x47,
|
||||
KeyCode::Pause => 0x48,
|
||||
KeyCode::Insert => 0x49,
|
||||
KeyCode::Home => 0x4A,
|
||||
KeyCode::PageUp => 0x4B,
|
||||
KeyCode::Delete => 0x4C,
|
||||
KeyCode::End => 0x4D,
|
||||
KeyCode::PageDown => 0x4E,
|
||||
KeyCode::ArrowRight => 0x4F,
|
||||
KeyCode::ArrowLeft => 0x50,
|
||||
KeyCode::ArrowDown => 0x51,
|
||||
KeyCode::ArrowUp => 0x52,
|
||||
KeyCode::NumLock => 0x53,
|
||||
KeyCode::NumpadDivide => 0x54,
|
||||
KeyCode::NumpadMultiply => 0x55,
|
||||
KeyCode::NumpadSubtract => 0x56,
|
||||
KeyCode::NumpadAdd => 0x57,
|
||||
KeyCode::NumpadEnter => 0x58,
|
||||
KeyCode::Numpad1 => 0x59,
|
||||
KeyCode::Numpad2 => 0x5A,
|
||||
KeyCode::Numpad3 => 0x5B,
|
||||
KeyCode::Numpad4 => 0x5C,
|
||||
KeyCode::Numpad5 => 0x5D,
|
||||
KeyCode::Numpad6 => 0x5E,
|
||||
KeyCode::Numpad7 => 0x5F,
|
||||
KeyCode::Numpad8 => 0x60,
|
||||
KeyCode::Numpad9 => 0x61,
|
||||
KeyCode::Numpad0 => 0x62,
|
||||
KeyCode::NumpadDecimal => 0x63,
|
||||
KeyCode::IntlBackslash => 0x64,
|
||||
KeyCode::ContextMenu => 0x65,
|
||||
KeyCode::F13 => 0x68,
|
||||
KeyCode::F14 => 0x69,
|
||||
KeyCode::F15 => 0x6A,
|
||||
KeyCode::F16 => 0x6B,
|
||||
KeyCode::F17 => 0x6C,
|
||||
KeyCode::F18 => 0x6D,
|
||||
KeyCode::F19 => 0x6E,
|
||||
KeyCode::F20 => 0x6F,
|
||||
KeyCode::F21 => 0x70,
|
||||
KeyCode::F22 => 0x71,
|
||||
KeyCode::F23 => 0x72,
|
||||
KeyCode::F24 => 0x73,
|
||||
KeyCode::ControlLeft => 0xE0,
|
||||
KeyCode::ShiftLeft => 0xE1,
|
||||
KeyCode::AltLeft => 0xE2,
|
||||
KeyCode::SuperLeft => 0xE3,
|
||||
KeyCode::ControlRight => 0xE4,
|
||||
KeyCode::ShiftRight => 0xE5,
|
||||
KeyCode::AltRight => 0xE6,
|
||||
KeyCode::SuperRight => 0xE7,
|
||||
_ => return None,
|
||||
};
|
||||
Some(keysym)
|
||||
Some(hid)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -133,94 +134,96 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn letters() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::KeyA), Some(0x41));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::KeyZ), Some(0x5A));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::KeyA), Some(0x04));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::KeyZ), Some(0x1D));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn digits() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Digit0), Some(0x30));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Digit9), Some(0x39));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Digit1), Some(0x1E));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Digit0), Some(0x27));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f_keys() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::F1), Some(0xFFBE));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::F12), Some(0xFFC9));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::F24), Some(0xFFD5));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::F1), Some(0x3A));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::F12), Some(0x45));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::F13), Some(0x68));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::F24), Some(0x73));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modifiers_left_right() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::ShiftLeft), Some(0xFFE1));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::ShiftRight), Some(0xFFE2));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::ControlLeft), Some(0xFFE3));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::ControlRight), Some(0xFFE4));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::AltLeft), Some(0xFFE9));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::AltRight), Some(0xFFEA));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::SuperLeft), Some(0xFFEB));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::SuperRight), Some(0xFFEC));
|
||||
fn modifiers() {
|
||||
assert_eq!(physical_key_to_hid(KeyCode::ControlLeft), Some(0xE0));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::ShiftLeft), Some(0xE1));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::AltLeft), Some(0xE2));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::SuperLeft), Some(0xE3));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::ControlRight), Some(0xE4));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::ShiftRight), Some(0xE5));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::AltRight), Some(0xE6));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::SuperRight), Some(0xE7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn navigation() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::ArrowLeft), Some(0xFF51));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::ArrowUp), Some(0xFF52));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::ArrowRight), Some(0xFF53));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::ArrowDown), Some(0xFF54));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Home), Some(0xFF50));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::End), Some(0xFF57));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::PageUp), Some(0xFF55));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::PageDown), Some(0xFF56));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::ArrowRight), Some(0x4F));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::ArrowLeft), Some(0x50));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::ArrowDown), Some(0x51));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::ArrowUp), Some(0x52));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Home), Some(0x4A));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::End), Some(0x4D));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::PageUp), Some(0x4B));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::PageDown), Some(0x4E));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editing_keys() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Backspace), Some(0xFF08));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Tab), Some(0xFF09));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Enter), Some(0xFF0D));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Escape), Some(0xFF1B));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Space), Some(0x0020));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Insert), Some(0xFF63));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Delete), Some(0xFFFF));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Enter), Some(0x28));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Escape), Some(0x29));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Backspace), Some(0x2A));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Tab), Some(0x2B));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Space), Some(0x2C));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Insert), Some(0x49));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Delete), Some(0x4C));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numpad() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Numpad0), Some(0xFFB0));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Numpad9), Some(0xFFB9));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::NumpadAdd), Some(0xFFAB));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::NumpadSubtract), Some(0xFFAD));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::NumpadMultiply), Some(0xFFAA));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::NumpadDivide), Some(0xFFAF));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::NumpadDecimal), Some(0xFFAE));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::NumpadEnter), Some(0xFF8D));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Numpad0), Some(0x62));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Numpad1), Some(0x59));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Numpad9), Some(0x61));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::NumpadAdd), Some(0x57));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::NumpadSubtract), Some(0x56));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::NumpadMultiply), Some(0x55));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::NumpadDivide), Some(0x54));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::NumpadDecimal), Some(0x63));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::NumpadEnter), Some(0x58));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbols_us_qwerty() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Minus), Some(0x5F));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Equal), Some(0x2B));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::BracketLeft), Some(0x5b));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::BracketRight), Some(0x5d));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Backslash), Some(0x5c));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Semicolon), Some(0x3A));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Quote), Some(0x22));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Backquote), Some(0x60));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Comma), Some(0x2c));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Period), Some(0x2e));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Slash), Some(0x2f));
|
||||
fn symbols() {
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Minus), Some(0x2D));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Equal), Some(0x2E));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::BracketLeft), Some(0x2F));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::BracketRight), Some(0x30));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Backslash), Some(0x31));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Semicolon), Some(0x33));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Quote), Some(0x34));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Backquote), Some(0x35));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Comma), Some(0x36));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Period), Some(0x37));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Slash), Some(0x38));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_keys() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::CapsLock), Some(0xFFE5));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::NumLock), Some(0xFF7F));
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::ScrollLock), Some(0xFF14));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::CapsLock), Some(0x39));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::NumLock), Some(0x53));
|
||||
assert_eq!(physical_key_to_hid(KeyCode::ScrollLock), Some(0x47));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_key_returns_none() {
|
||||
assert_eq!(physical_key_to_keysym(KeyCode::Fn), None);
|
||||
assert_eq!(physical_key_to_hid(KeyCode::Fn), None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ use slint::winit_030::{winit, EventResult, WinitWindowAccessor};
|
|||
|
||||
use aten_kvm::{InputEvent, KvmConfig, KvmEvent, KvmSession};
|
||||
|
||||
const TOOLBAR_WIDTH: f32 = 70.0;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "KVM console viewer for ATEN BMC")]
|
||||
struct Args {
|
||||
|
|
@ -61,7 +63,7 @@ fn main() {
|
|||
let input_tx_ctrl = input_tx.clone();
|
||||
app.on_ctrl_toggled(move |checked| {
|
||||
let _ = input_tx_ctrl.send(InputEvent::Key {
|
||||
keysym: 0xFFE3, // Control_L
|
||||
keycode: 0xE0, // ControlLeft
|
||||
down: checked,
|
||||
});
|
||||
});
|
||||
|
|
@ -69,33 +71,33 @@ fn main() {
|
|||
let input_tx_alt = input_tx.clone();
|
||||
app.on_alt_toggled(move |checked| {
|
||||
let _ = input_tx_alt.send(InputEvent::Key {
|
||||
keysym: 0xFFE9, // Alt_L
|
||||
keycode: 0xE2, // AltLeft
|
||||
down: checked,
|
||||
});
|
||||
});
|
||||
|
||||
let input_tx_cad = input_tx.clone();
|
||||
app.on_ctrl_alt_del_pressed(move || {
|
||||
for &(keysym, down) in &[
|
||||
(0xFFE3u32, true), // Ctrl down
|
||||
(0xFFE9, true), // Alt down
|
||||
(0xFFFF, true), // Delete down
|
||||
(0xFFFF, false), // Delete up
|
||||
(0xFFE9, false), // Alt up
|
||||
(0xFFE3, false), // Ctrl up
|
||||
for &(keycode, down) in &[
|
||||
(0xE0u32, true), // Ctrl down
|
||||
(0xE2, true), // Alt down
|
||||
(0x4C, true), // Delete down
|
||||
(0x4C, false), // Delete up
|
||||
(0xE2, false), // Alt up
|
||||
(0xE0, false), // Ctrl up
|
||||
] {
|
||||
let _ = input_tx_cad.send(InputEvent::Key { keysym, down });
|
||||
let _ = input_tx_cad.send(InputEvent::Key { keycode, down });
|
||||
}
|
||||
});
|
||||
|
||||
app.window().on_winit_window_event(move |_slint_window, event| {
|
||||
if let winit::event::WindowEvent::KeyboardInput { event, .. } = event {
|
||||
if let winit::keyboard::PhysicalKey::Code(keycode) = event.physical_key {
|
||||
if let Some(keysym) = keymap::physical_key_to_keysym(keycode) {
|
||||
if let winit::keyboard::PhysicalKey::Code(physical) = event.physical_key {
|
||||
if let Some(keycode) = keymap::physical_key_to_hid(physical) {
|
||||
let down = event.state == winit::event::ElementState::Pressed;
|
||||
let _ = input_tx.send(InputEvent::Key { keysym, down });
|
||||
let _ = input_tx.send(InputEvent::Key { keycode, down });
|
||||
} else {
|
||||
warn!("unmapped physical key: {keycode:?}");
|
||||
warn!("unmapped physical key: {physical:?}");
|
||||
}
|
||||
}
|
||||
return EventResult::PreventDefault;
|
||||
|
|
@ -116,7 +118,7 @@ fn main() {
|
|||
slint::invoke_from_event_loop(move || {
|
||||
if let Some(app) = weak.upgrade() {
|
||||
app.window().set_size(slint::LogicalSize::new(
|
||||
width as f32,
|
||||
width as f32 + TOOLBAR_WIDTH,
|
||||
height as f32,
|
||||
));
|
||||
}
|
||||
|
|
@ -129,7 +131,7 @@ fn main() {
|
|||
slint::invoke_from_event_loop(move || {
|
||||
if let Some(app) = weak.upgrade() {
|
||||
app.window().set_size(slint::LogicalSize::new(
|
||||
width as f32,
|
||||
width as f32 + TOOLBAR_WIDTH,
|
||||
height as f32,
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::sync::mpsc;
|
|||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum InputEvent {
|
||||
Key { keysym: u32, down: bool },
|
||||
Key { keycode: u32, down: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -106,11 +106,11 @@ pub fn send_keepalive_ack(stream: &mut impl Write) -> Result<(), KvmError> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_key_event(keysym: u32, down: bool) -> [u8; 18] {
|
||||
pub fn build_key_event(keycode: u32, down: bool) -> [u8; 18] {
|
||||
let mut pkt = [0u8; 18];
|
||||
pkt[0] = 0x04;
|
||||
pkt[2] = if down { 1 } else { 0 };
|
||||
pkt[5..9].copy_from_slice(&keysym.to_be_bytes());
|
||||
pkt[5..9].copy_from_slice(&keycode.to_be_bytes());
|
||||
pkt
|
||||
}
|
||||
|
||||
|
|
@ -235,9 +235,9 @@ pub fn run_session(
|
|||
debug!("writer thread started");
|
||||
while let Ok(event) = input_rx.recv() {
|
||||
match event {
|
||||
InputEvent::Key { keysym, down } => {
|
||||
debug!("sending key event: keysym=0x{keysym:04x} down={down}");
|
||||
if write_stream.write_all(&build_key_event(keysym, down)).is_err() {
|
||||
InputEvent::Key { keycode, down } => {
|
||||
debug!("sending key event: keycode=0x{keycode:04x} down={down}");
|
||||
if write_stream.write_all(&build_key_event(keycode, down)).is_err() {
|
||||
debug!("writer thread: write failed, exiting");
|
||||
break;
|
||||
}
|
||||
|
|
@ -577,21 +577,21 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_build_key_event_down() {
|
||||
let pkt = build_key_event(0xFF0D, true); // Enter key down
|
||||
let pkt = build_key_event(0x28, true); // Enter key down (HID 0x28)
|
||||
assert_eq!(pkt.len(), 18);
|
||||
assert_eq!(pkt[0], 0x04); // type
|
||||
assert_eq!(pkt[1], 0x00); // no encryption
|
||||
assert_eq!(pkt[2], 0x01); // down
|
||||
assert_eq!(pkt[3], 0x00);
|
||||
assert_eq!(pkt[4], 0x00);
|
||||
assert_eq!(u32::from_be_bytes([pkt[5], pkt[6], pkt[7], pkt[8]]), 0xFF0D);
|
||||
assert_eq!(u32::from_be_bytes([pkt[5], pkt[6], pkt[7], pkt[8]]), 0x28);
|
||||
assert!(pkt[9..18].iter().all(|&b| b == 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_key_event_up() {
|
||||
let pkt = build_key_event(0x61, false); // 'a' key up
|
||||
let pkt = build_key_event(0x04, false); // 'A' key up (HID 0x04)
|
||||
assert_eq!(pkt[2], 0x00); // up
|
||||
assert_eq!(u32::from_be_bytes([pkt[5], pkt[6], pkt[7], pkt[8]]), 0x61);
|
||||
assert_eq!(u32::from_be_bytes([pkt[5], pkt[6], pkt[7], pkt[8]]), 0x04);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue