mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-03-30 07:06:38 +08:00
fix: 优化 WebRTC 建连流程、修复平台信息、修复虚拟键盘键值映射
- WebRTC:默认 mDNS 调整为 QueryOnly,Answer 阶段改为等待 ICE gathering complete(2.5s 超时),提升首次建连成功率与候选完整性 - WebRTC:前端建连流程增加阶段化状态与串行保护(connectInFlight/ready gate),优化配置变更后的重连时机与失败处理,减少竞态和无效重试 - Device:平台信息补充 `/proc/device-tree/model` 回退并统一展示为“处理器/平台” - HID:键盘输入链路统一为 HID usage + modifier bitmask,修复虚拟键盘/宏/粘贴键值映射错误
This commit is contained in:
@@ -1,136 +1,135 @@
|
||||
// Character to JavaScript keyCode mapping for text paste functionality
|
||||
// Maps printable ASCII characters to JavaScript keyCodes that the backend expects
|
||||
// The backend (keymap.rs) will convert these JS keyCodes to USB HID keycodes
|
||||
// Character to HID usage mapping for text paste functionality.
|
||||
// The table follows US QWERTY layout semantics.
|
||||
|
||||
import { keys } from '@/lib/keyboardMappings'
|
||||
|
||||
export interface CharKeyMapping {
|
||||
keyCode: number // JavaScript keyCode (same as KeyboardEvent.keyCode)
|
||||
hidCode: number // USB HID usage code
|
||||
shift: boolean // Whether Shift modifier is needed
|
||||
}
|
||||
|
||||
// US QWERTY keyboard layout mapping
|
||||
// Maps characters to their JavaScript keyCode and whether Shift is required
|
||||
const charToKeyMap: Record<string, CharKeyMapping> = {
|
||||
// Lowercase letters (no shift) - JS keyCodes 65-90
|
||||
a: { keyCode: 65, shift: false },
|
||||
b: { keyCode: 66, shift: false },
|
||||
c: { keyCode: 67, shift: false },
|
||||
d: { keyCode: 68, shift: false },
|
||||
e: { keyCode: 69, shift: false },
|
||||
f: { keyCode: 70, shift: false },
|
||||
g: { keyCode: 71, shift: false },
|
||||
h: { keyCode: 72, shift: false },
|
||||
i: { keyCode: 73, shift: false },
|
||||
j: { keyCode: 74, shift: false },
|
||||
k: { keyCode: 75, shift: false },
|
||||
l: { keyCode: 76, shift: false },
|
||||
m: { keyCode: 77, shift: false },
|
||||
n: { keyCode: 78, shift: false },
|
||||
o: { keyCode: 79, shift: false },
|
||||
p: { keyCode: 80, shift: false },
|
||||
q: { keyCode: 81, shift: false },
|
||||
r: { keyCode: 82, shift: false },
|
||||
s: { keyCode: 83, shift: false },
|
||||
t: { keyCode: 84, shift: false },
|
||||
u: { keyCode: 85, shift: false },
|
||||
v: { keyCode: 86, shift: false },
|
||||
w: { keyCode: 87, shift: false },
|
||||
x: { keyCode: 88, shift: false },
|
||||
y: { keyCode: 89, shift: false },
|
||||
z: { keyCode: 90, shift: false },
|
||||
// Lowercase letters
|
||||
a: { hidCode: keys.KeyA, shift: false },
|
||||
b: { hidCode: keys.KeyB, shift: false },
|
||||
c: { hidCode: keys.KeyC, shift: false },
|
||||
d: { hidCode: keys.KeyD, shift: false },
|
||||
e: { hidCode: keys.KeyE, shift: false },
|
||||
f: { hidCode: keys.KeyF, shift: false },
|
||||
g: { hidCode: keys.KeyG, shift: false },
|
||||
h: { hidCode: keys.KeyH, shift: false },
|
||||
i: { hidCode: keys.KeyI, shift: false },
|
||||
j: { hidCode: keys.KeyJ, shift: false },
|
||||
k: { hidCode: keys.KeyK, shift: false },
|
||||
l: { hidCode: keys.KeyL, shift: false },
|
||||
m: { hidCode: keys.KeyM, shift: false },
|
||||
n: { hidCode: keys.KeyN, shift: false },
|
||||
o: { hidCode: keys.KeyO, shift: false },
|
||||
p: { hidCode: keys.KeyP, shift: false },
|
||||
q: { hidCode: keys.KeyQ, shift: false },
|
||||
r: { hidCode: keys.KeyR, shift: false },
|
||||
s: { hidCode: keys.KeyS, shift: false },
|
||||
t: { hidCode: keys.KeyT, shift: false },
|
||||
u: { hidCode: keys.KeyU, shift: false },
|
||||
v: { hidCode: keys.KeyV, shift: false },
|
||||
w: { hidCode: keys.KeyW, shift: false },
|
||||
x: { hidCode: keys.KeyX, shift: false },
|
||||
y: { hidCode: keys.KeyY, shift: false },
|
||||
z: { hidCode: keys.KeyZ, shift: false },
|
||||
|
||||
// Uppercase letters (with shift) - same keyCodes, just with Shift
|
||||
A: { keyCode: 65, shift: true },
|
||||
B: { keyCode: 66, shift: true },
|
||||
C: { keyCode: 67, shift: true },
|
||||
D: { keyCode: 68, shift: true },
|
||||
E: { keyCode: 69, shift: true },
|
||||
F: { keyCode: 70, shift: true },
|
||||
G: { keyCode: 71, shift: true },
|
||||
H: { keyCode: 72, shift: true },
|
||||
I: { keyCode: 73, shift: true },
|
||||
J: { keyCode: 74, shift: true },
|
||||
K: { keyCode: 75, shift: true },
|
||||
L: { keyCode: 76, shift: true },
|
||||
M: { keyCode: 77, shift: true },
|
||||
N: { keyCode: 78, shift: true },
|
||||
O: { keyCode: 79, shift: true },
|
||||
P: { keyCode: 80, shift: true },
|
||||
Q: { keyCode: 81, shift: true },
|
||||
R: { keyCode: 82, shift: true },
|
||||
S: { keyCode: 83, shift: true },
|
||||
T: { keyCode: 84, shift: true },
|
||||
U: { keyCode: 85, shift: true },
|
||||
V: { keyCode: 86, shift: true },
|
||||
W: { keyCode: 87, shift: true },
|
||||
X: { keyCode: 88, shift: true },
|
||||
Y: { keyCode: 89, shift: true },
|
||||
Z: { keyCode: 90, shift: true },
|
||||
// Uppercase letters
|
||||
A: { hidCode: keys.KeyA, shift: true },
|
||||
B: { hidCode: keys.KeyB, shift: true },
|
||||
C: { hidCode: keys.KeyC, shift: true },
|
||||
D: { hidCode: keys.KeyD, shift: true },
|
||||
E: { hidCode: keys.KeyE, shift: true },
|
||||
F: { hidCode: keys.KeyF, shift: true },
|
||||
G: { hidCode: keys.KeyG, shift: true },
|
||||
H: { hidCode: keys.KeyH, shift: true },
|
||||
I: { hidCode: keys.KeyI, shift: true },
|
||||
J: { hidCode: keys.KeyJ, shift: true },
|
||||
K: { hidCode: keys.KeyK, shift: true },
|
||||
L: { hidCode: keys.KeyL, shift: true },
|
||||
M: { hidCode: keys.KeyM, shift: true },
|
||||
N: { hidCode: keys.KeyN, shift: true },
|
||||
O: { hidCode: keys.KeyO, shift: true },
|
||||
P: { hidCode: keys.KeyP, shift: true },
|
||||
Q: { hidCode: keys.KeyQ, shift: true },
|
||||
R: { hidCode: keys.KeyR, shift: true },
|
||||
S: { hidCode: keys.KeyS, shift: true },
|
||||
T: { hidCode: keys.KeyT, shift: true },
|
||||
U: { hidCode: keys.KeyU, shift: true },
|
||||
V: { hidCode: keys.KeyV, shift: true },
|
||||
W: { hidCode: keys.KeyW, shift: true },
|
||||
X: { hidCode: keys.KeyX, shift: true },
|
||||
Y: { hidCode: keys.KeyY, shift: true },
|
||||
Z: { hidCode: keys.KeyZ, shift: true },
|
||||
|
||||
// Numbers (no shift) - JS keyCodes 48-57
|
||||
'0': { keyCode: 48, shift: false },
|
||||
'1': { keyCode: 49, shift: false },
|
||||
'2': { keyCode: 50, shift: false },
|
||||
'3': { keyCode: 51, shift: false },
|
||||
'4': { keyCode: 52, shift: false },
|
||||
'5': { keyCode: 53, shift: false },
|
||||
'6': { keyCode: 54, shift: false },
|
||||
'7': { keyCode: 55, shift: false },
|
||||
'8': { keyCode: 56, shift: false },
|
||||
'9': { keyCode: 57, shift: false },
|
||||
// Number row
|
||||
'0': { hidCode: keys.Digit0, shift: false },
|
||||
'1': { hidCode: keys.Digit1, shift: false },
|
||||
'2': { hidCode: keys.Digit2, shift: false },
|
||||
'3': { hidCode: keys.Digit3, shift: false },
|
||||
'4': { hidCode: keys.Digit4, shift: false },
|
||||
'5': { hidCode: keys.Digit5, shift: false },
|
||||
'6': { hidCode: keys.Digit6, shift: false },
|
||||
'7': { hidCode: keys.Digit7, shift: false },
|
||||
'8': { hidCode: keys.Digit8, shift: false },
|
||||
'9': { hidCode: keys.Digit9, shift: false },
|
||||
|
||||
// Shifted number row symbols (US layout)
|
||||
')': { keyCode: 48, shift: true }, // Shift + 0
|
||||
'!': { keyCode: 49, shift: true }, // Shift + 1
|
||||
'@': { keyCode: 50, shift: true }, // Shift + 2
|
||||
'#': { keyCode: 51, shift: true }, // Shift + 3
|
||||
$: { keyCode: 52, shift: true }, // Shift + 4
|
||||
'%': { keyCode: 53, shift: true }, // Shift + 5
|
||||
'^': { keyCode: 54, shift: true }, // Shift + 6
|
||||
'&': { keyCode: 55, shift: true }, // Shift + 7
|
||||
'*': { keyCode: 56, shift: true }, // Shift + 8
|
||||
'(': { keyCode: 57, shift: true }, // Shift + 9
|
||||
// Shifted number row symbols
|
||||
')': { hidCode: keys.Digit0, shift: true },
|
||||
'!': { hidCode: keys.Digit1, shift: true },
|
||||
'@': { hidCode: keys.Digit2, shift: true },
|
||||
'#': { hidCode: keys.Digit3, shift: true },
|
||||
'$': { hidCode: keys.Digit4, shift: true },
|
||||
'%': { hidCode: keys.Digit5, shift: true },
|
||||
'^': { hidCode: keys.Digit6, shift: true },
|
||||
'&': { hidCode: keys.Digit7, shift: true },
|
||||
'*': { hidCode: keys.Digit8, shift: true },
|
||||
'(': { hidCode: keys.Digit9, shift: true },
|
||||
|
||||
// Punctuation and symbols (no shift) - US layout JS keyCodes
|
||||
'-': { keyCode: 189, shift: false }, // Minus
|
||||
'=': { keyCode: 187, shift: false }, // Equal
|
||||
'[': { keyCode: 219, shift: false }, // Left bracket
|
||||
']': { keyCode: 221, shift: false }, // Right bracket
|
||||
'\\': { keyCode: 220, shift: false }, // Backslash
|
||||
';': { keyCode: 186, shift: false }, // Semicolon
|
||||
"'": { keyCode: 222, shift: false }, // Apostrophe/Quote
|
||||
'`': { keyCode: 192, shift: false }, // Grave/Backtick
|
||||
',': { keyCode: 188, shift: false }, // Comma
|
||||
'.': { keyCode: 190, shift: false }, // Period
|
||||
'/': { keyCode: 191, shift: false }, // Slash
|
||||
// Punctuation and symbols
|
||||
'-': { hidCode: keys.Minus, shift: false },
|
||||
'=': { hidCode: keys.Equal, shift: false },
|
||||
'[': { hidCode: keys.BracketLeft, shift: false },
|
||||
']': { hidCode: keys.BracketRight, shift: false },
|
||||
'\\': { hidCode: keys.Backslash, shift: false },
|
||||
';': { hidCode: keys.Semicolon, shift: false },
|
||||
"'": { hidCode: keys.Quote, shift: false },
|
||||
'`': { hidCode: keys.Backquote, shift: false },
|
||||
',': { hidCode: keys.Comma, shift: false },
|
||||
'.': { hidCode: keys.Period, shift: false },
|
||||
'/': { hidCode: keys.Slash, shift: false },
|
||||
|
||||
// Shifted punctuation and symbols (US layout)
|
||||
_: { keyCode: 189, shift: true }, // Shift + Minus = Underscore
|
||||
'+': { keyCode: 187, shift: true }, // Shift + Equal = Plus
|
||||
'{': { keyCode: 219, shift: true }, // Shift + [ = {
|
||||
'}': { keyCode: 221, shift: true }, // Shift + ] = }
|
||||
'|': { keyCode: 220, shift: true }, // Shift + \ = |
|
||||
':': { keyCode: 186, shift: true }, // Shift + ; = :
|
||||
'"': { keyCode: 222, shift: true }, // Shift + ' = "
|
||||
'~': { keyCode: 192, shift: true }, // Shift + ` = ~
|
||||
'<': { keyCode: 188, shift: true }, // Shift + , = <
|
||||
'>': { keyCode: 190, shift: true }, // Shift + . = >
|
||||
'?': { keyCode: 191, shift: true }, // Shift + / = ?
|
||||
// Shifted punctuation and symbols
|
||||
_: { hidCode: keys.Minus, shift: true },
|
||||
'+': { hidCode: keys.Equal, shift: true },
|
||||
'{': { hidCode: keys.BracketLeft, shift: true },
|
||||
'}': { hidCode: keys.BracketRight, shift: true },
|
||||
'|': { hidCode: keys.Backslash, shift: true },
|
||||
':': { hidCode: keys.Semicolon, shift: true },
|
||||
'"': { hidCode: keys.Quote, shift: true },
|
||||
'~': { hidCode: keys.Backquote, shift: true },
|
||||
'<': { hidCode: keys.Comma, shift: true },
|
||||
'>': { hidCode: keys.Period, shift: true },
|
||||
'?': { hidCode: keys.Slash, shift: true },
|
||||
|
||||
// Whitespace and control characters
|
||||
' ': { keyCode: 32, shift: false }, // Space
|
||||
'\t': { keyCode: 9, shift: false }, // Tab
|
||||
'\n': { keyCode: 13, shift: false }, // Enter (LF)
|
||||
'\r': { keyCode: 13, shift: false }, // Enter (CR)
|
||||
// Whitespace and control
|
||||
' ': { hidCode: keys.Space, shift: false },
|
||||
'\t': { hidCode: keys.Tab, shift: false },
|
||||
'\n': { hidCode: keys.Enter, shift: false },
|
||||
'\r': { hidCode: keys.Enter, shift: false },
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JavaScript keyCode and modifier state for a character
|
||||
* Get HID usage code and modifier state for a character
|
||||
* @param char - Single character to convert
|
||||
* @returns CharKeyMapping or null if character is not mappable
|
||||
*/
|
||||
export function charToKey(char: string): CharKeyMapping | null {
|
||||
if (char.length !== 1) return null
|
||||
return charToKeyMap[char] || null
|
||||
return charToKeyMap[char] ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +137,7 @@ export function charToKey(char: string): CharKeyMapping | null {
|
||||
* @param char - Single character to check
|
||||
*/
|
||||
export function isTypableChar(char: string): boolean {
|
||||
return char.length === 1 && char in charToKeyMap
|
||||
return charToKey(char) !== null
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -191,6 +191,13 @@ export const hidKeyToModifierMask: Record<number, number> = {
|
||||
0xe7: 0x80, // MetaRight
|
||||
}
|
||||
|
||||
// Update modifier mask when a HID modifier key is pressed/released.
|
||||
export function updateModifierMaskForHidKey(mask: number, hidKey: number, press: boolean): number {
|
||||
const bit = hidKeyToModifierMask[hidKey] ?? 0
|
||||
if (bit === 0) return mask
|
||||
return press ? (mask | bit) : (mask & ~bit)
|
||||
}
|
||||
|
||||
// Keys that latch (toggle state) instead of being held
|
||||
export const latchingKeys = ['CapsLock', 'ScrollLock', 'NumLock'] as const
|
||||
|
||||
@@ -220,6 +227,23 @@ export function getModifierMask(keyName: string): number {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Normalize browser-specific KeyboardEvent.code variants.
|
||||
export function normalizeKeyboardCode(code: string, key: string): string {
|
||||
if (code === 'IntlBackslash' && (key === '`' || key === '~')) return 'Backquote'
|
||||
if (code === 'Backquote' && (key === '§' || key === '±')) return 'IntlBackslash'
|
||||
if (code === 'IntlYen') return 'IntlBackslash'
|
||||
if (code === 'OSLeft') return 'MetaLeft'
|
||||
if (code === 'OSRight') return 'MetaRight'
|
||||
if (code === '' && key === 'Shift') return 'ShiftRight'
|
||||
return code
|
||||
}
|
||||
|
||||
// Convert KeyboardEvent.code/key to USB HID usage code.
|
||||
export function keyboardEventToHidCode(code: string, key: string): number | undefined {
|
||||
const normalizedCode = normalizeKeyboardCode(code, key)
|
||||
return keys[normalizedCode as KeyName]
|
||||
}
|
||||
|
||||
// Decode modifier byte into individual states
|
||||
export function decodeModifiers(modifier: number) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user