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
52 changes: 49 additions & 3 deletions cmd/dbc/drain_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,52 @@

package main

// no-op on Windows since I haven't reproduced this issue on a Windows terminal.
// See drain_unix.go for a description of the issue and the fix.
func suppressTerminalProbeResponses() {}
import (
"os"
"time"

"golang.org/x/sys/windows"
)

// Work around https://github.com/columnar-tech/dbc/issues/351
//
// suppressTerminalProbeResponses prevents BubbleTea v2's capability probe
// responses from appearing as garbled output in the shell. See drain_unix.go
// for more detail on the problem.
//
// Unix restores raw mode and drains the tty with non-blocking reads. Windows
// has no tty to drain: the fix is to discard the pending console input records
// with FlushConsoleInputBuffer before we exit.
//
// Note this also discards genuine type-ahead the user entered while the command
// was running. That matches the Unix drain, and is the accepted tradeoff: we
// cannot distinguish a user's keystrokes from a terminal's DECRPM reply without
// parsing the input records, and leaking escape sequences into the shell prompt
// is the worse failure.
func suppressTerminalProbeResponses() {
h := windows.Handle(os.Stdin.Fd())

// GetConsoleMode fails when stdin is not a console (piped or redirected
// input). In that case nothing was probed and nothing is buffered.
var mode uint32
if err := windows.GetConsoleMode(h, &mode); err != nil {
return
}

// Suppress echo while we wait, so a reply that arrives during the sleep is
// not written to the screen by the console host before we can flush it.
// This is belt-and-suspenders — the flush below is what actually fixes the
// reported symptom — so a failure here is not worth abandoning the flush.
if quiet := mode &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT); quiet != mode {
if err := windows.SetConsoleMode(h, quiet); err == nil {
defer windows.SetConsoleMode(h, mode) //nolint:errcheck
}
}

// Give in-flight responses time to land in the input buffer before we
// discard it. The local round-trip is typically <5ms; 50ms gives headroom.
// Matches the Unix implementation in drain_unix.go.
time.Sleep(50 * time.Millisecond)

windows.FlushConsoleInputBuffer(h) //nolint:errcheck
}