aboutsummaryrefslogtreecommitdiff
path: root/vendor/maunium.net/go/tcell/tscreen.go
diff options
context:
space:
mode:
authorTulir Asokan <tulir@maunium.net>2019-01-11 23:28:47 +0200
committerTulir Asokan <tulir@maunium.net>2019-01-11 23:28:47 +0200
commit331597b9f8a7942cbcb233a328301e4d5bf94fb0 (patch)
tree5ec624585ebf66c63549a098acb6f7421f1193a7 /vendor/maunium.net/go/tcell/tscreen.go
parent2fc3378b717f40f37f3a188b68407887242d9c06 (diff)
Switch to Go modules and make other changes
Diffstat (limited to 'vendor/maunium.net/go/tcell/tscreen.go')
-rw-r--r--vendor/maunium.net/go/tcell/tscreen.go1524
1 files changed, 0 insertions, 1524 deletions
diff --git a/vendor/maunium.net/go/tcell/tscreen.go b/vendor/maunium.net/go/tcell/tscreen.go
deleted file mode 100644
index dd49814..0000000
--- a/vendor/maunium.net/go/tcell/tscreen.go
+++ /dev/null
@@ -1,1524 +0,0 @@
-// Copyright 2017 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 (
- "bytes"
- "io"
- "os"
- "strconv"
- "strings"
- "sync"
- "time"
- "unicode/utf8"
-
- "golang.org/x/text/transform"
-
- "maunium.net/go/tcell/terminfo"
-)
-
-// NewTerminfoScreen returns a Screen that uses the stock TTY interface
-// and POSIX termios, combined with a terminfo description taken from
-// the $TERM environment variable. It returns an error if the terminal
-// is not supported for any reason.
-//
-// For terminals that do not support dynamic resize events, the $LINES
-// $COLUMNS environment variables can be set to the actual window size,
-// otherwise defaults taken from the terminal database are used.
-func NewTerminfoScreen() (Screen, error) {
- ti, e := terminfo.LookupTerminfo(os.Getenv("TERM"))
- if e != nil {
- return nil, e
- }
- t := &tScreen{ti: ti}
-
- t.keyexist = make(map[Key]bool)
- t.keycodes = make(map[string]*tKeyCode)
- if len(ti.Mouse) > 0 {
- t.mouse = []byte(ti.Mouse)
- }
- t.prepareKeys()
- t.buildAcsMap()
- t.sigwinch = make(chan os.Signal, 10)
- t.fallback = make(map[rune]string)
- for k, v := range RuneFallbacks {
- t.fallback[k] = v
- }
-
- return t, nil
-}
-
-// tKeyCode represents a combination of a key code and modifiers.
-type tKeyCode struct {
- key Key
- mod ModMask
-}
-
-// tScreen represents a screen backed by a terminfo implementation.
-type tScreen struct {
- ti *terminfo.Terminfo
- h int
- w int
- fini bool
- cells CellBuffer
- in *os.File
- out io.Writer
- curstyle Style
- style Style
- evch chan Event
- sigwinch chan os.Signal
- quit chan struct{}
- indoneq chan struct{}
- keyexist map[Key]bool
- keycodes map[string]*tKeyCode
- keychan chan []byte
- keytimer *time.Timer
- keyexpire time.Time
- cx int
- cy int
- mouse []byte
- clear bool
- cursorx int
- cursory int
- tiosp *termiosPrivate
- baud int
- wasbtn bool
- acs map[rune]string
- charset string
- encoder transform.Transformer
- decoder transform.Transformer
- fallback map[rune]string
- colors map[Color]Color
- palette []Color
- truecolor bool
- escaped bool
- buttondn bool
- hasSetTitle bool
-
- sync.Mutex
-}
-
-func (t *tScreen) Init() error {
- t.evch = make(chan Event, 10)
- t.indoneq = make(chan struct{})
- t.keychan = make(chan []byte, 10)
- t.keytimer = time.NewTimer(time.Millisecond * 50)
- t.charset = "UTF-8"
-
- t.charset = getCharset()
- if enc := GetEncoding(t.charset); enc != nil {
- t.encoder = enc.NewEncoder()
- t.decoder = enc.NewDecoder()
- } else {
- return ErrNoCharset
- }
- ti := t.ti
-
- // environment overrides
- w := ti.Columns
- h := ti.Lines
- if i, _ := strconv.Atoi(os.Getenv("LINES")); i != 0 {
- h = i
- }
- if i, _ := strconv.Atoi(os.Getenv("COLUMNS")); i != 0 {
- w = i
- }
- if e := t.termioInit(); e != nil {
- return e
- }
-
- if t.ti.SetFgBgRGB != "" || t.ti.SetFgRGB != "" || t.ti.SetBgRGB != "" {
- t.truecolor = true
- }
- // A user who wants to have his themes honored can
- // set this environment variable.
- if os.Getenv("TCELL_TRUECOLOR") == "disable" {
- t.truecolor = false
- }
- if !t.truecolor {
- t.colors = make(map[Color]Color)
- t.palette = make([]Color, t.Colors())
- for i := 0; i < t.Colors(); i++ {
- t.palette[i] = Color(i)
- // identity map for our builtin colors
- t.colors[Color(i)] = Color(i)
- }
- }
-
- t.TPuts(ti.EnterCA)
- t.TPuts(ti.HideCursor)
- t.TPuts(ti.EnableAcs)
- t.TPuts(ti.Clear)
- t.TPuts("\x1b[?2004h")
-
- t.quit = make(chan struct{})
-
- t.Lock()
- t.cx = -1
- t.cy = -1
- t.style = StyleDefault
- t.cells.Resize(w, h)
- t.cursorx = -1
- t.cursory = -1
- t.resize()
- t.Unlock()
-
- go t.mainLoop()
- go t.inputLoop()
-
- return nil
-}
-
-func (t *tScreen) prepareKeyMod(key Key, mod ModMask, val string) {
- if val != "" {
- // Do not overrride codes that already exist
- if _, exist := t.keycodes[val]; !exist {
- t.keyexist[key] = true
- t.keycodes[val] = &tKeyCode{key: key, mod: mod}
- }
- }
-}
-
-func (t *tScreen) prepareKey(key Key, val string) {
- t.prepareKeyMod(key, ModNone, val)
-}
-
-func (t *tScreen) prepareKeys() {
- ti := t.ti
- t.prepareKey(KeyBackspace, ti.KeyBackspace)
- t.prepareKey(KeyF1, ti.KeyF1)
- t.prepareKey(KeyF2, ti.KeyF2)
- t.prepareKey(KeyF3, ti.KeyF3)
- t.prepareKey(KeyF4, ti.KeyF4)
- t.prepareKey(KeyF5, ti.KeyF5)
- t.prepareKey(KeyF6, ti.KeyF6)
- t.prepareKey(KeyF7, ti.KeyF7)
- t.prepareKey(KeyF8, ti.KeyF8)
- t.prepareKey(KeyF9, ti.KeyF9)
- t.prepareKey(KeyF10, ti.KeyF10)
- t.prepareKey(KeyF11, ti.KeyF11)
- t.prepareKey(KeyF12, ti.KeyF12)
- t.prepareKey(KeyF13, ti.KeyF13)
- t.prepareKey(KeyF14, ti.KeyF14)
- t.prepareKey(KeyF15, ti.KeyF15)
- t.prepareKey(KeyF16, ti.KeyF16)
- t.prepareKey(KeyF17, ti.KeyF17)
- t.prepareKey(KeyF18, ti.KeyF18)
- t.prepareKey(KeyF19, ti.KeyF19)
- t.prepareKey(KeyF20, ti.KeyF20)
- t.prepareKey(KeyF21, ti.KeyF21)
- t.prepareKey(KeyF22, ti.KeyF22)
- t.prepareKey(KeyF23, ti.KeyF23)
- t.prepareKey(KeyF24, ti.KeyF24)
- t.prepareKey(KeyF25, ti.KeyF25)
- t.prepareKey(KeyF26, ti.KeyF26)
- t.prepareKey(KeyF27, ti.KeyF27)
- t.prepareKey(KeyF28, ti.KeyF28)
- t.prepareKey(KeyF29, ti.KeyF29)
- t.prepareKey(KeyF30, ti.KeyF30)
- t.prepareKey(KeyF31, ti.KeyF31)
- t.prepareKey(KeyF32, ti.KeyF32)
- t.prepareKey(KeyF33, ti.KeyF33)
- t.prepareKey(KeyF34, ti.KeyF34)
- t.prepareKey(KeyF35, ti.KeyF35)
- t.prepareKey(KeyF36, ti.KeyF36)
- t.prepareKey(KeyF37, ti.KeyF37)
- t.prepareKey(KeyF38, ti.KeyF38)
- t.prepareKey(KeyF39, ti.KeyF39)
- t.prepareKey(KeyF40, ti.KeyF40)
- t.prepareKey(KeyF41, ti.KeyF41)
- t.prepareKey(KeyF42, ti.KeyF42)
- t.prepareKey(KeyF43, ti.KeyF43)
- t.prepareKey(KeyF44, ti.KeyF44)
- t.prepareKey(KeyF45, ti.KeyF45)
- t.prepareKey(KeyF46, ti.KeyF46)
- t.prepareKey(KeyF47, ti.KeyF47)
- t.prepareKey(KeyF48, ti.KeyF48)
- t.prepareKey(KeyF49, ti.KeyF49)
- t.prepareKey(KeyF50, ti.KeyF50)
- t.prepareKey(KeyF51, ti.KeyF51)
- t.prepareKey(KeyF52, ti.KeyF52)
- t.prepareKey(KeyF53, ti.KeyF53)
- t.prepareKey(KeyF54, ti.KeyF54)
- t.prepareKey(KeyF55, ti.KeyF55)
- t.prepareKey(KeyF56, ti.KeyF56)
- t.prepareKey(KeyF57, ti.KeyF57)
- t.prepareKey(KeyF58, ti.KeyF58)
- t.prepareKey(KeyF59, ti.KeyF59)
- t.prepareKey(KeyF60, ti.KeyF60)
- t.prepareKey(KeyF61, ti.KeyF61)
- t.prepareKey(KeyF62, ti.KeyF62)
- t.prepareKey(KeyF63, ti.KeyF63)
- t.prepareKey(KeyF64, ti.KeyF64)
- t.prepareKey(KeyInsert, ti.KeyInsert)
- t.prepareKey(KeyDelete, ti.KeyDelete)
- t.prepareKey(KeyHome, ti.KeyHome)
- t.prepareKey(KeyEnd, ti.KeyEnd)
- t.prepareKey(KeyUp, ti.KeyUp)
- t.prepareKey(KeyDown, ti.KeyDown)
- t.prepareKey(KeyLeft, ti.KeyLeft)
- t.prepareKey(KeyRight, ti.KeyRight)
- t.prepareKey(KeyPgUp, ti.KeyPgUp)
- t.prepareKey(KeyPgDn, ti.KeyPgDn)
- t.prepareKey(KeyHelp, ti.KeyHelp)
- t.prepareKey(KeyPrint, ti.KeyPrint)
- t.prepareKey(KeyCancel, ti.KeyCancel)
- t.prepareKey(KeyExit, ti.KeyExit)
- t.prepareKey(KeyBacktab, ti.KeyBacktab)
-
- t.prepareKeyMod(KeyRight, ModShift, ti.KeyShfRight)
- t.prepareKeyMod(KeyLeft, ModShift, ti.KeyShfLeft)
- t.prepareKeyMod(KeyUp, ModShift, ti.KeyShfUp)
- t.prepareKeyMod(KeyDown, ModShift, ti.KeyShfDown)
- t.prepareKeyMod(KeyHome, ModShift, ti.KeyShfHome)
- t.prepareKeyMod(KeyEnd, ModShift, ti.KeyShfEnd)
-
- t.prepareKeyMod(KeyRight, ModCtrl, ti.KeyCtrlRight)
- t.prepareKeyMod(KeyLeft, ModCtrl, ti.KeyCtrlLeft)
- t.prepareKeyMod(KeyUp, ModCtrl, ti.KeyCtrlUp)
- t.prepareKeyMod(KeyDown, ModCtrl, ti.KeyCtrlDown)
- t.prepareKeyMod(KeyHome, ModCtrl, ti.KeyCtrlHome)
- t.prepareKeyMod(KeyEnd, ModCtrl, ti.KeyCtrlEnd)
-
- t.prepareKeyMod(KeyRight, ModAlt, ti.KeyAltRight)
- t.prepareKeyMod(KeyLeft, ModAlt, ti.KeyAltLeft)
- t.prepareKeyMod(KeyUp, ModAlt, ti.KeyAltUp)
- t.prepareKeyMod(KeyDown, ModAlt, ti.KeyAltDown)
- t.prepareKeyMod(KeyHome, ModAlt, ti.KeyAltHome)
- t.prepareKeyMod(KeyEnd, ModAlt, ti.KeyAltEnd)
-
- t.prepareKeyMod(KeyRight, ModAlt, ti.KeyMetaRight)
- t.prepareKeyMod(KeyLeft, ModAlt, ti.KeyMetaLeft)
- t.prepareKeyMod(KeyUp, ModAlt, ti.KeyMetaUp)
- t.prepareKeyMod(KeyDown, ModAlt, ti.KeyMetaDown)
- t.prepareKeyMod(KeyHome, ModAlt, ti.KeyMetaHome)
- t.prepareKeyMod(KeyEnd, ModAlt, ti.KeyMetaEnd)
-
- t.prepareKeyMod(KeyRight, ModAlt|ModShift, ti.KeyAltShfRight)
- t.prepareKeyMod(KeyLeft, ModAlt|ModShift, ti.KeyAltShfLeft)
- t.prepareKeyMod(KeyUp, ModAlt|ModShift, ti.KeyAltShfUp)
- t.prepareKeyMod(KeyDown, ModAlt|ModShift, ti.KeyAltShfDown)
- t.prepareKeyMod(KeyHome, ModAlt|ModShift, ti.KeyAltShfHome)
- t.prepareKeyMod(KeyEnd, ModAlt|ModShift, ti.KeyAltShfEnd)
-
- t.prepareKeyMod(KeyRight, ModAlt|ModShift, ti.KeyMetaShfRight)
- t.prepareKeyMod(KeyLeft, ModAlt|ModShift, ti.KeyMetaShfLeft)
- t.prepareKeyMod(KeyUp, ModAlt|ModShift, ti.KeyMetaShfUp)
- t.prepareKeyMod(KeyDown, ModAlt|ModShift, ti.KeyMetaShfDown)
- t.prepareKeyMod(KeyHome, ModAlt|ModShift, ti.KeyMetaShfHome)
- t.prepareKeyMod(KeyEnd, ModAlt|ModShift, ti.KeyMetaShfEnd)
-
- t.prepareKeyMod(KeyRight, ModCtrl|ModShift, ti.KeyCtrlShfRight)
- t.prepareKeyMod(KeyLeft, ModCtrl|ModShift, ti.KeyCtrlShfLeft)
- t.prepareKeyMod(KeyUp, ModCtrl|ModShift, ti.KeyCtrlShfUp)
- t.prepareKeyMod(KeyDown, ModCtrl|ModShift, ti.KeyCtrlShfDown)
- t.prepareKeyMod(KeyHome, ModCtrl|ModShift, ti.KeyCtrlShfHome)
- t.prepareKeyMod(KeyEnd, ModCtrl|ModShift, ti.KeyCtrlShfEnd)
-
- // Sadly, xterm handling of keycodes is somewhat erratic. In
- // particular, different codes are sent depending on application
- // mode is in use or not, and the entries for many of these are
- // simply absent from terminfo on many systems. So we insert
- // a number of escape sequences if they are not already used, in
- // order to have the widest correct usage. Note that prepareKey
- // will not inject codes if the escape sequence is already known.
- // We also only do this for terminals that have the application
- // mode present.
-
- // Cursor mode
- if ti.EnterKeypad != "" {
- t.prepareKey(KeyUp, "\x1b[A")
- t.prepareKey(KeyDown, "\x1b[B")
- t.prepareKey(KeyRight, "\x1b[C")
- t.prepareKey(KeyLeft, "\x1b[D")
- t.prepareKey(KeyEnd, "\x1b[F")
- t.prepareKey(KeyHome, "\x1b[H")
- t.prepareKey(KeyDelete, "\x1b[3~")
- t.prepareKey(KeyHome, "\x1b[1~")
- t.prepareKey(KeyEnd, "\x1b[4~")
- t.prepareKey(KeyPgUp, "\x1b[5~")
- t.prepareKey(KeyPgDn, "\x1b[6~")
-
- // Application mode
- t.prepareKey(KeyUp, "\x1bOA")
- t.prepareKey(KeyDown, "\x1bOB")
- t.prepareKey(KeyRight, "\x1bOC")
- t.prepareKey(KeyLeft, "\x1bOD")
- t.prepareKey(KeyHome, "\x1bOH")
-
- // Extra arrow key commands
- t.prepareKey(KeyAltUp, "\x1b[1;9A")
- t.prepareKey(KeyAltDown, "\x1b[1;9B")
- t.prepareKey(KeyAltLeft, "\x1b[1;9D")
- t.prepareKey(KeyAltRight, "\x1b[1;9C")
- t.prepareKey(KeyAltUp, "\x1b\x1b[A")
- t.prepareKey(KeyAltDown, "\x1b\x1b[B")
- t.prepareKey(KeyAltLeft, "\x1b\x1b[D")
- t.prepareKey(KeyAltRight, "\x1b\x1b[C")
- t.prepareKey(KeyAltUp, "\x1b[1;3A")
- t.prepareKey(KeyAltDown, "\x1b[1;3B")
- t.prepareKey(KeyAltLeft, "\x1b[1;3D")
- t.prepareKey(KeyAltRight, "\x1b[1;3C")
- t.prepareKey(KeyShiftUp, "\x1b[1;2A")
- t.prepareKey(KeyShiftDown, "\x1b[1;2B")
- t.prepareKey(KeyShiftLeft, "\x1b[1;2D")
- t.prepareKey(KeyShiftRight, "\x1b[1;2C")
- t.prepareKey(KeyCtrlUp, "\x1b[1;5A")
- t.prepareKey(KeyCtrlDown, "\x1b[1;5B")
- t.prepareKey(KeyCtrlLeft, "\x1b[1;5D")
- t.prepareKey(KeyCtrlRight, "\x1b[1;5C")
- t.prepareKey(KeyAltShiftUp, "\x1b[1;10A")
- t.prepareKey(KeyAltShiftDown, "\x1b[1;10B")
- t.prepareKey(KeyAltShiftLeft, "\x1b[1;10D")
- t.prepareKey(KeyAltShiftRight, "\x1b[1;10C")
- t.prepareKey(KeyAltShiftUp, "\x1b[1;4A")
- t.prepareKey(KeyAltShiftDown, "\x1b[1;4B")
- t.prepareKey(KeyAltShiftLeft, "\x1b[1;4D")
- t.prepareKey(KeyAltShiftRight, "\x1b[1;4C")
- t.prepareKey(KeyCtrlShiftUp, "\x1b[1;6A")
- t.prepareKey(KeyCtrlShiftDown, "\x1b[1;6B")
- t.prepareKey(KeyCtrlShiftLeft, "\x1b[1;6D")
- t.prepareKey(KeyCtrlShiftRight, "\x1b[1;6C")
- t.prepareKeyMod(KeyCtrlPgUp, ModCtrl, "\x1b[5;5~")
- t.prepareKeyMod(KeyCtrlPgDn, ModCtrl, "\x1b[6;5~")
- }
-
-outer:
- // Add key mappings for control keys.
- for i := 0; i < ' '; i++ {
- // Do not insert direct key codes for ambiguous keys.
- // For example, ESC is used for lots of other keys, so
- // when parsing this we don't want to fast path handling
- // of it, but instead wait a bit before parsing it as in
- // isolation.
- for esc := range t.keycodes {
- if []byte(esc)[0] == byte(i) {
- continue outer
- }
- }
-
- t.keyexist[Key(i)] = true
-
- mod := ModCtrl
- switch Key(i) {
- case KeyBS, KeyTAB, KeyESC, KeyCR:
- // directly typeable- no control sequence
- mod = ModNone
- }
- t.keycodes[string(rune(i))] = &tKeyCode{key: Key(i), mod: mod}
- }
-}
-
-func (t *tScreen) ResetTitle() {
- if t.hasSetTitle {
- //Reset terminal title. USERNAME for Windows support. Assumes USER and USERNAME will not both be set.
- wd, _ := os.Getwd()
- host, _ := os.Hostname()
- var titlestring string
- if strings.Contains(os.Getenv("TERM"), "xterm") {
- titlestring = "\033]2;" + os.Getenv("USER") + os.Getenv("USERNAME") + "@" + host + ": " + wd + "\007"
- t.TPuts(titlestring)
- }
- if os.Getenv("TERM") == "screen" {
- for _, s := range strings.Split(os.Getenv("SHELL"), "/") {
- titlestring = "\033k" + s + "\033\\"
- titlestring = "\033]2;" + os.Getenv("USER") + os.Getenv("USERNAME") + "@" + host + ": " + wd + "\007"
- }
- t.TPuts(titlestring)
- }
- }
-}
-
-func (t *tScreen) Fini() {
- t.Lock()
- defer t.Unlock()
-
- ti := t.ti
- t.cells.Resize(0, 0)
- t.TPuts(ti.ShowCursor)
- t.TPuts(ti.AttrOff)
- t.ResetTitle()
- t.TPuts(ti.Clear)
- t.TPuts(ti.ExitCA)
- t.TPuts(ti.ExitKeypad)
- // Close bracketed paste
- t.TPuts("\x1b[?2004l")
- t.DisableMouse()
- t.curstyle = Style(-1)
- t.clear = false
- t.fini = true
-
- select {
- case <-t.quit:
- // do nothing, already closed
-
- default:
- close(t.quit)
- }
-
- t.termioFini()
-}
-
-func (t *tScreen) SetStyle(style Style) {
- t.Lock()
- if !t.fini {
- t.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)
- ob := make([]byte, 6)
- num := utf8.EncodeRune(ob, r)
- ob = ob[:num]
- dst := 0
- var err error
- if enc := t.encoder; enc != nil {
- enc.Reset()
- dst, _, err = enc.Transform(nb, ob, true)
- }
- if err != nil || dst == 0 || nb[0] == '\x1a' {
- // Combining characters are elided
- if len(buf) == 0 {
- if acs, ok := t.acs[r]; ok {
- buf = append(buf, []byte(acs)...)
- } else if fb, ok := t.fallback[r]; ok {
- buf = append(buf, []byte(fb)...)
- } else {
- buf = append(buf, '?')
- }
- }
- } else {
- buf = append(buf, nb[:dst]...)
- }
-
- return buf
-}
-
-func (t *tScreen) sendFgBg(fg Color, bg Color) {
- ti := t.ti
- if ti.Colors == 0 {
- return
- }
- if t.truecolor {
- if ti.SetFgBgRGB != "" &&
- fg != ColorDefault && bg != ColorDefault {
- r1, g1, b1 := fg.RGB()
- r2, g2, b2 := bg.RGB()
- t.TPuts(ti.TParm(ti.SetFgBgRGB,
- int(r1), int(g1), int(b1),
- int(r2), int(g2), int(b2)))
- } else {
- if fg != ColorDefault && ti.SetFgRGB != "" {
- r, g, b := fg.RGB()
- t.TPuts(ti.TParm(ti.SetFgRGB,
- int(r), int(g), int(b)))
- }
- if bg != ColorDefault && ti.SetBgRGB != "" {
- r, g, b := bg.RGB()
- t.TPuts(ti.TParm(ti.SetBgRGB,
- int(r), int(g), int(b)))
- }
- }
- return
- }
-
- if fg != ColorDefault {
- if v, ok := t.colors[fg]; ok {
- fg = v
- } else {
- v = FindColor(fg, t.palette)
- t.colors[fg] = v
- fg = v
- }
- }
-
- if bg != ColorDefault {
- if v, ok := t.colors[bg]; ok {
- bg = v
- } else {
- v = FindColor(bg, t.palette)
- t.colors[bg] = v
- bg = v
- }
- }
-
- if ti.SetFgBg != "" && fg != ColorDefault && bg != ColorDefault {
- t.TPuts(ti.TParm(ti.SetFgBg, int(fg), int(bg)))
- } else {
- if fg != ColorDefault && ti.SetFg != "" {
- t.TPuts(ti.TParm(ti.SetFg, int(fg)))
- }
- if bg != ColorDefault && ti.SetBg != "" {
- t.TPuts(ti.TParm(ti.SetBg, int(bg)))
- }
- }
-}
-
-func (t *tScreen) drawCell(x, y int) int {
-
- ti := t.ti
-
- mainc, combc, style, width := t.cells.GetContent(x, y)
- if !t.cells.Dirty(x, y) {
- return width
- }
-
- if t.cy != y || t.cx != x {
- t.TPuts(ti.TGoto(x, y))
- t.cx = x
- t.cy = y
- }
-
- if style == StyleDefault {
- style = t.style
- }
- if style != t.curstyle {
- fg, bg, attrs := style.Decompose()
-
- t.TPuts(ti.AttrOff)
-
- t.sendFgBg(fg, bg)
- if attrs&AttrBold != 0 {
- t.TPuts(ti.Bold)
- }
- if attrs&AttrItalic != 0 {
- t.TPuts(ti.Italic)
- }
- if attrs&AttrStrikethrough != 0 {
- t.TPuts(ti.Strikethrough)
- }
- if attrs&AttrUnderline != 0 {
- t.TPuts(ti.Underline)
- }
- if attrs&AttrReverse != 0 {
- t.TPuts(ti.Reverse)
- }
- if attrs&AttrBlink != 0 {
- t.TPuts(ti.Blink)
- }
- if attrs&AttrDim != 0 {
- t.TPuts(ti.Dim)
- }
- t.curstyle = style
- }
- // now emit runes - taking care to not overrun width with a
- // wide character, and to ensure that we emit exactly one regular
- // character followed up by any residual combing characters
-
- if width < 1 {
- width = 1
- }
-
- var str string
-
- buf := make([]byte, 0, 6)
-
- buf = t.encodeRune(mainc, buf)
- for _, r := range combc {
- buf = t.encodeRune(r, buf)
- }
-
- str = string(buf)
- if width > 1 && str == "?" {
- // No FullWidth character support
- str = "? "
- t.cx = -1
- }
-
- // XXX: check for hazeltine not being able to display ~
-
- if x > t.w-width {
- // too wide to fit; emit a single space instead
- width = 1
- str = " "
- }
- io.WriteString(t.out, str)
- t.cx += width
- t.cells.SetDirty(x, y, false)
- if width > 1 {
- t.cx = -1
- }
-
- return width
-}
-
-func (t *tScreen) ShowCursor(x, y int) {
- t.Lock()
- t.cursorx = x
- t.cursory = y
- t.Unlock()
-}
-
-func (t *tScreen) HideCursor() {
- t.ShowCursor(-1, -1)
-}
-
-func (t *tScreen) showCursor() {
-
- x, y := t.cursorx, t.cursory
- w, h := t.cells.Size()
- if x < 0 || y < 0 || x >= w || y >= h {
- t.hideCursor()
- return
- }
- t.TPuts(t.ti.TGoto(x, y))
- t.TPuts(t.ti.ShowCursor)
- t.cx = x
- t.cy = y
-}
-
-func (t *tScreen) TPuts(s string) {
- t.ti.TPuts(t.out, s, t.baud)
-}
-
-func (t *tScreen) Show() {
- t.Lock()
- if !t.fini {
- t.resize()
- t.draw()
- }
- t.Unlock()
-}
-
-func (t *tScreen) clearScreen() {
- fg, bg, _ := t.style.Decompose()
- t.sendFgBg(fg, bg)
- t.TPuts(t.ti.Clear)
- t.clear = false
-}
-
-func (t *tScreen) hideCursor() {
- // does not update cursor position
- if t.ti.HideCursor != "" {
- t.TPuts(t.ti.HideCursor)
- } else {
- // No way to hide cursor, stick it
- // at bottom right of screen
- t.cx, t.cy = t.cells.Size()
- t.TPuts(t.ti.TGoto(t.cx, t.cy))
- }
-}
-
-func (t *tScreen) draw() {
- // Buffer all output instead of sending it directly to the terminal
- // We'll send it when the draw is over
- buf := &bytes.Buffer{}
- out := t.out
- t.out = buf
-
- // clobber cursor position, because we're gonna change it all
- t.cx = -1
- t.cy = -1
-
- // hide the cursor while we move stuff around
- t.hideCursor()
-
- if t.clear {
- t.clearScreen()
- }
-
- for y := 0; y < t.h; y++ {
- for x := 0; x < t.w; x++ {
- width := t.drawCell(x, y)
- if width > 1 {
- if x+1 < t.w {
- // this is necessary so that if we ever
- // go back to drawing that cell, we
- // actually will *draw* it.
- t.cells.SetDirty(x+1, y, true)
- }
- }
- x += width - 1
- }
- }
-
- // Send everything to the terminal
- t.out = out
- io.WriteString(t.out, buf.String())
-
- // restore the cursor
- t.showCursor()
-}
-
-func (t *tScreen) EnableMouse() {
- if len(t.mouse) != 0 {
- t.TPuts(t.ti.TParm(t.ti.MouseMode, 1))
- }
-}
-
-func (t *tScreen) DisableMouse() {
- if len(t.mouse) != 0 {
- if t.ti.MouseMode == "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h" {
- t.TPuts("\x1b[?1006l\x1b[?1015l\x1b[?10021l\x1b[?1000l")
- } else {
- t.TPuts(t.ti.TParm(t.ti.MouseMode, 0))
- }
- }
-}
-
-func (t *tScreen) Size() (int, int) {
- t.Lock()
- w, h := t.w, t.h
- t.Unlock()
- return w, h
-}
-
-func (t *tScreen) resize() {
- if w, h, e := t.getWinSize(); e == nil {
- if w != t.w || h != t.h {
- 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)
- }
- }
-}
-
-func (t *tScreen) Colors() int {
- // this doesn't change, no need for lock
- if t.truecolor {
- return 1 << 24
- }
- return t.ti.Colors
-}
-
-func (t *tScreen) PollEvent() Event {
- select {
- case <-t.quit:
- return nil
- case ev := <-t.evch:
- return ev
- }
-}
-
-// 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
-// displayed by printing "l" while in the alternate character set.
-// Its not quite that simple, since the "l" is the terminfo name,
-// and it may be necessary to use a different character based on
-// the terminal implementation (or the terminal may lack support for
-// this altogether). See buildAcsMap below for detail.
-var vtACSNames = map[byte]rune{
- '+': RuneRArrow,
- ',': RuneLArrow,
- '-': RuneUArrow,
- '.': RuneDArrow,
- '0': RuneBlock,
- '`': RuneDiamond,
- 'a': RuneCkBoard,
- 'b': '␉', // VT100, Not defined by terminfo
- 'c': '␌', // VT100, Not defined by terminfo
- 'd': '␋', // VT100, Not defined by terminfo
- 'e': '␊', // VT100, Not defined by terminfo
- 'f': RuneDegree,
- 'g': RunePlMinus,
- 'h': RuneBoard,
- 'i': RuneLantern,
- 'j': RuneLRCorner,
- 'k': RuneURCorner,
- 'l': RuneULCorner,
- 'm': RuneLLCorner,
- 'n': RunePlus,
- 'o': RuneS1,
- 'p': RuneS3,
- 'q': RuneHLine,
- 'r': RuneS7,
- 's': RuneS9,
- 't': RuneLTee,
- 'u': RuneRTee,
- 'v': RuneBTee,
- 'w': RuneTTee,
- 'x': RuneVLine,
- 'y': RuneLEqual,
- 'z': RuneGEqual,
- '{': RunePi,
- '|': RuneNEqual,
- '}': RuneSterling,
- '~': RuneBullet,
-}
-
-// buildAcsMap builds a map of characters that we translate from Unicode to
-// alternate character encodings. To do this, we use the standard VT100 ACS
-// maps. This is only done if the terminal lacks support for Unicode; we
-// always prefer to emit Unicode glyphs when we are able.
-func (t *tScreen) buildAcsMap() {
- acsstr := t.ti.AltChars
- t.acs = make(map[rune]string)
- for len(acsstr) > 2 {
- srcv := acsstr[0]
- dstv := string(acsstr[1])
- if r, ok := vtACSNames[srcv]; ok {
- t.acs[r] = t.ti.EnterAcs + dstv + t.ti.ExitAcs
- }
- acsstr = acsstr[2:]
- }
-}
-
-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 {
- x = 0
- }
- if y < 0 {
- y = 0
- }
- if x > w-1 {
- x = w - 1
- }
- if y > h-1 {
- y = h - 1
- }
- return x, y
-}
-
-func (t *tScreen) postMouseEvent(x, y, btn int, motion bool) {
-
- // XTerm mouse events only report at most one button at a time,
- // which may include a wheel button. Wheel motion events are
- // reported as single impulses, while other button events are reported
- // as separate press & release events.
-
- button := ButtonNone
- mod := ModNone
-
- // Mouse wheel has bit 6 set, no release events. It should be noted
- // that wheel events are sometimes misdelivered as mouse button events
- // during a click-drag, so we debounce these, considering them to be
- // button press events unless we see an intervening release event.
- switch btn & 0x43 {
- case 0:
- button = Button1
- t.wasbtn = true
- case 1:
- button = Button2
- t.wasbtn = true
- case 2:
- button = Button3
- t.wasbtn = true
- case 3:
- button = ButtonNone
- t.wasbtn = false
- case 0x40:
- if !t.wasbtn {
- button = WheelUp
- } else {
- button = Button1
- }
- case 0x41:
- if !t.wasbtn {
- button = WheelDown
- } else {
- button = Button2
- }
- }
-
- if btn&0x4 != 0 {
- mod |= ModShift
- }
- if btn&0x8 != 0 {
- mod |= ModAlt
- }
- if btn&0x10 != 0 {
- mod |= ModCtrl
- }
-
- // Some terminals will report mouse coordinates outside the
- // screen, especially with click-drag events. Clip the coordinates
- // to the screen in that case.
- x, y = t.clip(x, y)
-
- ev := NewEventMouse(x, y, button, mod, motion)
- t.PostEvent(ev)
-}
-
-// parseSgrMouse attempts to locate an SGR mouse record at the start of the
-// buffer. It returns true, true if it found one, and the associated bytes
-// be removed from the buffer. It returns true, false if the buffer might
-// contain such an event, but more bytes are necessary (partial match), and
-// false, false if the content is definitely *not* an SGR mouse record.
-func (t *tScreen) parseSgrMouse(buf *bytes.Buffer) (bool, bool) {
-
- b := buf.Bytes()
-
- var x, y, btn, state int
- dig := false
- neg := false
- motion := false
- i := 0
- val := 0
-
- for i = range b {
- switch b[i] {
- case '\x1b':
- if state != 0 {
- return false, false
- }
- state = 1
-
- case '\x9b':
- if state != 0 {
- return false, false
- }
- state = 2
-
- case '[':
- if state != 1 {
- return false, false
- }
- state = 2
-
- case '<':
- if state != 2 {
- return false, false
- }
- val = 0
- dig = false
- neg = false
- state = 3
-
- case '-':
- if state != 3 && state != 4 && state != 5 {
- return false, false
- }
- if dig || neg {
- return false, false
- }
- neg = true // stay in state
-
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- if state != 3 && state != 4 && state != 5 {
- return false, false
- }
- val *= 10
- val += int(b[i] - '0')
- dig = true // stay in state
-
- case ';':
- if neg {
- val = -val
- }
- switch state {
- case 3:
- btn, val = val, 0
- neg, dig, state = false, false, 4
- case 4:
- x, val = val-1, 0
- neg, dig, state = false, false, 5
- default:
- return false, false
- }
-
- case 'm', 'M':
- if state != 5 {
- return false, false
- }
- if neg {
- val = -val
- }
- y = val - 1
-
- motion = (btn & 32) != 0
- btn &^= 32
- if b[i] == 'm' {
- // mouse release, clear all buttons
- btn |= 3
- btn &^= 0x40
- t.buttondn = false
- } else if motion {
- /*
- * Some broken terminals appear to send
- * mouse button one motion events, instead of
- * encoding 35 (no buttons) into these events.
- * We resolve these by looking for a non-motion
- * event first.
- */
- if !t.buttondn {
- btn |= 3
- btn &^= 0x40
- }
- } else {
- t.buttondn = true
- }
- // consume the event bytes
- for i >= 0 {
- buf.ReadByte()
- i--
- }
- t.postMouseEvent(x, y, btn, motion)
- return true, true
- }
- }
-
- // incomplete & inconclusve at this point
- return true, false
-}
-
-// parseXtermMouse is like parseSgrMouse, but it parses a legacy
-// X11 mouse record.
-func (t *tScreen) parseXtermMouse(buf *bytes.Buffer) (bool, bool) {
-
- b := buf.Bytes()
-
- state := 0
- btn := 0
- x := 0
- y := 0
-
- for i := range b {
- switch state {
- case 0:
- switch b[i] {
- case '\x1b':
- state = 1
- case '\x9b':
- state = 2
- default:
- return false, false
- }
- case 1:
- if b[i] != '[' {
- return false, false
- }
- state = 2
- case 2:
- if b[i] != 'M' {
- return false, false
- }
- state++
- case 3:
- btn = int(b[i])
- if btn != 128 && btn != 129 {
- btn = int(b[i])
- } else {
- btn = 99
- }
- state++
- case 4:
- x = int(b[i]) - 32 - 1
- state++
- case 5:
- y = int(b[i]) - 32 - 1
- for i >= 0 {
- buf.ReadByte()
- i--
- }
- t.postMouseEvent(x, y, btn, false)
- return true, true
- }
- }
- return true, false
-}
-
-func (t *tScreen) parseFunctionKey(buf *bytes.Buffer) (bool, bool) {
- b := buf.Bytes()
- partial := false
- for e, k := range t.keycodes {
- esc := []byte(e)
- if (len(esc) == 1) && (esc[0] == '\x1b') {
- continue
- }
- if bytes.HasPrefix(b, esc) {
- // matched
- var r rune
- if len(esc) == 1 {
- r = rune(b[0])
- }
- mod := k.mod
- if t.escaped {
- mod |= ModAlt
- t.escaped = false
- }
- ev := NewEventKey(k.key, r, mod)
- t.PostEvent(ev)
- for i := 0; i < len(esc); i++ {
- buf.ReadByte()
- }
- return true, true
- }
- if bytes.HasPrefix(esc, b) {
- partial = true
- }
- }
- return partial, false
-}
-
-func (t *tScreen) parseRune(buf *bytes.Buffer) (bool, bool) {
- b := buf.Bytes()
- if b[0] >= ' ' && b[0] <= 0x7F {
- // printable ASCII easy to deal with -- no encodings
- mod := ModNone
- if t.escaped {
- mod = ModAlt
- t.escaped = false
- }
- ev := NewEventKey(KeyRune, rune(b[0]), mod)
- t.PostEvent(ev)
- buf.ReadByte()
- return true, true
- }
-
- if b[0] < 0x80 {
- // Low numbered values are control keys, not runes.
- return false, false
- }
-
- utfb := make([]byte, 12)
- for l := 1; l <= len(b); l++ {
- t.decoder.Reset()
- nout, nin, e := t.decoder.Transform(utfb, b[:l], true)
- if e == transform.ErrShortSrc {
- continue
- }
- if nout != 0 {
- r, _ := utf8.DecodeRune(utfb[:nout])
- if r != utf8.RuneError {
- mod := ModNone
- if t.escaped {
- mod = ModAlt
- t.escaped = false
- }
- ev := NewEventKey(KeyRune, r, mod)
- t.PostEvent(ev)
- }
- for nin > 0 {
- buf.ReadByte()
- nin--
- }
- return true, true
- }
- }
- // Looks like potential escape
- return true, false
-}
-
-func (t *tScreen) parseBracketedPaste(buf *bytes.Buffer) (bool, bool) {
- b := buf.Bytes()
-
- // Replace all carriage returns with newlines
- str := strings.Replace(string(b), "\r", "\n", -1)
- if strings.HasPrefix(str, "\x1b[200~") {
- // The bracketed paste has started
- if strings.HasSuffix(str, "\x1b[201~") {
- // The bracketed paste has ended
- // Strip out the start and end sequences
- ev := NewEventPaste(str[6 : len(b)-6])
- t.PostEvent(ev)
- for i := 0; i < len(b); i++ {
- buf.ReadByte()
- }
- return true, true
- }
- // There is still more coming
- return true, false
- }
- return false, false
-}
-
-func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) {
-
- t.Lock()
- defer t.Unlock()
-
- for {
- b := buf.Bytes()
- if len(b) == 0 {
- buf.Reset()
- return
- }
- if !bytes.Contains(b, []byte("\x1b")) && utf8.RuneCount(b) > 1 {
- ev := &EventPaste{t: time.Now(), text: string(bytes.Replace(b, []byte("\r"), []byte("\n"), -1))}
- t.PostEvent(ev)
- for i := 0; i < len(b); i++ {
- buf.ReadByte()
- }
- continue
- }
-
- partials := 0
-
- if part, comp := t.parseBracketedPaste(buf); comp {
- continue
- } else if part {
- partials++
- }
-
- if part, comp := t.parseRune(buf); comp {
- continue
- } else if part {
- partials++
- }
-
- if part, comp := t.parseFunctionKey(buf); comp {
- continue
- } else if part {
- partials++
- }
-
- // Only parse mouse records if this term claims to have
- // mouse support
-
- if t.ti.Mouse != "" {
- if part, comp := t.parseXtermMouse(buf); comp {
- continue
- } else if part {
- partials++
- }
-
- if part, comp := t.parseSgrMouse(buf); comp {
- continue
- } else if part {
- partials++
- }
- }
-
- if partials == 0 || expire {
- // 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.
- if b[0] == '\x1b' {
- if len(b) == 1 {
- ev := NewEventKey(KeyEsc, 0, ModNone)
- t.PostEvent(ev)
- t.escaped = false
- } else {
- t.escaped = true
- }
- buf.ReadByte()
- continue
- }
-
- by, _ := buf.ReadByte()
- mod := ModNone
- if t.escaped {
- t.escaped = false
- mod = ModAlt
- }
- ev := NewEventKey(KeyRune, rune(by), mod)
- t.PostEvent(ev)
- continue
- }
-
- // well we have some partial data, wait until we get
- // some more
- break
- }
-}
-
-func (t *tScreen) mainLoop() {
- buf := &bytes.Buffer{}
- for {
- select {
- case <-t.quit:
- close(t.indoneq)
- return
- case <-t.sigwinch:
- t.Lock()
- t.cx = -1
- t.cy = -1
- t.resize()
- t.cells.Invalidate()
- t.draw()
- t.Unlock()
- continue
- case <-t.keytimer.C:
- // If the timer fired, and the current time
- // is after the expiration of the escape sequence,
- // then we assume the escape sequence reached it's
- // conclusion, and process the chunk independently.
- // This lets us detect conflicts such as a lone ESC.
- if buf.Len() > 0 {
- if time.Now().After(t.keyexpire) {
- t.scanInput(buf, true)
- }
- }
- if buf.Len() > 0 {
- if !t.keytimer.Stop() {
- select {
- case <-t.keytimer.C:
- default:
- }
- }
- t.keytimer.Reset(time.Millisecond * 50)
- }
- case chunk := <-t.keychan:
- buf.Write(chunk)
- t.keyexpire = time.Now().Add(time.Millisecond * 50)
- t.scanInput(buf, false)
- if !t.keytimer.Stop() {
- select {
- case <-t.keytimer.C:
- default:
- }
- }
- if buf.Len() > 0 {
- t.keytimer.Reset(time.Millisecond * 50)
- }
- }
- }
-}
-
-func (t *tScreen) inputLoop() {
-
- for {
- chunk := make([]byte, 128)
- n, e := t.in.Read(chunk)
- switch e {
- case io.EOF:
- case nil:
- default:
- t.PostEvent(NewEventError(e))
- return
- }
- t.keychan <- chunk[:n]
- }
-}
-
-func (t *tScreen) Sync() {
- t.Lock()
- t.cx = -1
- t.cy = -1
- if !t.fini {
- t.resize()
- t.clear = true
- t.cells.Invalidate()
- t.draw()
- }
- t.Unlock()
-}
-
-func (t *tScreen) CharacterSet() string {
- return t.charset
-}
-
-func (t *tScreen) RegisterRuneFallback(orig rune, fallback string) {
- t.Lock()
- t.fallback[orig] = fallback
- t.Unlock()
-}
-
-func (t *tScreen) UnregisterRuneFallback(orig rune) {
- t.Lock()
- delete(t.fallback, orig)
- t.Unlock()
-}
-
-func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
-
- if enc := t.encoder; enc != nil {
- nb := make([]byte, 6)
- ob := make([]byte, 6)
- num := utf8.EncodeRune(ob, r)
-
- enc.Reset()
- dst, _, err := enc.Transform(nb, ob[:num], true)
- if dst != 0 && err == nil && nb[0] != '\x1A' {
- return true
- }
- }
- // Terminal fallbacks always permitted, since we assume they are
- // basically nearly perfect renditions.
- if _, ok := t.acs[r]; ok {
- return true
- }
- if !checkFallbacks {
- return false
- }
- if _, ok := t.fallback[r]; ok {
- return true
- }
- return false
-}
-
-func (t *tScreen) HasMouse() bool {
- return len(t.mouse) != 0
-}
-
-func (t *tScreen) HasKey(k Key) bool {
- if k == KeyRune {
- return true
- }
- return t.keyexist[k]
-}
-
-func (t *tScreen) Resize(int, int, int, int) {}
-
-func (t *tScreen) SetTitle(title string) {
- if strings.Compare(os.Getenv("TERM"), "screen") == 0 {
- t.hasSetTitle = true
- t.TPuts("\033k" + title + "\033\\")
- }
- if strings.Contains(os.Getenv("TERM"), "xterm") {
- t.TPuts("\033]2;" + title + "\007")
- t.hasSetTitle = true
- }
-}