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
16 changes: 12 additions & 4 deletions terminal_reader_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"context"
"fmt"
"strings"
"time"
"unicode/utf16"

"github.com/charmbracelet/x/ansi"
Expand All @@ -31,9 +30,10 @@ func (d *TerminalReader) streamData(ctx context.Context, readc chan []byte) erro
var buf bytes.Buffer
var records []xwindows.InputRecord
var err error
peekBuf := make([]xwindows.InputRecord, readBufSize) // pre-allocate, reused across iterations
for {
for {
records, err = peekNConsoleInputs(cc.conin, readBufSize)
records, err = peekNConsoleInputsInto(cc.conin, peekBuf)
if cc.isCanceled() {
return cancelreader.ErrCanceled
}
Expand All @@ -44,8 +44,10 @@ func (d *TerminalReader) streamData(ctx context.Context, readc chan []byte) erro
break
}

// Sleep for a bit to avoid busy waiting.
time.Sleep(10 * time.Millisecond)
// Block until input is available using a kernel wait instead of
// polling with PeekConsoleInput. This eliminates CPU usage when idle.
// A 1s timeout ensures cancellation is detected promptly.
windows.WaitForSingleObject(cc.conin, 1000)
}

records, err = readNConsoleInputs(cc.conin, uint32(len(records))) //nolint:gosec
Expand Down Expand Up @@ -291,6 +293,12 @@ func peekNConsoleInputs(console windows.Handle, maxEvents uint32) ([]xwindows.In
return records[:n], err
}

// peekNConsoleInputsInto reuses a pre-allocated buffer to avoid GC pressure.
func peekNConsoleInputsInto(console windows.Handle, buf []xwindows.InputRecord) ([]xwindows.InputRecord, error) {
n, err := peekConsoleInput(console, buf)
return buf[:n], err
}

//nolint:unused
func keyEventString(vkc, sc uint16, r rune, keyDown bool, cks uint32, repeatCount uint16) string {
var s strings.Builder
Expand Down