fix keyboard capturing to limit it a bit + default to canvas mode for everything

This commit is contained in:
KingKDot
2026-07-20 17:19:44 -05:00
parent ed177760b3
commit a129913bb0
3 changed files with 54 additions and 14 deletions
+3 -1
View File
@@ -122,6 +122,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s
let whepClient = null; let whepClient = null;
let p2pClient = null; let p2pClient = null;
let webrtcActive = false; let webrtcActive = false;
const CANVAS_TRANSPORT_PREF_VERSION = 1;
function getWebrtcMode() { function getWebrtcMode() {
return webrtcMode ? String(webrtcMode.value || "off") : "off"; return webrtcMode ? String(webrtcMode.value || "off") : "off";
@@ -178,7 +179,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s
savedDisplay = Number.isFinite(Number(settings.display)) ? Number(settings.display) : savedDisplay; savedDisplay = Number.isFinite(Number(settings.display)) ? Number(settings.display) : savedDisplay;
setSelectValue(backstageResolutionSelect, settings.resolution); setSelectValue(backstageResolutionSelect, settings.resolution);
setSelectValue(targetFpsSelect, settings.targetFps); setSelectValue(targetFpsSelect, settings.targetFps);
setSelectValue(webrtcMode, settings.webrtcMode); setSelectValue(webrtcMode, settings.transportPreferenceVersion === CANVAS_TRANSPORT_PREF_VERSION ? settings.webrtcMode : "off");
if (qualitySlider && settings.quality !== undefined) qualitySlider.value = String(settings.quality); if (qualitySlider && settings.quality !== undefined) qualitySlider.value = String(settings.quality);
if (mouseCtrl && typeof settings.mouse === "boolean") mouseCtrl.checked = settings.mouse; if (mouseCtrl && typeof settings.mouse === "boolean") mouseCtrl.checked = settings.mouse;
if (kbdCtrl && typeof settings.keyboard === "boolean") kbdCtrl.checked = settings.keyboard; if (kbdCtrl && typeof settings.keyboard === "boolean") kbdCtrl.checked = settings.keyboard;
@@ -206,6 +207,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s
quality: Number(qualitySlider?.value || 90), quality: Number(qualitySlider?.value || 90),
preferH264: !!prefersH264, preferH264: !!prefersH264,
webrtcMode: getWebrtcMode(), webrtcMode: getWebrtcMode(),
transportPreferenceVersion: CANVAS_TRANSPORT_PREF_VERSION,
mouse: !!mouseCtrl?.checked, mouse: !!mouseCtrl?.checked,
keyboard: !!kbdCtrl?.checked, keyboard: !!kbdCtrl?.checked,
clipboardSync: !!clipboardSyncCtrl?.checked, clipboardSync: !!clipboardSyncCtrl?.checked,
@@ -11,6 +11,12 @@ function isEditableTarget(container) {
return false; return false;
} }
function hasCaptureFocus(container) {
if (!container) return true;
const el = document.activeElement;
return !!el && (el === container || container.contains(el));
}
const SYSTEM_KEY_CODES = [ const SYSTEM_KEY_CODES = [
"Escape", "Escape",
"Tab", "Tab",
@@ -43,9 +49,39 @@ export function createKeyboardCapture({ container, sendKeyDown, sendKeyUp, onTex
let enabled = false; let enabled = false;
const pressed = new Map(); const pressed = new Map();
function setCaptureFlag(active) {
if (active) window[CAPTURE_FLAG] = true;
else delete window[CAPTURE_FLAG];
}
function refreshCaptureState() {
if (!enabled) {
setCaptureFlag(false);
return;
}
if (hasCaptureFocus(container)) {
setCaptureFlag(true);
tryLock();
} else {
setCaptureFlag(false);
tryUnlock();
releaseAll();
}
}
function shouldCapture() {
if (!enabled) return false;
if (!hasCaptureFocus(container)) {
refreshCaptureState();
return false;
}
if (isEditableTarget(container)) return false;
setCaptureFlag(true);
return true;
}
function handleDown(e) { function handleDown(e) {
if (!enabled) return; if (!shouldCapture()) return;
if (isEditableTarget(container)) return;
pressed.set(e.code || e.key, { key: e.key, code: e.code }); pressed.set(e.code || e.key, { key: e.key, code: e.code });
if (onTextInput && !e.ctrlKey && !e.metaKey && !e.altKey && typeof e.key === "string" && e.key.length === 1) { if (onTextInput && !e.ctrlKey && !e.metaKey && !e.altKey && typeof e.key === "string" && e.key.length === 1) {
onTextInput(e); onTextInput(e);
@@ -57,8 +93,7 @@ export function createKeyboardCapture({ container, sendKeyDown, sendKeyUp, onTex
} }
function handleUp(e) { function handleUp(e) {
if (!enabled) return; if (!shouldCapture()) return;
if (isEditableTarget(container)) return;
pressed.delete(e.code || e.key); pressed.delete(e.code || e.key);
if (onTextInput && !e.ctrlKey && !e.metaKey && !e.altKey && typeof e.key === "string" && e.key.length === 1) { if (onTextInput && !e.ctrlKey && !e.metaKey && !e.altKey && typeof e.key === "string" && e.key.length === 1) {
e.preventDefault(); e.preventDefault();
@@ -77,34 +112,35 @@ export function createKeyboardCapture({ container, sendKeyDown, sendKeyUp, onTex
function handleBlur() { releaseAll(); } function handleBlur() { releaseAll(); }
function handleVisibility() { if (document.visibilityState === "hidden") releaseAll(); } function handleVisibility() { if (document.visibilityState === "hidden") releaseAll(); }
function handleContextMenu(e) { if (enabled && !isEditableTarget(container)) e.preventDefault(); } function handleContextMenu(e) { if (shouldCapture()) e.preventDefault(); }
function handleFullscreenChange() { function handleFocusChange() { setTimeout(refreshCaptureState, 0); }
if (enabled) tryLock(); function handleFullscreenChange() { refreshCaptureState(); }
}
return { return {
isEnabled() { return enabled; }, isEnabled() { return enabled; },
enable() { enable() {
if (enabled) return; if (enabled) return;
enabled = true; enabled = true;
window[CAPTURE_FLAG] = true; refreshCaptureState();
window.addEventListener("keydown", handleDown, { capture: true }); window.addEventListener("keydown", handleDown, { capture: true });
window.addEventListener("keyup", handleUp, { capture: true }); window.addEventListener("keyup", handleUp, { capture: true });
window.addEventListener("blur", handleBlur); window.addEventListener("blur", handleBlur);
window.addEventListener("contextmenu", handleContextMenu, { capture: true }); window.addEventListener("contextmenu", handleContextMenu, { capture: true });
document.addEventListener("focusin", handleFocusChange);
document.addEventListener("focusout", handleFocusChange);
document.addEventListener("visibilitychange", handleVisibility); document.addEventListener("visibilitychange", handleVisibility);
document.addEventListener("fullscreenchange", handleFullscreenChange); document.addEventListener("fullscreenchange", handleFullscreenChange);
tryLock();
if (container) try { container.focus({ preventScroll: true }); } catch {}
}, },
disable() { disable() {
if (!enabled) return; if (!enabled) return;
enabled = false; enabled = false;
delete window[CAPTURE_FLAG]; setCaptureFlag(false);
window.removeEventListener("keydown", handleDown, { capture: true }); window.removeEventListener("keydown", handleDown, { capture: true });
window.removeEventListener("keyup", handleUp, { capture: true }); window.removeEventListener("keyup", handleUp, { capture: true });
window.removeEventListener("blur", handleBlur); window.removeEventListener("blur", handleBlur);
window.removeEventListener("contextmenu", handleContextMenu, { capture: true }); window.removeEventListener("contextmenu", handleContextMenu, { capture: true });
document.removeEventListener("focusin", handleFocusChange);
document.removeEventListener("focusout", handleFocusChange);
document.removeEventListener("visibilitychange", handleVisibility); document.removeEventListener("visibilitychange", handleVisibility);
document.removeEventListener("fullscreenchange", handleFullscreenChange); document.removeEventListener("fullscreenchange", handleFullscreenChange);
tryUnlock(); tryUnlock();
@@ -124,6 +124,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s
let lastRemoteCursor = null; let lastRemoteCursor = null;
let remoteCursorShape = null; let remoteCursorShape = null;
let remoteCursorImageUrl = ""; let remoteCursorImageUrl = "";
const CANVAS_TRANSPORT_PREF_VERSION = 1;
function getWebrtcMode() { function getWebrtcMode() {
return webrtcMode ? String(webrtcMode.value || "off") : "off"; return webrtcMode ? String(webrtcMode.value || "off") : "off";
} }
@@ -483,7 +484,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s
: savedStreamProfile); : savedStreamProfile);
setSelectValue(streamProfileSelect, savedStreamProfile); setSelectValue(streamProfileSelect, savedStreamProfile);
setSelectValue(bitrateSelect, settings.bitrateMbps); setSelectValue(bitrateSelect, settings.bitrateMbps);
setSelectValue(webrtcMode, settings.webrtcMode); setSelectValue(webrtcMode, settings.transportPreferenceVersion === CANVAS_TRANSPORT_PREF_VERSION ? settings.webrtcMode : "off");
setSelectValue(audioTransport, settings.audioTransport); setSelectValue(audioTransport, settings.audioTransport);
setSelectValue(recordMode, settings.recordMode); setSelectValue(recordMode, settings.recordMode);
setSelectValue(recordFps, settings.recordFps); setSelectValue(recordFps, settings.recordFps);
@@ -519,6 +520,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s
bitrateMbps: Number(bitrateSelect?.value || 0), bitrateMbps: Number(bitrateSelect?.value || 0),
preferH264: !!prefersH264, preferH264: !!prefersH264,
webrtcMode: getWebrtcMode(), webrtcMode: getWebrtcMode(),
transportPreferenceVersion: CANVAS_TRANSPORT_PREF_VERSION,
mouse: !!mouseCtrl?.checked, mouse: !!mouseCtrl?.checked,
keyboard: !!kbdCtrl?.checked, keyboard: !!kbdCtrl?.checked,
cursor: !!cursorCtrl?.checked, cursor: !!cursorCtrl?.checked,