Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions ui/src/components/WebRTCVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
code = "Henkan";
} else if (code === "NonConvert") {
code = "Muhenkan";
// Microsoft IME fix
} else if (key === "Shift" && code === "") {
code = "ShiftRight"
}

return code;
Expand Down Expand Up @@ -303,6 +306,13 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
altGrLoopRef.current = true;
lastKeyDownRef.current = null;
}

// Microsoft IME fix:
// Effective keydown events are consumed by IME (reported as "Process"),
// so we handle the full press/release cycle in the keyup handler instead.
if (["Zenkaku", "Hankaku", "ZenkakuHankaku"].includes(e.key)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to ignore them here only on isWindowClient condition?

Copy link
Contributor Author

@0141KoppePan 0141KoppePan Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IME keydown bypass is currently placed inside the isWindowsClient condition, so this logic runs only on Windows.
Since Windows is the only platform I can reliably test at the moment, I would prefer to keep this PR Windows-only for now.

However, I also have access to Linux and macOS, so I can follow up with a separate PR to address non-Windows IME behavior as well.

For reference, here is what I have observed so far:

  • On Linux (Ubuntu 25.10 + Mozc Japanese input method), pressing the Hankaku/Zenkaku key emits only a keyup event.
  • On macOS, the Hankaku/Zenkaku key is not used. Instead, 英数 (key: Eisu, code: Lang2) and かな (key: KanjiMode, code: Lang1) are used — and both produce only keyup events as well.

Figure 1. Linux(ubuntu + mozc) Keyboard Event
image

Figure 2. macOS Keyboard Event
image

Figure 3. Mac Keyboard(JIS) 英数 and かな keys
image

I will verify these behaviors more thoroughly and submit an additional PR to handle Linux and macOS explicitly.

return;
}
}

// When pressing the meta key + another key, the key will never trigger a keyup
Expand Down Expand Up @@ -342,21 +352,29 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
return;
}

// On Windows, handle ControlLeft specially to preserve FIFO semantics with AltGr buffering.
if (isWindowsClient && hidKey === keys.ControlLeft) {
if (isWindowsClient) {
// On Windows, handle ControlLeft specially to preserve FIFO semantics with AltGr buffering.
if (hidKey === keys.ControlLeft) {
// Synthetic AltGr ControlLeft: never sent a down, swallow the release as well.
if (altGrLoopRef.current) {
altGrLoopRef.current = false;
return;
}

// Synthetic AltGr ControlLeft: never sent a down, swallow the release as well.
if (altGrLoopRef.current) {
altGrLoopRef.current = false;
return;
}
// Very fast real Ctrl tap: flush the pending down before the up.
if (lastKeyDownRef.current?.hidKey === keys.ControlLeft) {
handleKeyPress(keys.ControlLeft, true);
}

// Very fast real Ctrl tap: flush the pending down before the up.
if (lastKeyDownRef.current?.hidKey === keys.ControlLeft) {
handleKeyPress(keys.ControlLeft, true);
lastKeyDownRef.current = null;
}

lastKeyDownRef.current = null;
// Microsoft IME fix:
// Synthesize the missing keydown event to ensure a complete key press cycle.
if (["Zenkaku", "Hankaku", "ZenkakuHankaku"].includes(e.key)) {
console.debug(`Synthesizing missed key down for IME key: ${e.key}`);
handleKeyPress(hidKey, true);
}
}

console.debug(`Key up: ${hidKey}`);
Expand Down