mirror of https://github.com/jetkvm/kvm.git
Compare commits
7 Commits
f9dcee1377
...
8e00b3d581
| Author | SHA1 | Date |
|---|---|---|
|
|
8e00b3d581 | |
|
|
e12ddaff79 | |
|
|
8de61db3d8 | |
|
|
da8c82da34 | |
|
|
49b9a35951 | |
|
|
05057cb6fa | |
|
|
57a7aa6a8b |
|
|
@ -42,16 +42,17 @@ const (
|
||||||
func GetQueueIndex(messageType MessageType) (int, time.Duration) {
|
func GetQueueIndex(messageType MessageType) (int, time.Duration) {
|
||||||
switch messageType {
|
switch messageType {
|
||||||
case TypeHandshake:
|
case TypeHandshake:
|
||||||
return HandshakeQueue, 1
|
return HandshakeQueue, 1 * time.Second
|
||||||
case TypeKeyboardReport, TypeKeypressReport, TypeKeyboardLedState, TypeKeydownState, TypeKeyboardMacroState:
|
case TypeKeyboardReport, TypeKeypressReport, TypeKeyboardLedState, TypeKeydownState, TypeKeyboardMacroState:
|
||||||
return KeyboardQueue, 1
|
return KeyboardQueue, 1 * time.Second
|
||||||
case TypePointerReport, TypeMouseReport, TypeWheelReport:
|
case TypePointerReport, TypeMouseReport, TypeWheelReport:
|
||||||
return MouseQueue, 1
|
return MouseQueue, 1 * time.Second
|
||||||
// we don't want to block the queue for these messages
|
// we don't want to block the queue for these messages
|
||||||
case TypeKeyboardMacroReport, TypeCancelKeyboardMacroReport, TypeKeyboardMacroTokenState:
|
case TypeKeyboardMacroReport, TypeCancelKeyboardMacroReport, TypeKeyboardMacroTokenState:
|
||||||
return MacroQueue, 60 // 1 minute timeout
|
return MacroQueue, 60 * time.Second
|
||||||
default:
|
default:
|
||||||
return OtherQueue, 5
|
return OtherQueue, 5 * time.Second
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/jetkvm/kvm/internal/usbgadget"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Message ..
|
// Message ..
|
||||||
|
|
@ -135,18 +136,16 @@ func (m *Message) KeyboardReport() (KeyboardReport, error) {
|
||||||
// Macro ..
|
// Macro ..
|
||||||
type KeyboardMacroStep struct {
|
type KeyboardMacroStep struct {
|
||||||
Modifier byte // 1 byte
|
Modifier byte // 1 byte
|
||||||
Keys []byte // 6 bytes: HidKeyBufferSize
|
Keys []byte // 6 bytes: usbgadget.HidKeyBufferSize
|
||||||
Delay uint16 // 2 bytes
|
Delay uint16 // 2 bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
type KeyboardMacroReport struct {
|
type KeyboardMacroReport struct {
|
||||||
IsPaste bool
|
IsPaste bool
|
||||||
StepCount uint32
|
StepCount uint32
|
||||||
Steps []KeyboardMacroStep
|
Steps []KeyboardMacroStep
|
||||||
}
|
}
|
||||||
|
|
||||||
// HidKeyBufferSize is the size of the keys buffer in the keyboard report.
|
|
||||||
const HidKeyBufferSize int = 6
|
|
||||||
|
|
||||||
// KeyboardMacroReport returns the keyboard macro report from the message.
|
// KeyboardMacroReport returns the keyboard macro report from the message.
|
||||||
func (m *Message) KeyboardMacroReport() (KeyboardMacroReport, error) {
|
func (m *Message) KeyboardMacroReport() (KeyboardMacroReport, error) {
|
||||||
if m.t != TypeKeyboardMacroReport {
|
if m.t != TypeKeyboardMacroReport {
|
||||||
|
|
@ -171,7 +170,7 @@ func (m *Message) KeyboardMacroReport() (KeyboardMacroReport, error) {
|
||||||
Delay: binary.BigEndian.Uint16(m.d[offset+7 : offset+9]),
|
Delay: binary.BigEndian.Uint16(m.d[offset+7 : offset+9]),
|
||||||
})
|
})
|
||||||
|
|
||||||
offset += 1 + HidKeyBufferSize + 2
|
offset += 1 + usbgadget.HidKeyBufferSize + 2
|
||||||
}
|
}
|
||||||
|
|
||||||
return KeyboardMacroReport{
|
return KeyboardMacroReport{
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ var keyboardReportDesc = []byte{
|
||||||
|
|
||||||
const (
|
const (
|
||||||
hidReadBufferSize = 8
|
hidReadBufferSize = 8
|
||||||
hidKeyBufferSize = 6
|
HidKeyBufferSize = 6
|
||||||
hidErrorRollOver = 0x01
|
hidErrorRollOver = 0x01
|
||||||
// https://www.usb.org/sites/default/files/documents/hid1_11.pdf
|
// https://www.usb.org/sites/default/files/documents/hid1_11.pdf
|
||||||
// https://www.usb.org/sites/default/files/hut1_2.pdf
|
// https://www.usb.org/sites/default/files/hut1_2.pdf
|
||||||
|
|
@ -161,13 +161,18 @@ func (u *UsbGadget) SetOnKeysDownChange(f func(state KeysDownState)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var suspendedKeyDownMessages bool = false
|
var suspendedKeyDownMessages bool = false
|
||||||
|
var suspendedKeyDownMessagesLock sync.Mutex
|
||||||
|
|
||||||
func (u *UsbGadget) SuspendKeyDownMessages() {
|
func (u *UsbGadget) SuspendKeyDownMessages() {
|
||||||
|
suspendedKeyDownMessagesLock.Lock()
|
||||||
suspendedKeyDownMessages = true
|
suspendedKeyDownMessages = true
|
||||||
|
suspendedKeyDownMessagesLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UsbGadget) ResumeSuspendKeyDownMessages() {
|
func (u *UsbGadget) ResumeSuspendKeyDownMessages() {
|
||||||
|
suspendedKeyDownMessagesLock.Lock()
|
||||||
suspendedKeyDownMessages = false
|
suspendedKeyDownMessages = false
|
||||||
|
suspendedKeyDownMessagesLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UsbGadget) SetOnKeepAliveReset(f func()) {
|
func (u *UsbGadget) SetOnKeepAliveReset(f func()) {
|
||||||
|
|
@ -337,7 +342,7 @@ func (u *UsbGadget) keyboardWriteHidFile(modifier byte, keys []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := u.writeWithTimeout(u.keyboardHidFile, append([]byte{modifier, 0x00}, keys[:hidKeyBufferSize]...))
|
_, err := u.writeWithTimeout(u.keyboardHidFile, append([]byte{modifier, 0x00}, keys[:HidKeyBufferSize]...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.logWithSuppression("keyboardWriteHidFile", 100, u.log, err, "failed to write to hidg0")
|
u.logWithSuppression("keyboardWriteHidFile", 100, u.log, err, "failed to write to hidg0")
|
||||||
u.keyboardHidFile.Close()
|
u.keyboardHidFile.Close()
|
||||||
|
|
@ -381,11 +386,11 @@ func (u *UsbGadget) UpdateKeysDown(modifier byte, keys []byte) KeysDownState {
|
||||||
func (u *UsbGadget) KeyboardReport(modifier byte, keys []byte) error {
|
func (u *UsbGadget) KeyboardReport(modifier byte, keys []byte) error {
|
||||||
defer u.resetUserInputTime()
|
defer u.resetUserInputTime()
|
||||||
|
|
||||||
if len(keys) > hidKeyBufferSize {
|
if len(keys) > HidKeyBufferSize {
|
||||||
keys = keys[:hidKeyBufferSize]
|
keys = keys[:HidKeyBufferSize]
|
||||||
}
|
}
|
||||||
if len(keys) < hidKeyBufferSize {
|
if len(keys) < HidKeyBufferSize {
|
||||||
keys = append(keys, make([]byte, hidKeyBufferSize-len(keys))...)
|
keys = append(keys, make([]byte, HidKeyBufferSize-len(keys))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := u.keyboardWriteHidFile(modifier, keys)
|
err := u.keyboardWriteHidFile(modifier, keys)
|
||||||
|
|
@ -468,7 +473,7 @@ func (u *UsbGadget) keypressReport(key byte, press bool) (KeysDownState, error)
|
||||||
// handle other keys that are not modifier keys by placing or removing them
|
// handle other keys that are not modifier keys by placing or removing them
|
||||||
// from the key buffer since the buffer tracks currently pressed keys
|
// from the key buffer since the buffer tracks currently pressed keys
|
||||||
overrun := true
|
overrun := true
|
||||||
for i := range hidKeyBufferSize {
|
for i := range HidKeyBufferSize {
|
||||||
// If we find the key in the buffer the buffer, we either remove it (if press is false)
|
// If we find the key in the buffer the buffer, we either remove it (if press is false)
|
||||||
// or do nothing (if down is true) because the buffer tracks currently pressed keys
|
// or do nothing (if down is true) because the buffer tracks currently pressed keys
|
||||||
// and if we find a zero byte, we can place the key there (if press is true)
|
// and if we find a zero byte, we can place the key there (if press is true)
|
||||||
|
|
@ -479,7 +484,7 @@ func (u *UsbGadget) keypressReport(key byte, press bool) (KeysDownState, error)
|
||||||
// we are releasing the key, remove it from the buffer
|
// we are releasing the key, remove it from the buffer
|
||||||
if keys[i] != 0 {
|
if keys[i] != 0 {
|
||||||
copy(keys[i:], keys[i+1:])
|
copy(keys[i:], keys[i+1:])
|
||||||
keys[hidKeyBufferSize-1] = 0 // Clear the last byte
|
keys[HidKeyBufferSize-1] = 0 // Clear the last byte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
overrun = false // We found a slot for the key
|
overrun = false // We found a slot for the key
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ func newUsbGadget(name string, configMap map[string]gadgetConfigItem, enabledDev
|
||||||
keyboardStateCtx: keyboardCtx,
|
keyboardStateCtx: keyboardCtx,
|
||||||
keyboardStateCancel: keyboardCancel,
|
keyboardStateCancel: keyboardCancel,
|
||||||
keyboardState: 0,
|
keyboardState: 0,
|
||||||
keysDownState: KeysDownState{Modifier: 0, Keys: []byte{0, 0, 0, 0, 0, 0}}, // must be initialized to hidKeyBufferSize (6) zero bytes
|
keysDownState: KeysDownState{Modifier: 0, Keys: []byte{0, 0, 0, 0, 0, 0}}, // must be initialized to usbgadget.HidKeyBufferSize (6) zero bytes
|
||||||
kbdAutoReleaseTimers: make(map[byte]*time.Timer),
|
kbdAutoReleaseTimers: make(map[byte]*time.Timer),
|
||||||
enabledDevices: *enabledDevices,
|
enabledDevices: *enabledDevices,
|
||||||
lastUserInput: time.Now(),
|
lastUserInput: time.Now(),
|
||||||
|
|
|
||||||
|
|
@ -1210,7 +1210,7 @@ func executeKeyboardMacro(ctx context.Context, isPaste bool, macro []hidrpc.Keyb
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// make sure keyboard state is reset and the client gets notified
|
// make sure keyboard state is reset and the client gets notified
|
||||||
gadget.ResumeSuspendKeyDownMessages()
|
gadget.ResumeSuspendKeyDownMessages()
|
||||||
err := rpcKeyboardReport(0, make([]byte, hidrpc.HidKeyBufferSize))
|
err := rpcKeyboardReport(0, make([]byte, usbgadget.HidKeyBufferSize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn().Err(err).Msg("failed to reset keyboard state")
|
logger.Warn().Err(err).Msg("failed to reset keyboard state")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@
|
||||||
"info_relayed_by_cloudflare": "Videresendt af Cloudflare",
|
"info_relayed_by_cloudflare": "Videresendt af Cloudflare",
|
||||||
"info_resolution": "Opløsning:",
|
"info_resolution": "Opløsning:",
|
||||||
"info_scroll_lock": "Scroll Lock",
|
"info_scroll_lock": "Scroll Lock",
|
||||||
"info_shift": "Flytte",
|
|
||||||
"info_usb_state": "USB-tilstand:",
|
"info_usb_state": "USB-tilstand:",
|
||||||
"info_video_size": "Videostørrelse:",
|
"info_video_size": "Videostørrelse:",
|
||||||
"input_disabled": "Input deaktiveret",
|
"input_disabled": "Input deaktiveret",
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@
|
||||||
"info_relayed_by_cloudflare": "Weitergeleitet von Cloudflare",
|
"info_relayed_by_cloudflare": "Weitergeleitet von Cloudflare",
|
||||||
"info_resolution": "Auflösung:",
|
"info_resolution": "Auflösung:",
|
||||||
"info_scroll_lock": "Rollen-Taste",
|
"info_scroll_lock": "Rollen-Taste",
|
||||||
"info_shift": "Schicht",
|
|
||||||
"info_usb_state": "USB-Status:",
|
"info_usb_state": "USB-Status:",
|
||||||
"info_video_size": "Videogröße:",
|
"info_video_size": "Videogröße:",
|
||||||
"input_disabled": "Eingabe deaktiviert",
|
"input_disabled": "Eingabe deaktiviert",
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@
|
||||||
"info_relayed_by_cloudflare": "Relayed by Cloudflare",
|
"info_relayed_by_cloudflare": "Relayed by Cloudflare",
|
||||||
"info_resolution": "Resolution:",
|
"info_resolution": "Resolution:",
|
||||||
"info_scroll_lock": "Scroll Lock",
|
"info_scroll_lock": "Scroll Lock",
|
||||||
"info_shift": "Shift",
|
|
||||||
"info_usb_state": "USB State:",
|
"info_usb_state": "USB State:",
|
||||||
"info_video_size": "Video Size:",
|
"info_video_size": "Video Size:",
|
||||||
"input_disabled": "Input disabled",
|
"input_disabled": "Input disabled",
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@
|
||||||
"info_relayed_by_cloudflare": "Retransmitido por Cloudflare",
|
"info_relayed_by_cloudflare": "Retransmitido por Cloudflare",
|
||||||
"info_resolution": "Resolución:",
|
"info_resolution": "Resolución:",
|
||||||
"info_scroll_lock": "Bloq Despl",
|
"info_scroll_lock": "Bloq Despl",
|
||||||
"info_shift": "Cambio",
|
|
||||||
"info_usb_state": "Estado USB:",
|
"info_usb_state": "Estado USB:",
|
||||||
"info_video_size": "Tamaño del vídeo:",
|
"info_video_size": "Tamaño del vídeo:",
|
||||||
"input_disabled": "Entrada deshabilitada",
|
"input_disabled": "Entrada deshabilitada",
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@
|
||||||
"info_relayed_by_cloudflare": "Relayé par Cloudflare",
|
"info_relayed_by_cloudflare": "Relayé par Cloudflare",
|
||||||
"info_resolution": "Résolution :",
|
"info_resolution": "Résolution :",
|
||||||
"info_scroll_lock": "Verrouillage du défilement",
|
"info_scroll_lock": "Verrouillage du défilement",
|
||||||
"info_shift": "Maj",
|
|
||||||
"info_usb_state": "État USB :",
|
"info_usb_state": "État USB :",
|
||||||
"info_video_size": "Taille de la vidéo :",
|
"info_video_size": "Taille de la vidéo :",
|
||||||
"input_disabled": "Entrée désactivée",
|
"input_disabled": "Entrée désactivée",
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@
|
||||||
"info_relayed_by_cloudflare": "Rilasciato da Cloudflare",
|
"info_relayed_by_cloudflare": "Rilasciato da Cloudflare",
|
||||||
"info_resolution": "Risoluzione:",
|
"info_resolution": "Risoluzione:",
|
||||||
"info_scroll_lock": "Blocco scorrimento",
|
"info_scroll_lock": "Blocco scorrimento",
|
||||||
"info_shift": "Spostare",
|
|
||||||
"info_usb_state": "Stato USB:",
|
"info_usb_state": "Stato USB:",
|
||||||
"info_video_size": "Dimensioni video:",
|
"info_video_size": "Dimensioni video:",
|
||||||
"input_disabled": "Input disabilitato",
|
"input_disabled": "Input disabilitato",
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@
|
||||||
"info_relayed_by_cloudflare": "Videresendt av Cloudflare",
|
"info_relayed_by_cloudflare": "Videresendt av Cloudflare",
|
||||||
"info_resolution": "Oppløsning:",
|
"info_resolution": "Oppløsning:",
|
||||||
"info_scroll_lock": "Scroll Lock",
|
"info_scroll_lock": "Scroll Lock",
|
||||||
"info_shift": "Skifte",
|
|
||||||
"info_usb_state": "USB-tilstand:",
|
"info_usb_state": "USB-tilstand:",
|
||||||
"info_video_size": "Videostørrelse:",
|
"info_video_size": "Videostørrelse:",
|
||||||
"input_disabled": "Inndata deaktivert",
|
"input_disabled": "Inndata deaktivert",
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@
|
||||||
"info_relayed_by_cloudflare": "Vidarebefordras av Cloudflare",
|
"info_relayed_by_cloudflare": "Vidarebefordras av Cloudflare",
|
||||||
"info_resolution": "Upplösning:",
|
"info_resolution": "Upplösning:",
|
||||||
"info_scroll_lock": "Scroll Lock",
|
"info_scroll_lock": "Scroll Lock",
|
||||||
"info_shift": "Flytta",
|
|
||||||
"info_usb_state": "USB-status:",
|
"info_usb_state": "USB-status:",
|
||||||
"info_video_size": "Videostorlek:",
|
"info_video_size": "Videostorlek:",
|
||||||
"input_disabled": "Inmatning inaktiverad",
|
"input_disabled": "Inmatning inaktiverad",
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@
|
||||||
"info_relayed_by_cloudflare": "由 Cloudflare 转发",
|
"info_relayed_by_cloudflare": "由 Cloudflare 转发",
|
||||||
"info_resolution": "分辨率:",
|
"info_resolution": "分辨率:",
|
||||||
"info_scroll_lock": "滚动锁定",
|
"info_scroll_lock": "滚动锁定",
|
||||||
"info_shift": "Shift",
|
|
||||||
"info_usb_state": "USB 状态:",
|
"info_usb_state": "USB 状态:",
|
||||||
"info_video_size": "视频大小:",
|
"info_video_size": "视频大小:",
|
||||||
"input_disabled": "输入禁用",
|
"input_disabled": "输入禁用",
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ import { TextAreaWithLabel } from "@components/TextArea";
|
||||||
// uint32 max value / 4
|
// uint32 max value / 4
|
||||||
const pasteMaxLength = 1073741824;
|
const pasteMaxLength = 1073741824;
|
||||||
const defaultDelay = 20;
|
const defaultDelay = 20;
|
||||||
|
const minimumDelay = 10;
|
||||||
|
const maximumDelay = 65534;
|
||||||
|
|
||||||
export default function PasteModal() {
|
export default function PasteModal() {
|
||||||
const TextAreaRef = useRef<HTMLTextAreaElement>(null);
|
const TextAreaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
@ -31,7 +33,7 @@ export default function PasteModal() {
|
||||||
const [invalidChars, setInvalidChars] = useState<string[]>([]);
|
const [invalidChars, setInvalidChars] = useState<string[]>([]);
|
||||||
const [delayValue, setDelayValue] = useState(defaultDelay);
|
const [delayValue, setDelayValue] = useState(defaultDelay);
|
||||||
const delay = useMemo(() => {
|
const delay = useMemo(() => {
|
||||||
if (delayValue < 0 || delayValue > 65534) {
|
if (delayValue < minimumDelay || delayValue > maximumDelay) {
|
||||||
return defaultDelay;
|
return defaultDelay;
|
||||||
}
|
}
|
||||||
return delayValue;
|
return delayValue;
|
||||||
|
|
@ -189,18 +191,18 @@ export default function PasteModal() {
|
||||||
type="number"
|
type="number"
|
||||||
label={m.paste_modal_delay_between_keys()}
|
label={m.paste_modal_delay_between_keys()}
|
||||||
placeholder={m.paste_modal_delay_between_keys()}
|
placeholder={m.paste_modal_delay_between_keys()}
|
||||||
min={50}
|
min={minimumDelay}
|
||||||
max={65534}
|
max={maximumDelay}
|
||||||
value={delayValue}
|
value={delayValue}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
setDelayValue(parseInt(e.target.value, 10));
|
setDelayValue(parseInt(e.target.value, 10));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{(delayValue < defaultDelay || delayValue > 65534) && (
|
{(delayValue < minimumDelay || delayValue > maximumDelay) && (
|
||||||
<div className="mt-2 flex items-center gap-x-2">
|
<div className="mt-2 flex items-center gap-x-2">
|
||||||
<ExclamationCircleIcon className="h-4 w-4 text-red-500 dark:text-red-400" />
|
<ExclamationCircleIcon className="h-4 w-4 text-red-500 dark:text-red-400" />
|
||||||
<span className="text-xs text-red-500 dark:text-red-400">
|
<span className="text-xs text-red-500 dark:text-red-400">
|
||||||
{m.paste_modal_delay_out_of_range({ min: 50, max: 65534 })}
|
{m.paste_modal_delay_out_of_range({ min: minimumDelay, max: maximumDelay })}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,7 @@ export class CancelKeyboardMacroReportMessage extends RpcMessage {
|
||||||
|
|
||||||
constructor(token: string) {
|
constructor(token: string) {
|
||||||
super(HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport);
|
super(HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport);
|
||||||
this.token = (token == null || token === undefined || token === "")
|
this.token = (token == null || token === "")
|
||||||
? "00000000-0000-0000-0000-000000000000"
|
? "00000000-0000-0000-0000-000000000000"
|
||||||
: token;
|
: token;
|
||||||
}
|
}
|
||||||
|
|
@ -397,11 +397,11 @@ export class CancelKeyboardMacroReportMessage extends RpcMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unmarshal(data: Uint8Array): CancelKeyboardMacroReportMessage | undefined {
|
public static unmarshal(data: Uint8Array): CancelKeyboardMacroReportMessage | undefined {
|
||||||
if (data.length == 0) {
|
if (data.length === 0) {
|
||||||
return new CancelKeyboardMacroReportMessage("00000000-0000-0000-0000-000000000000");
|
return new CancelKeyboardMacroReportMessage("00000000-0000-0000-0000-000000000000");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.length != 16) {
|
if (data.length !== 16) {
|
||||||
throw new Error(`Invalid cancel message length: ${data.length}`);
|
throw new Error(`Invalid cancel message length: ${data.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ def main(argv):
|
||||||
)
|
)
|
||||||
|
|
||||||
report = {
|
report = {
|
||||||
"generated_at": datetime.utcnow().isoformat() + "Z",
|
"generated_at": datetime.now().isoformat(),
|
||||||
"en_json": str(en_path),
|
"en_json": str(en_path),
|
||||||
"total_string_keys": total_keys,
|
"total_string_keys": total_keys,
|
||||||
"duplicate_groups": sorted(
|
"duplicate_groups": sorted(
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ def main():
|
||||||
|
|
||||||
print(f"Generating report for {len(usages)} usages ...")
|
print(f"Generating report for {len(usages)} usages ...")
|
||||||
report = {
|
report = {
|
||||||
"generated_at": datetime.utcnow().isoformat() + "Z",
|
"generated_at": datetime.now().isoformat(),
|
||||||
"en_json": str(en_path),
|
"en_json": str(en_path),
|
||||||
"src_root": args.src,
|
"src_root": args.src,
|
||||||
"total_keys": len(keys),
|
"total_keys": len(keys),
|
||||||
|
|
|
||||||
|
|
@ -261,8 +261,8 @@ func newSession(config SessionConfig) (*Session, error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for queue := range session.hidQueues {
|
for queueIndex := range session.hidQueues {
|
||||||
go session.handleQueue(session.hidQueues[queue])
|
go session.handleQueue(session.hidQueues[queueIndex])
|
||||||
}
|
}
|
||||||
|
|
||||||
peerConnection.OnDataChannel(func(d *webrtc.DataChannel) {
|
peerConnection.OnDataChannel(func(d *webrtc.DataChannel) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue