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