Skip to content

Commit 51d80be

Browse files
committed
Run paste/macros in background on their own queue.
Also returns a token for cancellation. Fixed error in length check for macro key state. Removed redundant clear operation.
1 parent 0321a74 commit 51d80be

File tree

11 files changed

+260
-73
lines changed

11 files changed

+260
-73
lines changed

cloud.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ func handleSessionRequest(
477477
cloudLogger.Trace().Interface("session", session).Msg("new session accepted")
478478

479479
// Cancel any ongoing keyboard macro when session changes
480-
cancelKeyboardMacro()
480+
cancelAllRunningKeyboardMacros()
481481

482482
currentSession = session
483483
_ = wsjson.Write(context.Background(), c, gin.H{"type": "answer", "data": sd})

hidrpc.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,34 +26,61 @@ func handleHidRPCMessage(message hidrpc.Message, session *Session) {
2626
return
2727
}
2828
session.hidRPCAvailable = true
29+
2930
case hidrpc.TypeKeypressReport, hidrpc.TypeKeyboardReport:
3031
rpcErr = handleHidRPCKeyboardInput(message)
32+
3133
case hidrpc.TypeKeyboardMacroReport:
3234
keyboardMacroReport, err := message.KeyboardMacroReport()
3335
if err != nil {
3436
logger.Warn().Err(err).Msg("failed to get keyboard macro report")
3537
return
3638
}
37-
rpcErr = rpcExecuteKeyboardMacro(keyboardMacroReport.Steps)
39+
token := rpcExecuteKeyboardMacro(keyboardMacroReport.IsPaste, keyboardMacroReport.Steps)
40+
logger.Debug().Str("token", token.String()).Msg("started keyboard macro")
41+
message, err := hidrpc.NewKeyboardMacroTokenMessage(token).Marshal()
42+
43+
if err != nil {
44+
logger.Warn().Err(err).Msg("failed to marshal running macro token message")
45+
return
46+
}
47+
if err := session.HidChannel.Send(message); err != nil {
48+
logger.Warn().Err(err).Msg("failed to send running macro token message")
49+
return
50+
}
51+
3852
case hidrpc.TypeCancelKeyboardMacroReport:
3953
rpcCancelKeyboardMacro()
4054
return
55+
56+
case hidrpc.TypeCancelKeyboardMacroByTokenReport:
57+
token, err := message.KeyboardMacroToken()
58+
if err != nil {
59+
logger.Warn().Err(err).Msg("failed to get keyboard macro token")
60+
return
61+
}
62+
rpcCancelKeyboardMacroByToken(token)
63+
return
64+
4165
case hidrpc.TypeKeypressKeepAliveReport:
4266
rpcErr = handleHidRPCKeypressKeepAlive(session)
67+
4368
case hidrpc.TypePointerReport:
4469
pointerReport, err := message.PointerReport()
4570
if err != nil {
4671
logger.Warn().Err(err).Msg("failed to get pointer report")
4772
return
4873
}
4974
rpcErr = rpcAbsMouseReport(pointerReport.X, pointerReport.Y, pointerReport.Button)
75+
5076
case hidrpc.TypeMouseReport:
5177
mouseReport, err := message.MouseReport()
5278
if err != nil {
5379
logger.Warn().Err(err).Msg("failed to get mouse report")
5480
return
5581
}
5682
rpcErr = rpcRelMouseReport(mouseReport.DX, mouseReport.DY, mouseReport.Button)
83+
5784
default:
5885
logger.Warn().Uint8("type", uint8(message.Type())).Msg("unknown HID RPC message type")
5986
}
@@ -98,7 +125,7 @@ func onHidMessage(msg hidQueueMessage, session *Session) {
98125
r <- nil
99126
}()
100127
select {
101-
case <-time.After(30 * time.Second):
128+
case <-time.After(1 * time.Second):
102129
scopedLogger.Warn().Msg("HID RPC message timed out")
103130
case <-r:
104131
scopedLogger.Debug().Dur("duration", time.Since(t)).Msg("HID RPC message handled")

internal/hidrpc/hidrpc.go

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,54 @@ package hidrpc
33
import (
44
"fmt"
55

6+
"github.com/google/uuid"
67
"github.com/jetkvm/kvm/internal/usbgadget"
78
)
89

910
// MessageType is the type of the HID RPC message
1011
type MessageType byte
1112

1213
const (
13-
TypeHandshake MessageType = 0x01
14-
TypeKeyboardReport MessageType = 0x02
15-
TypePointerReport MessageType = 0x03
16-
TypeWheelReport MessageType = 0x04
17-
TypeKeypressReport MessageType = 0x05
18-
TypeKeypressKeepAliveReport MessageType = 0x09
19-
TypeMouseReport MessageType = 0x06
20-
TypeKeyboardMacroReport MessageType = 0x07
21-
TypeCancelKeyboardMacroReport MessageType = 0x08
22-
TypeKeyboardLedState MessageType = 0x32
23-
TypeKeydownState MessageType = 0x33
24-
TypeKeyboardMacroState MessageType = 0x34
14+
TypeHandshake MessageType = 0x01
15+
TypeKeyboardReport MessageType = 0x02
16+
TypePointerReport MessageType = 0x03
17+
TypeWheelReport MessageType = 0x04
18+
TypeKeypressReport MessageType = 0x05
19+
TypeKeypressKeepAliveReport MessageType = 0x09
20+
TypeMouseReport MessageType = 0x06
21+
TypeKeyboardMacroReport MessageType = 0x07
22+
TypeCancelKeyboardMacroReport MessageType = 0x08
23+
TypeKeyboardLedState MessageType = 0x32
24+
TypeKeydownState MessageType = 0x33
25+
TypeKeyboardMacroState MessageType = 0x34
26+
TypeCancelKeyboardMacroByTokenReport MessageType = 0x35
2527
)
2628

29+
type QueueIndex int
30+
2731
const (
28-
Version byte = 0x01 // Version of the HID RPC protocol
32+
Version byte = 0x01 // Version of the HID RPC protocol
33+
HandshakeQueue int = 0 // Queue index for handshake messages
34+
KeyboardQueue int = 1 // Queue index for keyboard and macro messages
35+
MouseQueue int = 2 // Queue index for mouse messages
36+
MacroQueue int = 3 // Queue index for macro cancel messages
37+
OtherQueue int = 4 // Queue index for other messages
2938
)
3039

3140
// GetQueueIndex returns the index of the queue to which the message should be enqueued.
3241
func GetQueueIndex(messageType MessageType) int {
3342
switch messageType {
3443
case TypeHandshake:
35-
return 0
36-
case TypeKeyboardReport, TypeKeypressReport, TypeKeyboardMacroReport, TypeKeyboardLedState, TypeKeydownState, TypeKeyboardMacroState:
37-
return 1
44+
return HandshakeQueue
45+
case TypeKeyboardReport, TypeKeypressReport, TypeKeyboardLedState, TypeKeydownState, TypeKeyboardMacroState:
46+
return KeyboardQueue
3847
case TypePointerReport, TypeMouseReport, TypeWheelReport:
39-
return 2
40-
// we don't want to block the queue for this message
41-
case TypeCancelKeyboardMacroReport:
42-
return 3
48+
return MouseQueue
49+
// we don't want to block the queue for these messages
50+
case TypeKeyboardMacroReport, TypeCancelKeyboardMacroReport, TypeCancelKeyboardMacroByTokenReport:
51+
return MacroQueue
4352
default:
44-
return 3
53+
return OtherQueue
4554
}
4655
}
4756

@@ -121,3 +130,13 @@ func NewKeyboardMacroStateMessage(state bool, isPaste bool) *Message {
121130
d: data,
122131
}
123132
}
133+
134+
// NewKeyboardMacroTokenMessage creates a new keyboard macro token message.
135+
func NewKeyboardMacroTokenMessage(token uuid.UUID) *Message {
136+
data, _ := token.MarshalBinary()
137+
138+
return &Message{
139+
t: TypeKeyboardMacroState,
140+
d: data,
141+
}
142+
}

internal/hidrpc/message.go

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package hidrpc
33
import (
44
"encoding/binary"
55
"fmt"
6+
7+
"github.com/google/uuid"
68
)
79

810
// Message ..
@@ -23,6 +25,9 @@ func (m *Message) Type() MessageType {
2325
func (m *Message) String() string {
2426
switch m.t {
2527
case TypeHandshake:
28+
if len(m.d) != 0 {
29+
return fmt.Sprintf("Handshake{Malformed: %v}", m.d)
30+
}
2631
return "Handshake"
2732
case TypeKeypressReport:
2833
if len(m.d) < 2 {
@@ -45,12 +50,45 @@ func (m *Message) String() string {
4550
}
4651
return fmt.Sprintf("MouseReport{DX: %d, DY: %d, Button: %d}", m.d[0], m.d[1], m.d[2])
4752
case TypeKeypressKeepAliveReport:
53+
if len(m.d) != 0 {
54+
return fmt.Sprintf("KeypressKeepAliveReport{Malformed: %v}", m.d)
55+
}
4856
return "KeypressKeepAliveReport"
57+
case TypeWheelReport:
58+
if len(m.d) < 3 {
59+
return fmt.Sprintf("WheelReport{Malformed: %v}", m.d)
60+
}
61+
return fmt.Sprintf("WheelReport{Vertical: %d, Horizontal: %d}", int8(m.d[0]), int8(m.d[1]))
4962
case TypeKeyboardMacroReport:
5063
if len(m.d) < 5 {
5164
return fmt.Sprintf("KeyboardMacroReport{Malformed: %v}", m.d)
5265
}
5366
return fmt.Sprintf("KeyboardMacroReport{IsPaste: %v, Length: %d}", m.d[0] == uint8(1), binary.BigEndian.Uint32(m.d[1:5]))
67+
case TypeCancelKeyboardMacroReport:
68+
if len(m.d) != 0 {
69+
return fmt.Sprintf("CancelKeyboardMacroReport{Malformed: %v}", m.d)
70+
}
71+
return "CancelKeyboardMacroReport"
72+
case TypeCancelKeyboardMacroByTokenReport:
73+
if len(m.d) != 16 {
74+
return fmt.Sprintf("CancelKeyboardMacroByTokenReport{Malformed: %v}", m.d)
75+
}
76+
return fmt.Sprintf("CancelKeyboardMacroByTokenReport{Token: %s}", uuid.Must(uuid.FromBytes(m.d)).String())
77+
case TypeKeyboardLedState:
78+
if len(m.d) < 1 {
79+
return fmt.Sprintf("KeyboardLedState{Malformed: %v}", m.d)
80+
}
81+
return fmt.Sprintf("KeyboardLedState{State: %d}", m.d[0])
82+
case TypeKeydownState:
83+
if len(m.d) < 1 {
84+
return fmt.Sprintf("KeydownState{Malformed: %v}", m.d)
85+
}
86+
return fmt.Sprintf("KeydownState{State: %d}", m.d[0])
87+
case TypeKeyboardMacroState:
88+
if len(m.d) < 2 {
89+
return fmt.Sprintf("KeyboardMacroState{Malformed: %v}", m.d)
90+
}
91+
return fmt.Sprintf("KeyboardMacroState{State: %v, IsPaste: %v}", m.d[0] == uint8(1), m.d[1] == uint8(1))
5492
default:
5593
return fmt.Sprintf("Unknown{Type: %d, Data: %v}", m.t, m.d)
5694
}
@@ -67,7 +105,9 @@ func (m *Message) KeypressReport() (KeypressReport, error) {
67105
if m.t != TypeKeypressReport {
68106
return KeypressReport{}, fmt.Errorf("invalid message type: %d", m.t)
69107
}
70-
108+
if len(m.d) < 2 {
109+
return KeypressReport{}, fmt.Errorf("invalid message data length: %d", len(m.d))
110+
}
71111
return KeypressReport{
72112
Key: m.d[0],
73113
Press: m.d[1] == uint8(1),
@@ -95,7 +135,7 @@ func (m *Message) KeyboardReport() (KeyboardReport, error) {
95135
// Macro ..
96136
type KeyboardMacroStep struct {
97137
Modifier byte // 1 byte
98-
Keys []byte // 6 bytes: hidKeyBufferSize
138+
Keys []byte // 6 bytes: HidKeyBufferSize
99139
Delay uint16 // 2 bytes
100140
}
101141
type KeyboardMacroReport struct {
@@ -105,7 +145,7 @@ type KeyboardMacroReport struct {
105145
}
106146

107147
// HidKeyBufferSize is the size of the keys buffer in the keyboard report.
108-
const HidKeyBufferSize = 6
148+
const HidKeyBufferSize int = 6
109149

110150
// KeyboardMacroReport returns the keyboard macro report from the message.
111151
func (m *Message) KeyboardMacroReport() (KeyboardMacroReport, error) {
@@ -205,3 +245,20 @@ func (m *Message) KeyboardMacroState() (KeyboardMacroState, error) {
205245
IsPaste: m.d[1] == uint8(1),
206246
}, nil
207247
}
248+
249+
// KeyboardMacroToken returns the keyboard macro token UUID from the message.
250+
func (m *Message) KeyboardMacroToken() (uuid.UUID, error) {
251+
if m.t != TypeCancelKeyboardMacroByTokenReport {
252+
return uuid.Nil, fmt.Errorf("invalid message type: %d", m.t)
253+
}
254+
255+
if len(m.d) == 0 {
256+
return uuid.Nil, nil
257+
}
258+
259+
if len(m.d) != 16 {
260+
return uuid.Nil, fmt.Errorf("invalid UUID length: %d", len(m.d))
261+
}
262+
263+
return uuid.FromBytes(m.d)
264+
}

0 commit comments

Comments
 (0)