Skip to content

Commit 1e57f4b

Browse files
committed
Use the KeysDownState for the infobar
Strong typed in the typescript realm.
1 parent b3d8c3b commit 1e57f4b

File tree

13 files changed

+230
-208
lines changed

13 files changed

+230
-208
lines changed

internal/usbgadget/hid_keyboard.go

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ func (u *UsbGadget) listenKeyboardEvents() {
209209
}
210210
u.resetLogSuppressionCounter("keyboardHidFileRead")
211211

212-
l.Trace().Int("n", n).Bytes("buf", buf).Msg("got data from keyboard")
212+
l.Trace().Int("n", n).Uints8("buf", buf).Msg("got data from keyboard")
213213
if n != 1 {
214214
l.Trace().Int("n", n).Msg("expected 1 byte, got")
215215
continue
@@ -250,7 +250,7 @@ func (u *UsbGadget) keyboardWriteHidFile(modifier byte, keys []byte) error {
250250
return err
251251
}
252252

253-
_, err := u.keyboardHidFile.Write(append([]byte{modifier, 0x00}, keys[:]...))
253+
_, err := u.keyboardHidFile.Write(append([]byte{modifier, 0x00}, keys[:hidKeyBufferSize]...))
254254
if err != nil {
255255
u.logWithSuppression("keyboardWriteHidFile", 100, u.log, err, "failed to write to hidg0")
256256
u.keyboardHidFile.Close()
@@ -289,14 +289,8 @@ const (
289289
RightSuper = 0xE7 // Right GUI (e.g. Windows key, Apple Command key)
290290
)
291291

292-
// KeyCodeMask maps a key code to its corresponding bit mask
293-
type KeyCodeMask struct {
294-
KeyCode byte
295-
Mask byte
296-
}
297-
298292
// KeyCodeToMaskMap is a slice of KeyCodeMask for quick lookup
299-
var KeyCodeToMaskMap = map[uint8]uint8{
293+
var KeyCodeToMaskMap = map[byte]byte{
300294
LeftControl: ModifierMaskLeftControl,
301295
LeftShift: ModifierMaskLeftShift,
302296
LeftAlt: ModifierMaskLeftAlt,
@@ -314,7 +308,7 @@ func (u *UsbGadget) KeypressReport(key byte, press bool) (KeysDownState, error)
314308

315309
var state = u.keysDownState
316310
modifier := state.Modifier
317-
keys := state.Keys[:]
311+
keys := append([]byte(nil), state.Keys...)
318312

319313
if mask, exists := KeyCodeToMaskMap[key]; exists {
320314
// If the key is a modifier key, we update the keyboardModifier state
@@ -364,13 +358,15 @@ func (u *UsbGadget) KeypressReport(key byte, press bool) (KeysDownState, error)
364358
}
365359

366360
if err := u.keyboardWriteHidFile(modifier, keys); err != nil {
367-
u.log.Warn().Uint8("modifier", modifier).Bytes("keys", keys).Msg("Could not write keypress report to hidg0")
361+
u.log.Warn().Uint8("modifier", modifier).Uints8("keys", keys).Msg("Could not write keypress report to hidg0")
368362
}
369363

370-
state.Modifier = modifier
371-
state.Keys = keys
364+
var result = KeysDownState{
365+
Modifier: modifier,
366+
Keys: []byte(keys[:]),
367+
}
372368

373-
u.updateKeyDownState(state)
369+
u.updateKeyDownState(result)
374370

375-
return state, nil
371+
return result, nil
376372
}

internal/usbgadget/hid_mouse_absolute.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,17 @@ func (u *UsbGadget) absMouseWriteHidFile(data []byte) error {
8585
return nil
8686
}
8787

88-
func (u *UsbGadget) AbsMouseReport(x, y int, buttons uint8) error {
88+
func (u *UsbGadget) AbsMouseReport(x int, y int, buttons uint8) error {
8989
u.absMouseLock.Lock()
9090
defer u.absMouseLock.Unlock()
9191

9292
err := u.absMouseWriteHidFile([]byte{
93-
1, // Report ID 1
94-
buttons, // Buttons
95-
uint8(x), // X Low Byte
96-
uint8(x >> 8), // X High Byte
97-
uint8(y), // Y Low Byte
98-
uint8(y >> 8), // Y High Byte
93+
1, // Report ID 1
94+
buttons, // Buttons
95+
byte(x), // X Low Byte
96+
byte(x >> 8), // X High Byte
97+
byte(y), // Y Low Byte
98+
byte(y >> 8), // Y High Byte
9999
})
100100
if err != nil {
101101
return err

internal/usbgadget/hid_mouse_relative.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,15 @@ func (u *UsbGadget) relMouseWriteHidFile(data []byte) error {
7575
return nil
7676
}
7777

78-
func (u *UsbGadget) RelMouseReport(mx, my int8, buttons uint8) error {
78+
func (u *UsbGadget) RelMouseReport(mx int8, my int8, buttons uint8) error {
7979
u.relMouseLock.Lock()
8080
defer u.relMouseLock.Unlock()
8181

8282
err := u.relMouseWriteHidFile([]byte{
83-
buttons, // Buttons
84-
uint8(mx), // X
85-
uint8(my), // Y
86-
0, // Wheel
83+
buttons, // Buttons
84+
byte(mx), // X
85+
byte(my), // Y
86+
0, // Wheel
8787
})
8888
if err != nil {
8989
return err

internal/usbgadget/usbgadget.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ var defaultUsbGadgetDevices = Devices{
4242
}
4343

4444
type KeysDownState struct {
45-
Modifier byte `json:"modifier"`
46-
Keys []byte `json:"keys"`
45+
Modifier byte `json:"modifier"`
46+
Keys ByteSlice `json:"keys"`
4747
}
4848

4949
// UsbGadget is a struct that represents a USB gadget.

internal/usbgadget/utils.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package usbgadget
22

33
import (
44
"bytes"
5+
"encoding/json"
56
"fmt"
67
"path/filepath"
78
"strconv"
@@ -10,6 +11,31 @@ import (
1011
"github.com/rs/zerolog"
1112
)
1213

14+
type ByteSlice []byte
15+
16+
func (s ByteSlice) MarshalJSON() ([]byte, error) {
17+
vals := make([]int, len(s))
18+
for i, v := range s {
19+
vals[i] = int(v)
20+
}
21+
return json.Marshal(vals)
22+
}
23+
24+
func (s *ByteSlice) UnmarshalJSON(data []byte) error {
25+
var vals []int
26+
if err := json.Unmarshal(data, &vals); err != nil {
27+
return err
28+
}
29+
*s = make([]byte, len(vals))
30+
for i, v := range vals {
31+
if v < 0 || v > 255 {
32+
return fmt.Errorf("value %d out of byte range", v)
33+
}
34+
(*s)[i] = byte(v)
35+
}
36+
return nil
37+
}
38+
1339
func joinPath(basePath string, paths []string) string {
1440
pathArr := append([]string{basePath}, paths...)
1541
return filepath.Join(pathArr...)

jsonrpc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ func riskyCallRPCHandler(logger zerolog.Logger, handler RPCHandler, params map[s
566566
}
567567
}
568568

569-
logger.Trace().Interface("args", args).Msg("Calling RPC handler")
569+
logger.Trace().Msg("Calling RPC handler")
570570
results := handlerValue.Call(args)
571571

572572
if len(results) == 0 {

ui/src/components/InfoBar.tsx

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,65 @@
1-
import { useEffect } from "react";
1+
import { useEffect, useMemo } from "react";
22

33
import { cx } from "@/cva.config";
44
import {
5+
HidState,
6+
KeysDownState,
7+
MouseState,
8+
RTCState,
9+
SettingsState,
510
useHidStore,
611
useMouseStore,
712
useRTCStore,
813
useSettingsStore,
914
useVideoStore,
15+
VideoState,
1016
} from "@/hooks/stores";
1117
import { keys, modifiers } from "@/keyboardMappings";
1218

1319
export default function InfoBar() {
14-
const activeKeys = useHidStore(state => state.activeKeys);
15-
const activeModifiers = useHidStore(state => state.activeModifiers);
16-
const mouseX = useMouseStore(state => state.mouseX);
17-
const mouseY = useMouseStore(state => state.mouseY);
18-
const mouseMove = useMouseStore(state => state.mouseMove);
20+
const keysDownState = useHidStore((state: HidState) => state.keysDownState);
21+
const mouseX = useMouseStore((state: MouseState) => state.mouseX);
22+
const mouseY = useMouseStore((state: MouseState) => state.mouseY);
23+
const mouseMove = useMouseStore((state: MouseState) => state.mouseMove);
1924

2025
const videoClientSize = useVideoStore(
21-
state => `${Math.round(state.clientWidth)}x${Math.round(state.clientHeight)}`,
26+
(state: VideoState) => `${Math.round(state.clientWidth)}x${Math.round(state.clientHeight)}`,
2227
);
2328

2429
const videoSize = useVideoStore(
25-
state => `${Math.round(state.width)}x${Math.round(state.height)}`,
30+
(state: VideoState) => `${Math.round(state.width)}x${Math.round(state.height)}`,
2631
);
2732

28-
const rpcDataChannel = useRTCStore(state => state.rpcDataChannel);
33+
const rpcDataChannel = useRTCStore((state: RTCState) => state.rpcDataChannel);
2934

3035
const settings = useSettingsStore();
31-
const showPressedKeys = useSettingsStore(state => state.showPressedKeys);
36+
const showPressedKeys = useSettingsStore((state: SettingsState) => state.showPressedKeys);
3237

3338
useEffect(() => {
3439
if (!rpcDataChannel) return;
3540
rpcDataChannel.onclose = () => console.log("rpcDataChannel has closed");
36-
rpcDataChannel.onerror = e =>
41+
rpcDataChannel.onerror = (e: Event) =>
3742
console.log(`Error on DataChannel '${rpcDataChannel.label}': ${e}`);
3843
}, [rpcDataChannel]);
3944

40-
const keyboardLedState = useHidStore(state => state.keyboardLedState);
41-
const isTurnServerInUse = useRTCStore(state => state.isTurnServerInUse);
45+
const keyboardLedState = useHidStore((state: HidState) => state.keyboardLedState);
46+
const isTurnServerInUse = useRTCStore((state: RTCState) => state.isTurnServerInUse);
4247

43-
const usbState = useHidStore(state => state.usbState);
44-
const hdmiState = useVideoStore(state => state.hdmiState);
48+
const usbState = useHidStore((state: HidState) => state.usbState);
49+
const hdmiState = useVideoStore((state: VideoState) => state.hdmiState);
50+
51+
const displayKeys = useMemo(() => {
52+
if (!showPressedKeys || !keysDownState)
53+
return "";
54+
55+
const state = keysDownState as KeysDownState;
56+
const activeModifierMask = state.modifier || 0;
57+
const keysDown = state.keys || [];
58+
const modifierNames = Object.entries(modifiers).filter(([_, mask]) => (activeModifierMask & mask) !== 0).map(([name, _]) => name);
59+
const keyNames = Object.entries(keys).filter(([_, value]) => keysDown.includes(value)).map(([name, _]) => name);
60+
61+
return [...modifierNames,...keyNames].join(", ");
62+
}, [keysDownState, showPressedKeys]);
4563

4664
return (
4765
<div className="bg-white border-t border-t-slate-800/30 text-slate-800 dark:border-t-slate-300/20 dark:bg-slate-900 dark:text-slate-300">
@@ -99,14 +117,7 @@ export default function InfoBar() {
99117
<div className="flex items-center gap-x-1">
100118
<span className="text-xs font-semibold">Keys:</span>
101119
<h2 className="text-xs">
102-
{[
103-
...activeKeys.map(
104-
x => Object.entries(keys).filter(y => y[1] === x)[0][0],
105-
),
106-
activeModifiers.map(
107-
x => Object.entries(modifiers).filter(y => y[1] === x)[0][0],
108-
),
109-
].join(", ")}
120+
{displayKeys}
110121
</h2>
111122
</div>
112123
)}

ui/src/components/WebRTCVideo.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@ import { useJsonRpc } from "@/hooks/useJsonRpc";
1111
import { cx } from "@/cva.config";
1212
import { keys } from "@/keyboardMappings";
1313
import {
14+
MouseState,
15+
RTCState,
16+
SettingsState,
1417
useMouseStore,
1518
useRTCStore,
1619
useSettingsStore,
1720
useVideoStore,
21+
VideoState,
1822
} from "@/hooks/stores";
1923

2024
import {
@@ -27,15 +31,15 @@ import {
2731
export default function WebRTCVideo() {
2832
// Video and stream related refs and states
2933
const videoElm = useRef<HTMLVideoElement>(null);
30-
const mediaStream = useRTCStore(state => state.mediaStream);
34+
const mediaStream = useRTCStore((state: RTCState) => state.mediaStream);
3135
const [isPlaying, setIsPlaying] = useState(false);
32-
const peerConnectionState = useRTCStore(state => state.peerConnectionState);
36+
const peerConnectionState = useRTCStore((state: RTCState) => state.peerConnectionState);
3337
const [isPointerLockActive, setIsPointerLockActive] = useState(false);
3438
// Store hooks
3539
const settings = useSettingsStore();
3640
const { sendKeypressEvent, resetKeyboardState } = useKeyboard();
37-
const setMousePosition = useMouseStore(state => state.setMousePosition);
38-
const setMouseMove = useMouseStore(state => state.setMouseMove);
41+
const setMousePosition = useMouseStore((state: MouseState) => state.setMousePosition);
42+
const setMouseMove = useMouseStore((state: MouseState) => state.setMouseMove);
3943
const {
4044
setClientSize: setVideoClientSize,
4145
setSize: setVideoSize,
@@ -46,15 +50,15 @@ export default function WebRTCVideo() {
4650
} = useVideoStore();
4751

4852
// Video enhancement settings
49-
const videoSaturation = useSettingsStore(state => state.videoSaturation);
50-
const videoBrightness = useSettingsStore(state => state.videoBrightness);
51-
const videoContrast = useSettingsStore(state => state.videoContrast);
53+
const videoSaturation = useSettingsStore((state: SettingsState) => state.videoSaturation);
54+
const videoBrightness = useSettingsStore((state: SettingsState) => state.videoBrightness);
55+
const videoContrast = useSettingsStore((state: SettingsState) => state.videoContrast);
5256

5357
// RTC related states
54-
const peerConnection = useRTCStore(state => state.peerConnection);
58+
const peerConnection = useRTCStore((state: RTCState ) => state.peerConnection);
5559

5660
// HDMI and UI states
57-
const hdmiState = useVideoStore(state => state.hdmiState);
61+
const hdmiState = useVideoStore((state: VideoState) => state.hdmiState);
5862
const hdmiError = ["no_lock", "no_signal", "out_of_range"].includes(hdmiState);
5963
const isVideoLoading = !isPlaying;
6064

0 commit comments

Comments
 (0)