chore: resolve conflicts

This commit is contained in:
Siyuan Miao 2025-09-18 13:17:32 +02:00
parent 44b25eb33f
commit 8484bb3f38
1 changed files with 22 additions and 86 deletions

View File

@ -1,27 +1,20 @@
import { useCallback, useRef } from "react"; import { useCallback, useRef } from "react";
import {
hidErrorRollOver,
hidKeyBufferSize,
KeysDownState,
useHidStore,
useRTCStore,
} from "@/hooks/stores";
import {
hidErrorRollOver,
hidKeyBufferSize,
KeysDownState,
useHidStore,
useRTCStore,
} from "@/hooks/stores";
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
import { useHidRpc } from "@/hooks/useHidRpc";
import { import {
KeyboardLedStateMessage, KeyboardLedStateMessage,
KeyboardMacroStateMessage, KeyboardMacroStateMessage,
KeyboardMacroStep, KeyboardMacroStep,
KeysDownStateMessage, KeysDownStateMessage,
} from "@/hooks/hidRpc"; } from "@/hooks/hidRpc";
import {
hidErrorRollOver,
hidKeyBufferSize,
KeysDownState,
useHidStore,
useRTCStore,
} from "@/hooks/stores";
import { useHidRpc } from "@/hooks/useHidRpc";
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
import { hidKeyToModifierMask, keys, modifiers } from "@/keyboardMappings"; import { hidKeyToModifierMask, keys, modifiers } from "@/keyboardMappings";
const MACRO_RESET_KEYBOARD_STATE = { const MACRO_RESET_KEYBOARD_STATE = {
@ -72,7 +65,6 @@ export default function useKeyboard() {
cancelOngoingKeyboardMacro: cancelOngoingKeyboardMacroHidRpc, cancelOngoingKeyboardMacro: cancelOngoingKeyboardMacroHidRpc,
reportKeypressKeepAlive: sendKeypressKeepAliveHidRpc, reportKeypressKeepAlive: sendKeypressKeepAliveHidRpc,
rpcHidReady, rpcHidReady,
} = useHidRpc(message => {
} = useHidRpc(message => { } = useHidRpc(message => {
switch (message.constructor) { switch (message.constructor) {
case KeysDownStateMessage: case KeysDownStateMessage:
@ -100,36 +92,10 @@ export default function useKeyboard() {
// On older backends, we need to set the keysDownState manually since without the hidRpc API, the state doesn't trickle down from the backend // On older backends, we need to set the keysDownState manually since without the hidRpc API, the state doesn't trickle down from the backend
setKeysDownState({ modifier, keys }); setKeysDownState({ modifier, keys });
}); });
// sendKeyboardEvent is used to send the full keyboard state to the device for macro handling
// and resetting keyboard state. It sends the keys currently pressed and the modifier state.
// The device will respond with the keysDownState if it supports the keyPressReport API
// or just accept the state if it does not support (returning no result)
const sendKeyboardEvent = useCallback(
async (state: KeysDownState) => {
if (rpcDataChannel?.readyState !== "open" && !rpcHidReady) return;
console.debug(
`Send keyboardReport keys: ${state.keys}, modifier: ${state.modifier}`,
);
if (rpcHidReady) {
console.debug("Sending keyboard report via HidRPC");
sendKeyboardEventHidRpc(state.keys, state.modifier);
return;
}
send(
"keyboardReport",
{ keys: state.keys, modifier: state.modifier },
(resp: JsonRpcResponse) => {
if ("error" in resp) {
console.error(`Failed to send keyboard report ${state}`, resp.error);
}
},
);
}, },
[send, setKeysDownState], [send, setKeysDownState],
); );
const sendKeystrokeLegacy = useCallback(async (keys: number[], modifier: number, ac?: AbortController) => { const sendKeystrokeLegacy = useCallback(async (keys: number[], modifier: number, ac?: AbortController) => {
return await new Promise<void>((resolve, reject) => { return await new Promise<void>((resolve, reject) => {
const abortListener = () => { const abortListener = () => {
@ -149,22 +115,6 @@ export default function useKeyboard() {
}); });
}, [send]); }, [send]);
// resetKeyboardState is used to reset the keyboard state to no keys pressed and no modifiers.
// This is useful for macros and when the browser loses focus to ensure that the keyboard state
// is clean.
const resetKeyboardState = useCallback(async () => {
// Reset the keys buffer to zeros and the modifier state to zero
const { keys, modifier } = MACRO_RESET_KEYBOARD_STATE;
if (rpcHidReady) {
sendKeyboardEventHidRpc(keys, modifier);
} else {
// Older backends don't support the hidRpc API, so we send the full reset state
handleLegacyKeyboardReport(keys, modifier);
}
}, [rpcHidReady, sendKeyboardEventHidRpc, handleLegacyKeyboardReport]);
[rpcDataChannel?.readyState, rpcHidReady, send, sendKeyboardEventHidRpc],
);
// executeMacro is used to execute a macro consisting of multiple steps. // executeMacro is used to execute a macro consisting of multiple steps.
// Each step can have multiple keys, multiple modifiers and a delay. // Each step can have multiple keys, multiple modifiers and a delay.
@ -260,7 +210,6 @@ export default function useKeyboard() {
// we don't need to cancel it actually // we don't need to cancel it actually
cancelOngoingKeyboardMacroHidRpc(); cancelOngoingKeyboardMacroHidRpc();
}, [rpcHidReady, cancelOngoingKeyboardMacroHidRpc, abortController]); }, [rpcHidReady, cancelOngoingKeyboardMacroHidRpc, abortController]);
};
const KEEPALIVE_INTERVAL = 50; const KEEPALIVE_INTERVAL = 50;
@ -285,13 +234,15 @@ export default function useKeyboard() {
const resetKeyboardState = useCallback(async () => { const resetKeyboardState = useCallback(async () => {
// Cancel keepalive since we're resetting the keyboard state // Cancel keepalive since we're resetting the keyboard state
cancelKeepAlive(); cancelKeepAlive();
// Reset the keys buffer to zeros and the modifier state to zero // Reset the keys buffer to zeros and the modifier state to zero
keysDownState.keys.length = hidKeyBufferSize; const { keys, modifier } = MACRO_RESET_KEYBOARD_STATE;
keysDownState.keys.fill(0); if (rpcHidReady) {
keysDownState.modifier = 0; sendKeyboardEventHidRpc(keys, modifier);
sendKeyboardEvent(keysDownState); } else {
}, [keysDownState, sendKeyboardEvent, cancelKeepAlive]); // Older backends don't support the hidRpc API, so we send the full reset state
handleLegacyKeyboardReport(keys, modifier);
}
}, [rpcHidReady, sendKeyboardEventHidRpc, handleLegacyKeyboardReport, cancelKeepAlive]);
// handleKeyPress is used to handle a key press or release event. // handleKeyPress is used to handle a key press or release event.
// This function handle both key press and key release events. // This function handle both key press and key release events.
@ -331,13 +282,9 @@ export default function useKeyboard() {
// 1. Calculate the state // 1. Calculate the state
// 2. Send the newly calculated state to the device // 2. Send the newly calculated state to the device
const downState = simulateDeviceSideKeyHandlingForLegacyDevices( const downState = simulateDeviceSideKeyHandlingForLegacyDevices(
keysDownState, keysDownState,
key, key,
press, press,
,
); );
handleLegacyKeyboardReport(downState.keys, downState.modifier); handleLegacyKeyboardReport(downState.keys, downState.modifier);
@ -351,25 +298,18 @@ export default function useKeyboard() {
[ [
rpcDataChannel?.readyState, rpcDataChannel?.readyState,
rpcHidReady, rpcHidReady,
sendKeypressEventHidRpc,
keysDownState, keysDownState,
handleLegacyKeyboardReport, handleLegacyKeyboardReport,
resetKeyboardState, resetKeyboardState,
rpcDataChannel?.readyState,
sendKeyboardEvent,
sendKeypress, sendKeypress,
], ],
); );
// IMPORTANT: See the keyPressReportApiAvailable comment above for the reason this exists // IMPORTANT: See the keyPressReportApiAvailable comment above for the reason this exists
function simulateDeviceSideKeyHandlingForLegacyDevices( function simulateDeviceSideKeyHandlingForLegacyDevices(
state: KeysDownState, state: KeysDownState,
key: number, key: number,
press: boolean, press: boolean,
,
): KeysDownState { ): KeysDownState {
// IMPORTANT: This code parallels the logic in the kernel's hid-gadget driver // IMPORTANT: This code parallels the logic in the kernel's hid-gadget driver
// for handling key presses and releases. It ensures that the USB gadget // for handling key presses and releases. It ensures that the USB gadget
@ -415,17 +355,13 @@ export default function useKeyboard() {
// If we reach here it means we didn't find an empty slot or the key in the buffer // If we reach here it means we didn't find an empty slot or the key in the buffer
if (overrun) { if (overrun) {
if (press) { if (press) {
console.warn( console.warn(`keyboard buffer overflow current keys ${keys}, key: ${key} not added`);
`keyboard buffer overflow current keys ${keys}, key: ${key} not added`,
,
);
// Fill all key slots with ErrorRollOver (0x01) to indicate overflow // Fill all key slots with ErrorRollOver (0x01) to indicate overflow
keys.length = hidKeyBufferSize; keys.length = hidKeyBufferSize;
keys.fill(hidErrorRollOver); keys.fill(hidErrorRollOver);
} else { } else {
// If we are releasing a key, and we didn't find it in a slot, who cares? // If we are releasing a key, and we didn't find it in a slot, who cares?
console.debug(`key ${key} not found in buffer, nothing to release`);; console.debug(`key ${key} not found in buffer, nothing to release`);
} }
} }
} }