mirror of https://github.com/jetkvm/kvm.git
Compare commits
2 Commits
7622697887
...
5e6da5b782
Author | SHA1 | Date |
---|---|---|
|
5e6da5b782 | |
|
94f6bbd27a |
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"version": "2025.08.15.001",
|
"version": "2025.08.15.2119",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"version": "2025.08.15.001",
|
"version": "2025.08.15.2119",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.2.7",
|
"@headlessui/react": "^2.2.7",
|
||||||
"@headlessui/tailwindcss": "^0.2.2",
|
"@headlessui/tailwindcss": "^0.2.2",
|
||||||
|
@ -28,10 +28,10 @@
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-animate-height": "^3.2.3",
|
"react-animate-height": "^3.2.3",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-hot-toast": "^2.5.2",
|
"react-hot-toast": "^2.6.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.22.3",
|
||||||
"react-simple-keyboard": "^3.8.109",
|
"react-simple-keyboard": "^3.8.111",
|
||||||
"react-use-websocket": "^4.13.0",
|
"react-use-websocket": "^4.13.0",
|
||||||
"react-xtermjs": "^1.0.10",
|
"react-xtermjs": "^1.0.10",
|
||||||
"recharts": "^2.15.3",
|
"recharts": "^2.15.3",
|
||||||
|
@ -3156,9 +3156,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.201",
|
"version": "1.5.202",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.201.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.202.tgz",
|
||||||
"integrity": "sha512-ZG65vsrLClodGqywuigc+7m0gr4ISoTQttfVh7nfpLv0M7SIwF4WbFNEOywcqTiujs12AUeeXbFyQieDICAIxg==",
|
"integrity": "sha512-NxbYjRmiHcHXV1Ws3fWUW+SLb62isauajk45LUJ/HgIOkUA7jLZu/X2Iif+X9FBNK8QkF9Zb4Q2mcwXCcY30mg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
@ -5784,9 +5784,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-hot-toast": {
|
"node_modules/react-hot-toast": {
|
||||||
"version": "2.5.2",
|
"version": "2.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz",
|
||||||
"integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==",
|
"integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.1.3",
|
"csstype": "^3.1.3",
|
||||||
|
@ -5848,9 +5848,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-simple-keyboard": {
|
"node_modules/react-simple-keyboard": {
|
||||||
"version": "3.8.109",
|
"version": "3.8.111",
|
||||||
"resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.109.tgz",
|
"resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.111.tgz",
|
||||||
"integrity": "sha512-FLlivKL4tb5G2cWOo2slOrMEkzzFX0Yg8P7k5qzisN8+TnqUPq+8G7N8D2+0oVkSmfeqZn6PyLCurGSitK4QIQ==",
|
"integrity": "sha512-zR6qeGeH1bhaP8GDMLwRBqpyU98jGUOmuNKZT6Z0056kjR4EVRo99Z/eVCafN0ySKpweQ6x0gVAjxkegy6EDFg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2025.08.15.001",
|
"version": "2025.08.15.2119",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "22.15.0"
|
"node": "22.15.0"
|
||||||
|
@ -39,10 +39,10 @@
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-animate-height": "^3.2.3",
|
"react-animate-height": "^3.2.3",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-hot-toast": "^2.5.2",
|
"react-hot-toast": "^2.6.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.22.3",
|
||||||
"react-simple-keyboard": "^3.8.109",
|
"react-simple-keyboard": "^3.8.111",
|
||||||
"react-use-websocket": "^4.13.0",
|
"react-use-websocket": "^4.13.0",
|
||||||
"react-xtermjs": "^1.0.10",
|
"react-xtermjs": "^1.0.10",
|
||||||
"recharts": "^2.15.3",
|
"recharts": "^2.15.3",
|
||||||
|
|
|
@ -36,7 +36,7 @@ export function useJsonRpc(onRequest?: (payload: JsonRpcRequest) => void) {
|
||||||
const { rpcDataChannel } = useRTCStore();
|
const { rpcDataChannel } = useRTCStore();
|
||||||
|
|
||||||
const send = useCallback(
|
const send = useCallback(
|
||||||
(method: string, params: unknown, callback?: (resp: JsonRpcResponse) => void) => {
|
async (method: string, params: unknown, callback?: (resp: JsonRpcResponse) => void) => {
|
||||||
if (rpcDataChannel?.readyState !== "open") return;
|
if (rpcDataChannel?.readyState !== "open") return;
|
||||||
requestCounter++;
|
requestCounter++;
|
||||||
const payload = { jsonrpc: "2.0", method, params, id: requestCounter };
|
const payload = { jsonrpc: "2.0", method, params, id: requestCounter };
|
||||||
|
@ -61,7 +61,7 @@ export function useJsonRpc(onRequest?: (payload: JsonRpcRequest) => void) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("error" in payload) console.error(payload.error);
|
if ("error" in payload) console.error("RPC error", payload);
|
||||||
if (!payload.id) return;
|
if (!payload.id) return;
|
||||||
|
|
||||||
const callback = callbackStore.get(payload.id);
|
const callback = callbackStore.get(payload.id);
|
||||||
|
|
|
@ -28,7 +28,7 @@ export default function useKeyboard() {
|
||||||
// The device will respond with the keysDownState if it supports the keyPressReport API
|
// 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)
|
// or just accept the state if it does not support (returning no result)
|
||||||
const sendKeyboardEvent = useCallback(
|
const sendKeyboardEvent = useCallback(
|
||||||
(state: KeysDownState) => {
|
async (state: KeysDownState) => {
|
||||||
if (rpcDataChannel?.readyState !== "open") return;
|
if (rpcDataChannel?.readyState !== "open") return;
|
||||||
|
|
||||||
console.debug(`Send keyboardReport keys: ${state.keys}, modifier: ${state.modifier}`);
|
console.debug(`Send keyboardReport keys: ${state.keys}, modifier: ${state.modifier}`);
|
||||||
|
@ -45,13 +45,14 @@ export default function useKeyboard() {
|
||||||
setkeyPressReportApiAvailable(true); // if they returned a keysDownState, we ALSO know they also support keyPressReport
|
setkeyPressReportApiAvailable(true); // if they returned a keysDownState, we ALSO know they also support keyPressReport
|
||||||
} else {
|
} else {
|
||||||
// older devices versions do not return the keyDownState
|
// older devices versions do not return the keyDownState
|
||||||
setKeysDownState(state); // we just pretend they accepted what we sent
|
// so we just pretend they accepted what we sent
|
||||||
|
setKeysDownState(state);
|
||||||
setkeyPressReportApiAvailable(false); // we ALSO know they do not support keyPressReport
|
setkeyPressReportApiAvailable(false); // we ALSO know they do not support keyPressReport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[rpcDataChannel?.readyState, send, setkeyPressReportApiAvailable, setKeysDownState],
|
[rpcDataChannel?.readyState, send, setKeysDownState, setkeyPressReportApiAvailable],
|
||||||
);
|
);
|
||||||
|
|
||||||
// sendKeypressEvent is used to send a single key press/release event to the device.
|
// sendKeypressEvent is used to send a single key press/release event to the device.
|
||||||
|
@ -61,7 +62,7 @@ export default function useKeyboard() {
|
||||||
// In that case we will switch to local key handling and update the keysDownState
|
// In that case we will switch to local key handling and update the keysDownState
|
||||||
// in client/browser-side code using simulateDeviceSideKeyHandlingForLegacyDevices.
|
// in client/browser-side code using simulateDeviceSideKeyHandlingForLegacyDevices.
|
||||||
const sendKeypressEvent = useCallback(
|
const sendKeypressEvent = useCallback(
|
||||||
(key: number, press: boolean) => {
|
async (key: number, press: boolean) => {
|
||||||
if (rpcDataChannel?.readyState !== "open") return;
|
if (rpcDataChannel?.readyState !== "open") return;
|
||||||
|
|
||||||
console.debug(`Send keypressEvent key: ${key}, press: ${press}`);
|
console.debug(`Send keypressEvent key: ${key}, press: ${press}`);
|
||||||
|
@ -90,12 +91,15 @@ export default function useKeyboard() {
|
||||||
// resetKeyboardState is used to reset the keyboard state to no keys pressed and no modifiers.
|
// 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
|
// This is useful for macros and when the browser loses focus to ensure that the keyboard state
|
||||||
// is clean.
|
// is clean.
|
||||||
const resetKeyboardState = useCallback(() => {
|
const resetKeyboardState = useCallback(
|
||||||
console.debug("Resetting keyboard state");
|
async () => {
|
||||||
keysDownState.keys.fill(0); // Reset the keys buffer to zeros
|
console.debug("Resetting keyboard state");
|
||||||
keysDownState.modifier = 0; // Reset the modifier state to zero
|
// Reset the keys buffer to zeros and the modifier state to zero
|
||||||
sendKeyboardEvent(keysDownState);
|
keysDownState.keys.length = hidKeyBufferSize;
|
||||||
}, [keysDownState, sendKeyboardEvent]);
|
keysDownState.keys.fill(0);
|
||||||
|
keysDownState.modifier = 0;
|
||||||
|
sendKeyboardEvent(keysDownState);
|
||||||
|
}, [keysDownState, sendKeyboardEvent]);
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -133,7 +137,7 @@ export default function useKeyboard() {
|
||||||
// handling for legacy devices and updates the keysDownState accordingly.
|
// handling for legacy devices and updates the keysDownState accordingly.
|
||||||
// It then sends the full keyboard state to the device.
|
// It then sends the full keyboard state to the device.
|
||||||
const handleKeyPress = useCallback(
|
const handleKeyPress = useCallback(
|
||||||
(key: number, press: boolean) => {
|
async (key: number, press: boolean) => {
|
||||||
if (rpcDataChannel?.readyState !== "open") return;
|
if (rpcDataChannel?.readyState !== "open") return;
|
||||||
|
|
||||||
if (keyPressReportApiAvailable) {
|
if (keyPressReportApiAvailable) {
|
||||||
|
@ -143,9 +147,14 @@ export default function useKeyboard() {
|
||||||
// if the keyPress api is not available, we need to handle the key locally
|
// if the keyPress api is not available, we need to handle the key locally
|
||||||
const downState = simulateDeviceSideKeyHandlingForLegacyDevices(keysDownState, key, press);
|
const downState = simulateDeviceSideKeyHandlingForLegacyDevices(keysDownState, key, press);
|
||||||
sendKeyboardEvent(downState); // then we send the full state
|
sendKeyboardEvent(downState); // then we send the full state
|
||||||
|
|
||||||
|
// if we just sent ErrorRollOver, reset to empty state
|
||||||
|
if (downState.keys[0] === hidErrorRollOver) {
|
||||||
|
resetKeyboardState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[keyPressReportApiAvailable, keysDownState, rpcDataChannel?.readyState, sendKeyboardEvent, sendKeypressEvent],
|
[keyPressReportApiAvailable, keysDownState, resetKeyboardState, rpcDataChannel?.readyState, sendKeyboardEvent, sendKeypressEvent],
|
||||||
);
|
);
|
||||||
|
|
||||||
// IMPORTANT: See the keyPressReportApiAvailable comment above for the reason this exists
|
// IMPORTANT: See the keyPressReportApiAvailable comment above for the reason this exists
|
||||||
|
@ -154,8 +163,8 @@ export default function useKeyboard() {
|
||||||
// for handling key presses and releases. It ensures that the USB gadget
|
// for handling key presses and releases. It ensures that the USB gadget
|
||||||
// behaves similarly to a real USB HID keyboard. This logic is paralleled
|
// behaves similarly to a real USB HID keyboard. This logic is paralleled
|
||||||
// in the device-side code in hid_keyboard.go so make sure to keep them in sync.
|
// in the device-side code in hid_keyboard.go so make sure to keep them in sync.
|
||||||
const keys = state.keys;
|
|
||||||
let modifiers = state.modifier;
|
let modifiers = state.modifier;
|
||||||
|
const keys = state.keys;
|
||||||
const modifierMask = hidKeyToModifierMask[key] || 0;
|
const modifierMask = hidKeyToModifierMask[key] || 0;
|
||||||
|
|
||||||
if (modifierMask !== 0) {
|
if (modifierMask !== 0) {
|
||||||
|
@ -163,7 +172,6 @@ export default function useKeyboard() {
|
||||||
// by setting or clearing the corresponding bit in the modifier byte.
|
// by setting or clearing the corresponding bit in the modifier byte.
|
||||||
// This allows us to track the state of dynamic modifier keys like
|
// This allows us to track the state of dynamic modifier keys like
|
||||||
// Shift, Control, Alt, and Super.
|
// Shift, Control, Alt, and Super.
|
||||||
console.debug(`Handling modifier key: ${key}, press: ${press}, current modifiers: ${modifiers}, modifier mask: ${modifierMask}`);
|
|
||||||
if (press) {
|
if (press) {
|
||||||
modifiers |= modifierMask;
|
modifiers |= modifierMask;
|
||||||
} else {
|
} else {
|
||||||
|
@ -173,34 +181,35 @@ export default function useKeyboard() {
|
||||||
// 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
|
||||||
let overrun = true;
|
let overrun = true;
|
||||||
for (let i = 0; i < hidKeyBufferSize && overrun; i++) {
|
for (let i = 0; i < hidKeyBufferSize; i++) {
|
||||||
// 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)
|
||||||
if (keys[i] == key || keys[i] == 0) {
|
if (keys[i] === key || keys[i] === 0) {
|
||||||
if (press) {
|
if (press) {
|
||||||
keys[i] = key // overwrites the zero byte or the same key if already pressed
|
keys[i] = key // overwrites the zero byte or the same key if already pressed
|
||||||
} else {
|
} else {
|
||||||
// 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) {
|
||||||
keys.splice(i, 1);
|
keys.splice(i, 1);
|
||||||
keys.push(0); // add a zero at the end
|
keys.push(0); // add a zero at the end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
overrun = false // We found a slot for the key
|
overrun = false; // We found a slot for the key
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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(`keyboard buffer overflow, key: ${key} not added`);
|
console.warn(`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 = 6;
|
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`)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,27 +248,27 @@ export default function KvmIdRoute() {
|
||||||
reconnectAttempts: 15,
|
reconnectAttempts: 15,
|
||||||
reconnectInterval: 1000,
|
reconnectInterval: 1000,
|
||||||
onReconnectStop: () => {
|
onReconnectStop: () => {
|
||||||
console.log("Reconnect stopped");
|
console.debug("Reconnect stopped");
|
||||||
cleanupAndStopReconnecting();
|
cleanupAndStopReconnecting();
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldReconnect(event) {
|
shouldReconnect(event) {
|
||||||
console.log("[Websocket] shouldReconnect", event);
|
console.debug("[Websocket] shouldReconnect", event);
|
||||||
// TODO: Why true?
|
// TODO: Why true?
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
onClose(event) {
|
onClose(event) {
|
||||||
console.log("[Websocket] onClose", event);
|
console.debug("[Websocket] onClose", event);
|
||||||
// We don't want to close everything down, we wait for the reconnect to stop instead
|
// We don't want to close everything down, we wait for the reconnect to stop instead
|
||||||
},
|
},
|
||||||
|
|
||||||
onError(event) {
|
onError(event) {
|
||||||
console.log("[Websocket] onError", event);
|
console.error("[Websocket] onError", event);
|
||||||
// We don't want to close everything down, we wait for the reconnect to stop instead
|
// We don't want to close everything down, we wait for the reconnect to stop instead
|
||||||
},
|
},
|
||||||
onOpen() {
|
onOpen() {
|
||||||
console.log("[Websocket] onOpen");
|
console.debug("[Websocket] onOpen");
|
||||||
},
|
},
|
||||||
|
|
||||||
onMessage: message => {
|
onMessage: message => {
|
||||||
|
@ -290,8 +290,8 @@ export default function KvmIdRoute() {
|
||||||
const parsedMessage = JSON.parse(message.data);
|
const parsedMessage = JSON.parse(message.data);
|
||||||
if (parsedMessage.type === "device-metadata") {
|
if (parsedMessage.type === "device-metadata") {
|
||||||
const { deviceVersion } = parsedMessage.data;
|
const { deviceVersion } = parsedMessage.data;
|
||||||
console.log("[Websocket] Received device-metadata message");
|
console.debug("[Websocket] Received device-metadata message");
|
||||||
console.log("[Websocket] Device version", deviceVersion);
|
console.debug("[Websocket] Device version", deviceVersion);
|
||||||
// If the device version is not set, we can assume the device is using the legacy signaling
|
// If the device version is not set, we can assume the device is using the legacy signaling
|
||||||
if (!deviceVersion) {
|
if (!deviceVersion) {
|
||||||
console.log("[Websocket] Device is using legacy signaling");
|
console.log("[Websocket] Device is using legacy signaling");
|
||||||
|
@ -309,7 +309,7 @@ export default function KvmIdRoute() {
|
||||||
|
|
||||||
if (!peerConnection) return;
|
if (!peerConnection) return;
|
||||||
if (parsedMessage.type === "answer") {
|
if (parsedMessage.type === "answer") {
|
||||||
console.log("[Websocket] Received answer");
|
console.debug("[Websocket] Received answer");
|
||||||
const readyForOffer =
|
const readyForOffer =
|
||||||
// If we're making an offer, we don't want to accept an answer
|
// If we're making an offer, we don't want to accept an answer
|
||||||
!makingOffer &&
|
!makingOffer &&
|
||||||
|
@ -323,7 +323,7 @@ export default function KvmIdRoute() {
|
||||||
|
|
||||||
// Set so we don't accept an answer while we're setting the remote description
|
// Set so we don't accept an answer while we're setting the remote description
|
||||||
isSettingRemoteAnswerPending.current = parsedMessage.type === "answer";
|
isSettingRemoteAnswerPending.current = parsedMessage.type === "answer";
|
||||||
console.log(
|
console.debug(
|
||||||
"[Websocket] Setting remote answer pending",
|
"[Websocket] Setting remote answer pending",
|
||||||
isSettingRemoteAnswerPending.current,
|
isSettingRemoteAnswerPending.current,
|
||||||
);
|
);
|
||||||
|
@ -339,7 +339,7 @@ export default function KvmIdRoute() {
|
||||||
// Reset the remote answer pending flag
|
// Reset the remote answer pending flag
|
||||||
isSettingRemoteAnswerPending.current = false;
|
isSettingRemoteAnswerPending.current = false;
|
||||||
} else if (parsedMessage.type === "new-ice-candidate") {
|
} else if (parsedMessage.type === "new-ice-candidate") {
|
||||||
console.log("[Websocket] Received new-ice-candidate");
|
console.debug("[Websocket] Received new-ice-candidate");
|
||||||
const candidate = parsedMessage.data;
|
const candidate = parsedMessage.data;
|
||||||
peerConnection.addIceCandidate(candidate);
|
peerConnection.addIceCandidate(candidate);
|
||||||
}
|
}
|
||||||
|
@ -385,7 +385,7 @@ export default function KvmIdRoute() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Successfully got Remote Session Description. Setting.");
|
console.debug("Successfully got Remote Session Description. Setting.");
|
||||||
setLoadingMessage("Setting remote session description...");
|
setLoadingMessage("Setting remote session description...");
|
||||||
|
|
||||||
const decodedSd = atob(json.sd);
|
const decodedSd = atob(json.sd);
|
||||||
|
@ -396,13 +396,13 @@ export default function KvmIdRoute() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const setupPeerConnection = useCallback(async () => {
|
const setupPeerConnection = useCallback(async () => {
|
||||||
console.log("[setupPeerConnection] Setting up peer connection");
|
console.debug("[setupPeerConnection] Setting up peer connection");
|
||||||
setConnectionFailed(false);
|
setConnectionFailed(false);
|
||||||
setLoadingMessage("Connecting to device...");
|
setLoadingMessage("Connecting to device...");
|
||||||
|
|
||||||
let pc: RTCPeerConnection;
|
let pc: RTCPeerConnection;
|
||||||
try {
|
try {
|
||||||
console.log("[setupPeerConnection] Creating peer connection");
|
console.debug("[setupPeerConnection] Creating peer connection");
|
||||||
setLoadingMessage("Creating peer connection...");
|
setLoadingMessage("Creating peer connection...");
|
||||||
pc = new RTCPeerConnection({
|
pc = new RTCPeerConnection({
|
||||||
// We only use STUN or TURN servers if we're in the cloud
|
// We only use STUN or TURN servers if we're in the cloud
|
||||||
|
@ -412,7 +412,7 @@ export default function KvmIdRoute() {
|
||||||
});
|
});
|
||||||
|
|
||||||
setPeerConnectionState(pc.connectionState);
|
setPeerConnectionState(pc.connectionState);
|
||||||
console.log("[setupPeerConnection] Peer connection created", pc);
|
console.debug("[setupPeerConnection] Peer connection created", pc);
|
||||||
setLoadingMessage("Setting up connection to device...");
|
setLoadingMessage("Setting up connection to device...");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`[setupPeerConnection] Error creating peer connection: ${e}`);
|
console.error(`[setupPeerConnection] Error creating peer connection: ${e}`);
|
||||||
|
@ -424,13 +424,13 @@ export default function KvmIdRoute() {
|
||||||
|
|
||||||
// Set up event listeners and data channels
|
// Set up event listeners and data channels
|
||||||
pc.onconnectionstatechange = () => {
|
pc.onconnectionstatechange = () => {
|
||||||
console.log("[setupPeerConnection] Connection state changed", pc.connectionState);
|
console.debug("[setupPeerConnection] Connection state changed", pc.connectionState);
|
||||||
setPeerConnectionState(pc.connectionState);
|
setPeerConnectionState(pc.connectionState);
|
||||||
};
|
};
|
||||||
|
|
||||||
pc.onnegotiationneeded = async () => {
|
pc.onnegotiationneeded = async () => {
|
||||||
try {
|
try {
|
||||||
console.log("[setupPeerConnection] Creating offer");
|
console.debug("[setupPeerConnection] Creating offer");
|
||||||
makingOffer.current = true;
|
makingOffer.current = true;
|
||||||
|
|
||||||
const offer = await pc.createOffer();
|
const offer = await pc.createOffer();
|
||||||
|
@ -462,7 +462,7 @@ export default function KvmIdRoute() {
|
||||||
pc.onicegatheringstatechange = event => {
|
pc.onicegatheringstatechange = event => {
|
||||||
const pc = event.currentTarget as RTCPeerConnection;
|
const pc = event.currentTarget as RTCPeerConnection;
|
||||||
if (pc.iceGatheringState === "complete") {
|
if (pc.iceGatheringState === "complete") {
|
||||||
console.log("ICE Gathering completed");
|
console.debug("ICE Gathering completed");
|
||||||
setLoadingMessage("ICE Gathering completed");
|
setLoadingMessage("ICE Gathering completed");
|
||||||
|
|
||||||
if (isLegacySignalingEnabled.current) {
|
if (isLegacySignalingEnabled.current) {
|
||||||
|
@ -470,7 +470,7 @@ export default function KvmIdRoute() {
|
||||||
legacyHTTPSignaling(pc);
|
legacyHTTPSignaling(pc);
|
||||||
}
|
}
|
||||||
} else if (pc.iceGatheringState === "gathering") {
|
} else if (pc.iceGatheringState === "gathering") {
|
||||||
console.log("ICE Gathering Started");
|
console.debug("ICE Gathering Started");
|
||||||
setLoadingMessage("Gathering ICE candidates...");
|
setLoadingMessage("Gathering ICE candidates...");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -597,11 +597,15 @@ export default function KvmIdRoute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.method === "usbState") {
|
if (resp.method === "usbState") {
|
||||||
setUsbState(resp.params as unknown as USBStates);
|
const usbState = resp.params as unknown as USBStates;
|
||||||
|
console.debug("Setting USB state", usbState);
|
||||||
|
setUsbState(usbState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.method === "videoInputState") {
|
if (resp.method === "videoInputState") {
|
||||||
setHdmiState(resp.params as Parameters<VideoState["setHdmiState"]>[0]);
|
const hdmiState = resp.params as Parameters<VideoState["setHdmiState"]>[0];
|
||||||
|
console.debug("Setting HDMI state", hdmiState);
|
||||||
|
setHdmiState(hdmiState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.method === "networkState") {
|
if (resp.method === "networkState") {
|
||||||
|
@ -617,16 +621,14 @@ export default function KvmIdRoute() {
|
||||||
|
|
||||||
if (resp.method === "keysDownState") {
|
if (resp.method === "keysDownState") {
|
||||||
const downState = resp.params as KeysDownState;
|
const downState = resp.params as KeysDownState;
|
||||||
|
console.debug("Setting key down state:", downState);
|
||||||
if (downState) {
|
setKeysDownState(downState);
|
||||||
console.debug("Setting key down state:", downState);
|
setkeyPressReportApiAvailable(true); // if they returned a keyDownState, we know they also support keyPressReport
|
||||||
setKeysDownState(downState);
|
|
||||||
setkeyPressReportApiAvailable(true); // if they returned a keyDownState, we know they also support keyPressReport
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.method === "otaState") {
|
if (resp.method === "otaState") {
|
||||||
const otaState = resp.params as OtaState;
|
const otaState = resp.params as OtaState;
|
||||||
|
console.debug("Setting OTA state", otaState);
|
||||||
setOtaState(otaState);
|
setOtaState(otaState);
|
||||||
|
|
||||||
if (otaState.updating === true) {
|
if (otaState.updating === true) {
|
||||||
|
@ -654,9 +656,12 @@ export default function KvmIdRoute() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (rpcDataChannel?.readyState !== "open") return;
|
if (rpcDataChannel?.readyState !== "open") return;
|
||||||
|
console.log("Requesting video state");
|
||||||
send("getVideoState", {}, (resp: JsonRpcResponse) => {
|
send("getVideoState", {}, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) return;
|
if ("error" in resp) return;
|
||||||
setHdmiState(resp.result as Parameters<VideoState["setHdmiState"]>[0]);
|
const hdmiState = resp.result as Parameters<VideoState["setHdmiState"]>[0];
|
||||||
|
console.debug("Setting HDMI state", hdmiState);
|
||||||
|
setHdmiState(hdmiState);
|
||||||
});
|
});
|
||||||
}, [rpcDataChannel?.readyState, send, setHdmiState]);
|
}, [rpcDataChannel?.readyState, send, setHdmiState]);
|
||||||
|
|
||||||
|
@ -672,12 +677,10 @@ export default function KvmIdRoute() {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
console.error("Failed to get keyboard led state", resp.error);
|
console.error("Failed to get keyboard led state", resp.error);
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
const ledState = resp.result as KeyboardLedState;
|
const ledState = resp.result as KeyboardLedState;
|
||||||
|
console.debug("Keyboard led state: ", ledState);
|
||||||
if (ledState) {
|
setKeyboardLedState(ledState);
|
||||||
console.debug("Keyboard led state: ", resp.result);
|
|
||||||
setKeyboardLedState(resp.result as KeyboardLedState);
|
|
||||||
}
|
}
|
||||||
setNeedLedState(false);
|
setNeedLedState(false);
|
||||||
});
|
});
|
||||||
|
@ -696,19 +699,16 @@ export default function KvmIdRoute() {
|
||||||
// -32601 means the method is not supported
|
// -32601 means the method is not supported
|
||||||
if (resp.error.code === -32601) {
|
if (resp.error.code === -32601) {
|
||||||
// if we don't support key down state, we know key press is also not available
|
// if we don't support key down state, we know key press is also not available
|
||||||
console.error("Failed to get key down state, switching to old-school", resp.error);
|
console.warn("Failed to get key down state, switching to old-school", resp.error);
|
||||||
setkeyPressReportApiAvailable(false);
|
setkeyPressReportApiAvailable(false);
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to get key down state", resp.error);
|
console.error("Failed to get key down state", resp.error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const downState = resp.result as KeysDownState;
|
const downState = resp.result as KeysDownState;
|
||||||
|
console.debug("Keyboard key down state", downState);
|
||||||
if (downState) {
|
setKeysDownState(downState);
|
||||||
console.debug("Keyboard key down state", downState);
|
setkeyPressReportApiAvailable(true); // if they returned a keyDownState, we know they also support keyPressReport
|
||||||
setKeysDownState(downState);
|
|
||||||
setkeyPressReportApiAvailable(true); // if they returned a keyDownState, we know they also support keyPressReport
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setNeedKeyDownState(false);
|
setNeedKeyDownState(false);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue