77 "context"
88 "fmt"
99 "io"
10+ "time"
1011
1112 "github.com/erikgeiser/coninput"
1213 localereader "github.com/mattn/go-localereader"
@@ -25,14 +26,10 @@ func readConInputs(ctx context.Context, msgsch chan<- Msg, con *conInputReader)
2526 var ps coninput.ButtonState // keep track of previous mouse state
2627 var ws coninput.WindowBufferSizeEventRecord // keep track of the last window size event
2728 for {
28- events , err := coninput . ReadNConsoleInputs (con . conin , 16 )
29+ events , err := peekAndReadConsInput (con )
2930 if err != nil {
30- if con .isCanceled () {
31- return cancelreader .ErrCanceled
32- }
33- return fmt .Errorf ("read coninput events: %w" , err )
31+ return err
3432 }
35-
3633 for _ , event := range events {
3734 var msgs []Msg
3835 switch e := event .Unwrap ().(type ) {
@@ -94,6 +91,50 @@ func readConInputs(ctx context.Context, msgsch chan<- Msg, con *conInputReader)
9491 }
9592}
9693
94+ // Peek for new input in a tight loop and then read the input.
95+ // windows.CancelIo* does not work reliably so peek first and only use the data if
96+ // the console input is not cancelled.
97+ func peekAndReadConsInput (con * conInputReader ) ([]coninput.InputRecord , error ) {
98+ events , err := peekConsInput (con )
99+ if err != nil {
100+ return events , err
101+ }
102+ events , err = coninput .ReadNConsoleInputs (con .conin , intToUint32OrDie (len (events )))
103+ if con .isCanceled () {
104+ return events , cancelreader .ErrCanceled
105+ }
106+ if err != nil {
107+ return events , fmt .Errorf ("read coninput events: %w" , err )
108+ }
109+ return events , nil
110+ }
111+
112+ // Convert i to unit32 or panic if it cannot be converted. Check satisifes lint G115.
113+ func intToUint32OrDie (i int ) uint32 {
114+ if i < 0 {
115+ panic ("cannot convert numEvents " + fmt .Sprint (i ) + " to uint32" )
116+ }
117+ return uint32 (i )
118+ }
119+
120+ // Keeps peeking until there is data or the input is cancelled.
121+ func peekConsInput (con * conInputReader ) ([]coninput.InputRecord , error ) {
122+ for {
123+ events , err := coninput .PeekNConsoleInputs (con .conin , 16 )
124+ if con .isCanceled () {
125+ return events , cancelreader .ErrCanceled
126+ }
127+ if err != nil {
128+ return events , fmt .Errorf ("peek coninput events: %w" , err )
129+ }
130+ if len (events ) > 0 {
131+ return events , nil
132+ }
133+ // Sleep for a bit to avoid busy waiting.
134+ time .Sleep (16 * time .Millisecond )
135+ }
136+ }
137+
97138func mouseEventButton (p , s coninput.ButtonState ) (button MouseButton , action MouseAction ) {
98139 btn := p ^ s
99140 action = MouseActionPress
0 commit comments