diff --git a/src/main/hook.ts b/src/main/hook.ts index a12dae0b0..e36f9577e 100644 --- a/src/main/hook.ts +++ b/src/main/hook.ts @@ -25,6 +25,7 @@ let impostorRadioShortcut: K | undefined; let keySender: WebContents | undefined; let pushToTalkHeld = false; let impostorRadioHeld = false; +const toggleGrants = new Map(); function releaseHeldKeys(): void { if (pushToTalkHeld) { @@ -43,7 +44,6 @@ function resetKeyHooks(): void { deafenShortcut = store.get('deafenShortcut', 'RControl') as K; muteShortcut = store.get('muteShortcut', 'RAlt') as K; impostorRadioShortcut = store.get('impostorRadioShortcut', 'F') as K; - keyboardWatcher.clearKeyHooks(); addKeyHandler(pushToTalkShortcut); addKeyHandler(deafenShortcut); addKeyHandler(muteShortcut); @@ -96,6 +96,12 @@ ipcMain.handle(IpcHandlerMessages.START_HOOK, async (event) => { impostorRadioHeld = true; event.sender.send(IpcRendererMessages.IMPOSTOR_RADIO, true); } + if (!toggleGrants.has(keyId)) { + toggleGrants.set(keyId, { + mute: keyCodeMatches(muteShortcut!, keyId), + deafen: keyCodeMatches(deafenShortcut!, keyId), + }); + } }); keyboardWatcher.on('keyup', (keyId: number) => { @@ -103,10 +109,12 @@ ipcMain.handle(IpcHandlerMessages.START_HOOK, async (event) => { pushToTalkHeld = false; event.sender.send(IpcRendererMessages.PUSH_TO_TALK, false); } - if (keyCodeMatches(deafenShortcut!, keyId)) { + const grant = toggleGrants.get(keyId); + toggleGrants.delete(keyId); + if (grant?.deafen) { event.sender.send(IpcRendererMessages.TOGGLE_DEAFEN); } - if (keyCodeMatches(muteShortcut!, keyId)) { + if (grant?.mute) { event.sender.send(IpcRendererMessages.TOGGLE_MUTE); } if (keyCodeMatches(impostorRadioShortcut!, keyId) && impostorRadioHeld) { diff --git a/src/renderer/settings/sections/KeybindsSection.tsx b/src/renderer/settings/sections/KeybindsSection.tsx index 69fa2e036..71c03f525 100644 --- a/src/renderer/settings/sections/KeybindsSection.tsx +++ b/src/renderer/settings/sections/KeybindsSection.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useRef, useState } from 'react'; import { TFunction } from 'i18next'; import Alert from '@mui/material/Alert'; import Box from '@mui/material/Box'; @@ -80,6 +80,8 @@ const ShortcutField: React.FC = function ({ onStopRecording, onCapture, }) { + // What the last key press captured, held until that key is released. + const pendingCapture = useRef(null); const unset = !value || value === 'Disabled'; return ( = function ({ onKeyDown={(ev) => { if (ev.key === 'Tab') return; ev.preventDefault(); - const captured = keyFromEvent(ev); + pendingCapture.current = keyFromEvent(ev) ?? null; + }} + onKeyUp={(ev) => { + if (ev.key === 'Tab') return; + ev.preventDefault(); + const captured = pendingCapture.current; + pendingCapture.current = null; if (captured) onCapture(captured); }} + onMouseUp={(ev) => { + if (!recording || ev.button <= 2) return; + ev.preventDefault(); + onCapture(`MouseButton${ev.button + 1}`); + }} onMouseDown={(ev) => { if (recording && ev.button > 2) { ev.preventDefault(); - onCapture(`MouseButton${ev.button + 1}`); return; } if (ev.button !== 0) return;