diff --git a/packages/tui/src/terminal-win32.bun.ts b/packages/tui/src/terminal-win32.bun.ts index 1aaa80aecd69..d62dcd82810a 100644 --- a/packages/tui/src/terminal-win32.bun.ts +++ b/packages/tui/src/terminal-win32.bun.ts @@ -1,5 +1,4 @@ -import { dlopen, ptr } from "bun:ffi" -import type { ReadStream } from "node:tty" +import { dlopen } from "bun:ffi" const STD_INPUT_HANDLE = -10 const ENABLE_PROCESSED_INPUT = 0x0001 @@ -7,7 +6,7 @@ const ENABLE_PROCESSED_INPUT = 0x0001 const kernel = () => dlopen("kernel32.dll", { GetStdHandle: { args: ["i32"], returns: "ptr" }, - GetConsoleMode: { args: ["ptr", "ptr"], returns: "i32" }, + GetConsoleMode: { args: ["ptr", "buffer"], returns: "i32" }, SetConsoleMode: { args: ["ptr", "u32"], returns: "i32" }, FlushConsoleInputBuffer: { args: ["ptr"], returns: "i32" }, }) @@ -34,7 +33,7 @@ export function win32DisableProcessedInput() { const handle = k32!.symbols.GetStdHandle(STD_INPUT_HANDLE) const buf = new Uint32Array(1) - if (k32!.symbols.GetConsoleMode(handle, ptr(buf)) === 0) return + if (k32!.symbols.GetConsoleMode(handle, buf) === 0) return const mode = buf[0]! if ((mode & ENABLE_PROCESSED_INPUT) === 0) return @@ -52,79 +51,3 @@ export function win32FlushInputBuffer() { const handle = k32!.symbols.GetStdHandle(STD_INPUT_HANDLE) k32!.symbols.FlushConsoleInputBuffer(handle) } - -let unhook: (() => void) | undefined - -/** - * Keep ENABLE_PROCESSED_INPUT disabled. - * - * On Windows, Ctrl+C becomes a CTRL_C_EVENT (instead of stdin input) when - * ENABLE_PROCESSED_INPUT is set. Various runtimes can re-apply console modes - * (sometimes on a later tick), and the flag is console-global, not per-process. - * - * We combine: - * - A `setRawMode(...)` hook to re-clear after known raw-mode toggles. - * - A low-frequency poll as a backstop for native/external mode changes. - */ -export function win32InstallCtrlCGuard() { - if (process.platform !== "win32") return - if (!process.stdin.isTTY) return - if (!load()) return - if (unhook) return unhook - - const stdin = process.stdin as ReadStream - const original = stdin.setRawMode - - const handle = k32!.symbols.GetStdHandle(STD_INPUT_HANDLE) - const buf = new Uint32Array(1) - - if (k32!.symbols.GetConsoleMode(handle, ptr(buf)) === 0) return - const initial = buf[0]! - - const enforce = () => { - if (k32!.symbols.GetConsoleMode(handle, ptr(buf)) === 0) return - const mode = buf[0]! - if ((mode & ENABLE_PROCESSED_INPUT) === 0) return - k32!.symbols.SetConsoleMode(handle, mode & ~ENABLE_PROCESSED_INPUT) - } - - // Some runtimes can re-apply console modes on the next tick; enforce twice. - const later = () => { - enforce() - setImmediate(enforce) - } - - let wrapped: ReadStream["setRawMode"] | undefined - - if (typeof original === "function") { - wrapped = (mode: boolean) => { - const result = original.call(stdin, mode) - later() - return result - } - - stdin.setRawMode = wrapped - } - - // Ensure it's cleared immediately too (covers any earlier mode changes). - later() - - const interval = setInterval(enforce, 100) - interval.unref() - - let done = false - unhook = () => { - if (done) return - done = true - - clearInterval(interval) - if (wrapped && stdin.setRawMode === wrapped) { - stdin.setRawMode = original - } - - k32!.symbols.SetConsoleMode(handle, initial) - unhook = undefined - } - - return unhook -} diff --git a/packages/tui/src/terminal-win32.node.ts b/packages/tui/src/terminal-win32.node.ts index 11f2f9c9930b..b0146636c4f0 100644 --- a/packages/tui/src/terminal-win32.node.ts +++ b/packages/tui/src/terminal-win32.node.ts @@ -1,5 +1,4 @@ import { dlopen } from "node:ffi" -import type { ReadStream } from "node:tty" const STD_INPUT_HANDLE = -10 const ENABLE_PROCESSED_INPUT = 0x0001 @@ -38,40 +37,3 @@ export function win32FlushInputBuffer() { if (process.platform !== "win32" || !process.stdin.isTTY || !load()) return k32!.FlushConsoleInputBuffer(k32!.GetStdHandle(STD_INPUT_HANDLE)) } - -let unhook: (() => void) | undefined - -export function win32InstallCtrlCGuard() { - if (process.platform !== "win32" || !process.stdin.isTTY || !load() || unhook) return unhook - const stdin = process.stdin as ReadStream - const original = stdin.setRawMode - const handle = k32!.GetStdHandle(STD_INPUT_HANDLE) - const buffer = new Uint32Array(1) - if (k32!.GetConsoleMode(handle, buffer) === 0) return - const initial = buffer[0]! - const enforce = () => { - if (k32!.GetConsoleMode(handle, buffer) === 0) return - const mode = buffer[0]! - if ((mode & ENABLE_PROCESSED_INPUT) !== 0) k32!.SetConsoleMode(handle, mode & ~ENABLE_PROCESSED_INPUT) - } - const later = () => { - enforce() - setImmediate(enforce) - } - const wrapped: ReadStream["setRawMode"] = (mode) => { - const result = original.call(stdin, mode) - later() - return result - } - stdin.setRawMode = wrapped - later() - const interval = setInterval(enforce, 100) - interval.unref() - unhook = () => { - clearInterval(interval) - if (stdin.setRawMode === wrapped) stdin.setRawMode = original - k32!.SetConsoleMode(handle, initial) - unhook = undefined - } - return unhook -} diff --git a/packages/tui/src/terminal-win32.ts b/packages/tui/src/terminal-win32.ts index e58c0e61489a..2b9a1da69b9a 100644 --- a/packages/tui/src/terminal-win32.ts +++ b/packages/tui/src/terminal-win32.ts @@ -1 +1 @@ -export { win32DisableProcessedInput, win32FlushInputBuffer, win32InstallCtrlCGuard } from "#terminal-win32" +export { win32DisableProcessedInput, win32FlushInputBuffer } from "#terminal-win32"