pull/20768/merge
Kiwi 2026-04-08 06:19:09 +00:00 committed by GitHub
commit 2f7b938b78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 42 additions and 26 deletions

View File

@ -16,7 +16,7 @@ import {
on,
onCleanup,
} from "solid-js"
import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
import { win32DisableProcessedInput, win32Init, win32InstallCtrlCGuard } from "./win32"
import { Flag } from "@/flag/flag"
import semver from "semver"
import { DialogProvider, useDialog } from "@tui/ui/dialog"
@ -176,6 +176,7 @@ export function tui(input: {
}) {
// promise to prevent immediate exit
return new Promise<void>(async (resolve) => {
await win32Init()
const unguard = win32InstallCtrlCGuard()
win32DisableProcessedInput()

View File

@ -1,24 +1,34 @@
import { dlopen, ptr } from "bun:ffi"
const STD_INPUT_HANDLE = -10
const ENABLE_PROCESSED_INPUT = 0x0001
const kernel = () =>
dlopen("kernel32.dll", {
GetStdHandle: { args: ["i32"], returns: "ptr" },
GetConsoleMode: { args: ["ptr", "ptr"], returns: "i32" },
SetConsoleMode: { args: ["ptr", "u32"], returns: "i32" },
FlushConsoleInputBuffer: { args: ["ptr"], returns: "i32" },
})
let mod: typeof import("bun:ffi") | undefined
type K32 = {
GetStdHandle: (n: number) => bigint | number
GetConsoleMode: (h: bigint | number, mode: unknown) => number
SetConsoleMode: (h: bigint | number, mode: number) => number
FlushConsoleInputBuffer: (h: bigint | number) => number
}
let k32: ReturnType<typeof kernel> | undefined
let k32: K32 | undefined
let ready: boolean | undefined
function load() {
type Ptr = typeof import("bun:ffi").ptr
export async function win32Init() {
if (process.platform !== "win32") return false
if (ready !== undefined) return ready
try {
k32 ??= kernel()
mod = await import("bun:ffi")
k32 = (mod.dlopen("kernel32.dll", {
GetStdHandle: { args: ["i32"], returns: "ptr" },
GetConsoleMode: { args: ["ptr", "ptr"], returns: "i32" },
SetConsoleMode: { args: ["ptr", "u32"], returns: "i32" },
FlushConsoleInputBuffer: { args: ["ptr"], returns: "i32" },
}).symbols as unknown as K32)
ready = true
return true
} catch {
ready = false
return false
}
}
@ -29,15 +39,17 @@ function load() {
export function win32DisableProcessedInput() {
if (process.platform !== "win32") return
if (!process.stdin.isTTY) return
if (!load()) return
if (!k32 || !mod) return
const ptr = mod.ptr as Ptr
const handle = k32!.symbols.GetStdHandle(STD_INPUT_HANDLE)
const lib = k32
const handle = lib.GetStdHandle(STD_INPUT_HANDLE)
const buf = new Uint32Array(1)
if (k32!.symbols.GetConsoleMode(handle, ptr(buf)) === 0) return
if (lib.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)
lib.SetConsoleMode(handle, mode & ~ENABLE_PROCESSED_INPUT)
}
/**
@ -46,10 +58,11 @@ export function win32DisableProcessedInput() {
export function win32FlushInputBuffer() {
if (process.platform !== "win32") return
if (!process.stdin.isTTY) return
if (!load()) return
if (!k32) return
const handle = k32!.symbols.GetStdHandle(STD_INPUT_HANDLE)
k32!.symbols.FlushConsoleInputBuffer(handle)
const lib = k32
const handle = lib.GetStdHandle(STD_INPUT_HANDLE)
lib.FlushConsoleInputBuffer(handle)
}
let unhook: (() => void) | undefined
@ -68,23 +81,25 @@ let unhook: (() => void) | undefined
export function win32InstallCtrlCGuard() {
if (process.platform !== "win32") return
if (!process.stdin.isTTY) return
if (!load()) return
if (!k32 || !mod) return
if (unhook) return unhook
const ptr = mod.ptr as Ptr
const lib = k32
const stdin = process.stdin as any
const original = stdin.setRawMode
const handle = k32!.symbols.GetStdHandle(STD_INPUT_HANDLE)
const handle = lib.GetStdHandle(STD_INPUT_HANDLE)
const buf = new Uint32Array(1)
if (k32!.symbols.GetConsoleMode(handle, ptr(buf)) === 0) return
if (lib.GetConsoleMode(handle, ptr(buf)) === 0) return
const initial = buf[0]!
const enforce = () => {
if (k32!.symbols.GetConsoleMode(handle, ptr(buf)) === 0) return
if (lib.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)
lib.SetConsoleMode(handle, mode & ~ENABLE_PROCESSED_INPUT)
}
// Some runtimes can re-apply console modes on the next tick; enforce twice.
@ -121,7 +136,7 @@ export function win32InstallCtrlCGuard() {
stdin.setRawMode = original
}
k32!.symbols.SetConsoleMode(handle, initial)
lib.SetConsoleMode(handle, initial)
unhook = undefined
}