From 6521ce91d38938db9f674ce8d3c69c87400d9c87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 20:31:42 +0000 Subject: [PATCH] Bump github.com/gdamore/tcell/v2 from 2.6.0 to 2.7.0 Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.6.0 to 2.7.0. - [Release notes](https://github.com/gdamore/tcell/releases) - [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv2.md) - [Commits](https://github.com/gdamore/tcell/compare/v2.6.0...v2.7.0) --- updated-dependencies: - dependency-name: github.com/gdamore/tcell/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 9 +- vendor/github.com/gdamore/tcell/v2/README.md | 2 + vendor/github.com/gdamore/tcell/v2/cell.go | 72 +++++- vendor/github.com/gdamore/tcell/v2/color.go | 49 +++- .../gdamore/tcell/v2/console_win.go | 157 ++++-------- .../github.com/gdamore/tcell/v2/encoding.go | 1 - vendor/github.com/gdamore/tcell/v2/focus.go | 28 ++ vendor/github.com/gdamore/tcell/v2/resize.go | 34 ++- vendor/github.com/gdamore/tcell/v2/screen.go | 179 ++++++++++++- .../github.com/gdamore/tcell/v2/simulation.go | 126 +++------ .../github.com/gdamore/tcell/v2/stdin_unix.go | 16 +- .../tcell/v2/terminfo/dynamic/dynamic.go | 10 +- .../gdamore/tcell/v2/terminfo/gen.sh | 1 + .../gdamore/tcell/v2/terminfo/terminfo.go | 2 + vendor/github.com/gdamore/tcell/v2/tscreen.go | 239 ++++++++++-------- vendor/github.com/gdamore/tcell/v2/tty.go | 2 +- .../github.com/gdamore/tcell/v2/tty_unix.go | 16 +- vendor/github.com/gdamore/tcell/v2/wscreen.go | 185 ++++++-------- vendor/modules.txt | 2 +- 20 files changed, 682 insertions(+), 450 deletions(-) create mode 100644 vendor/github.com/gdamore/tcell/v2/focus.go diff --git a/go.mod b/go.mod index 13e932192..4922fd742 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/distribution/reference v0.5.0 github.com/docker/docker v24.0.7+incompatible github.com/docker/go-units v0.5.0 - github.com/gdamore/tcell/v2 v2.6.0 + github.com/gdamore/tcell/v2 v2.7.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 github.com/navidys/tvxwidgets v0.4.1 diff --git a/go.sum b/go.sum index 0a073b420..7a7b387eb 100644 --- a/go.sum +++ b/go.sum @@ -348,8 +348,8 @@ github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYis github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= -github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= -github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= +github.com/gdamore/tcell/v2 v2.7.0 h1:I5LiGTQuwrysAt1KS9wg1yFfOI3arI3ucFrxtd/xqaA= +github.com/gdamore/tcell/v2 v2.7.0/go.mod h1:hl/KtAANGBecfIPxk+FzKvThTqI84oplgbPEmVX60b8= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -655,7 +655,6 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= @@ -1043,6 +1042,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1088,6 +1088,7 @@ golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1108,6 +1109,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1277,6 +1279,7 @@ golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4X golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/vendor/github.com/gdamore/tcell/v2/README.md b/vendor/github.com/gdamore/tcell/v2/README.md index 347e274a2..37c7dea3c 100644 --- a/vendor/github.com/gdamore/tcell/v2/README.md +++ b/vendor/github.com/gdamore/tcell/v2/README.md @@ -65,6 +65,8 @@ A brief, and still somewhat rough, [tutorial](TUTORIAL.md) is available. - [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)) ## Pure Go Terminfo Database diff --git a/vendor/github.com/gdamore/tcell/v2/cell.go b/vendor/github.com/gdamore/tcell/v2/cell.go index 756a5068d..f01b113dd 100644 --- a/vendor/github.com/gdamore/tcell/v2/cell.go +++ b/vendor/github.com/gdamore/tcell/v2/cell.go @@ -1,4 +1,4 @@ -// Copyright 2022 The TCell Authors +// Copyright 2023 The TCell Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use file except in compliance with the License. @@ -28,9 +28,10 @@ type cell struct { lastStyle Style lastComb []rune width int + lock bool } -// CellBuffer represents a two dimensional array of character cells. +// CellBuffer represents a two-dimensional array of character cells. // This is primarily intended for use by Screen implementors; it // contains much of the common code they need. To create one, just // declare a variable of its type; no explicit initialization is necessary. @@ -43,10 +44,12 @@ type CellBuffer struct { } // SetContent sets the contents (primary rune, combining runes, -// and style) for a cell at a given location. +// and style) for a cell at a given location. If the background or +// foreground of the style is set to ColorNone, then the respective +// color is left un changed. func (cb *CellBuffer) SetContent(x int, y int, - mainc rune, combc []rune, style Style) { - + mainc rune, combc []rune, style Style, +) { if x >= 0 && y >= 0 && x < cb.w && y < cb.h { c := &cb.cells[(y*cb.w)+x] @@ -60,6 +63,12 @@ func (cb *CellBuffer) SetContent(x int, y int, c.width = runewidth.RuneWidth(mainc) } c.currMain = mainc + if style.fg == ColorNone { + style.fg = c.currStyle.fg + } + if style.bg == ColorNone { + style.bg = c.currStyle.bg + } c.currStyle = style } } @@ -96,13 +105,15 @@ func (cb *CellBuffer) Invalidate() { } } -// Dirty checks if a character at the given location needs an -// to be refreshed on the physical display. This returns true -// if the cell content is different since the last time it was -// marked clean. +// Dirty checks if a character at the given location needs to be +// refreshed on the physical display. This returns true if the cell +// content is different since the last time it was marked clean. func (cb *CellBuffer) Dirty(x, y int) bool { if x >= 0 && y >= 0 && x < cb.w && y < cb.h { c := &cb.cells[(y*cb.w)+x] + if c.lock { + return false + } if c.lastMain == rune(0) { return true } @@ -143,11 +154,39 @@ func (cb *CellBuffer) SetDirty(x, y int, dirty bool) { } } +// LockCell locks a cell from being drawn, effectively marking it "clean" until +// the lock is removed. This can be used to prevent tcell from drawing a given +// cell, even if the underlying content has changed. For example, when drawing a +// sixel graphic directly to a TTY screen an implementer must lock the region +// underneath the graphic to prevent tcell from drawing on top of the graphic. +func (cb *CellBuffer) LockCell(x, y int) { + if x < 0 || y < 0 { + return + } + if x >= cb.w || y >= cb.h { + return + } + c := &cb.cells[(y*cb.w)+x] + c.lock = true +} + +// UnlockCell removes a lock from the cell and marks it as dirty +func (cb *CellBuffer) UnlockCell(x, y int) { + if x < 0 || y < 0 { + return + } + if x >= cb.w || y >= cb.h { + return + } + c := &cb.cells[(y*cb.w)+x] + c.lock = false + cb.SetDirty(x, y, true) +} + // Resize is used to resize the cells array, with different dimensions, // while preserving the original contents. The cells will be invalidated // so that they can be redrawn. func (cb *CellBuffer) Resize(w, h int) { - if cb.h == h && cb.w == w { return } @@ -172,12 +211,21 @@ func (cb *CellBuffer) Resize(w, h int) { // Fill fills the entire cell buffer array with the specified character // and style. Normally choose ' ' to clear the screen. This API doesn't // support combining characters, or characters with a width larger than one. +// If either the foreground or background are ColorNone, then the respective +// color is unchanged. func (cb *CellBuffer) Fill(r rune, style Style) { for i := range cb.cells { c := &cb.cells[i] c.currMain = r c.currComb = nil - c.currStyle = style + cs := style + if cs.fg == ColorNone { + cs.fg = c.currStyle.fg + } + if cs.bg == ColorNone { + cs.bg = c.currStyle.bg + } + c.currStyle = cs c.width = 1 } } @@ -192,7 +240,7 @@ func init() { runewidth.DefaultCondition.EastAsianWidth = false } - // For performance reasons, we create a lookup table. However some users + // For performance reasons, we create a lookup table. However, some users // might be more memory conscious. If that's you, set the TCELL_MINIMIZE // environment variable. if os.Getenv("TCELL_MINIMIZE") == "" { diff --git a/vendor/github.com/gdamore/tcell/v2/color.go b/vendor/github.com/gdamore/tcell/v2/color.go index e6581b0f6..face860fb 100644 --- a/vendor/github.com/gdamore/tcell/v2/color.go +++ b/vendor/github.com/gdamore/tcell/v2/color.go @@ -1,4 +1,4 @@ -// Copyright 2020 The TCell Authors +// Copyright 2023 The TCell Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use file except in compliance with the License. @@ -15,6 +15,7 @@ package tcell import ( + "fmt" ic "image/color" "strconv" ) @@ -838,6 +839,11 @@ const ( // ColorReset is used to indicate that the color should use the // vanilla terminal colors. (Basically go back to the defaults.) ColorReset = ColorSpecial | iota + + // ColorNone indicates that we should not change the color from + // whatever is already displayed. This can only be used in limited + // circumstances. + ColorNone ) // ColorNames holds the written names of colors. Useful to present a list of @@ -1001,6 +1007,47 @@ func (c Color) IsRGB() bool { return c&(ColorValid|ColorIsRGB) == (ColorValid | ColorIsRGB) } +// CSS returns the CSS hex string ( #ABCDEF ) if valid +// if not a valid color returns empty string +func (c Color) CSS() string { + if !c.Valid() { + return "" + } + return fmt.Sprintf("#%06X", c.Hex()) +} + +// String implements fmt.Stringer to return either the +// W3C name if it has one or the CSS hex string '#ABCDEF' +func (c Color) String() string { + if !c.Valid() { + switch c { + case ColorNone: + return "none" + case ColorDefault: + return "default" + case ColorReset: + return "reset" + } + return "" + } + return c.Name(true) +} + +// Name returns W3C name or an empty string if no arguments +// if passed true as an argument it will falls back to +// the CSS hex string if no W3C name found '#ABCDEF' +func (c Color) Name(css ...bool) string { + for name, hex := range ColorNames { + if c == hex { + return name + } + } + if len(css) > 0 && css[0] { + return c.CSS() + } + return "" +} + // Hex returns the color's hexadecimal RGB 24-bit value with each component // consisting of a single byte, R << 16 | G << 8 | B. If the color // is unknown or unset, -1 is returned. diff --git a/vendor/github.com/gdamore/tcell/v2/console_win.go b/vendor/github.com/gdamore/tcell/v2/console_win.go index 5f0063e26..a4036680f 100644 --- a/vendor/github.com/gdamore/tcell/v2/console_win.go +++ b/vendor/github.com/gdamore/tcell/v2/console_win.go @@ -1,7 +1,7 @@ //go:build windows // +build windows -// Copyright 2022 The TCell Authors +// Copyright 2023 The TCell Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use file except in compliance with the License. @@ -33,12 +33,10 @@ type cScreen struct { out syscall.Handle cancelflag syscall.Handle scandone chan struct{} - evch chan Event quit chan struct{} curx int cury int style Style - clear bool fini bool vten bool truecolor bool @@ -54,11 +52,11 @@ type cScreen struct { oomode uint32 cells CellBuffer - finiOnce sync.Once - mouseEnabled bool wg sync.WaitGroup + eventQ chan Event stopQ chan struct{} + finiOnce sync.Once sync.Mutex } @@ -147,7 +145,7 @@ const ( vtSgr0 = "\x1b[0m" vtBold = "\x1b[1m" vtUnderline = "\x1b[4m" - vtBlink = "\x1b[5m" // Not sure this is processed + vtBlink = "\x1b[5m" // Not sure if this is processed vtReverse = "\x1b[7m" vtSetFg = "\x1b[38;5;%dm" vtSetBg = "\x1b[48;5;%dm" @@ -176,11 +174,11 @@ var vtCursorStyles = map[CursorStyle]string{ // with the current process. The Screen makes use of the Windows Console // API to display content and read events. func NewConsoleScreen() (Screen, error) { - return &cScreen{}, nil + return &baseScreen{screenImpl: &cScreen{}}, nil } func (s *cScreen) Init() error { - s.evch = make(chan Event, 10) + s.eventQ = make(chan Event, 10) s.quit = make(chan struct{}) s.scandone = make(chan struct{}) @@ -231,7 +229,7 @@ func (s *cScreen) Init() error { // 24-bit color is opt-in for now, because we can't figure out // to make it work consistently. if s.truecolor { - s.setOutMode(modeVtOutput | modeNoAutoNL | modeCookedOut) + s.setOutMode(modeVtOutput | modeNoAutoNL | modeCookedOut | modeUnderline) var om uint32 s.getOutMode(&om) if om&modeVtOutput == modeVtOutput { @@ -282,8 +280,15 @@ func (s *cScreen) EnablePaste() {} func (s *cScreen) DisablePaste() {} +func (s *cScreen) EnableFocus() {} + +func (s *cScreen) DisableFocus() {} + func (s *cScreen) Fini() { - s.disengage() + s.finiOnce.Do(func() { + close(s.quit) + s.disengage() + }) } func (s *cScreen) disengage() { @@ -334,7 +339,7 @@ func (s *cScreen) engage() error { s.enableMouse(s.mouseEnabled) if s.vten { - s.setOutMode(modeVtOutput | modeNoAutoNL | modeCookedOut) + s.setOutMode(modeVtOutput | modeNoAutoNL | modeCookedOut | modeUnderline) } else { s.setOutMode(0) } @@ -353,52 +358,6 @@ func (s *cScreen) engage() error { return nil } -func (s *cScreen) PostEventWait(ev Event) { - s.evch <- ev -} - -func (s *cScreen) PostEvent(ev Event) error { - select { - case s.evch <- ev: - return nil - default: - return ErrEventQFull - } -} - -func (s *cScreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { - defer close(ch) - for { - select { - case <-quit: - return - case <-s.stopQ: - return - case ev := <-s.evch: - select { - case <-quit: - return - case <-s.stopQ: - return - case ch <- ev: - } - } - } -} - -func (s *cScreen) PollEvent() Event { - select { - case <-s.stopQ: - return nil - case ev := <-s.evch: - return ev - } -} - -func (s *cScreen) HasPendingEvent() bool { - return len(s.evch) > 0 -} - type cursorInfo struct { size uint32 visible uint32 @@ -698,6 +657,13 @@ func mrec2btns(mbtns, flags uint32) ButtonMask { return btns } +func (s *cScreen) postEvent(ev Event) { + select { + case s.eventQ <- ev: + case <-s.quit: + } +} + func (s *cScreen) getConsoleInput() error { // cancelFlag comes first as WaitForMultipleObjects returns the lowest index // in the event that both events are signalled. @@ -740,7 +706,7 @@ func (s *cScreen) getConsoleInput() error { krec.mod = getu32(rec.data[12:]) if krec.isdown == 0 || krec.repeat < 1 { - // its a key release event, ignore it + // it's a key release event, ignore it return nil } if krec.ch != 0 { @@ -748,11 +714,9 @@ func (s *cScreen) getConsoleInput() error { for krec.repeat > 0 { // convert shift+tab to backtab if mod2mask(krec.mod) == ModShift && krec.ch == vkTab { - s.PostEventWait(NewEventKey(KeyBacktab, 0, - ModNone)) + s.postEvent(NewEventKey(KeyBacktab, 0, ModNone)) } else { - s.PostEventWait(NewEventKey(KeyRune, rune(krec.ch), - mod2mask(krec.mod))) + s.postEvent(NewEventKey(KeyRune, rune(krec.ch), mod2mask(krec.mod))) } krec.repeat-- } @@ -764,8 +728,7 @@ func (s *cScreen) getConsoleInput() error { return nil } for krec.repeat > 0 { - s.PostEventWait(NewEventKey(key, rune(krec.ch), - mod2mask(krec.mod))) + s.postEvent(NewEventKey(key, rune(krec.ch), mod2mask(krec.mod))) krec.repeat-- } @@ -778,14 +741,13 @@ 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.PostEventWait(NewEventMouse(int(mrec.x), int(mrec.y), btns, - mod2mask(mrec.mod))) + s.postEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns, mod2mask(mrec.mod))) case resizeEvent: var rrec resizeRecord rrec.x = geti16(rec.data[0:]) rrec.y = geti16(rec.data[2:]) - s.PostEventWait(NewEventResize(int(rrec.x), int(rrec.y))) + s.postEvent(NewEventResize(int(rrec.x), int(rrec.y))) default: } @@ -891,29 +853,6 @@ func (s *cScreen) mapStyle(style Style) uint16 { return attr } -func (s *cScreen) SetCell(x, y int, style Style, ch ...rune) { - if len(ch) > 0 { - s.SetContent(x, y, ch[0], ch[1:], style) - } else { - s.SetContent(x, y, ' ', nil, style) - } -} - -func (s *cScreen) SetContent(x, y int, primary rune, combining []rune, style Style) { - s.Lock() - if !s.fini { - s.cells.SetContent(x, y, primary, combining, style) - } - s.Unlock() -} - -func (s *cScreen) GetContent(x, y int) (rune, []rune, Style, int) { - s.Lock() - primary, combining, style, width := s.cells.GetContent(x, y) - s.Unlock() - return primary, combining, style, width -} - func (s *cScreen) sendVtStyle(style Style) { esc := &strings.Builder{} @@ -968,11 +907,6 @@ func (s *cScreen) writeString(x, y int, style Style, ch []uint16) { 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. - if s.clear { - s.clearScreen(s.style, s.vten) - s.clear = false - s.cells.Invalidate() - } buf := make([]uint16, 0, s.w) wcs := buf[:] lstyle := styleInvalid @@ -1153,20 +1087,10 @@ func (s *cScreen) resize() { uintptr(s.out), uintptr(1), uintptr(unsafe.Pointer(&r))) - _ = s.PostEvent(NewEventResize(w, h)) -} - -func (s *cScreen) Clear() { - s.Fill(' ', s.style) -} - -func (s *cScreen) Fill(r rune, style Style) { - s.Lock() - if !s.fini { - s.cells.Fill(r, style) - s.clear = true + select { + case s.eventQ <- NewEventResize(w, h): + default: } - s.Unlock() } func (s *cScreen) clearScreen(style Style, vtEnable bool) { @@ -1213,6 +1137,7 @@ const ( modeCookedOut uint32 = 0x0001 modeVtOutput = 0x0004 modeNoAutoNL = 0x0008 + modeUnderline = 0x0010 // ENABLE_LVB_GRID_WORLDWIDE, needed for underlines // modeWrapEOL = 0x0002 ) @@ -1327,3 +1252,19 @@ func (s *cScreen) Suspend() error { func (s *cScreen) Resume() error { return s.engage() } + +func (s *cScreen) Tty() (Tty, bool) { + return nil, false +} + +func (s *cScreen) GetCells() *CellBuffer { + return &s.cells +} + +func (s *cScreen) EventQ() chan Event { + return s.eventQ +} + +func (s *cScreen) StopQ() <-chan struct{} { + return s.stopQ +} diff --git a/vendor/github.com/gdamore/tcell/v2/encoding.go b/vendor/github.com/gdamore/tcell/v2/encoding.go index 8bb449d67..b7644c27e 100644 --- a/vendor/github.com/gdamore/tcell/v2/encoding.go +++ b/vendor/github.com/gdamore/tcell/v2/encoding.go @@ -69,7 +69,6 @@ var encodingFallback EncodingFallback = EncodingFallbackFail // The East Asian encodings have been seen to add 100-200K per encoding to the // size of the resulting binary. -// func RegisterEncoding(charset string, enc encoding.Encoding) { encodingLk.Lock() charset = strings.ToLower(charset) diff --git a/vendor/github.com/gdamore/tcell/v2/focus.go b/vendor/github.com/gdamore/tcell/v2/focus.go new file mode 100644 index 000000000..e9b93ef6d --- /dev/null +++ b/vendor/github.com/gdamore/tcell/v2/focus.go @@ -0,0 +1,28 @@ +// Copyright 2023 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 + +// EventFocus is a focus event. It is sent when the terminal window (or tab) +// gets or loses focus. +type EventFocus struct { + *EventTime + + // True if the window received focus, false if it lost focus + Focused bool +} + +func NewEventFocus(focused bool) *EventFocus { + return &EventFocus{Focused: focused} +} diff --git a/vendor/github.com/gdamore/tcell/v2/resize.go b/vendor/github.com/gdamore/tcell/v2/resize.go index 0385673c8..f3e2b3a5f 100644 --- a/vendor/github.com/gdamore/tcell/v2/resize.go +++ b/vendor/github.com/gdamore/tcell/v2/resize.go @@ -20,15 +20,18 @@ import ( // EventResize is sent when the window size changes. type EventResize struct { - t time.Time - w int - h int + t time.Time + ws WindowSize } // NewEventResize creates an EventResize with the new updated window size, // which is given in character cells. func NewEventResize(width, height int) *EventResize { - return &EventResize{t: time.Now(), w: width, h: height} + ws := WindowSize{ + Width: width, + Height: height, + } + return &EventResize{t: time.Now(), ws: ws} } // When returns the time when the Event was created. @@ -38,5 +41,26 @@ func (ev *EventResize) When() time.Time { // Size returns the new window size as width, height in character cells. func (ev *EventResize) Size() (int, int) { - return ev.w, ev.h + return ev.ws.Width, ev.ws.Height +} + +// PixelSize returns the new window size as width, height in pixels. The size +// will be 0,0 if the screen doesn't support this feature +func (ev *EventResize) PixelSize() (int, int) { + return ev.ws.PixelWidth, ev.ws.PixelHeight +} + +type WindowSize struct { + Width int + Height int + PixelWidth int + PixelHeight int +} + +// CellDimensions returns the dimensions of a single cell, in pixels +func (ws WindowSize) CellDimensions() (int, int) { + if ws.PixelWidth == 0 || ws.PixelHeight == 0 { + return 0, 0 + } + return (ws.PixelWidth / ws.Width), (ws.PixelHeight / ws.Height) } diff --git a/vendor/github.com/gdamore/tcell/v2/screen.go b/vendor/github.com/gdamore/tcell/v2/screen.go index 5598a8c7a..6ab27ca96 100644 --- a/vendor/github.com/gdamore/tcell/v2/screen.go +++ b/vendor/github.com/gdamore/tcell/v2/screen.go @@ -1,4 +1,4 @@ -// Copyright 2022 The TCell Authors +// Copyright 2023 The TCell Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use file except in compliance with the License. @@ -14,6 +14,8 @@ package tcell +import "sync" + // Screen represents the physical (or emulated) screen. // This can be a terminal window or a physical console. Platforms implement // this differently. @@ -139,6 +141,12 @@ type Screen interface { // DisablePaste disables bracketed paste mode. DisablePaste() + // EnableFocus enables reporting of focus events, if your terminal supports it. + EnableFocus() + + // DisableFocus disables reporting of focus events. + DisableFocus() + // HasMouse returns true if the terminal (apparently) supports a // mouse. Note that the return value of true doesn't guarantee that // a mouse/pointing device is present; a false return definitely @@ -249,6 +257,14 @@ type Screen interface { // does not support application-initiated resizing, whereas the legacy terminal does. // Also, some emulators can support this but may have it disabled by default. SetSize(int, int) + + // LockRegion sets or unsets a lock on a region of cells. A lock on a + // cell prevents the cell from being redrawn. + LockRegion(x, y, width, height int, lock bool) + + // Tty returns the underlying Tty. If the screen is not a terminal, the + // returned bool will be false + Tty() (Tty, bool) } // NewScreen returns a default Screen suitable for the user's terminal @@ -287,3 +303,164 @@ const ( CursorStyleBlinkingBar CursorStyleSteadyBar ) + +// screenImpl is a subset of Screen that can be used with baseScreen to formulate +// a complete implementation of Screen. See Screen for doc comments about methods. +type screenImpl interface { + Init() error + Fini() + SetStyle(style Style) + ShowCursor(x int, y int) + HideCursor() + SetCursorStyle(CursorStyle) + Size() (width, height int) + EnableMouse(...MouseFlags) + DisableMouse() + EnablePaste() + DisablePaste() + EnableFocus() + DisableFocus() + HasMouse() bool + Colors() int + Show() + Sync() + CharacterSet() string + RegisterRuneFallback(r rune, subst string) + UnregisterRuneFallback(r rune) + CanDisplay(r rune, checkFallbacks bool) bool + Resize(int, int, int, int) + HasKey(Key) bool + Suspend() error + Resume() error + Beep() error + SetSize(int, int) + Tty() (Tty, bool) + + // Following methods are not part of the Screen api, but are used for interaction with + // the common layer code. + + // Locker locks the underlying data structures so that we can access them + // in a thread-safe way. + sync.Locker + + // GetCells returns a pointer to the underlying CellBuffer that the implementation uses. + // Various methods will write to these for performance, but will use the lock to do so. + GetCells() *CellBuffer + + // StopQ is closed when the screen is shut down via Fini. It remains open if the screen + // is merely suspended. + StopQ() <-chan struct{} + + // EventQ delivers events. Events are posted to this by the screen in response to + // key presses, resizes, etc. Application code receives events from this via the + // Screen.PollEvent, Screen.ChannelEvents APIs. + EventQ() chan Event +} + +type baseScreen struct { + screenImpl +} + +func (b *baseScreen) SetCell(x int, y int, style Style, ch ...rune) { + if len(ch) > 0 { + b.SetContent(x, y, ch[0], ch[1:], style) + } else { + b.SetContent(x, y, ' ', nil, style) + } +} + +func (b *baseScreen) Clear() { + b.Fill(' ', StyleDefault) +} + +func (b *baseScreen) Fill(r rune, style Style) { + cb := b.GetCells() + b.Lock() + cb.Fill(r, style) + b.Unlock() +} + +func (b *baseScreen) SetContent(x, y int, mainc rune, combc []rune, st Style) { + + cells := b.GetCells() + b.Lock() + cells.SetContent(x, y, mainc, combc, st) + b.Unlock() +} + +func (b *baseScreen) GetContent(x, y int) (rune, []rune, Style, int) { + var primary rune + var combining []rune + var style Style + var width int + cells := b.GetCells() + b.Lock() + primary, combining, style, width = cells.GetContent(x, y) + b.Unlock() + return primary, combining, style, width +} + +func (b *baseScreen) LockRegion(x, y, width, height int, lock bool) { + cells := b.GetCells() + b.Lock() + for j := y; j < (y + height); j += 1 { + for i := x; i < (x + width); i += 1 { + switch lock { + case true: + cells.LockCell(i, j) + case false: + cells.UnlockCell(i, j) + } + } + } + b.Unlock() +} + +func (b *baseScreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { + defer close(ch) + for { + select { + case <-quit: + return + case <-b.StopQ(): + return + case ev := <-b.EventQ(): + select { + case <-quit: + return + case <-b.StopQ(): + return + case ch <- ev: + } + } + } +} + +func (b *baseScreen) PollEvent() Event { + select { + case <-b.StopQ(): + return nil + case ev := <-b.EventQ(): + return ev + } +} + +func (b *baseScreen) HasPendingEvent() bool { + return len(b.EventQ()) > 0 +} + +func (b *baseScreen) PostEventWait(ev Event) { + select { + case b.EventQ() <- ev: + case <-b.StopQ(): + } +} + +func (b *baseScreen) PostEvent(ev Event) error { + select { + case b.EventQ() <- ev: + return nil + default: + return ErrEventQFull + } +} diff --git a/vendor/github.com/gdamore/tcell/v2/simulation.go b/vendor/github.com/gdamore/tcell/v2/simulation.go index 9ad6131ec..eb08b8fe9 100644 --- a/vendor/github.com/gdamore/tcell/v2/simulation.go +++ b/vendor/github.com/gdamore/tcell/v2/simulation.go @@ -1,4 +1,4 @@ -// Copyright 2022 The TCell Authors +// Copyright 2023 The TCell Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use file except in compliance with the License. @@ -27,14 +27,17 @@ func NewSimulationScreen(charset string) SimulationScreen { if charset == "" { charset = "UTF-8" } - s := &simscreen{charset: charset} - return s + ss := &simscreen{charset: charset} + ss.Screen = &baseScreen{screenImpl: ss} + return ss } // SimulationScreen represents a screen simulation. This is intended to // be a superset of normal Screens, but also adds some important interfaces // for testing. type SimulationScreen interface { + Screen + // InjectKeyBytes injects a stream of bytes corresponding to // the native encoding (see charset). It turns true if the entire // set of bytes were processed and delivered as KeyEvents, false @@ -57,8 +60,6 @@ type SimulationScreen interface { // GetCursor returns the cursor details. GetCursor() (x int, y int, visible bool) - - Screen } // SimCell represents a simulated screen cell. The purpose of this @@ -98,6 +99,7 @@ type simscreen struct { fillstyle Style fallback map[rune]string + Screen sync.Mutex } @@ -150,43 +152,6 @@ func (s *simscreen) SetStyle(style Style) { s.Unlock() } -func (s *simscreen) Clear() { - s.Fill(' ', s.style) -} - -func (s *simscreen) Fill(r rune, style Style) { - s.Lock() - s.back.Fill(r, style) - s.Unlock() -} - -func (s *simscreen) SetCell(x, y int, style Style, ch ...rune) { - - if len(ch) > 0 { - s.SetContent(x, y, ch[0], ch[1:], style) - } else { - s.SetContent(x, y, ' ', nil, style) - } -} - -func (s *simscreen) SetContent(x, y int, mainc rune, combc []rune, st Style) { - - s.Lock() - s.back.SetContent(x, y, mainc, combc, st) - s.Unlock() -} - -func (s *simscreen) GetContent(x, y int) (rune, []rune, Style, int) { - var mainc rune - var combc []rune - var style Style - var width int - s.Lock() - mainc, combc, style, width = s.back.GetContent(x, y) - s.Unlock() - return mainc, combc, style, width -} - func (s *simscreen) drawCell(x, y int) int { mainc, combc, style, width := s.back.GetContent(x, y) @@ -325,6 +290,12 @@ func (s *simscreen) DisablePaste() { s.paste = false } +func (s *simscreen) EnableFocus() { +} + +func (s *simscreen) DisableFocus() { +} + func (s *simscreen) Size() (int, int) { s.Lock() w, h := s.back.Size() @@ -338,7 +309,7 @@ func (s *simscreen) resize() { if w != ow || h != oh { s.back.Resize(w, h) ev := NewEventResize(w, h) - s.PostEvent(ev) + s.postEvent(ev) } } @@ -346,60 +317,21 @@ func (s *simscreen) Colors() int { return 256 } -func (s *simscreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { - defer close(ch) - for { - select { - case <-quit: - return - case <-s.quit: - return - case ev := <-s.evch: - select { - case <-quit: - return - case <-s.quit: - return - case ch <- ev: - } - } - } -} - -func (s *simscreen) PollEvent() Event { - select { - case <-s.quit: - return nil - case ev := <-s.evch: - return ev - } -} - -func (s *simscreen) HasPendingEvent() bool { - return len(s.evch) > 0 -} - -func (s *simscreen) PostEventWait(ev Event) { - s.evch <- ev -} - -func (s *simscreen) PostEvent(ev Event) error { +func (s *simscreen) postEvent(ev Event) { select { case s.evch <- ev: - return nil - default: - return ErrEventQFull + case <-s.quit: } } func (s *simscreen) InjectMouse(x, y int, buttons ButtonMask, mod ModMask) { ev := NewEventMouse(x, y, buttons, mod) - s.PostEvent(ev) + s.postEvent(ev) } func (s *simscreen) InjectKey(key Key, r rune, mod ModMask) { ev := NewEventKey(key, r, mod) - s.PostEvent(ev) + s.postEvent(ev) } func (s *simscreen) InjectKeyBytes(b []byte) bool { @@ -410,7 +342,7 @@ outer: if b[0] >= ' ' && b[0] <= 0x7F { // printable ASCII easy to deal with -- no encodings ev := NewEventKey(KeyRune, rune(b[0]), ModNone) - s.PostEvent(ev) + s.postEvent(ev) b = b[1:] continue } @@ -422,7 +354,7 @@ outer: mod = ModCtrl } ev := NewEventKey(Key(b[0]), 0, mod) - s.PostEvent(ev) + s.postEvent(ev) b = b[1:] continue } @@ -436,7 +368,7 @@ outer: r, _ := utf8.DecodeRune(utfb[:nout]) if r != utf8.RuneError { ev := NewEventKey(KeyRune, r, ModNone) - s.PostEvent(ev) + s.postEvent(ev) } b = b[nin:] continue outer @@ -547,3 +479,19 @@ func (s *simscreen) Suspend() error { func (s *simscreen) Resume() error { return nil } + +func (s *simscreen) Tty() (Tty, bool) { + return nil, false +} + +func (s *simscreen) GetCells() *CellBuffer { + return &s.back +} + +func (s *simscreen) EventQ() chan Event { + return s.evch +} + +func (s *simscreen) StopQ() <-chan struct{} { + return s.quit +} diff --git a/vendor/github.com/gdamore/tcell/v2/stdin_unix.go b/vendor/github.com/gdamore/tcell/v2/stdin_unix.go index 4c0d6e12d..b478b8918 100644 --- a/vendor/github.com/gdamore/tcell/v2/stdin_unix.go +++ b/vendor/github.com/gdamore/tcell/v2/stdin_unix.go @@ -27,6 +27,7 @@ import ( "syscall" "time" + "golang.org/x/sys/unix" "golang.org/x/term" ) @@ -133,11 +134,14 @@ func (tty *stdIoTty) Stop() error { return nil } -func (tty *stdIoTty) WindowSize() (int, int, error) { - w, h, err := term.GetSize(tty.fd) +func (tty *stdIoTty) WindowSize() (WindowSize, error) { + size := WindowSize{} + ws, err := unix.IoctlGetWinsize(tty.fd, unix.TIOCGWINSZ) if err != nil { - return 0, 0, err + return size, err } + w := int(ws.Col) + h := int(ws.Row) if w == 0 { w, _ = strconv.Atoi(os.Getenv("COLUMNS")) } @@ -150,7 +154,11 @@ func (tty *stdIoTty) WindowSize() (int, int, error) { if h == 0 { h = 25 // default } - return w, h, nil + size.Width = w + size.Height = h + size.PixelWidth = int(ws.Xpixel) + size.PixelHeight = int(ws.Ypixel) + return size, nil } func (tty *stdIoTty) NotifyResize(cb func()) { diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/dynamic/dynamic.go b/vendor/github.com/gdamore/tcell/v2/terminfo/dynamic/dynamic.go index 08ff24842..047ebded6 100644 --- a/vendor/github.com/gdamore/tcell/v2/terminfo/dynamic/dynamic.go +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/dynamic/dynamic.go @@ -185,16 +185,10 @@ func (tc *termcap) setupterm(name string) error { func LoadTerminfo(name string) (*terminfo.Terminfo, string, error) { var tc termcap if err := tc.setupterm(name); err != nil { - if err != nil { - return nil, "", err - } + return nil, "", err } t := &terminfo.Terminfo{} - // If this is an alias record, then just emit the alias t.Name = tc.name - if t.Name != name { - return t, "", nil - } t.Aliases = tc.aliases t.Colors = tc.getnum("colors") t.Columns = tc.getnum("cols") @@ -314,6 +308,8 @@ func LoadTerminfo(name string) (*terminfo.Terminfo, string, error) { // but modern XTerm and emulators often have them. Let's add them, // if the shifted right and left arrows are defined. if t.KeyShfRight == "\x1b[1;2C" && t.KeyShfLeft == "\x1b[1;2D" { + t.Modifiers = terminfo.ModifiersXTerm + t.KeyShfUp = "\x1b[1;2A" t.KeyShfDown = "\x1b[1;2B" t.KeyMetaUp = "\x1b[1;9A" diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/gen.sh b/vendor/github.com/gdamore/tcell/v2/terminfo/gen.sh index 2fc061123..851175a3f 100644 --- a/vendor/github.com/gdamore/tcell/v2/terminfo/gen.sh +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/gen.sh @@ -1,3 +1,4 @@ +#!/bin/bash while read line do case "$line" in diff --git a/vendor/github.com/gdamore/tcell/v2/terminfo/terminfo.go b/vendor/github.com/gdamore/tcell/v2/terminfo/terminfo.go index cf1578648..c248dd49c 100644 --- a/vendor/github.com/gdamore/tcell/v2/terminfo/terminfo.go +++ b/vendor/github.com/gdamore/tcell/v2/terminfo/terminfo.go @@ -230,6 +230,8 @@ type Terminfo struct { EnterUrl string ExitUrl string SetWindowSize string + EnableFocusReporting string + DisableFocusReporting string } const ( diff --git a/vendor/github.com/gdamore/tcell/v2/tscreen.go b/vendor/github.com/gdamore/tcell/v2/tscreen.go index e36e6e41f..b804affd0 100644 --- a/vendor/github.com/gdamore/tcell/v2/tscreen.go +++ b/vendor/github.com/gdamore/tcell/v2/tscreen.go @@ -1,4 +1,4 @@ -// Copyright 2022 The TCell Authors +// Copyright 2023 The TCell Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use file except in compliance with the License. @@ -94,7 +94,7 @@ func NewTerminfoScreenFromTtyTerminfo(tty Tty, ti *terminfo.Terminfo) (s Screen, t.fallback[k] = v } - return t, nil + return &baseScreen{screenImpl: t}, nil } // NewTerminfoScreenFromTty returns a Screen using a custom Tty implementation. @@ -123,7 +123,6 @@ type tScreen struct { buf bytes.Buffer curstyle Style style Style - evch chan Event resizeQ chan bool quit chan struct{} keyexist map[Key]bool @@ -153,14 +152,18 @@ type tScreen struct { enterUrl string exitUrl string setWinSize string + enableFocus string + disableFocus string cursorStyles map[CursorStyle]string cursorStyle CursorStyle saved *term.State stopQ chan struct{} + eventQ chan Event running bool wg sync.WaitGroup mouseFlags MouseFlags pasteEnabled bool + focusEnabled bool sync.Mutex } @@ -170,7 +173,6 @@ func (t *tScreen) Init() error { return e } - t.evch = make(chan Event, 10) t.keychan = make(chan []byte, 10) t.keytimer = time.NewTimer(time.Millisecond * 50) t.charset = "UTF-8" @@ -214,6 +216,7 @@ func (t *tScreen) Init() error { } t.quit = make(chan struct{}) + t.eventQ = make(chan Event, 10) t.Lock() t.cx = -1 @@ -346,8 +349,8 @@ func (t *tScreen) prepareBracketedPaste() { func (t *tScreen) prepareExtendedOSC() { // Linux is a special beast - because it has a mouse entry, but does // not swallow these OSC commands properly. - if (strings.Contains(t.ti.Name, "linux")) { - return; + if strings.Contains(t.ti.Name, "linux") { + return } // More stuff for limits in terminfo. This time we are applying // the most common OSC (operating system commands). Generally @@ -366,6 +369,17 @@ func (t *tScreen) prepareExtendedOSC() { } else if t.ti.Mouse != "" { t.setWinSize = "\x1b[8;%p1%p2%d;%dt" } + + if t.ti.EnableFocusReporting != "" { + t.enableFocus = t.ti.EnableFocusReporting + } else if t.ti.Mouse != "" { + t.enableFocus = "\x1b[?1004h" + } + if t.ti.DisableFocusReporting != "" { + t.disableFocus = t.ti.DisableFocusReporting + } else if t.ti.Mouse != "" { + t.disableFocus = "\x1b[?1004l" + } } func (t *tScreen) prepareCursorStyles() { @@ -581,41 +595,6 @@ func (t *tScreen) SetStyle(style Style) { t.Unlock() } -func (t *tScreen) Clear() { - t.Fill(' ', t.style) -} - -func (t *tScreen) Fill(r rune, style Style) { - t.Lock() - if !t.fini { - t.cells.Fill(r, style) - } - t.Unlock() -} - -func (t *tScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) { - t.Lock() - if !t.fini { - t.cells.SetContent(x, y, mainc, combc, style) - } - t.Unlock() -} - -func (t *tScreen) GetContent(x, y int) (rune, []rune, Style, int) { - t.Lock() - mainc, combc, style, width := t.cells.GetContent(x, y) - t.Unlock() - return mainc, combc, style, width -} - -func (t *tScreen) SetCell(x, y int, style Style, ch ...rune) { - if len(ch) > 0 { - t.SetContent(x, y, ch[0], ch[1:], style) - } else { - t.SetContent(x, y, ' ', nil, style) - } -} - func (t *tScreen) encodeRune(r rune, buf []byte) []byte { nb := make([]byte, 6) @@ -1043,6 +1022,32 @@ func (t *tScreen) enablePasting(on bool) { } } +func (t *tScreen) EnableFocus() { + t.Lock() + t.focusEnabled = true + t.enableFocusReporting() + t.Unlock() +} + +func (t *tScreen) DisableFocus() { + t.Lock() + t.focusEnabled = false + t.disableFocusReporting() + t.Unlock() +} + +func (t *tScreen) enableFocusReporting() { + if t.enableFocus != "" { + t.TPuts(t.enableFocus) + } +} + +func (t *tScreen) disableFocusReporting() { + if t.disableFocus != "" { + t.TPuts(t.disableFocus) + } +} + func (t *tScreen) Size() (int, int) { t.Lock() w, h := t.w, t.h @@ -1051,18 +1056,24 @@ func (t *tScreen) Size() (int, int) { } func (t *tScreen) resize() { - if w, h, e := t.tty.WindowSize(); e == nil { - if w != t.w || h != t.h { - t.cx = -1 - t.cy = -1 + ws, err := t.tty.WindowSize() + if err != nil { + return + } + if ws.Width == t.w && ws.Height == t.h { + return + } + t.cx = -1 + t.cy = -1 - t.cells.Resize(w, h) - t.cells.Invalidate() - t.h = h - t.w = w - ev := NewEventResize(w, h) - _ = t.PostEvent(ev) - } + t.cells.Resize(ws.Width, ws.Height) + t.cells.Invalidate() + t.h = ws.Height + t.w = ws.Width + ev := &EventResize{t: time.Now(), ws: ws} + select { + case t.eventQ <- ev: + default: } } @@ -1081,39 +1092,6 @@ func (t *tScreen) nColors() int { return t.ti.Colors } -func (t *tScreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { - defer close(ch) - for { - select { - case <-quit: - return - case <-t.quit: - return - case ev := <-t.evch: - select { - case <-quit: - return - case <-t.quit: - return - case ch <- ev: - } - } - } -} - -func (t *tScreen) PollEvent() Event { - select { - case <-t.quit: - return nil - case ev := <-t.evch: - return ev - } -} - -func (t *tScreen) HasPendingEvent() bool { - return len(t.evch) > 0 -} - // vtACSNames is a map of bytes defined by terminfo that are used in // the terminals Alternate Character Set to represent other glyphs. // For example, the upper left corner of the box drawing set can be @@ -1178,19 +1156,6 @@ func (t *tScreen) buildAcsMap() { } } -func (t *tScreen) PostEventWait(ev Event) { - t.evch <- ev -} - -func (t *tScreen) PostEvent(ev Event) error { - select { - case t.evch <- ev: - return nil - default: - return ErrEventQFull - } -} - func (t *tScreen) clip(x, y int) (int, int) { w, h := t.cells.Size() if x < 0 { @@ -1271,6 +1236,7 @@ func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { dig := false neg := false motion := false + scroll := false i := 0 val := 0 @@ -1345,6 +1311,7 @@ func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { y = val - 1 motion = (btn & 32) != 0 + scroll = (btn & 0x42) == 0x40 btn &^= 32 if b[i] == 'm' { // mouse release, clear all buttons @@ -1363,7 +1330,7 @@ func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { btn |= 3 btn &^= 0x40 } - } else { + } else if !scroll { t.buttondn = true } // consume the event bytes @@ -1380,6 +1347,35 @@ func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { return true, false } +func (t *tScreen) parseFocus(buf *bytes.Buffer, evs *[]Event) (bool, bool) { + state := 0 + b := buf.Bytes() + for i := range b { + switch state { + case 0: + if b[i] != '\x1b' { + return false, false + } + state = 1 + case 1: + if b[i] != '[' { + return false, false + } + state = 2 + case 2: + if b[i] != 'I' && b[i] != 'O' { + return false, false + } + *evs = append(*evs, NewEventFocus(b[i] == 'I')) + _, _ = buf.ReadByte() + _, _ = buf.ReadByte() + _, _ = buf.ReadByte() + return true, true + } + } + return true, false +} + // parseXtermMouse is like parseSgrMouse, but it parses a legacy // X11 mouse record. func (t *tScreen) parseXtermMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { @@ -1521,7 +1517,11 @@ func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) { evs := t.collectEventsFromInput(buf, expire) for _, ev := range evs { - t.PostEventWait(ev) + select { + case t.eventQ <- ev: + case <-t.quit: + return + } } } @@ -1556,6 +1556,12 @@ func (t *tScreen) collectEventsFromInput(buf *bytes.Buffer, expire bool) []Event partials++ } + if part, comp := t.parseFocus(buf, &res); comp { + continue + } else if part { + partials++ + } + // Only parse mouse records if this term claims to have // mouse support @@ -1584,7 +1590,7 @@ func (t *tScreen) collectEventsFromInput(buf *bytes.Buffer, expire bool) []Event _, _ = buf.ReadByte() continue } - // Nothing was going to match, or we timed out + // Nothing was going to match, or we timed-out // waiting for more data -- just deliver the characters // to the app & let them sort it out. Possibly we // should only do this for control characters like ESC. @@ -1679,7 +1685,10 @@ func (t *tScreen) inputLoop(stopQ chan struct{}) { running := t.running t.Unlock() if running { - _ = t.PostEvent(NewEventError(e)) + select { + case t.eventQ <- NewEventError(e): + case <-t.quit: + } } return } @@ -1775,6 +1784,10 @@ func (t *tScreen) Resume() error { return t.engage() } +func (t *tScreen) Tty() (Tty, bool) { + return t.tty, true +} + // engage is used to place the terminal in raw mode and establish screen size, etc. // Think of this is as tcell "engaging" the clutch, as it's going to be driving the // terminal interface. @@ -1797,13 +1810,16 @@ func (t *tScreen) engage() error { return err } t.running = true - if w, h, err := t.tty.WindowSize(); err == nil && w != 0 && h != 0 { - t.cells.Resize(w, h) + if ws, err := t.tty.WindowSize(); err == nil && ws.Width != 0 && ws.Height != 0 { + t.cells.Resize(ws.Width, ws.Height) } stopQ := make(chan struct{}) t.stopQ = stopQ t.enableMouse(t.mouseFlags) t.enablePasting(t.pasteEnabled) + if t.focusEnabled { + t.enableFocusReporting() + } ti := t.ti t.TPuts(ti.EnterCA) @@ -1844,7 +1860,7 @@ func (t *tScreen) disengage() { t.cells.Resize(0, 0) t.TPuts(ti.ShowCursor) if t.cursorStyles != nil && t.cursorStyle != CursorStyleDefault { - t.TPuts(t.cursorStyles[t.cursorStyle]) + t.TPuts(t.cursorStyles[CursorStyleDefault]) } t.TPuts(ti.ResetFgBg) t.TPuts(ti.AttrOff) @@ -1853,6 +1869,7 @@ func (t *tScreen) disengage() { t.TPuts(ti.ExitKeypad) t.enableMouse(0) t.enablePasting(false) + t.disableFocusReporting() _ = t.tty.Stop() } @@ -1869,3 +1886,15 @@ func (t *tScreen) finalize() { t.disengage() _ = t.tty.Close() } + +func (t *tScreen) StopQ() <-chan struct{} { + return t.stopQ +} + +func (t *tScreen) EventQ() chan Event { + return t.eventQ +} + +func (t *tScreen) GetCells() *CellBuffer { + return &t.cells +} diff --git a/vendor/github.com/gdamore/tcell/v2/tty.go b/vendor/github.com/gdamore/tcell/v2/tty.go index 1e7c02e4e..8bb1ac506 100644 --- a/vendor/github.com/gdamore/tcell/v2/tty.go +++ b/vendor/github.com/gdamore/tcell/v2/tty.go @@ -50,7 +50,7 @@ type Tty interface { // WindowSize is called to determine the terminal dimensions. This might be determined // by an ioctl or other means. - WindowSize() (width int, height int, err error) + WindowSize() (WindowSize, error) io.ReadWriteCloser } diff --git a/vendor/github.com/gdamore/tcell/v2/tty_unix.go b/vendor/github.com/gdamore/tcell/v2/tty_unix.go index 05d5a7dd0..ca82d83d8 100644 --- a/vendor/github.com/gdamore/tcell/v2/tty_unix.go +++ b/vendor/github.com/gdamore/tcell/v2/tty_unix.go @@ -27,6 +27,7 @@ import ( "syscall" "time" + "golang.org/x/sys/unix" "golang.org/x/term" ) @@ -136,11 +137,14 @@ func (tty *devTty) Stop() error { return nil } -func (tty *devTty) WindowSize() (int, int, error) { - w, h, err := term.GetSize(tty.fd) +func (tty *devTty) WindowSize() (WindowSize, error) { + size := WindowSize{} + ws, err := unix.IoctlGetWinsize(tty.fd, unix.TIOCGWINSZ) if err != nil { - return 0, 0, err + return size, err } + w := int(ws.Col) + h := int(ws.Row) if w == 0 { w, _ = strconv.Atoi(os.Getenv("COLUMNS")) } @@ -153,7 +157,11 @@ func (tty *devTty) WindowSize() (int, int, error) { if h == 0 { h = 25 // default } - return w, h, nil + size.Width = w + size.Height = h + size.PixelWidth = int(ws.Xpixel) + size.PixelHeight = int(ws.Ypixel) + return size, nil } func (tty *devTty) NotifyResize(cb func()) { diff --git a/vendor/github.com/gdamore/tcell/v2/wscreen.go b/vendor/github.com/gdamore/tcell/v2/wscreen.go index 080472065..137968cc4 100644 --- a/vendor/github.com/gdamore/tcell/v2/wscreen.go +++ b/vendor/github.com/gdamore/tcell/v2/wscreen.go @@ -19,6 +19,7 @@ package tcell import ( "errors" + "github.com/gdamore/tcell/v2/terminfo" "strings" "sync" "syscall/js" @@ -29,7 +30,7 @@ func NewTerminfoScreen() (Screen, error) { t := &wScreen{} t.fallback = make(map[rune]string) - return t, nil + return &baseScreen{screenImpl: t}, nil } type wScreen struct { @@ -48,6 +49,7 @@ type wScreen struct { quit chan struct{} evch chan Event fallback map[rune]string + finiOnce sync.Once sync.Mutex } @@ -69,7 +71,9 @@ func (t *wScreen) Init() error { } func (t *wScreen) Fini() { - close(t.quit) + t.finiOnce.Do(func() { + close(t.quit) + }) } func (t *wScreen) SetStyle(style Style) { @@ -78,65 +82,34 @@ func (t *wScreen) SetStyle(style Style) { t.Unlock() } -func (t *wScreen) Clear() { - t.Fill(' ', t.style) -} - -func (t *wScreen) Fill(r rune, style Style) { - t.Lock() - t.cells.Fill(r, style) - t.Unlock() -} - -func (t *wScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) { - t.Lock() - t.cells.SetContent(x, y, mainc, combc, style) - t.Unlock() -} - -func (t *wScreen) GetContent(x, y int) (rune, []rune, Style, int) { - t.Lock() - mainc, combc, style, width := t.cells.GetContent(x, y) - t.Unlock() - return mainc, combc, style, width -} - -func (t *wScreen) SetCell(x, y int, style Style, ch ...rune) { - if len(ch) > 0 { - t.SetContent(x, y, ch[0], ch[1:], style) - } else { - t.SetContent(x, y, ' ', nil, style) - } -} - // paletteColor gives a more natural palette color actually matching // typical XTerm. We might in the future want to permit styling these // via CSS. var palette = map[Color]int32{ - ColorBlack: 0x000000, - ColorMaroon: 0xcd0000, - ColorGreen: 0x00cd00, - ColorOlive: 0xcdcd00, - ColorNavy: 0x0000ee, - ColorPurple: 0xcd00cd, - ColorTeal: 0x00cdcd, - ColorSilver: 0xe5e5e5, - ColorGray: 0x7f7f7f, - ColorRed: 0xff0000, - ColorLime: 0x00ff00, - ColorYellow: 0xffff00, - ColorBlue: 0x5c5cff, + ColorBlack: 0x000000, + ColorMaroon: 0xcd0000, + ColorGreen: 0x00cd00, + ColorOlive: 0xcdcd00, + ColorNavy: 0x0000ee, + ColorPurple: 0xcd00cd, + ColorTeal: 0x00cdcd, + ColorSilver: 0xe5e5e5, + ColorGray: 0x7f7f7f, + ColorRed: 0xff0000, + ColorLime: 0x00ff00, + ColorYellow: 0xffff00, + ColorBlue: 0x5c5cff, ColorFuchsia: 0xff00ff, - ColorAqua: 0x00ffff, - ColorWhite: 0xffffff, + ColorAqua: 0x00ffff, + ColorWhite: 0xffffff, } func paletteColor(c Color) int32 { - if (c.IsRGB()) { - return int32(c & 0xffffff); + if c.IsRGB() { + return int32(c & 0xffffff) } - if (c >= ColorBlack && c <= ColorWhite) { + if c >= ColorBlack && c <= ColorWhite { return palette[c] } return c.Hex() @@ -154,11 +127,11 @@ func (t *wScreen) drawCell(x, y int) int { } fg, bg := paletteColor(style.fg), paletteColor(style.bg) - if (fg == -1) { - fg = 0xe5e5e5; + if fg == -1 { + fg = 0xe5e5e5 } - if (bg == -1) { - bg = 0x000000; + if bg == -1 { + bg = 0x000000 } var combcarr []interface{} = make([]interface{}, len(combc)) @@ -275,6 +248,18 @@ func (t *wScreen) enablePasting(on bool) { } } +func (t *wScreen) EnableFocus() { + t.Lock() + js.Global().Set("onFocus", js.FuncOf(t.onFocus)) + t.Unlock() +} + +func (t *wScreen) DisableFocus() { + t.Lock() + js.Global().Set("onFocus", js.FuncOf(t.unset)) + t.Unlock() +} + func (t *wScreen) Size() (int, int) { t.Lock() w, h := t.w, t.h @@ -290,52 +275,6 @@ func (t *wScreen) Colors() int { return 16777216 // 256 ^ 3 } -func (t *wScreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) { - defer close(ch) - for { - select { - case <-quit: - return - case <-t.quit: - return - case ev := <-t.evch: - select { - case <-quit: - return - case <-t.quit: - return - case ch <- ev: - } - } - } -} - -func (t *wScreen) PollEvent() Event { - select { - case <-t.quit: - return nil - case ev := <-t.evch: - return ev - } -} - -func (t *wScreen) HasPendingEvent() bool { - return len(t.evch) > 0 -} - -func (t *wScreen) PostEventWait(ev Event) { - t.evch <- ev -} - -func (t *wScreen) PostEvent(ev Event) error { - select { - case t.evch <- ev: - return nil - default: - return ErrEventQFull - } -} - func (t *wScreen) clip(x, y int) (int, int) { w, h := t.cells.Size() if x < 0 { @@ -353,6 +292,13 @@ func (t *wScreen) clip(x, y int) (int, int) { return x, y } +func (t *wScreen) postEvent(ev Event) { + select { + case t.evch <- ev: + case <-t.quit: + } +} + func (t *wScreen) onMouseEvent(this js.Value, args []js.Value) interface{} { mod := ModNone button := ButtonNone @@ -384,7 +330,7 @@ func (t *wScreen) onMouseEvent(this js.Value, args []js.Value) interface{} { mod |= ModCtrl } - t.PostEventWait(NewEventMouse(args[0].Int(), args[1].Int(), button, mod)) + t.postEvent(NewEventMouse(args[0].Int(), args[1].Int(), button, mod)) return nil } @@ -416,25 +362,30 @@ func (t *wScreen) onKeyEvent(this js.Value, args []js.Value) interface{} { // check for special case of Ctrl + key if mod == ModCtrl { if k, ok := WebKeyNames["Ctrl-"+strings.ToLower(key)]; ok { - t.PostEventWait(NewEventKey(k, 0, mod)) + t.postEvent(NewEventKey(k, 0, mod)) return nil } } // next try function keys if k, ok := WebKeyNames[key]; ok { - t.PostEventWait(NewEventKey(k, 0, mod)) + t.postEvent(NewEventKey(k, 0, mod)) return nil } // finally try normal, printable chars r, _ := utf8.DecodeRuneInString(key) - t.PostEventWait(NewEventKey(KeyRune, r, mod)) + t.postEvent(NewEventKey(KeyRune, r, mod)) return nil } func (t *wScreen) onPaste(this js.Value, args []js.Value) interface{} { - t.PostEventWait(NewEventPaste(args[0].Bool())) + t.postEvent(NewEventPaste(args[0].Bool())) + return nil +} + +func (t *wScreen) onFocus(this js.Value, args []js.Value) interface{} { + t.postEvent(NewEventFocus(args[0].Bool())) return nil } @@ -501,7 +452,7 @@ func (t *wScreen) SetSize(w, h int) { t.cells.Resize(w, h) js.Global().Call("resize", w, h) t.w, t.h = w, h - t.PostEvent(NewEventResize(w, h)) + t.postEvent(NewEventResize(w, h)) } func (t *wScreen) Resize(int, int, int, int) {} @@ -544,6 +495,22 @@ func (t *wScreen) Beep() error { return nil } +func (t *wScreen) Tty() (Tty, bool) { + return nil, false +} + +func (t *wScreen) GetCells() *CellBuffer { + return &t.cells +} + +func (t *wScreen) EventQ() chan Event { + return t.evch +} + +func (t *wScreen) StopQ() <-chan struct{} { + return t.quit +} + // WebKeyNames maps string names reported from HTML // (KeyboardEvent.key) to tcell accepted keys. var WebKeyNames = map[string]Key{ @@ -676,3 +643,7 @@ var curStyleClasses = map[CursorStyle]string{ CursorStyleBlinkingBar: "cursor-blinking-bar", CursorStyleSteadyBar: "cursor-steady-bar", } + +func LookupTerminfo(name string) (ti *terminfo.Terminfo, e error) { + return nil, errors.New("LookupTermInfo not supported") +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4da381ea4..c029c880d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -352,7 +352,7 @@ github.com/fsnotify/fsnotify # github.com/gdamore/encoding v1.0.0 ## explicit; go 1.9 github.com/gdamore/encoding -# github.com/gdamore/tcell/v2 v2.6.0 +# github.com/gdamore/tcell/v2 v2.7.0 ## explicit; go 1.12 github.com/gdamore/tcell/v2 github.com/gdamore/tcell/v2/terminfo