115 lines
3.6 KiB
JavaScript
115 lines
3.6 KiB
JavaScript
export const CAPTURE_FLAG = "__overlordCapturingKeyboard";
|
|
|
|
function isEditableTarget(container) {
|
|
const el = document.activeElement;
|
|
if (!el) return false;
|
|
if (el === document.body) return false;
|
|
if (container && el === container) return false;
|
|
const tag = el.tagName;
|
|
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
if (el.isContentEditable) return true;
|
|
return false;
|
|
}
|
|
|
|
const SYSTEM_KEY_CODES = [
|
|
"Escape",
|
|
"Tab",
|
|
"ContextMenu",
|
|
"MetaLeft", "MetaRight", "OSLeft", "OSRight",
|
|
"AltLeft", "AltRight",
|
|
"ControlLeft", "ControlRight",
|
|
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
|
|
"KeyT", "KeyN", "KeyW", "KeyL", "KeyR", "KeyS", "KeyF", "KeyP", "KeyJ", "KeyH",
|
|
"PrintScreen",
|
|
];
|
|
|
|
function tryLock() {
|
|
try {
|
|
if (navigator.keyboard && navigator.keyboard.lock) {
|
|
navigator.keyboard.lock(SYSTEM_KEY_CODES);
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
function tryUnlock() {
|
|
try {
|
|
if (navigator.keyboard && navigator.keyboard.unlock) {
|
|
navigator.keyboard.unlock();
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
export function createKeyboardCapture({ container, sendKeyDown, sendKeyUp, onTextInput }) {
|
|
let enabled = false;
|
|
const pressed = new Map();
|
|
|
|
function handleDown(e) {
|
|
if (!enabled) return;
|
|
if (isEditableTarget(container)) return;
|
|
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) {
|
|
onTextInput(e);
|
|
} else {
|
|
sendKeyDown(e);
|
|
}
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
|
|
function handleUp(e) {
|
|
if (!enabled) return;
|
|
if (isEditableTarget(container)) return;
|
|
pressed.delete(e.code || e.key);
|
|
if (onTextInput && !e.ctrlKey && !e.metaKey && !e.altKey && typeof e.key === "string" && e.key.length === 1) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return;
|
|
}
|
|
sendKeyUp(e);
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
|
|
function releaseAll() {
|
|
for (const v of pressed.values()) sendKeyUp(v);
|
|
pressed.clear();
|
|
}
|
|
|
|
function handleBlur() { releaseAll(); }
|
|
function handleVisibility() { if (document.visibilityState === "hidden") releaseAll(); }
|
|
function handleContextMenu(e) { if (enabled && !isEditableTarget(container)) e.preventDefault(); }
|
|
function handleFullscreenChange() {
|
|
if (enabled) tryLock();
|
|
}
|
|
|
|
return {
|
|
isEnabled() { return enabled; },
|
|
enable() {
|
|
if (enabled) return;
|
|
enabled = true;
|
|
window[CAPTURE_FLAG] = true;
|
|
window.addEventListener("keydown", handleDown, { capture: true });
|
|
window.addEventListener("keyup", handleUp, { capture: true });
|
|
window.addEventListener("blur", handleBlur);
|
|
window.addEventListener("contextmenu", handleContextMenu, { capture: true });
|
|
document.addEventListener("visibilitychange", handleVisibility);
|
|
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
|
tryLock();
|
|
if (container) try { container.focus({ preventScroll: true }); } catch {}
|
|
},
|
|
disable() {
|
|
if (!enabled) return;
|
|
enabled = false;
|
|
delete window[CAPTURE_FLAG];
|
|
window.removeEventListener("keydown", handleDown, { capture: true });
|
|
window.removeEventListener("keyup", handleUp, { capture: true });
|
|
window.removeEventListener("blur", handleBlur);
|
|
window.removeEventListener("contextmenu", handleContextMenu, { capture: true });
|
|
document.removeEventListener("visibilitychange", handleVisibility);
|
|
document.removeEventListener("fullscreenchange", handleFullscreenChange);
|
|
tryUnlock();
|
|
releaseAll();
|
|
},
|
|
};
|
|
}
|