mirror of
https://github.com/luckfox-eng29/kvm.git
synced 2026-05-28 09:01:22 +02:00
refactor(hid): improve keyboard layout compatibility in HID handling functions
Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
This commit is contained in:
47
internal/hidrpc/hidrpc.go
Normal file
47
internal/hidrpc/hidrpc.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package hidrpc
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Handler interface {
|
||||
HandleKeyboardReport(modifier byte, keys []byte) error
|
||||
HandleKeypressReport(key byte, press bool) error
|
||||
HandleKeypressKeepAlive() error
|
||||
HandleKeyboardMacroReport(data []byte) error
|
||||
HandleCancelKeyboardMacro() error
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
handler Handler
|
||||
}
|
||||
|
||||
func NewServer(handler Handler) *Server {
|
||||
return &Server{handler: handler}
|
||||
}
|
||||
|
||||
func (s *Server) HandleMessage(data []byte) error {
|
||||
msg, err := UnmarshalMessage(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
case MessageTypeKeyboardReport:
|
||||
if len(msg.Data) < 7 {
|
||||
return fmt.Errorf("invalid keyboard report length: %d", len(msg.Data))
|
||||
}
|
||||
return s.handler.HandleKeyboardReport(msg.Data[0], msg.Data[1:7])
|
||||
case MessageTypeKeypressReport:
|
||||
if len(msg.Data) < 2 {
|
||||
return fmt.Errorf("invalid keypress report length: %d", len(msg.Data))
|
||||
}
|
||||
return s.handler.HandleKeypressReport(msg.Data[0], msg.Data[1] != 0)
|
||||
case MessageTypeKeypressKeepAlive:
|
||||
return s.handler.HandleKeypressKeepAlive()
|
||||
case MessageTypeKeyboardMacroReport:
|
||||
return s.handler.HandleKeyboardMacroReport(msg.Data)
|
||||
case MessageTypeCancelKeyboardMacro:
|
||||
return s.handler.HandleCancelKeyboardMacro()
|
||||
default:
|
||||
return fmt.Errorf("unknown message type: 0x%02x", msg.Type)
|
||||
}
|
||||
}
|
||||
66
internal/hidrpc/message.go
Normal file
66
internal/hidrpc/message.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package hidrpc
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
MessageTypeHandshake = 0x01
|
||||
MessageTypeKeyboardReport = 0x02
|
||||
MessageTypePointerReport = 0x03
|
||||
MessageTypeWheelReport = 0x04
|
||||
MessageTypeKeypressReport = 0x05
|
||||
MessageTypeMouseReport = 0x06
|
||||
MessageTypeKeyboardMacroReport = 0x07
|
||||
MessageTypeCancelKeyboardMacro = 0x08
|
||||
MessageTypeKeypressKeepAlive = 0x09
|
||||
MessageTypeKeyboardLedState = 0x32
|
||||
MessageTypeKeysDownState = 0x33
|
||||
MessageTypeKeyboardMacroState = 0x34
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Type byte
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func MarshalKeyboardReport(modifier byte, keys []byte) []byte {
|
||||
data := make([]byte, 8)
|
||||
data[0] = MessageTypeKeyboardReport
|
||||
data[1] = modifier
|
||||
copy(data[2:], keys)
|
||||
return data
|
||||
}
|
||||
|
||||
func MarshalKeypressReport(key byte, press bool) []byte {
|
||||
data := make([]byte, 3)
|
||||
data[0] = MessageTypeKeypressReport
|
||||
data[1] = key
|
||||
if press {
|
||||
data[2] = 1
|
||||
} else {
|
||||
data[2] = 0
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func MarshalKeypressKeepAlive() []byte {
|
||||
return []byte{MessageTypeKeypressKeepAlive}
|
||||
}
|
||||
|
||||
func MarshalKeyboardLedState(state byte) []byte {
|
||||
return []byte{MessageTypeKeyboardLedState, state}
|
||||
}
|
||||
|
||||
func MarshalKeysDownState(modifier byte, keys []byte) []byte {
|
||||
data := make([]byte, 8)
|
||||
data[0] = MessageTypeKeysDownState
|
||||
data[1] = modifier
|
||||
copy(data[2:], keys)
|
||||
return data
|
||||
}
|
||||
|
||||
func UnmarshalMessage(data []byte) (Message, error) {
|
||||
if len(data) < 1 {
|
||||
return Message{}, fmt.Errorf("empty message")
|
||||
}
|
||||
return Message{Type: data[0], Data: data[1:]}, nil
|
||||
}
|
||||
@@ -226,15 +226,153 @@ func (u *UsbGadget) keyboardWriteHidFile(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error {
|
||||
type autoReleaseTimer struct {
|
||||
timer *time.Timer
|
||||
key byte
|
||||
active bool
|
||||
}
|
||||
|
||||
type KeysDownState struct {
|
||||
Modifier byte
|
||||
Keys [6]byte
|
||||
}
|
||||
|
||||
func (u *UsbGadget) scheduleAutoRelease(key byte) {
|
||||
// Cancel existing timer for this key
|
||||
for i := range u.autoReleaseTimers {
|
||||
if u.autoReleaseTimers[i].key == key && u.autoReleaseTimers[i].active {
|
||||
u.autoReleaseTimers[i].timer.Stop()
|
||||
u.autoReleaseTimers[i].active = false
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule new timer
|
||||
timer := time.AfterFunc(100*time.Millisecond, func() {
|
||||
u.autoReleaseKey(key)
|
||||
})
|
||||
|
||||
u.autoReleaseTimers = append(u.autoReleaseTimers, autoReleaseTimer{
|
||||
timer: timer,
|
||||
key: key,
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (u *UsbGadget) autoReleaseKey(key byte) {
|
||||
u.keyboardLock.Lock()
|
||||
defer u.keyboardLock.Unlock()
|
||||
|
||||
// Remove key from buffer
|
||||
found := false
|
||||
for i := 0; i < len(u.keysDownState.Keys); i++ {
|
||||
if u.keysDownState.Keys[i] == key {
|
||||
found = true
|
||||
}
|
||||
if found && i < len(u.keysDownState.Keys)-1 {
|
||||
u.keysDownState.Keys[i] = u.keysDownState.Keys[i+1]
|
||||
}
|
||||
}
|
||||
if found {
|
||||
u.keysDownState.Keys[len(u.keysDownState.Keys)-1] = 0
|
||||
u.keyboardWriteHidFileLocked(u.keysDownState.Modifier, u.keysDownState.Keys[:])
|
||||
}
|
||||
|
||||
// Mark timer as inactive
|
||||
for i := range u.autoReleaseTimers {
|
||||
if u.autoReleaseTimers[i].key == key && u.autoReleaseTimers[i].active {
|
||||
u.autoReleaseTimers[i].active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UsbGadget) cancelAutoRelease(key byte) {
|
||||
for i := range u.autoReleaseTimers {
|
||||
if u.autoReleaseTimers[i].key == key && u.autoReleaseTimers[i].active {
|
||||
u.autoReleaseTimers[i].timer.Stop()
|
||||
u.autoReleaseTimers[i].active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UsbGadget) resetAllAutoReleaseTimers() {
|
||||
for i := range u.autoReleaseTimers {
|
||||
if u.autoReleaseTimers[i].active {
|
||||
u.autoReleaseTimers[i].timer.Stop()
|
||||
u.autoReleaseTimers[i].active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UsbGadget) KeypressReport(key byte, press bool) error {
|
||||
u.keyboardLock.Lock()
|
||||
defer u.keyboardLock.Unlock()
|
||||
|
||||
if press {
|
||||
// Check if key already in buffer
|
||||
for _, k := range u.keysDownState.Keys {
|
||||
if k == key {
|
||||
return nil // Already pressed
|
||||
}
|
||||
}
|
||||
|
||||
// Find empty slot
|
||||
emptySlot := -1
|
||||
for i, k := range u.keysDownState.Keys {
|
||||
if k == 0 {
|
||||
emptySlot = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if emptySlot == -1 {
|
||||
// Buffer full - ErrorRollOver
|
||||
u.keysDownState.Keys = [6]byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01}
|
||||
} else {
|
||||
u.keysDownState.Keys[emptySlot] = key
|
||||
}
|
||||
|
||||
u.scheduleAutoRelease(key)
|
||||
} else {
|
||||
// Remove key from buffer
|
||||
found := false
|
||||
for i := 0; i < len(u.keysDownState.Keys); i++ {
|
||||
if u.keysDownState.Keys[i] == key {
|
||||
found = true
|
||||
}
|
||||
if found && i < len(u.keysDownState.Keys)-1 {
|
||||
u.keysDownState.Keys[i] = u.keysDownState.Keys[i+1]
|
||||
}
|
||||
}
|
||||
if found {
|
||||
u.keysDownState.Keys[len(u.keysDownState.Keys)-1] = 0
|
||||
}
|
||||
|
||||
u.cancelAutoRelease(key)
|
||||
}
|
||||
|
||||
return u.keyboardWriteHidFileLocked(u.keysDownState.Modifier, u.keysDownState.Keys[:])
|
||||
}
|
||||
|
||||
func (u *UsbGadget) KeypressKeepAlive() error {
|
||||
u.keyboardLock.Lock()
|
||||
defer u.keyboardLock.Unlock()
|
||||
|
||||
// Reset auto-release timers for all currently held keys
|
||||
for _, key := range u.keysDownState.Keys {
|
||||
if key != 0 {
|
||||
u.scheduleAutoRelease(key)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UsbGadget) keyboardWriteHidFileLocked(modifier byte, keys []byte) error {
|
||||
if len(keys) > 6 {
|
||||
keys = keys[:6]
|
||||
}
|
||||
if len(keys) < 6 {
|
||||
keys = append(keys, make([]uint8, 6-len(keys))...)
|
||||
keys = append(keys, make([]byte, 6-len(keys))...)
|
||||
}
|
||||
|
||||
err := u.keyboardWriteHidFile([]byte{modifier, 0, keys[0], keys[1], keys[2], keys[3], keys[4], keys[5]})
|
||||
@@ -245,3 +383,13 @@ func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error {
|
||||
u.resetUserInputTime()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UsbGadget) KeyboardReport(modifier uint8, keys []uint8) error {
|
||||
u.keyboardLock.Lock()
|
||||
defer u.keyboardLock.Unlock()
|
||||
|
||||
u.keysDownState.Modifier = modifier
|
||||
copy(u.keysDownState.Keys[:], keys)
|
||||
|
||||
return u.keyboardWriteHidFileLocked(modifier, keys)
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ type UsbGadget struct {
|
||||
onKeyboardStateChange *func(state KeyboardState)
|
||||
onHidDeviceMissing *func(device string, err error)
|
||||
|
||||
keysDownState KeysDownState
|
||||
autoReleaseTimers []autoReleaseTimer
|
||||
|
||||
log *zerolog.Logger
|
||||
|
||||
logSuppressionCounter map[string]int
|
||||
|
||||
Reference in New Issue
Block a user