mirror of
https://github.com/luckfox-eng29/kvm.git
synced 2026-01-18 03:28:19 +01:00
* feat: release keyPress automatically * send keepalive when pressing the key * remove logging * clean up logging * chore: use unreliable channel to send keepalive events * chore: use ordered unreliable channel for pointer events * chore: adjust auto release key interval * chore: update logging for kbdAutoReleaseLock * chore: update comment for KEEPALIVE_INTERVAL * fix: should cancelAutorelease when pressed is true * fix: handshake won't happen if webrtc reconnects * chore: add trace log for writeWithTimeout * chore: add timeout for KeypressReport * chore: use the proper key to send release command * refactor: simplify HID RPC keyboard input handling and improve key state management - Updated `handleHidRPCKeyboardInput` to return errors directly instead of keys down state. - Refactored `rpcKeyboardReport` and `rpcKeypressReport` to return errors instead of states. - Introduced a queue for managing key down state updates in the `Session` struct to prevent input handling stalls. - Adjusted the `UpdateKeysDown` method to handle state changes more efficiently. - Removed unnecessary logging and commented-out code for clarity. * refactor: enhance keyboard auto-release functionality and key state management * fix: correct Windows default auto-repeat delay comment from 1ms to 1s * refactor: send keypress as early as possible * refactor: replace console.warn with console.info for HID RPC channel events * refactor: remove unused NewKeypressKeepAliveMessage function from HID RPC * fix: handle error in key release process and log warnings * fix: log warning on keypress report failure * fix: update auto-release keyboard interval to 225 * refactor: enhance keep-alive handling and jitter compensation in HID RPC - Implemented staleness guard to ignore outdated keep-alive packets. - Added jitter compensation logic to adjust timer extensions based on packet arrival times. - Introduced new methods for managing keep-alive state and reset functionality in the Session struct. - Updated auto-release delay mechanism to use dynamic durations based on keep-alive timing. - Adjusted keep-alive interval in the UI to improve responsiveness. * gofmt * clean up code * chore: use dynamic duration for scheduleAutoRelease * Use harcoded timer reset value for now * fix: prevent nil pointer dereference when stopping timers in Close method * refactor: remove nil check for kbdAutoReleaseTimers in DelayAutoReleaseWithDuration * refactor: optimize dependencies in useHidRpc hooks * refactor: streamline keep-alive timer management in useKeyboard hook * refactor: clarify comments in useKeyboard hook for resetKeyboardState function * refactor: reduce keysDownStateQueueSize * refactor: close and reset keysDownStateQueue in newSession function * chore: resolve conflicts * resolve conflicts --------- Co-authored-by: Adam Shiervani <adam.shiervani@gmail.com>
208 lines
5.0 KiB
Go
208 lines
5.0 KiB
Go
package hidrpc
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
)
|
|
|
|
// Message ..
|
|
type Message struct {
|
|
t MessageType
|
|
d []byte
|
|
}
|
|
|
|
// Marshal marshals the message to a byte array.
|
|
func (m *Message) Marshal() ([]byte, error) {
|
|
return Marshal(m)
|
|
}
|
|
|
|
func (m *Message) Type() MessageType {
|
|
return m.t
|
|
}
|
|
|
|
func (m *Message) String() string {
|
|
switch m.t {
|
|
case TypeHandshake:
|
|
return "Handshake"
|
|
case TypeKeypressReport:
|
|
if len(m.d) < 2 {
|
|
return fmt.Sprintf("KeypressReport{Malformed: %v}", m.d)
|
|
}
|
|
return fmt.Sprintf("KeypressReport{Key: %d, Press: %v}", m.d[0], m.d[1] == uint8(1))
|
|
case TypeKeyboardReport:
|
|
if len(m.d) < 2 {
|
|
return fmt.Sprintf("KeyboardReport{Malformed: %v}", m.d)
|
|
}
|
|
return fmt.Sprintf("KeyboardReport{Modifier: %d, Keys: %v}", m.d[0], m.d[1:])
|
|
case TypePointerReport:
|
|
if len(m.d) < 9 {
|
|
return fmt.Sprintf("PointerReport{Malformed: %v}", m.d)
|
|
}
|
|
return fmt.Sprintf("PointerReport{X: %d, Y: %d, Button: %d}", m.d[0:4], m.d[4:8], m.d[8])
|
|
case TypeMouseReport:
|
|
if len(m.d) < 3 {
|
|
return fmt.Sprintf("MouseReport{Malformed: %v}", m.d)
|
|
}
|
|
return fmt.Sprintf("MouseReport{DX: %d, DY: %d, Button: %d}", m.d[0], m.d[1], m.d[2])
|
|
case TypeKeypressKeepAliveReport:
|
|
return "KeypressKeepAliveReport"
|
|
case TypeKeyboardMacroReport:
|
|
if len(m.d) < 5 {
|
|
return fmt.Sprintf("KeyboardMacroReport{Malformed: %v}", m.d)
|
|
}
|
|
return fmt.Sprintf("KeyboardMacroReport{IsPaste: %v, Length: %d}", m.d[0] == uint8(1), binary.BigEndian.Uint32(m.d[1:5]))
|
|
default:
|
|
return fmt.Sprintf("Unknown{Type: %d, Data: %v}", m.t, m.d)
|
|
}
|
|
}
|
|
|
|
// KeypressReport ..
|
|
type KeypressReport struct {
|
|
Key byte
|
|
Press bool
|
|
}
|
|
|
|
// KeypressReport returns the keypress report from the message.
|
|
func (m *Message) KeypressReport() (KeypressReport, error) {
|
|
if m.t != TypeKeypressReport {
|
|
return KeypressReport{}, fmt.Errorf("invalid message type: %d", m.t)
|
|
}
|
|
|
|
return KeypressReport{
|
|
Key: m.d[0],
|
|
Press: m.d[1] == uint8(1),
|
|
}, nil
|
|
}
|
|
|
|
// KeyboardReport ..
|
|
type KeyboardReport struct {
|
|
Modifier byte
|
|
Keys []byte
|
|
}
|
|
|
|
// KeyboardReport returns the keyboard report from the message.
|
|
func (m *Message) KeyboardReport() (KeyboardReport, error) {
|
|
if m.t != TypeKeyboardReport {
|
|
return KeyboardReport{}, fmt.Errorf("invalid message type: %d", m.t)
|
|
}
|
|
|
|
return KeyboardReport{
|
|
Modifier: m.d[0],
|
|
Keys: m.d[1:],
|
|
}, nil
|
|
}
|
|
|
|
// Macro ..
|
|
type KeyboardMacroStep struct {
|
|
Modifier byte // 1 byte
|
|
Keys []byte // 6 bytes: hidKeyBufferSize
|
|
Delay uint16 // 2 bytes
|
|
}
|
|
type KeyboardMacroReport struct {
|
|
IsPaste bool
|
|
StepCount uint32
|
|
Steps []KeyboardMacroStep
|
|
}
|
|
|
|
// HidKeyBufferSize is the size of the keys buffer in the keyboard report.
|
|
const HidKeyBufferSize = 6
|
|
|
|
// KeyboardMacroReport returns the keyboard macro report from the message.
|
|
func (m *Message) KeyboardMacroReport() (KeyboardMacroReport, error) {
|
|
if m.t != TypeKeyboardMacroReport {
|
|
return KeyboardMacroReport{}, fmt.Errorf("invalid message type: %d", m.t)
|
|
}
|
|
|
|
isPaste := m.d[0] == uint8(1)
|
|
stepCount := binary.BigEndian.Uint32(m.d[1:5])
|
|
|
|
// check total length
|
|
expectedLength := int(stepCount)*9 + 5
|
|
if len(m.d) != expectedLength {
|
|
return KeyboardMacroReport{}, fmt.Errorf("invalid length: %d, expected: %d", len(m.d), expectedLength)
|
|
}
|
|
|
|
steps := make([]KeyboardMacroStep, 0, int(stepCount))
|
|
offset := 5
|
|
for i := 0; i < int(stepCount); i++ {
|
|
steps = append(steps, KeyboardMacroStep{
|
|
Modifier: m.d[offset],
|
|
Keys: m.d[offset+1 : offset+7],
|
|
Delay: binary.BigEndian.Uint16(m.d[offset+7 : offset+9]),
|
|
})
|
|
|
|
offset += 1 + HidKeyBufferSize + 2
|
|
}
|
|
|
|
return KeyboardMacroReport{
|
|
IsPaste: isPaste,
|
|
Steps: steps,
|
|
StepCount: stepCount,
|
|
}, nil
|
|
}
|
|
|
|
// PointerReport ..
|
|
type PointerReport struct {
|
|
X int
|
|
Y int
|
|
Button uint8
|
|
}
|
|
|
|
func toInt(b []byte) int {
|
|
return int(b[0])<<24 + int(b[1])<<16 + int(b[2])<<8 + int(b[3])<<0
|
|
}
|
|
|
|
// PointerReport returns the point report from the message.
|
|
func (m *Message) PointerReport() (PointerReport, error) {
|
|
if m.t != TypePointerReport {
|
|
return PointerReport{}, fmt.Errorf("invalid message type: %d", m.t)
|
|
}
|
|
|
|
if len(m.d) != 9 {
|
|
return PointerReport{}, fmt.Errorf("invalid message length: %d", len(m.d))
|
|
}
|
|
|
|
return PointerReport{
|
|
X: toInt(m.d[0:4]),
|
|
Y: toInt(m.d[4:8]),
|
|
Button: uint8(m.d[8]),
|
|
}, nil
|
|
}
|
|
|
|
// MouseReport ..
|
|
type MouseReport struct {
|
|
DX int8
|
|
DY int8
|
|
Button uint8
|
|
}
|
|
|
|
// MouseReport returns the mouse report from the message.
|
|
func (m *Message) MouseReport() (MouseReport, error) {
|
|
if m.t != TypeMouseReport {
|
|
return MouseReport{}, fmt.Errorf("invalid message type: %d", m.t)
|
|
}
|
|
|
|
return MouseReport{
|
|
DX: int8(m.d[0]),
|
|
DY: int8(m.d[1]),
|
|
Button: uint8(m.d[2]),
|
|
}, nil
|
|
}
|
|
|
|
type KeyboardMacroState struct {
|
|
State bool
|
|
IsPaste bool
|
|
}
|
|
|
|
// KeyboardMacroState returns the keyboard macro state report from the message.
|
|
func (m *Message) KeyboardMacroState() (KeyboardMacroState, error) {
|
|
if m.t != TypeKeyboardMacroState {
|
|
return KeyboardMacroState{}, fmt.Errorf("invalid message type: %d", m.t)
|
|
}
|
|
|
|
return KeyboardMacroState{
|
|
State: m.d[0] == uint8(1),
|
|
IsPaste: m.d[1] == uint8(1),
|
|
}, nil
|
|
}
|