Bump github.com/gdamore/tcell/v3 from 3.4.1 to 3.4.2 (#5970)

Bumps [github.com/gdamore/tcell/v3](https://github.com/gdamore/tcell)
from 3.4.1 to 3.4.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/gdamore/tcell/releases">github.com/gdamore/tcell/v3's
releases</a>.</em></p>
<blockquote>
<h2>Version 3.4.2 Bug Fix Release</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(deps): bump actions/setup-go from 6 to 7 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/gdamore/tcell/pull/1141">gdamore/tcell#1141</a></li>
<li>perf: skip grapheme iterator for printable ASCII by <a
href="https://github.com/ayn2op"><code>@​ayn2op</code></a> in <a
href="https://redirect.github.com/gdamore/tcell/pull/1145">gdamore/tcell#1145</a></li>
<li>chore(deps): bump github.com/lucasb-eyer/go-colorful from 1.4.0 to
1.4.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/gdamore/tcell/pull/1153">gdamore/tcell#1153</a></li>
<li>chore(deps): bump golang.org/x/text from 0.40.0 to 0.41.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/gdamore/tcell/pull/1157">gdamore/tcell#1157</a></li>
<li>fix(tscreen): guard inputLoop keyQ send against shutdown deadlock
(revives <a
href="https://redirect.github.com/gdamore/tcell/issues/673">#673</a>) by
<a href="https://github.com/gmlewis"><code>@​gmlewis</code></a> in <a
href="https://redirect.github.com/gdamore/tcell/pull/1155">gdamore/tcell#1155</a></li>
<li>fix(st): The st terminal is very limited - disable extensions that
br… by <a href="https://github.com/gdamore"><code>@​gdamore</code></a>
in <a
href="https://redirect.github.com/gdamore/tcell/pull/1160">gdamore/tcell#1160</a></li>
<li>fix: Fix SS3 application-keypad sequences (fixes <a
href="https://redirect.github.com/gdamore/tcell/issues/1159">#1159</a>)
by <a href="https://github.com/gdamore"><code>@​gdamore</code></a> in <a
href="https://redirect.github.com/gdamore/tcell/pull/1161">gdamore/tcell#1161</a></li>
<li>fix(input): extend escape sequence timeouts by <a
href="https://github.com/gdamore"><code>@​gdamore</code></a> in <a
href="https://redirect.github.com/gdamore/tcell/pull/1162">gdamore/tcell#1162</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/gmlewis"><code>@​gmlewis</code></a> made
their first contribution in <a
href="https://redirect.github.com/gdamore/tcell/pull/1155">gdamore/tcell#1155</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/gdamore/tcell/compare/v3.4.1...v3.4.2">https://github.com/gdamore/tcell/compare/v3.4.1...v3.4.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c72e284716"><code>c72e284</code></a>
fix(input): extend escape sequence timeouts (<a
href="https://redirect.github.com/gdamore/tcell/issues/1162">#1162</a>)</li>
<li><a
href="36892cde4e"><code>36892cd</code></a>
test: cover modified SS3 keypad runes</li>
<li><a
href="e794ff2439"><code>e794ff2</code></a>
fix: Fix SS3 application-keypad sequences (fixes <a
href="https://redirect.github.com/gdamore/tcell/issues/1159">#1159</a>)</li>
<li><a
href="331861bcbd"><code>331861b</code></a>
fix(st): The st terminal is very limited - disable extensions that break
it (...</li>
<li><a
href="22ae0b117b"><code>22ae0b1</code></a>
fix(tscreen): guard inputLoop keyQ send against shutdown deadlock</li>
<li><a
href="ec458f1327"><code>ec458f1</code></a>
chore(deps): bump golang.org/x/text from 0.40.0 to 0.41.0</li>
<li><a
href="8d07aa89c2"><code>8d07aa8</code></a>
chore(deps): bump github.com/lucasb-eyer/go-colorful from 1.4.0 to
1.4.1</li>
<li><a
href="3182f3e975"><code>3182f3e</code></a>
perf: skip grapheme iterator for printable ASCII</li>
<li><a
href="45d70ee4ab"><code>45d70ee</code></a>
chore(deps): bump actions/setup-go from 6 to 7</li>
<li>See full diff in <a
href="https://github.com/gdamore/tcell/compare/v3.4.1...v3.4.2">compare
view</a></li>
</ul>
</details>
<br />
This commit is contained in:
Stefan Haller 2026-09-05 16:19:12 +02:00 committed by GitHub
commit 9aa2c0b85a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 165 additions and 38 deletions

4
go.mod
View file

@ -16,7 +16,7 @@ require (
github.com/cli/go-gh/v2 v2.13.0
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/creack/pty v1.1.24
github.com/gdamore/tcell/v3 v3.4.1
github.com/gdamore/tcell/v3 v3.4.2
github.com/go-errors/errors v1.5.1
github.com/gookit/color v1.6.1
github.com/integrii/flaggy v1.8.0
@ -68,7 +68,7 @@ require (
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/mod v0.38.0 // indirect
golang.org/x/term v0.45.0 // indirect
golang.org/x/text v0.40.0 // indirect
golang.org/x/text v0.41.0 // indirect
golang.org/x/tools v0.48.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect

8
go.sum
View file

@ -30,8 +30,8 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
github.com/gdamore/tcell/v3 v3.4.1 h1:22227t1EUwqxTlmCX9vw0RUE2IEPGw6oYcNan+bPe4w=
github.com/gdamore/tcell/v3 v3.4.1/go.mod h1:YWwuxZNi14VGQC5g2VGNEDRXpBraTwvVjMovRH6G6hw=
github.com/gdamore/tcell/v3 v3.4.2 h1:gGW+6z2Bz5Wl2mNwFlm9+eRmg2JQrWcKjSkL1LRfpNU=
github.com/gdamore/tcell/v3 v3.4.2/go.mod h1:Oe5U3S3jm3NzypswDNUhe+LUnF5CoFq2b4sepD++QHo=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
@ -174,8 +174,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

View file

@ -14,6 +14,8 @@
package tcell
import "unicode/utf8"
type cell struct {
currStr string
lastStr string
@ -76,6 +78,10 @@ func (cb *CellBuffer) put(x int, y int, str string, style Style) (string, int) {
// Identical re-Put (a full-screen redraw): the grapheme split is
// unchanged, so reuse the measured width instead of segmenting.
cl, width, str = str, c.width, ""
} else if len(str) > 0 && str[0] >= ' ' && str[0] <= '~' && (len(str) == 1 || str[1] < utf8.RuneSelf) {
// Printable ASCII followed by ASCII cannot be part of a larger
// grapheme cluster, so avoid constructing a grapheme iterator.
cl, width, str = str[:1], 1, str[1:]
} else {
g := textWidthOptions.StringGraphemes(str)
for width == 0 && g.Next() {

View file

@ -63,10 +63,22 @@ const (
// before they can grow without bound while waiting for a string terminator.
const defaultControlStringLimit = 64 * 1024
const (
// loneEscapeTimeout keeps bare Escape responsive when using legacy
// keyboard reporting, where ESC can also prefix an Alt-modified key.
loneEscapeTimeout = 200 * time.Millisecond
// escapeSequenceTimeout bounds incomplete escape sequences. Once a
// sequence introducer has arrived, it is no longer ambiguous with a lone
// Escape and can tolerate a substantially longer inter-byte delay.
escapeSequenceTimeout = time.Second
)
func newInputParser(eq chan<- Event) *inputParser {
return &inputParser{
evch: eq,
buf: make([]rune, 0, 128),
legacy: true,
controlStringMax: defaultControlStringLimit,
}
}
@ -91,6 +103,7 @@ type inputParser struct {
nested *inputParser // for buggy win32-input-mode implementations
surrogate rune // high surrogate pair seen (for Win32 input mode)
advanced bool // use advanced key reporting semantics
legacy bool // keyboard protocol has ambiguous ESC prefixes
controlStringMax int // maximum inbound OSC/XDA payload size; 0 means unlimited
discardString bool // drop the rest of an over-limit OSC/XDA sequence
}
@ -129,6 +142,35 @@ func (ip *inputParser) Waiting() bool {
return ip.state != istInit
}
// waitDuration reports how long to wait for the next byte before resetting an
// incomplete escape sequence. A bare ESC is only ambiguous with legacy
// keyboard reporting; other protocols can use the longer sequence deadline.
func (ip *inputParser) waitDuration() time.Duration {
if ip.state == istInit {
return 0
}
if ip.state == istEsc && ip.legacy {
return loneEscapeTimeout
}
return escapeSequenceTimeout
}
func (ip *inputParser) WaitDuration() time.Duration {
ip.l.Lock()
defer ip.l.Unlock()
return ip.waitDuration()
}
func (ip *inputParser) SetKeyboardProtocol(protocol KeyProtocol) {
ip.l.Lock()
ip.legacy = protocol == LegacyKeyboard
nested := ip.nested
ip.l.Unlock()
if nested != nil {
nested.SetKeyboardProtocol(protocol)
}
}
// SetPixelMouse toggles whether SGR mouse reports are interpreted as
// pixel coordinates (CSI ?1016h) rather than character cells (CSI ?1006h).
// When enabled, mouse coordinates are not clipped to the screen size.
@ -488,25 +530,46 @@ var winKeys = map[int]Key{
0x87: KeyF24, // vkF24
}
type ss3Key struct {
key Key
str string
}
// keys by their SS3 - used in application mode usually (legacy VT-style)
var ss3Keys = map[rune]Key{
'A': KeyUp,
'B': KeyDown,
'C': KeyRight,
'D': KeyLeft,
'E': KeyClear,
'F': KeyEnd,
'H': KeyHome,
'P': KeyF1,
'Q': KeyF2,
'R': KeyF3,
'S': KeyF4,
't': KeyF5,
'u': KeyF6,
'v': KeyF7,
'l': KeyF8,
'w': KeyF9,
'x': KeyF10,
var ss3Keys = map[rune]ss3Key{
'A': {key: KeyUp},
'B': {key: KeyDown},
'C': {key: KeyRight},
'D': {key: KeyLeft},
'E': {key: KeyClear},
'F': {key: KeyEnd},
'H': {key: KeyHome},
'P': {key: KeyF1},
'Q': {key: KeyF2},
'R': {key: KeyF3},
'S': {key: KeyF4},
// DEC application-keypad sequences. The VT100 terminfo entry calls some
// of these F5-F10, but that is a terminfo naming artifact: a VT100 has
// only PF1-PF4. Decode them by their PC keypad navigation meanings.
'p': {key: KeyInsert},
'q': {key: KeyEnd},
'r': {key: KeyDown},
's': {key: KeyPgDn},
't': {key: KeyLeft},
'u': {key: KeyClear},
'v': {key: KeyRight},
'w': {key: KeyHome},
'x': {key: KeyUp},
'y': {key: KeyPgUp},
'M': {key: KeyEnter},
'n': {key: KeyDelete},
'j': {key: KeyRune, str: "*"},
'k': {key: KeyRune, str: "+"},
'l': {key: KeyRune, str: ","},
'm': {key: KeyRune, str: "-"},
'o': {key: KeyRune, str: "/"},
'X': {key: KeyRune, str: "="},
}
// linux terminal uses these non ECMA keys prefixed by CSI-[
@ -669,16 +732,16 @@ func (ip *inputParser) scan() {
// parameters that do not match one of these forms, we just discard it.
if len(ip.csiParams) == 0 {
// simple SS3 case
ip.postKey(k, "", ModNone)
ip.postKey(k.key, k.str, ModNone)
} else if parts := strings.Split(string(ip.csiParams), ";"); len(parts) >= 1 {
// SS3 with modifier (old style). Note old terminfo would declare these as high
// numbered function keys, but we encode as modified since that's how they are entered.
if len(parts) >= 2 {
if m, err := strconv.Atoi(parts[1]); err == nil && (parts[0] == "1" || parts[0] == "") {
ip.postKey(k, "", calcModifier(m))
ip.postKey(k.key, k.str, calcModifier(m))
}
} else if m, err := strconv.Atoi(parts[0]); err == nil {
ip.postKey(k, "", calcModifier(m))
ip.postKey(k.key, k.str, calcModifier(m))
}
}
}
@ -757,7 +820,7 @@ func (ip *inputParser) scan() {
}
}
if ip.state != istInit && time.Since(ip.keyTime) > time.Millisecond*50 {
if timeout := ip.waitDuration(); timeout > 0 && time.Since(ip.keyTime) > timeout {
if ip.state == istEsc {
ip.postKey(KeyEscape, "", ModNone)
} else if ec := ip.escChar; ec != 0 {
@ -1056,6 +1119,7 @@ func (ip *inputParser) handleWinKey(P []int) {
rows: ip.rows,
cols: ip.cols,
advanced: ip.advanced,
legacy: ip.legacy,
pixelMouse: ip.pixelMouse,
controlStringMax: ip.controlStringMax,
}
@ -1425,7 +1489,7 @@ func (ip *inputParser) handleCsi(mode rune, params []byte, intermediate []byte)
// this might have been an SS3 style key with modifiers applied
if k, ok := ss3Keys[mode]; ok && P0 == 1 && len(P) > 1 {
ip.postKeyEx(k, "", calcModifier(P[1]), pressed, 0, repeat)
ip.postKeyEx(k.key, k.str, calcModifier(P[1]), pressed, 0, repeat)
return
}
// if we got here we just swallow the unknown sequence

View file

@ -286,6 +286,11 @@ type tScreen struct {
advancedKeys bool
controlStringLimit int
input *inputParser
compat struct {
mouseUnsupported bool
focusUnsupported bool
clipboardReadUnsupported bool
}
sync.Mutex
}
@ -780,6 +785,10 @@ func (t *tScreen) emitAttrs(attrs AttrMask) {
// The assumption is that sgr0 was already printed ahead of this.
func (t *tScreen) emitUnderline(us UnderlineStyle, uc Color) {
if us != UnderlineStyleNone {
if t.legacy {
t.Print(underline)
return
}
// NB: under color should have been reset by sgr0
if uc.IsRGB() {
r, g, b := uc.RGB()
@ -1100,6 +1109,9 @@ func (t *tScreen) enableMouse(f MouseFlags) {
// so we enable the mouse unconditionally unless we get a report
// that says we have mouse, but not SGR mouse. This is suboptimal, but
// a concession forced by the sorry state of terminal emulators.
if t.compat.mouseUnsupported {
return
}
if t.mouseDisabled {
f = 0
}
@ -1203,10 +1215,16 @@ func (t *tScreen) DisableFocus() {
}
func (t *tScreen) enableFocusReporting() {
if t.compat.focusUnsupported {
return
}
t.Print(vt.PmFocusReports.Enable())
}
func (t *tScreen) disableFocusReporting() {
if t.compat.focusUnsupported {
return
}
t.Print(vt.PmFocusReports.Disable())
}
@ -1336,13 +1354,18 @@ func (t *tScreen) mainLoop(stopQ chan struct{}) {
case chunk := <-t.keyQ:
buf.Write(chunk)
t.scanInput(buf)
if t.input.Waiting() {
ta = time.After(time.Millisecond * 100)
if timeout := t.input.WaitDuration(); timeout > 0 {
ta = time.After(timeout)
} else {
ta = nil
}
case <-ta:
t.input.Scan()
if timeout := t.input.WaitDuration(); timeout > 0 {
ta = time.After(timeout)
} else {
ta = nil
}
}
}
}
@ -1373,7 +1396,11 @@ func (t *tScreen) inputLoop(stopQ chan struct{}) {
return
}
if n > 0 {
t.keyQ <- chunk[:n]
select {
case t.keyQ <- chunk[:n]:
case <-t.quit:
return
}
}
}
}
@ -1449,7 +1476,31 @@ func (t *tScreen) Tty() (Tty, bool) {
return t.tty, true
}
func (t *tScreen) applyKnownTerminalProfile(goos, termProgram string) bool {
func isSTTerminal(term string) bool {
return term == "st" || strings.HasPrefix(term, "st-")
}
func (t *tScreen) applyKnownTerminalProfile(goos, term, termProgram string) bool {
if isSTTerminal(term) {
// st implements a small subset of xterm extensions. In particular,
// it has neither an advanced keyboard protocol nor SGR mouse or focus
// reporting. It also reports unsupported CSI and OSC sequences to
// stderr, so avoid probing or using extensions it does not implement.
t.legacy = true
t.compat.mouseUnsupported = true
t.compat.focusUnsupported = true
t.enterUrl = ""
t.exitUrl = ""
t.setWinSize = ""
t.saveTitle = ""
t.restoreTitle = ""
t.setTitle = "\x1b]2;%s\x1b\\"
t.notifyDesktop = ""
t.compat.clipboardReadUnsupported = true
t.termName = "st"
return true
}
switch termProgram {
case "Apple_Terminal":
// macOS Terminal.app cannot handle the startup queries, but it does
@ -1525,7 +1576,7 @@ func (t *tScreen) engageLocked() error {
// Eventually they'll hopefully fix this. As the environment variable
// does not convey by default via ssh, remote sessions might see spurious characters
// emitted during startup. See the blog post for alternatives.
if !t.applyKnownTerminalProfile(runtime.GOOS, os.Getenv("TERM_PROGRAM")) && t.negotiate {
if !t.applyKnownTerminalProfile(runtime.GOOS, t.term, os.Getenv("TERM_PROGRAM")) && t.negotiate {
if useVTWindowSizeQuery(runtime.GOOS) {
t.Print(requestWindowSize)
}
@ -1551,6 +1602,7 @@ func (t *tScreen) engageLocked() error {
}
t.processInitQ()
t.applyKeyboardProtocolOverride()
t.input.SetKeyboardProtocol(t.keyboardProtocol())
if t.useAltScreen() {
// Technically this may not be right, but every terminal we know about
// (even Wyse 60) uses this to enter the alternate screen buffer, and
@ -1589,7 +1641,7 @@ func (t *tScreen) engageLocked() error {
if t.title != "" && t.setTitle != "" {
t.Printf(t.setTitle, t.title)
}
if t.negotiate && useVTWindowSizeQuery(runtime.GOOS) {
if t.negotiate && !t.legacy && useVTWindowSizeQuery(runtime.GOOS) {
t.Print(requestWindowSize)
}
@ -1744,7 +1796,7 @@ func (t *tScreen) GetClipboard() {
t.Unlock()
return
}
if t.setClipboard != "" {
if !t.compat.clipboardReadUnsupported && t.setClipboard != "" {
t.Printf(t.setClipboard, "?")
}
t.Unlock()
@ -1773,6 +1825,11 @@ func (t *tScreen) Terminal() (string, string) {
func (t *tScreen) KeyboardProtocol() KeyProtocol {
t.Lock()
defer t.Unlock()
return t.keyboardProtocol()
}
// keyboardProtocol reports the selected keyboard protocol while t is locked.
func (t *tScreen) keyboardProtocol() KeyProtocol {
if t.haveWin32Kbd {
return Win32Keyboard
}

4
vendor/modules.txt vendored
View file

@ -45,7 +45,7 @@ github.com/fatih/color
# github.com/gdamore/encoding v1.0.1
## explicit; go 1.9
github.com/gdamore/encoding
# github.com/gdamore/tcell/v3 v3.4.1
# github.com/gdamore/tcell/v3 v3.4.2
## explicit; go 1.25.0
github.com/gdamore/tcell/v3
github.com/gdamore/tcell/v3/color
@ -185,7 +185,7 @@ golang.org/x/sys/windows
# golang.org/x/term v0.45.0
## explicit; go 1.25.0
golang.org/x/term
# golang.org/x/text v0.40.0
# golang.org/x/text v0.41.0
## explicit; go 1.25.0
golang.org/x/text/cases
golang.org/x/text/encoding