1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-11-28 04:23:58 +03:00

Bump gocui

The main change here is https://github.com/jesseduffield/gocui/pull/85, which
avoids breaking lines after footnote symbols in commit messages (e.g. [1]).

[1]: https://www.example.com/this-is-a-really-long-url-that-lazy-git-automatically-wraps-and-is-an-issue
This commit is contained in:
Stefan Haller
2025-10-02 17:25:46 +02:00
parent cb2b5c3738
commit ef92e30315
119 changed files with 3883 additions and 17382 deletions

View File

@@ -1,13 +0,0 @@
version: 1.0.{build}
clone_folder: c:\gopath\src\github.com\gdamore\tcell
environment:
GOPATH: c:\gopath
build_script:
- go version
- go env
- SET PATH=%LOCALAPPDATA%\atom\bin;%GOPATH%\bin;%PATH%
- go get -t ./...
- go build
- go install ./...
test_script:
- go test ./...

View File

@@ -1,18 +0,0 @@
language: go
go:
- 1.15.x
- master
arch:
- amd64
- ppc64le
before_install:
- go get -t -v ./...
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@@ -7,12 +7,14 @@ It was inspired by _termbox_, but includes many additional improvements.
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)
[![Linux](https://img.shields.io/github/actions/workflow/status/gdamore/tcell/linux.yml?branch=main&logoColor=grey&logo=linux&label=)](https://github.com/gdamore/tcell/actions/workflows/linux.yml)
[![Windows](https://img.shields.io/github/actions/workflow/status/gdamore/tcell/windows.yml?branch=main&logoColor=grey&logo=windows&label=)](https://github.com/gdamore/tcell/actions/workflows/windows.yml)
[![Windows](https://img.shields.io/github/actions/workflow/status/gdamore/tcell/windows.yml?branch=main&logoColor=grey&label=Windows)](https://github.com/gdamore/tcell/actions/workflows/windows.yml)
[![Web Assembly](https://img.shields.io/github/actions/workflow/status/gdamore/tcell/webasm.yml?branch=main&logoColor=grey&logo=webassembly&label=)](https://github.com/gdamore/tcell/actions/workflows/webasm.yml)
[![Apache License](https://img.shields.io/github/license/gdamore/tcell.svg?logoColor=silver&logo=opensourceinitiative&color=blue&label=)](https://github.com/gdamore/tcell/blob/master/LICENSE)
[![Docs](https://img.shields.io/badge/godoc-reference-blue.svg?label=&logo=go)](https://pkg.go.dev/github.com/gdamore/tcell/v2)
[![Discord](https://img.shields.io/discord/639503822733180969?label=&logo=discord)](https://discord.gg/urTTxDN)
[![Coverage](https://img.shields.io/codecov/c/github/gdamore/tcell?logoColor=grey&logo=codecov&label=)](https://codecov.io/gh/gdamore/tcell)
[![Go Report Card](https://goreportcard.com/badge/github.com/gdamore/tcell/v2)](https://goreportcard.com/report/github.com/gdamore/tcell/v2)
[![Latest Release](https://img.shields.io/github/v/release/gdamore/tcell.svg?logo=github&label=)](https://github.com/gdamore/tcell/releases)
Please see [here](UKRAINE.md) for an important message for the people of Russia.
@@ -25,50 +27,9 @@ A brief, and still somewhat rough, [tutorial](TUTORIAL.md) is available.
## Examples
- [proxima5](https://github.com/gdamore/proxima5) - space shooter ([video](https://youtu.be/jNxKTCmY_bQ))
- [govisor](https://github.com/gdamore/govisor) - service management UI ([screenshot](http://2.bp.blogspot.com/--OsvnfzSNow/Vf7aqMw3zXI/AAAAAAAAARo/uOMtOvw4Sbg/s1600/Screen%2BShot%2B2015-09-20%2Bat%2B9.08.41%2BAM.png))
- mouse demo - included mouse test ([screenshot](http://2.bp.blogspot.com/-fWvW5opT0es/VhIdItdKqJI/AAAAAAAAATE/7Ojc0L1SpB0/s1600/Screen%2BShot%2B2015-10-04%2Bat%2B11.47.13%2BPM.png))
- [gomatrix](https://github.com/gdamore/gomatrix) - converted from Termbox
- [micro](https://github.com/zyedidia/micro/) - lightweight text editor with syntax-highlighting and themes
- [godu](https://github.com/viktomas/godu) - utility to discover large files/folders
- [tview](https://github.com/rivo/tview/) - rich interactive widgets
- [cview](https://code.rocketnine.space/tslocum/cview) - user interface toolkit (fork of _tview_)
- [awesome gocui](https://github.com/awesome-gocui/gocui) - Go Console User Interface
- [gomandelbrot](https://github.com/rgm3/gomandelbrot) - Mandelbrot!
- [WTF](https://github.com/senorprogrammer/wtf) - personal information dashboard
- [browsh](https://github.com/browsh-org/browsh) - modern web browser ([video](https://www.youtube.com/watch?v=HZq86XfBoRo))
- [go-life](https://github.com/sachaos/go-life) - Conway's Game of Life
- [gowid](https://github.com/gcla/gowid) - compositional widgets for terminal UIs, inspired by _urwid_
- [termshark](https://termshark.io) - interface for _tshark_, inspired by Wireshark, built on _gowid_
- [go-tetris](https://github.com/MichaelS11/go-tetris) - Go Tetris with AI option
- [fzf](https://github.com/junegunn/fzf) - command-line fuzzy finder
- [ascii-fluid](https://github.com/esimov/ascii-fluid) - fluid simulation controlled by webcam
- [cbind](https://code.rocketnine.space/tslocum/cbind) - key event encoding, decoding and handling
- [tpong](https://github.com/spinzed/tpong) - old-school Pong
- [aerc](https://git.sr.ht/~sircmpwn/aerc) - email client
- [tblogs](https://github.com/ezeoleaf/tblogs) - development blogs reader
- [spinc](https://github.com/lallassu/spinc) - _irssi_ inspired chat application for Cisco Spark/WebEx
- [gorss](https://github.com/lallassu/gorss) - RSS/Atom feed reader
- [memoryalike](https://github.com/Bios-Marcel/memoryalike) - memorization game
- [lf](https://github.com/gokcehan/lf) - file manager
- [goful](https://github.com/anmitsu/goful) - CUI file manager
- [gokeybr](https://github.com/bunyk/gokeybr) - deliberately practice your typing
- [gonano](https://github.com/jbaramidze/gonano) - editor, mimics _nano_
- [uchess](https://github.com/tmountain/uchess) - UCI chess client
- [min](https://github.com/a-h/min) - Gemini browser
- [ov](https://github.com/noborus/ov) - file pager
- [tmux-wormhole](https://github.com/gcla/tmux-wormhole) - _tmux_ plugin to transfer files
- [gruid-tcell](https://github.com/anaseto/gruid-tcell) - driver for the grid based UI and game framework
- [aretext](https://github.com/aretext/aretext) - minimalist text editor with _vim_ key bindings
- [sync](https://github.com/kyprifog/sync) - GitHub repo synchronization tool
- [statusbar](https://github.com/kyprifog/statusbar) - statusbar motivation tool for tracking periodic tasks/goals
- [todo](https://github.com/kyprifog/todo) - simple todo app
- [gosnakego](https://github.com/liweiyi88/gosnakego) - a snake game
- [gbb](https://github.com/sdemingo/gbb) - A classical bulletin board app for tildes or public unix servers
- [lil](https://github.com/andrievsky/lil) - A simple and flexible interface for any service by implementing only list and get operations
- [hero.go](https://github.com/barisbll/hero.go) - 2d monster shooter ([video](https://user-images.githubusercontent.com/40062673/277157369-240d7606-b471-4aa1-8c54-4379a513122b.mp4))
- [go-tetris](https://github.com/aaronriekenberg/go-tetris) - simple tetris game for native terminal and WASM using github actions+pages
- [oddshub](https://github.com/dos-2/oddshub) - A TUI designed for analyzing sports betting odds
A number of example are posted up on our [Gallery](https://github.com/gdamore/tcell/wikis/Gallery/).
Let us know if you want to add your masterpiece to the list!
## Pure Go Terminfo Database
@@ -85,6 +46,11 @@ _Tcell_ is portable to a wide variety of systems, and is pure Go, without
any need for CGO.
_Tcell_ is believed to work with mainstream systems officially supported by golang.
Following the Go support policy, _Tcell_ officially only supports the current ("stable") version of go,
and the version immediately prior ("oldstable"). This policy is necessary to make sure that we can
update dependencies to pick up security fixes and new features, and it allows us to adopt changes
(such as library and language features) that are only supported in newer versions of Go.
## No Async IO
_Tcell_ is able to operate without requiring `SIGIO` signals (unlike _termbox_),
@@ -117,11 +83,6 @@ _Tcell_ will respect your terminal's color space as specified within your termin
For example attempts to emit color sequences on VT100 terminals
won't result in unintended consequences.
In legacy Windows mode, _Tcell_ supports 16 colors, bold, dim, and reverse,
instead of just termbox's 8 colors with reverse. (Note that there is some
conflation with bold/dim and colors.)
Modern Windows 10 can benefit from much richer colors however.
_Tcell_ maps 16 colors down to 8, for terminals that need it.
(The upper 8 colors are just brighter versions of the lower 8.)
@@ -130,10 +91,6 @@ _Tcell_ maps 16 colors down to 8, for terminals that need it.
_Tcell_ supports enhanced mouse tracking mode, so your application can receive
regular mouse motion events, and wheel events, if your terminal supports it.
(Note: The Windows 10 Terminal application suffers from a flaw in this regard,
and does not support mouse interaction. The stock Windows 10 console host
fired up with cmd.exe or PowerShell works fine however.)
## _Termbox_ Compatibility
A compatibility layer for _termbox_ is provided in the `compat` directory.
@@ -159,9 +116,6 @@ taken in the application to avoid explicitly attempting to set content in the
next cell, otherwise the results are undefined. (Normally the wide character
is displayed, and the other character is not; do not depend on that behavior.)
Older terminal applications (especially on systems like Windows 8) lack support
for advanced Unicode, and thus may not fare well.
## Colors
_Tcell_ assumes the ANSI/XTerm color model, including the 256 color map that
@@ -265,22 +219,16 @@ platforms (e.g., AIX) may need to be added. Pull requests are welcome!
Windows console mode applications are supported.
Modern console applications like ConEmu and the Windows 10 terminal,
Modern console applications like ConEmu and the Windows Terminal,
support all the good features (resize, mouse tracking, etc.)
### WASM
WASM is supported, but needs additional setup detailed in [README-wasm](README-wasm.md).
### Plan9 and others
### Plan9 and its variants
These platforms won't work, but compilation stubs are supplied
for folks that want to include parts of this in software for those
platforms. The Simulation screen works, but as _Tcell_ doesn't know how to
allocate a real screen object on those platforms, `NewScreen()` will fail.
If anyone has wisdom about how to improve support for these,
please let me know. PRs are especially welcome.
Plan 9 is supported on a limited basis. The Plan 9 backend opens `/dev/cons` for I/O, enables raw mode by writing `rawon`/`rawoff` to `/dev/consctl`, watches `/dev/wctl` for resize notifications, and then constructs a **terminfo-backed** `Screen` (so `NewScreen` works as on other platforms). Typical usage is inside `vt(1)` with `TERM=vt100`. Expect **monochrome text** and **no mouse reporting** under stock `vt(1)` (it generally does not emit ANSI color or xterm mouse sequences). If a Plan 9 terminal supplies ANSI color escape sequences and xterm-style mouse reporting, color can be picked up via **terminfo** and mouse support could be added by wiring those sequences into the Plan 9 TTY path; contributions that improve terminal detection and broaden feature support are welcome.
### Commercial Support

23
vendor/github.com/gdamore/tcell/v2/charset_plan9.go generated vendored Normal file
View File

@@ -0,0 +1,23 @@
//go:build plan9
// +build plan9
// Copyright 2025 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
// Plan 9 uses UTF-8 system-wide, so we return "UTF-8" unconditionally.
func getCharset() string {
return "UTF-8"
}

View File

@@ -1,5 +1,5 @@
//go:build plan9 || nacl
// +build plan9 nacl
//go:build nacl
// +build nacl
// Copyright 2015 The TCell Authors
//

View File

@@ -655,25 +655,28 @@ var vkKeys = map[uint16]Key{
func getu32(v []byte) uint32 {
return uint32(v[0]) + (uint32(v[1]) << 8) + (uint32(v[2]) << 16) + (uint32(v[3]) << 24)
}
func geti32(v []byte) int32 {
return int32(getu32(v))
}
func getu16(v []byte) uint16 {
return uint16(v[0]) + (uint16(v[1]) << 8)
}
func geti16(v []byte) int16 {
return int16(getu16(v))
}
// Convert windows dwControlKeyState to modifier mask
func mod2mask(cks uint32) ModMask {
func mod2mask(cks uint32, filter_ctrl_alt bool) ModMask {
mm := ModNone
// Left or right control
ctrl := (cks & (0x0008 | 0x0004)) != 0
// Left or right alt
alt := (cks & (0x0002 | 0x0001)) != 0
// Filter out ctrl+alt (it means AltGr)
if !(ctrl && alt) {
if !filter_ctrl_alt || !(ctrl && alt) {
if ctrl {
mm |= ModCtrl
}
@@ -788,10 +791,10 @@ func (s *cScreen) getConsoleInput() error {
// synthesized key code
for krec.repeat > 0 {
// convert shift+tab to backtab
if mod2mask(krec.mod) == ModShift && krec.ch == vkTab {
if mod2mask(krec.mod, false) == ModShift && krec.ch == vkTab {
s.postEvent(NewEventKey(KeyBacktab, 0, ModNone))
} else {
s.postEvent(NewEventKey(KeyRune, rune(krec.ch), mod2mask(krec.mod)))
s.postEvent(NewEventKey(KeyRune, rune(krec.ch), mod2mask(krec.mod, true)))
}
krec.repeat--
}
@@ -803,7 +806,7 @@ func (s *cScreen) getConsoleInput() error {
return nil
}
for krec.repeat > 0 {
s.postEvent(NewEventKey(key, rune(krec.ch), mod2mask(krec.mod)))
s.postEvent(NewEventKey(key, rune(krec.ch), mod2mask(krec.mod, false)))
krec.repeat--
}
@@ -816,7 +819,7 @@ func (s *cScreen) getConsoleInput() error {
mrec.flags = getu32(rec.data[12:])
btns := mrec2btns(mrec.btns, mrec.flags)
// we ignore double click, events are delivered normally
s.postEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns, mod2mask(mrec.mod)))
s.postEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns, mod2mask(mrec.mod, false)))
case resizeEvent:
var rrec resizeRecord
@@ -938,7 +941,7 @@ func (s *cScreen) mapStyle(style Style) uint16 {
return attr
}
func (s *cScreen) sendVtStyle(style Style) {
func (s *cScreen) makeVtStyle(style Style) string {
esc := &strings.Builder{}
fg, bg, attrs := style.fg, style.bg, style.attrs
@@ -998,30 +1001,40 @@ func (s *cScreen) sendVtStyle(style Style) {
esc.WriteString(vtExitUrl)
}
s.emitVtString(esc.String())
return esc.String()
}
func (s *cScreen) writeString(x, y int, style Style, ch []uint16) {
func (s *cScreen) sendVtStyle(style Style) {
s.emitVtString(s.makeVtStyle(style))
}
func (s *cScreen) writeString(x, y int, style Style, vtBuf, ch []uint16) {
// we assume the caller has hidden the cursor
if len(ch) == 0 {
return
}
s.setCursorPos(x, y, s.vten)
if s.vten {
s.sendVtStyle(style)
vtBuf = append(vtBuf, utf16.Encode([]rune(fmt.Sprintf(vtCursorPos, y+1, x+1)))...)
styleStr := s.makeVtStyle(style)
vtBuf = append(vtBuf, utf16.Encode([]rune(styleStr))...)
vtBuf = append(vtBuf, ch...)
_ = syscall.WriteConsole(s.out, &vtBuf[0], uint32(len(vtBuf)), nil, nil)
vtBuf = vtBuf[:0]
} else {
s.setCursorPos(x, y, s.vten)
_, _, _ = procSetConsoleTextAttribute.Call(
uintptr(s.out),
uintptr(s.mapStyle(style)))
_ = syscall.WriteConsole(s.out, &ch[0], uint32(len(ch)), nil, nil)
}
_ = syscall.WriteConsole(s.out, &ch[0], uint32(len(ch)), nil, nil)
}
func (s *cScreen) draw() {
// allocate a scratch line bit enough for no combining chars.
// if you have combining characters, you may pay for extra allocations.
buf := make([]uint16, 0, s.w)
var vtBuf []uint16
wcs := buf[:]
lstyle := styleInvalid
@@ -1040,7 +1053,7 @@ func (s *cScreen) draw() {
// write out any data queued thus far
// because we are going to skip over some
// cells, or because we need to change styles
s.writeString(lx, ly, lstyle, wcs)
s.writeString(lx, ly, lstyle, vtBuf, wcs)
wcs = buf[0:0]
lstyle = StyleDefault
if !dirty {
@@ -1067,7 +1080,7 @@ func (s *cScreen) draw() {
}
x += width - 1
}
s.writeString(lx, ly, lstyle, wcs)
s.writeString(lx, ly, lstyle, vtBuf, wcs)
wcs = buf[0:0]
lstyle = styleInvalid
}

View File

@@ -164,6 +164,16 @@ func (s Style) Underline(params ...interface{}) Style {
return s2
}
// GetUnderlineStyle returns the underline style for the style.
func (s Style) GetUnderlineStyle() UnderlineStyle {
return s.ulStyle
}
// GetUnderlineColor returns the underline color for the style.
func (s Style) GetUnderlineColor() Color {
return s.ulColor
}
// Attributes returns a new style based on s, with its attributes set as
// specified.
func (s Style) Attributes(attrs AttrMask) Style {

View File

@@ -25,6 +25,7 @@ import (
// The following imports just register themselves --
// these are the terminal types we aggregate in this package.
_ "github.com/gdamore/tcell/v2/terminfo/a/ansi"
_ "github.com/gdamore/tcell/v2/terminfo/t/tmux"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt100"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt102"
_ "github.com/gdamore/tcell/v2/terminfo/v/vt220"

View File

@@ -898,7 +898,7 @@ func (t *tScreen) drawCell(x, y int) int {
}
// URL string can be long, so don't send it unless we really need to
if t.enterUrl != "" && t.curstyle != style {
if t.enterUrl != "" && t.curstyle.url != style.url {
if style.url != "" {
t.TPuts(ti.TParm(t.enterUrl, style.url, style.urlId))
} else {
@@ -1339,6 +1339,10 @@ func (t *tScreen) buildMouseEvent(x, y, btn int) *EventMouse {
button = WheelUp
case 0x41:
button = WheelDown
case 0x42:
button = WheelLeft
case 0x43:
button = WheelRight
}
if btn&0x4 != 0 {

36
vendor/github.com/gdamore/tcell/v2/tscreen_plan9.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
//go:build plan9
// +build plan9
// Copyright 2025 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import "os"
// initialize on Plan 9: if no TTY was provided, use the Plan 9 TTY.
func (t *tScreen) initialize() error {
if os.Getenv("TERM") == "" {
// TERM should be "vt100" in a vt(1) window; color/mouse support will be limited.
_ = os.Setenv("TERM", "vt100")
}
if t.tty == nil {
tty, err := NewDevTty()
if err != nil {
return err
}
t.tty = tty
}
return nil
}

View File

@@ -1,5 +1,5 @@
//go:build plan9 || windows
// +build plan9 windows
//go:build windows
// +build windows
// Copyright 2022 The TCell Authors
//

270
vendor/github.com/gdamore/tcell/v2/tty_plan9.go generated vendored Normal file
View File

@@ -0,0 +1,270 @@
//go:build plan9
// +build plan9
// Copyright 2025 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"
"sync"
"sync/atomic"
)
// p9Tty implements tcell.Tty using Plan 9's /dev/cons and /dev/consctl.
// Raw mode is enabled by writing "rawon" to /dev/consctl while the fd stays open.
// Resize notifications are read from /dev/wctl: the first read returns geometry,
// subsequent reads block until the window changes (rio(4)).
//
// References:
// - kbdfs(8): cons/consctl rawon|rawoff semantics
// - rio(4): wctl geometry and blocking-on-change behavior
// - vt(1): VT100 emulator typically used for TUI programs on Plan 9
//
// Limitations:
// - We assume VT100-level capabilities (often no colors, no mouse).
// - Window size is conservative: we return 80x24 unless overridden.
// Set LINES/COLUMNS (or TCELL_LINES/TCELL_COLS) to refine.
// - Mouse and bracketed paste are not wired; terminfo/xterm queries
// are not attempted because vt(1) may not support them.
type p9Tty struct {
cons *os.File // /dev/cons (read+write)
consctl *os.File // /dev/consctl (write "rawon"/"rawoff")
wctl *os.File // /dev/wctl (resize notifications)
// protect close/stop; Read/Write are serialized by os.File
mu sync.Mutex
closed atomic.Bool
// resize callback
onResize atomic.Value // func()
wg sync.WaitGroup
stopCh chan struct{}
}
func NewDevTty() (Tty, error) { // tcell signature
return newPlan9TTY()
}
func NewStdIoTty() (Tty, error) { // also required by tcell
// On Plan 9 there is no POSIX tty discipline on stdin/stdout;
// use /dev/cons explicitly for robustness.
return newPlan9TTY()
}
func NewDevTtyFromDev(_ string) (Tty, error) { // required by tcell
// Plan 9 does not have multiple "ttys" in the POSIX sense;
// always bind to /dev/cons and /dev/consctl.
return newPlan9TTY()
}
func newPlan9TTY() (Tty, error) {
cons, err := os.OpenFile("/dev/cons", os.O_RDWR, 0)
if err != nil {
return nil, fmt.Errorf("open /dev/cons: %w", err)
}
consctl, err := os.OpenFile("/dev/consctl", os.O_WRONLY, 0)
if err != nil {
_ = cons.Close()
return nil, fmt.Errorf("open /dev/consctl: %w", err)
}
// /dev/wctl may not exist (console without rio); best-effort.
wctl, _ := os.OpenFile("/dev/wctl", os.O_RDWR, 0)
t := &p9Tty{
cons: cons,
consctl: consctl,
wctl: wctl,
stopCh: make(chan struct{}),
}
return t, nil
}
func (t *p9Tty) Start() error {
t.mu.Lock()
defer t.mu.Unlock()
if t.closed.Load() {
return errors.New("tty closed")
}
// Recreate stop channel if absent or closed (supports resume).
if t.stopCh == nil || isClosed(t.stopCh) {
t.stopCh = make(chan struct{})
}
// Put console into raw mode; remains active while consctl is open.
if _, err := t.consctl.Write([]byte("rawon")); err != nil {
return fmt.Errorf("enable raw mode: %w", err)
}
// Reopen /dev/wctl on resume; best-effort (system console may lack it).
if t.wctl == nil {
if f, err := os.OpenFile("/dev/wctl", os.O_RDWR, 0); err == nil {
t.wctl = f
}
}
if t.wctl != nil {
t.wg.Add(1)
go t.watchResize()
}
return nil
}
func (t *p9Tty) Drain() error {
// Per tcell docs, this may reasonably be a no-op on non-POSIX ttys.
// Read deadlines are not available on plan9 os.File; we rely on Stop().
return nil
}
func (t *p9Tty) Stop() error {
t.mu.Lock()
defer t.mu.Unlock()
// Signal watcher to stop (if not already).
if t.stopCh != nil && !isClosed(t.stopCh) {
close(t.stopCh)
}
// Exit raw mode first.
_, _ = t.consctl.Write([]byte("rawoff"))
// Closing wctl unblocks watchResize; nil it so Start() can reopen later.
if t.wctl != nil {
_ = t.wctl.Close()
t.wctl = nil
}
// Ensure watcher goroutine has exited before returning.
t.wg.Wait()
return nil
}
func (t *p9Tty) Close() error {
t.mu.Lock()
defer t.mu.Unlock()
if t.closed.Swap(true) {
return nil
}
if t.stopCh != nil && !isClosed(t.stopCh) {
close(t.stopCh)
}
_, _ = t.consctl.Write([]byte("rawoff"))
_ = t.cons.Close()
_ = t.consctl.Close()
if t.wctl != nil {
_ = t.wctl.Close()
t.wctl = nil
}
t.wg.Wait()
return nil
}
func (t *p9Tty) Read(p []byte) (int, error) {
return t.cons.Read(p)
}
func (t *p9Tty) Write(p []byte) (int, error) {
return t.cons.Write(p)
}
func (t *p9Tty) NotifyResize(cb func()) {
if cb == nil {
t.onResize.Store((func())(nil))
return
}
t.onResize.Store(cb)
}
func (t *p9Tty) WindowSize() (WindowSize, error) {
// Strategy:
// 1) honor explicit overrides (TCELL_LINES/TCELL_COLS, LINES/COLUMNS),
// 2) otherwise return conservative 80x24.
// Reading /dev/wctl gives pixel geometry, but char cell metrics are
// not generally available to non-draw clients; vt(1) is fixed-cell.
lines, cols := envInt("TCELL_LINES"), envInt("TCELL_COLS")
if lines == 0 {
lines = envInt("LINES")
}
if cols == 0 {
cols = envInt("COLUMNS")
}
if lines <= 0 {
lines = 24
}
if cols <= 0 {
cols = 80
}
return WindowSize{Width: cols, Height: lines}, nil
}
// watchResize blocks on /dev/wctl reads; each read returns when the window
// changes size/position/state, per rio(4). We ignore the parsed geometry and
// just notify tcell to re-query WindowSize().
func (t *p9Tty) watchResize() {
defer t.wg.Done()
r := bufio.NewReader(t.wctl)
for {
select {
case <-t.stopCh:
return
default:
}
// Each read delivers something like:
// " minx miny maxx maxy visible current\n"
// We don't need to parse here; just signal.
_, err := r.ReadString('\n')
if err != nil {
if errors.Is(err, io.EOF) {
return
}
// transient errors: continue
}
if cb, _ := t.onResize.Load().(func()); cb != nil {
cb()
}
}
}
func envInt(name string) int {
if s := strings.TrimSpace(os.Getenv(name)); s != "" {
if v, err := strconv.Atoi(s); err == nil {
return v
}
}
return 0
}
// helper: safe check if a channel is closed
func isClosed(ch <-chan struct{}) bool {
select {
case <-ch:
return true
default:
return false
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2024 The TCell Authors
// Copyright 2025 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
@@ -145,7 +145,7 @@ func (t *wScreen) drawCell(x, y int) int {
s := ""
if len(combc) > 0 {
b := make([]rune, 0, 1 + len(combc))
b := make([]rune, 0, 1+len(combc))
b = append(b, mainc)
b = append(b, combc...)
s = string(b)
@@ -277,6 +277,12 @@ func (t *wScreen) DisableFocus() {
t.Unlock()
}
func (s *wScreen) GetClipboard() {
}
func (s *wScreen) SetClipboard(_ []byte) {
}
func (t *wScreen) Size() (int, int) {
t.Lock()
w, h := t.w, t.h