Skip to content

Commit 0b5424d

Browse files
committed
fix: 3rd party Windows terminals double encoding win32-input-mode breaks some keys
The encoding for things like SS3 sequences used in terminals like WezTerm and Alacritty breaks the processor, since the CSI state machine changes interferes with the SS3 state. We introduce a nested state machine for this case. Note that ConEmu still seems busted when running locally, although it is interesting that using it from an SSH hosted session works well. Nonetheless, we don't recommend ConEmu for a variety of reasons.
1 parent cbb8618 commit 0b5424d

File tree

2 files changed

+32
-22
lines changed

2 files changed

+32
-22
lines changed

input.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,18 @@ type inputProcessor struct {
8080
timer *time.Timer
8181
expire time.Time
8282
l sync.Mutex
83-
reencode bool // bug with win32-input-mode on older terms
8483
encBuf []rune
8584
evch chan<- Event
8685
rows int // used for clipping mouse coordinates
8786
cols int // used for clipping mouse coordinates
87+
nested *inputProcessor
8888
}
8989

9090
func (ip *inputProcessor) SetSize(w, h int) {
91+
if ip.nested != nil {
92+
ip.nested.SetSize(w, h)
93+
return
94+
}
9195
go func() {
9296
ip.l.Lock()
9397
ip.rows = h
@@ -401,7 +405,7 @@ func (ip *inputProcessor) scan() {
401405
case '\x1b':
402406
// escape.. pending
403407
ip.state = inpStateEsc
404-
if len(ip.buf) == 0 {
408+
if len(ip.buf) == 0 && ip.nested == nil {
405409
ip.expire = time.Now().Add(time.Millisecond * 50)
406410
ip.timer = time.AfterFunc(time.Millisecond*60, ip.escTimeout)
407411
}
@@ -671,21 +675,16 @@ func (ip *inputProcessor) handleWinKey(P []int) {
671675
// key up event ignore ignore
672676
return
673677
}
674-
if P[0] == 0 {
675-
if P[2] == 27 && !ip.reencode {
676-
ip.reencode = true
678+
if P[0] == 0 && P[2] == 27 && ip.nested == nil {
679+
ip.nested = &inputProcessor{
680+
evch: ip.evch,
681+
rows: ip.rows,
682+
cols: ip.cols,
677683
}
678684
}
679-
if ip.reencode {
680-
ip.encBuf = append(ip.encBuf, rune(P[2]))
681-
// embedded content will itself be a CSI, so terminated by a CSI terminator
682-
if len(ip.encBuf) > 2 && P[2] >= 0x40 && P[2] < 0x7F {
683-
eb := ip.encBuf
684-
ip.encBuf = nil
685-
ip.reencode = false
686-
ip.buf = append(eb, ip.buf...)
687-
go ip.ScanUTF8(nil)
688-
}
685+
686+
if ip.nested != nil && P[2] > 0 && P[2] < 0x80 { // only ASCII in win32-input-mode
687+
ip.nested.ScanUTF8([]byte{byte(P[2])})
689688
return
690689
}
691690

@@ -814,7 +813,7 @@ func (ip *inputProcessor) handleCsi(mode rune, params []byte, intermediate []byt
814813
ip.post(NewEventKey(ks.Key, 0, mod))
815814
return
816815
}
817-
if P0 == 27 && len(P) > 2 {
816+
if P0 == 27 && len(P) > 2 && P[2] > 0 && P[2] <= 0xff {
818817
if P[2] < ' ' || P[2] == 0x7F {
819818
ip.post(NewEventKey(Key(P[2]), 0, mod))
820819
} else {

tscreen.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"io"
2525
"maps"
2626
"os"
27+
"runtime"
2728
"strconv"
2829
"strings"
2930
"sync"
@@ -319,12 +320,22 @@ func (t *tScreen) prepareExtendedOSC() {
319320
}
320321

321322
if t.enableCsiU == "" && t.ti.XTermLike {
322-
// three advanced keyboard protocols:
323-
// - xterm modifyOtherKeys (uses CSI 27 ~ )
324-
// - kitty csi-u (uses CSI u)
325-
// - win32-input-mode (uses CSI _)
326-
t.enableCsiU = "\x1b[>4;2m" + "\x1b[>1u" + "\x1b[9001h"
327-
t.disableCsiU = "\x1b[9001l" + "\x1b[<u" + "\x1b[>4;0m"
323+
if runtime.GOOS == "windows" && (os.Getenv("TERM") == "" || os.Getenv("TERM_PROGRAM") == "WezTerm") {
324+
// on Windows, if we don't have a TERM, use only win32-input-mode
325+
t.enableCsiU = "\x1b[?9001h"
326+
t.disableCsiU = "\x1b[?9001l"
327+
} else if os.Getenv("TERM_PROGRAM") == "WezTerm" {
328+
// WezTerm is unhappy if we ask for other modes
329+
t.enableCsiU = "\x1b[>1u"
330+
t.disableCsiU = "\x1b[<u"
331+
} else {
332+
// three advanced keyboard protocols:
333+
// - xterm modifyOtherKeys (uses CSI 27 ~ )
334+
// - kitty csi-u (uses CSI u)
335+
// - win32-input-mode (uses CSI _)
336+
t.enableCsiU = "\x1b[>4;2m" + "\x1b[>1u" + "\x1b[9001h"
337+
t.disableCsiU = "\x1b[9001l" + "\x1b[<u" + "\x1b[>4;0m"
338+
}
328339
}
329340
}
330341

0 commit comments

Comments
 (0)