...

Source file src/github.com/gdamore/tcell/v2/tscreen.go

Documentation: github.com/gdamore/tcell/v2

     1  // Copyright 2022 The TCell Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use file except in compliance with the License.
     5  // You may obtain a copy of the license at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build !(js && wasm)
    16  // +build !js !wasm
    17  
    18  package tcell
    19  
    20  import (
    21  	"bytes"
    22  	"errors"
    23  	"io"
    24  	"os"
    25  	"strconv"
    26  	"strings"
    27  	"sync"
    28  	"time"
    29  	"unicode/utf8"
    30  
    31  	"golang.org/x/term"
    32  	"golang.org/x/text/transform"
    33  
    34  	"github.com/gdamore/tcell/v2/terminfo"
    35  
    36  	// import the stock terminals
    37  	_ "github.com/gdamore/tcell/v2/terminfo/base"
    38  )
    39  
    40  // NewTerminfoScreen returns a Screen that uses the stock TTY interface
    41  // and POSIX terminal control, combined with a terminfo description taken from
    42  // the $TERM environment variable.  It returns an error if the terminal
    43  // is not supported for any reason.
    44  //
    45  // For terminals that do not support dynamic resize events, the $LINES
    46  // $COLUMNS environment variables can be set to the actual window size,
    47  // otherwise defaults taken from the terminal database are used.
    48  func NewTerminfoScreen() (Screen, error) {
    49  	return NewTerminfoScreenFromTty(nil)
    50  }
    51  
    52  // LookupTerminfo attempts to find a definition for the named $TERM falling
    53  // back to attempting to parse the output from infocmp.
    54  func LookupTerminfo(name string) (ti *terminfo.Terminfo, e error) {
    55  	ti, e = terminfo.LookupTerminfo(name)
    56  	if e != nil {
    57  		ti, e = loadDynamicTerminfo(name)
    58  		if e != nil {
    59  			return nil, e
    60  		}
    61  		terminfo.AddTerminfo(ti)
    62  	}
    63  
    64  	return
    65  }
    66  
    67  // NewTerminfoScreenFromTtyTerminfo returns a Screen using a custom Tty
    68  // implementation  and custom terminfo specification.
    69  // If the passed in tty is nil, then a reasonable default (typically /dev/tty)
    70  // is presumed, at least on UNIX hosts. (Windows hosts will typically fail this
    71  // call altogether.)
    72  // If passed terminfo is nil, then TERM environment variable is queried for
    73  // terminal specification.
    74  func NewTerminfoScreenFromTtyTerminfo(tty Tty, ti *terminfo.Terminfo) (s Screen, e error) {
    75  	if ti == nil {
    76  		ti, e = LookupTerminfo(os.Getenv("TERM"))
    77  		if e != nil {
    78  			return
    79  		}
    80  	}
    81  
    82  	t := &tScreen{ti: ti, tty: tty}
    83  
    84  	t.keyexist = make(map[Key]bool)
    85  	t.keycodes = make(map[string]*tKeyCode)
    86  	if len(ti.Mouse) > 0 {
    87  		t.mouse = []byte(ti.Mouse)
    88  	}
    89  	t.prepareKeys()
    90  	t.buildAcsMap()
    91  	t.resizeQ = make(chan bool, 1)
    92  	t.fallback = make(map[rune]string)
    93  	for k, v := range RuneFallbacks {
    94  		t.fallback[k] = v
    95  	}
    96  
    97  	return t, nil
    98  }
    99  
   100  // NewTerminfoScreenFromTty returns a Screen using a custom Tty implementation.
   101  // If the passed in tty is nil, then a reasonable default (typically /dev/tty)
   102  // is presumed, at least on UNIX hosts. (Windows hosts will typically fail this
   103  // call altogether.)
   104  func NewTerminfoScreenFromTty(tty Tty) (Screen, error) {
   105  	return NewTerminfoScreenFromTtyTerminfo(tty, nil)
   106  }
   107  
   108  // tKeyCode represents a combination of a key code and modifiers.
   109  type tKeyCode struct {
   110  	key Key
   111  	mod ModMask
   112  }
   113  
   114  // tScreen represents a screen backed by a terminfo implementation.
   115  type tScreen struct {
   116  	ti           *terminfo.Terminfo
   117  	tty          Tty
   118  	h            int
   119  	w            int
   120  	fini         bool
   121  	cells        CellBuffer
   122  	buffering    bool // true if we are collecting writes to buf instead of sending directly to out
   123  	buf          bytes.Buffer
   124  	curstyle     Style
   125  	style        Style
   126  	evch         chan Event
   127  	resizeQ      chan bool
   128  	quit         chan struct{}
   129  	keyexist     map[Key]bool
   130  	keycodes     map[string]*tKeyCode
   131  	keychan      chan []byte
   132  	keytimer     *time.Timer
   133  	keyexpire    time.Time
   134  	cx           int
   135  	cy           int
   136  	mouse        []byte
   137  	clear        bool
   138  	cursorx      int
   139  	cursory      int
   140  	acs          map[rune]string
   141  	charset      string
   142  	encoder      transform.Transformer
   143  	decoder      transform.Transformer
   144  	fallback     map[rune]string
   145  	colors       map[Color]Color
   146  	palette      []Color
   147  	truecolor    bool
   148  	escaped      bool
   149  	buttondn     bool
   150  	finiOnce     sync.Once
   151  	enablePaste  string
   152  	disablePaste string
   153  	enterUrl     string
   154  	exitUrl      string
   155  	setWinSize   string
   156  	cursorStyles map[CursorStyle]string
   157  	cursorStyle  CursorStyle
   158  	saved        *term.State
   159  	stopQ        chan struct{}
   160  	running      bool
   161  	wg           sync.WaitGroup
   162  	mouseFlags   MouseFlags
   163  	pasteEnabled bool
   164  
   165  	sync.Mutex
   166  }
   167  
   168  func (t *tScreen) Init() error {
   169  	if e := t.initialize(); e != nil {
   170  		return e
   171  	}
   172  
   173  	t.evch = make(chan Event, 10)
   174  	t.keychan = make(chan []byte, 10)
   175  	t.keytimer = time.NewTimer(time.Millisecond * 50)
   176  	t.charset = "UTF-8"
   177  
   178  	t.charset = getCharset()
   179  	if enc := GetEncoding(t.charset); enc != nil {
   180  		t.encoder = enc.NewEncoder()
   181  		t.decoder = enc.NewDecoder()
   182  	} else {
   183  		return ErrNoCharset
   184  	}
   185  	ti := t.ti
   186  
   187  	// environment overrides
   188  	w := ti.Columns
   189  	h := ti.Lines
   190  	if i, _ := strconv.Atoi(os.Getenv("LINES")); i != 0 {
   191  		h = i
   192  	}
   193  	if i, _ := strconv.Atoi(os.Getenv("COLUMNS")); i != 0 {
   194  		w = i
   195  	}
   196  	if t.ti.SetFgBgRGB != "" || t.ti.SetFgRGB != "" || t.ti.SetBgRGB != "" {
   197  		t.truecolor = true
   198  	}
   199  	// A user who wants to have his themes honored can
   200  	// set this environment variable.
   201  	if os.Getenv("TCELL_TRUECOLOR") == "disable" {
   202  		t.truecolor = false
   203  	}
   204  	nColors := t.nColors()
   205  	if nColors > 256 {
   206  		nColors = 256 // clip to reasonable limits
   207  	}
   208  	t.colors = make(map[Color]Color, nColors)
   209  	t.palette = make([]Color, nColors)
   210  	for i := 0; i < nColors; i++ {
   211  		t.palette[i] = Color(i) | ColorValid
   212  		// identity map for our builtin colors
   213  		t.colors[Color(i)|ColorValid] = Color(i) | ColorValid
   214  	}
   215  
   216  	t.quit = make(chan struct{})
   217  
   218  	t.Lock()
   219  	t.cx = -1
   220  	t.cy = -1
   221  	t.style = StyleDefault
   222  	t.cells.Resize(w, h)
   223  	t.cursorx = -1
   224  	t.cursory = -1
   225  	t.resize()
   226  	t.Unlock()
   227  
   228  	if err := t.engage(); err != nil {
   229  		return err
   230  	}
   231  
   232  	return nil
   233  }
   234  
   235  func (t *tScreen) prepareKeyMod(key Key, mod ModMask, val string) {
   236  	if val != "" {
   237  		// Do not override codes that already exist
   238  		if _, exist := t.keycodes[val]; !exist {
   239  			t.keyexist[key] = true
   240  			t.keycodes[val] = &tKeyCode{key: key, mod: mod}
   241  		}
   242  	}
   243  }
   244  
   245  func (t *tScreen) prepareKeyModReplace(key Key, replace Key, mod ModMask, val string) {
   246  	if val != "" {
   247  		// Do not override codes that already exist
   248  		if old, exist := t.keycodes[val]; !exist || old.key == replace {
   249  			t.keyexist[key] = true
   250  			t.keycodes[val] = &tKeyCode{key: key, mod: mod}
   251  		}
   252  	}
   253  }
   254  
   255  func (t *tScreen) prepareKeyModXTerm(key Key, val string) {
   256  
   257  	if strings.HasPrefix(val, "\x1b[") && strings.HasSuffix(val, "~") {
   258  
   259  		// Drop the trailing ~
   260  		val = val[:len(val)-1]
   261  
   262  		// These suffixes are calculated assuming Xterm style modifier suffixes.
   263  		// Please see https://invisible-island.net/xterm/ctlseqs/ctlseqs.pdf for
   264  		// more information (specifically "PC-Style Function Keys").
   265  		t.prepareKeyModReplace(key, key+12, ModShift, val+";2~")
   266  		t.prepareKeyModReplace(key, key+48, ModAlt, val+";3~")
   267  		t.prepareKeyModReplace(key, key+60, ModAlt|ModShift, val+";4~")
   268  		t.prepareKeyModReplace(key, key+24, ModCtrl, val+";5~")
   269  		t.prepareKeyModReplace(key, key+36, ModCtrl|ModShift, val+";6~")
   270  		t.prepareKeyMod(key, ModAlt|ModCtrl, val+";7~")
   271  		t.prepareKeyMod(key, ModShift|ModAlt|ModCtrl, val+";8~")
   272  		t.prepareKeyMod(key, ModMeta, val+";9~")
   273  		t.prepareKeyMod(key, ModMeta|ModShift, val+";10~")
   274  		t.prepareKeyMod(key, ModMeta|ModAlt, val+";11~")
   275  		t.prepareKeyMod(key, ModMeta|ModAlt|ModShift, val+";12~")
   276  		t.prepareKeyMod(key, ModMeta|ModCtrl, val+";13~")
   277  		t.prepareKeyMod(key, ModMeta|ModCtrl|ModShift, val+";14~")
   278  		t.prepareKeyMod(key, ModMeta|ModCtrl|ModAlt, val+";15~")
   279  		t.prepareKeyMod(key, ModMeta|ModCtrl|ModAlt|ModShift, val+";16~")
   280  	} else if strings.HasPrefix(val, "\x1bO") && len(val) == 3 {
   281  		val = val[2:]
   282  		t.prepareKeyModReplace(key, key+12, ModShift, "\x1b[1;2"+val)
   283  		t.prepareKeyModReplace(key, key+48, ModAlt, "\x1b[1;3"+val)
   284  		t.prepareKeyModReplace(key, key+24, ModCtrl, "\x1b[1;5"+val)
   285  		t.prepareKeyModReplace(key, key+36, ModCtrl|ModShift, "\x1b[1;6"+val)
   286  		t.prepareKeyModReplace(key, key+60, ModAlt|ModShift, "\x1b[1;4"+val)
   287  		t.prepareKeyMod(key, ModAlt|ModCtrl, "\x1b[1;7"+val)
   288  		t.prepareKeyMod(key, ModShift|ModAlt|ModCtrl, "\x1b[1;8"+val)
   289  		t.prepareKeyMod(key, ModMeta, "\x1b[1;9"+val)
   290  		t.prepareKeyMod(key, ModMeta|ModShift, "\x1b[1;10"+val)
   291  		t.prepareKeyMod(key, ModMeta|ModAlt, "\x1b[1;11"+val)
   292  		t.prepareKeyMod(key, ModMeta|ModAlt|ModShift, "\x1b[1;12"+val)
   293  		t.prepareKeyMod(key, ModMeta|ModCtrl, "\x1b[1;13"+val)
   294  		t.prepareKeyMod(key, ModMeta|ModCtrl|ModShift, "\x1b[1;14"+val)
   295  		t.prepareKeyMod(key, ModMeta|ModCtrl|ModAlt, "\x1b[1;15"+val)
   296  		t.prepareKeyMod(key, ModMeta|ModCtrl|ModAlt|ModShift, "\x1b[1;16"+val)
   297  	}
   298  }
   299  
   300  func (t *tScreen) prepareXtermModifiers() {
   301  	if t.ti.Modifiers != terminfo.ModifiersXTerm {
   302  		return
   303  	}
   304  	t.prepareKeyModXTerm(KeyRight, t.ti.KeyRight)
   305  	t.prepareKeyModXTerm(KeyLeft, t.ti.KeyLeft)
   306  	t.prepareKeyModXTerm(KeyUp, t.ti.KeyUp)
   307  	t.prepareKeyModXTerm(KeyDown, t.ti.KeyDown)
   308  	t.prepareKeyModXTerm(KeyInsert, t.ti.KeyInsert)
   309  	t.prepareKeyModXTerm(KeyDelete, t.ti.KeyDelete)
   310  	t.prepareKeyModXTerm(KeyPgUp, t.ti.KeyPgUp)
   311  	t.prepareKeyModXTerm(KeyPgDn, t.ti.KeyPgDn)
   312  	t.prepareKeyModXTerm(KeyHome, t.ti.KeyHome)
   313  	t.prepareKeyModXTerm(KeyEnd, t.ti.KeyEnd)
   314  	t.prepareKeyModXTerm(KeyF1, t.ti.KeyF1)
   315  	t.prepareKeyModXTerm(KeyF2, t.ti.KeyF2)
   316  	t.prepareKeyModXTerm(KeyF3, t.ti.KeyF3)
   317  	t.prepareKeyModXTerm(KeyF4, t.ti.KeyF4)
   318  	t.prepareKeyModXTerm(KeyF5, t.ti.KeyF5)
   319  	t.prepareKeyModXTerm(KeyF6, t.ti.KeyF6)
   320  	t.prepareKeyModXTerm(KeyF7, t.ti.KeyF7)
   321  	t.prepareKeyModXTerm(KeyF8, t.ti.KeyF8)
   322  	t.prepareKeyModXTerm(KeyF9, t.ti.KeyF9)
   323  	t.prepareKeyModXTerm(KeyF10, t.ti.KeyF10)
   324  	t.prepareKeyModXTerm(KeyF11, t.ti.KeyF11)
   325  	t.prepareKeyModXTerm(KeyF12, t.ti.KeyF12)
   326  }
   327  
   328  func (t *tScreen) prepareBracketedPaste() {
   329  	// Another workaround for lack of reporting in terminfo.
   330  	// We assume if the terminal has a mouse entry, that it
   331  	// offers bracketed paste.  But we allow specific overrides
   332  	// via our terminal database.
   333  	if t.ti.EnablePaste != "" {
   334  		t.enablePaste = t.ti.EnablePaste
   335  		t.disablePaste = t.ti.DisablePaste
   336  		t.prepareKey(keyPasteStart, t.ti.PasteStart)
   337  		t.prepareKey(keyPasteEnd, t.ti.PasteEnd)
   338  	} else if t.ti.Mouse != "" {
   339  		t.enablePaste = "\x1b[?2004h"
   340  		t.disablePaste = "\x1b[?2004l"
   341  		t.prepareKey(keyPasteStart, "\x1b[200~")
   342  		t.prepareKey(keyPasteEnd, "\x1b[201~")
   343  	}
   344  }
   345  
   346  func (t *tScreen) prepareExtendedOSC() {
   347  	// Linux is a special beast - because it has a mouse entry, but does
   348  	// not swallow these OSC commands properly.
   349  	if (strings.Contains(t.ti.Name, "linux")) {
   350  		return;
   351  	}
   352  	// More stuff for limits in terminfo.  This time we are applying
   353  	// the most common OSC (operating system commands).  Generally
   354  	// terminals that don't understand these will ignore them.
   355  	// Again, we condition this based on mouse capabilities.
   356  	if t.ti.EnterUrl != "" {
   357  		t.enterUrl = t.ti.EnterUrl
   358  		t.exitUrl = t.ti.ExitUrl
   359  	} else if t.ti.Mouse != "" {
   360  		t.enterUrl = "\x1b]8;%p2%s;%p1%s\x1b\\"
   361  		t.exitUrl = "\x1b]8;;\x1b\\"
   362  	}
   363  
   364  	if t.ti.SetWindowSize != "" {
   365  		t.setWinSize = t.ti.SetWindowSize
   366  	} else if t.ti.Mouse != "" {
   367  		t.setWinSize = "\x1b[8;%p1%p2%d;%dt"
   368  	}
   369  }
   370  
   371  func (t *tScreen) prepareCursorStyles() {
   372  	// Another workaround for lack of reporting in terminfo.
   373  	// We assume if the terminal has a mouse entry, that it
   374  	// offers bracketed paste.  But we allow specific overrides
   375  	// via our terminal database.
   376  	if t.ti.CursorDefault != "" {
   377  		t.cursorStyles = map[CursorStyle]string{
   378  			CursorStyleDefault:           t.ti.CursorDefault,
   379  			CursorStyleBlinkingBlock:     t.ti.CursorBlinkingBlock,
   380  			CursorStyleSteadyBlock:       t.ti.CursorSteadyBlock,
   381  			CursorStyleBlinkingUnderline: t.ti.CursorBlinkingUnderline,
   382  			CursorStyleSteadyUnderline:   t.ti.CursorSteadyUnderline,
   383  			CursorStyleBlinkingBar:       t.ti.CursorBlinkingBar,
   384  			CursorStyleSteadyBar:         t.ti.CursorSteadyBar,
   385  		}
   386  	} else if t.ti.Mouse != "" {
   387  		t.cursorStyles = map[CursorStyle]string{
   388  			CursorStyleDefault:           "\x1b[0 q",
   389  			CursorStyleBlinkingBlock:     "\x1b[1 q",
   390  			CursorStyleSteadyBlock:       "\x1b[2 q",
   391  			CursorStyleBlinkingUnderline: "\x1b[3 q",
   392  			CursorStyleSteadyUnderline:   "\x1b[4 q",
   393  			CursorStyleBlinkingBar:       "\x1b[5 q",
   394  			CursorStyleSteadyBar:         "\x1b[6 q",
   395  		}
   396  	}
   397  }
   398  
   399  func (t *tScreen) prepareKey(key Key, val string) {
   400  	t.prepareKeyMod(key, ModNone, val)
   401  }
   402  
   403  func (t *tScreen) prepareKeys() {
   404  	ti := t.ti
   405  	t.prepareKey(KeyBackspace, ti.KeyBackspace)
   406  	t.prepareKey(KeyF1, ti.KeyF1)
   407  	t.prepareKey(KeyF2, ti.KeyF2)
   408  	t.prepareKey(KeyF3, ti.KeyF3)
   409  	t.prepareKey(KeyF4, ti.KeyF4)
   410  	t.prepareKey(KeyF5, ti.KeyF5)
   411  	t.prepareKey(KeyF6, ti.KeyF6)
   412  	t.prepareKey(KeyF7, ti.KeyF7)
   413  	t.prepareKey(KeyF8, ti.KeyF8)
   414  	t.prepareKey(KeyF9, ti.KeyF9)
   415  	t.prepareKey(KeyF10, ti.KeyF10)
   416  	t.prepareKey(KeyF11, ti.KeyF11)
   417  	t.prepareKey(KeyF12, ti.KeyF12)
   418  	t.prepareKey(KeyF13, ti.KeyF13)
   419  	t.prepareKey(KeyF14, ti.KeyF14)
   420  	t.prepareKey(KeyF15, ti.KeyF15)
   421  	t.prepareKey(KeyF16, ti.KeyF16)
   422  	t.prepareKey(KeyF17, ti.KeyF17)
   423  	t.prepareKey(KeyF18, ti.KeyF18)
   424  	t.prepareKey(KeyF19, ti.KeyF19)
   425  	t.prepareKey(KeyF20, ti.KeyF20)
   426  	t.prepareKey(KeyF21, ti.KeyF21)
   427  	t.prepareKey(KeyF22, ti.KeyF22)
   428  	t.prepareKey(KeyF23, ti.KeyF23)
   429  	t.prepareKey(KeyF24, ti.KeyF24)
   430  	t.prepareKey(KeyF25, ti.KeyF25)
   431  	t.prepareKey(KeyF26, ti.KeyF26)
   432  	t.prepareKey(KeyF27, ti.KeyF27)
   433  	t.prepareKey(KeyF28, ti.KeyF28)
   434  	t.prepareKey(KeyF29, ti.KeyF29)
   435  	t.prepareKey(KeyF30, ti.KeyF30)
   436  	t.prepareKey(KeyF31, ti.KeyF31)
   437  	t.prepareKey(KeyF32, ti.KeyF32)
   438  	t.prepareKey(KeyF33, ti.KeyF33)
   439  	t.prepareKey(KeyF34, ti.KeyF34)
   440  	t.prepareKey(KeyF35, ti.KeyF35)
   441  	t.prepareKey(KeyF36, ti.KeyF36)
   442  	t.prepareKey(KeyF37, ti.KeyF37)
   443  	t.prepareKey(KeyF38, ti.KeyF38)
   444  	t.prepareKey(KeyF39, ti.KeyF39)
   445  	t.prepareKey(KeyF40, ti.KeyF40)
   446  	t.prepareKey(KeyF41, ti.KeyF41)
   447  	t.prepareKey(KeyF42, ti.KeyF42)
   448  	t.prepareKey(KeyF43, ti.KeyF43)
   449  	t.prepareKey(KeyF44, ti.KeyF44)
   450  	t.prepareKey(KeyF45, ti.KeyF45)
   451  	t.prepareKey(KeyF46, ti.KeyF46)
   452  	t.prepareKey(KeyF47, ti.KeyF47)
   453  	t.prepareKey(KeyF48, ti.KeyF48)
   454  	t.prepareKey(KeyF49, ti.KeyF49)
   455  	t.prepareKey(KeyF50, ti.KeyF50)
   456  	t.prepareKey(KeyF51, ti.KeyF51)
   457  	t.prepareKey(KeyF52, ti.KeyF52)
   458  	t.prepareKey(KeyF53, ti.KeyF53)
   459  	t.prepareKey(KeyF54, ti.KeyF54)
   460  	t.prepareKey(KeyF55, ti.KeyF55)
   461  	t.prepareKey(KeyF56, ti.KeyF56)
   462  	t.prepareKey(KeyF57, ti.KeyF57)
   463  	t.prepareKey(KeyF58, ti.KeyF58)
   464  	t.prepareKey(KeyF59, ti.KeyF59)
   465  	t.prepareKey(KeyF60, ti.KeyF60)
   466  	t.prepareKey(KeyF61, ti.KeyF61)
   467  	t.prepareKey(KeyF62, ti.KeyF62)
   468  	t.prepareKey(KeyF63, ti.KeyF63)
   469  	t.prepareKey(KeyF64, ti.KeyF64)
   470  	t.prepareKey(KeyInsert, ti.KeyInsert)
   471  	t.prepareKey(KeyDelete, ti.KeyDelete)
   472  	t.prepareKey(KeyHome, ti.KeyHome)
   473  	t.prepareKey(KeyEnd, ti.KeyEnd)
   474  	t.prepareKey(KeyUp, ti.KeyUp)
   475  	t.prepareKey(KeyDown, ti.KeyDown)
   476  	t.prepareKey(KeyLeft, ti.KeyLeft)
   477  	t.prepareKey(KeyRight, ti.KeyRight)
   478  	t.prepareKey(KeyPgUp, ti.KeyPgUp)
   479  	t.prepareKey(KeyPgDn, ti.KeyPgDn)
   480  	t.prepareKey(KeyHelp, ti.KeyHelp)
   481  	t.prepareKey(KeyPrint, ti.KeyPrint)
   482  	t.prepareKey(KeyCancel, ti.KeyCancel)
   483  	t.prepareKey(KeyExit, ti.KeyExit)
   484  	t.prepareKey(KeyBacktab, ti.KeyBacktab)
   485  
   486  	t.prepareKeyMod(KeyRight, ModShift, ti.KeyShfRight)
   487  	t.prepareKeyMod(KeyLeft, ModShift, ti.KeyShfLeft)
   488  	t.prepareKeyMod(KeyUp, ModShift, ti.KeyShfUp)
   489  	t.prepareKeyMod(KeyDown, ModShift, ti.KeyShfDown)
   490  	t.prepareKeyMod(KeyHome, ModShift, ti.KeyShfHome)
   491  	t.prepareKeyMod(KeyEnd, ModShift, ti.KeyShfEnd)
   492  	t.prepareKeyMod(KeyPgUp, ModShift, ti.KeyShfPgUp)
   493  	t.prepareKeyMod(KeyPgDn, ModShift, ti.KeyShfPgDn)
   494  
   495  	t.prepareKeyMod(KeyRight, ModCtrl, ti.KeyCtrlRight)
   496  	t.prepareKeyMod(KeyLeft, ModCtrl, ti.KeyCtrlLeft)
   497  	t.prepareKeyMod(KeyUp, ModCtrl, ti.KeyCtrlUp)
   498  	t.prepareKeyMod(KeyDown, ModCtrl, ti.KeyCtrlDown)
   499  	t.prepareKeyMod(KeyHome, ModCtrl, ti.KeyCtrlHome)
   500  	t.prepareKeyMod(KeyEnd, ModCtrl, ti.KeyCtrlEnd)
   501  
   502  	// Sadly, xterm handling of keycodes is somewhat erratic.  In
   503  	// particular, different codes are sent depending on application
   504  	// mode is in use or not, and the entries for many of these are
   505  	// simply absent from terminfo on many systems.  So we insert
   506  	// a number of escape sequences if they are not already used, in
   507  	// order to have the widest correct usage.  Note that prepareKey
   508  	// will not inject codes if the escape sequence is already known.
   509  	// We also only do this for terminals that have the application
   510  	// mode present.
   511  
   512  	// Cursor mode
   513  	if ti.EnterKeypad != "" {
   514  		t.prepareKey(KeyUp, "\x1b[A")
   515  		t.prepareKey(KeyDown, "\x1b[B")
   516  		t.prepareKey(KeyRight, "\x1b[C")
   517  		t.prepareKey(KeyLeft, "\x1b[D")
   518  		t.prepareKey(KeyEnd, "\x1b[F")
   519  		t.prepareKey(KeyHome, "\x1b[H")
   520  		t.prepareKey(KeyDelete, "\x1b[3~")
   521  		t.prepareKey(KeyHome, "\x1b[1~")
   522  		t.prepareKey(KeyEnd, "\x1b[4~")
   523  		t.prepareKey(KeyPgUp, "\x1b[5~")
   524  		t.prepareKey(KeyPgDn, "\x1b[6~")
   525  
   526  		// Application mode
   527  		t.prepareKey(KeyUp, "\x1bOA")
   528  		t.prepareKey(KeyDown, "\x1bOB")
   529  		t.prepareKey(KeyRight, "\x1bOC")
   530  		t.prepareKey(KeyLeft, "\x1bOD")
   531  		t.prepareKey(KeyHome, "\x1bOH")
   532  	}
   533  
   534  	t.prepareKey(keyPasteStart, ti.PasteStart)
   535  	t.prepareKey(keyPasteEnd, ti.PasteEnd)
   536  	t.prepareXtermModifiers()
   537  	t.prepareBracketedPaste()
   538  	t.prepareCursorStyles()
   539  	t.prepareExtendedOSC()
   540  
   541  outer:
   542  	// Add key mappings for control keys.
   543  	for i := 0; i < ' '; i++ {
   544  		// Do not insert direct key codes for ambiguous keys.
   545  		// For example, ESC is used for lots of other keys, so
   546  		// when parsing this we don't want to fast path handling
   547  		// of it, but instead wait a bit before parsing it as in
   548  		// isolation.
   549  		for esc := range t.keycodes {
   550  			if []byte(esc)[0] == byte(i) {
   551  				continue outer
   552  			}
   553  		}
   554  
   555  		t.keyexist[Key(i)] = true
   556  
   557  		mod := ModCtrl
   558  		switch Key(i) {
   559  		case KeyBS, KeyTAB, KeyESC, KeyCR:
   560  			// directly type-able- no control sequence
   561  			mod = ModNone
   562  		}
   563  		t.keycodes[string(rune(i))] = &tKeyCode{key: Key(i), mod: mod}
   564  	}
   565  }
   566  
   567  func (t *tScreen) Fini() {
   568  	t.finiOnce.Do(t.finish)
   569  }
   570  
   571  func (t *tScreen) finish() {
   572  	close(t.quit)
   573  	t.finalize()
   574  }
   575  
   576  func (t *tScreen) SetStyle(style Style) {
   577  	t.Lock()
   578  	if !t.fini {
   579  		t.style = style
   580  	}
   581  	t.Unlock()
   582  }
   583  
   584  func (t *tScreen) Clear() {
   585  	t.Fill(' ', t.style)
   586  }
   587  
   588  func (t *tScreen) Fill(r rune, style Style) {
   589  	t.Lock()
   590  	if !t.fini {
   591  		t.cells.Fill(r, style)
   592  	}
   593  	t.Unlock()
   594  }
   595  
   596  func (t *tScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) {
   597  	t.Lock()
   598  	if !t.fini {
   599  		t.cells.SetContent(x, y, mainc, combc, style)
   600  	}
   601  	t.Unlock()
   602  }
   603  
   604  func (t *tScreen) GetContent(x, y int) (rune, []rune, Style, int) {
   605  	t.Lock()
   606  	mainc, combc, style, width := t.cells.GetContent(x, y)
   607  	t.Unlock()
   608  	return mainc, combc, style, width
   609  }
   610  
   611  func (t *tScreen) SetCell(x, y int, style Style, ch ...rune) {
   612  	if len(ch) > 0 {
   613  		t.SetContent(x, y, ch[0], ch[1:], style)
   614  	} else {
   615  		t.SetContent(x, y, ' ', nil, style)
   616  	}
   617  }
   618  
   619  func (t *tScreen) encodeRune(r rune, buf []byte) []byte {
   620  
   621  	nb := make([]byte, 6)
   622  	ob := make([]byte, 6)
   623  	num := utf8.EncodeRune(ob, r)
   624  	ob = ob[:num]
   625  	dst := 0
   626  	var err error
   627  	if enc := t.encoder; enc != nil {
   628  		enc.Reset()
   629  		dst, _, err = enc.Transform(nb, ob, true)
   630  	}
   631  	if err != nil || dst == 0 || nb[0] == '\x1a' {
   632  		// Combining characters are elided
   633  		if len(buf) == 0 {
   634  			if acs, ok := t.acs[r]; ok {
   635  				buf = append(buf, []byte(acs)...)
   636  			} else if fb, ok := t.fallback[r]; ok {
   637  				buf = append(buf, []byte(fb)...)
   638  			} else {
   639  				buf = append(buf, '?')
   640  			}
   641  		}
   642  	} else {
   643  		buf = append(buf, nb[:dst]...)
   644  	}
   645  
   646  	return buf
   647  }
   648  
   649  func (t *tScreen) sendFgBg(fg Color, bg Color, attr AttrMask) AttrMask {
   650  	ti := t.ti
   651  	if ti.Colors == 0 {
   652  		// foreground vs background, we calculate luminance
   653  		// and possibly do a reverse video
   654  		if !fg.Valid() {
   655  			return attr
   656  		}
   657  		v, ok := t.colors[fg]
   658  		if !ok {
   659  			v = FindColor(fg, []Color{ColorBlack, ColorWhite})
   660  			t.colors[fg] = v
   661  		}
   662  		switch v {
   663  		case ColorWhite:
   664  			return attr
   665  		case ColorBlack:
   666  			return attr ^ AttrReverse
   667  		}
   668  	}
   669  
   670  	if fg == ColorReset || bg == ColorReset {
   671  		t.TPuts(ti.ResetFgBg)
   672  	}
   673  	if t.truecolor {
   674  		if ti.SetFgBgRGB != "" && fg.IsRGB() && bg.IsRGB() {
   675  			r1, g1, b1 := fg.RGB()
   676  			r2, g2, b2 := bg.RGB()
   677  			t.TPuts(ti.TParm(ti.SetFgBgRGB,
   678  				int(r1), int(g1), int(b1),
   679  				int(r2), int(g2), int(b2)))
   680  			return attr
   681  		}
   682  
   683  		if fg.IsRGB() && ti.SetFgRGB != "" {
   684  			r, g, b := fg.RGB()
   685  			t.TPuts(ti.TParm(ti.SetFgRGB, int(r), int(g), int(b)))
   686  			fg = ColorDefault
   687  		}
   688  
   689  		if bg.IsRGB() && ti.SetBgRGB != "" {
   690  			r, g, b := bg.RGB()
   691  			t.TPuts(ti.TParm(ti.SetBgRGB,
   692  				int(r), int(g), int(b)))
   693  			bg = ColorDefault
   694  		}
   695  	}
   696  
   697  	if fg.Valid() {
   698  		if v, ok := t.colors[fg]; ok {
   699  			fg = v
   700  		} else {
   701  			v = FindColor(fg, t.palette)
   702  			t.colors[fg] = v
   703  			fg = v
   704  		}
   705  	}
   706  
   707  	if bg.Valid() {
   708  		if v, ok := t.colors[bg]; ok {
   709  			bg = v
   710  		} else {
   711  			v = FindColor(bg, t.palette)
   712  			t.colors[bg] = v
   713  			bg = v
   714  		}
   715  	}
   716  
   717  	if fg.Valid() && bg.Valid() && ti.SetFgBg != "" {
   718  		t.TPuts(ti.TParm(ti.SetFgBg, int(fg&0xff), int(bg&0xff)))
   719  	} else {
   720  		if fg.Valid() && ti.SetFg != "" {
   721  			t.TPuts(ti.TParm(ti.SetFg, int(fg&0xff)))
   722  		}
   723  		if bg.Valid() && ti.SetBg != "" {
   724  			t.TPuts(ti.TParm(ti.SetBg, int(bg&0xff)))
   725  		}
   726  	}
   727  	return attr
   728  }
   729  
   730  func (t *tScreen) drawCell(x, y int) int {
   731  
   732  	ti := t.ti
   733  
   734  	mainc, combc, style, width := t.cells.GetContent(x, y)
   735  	if !t.cells.Dirty(x, y) {
   736  		return width
   737  	}
   738  
   739  	if y == t.h-1 && x == t.w-1 && t.ti.AutoMargin && ti.InsertChar != "" {
   740  		// our solution is somewhat goofy.
   741  		// we write to the second to the last cell what we want in the last cell, then we
   742  		// insert a character at that 2nd to last position to shift the last column into
   743  		// place, then we rewrite that 2nd to last cell.  Old terminals suck.
   744  		t.TPuts(ti.TGoto(x-1, y))
   745  		defer func() {
   746  			t.TPuts(ti.TGoto(x-1, y))
   747  			t.TPuts(ti.InsertChar)
   748  			t.cy = y
   749  			t.cx = x - 1
   750  			t.cells.SetDirty(x-1, y, true)
   751  			_ = t.drawCell(x-1, y)
   752  			t.TPuts(t.ti.TGoto(0, 0))
   753  			t.cy = 0
   754  			t.cx = 0
   755  		}()
   756  	} else if t.cy != y || t.cx != x {
   757  		t.TPuts(ti.TGoto(x, y))
   758  		t.cx = x
   759  		t.cy = y
   760  	}
   761  
   762  	if style == StyleDefault {
   763  		style = t.style
   764  	}
   765  	if style != t.curstyle {
   766  		fg, bg, attrs := style.Decompose()
   767  
   768  		t.TPuts(ti.AttrOff)
   769  
   770  		attrs = t.sendFgBg(fg, bg, attrs)
   771  		if attrs&AttrBold != 0 {
   772  			t.TPuts(ti.Bold)
   773  		}
   774  		if attrs&AttrUnderline != 0 {
   775  			t.TPuts(ti.Underline)
   776  		}
   777  		if attrs&AttrReverse != 0 {
   778  			t.TPuts(ti.Reverse)
   779  		}
   780  		if attrs&AttrBlink != 0 {
   781  			t.TPuts(ti.Blink)
   782  		}
   783  		if attrs&AttrDim != 0 {
   784  			t.TPuts(ti.Dim)
   785  		}
   786  		if attrs&AttrItalic != 0 {
   787  			t.TPuts(ti.Italic)
   788  		}
   789  		if attrs&AttrStrikeThrough != 0 {
   790  			t.TPuts(ti.StrikeThrough)
   791  		}
   792  
   793  		// URL string can be long, so don't send it unless we really need to
   794  		if t.enterUrl != "" && t.curstyle != style {
   795  			if style.url != "" {
   796  				t.TPuts(ti.TParm(t.enterUrl, style.url, style.urlId))
   797  			} else {
   798  				t.TPuts(t.exitUrl)
   799  			}
   800  		}
   801  
   802  		t.curstyle = style
   803  	}
   804  
   805  	// now emit runes - taking care to not overrun width with a
   806  	// wide character, and to ensure that we emit exactly one regular
   807  	// character followed up by any residual combing characters
   808  
   809  	if width < 1 {
   810  		width = 1
   811  	}
   812  
   813  	var str string
   814  
   815  	buf := make([]byte, 0, 6)
   816  
   817  	buf = t.encodeRune(mainc, buf)
   818  	for _, r := range combc {
   819  		buf = t.encodeRune(r, buf)
   820  	}
   821  
   822  	str = string(buf)
   823  	if width > 1 && str == "?" {
   824  		// No FullWidth character support
   825  		str = "? "
   826  		t.cx = -1
   827  	}
   828  
   829  	if x > t.w-width {
   830  		// too wide to fit; emit a single space instead
   831  		width = 1
   832  		str = " "
   833  	}
   834  	t.writeString(str)
   835  	t.cx += width
   836  	t.cells.SetDirty(x, y, false)
   837  	if width > 1 {
   838  		t.cx = -1
   839  	}
   840  
   841  	return width
   842  }
   843  
   844  func (t *tScreen) ShowCursor(x, y int) {
   845  	t.Lock()
   846  	t.cursorx = x
   847  	t.cursory = y
   848  	t.Unlock()
   849  }
   850  
   851  func (t *tScreen) SetCursorStyle(cs CursorStyle) {
   852  	t.Lock()
   853  	t.cursorStyle = cs
   854  	t.Unlock()
   855  }
   856  
   857  func (t *tScreen) HideCursor() {
   858  	t.ShowCursor(-1, -1)
   859  }
   860  
   861  func (t *tScreen) showCursor() {
   862  
   863  	x, y := t.cursorx, t.cursory
   864  	w, h := t.cells.Size()
   865  	if x < 0 || y < 0 || x >= w || y >= h {
   866  		t.hideCursor()
   867  		return
   868  	}
   869  	t.TPuts(t.ti.TGoto(x, y))
   870  	t.TPuts(t.ti.ShowCursor)
   871  	if t.cursorStyles != nil {
   872  		if esc, ok := t.cursorStyles[t.cursorStyle]; ok {
   873  			t.TPuts(esc)
   874  		}
   875  	}
   876  	t.cx = x
   877  	t.cy = y
   878  }
   879  
   880  // writeString sends a string to the terminal. The string is sent as-is and
   881  // this function does not expand inline padding indications (of the form
   882  // $<[delay]> where [delay] is msec). In order to have these expanded, use
   883  // TPuts. If the screen is "buffering", the string is collected in a buffer,
   884  // with the intention that the entire buffer be sent to the terminal in one
   885  // write operation at some point later.
   886  func (t *tScreen) writeString(s string) {
   887  	if t.buffering {
   888  		_, _ = io.WriteString(&t.buf, s)
   889  	} else {
   890  		_, _ = io.WriteString(t.tty, s)
   891  	}
   892  }
   893  
   894  func (t *tScreen) TPuts(s string) {
   895  	if t.buffering {
   896  		t.ti.TPuts(&t.buf, s)
   897  	} else {
   898  		t.ti.TPuts(t.tty, s)
   899  	}
   900  }
   901  
   902  func (t *tScreen) Show() {
   903  	t.Lock()
   904  	if !t.fini {
   905  		t.resize()
   906  		t.draw()
   907  	}
   908  	t.Unlock()
   909  }
   910  
   911  func (t *tScreen) clearScreen() {
   912  	t.TPuts(t.ti.AttrOff)
   913  	t.TPuts(t.exitUrl)
   914  	fg, bg, _ := t.style.Decompose()
   915  	_ = t.sendFgBg(fg, bg, AttrNone)
   916  	t.TPuts(t.ti.Clear)
   917  	t.clear = false
   918  }
   919  
   920  func (t *tScreen) hideCursor() {
   921  	// does not update cursor position
   922  	if t.ti.HideCursor != "" {
   923  		t.TPuts(t.ti.HideCursor)
   924  	} else {
   925  		// No way to hide cursor, stick it
   926  		// at bottom right of screen
   927  		t.cx, t.cy = t.cells.Size()
   928  		t.TPuts(t.ti.TGoto(t.cx, t.cy))
   929  	}
   930  }
   931  
   932  func (t *tScreen) draw() {
   933  	// clobber cursor position, because we're going to change it all
   934  	t.cx = -1
   935  	t.cy = -1
   936  	// make no style assumptions
   937  	t.curstyle = styleInvalid
   938  
   939  	t.buf.Reset()
   940  	t.buffering = true
   941  	defer func() {
   942  		t.buffering = false
   943  	}()
   944  
   945  	// hide the cursor while we move stuff around
   946  	t.hideCursor()
   947  
   948  	if t.clear {
   949  		t.clearScreen()
   950  	}
   951  
   952  	for y := 0; y < t.h; y++ {
   953  		for x := 0; x < t.w; x++ {
   954  			width := t.drawCell(x, y)
   955  			if width > 1 {
   956  				if x+1 < t.w {
   957  					// this is necessary so that if we ever
   958  					// go back to drawing that cell, we
   959  					// actually will *draw* it.
   960  					t.cells.SetDirty(x+1, y, true)
   961  				}
   962  			}
   963  			x += width - 1
   964  		}
   965  	}
   966  
   967  	// restore the cursor
   968  	t.showCursor()
   969  
   970  	_, _ = t.buf.WriteTo(t.tty)
   971  }
   972  
   973  func (t *tScreen) EnableMouse(flags ...MouseFlags) {
   974  	var f MouseFlags
   975  	flagsPresent := false
   976  	for _, flag := range flags {
   977  		f |= flag
   978  		flagsPresent = true
   979  	}
   980  	if !flagsPresent {
   981  		f = MouseMotionEvents | MouseDragEvents | MouseButtonEvents
   982  	}
   983  
   984  	t.Lock()
   985  	t.mouseFlags = f
   986  	t.enableMouse(f)
   987  	t.Unlock()
   988  }
   989  
   990  func (t *tScreen) enableMouse(f MouseFlags) {
   991  	// Rather than using terminfo to find mouse escape sequences, we rely on the fact that
   992  	// pretty much *every* terminal that supports mouse tracking follows the
   993  	// XTerm standards (the modern ones).
   994  	if len(t.mouse) != 0 {
   995  		// start by disabling all tracking.
   996  		t.TPuts("\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l")
   997  		if f&MouseButtonEvents != 0 {
   998  			t.TPuts("\x1b[?1000h")
   999  		}
  1000  		if f&MouseDragEvents != 0 {
  1001  			t.TPuts("\x1b[?1002h")
  1002  		}
  1003  		if f&MouseMotionEvents != 0 {
  1004  			t.TPuts("\x1b[?1003h")
  1005  		}
  1006  		if f&(MouseButtonEvents|MouseDragEvents|MouseMotionEvents) != 0 {
  1007  			t.TPuts("\x1b[?1006h")
  1008  		}
  1009  	}
  1010  
  1011  }
  1012  
  1013  func (t *tScreen) DisableMouse() {
  1014  	t.Lock()
  1015  	t.mouseFlags = 0
  1016  	t.enableMouse(0)
  1017  	t.Unlock()
  1018  }
  1019  
  1020  func (t *tScreen) EnablePaste() {
  1021  	t.Lock()
  1022  	t.pasteEnabled = true
  1023  	t.enablePasting(true)
  1024  	t.Unlock()
  1025  }
  1026  
  1027  func (t *tScreen) DisablePaste() {
  1028  	t.Lock()
  1029  	t.pasteEnabled = false
  1030  	t.enablePasting(false)
  1031  	t.Unlock()
  1032  }
  1033  
  1034  func (t *tScreen) enablePasting(on bool) {
  1035  	var s string
  1036  	if on {
  1037  		s = t.enablePaste
  1038  	} else {
  1039  		s = t.disablePaste
  1040  	}
  1041  	if s != "" {
  1042  		t.TPuts(s)
  1043  	}
  1044  }
  1045  
  1046  func (t *tScreen) Size() (int, int) {
  1047  	t.Lock()
  1048  	w, h := t.w, t.h
  1049  	t.Unlock()
  1050  	return w, h
  1051  }
  1052  
  1053  func (t *tScreen) resize() {
  1054  	if w, h, e := t.tty.WindowSize(); e == nil {
  1055  		if w != t.w || h != t.h {
  1056  			t.cx = -1
  1057  			t.cy = -1
  1058  
  1059  			t.cells.Resize(w, h)
  1060  			t.cells.Invalidate()
  1061  			t.h = h
  1062  			t.w = w
  1063  			ev := NewEventResize(w, h)
  1064  			_ = t.PostEvent(ev)
  1065  		}
  1066  	}
  1067  }
  1068  
  1069  func (t *tScreen) Colors() int {
  1070  	// this doesn't change, no need for lock
  1071  	if t.truecolor {
  1072  		return 1 << 24
  1073  	}
  1074  	return t.ti.Colors
  1075  }
  1076  
  1077  // nColors returns the size of the built-in palette.
  1078  // This is distinct from Colors(), as it will generally
  1079  // always be a small number. (<= 256)
  1080  func (t *tScreen) nColors() int {
  1081  	return t.ti.Colors
  1082  }
  1083  
  1084  func (t *tScreen) ChannelEvents(ch chan<- Event, quit <-chan struct{}) {
  1085  	defer close(ch)
  1086  	for {
  1087  		select {
  1088  		case <-quit:
  1089  			return
  1090  		case <-t.quit:
  1091  			return
  1092  		case ev := <-t.evch:
  1093  			select {
  1094  			case <-quit:
  1095  				return
  1096  			case <-t.quit:
  1097  				return
  1098  			case ch <- ev:
  1099  			}
  1100  		}
  1101  	}
  1102  }
  1103  
  1104  func (t *tScreen) PollEvent() Event {
  1105  	select {
  1106  	case <-t.quit:
  1107  		return nil
  1108  	case ev := <-t.evch:
  1109  		return ev
  1110  	}
  1111  }
  1112  
  1113  func (t *tScreen) HasPendingEvent() bool {
  1114  	return len(t.evch) > 0
  1115  }
  1116  
  1117  // vtACSNames is a map of bytes defined by terminfo that are used in
  1118  // the terminals Alternate Character Set to represent other glyphs.
  1119  // For example, the upper left corner of the box drawing set can be
  1120  // displayed by printing "l" while in the alternate character set.
  1121  // It's not quite that simple, since the "l" is the terminfo name,
  1122  // and it may be necessary to use a different character based on
  1123  // the terminal implementation (or the terminal may lack support for
  1124  // this altogether).  See buildAcsMap below for detail.
  1125  var vtACSNames = map[byte]rune{
  1126  	'+': RuneRArrow,
  1127  	',': RuneLArrow,
  1128  	'-': RuneUArrow,
  1129  	'.': RuneDArrow,
  1130  	'0': RuneBlock,
  1131  	'`': RuneDiamond,
  1132  	'a': RuneCkBoard,
  1133  	'b': '␉', // VT100, Not defined by terminfo
  1134  	'c': '␌', // VT100, Not defined by terminfo
  1135  	'd': '␋', // VT100, Not defined by terminfo
  1136  	'e': '␊', // VT100, Not defined by terminfo
  1137  	'f': RuneDegree,
  1138  	'g': RunePlMinus,
  1139  	'h': RuneBoard,
  1140  	'i': RuneLantern,
  1141  	'j': RuneLRCorner,
  1142  	'k': RuneURCorner,
  1143  	'l': RuneULCorner,
  1144  	'm': RuneLLCorner,
  1145  	'n': RunePlus,
  1146  	'o': RuneS1,
  1147  	'p': RuneS3,
  1148  	'q': RuneHLine,
  1149  	'r': RuneS7,
  1150  	's': RuneS9,
  1151  	't': RuneLTee,
  1152  	'u': RuneRTee,
  1153  	'v': RuneBTee,
  1154  	'w': RuneTTee,
  1155  	'x': RuneVLine,
  1156  	'y': RuneLEqual,
  1157  	'z': RuneGEqual,
  1158  	'{': RunePi,
  1159  	'|': RuneNEqual,
  1160  	'}': RuneSterling,
  1161  	'~': RuneBullet,
  1162  }
  1163  
  1164  // buildAcsMap builds a map of characters that we translate from Unicode to
  1165  // alternate character encodings.  To do this, we use the standard VT100 ACS
  1166  // maps.  This is only done if the terminal lacks support for Unicode; we
  1167  // always prefer to emit Unicode glyphs when we are able.
  1168  func (t *tScreen) buildAcsMap() {
  1169  	acsstr := t.ti.AltChars
  1170  	t.acs = make(map[rune]string)
  1171  	for len(acsstr) > 2 {
  1172  		srcv := acsstr[0]
  1173  		dstv := string(acsstr[1])
  1174  		if r, ok := vtACSNames[srcv]; ok {
  1175  			t.acs[r] = t.ti.EnterAcs + dstv + t.ti.ExitAcs
  1176  		}
  1177  		acsstr = acsstr[2:]
  1178  	}
  1179  }
  1180  
  1181  func (t *tScreen) PostEventWait(ev Event) {
  1182  	t.evch <- ev
  1183  }
  1184  
  1185  func (t *tScreen) PostEvent(ev Event) error {
  1186  	select {
  1187  	case t.evch <- ev:
  1188  		return nil
  1189  	default:
  1190  		return ErrEventQFull
  1191  	}
  1192  }
  1193  
  1194  func (t *tScreen) clip(x, y int) (int, int) {
  1195  	w, h := t.cells.Size()
  1196  	if x < 0 {
  1197  		x = 0
  1198  	}
  1199  	if y < 0 {
  1200  		y = 0
  1201  	}
  1202  	if x > w-1 {
  1203  		x = w - 1
  1204  	}
  1205  	if y > h-1 {
  1206  		y = h - 1
  1207  	}
  1208  	return x, y
  1209  }
  1210  
  1211  // buildMouseEvent returns an event based on the supplied coordinates and button
  1212  // state. Note that the screen's mouse button state is updated based on the
  1213  // input to this function (i.e. it mutates the receiver).
  1214  func (t *tScreen) buildMouseEvent(x, y, btn int) *EventMouse {
  1215  
  1216  	// XTerm mouse events only report at most one button at a time,
  1217  	// which may include a wheel button.  Wheel motion events are
  1218  	// reported as single impulses, while other button events are reported
  1219  	// as separate press & release events.
  1220  
  1221  	button := ButtonNone
  1222  	mod := ModNone
  1223  
  1224  	// Mouse wheel has bit 6 set, no release events.  It should be noted
  1225  	// that wheel events are sometimes misdelivered as mouse button events
  1226  	// during a click-drag, so we debounce these, considering them to be
  1227  	// button press events unless we see an intervening release event.
  1228  	switch btn & 0x43 {
  1229  	case 0:
  1230  		button = Button1
  1231  	case 1:
  1232  		button = Button3 // Note we prefer to treat right as button 2
  1233  	case 2:
  1234  		button = Button2 // And the middle button as button 3
  1235  	case 3:
  1236  		button = ButtonNone
  1237  	case 0x40:
  1238  		button = WheelUp
  1239  	case 0x41:
  1240  		button = WheelDown
  1241  	}
  1242  
  1243  	if btn&0x4 != 0 {
  1244  		mod |= ModShift
  1245  	}
  1246  	if btn&0x8 != 0 {
  1247  		mod |= ModAlt
  1248  	}
  1249  	if btn&0x10 != 0 {
  1250  		mod |= ModCtrl
  1251  	}
  1252  
  1253  	// Some terminals will report mouse coordinates outside the
  1254  	// screen, especially with click-drag events.  Clip the coordinates
  1255  	// to the screen in that case.
  1256  	x, y = t.clip(x, y)
  1257  
  1258  	return NewEventMouse(x, y, button, mod)
  1259  }
  1260  
  1261  // parseSgrMouse attempts to locate an SGR mouse record at the start of the
  1262  // buffer.  It returns true, true if it found one, and the associated bytes
  1263  // be removed from the buffer.  It returns true, false if the buffer might
  1264  // contain such an event, but more bytes are necessary (partial match), and
  1265  // false, false if the content is definitely *not* an SGR mouse record.
  1266  func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
  1267  
  1268  	b := buf.Bytes()
  1269  
  1270  	var x, y, btn, state int
  1271  	dig := false
  1272  	neg := false
  1273  	motion := false
  1274  	i := 0
  1275  	val := 0
  1276  
  1277  	for i = range b {
  1278  		switch b[i] {
  1279  		case '\x1b':
  1280  			if state != 0 {
  1281  				return false, false
  1282  			}
  1283  			state = 1
  1284  
  1285  		case '\x9b':
  1286  			if state != 0 {
  1287  				return false, false
  1288  			}
  1289  			state = 2
  1290  
  1291  		case '[':
  1292  			if state != 1 {
  1293  				return false, false
  1294  			}
  1295  			state = 2
  1296  
  1297  		case '<':
  1298  			if state != 2 {
  1299  				return false, false
  1300  			}
  1301  			val = 0
  1302  			dig = false
  1303  			neg = false
  1304  			state = 3
  1305  
  1306  		case '-':
  1307  			if state != 3 && state != 4 && state != 5 {
  1308  				return false, false
  1309  			}
  1310  			if dig || neg {
  1311  				return false, false
  1312  			}
  1313  			neg = true // stay in state
  1314  
  1315  		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
  1316  			if state != 3 && state != 4 && state != 5 {
  1317  				return false, false
  1318  			}
  1319  			val *= 10
  1320  			val += int(b[i] - '0')
  1321  			dig = true // stay in state
  1322  
  1323  		case ';':
  1324  			if neg {
  1325  				val = -val
  1326  			}
  1327  			switch state {
  1328  			case 3:
  1329  				btn, val = val, 0
  1330  				neg, dig, state = false, false, 4
  1331  			case 4:
  1332  				x, val = val-1, 0
  1333  				neg, dig, state = false, false, 5
  1334  			default:
  1335  				return false, false
  1336  			}
  1337  
  1338  		case 'm', 'M':
  1339  			if state != 5 {
  1340  				return false, false
  1341  			}
  1342  			if neg {
  1343  				val = -val
  1344  			}
  1345  			y = val - 1
  1346  
  1347  			motion = (btn & 32) != 0
  1348  			btn &^= 32
  1349  			if b[i] == 'm' {
  1350  				// mouse release, clear all buttons
  1351  				btn |= 3
  1352  				btn &^= 0x40
  1353  				t.buttondn = false
  1354  			} else if motion {
  1355  				/*
  1356  				 * Some broken terminals appear to send
  1357  				 * mouse button one motion events, instead of
  1358  				 * encoding 35 (no buttons) into these events.
  1359  				 * We resolve these by looking for a non-motion
  1360  				 * event first.
  1361  				 */
  1362  				if !t.buttondn {
  1363  					btn |= 3
  1364  					btn &^= 0x40
  1365  				}
  1366  			} else {
  1367  				t.buttondn = true
  1368  			}
  1369  			// consume the event bytes
  1370  			for i >= 0 {
  1371  				_, _ = buf.ReadByte()
  1372  				i--
  1373  			}
  1374  			*evs = append(*evs, t.buildMouseEvent(x, y, btn))
  1375  			return true, true
  1376  		}
  1377  	}
  1378  
  1379  	// incomplete & inconclusive at this point
  1380  	return true, false
  1381  }
  1382  
  1383  // parseXtermMouse is like parseSgrMouse, but it parses a legacy
  1384  // X11 mouse record.
  1385  func (t *tScreen) parseXtermMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
  1386  
  1387  	b := buf.Bytes()
  1388  
  1389  	state := 0
  1390  	btn := 0
  1391  	x := 0
  1392  	y := 0
  1393  
  1394  	for i := range b {
  1395  		switch state {
  1396  		case 0:
  1397  			switch b[i] {
  1398  			case '\x1b':
  1399  				state = 1
  1400  			case '\x9b':
  1401  				state = 2
  1402  			default:
  1403  				return false, false
  1404  			}
  1405  		case 1:
  1406  			if b[i] != '[' {
  1407  				return false, false
  1408  			}
  1409  			state = 2
  1410  		case 2:
  1411  			if b[i] != 'M' {
  1412  				return false, false
  1413  			}
  1414  			state++
  1415  		case 3:
  1416  			btn = int(b[i])
  1417  			state++
  1418  		case 4:
  1419  			x = int(b[i]) - 32 - 1
  1420  			state++
  1421  		case 5:
  1422  			y = int(b[i]) - 32 - 1
  1423  			for i >= 0 {
  1424  				_, _ = buf.ReadByte()
  1425  				i--
  1426  			}
  1427  			*evs = append(*evs, t.buildMouseEvent(x, y, btn))
  1428  			return true, true
  1429  		}
  1430  	}
  1431  	return true, false
  1432  }
  1433  
  1434  func (t *tScreen) parseFunctionKey(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
  1435  	b := buf.Bytes()
  1436  	partial := false
  1437  	for e, k := range t.keycodes {
  1438  		esc := []byte(e)
  1439  		if (len(esc) == 1) && (esc[0] == '\x1b') {
  1440  			continue
  1441  		}
  1442  		if bytes.HasPrefix(b, esc) {
  1443  			// matched
  1444  			var r rune
  1445  			if len(esc) == 1 {
  1446  				r = rune(b[0])
  1447  			}
  1448  			mod := k.mod
  1449  			if t.escaped {
  1450  				mod |= ModAlt
  1451  				t.escaped = false
  1452  			}
  1453  			switch k.key {
  1454  			case keyPasteStart:
  1455  				*evs = append(*evs, NewEventPaste(true))
  1456  			case keyPasteEnd:
  1457  				*evs = append(*evs, NewEventPaste(false))
  1458  			default:
  1459  				*evs = append(*evs, NewEventKey(k.key, r, mod))
  1460  			}
  1461  			for i := 0; i < len(esc); i++ {
  1462  				_, _ = buf.ReadByte()
  1463  			}
  1464  			return true, true
  1465  		}
  1466  		if bytes.HasPrefix(esc, b) {
  1467  			partial = true
  1468  		}
  1469  	}
  1470  	return partial, false
  1471  }
  1472  
  1473  func (t *tScreen) parseRune(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
  1474  	b := buf.Bytes()
  1475  	if b[0] >= ' ' && b[0] <= 0x7F {
  1476  		// printable ASCII easy to deal with -- no encodings
  1477  		mod := ModNone
  1478  		if t.escaped {
  1479  			mod = ModAlt
  1480  			t.escaped = false
  1481  		}
  1482  		*evs = append(*evs, NewEventKey(KeyRune, rune(b[0]), mod))
  1483  		_, _ = buf.ReadByte()
  1484  		return true, true
  1485  	}
  1486  
  1487  	if b[0] < 0x80 {
  1488  		// Low numbered values are control keys, not runes.
  1489  		return false, false
  1490  	}
  1491  
  1492  	utf := make([]byte, 12)
  1493  	for l := 1; l <= len(b); l++ {
  1494  		t.decoder.Reset()
  1495  		nOut, nIn, e := t.decoder.Transform(utf, b[:l], true)
  1496  		if e == transform.ErrShortSrc {
  1497  			continue
  1498  		}
  1499  		if nOut != 0 {
  1500  			r, _ := utf8.DecodeRune(utf[:nOut])
  1501  			if r != utf8.RuneError {
  1502  				mod := ModNone
  1503  				if t.escaped {
  1504  					mod = ModAlt
  1505  					t.escaped = false
  1506  				}
  1507  				*evs = append(*evs, NewEventKey(KeyRune, r, mod))
  1508  			}
  1509  			for nIn > 0 {
  1510  				_, _ = buf.ReadByte()
  1511  				nIn--
  1512  			}
  1513  			return true, true
  1514  		}
  1515  	}
  1516  	// Looks like potential escape
  1517  	return true, false
  1518  }
  1519  
  1520  func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) {
  1521  	evs := t.collectEventsFromInput(buf, expire)
  1522  
  1523  	for _, ev := range evs {
  1524  		t.PostEventWait(ev)
  1525  	}
  1526  }
  1527  
  1528  // Return an array of Events extracted from the supplied buffer. This is done
  1529  // while holding the screen's lock - the events can then be queued for
  1530  // application processing with the lock released.
  1531  func (t *tScreen) collectEventsFromInput(buf *bytes.Buffer, expire bool) []Event {
  1532  
  1533  	res := make([]Event, 0, 20)
  1534  
  1535  	t.Lock()
  1536  	defer t.Unlock()
  1537  
  1538  	for {
  1539  		b := buf.Bytes()
  1540  		if len(b) == 0 {
  1541  			buf.Reset()
  1542  			return res
  1543  		}
  1544  
  1545  		partials := 0
  1546  
  1547  		if part, comp := t.parseRune(buf, &res); comp {
  1548  			continue
  1549  		} else if part {
  1550  			partials++
  1551  		}
  1552  
  1553  		if part, comp := t.parseFunctionKey(buf, &res); comp {
  1554  			continue
  1555  		} else if part {
  1556  			partials++
  1557  		}
  1558  
  1559  		// Only parse mouse records if this term claims to have
  1560  		// mouse support
  1561  
  1562  		if t.ti.Mouse != "" {
  1563  			if part, comp := t.parseXtermMouse(buf, &res); comp {
  1564  				continue
  1565  			} else if part {
  1566  				partials++
  1567  			}
  1568  
  1569  			if part, comp := t.parseSgrMouse(buf, &res); comp {
  1570  				continue
  1571  			} else if part {
  1572  				partials++
  1573  			}
  1574  		}
  1575  
  1576  		if partials == 0 || expire {
  1577  			if b[0] == '\x1b' {
  1578  				if len(b) == 1 {
  1579  					res = append(res, NewEventKey(KeyEsc, 0, ModNone))
  1580  					t.escaped = false
  1581  				} else {
  1582  					t.escaped = true
  1583  				}
  1584  				_, _ = buf.ReadByte()
  1585  				continue
  1586  			}
  1587  			// Nothing was going to match, or we timed out
  1588  			// waiting for more data -- just deliver the characters
  1589  			// to the app & let them sort it out.  Possibly we
  1590  			// should only do this for control characters like ESC.
  1591  			by, _ := buf.ReadByte()
  1592  			mod := ModNone
  1593  			if t.escaped {
  1594  				t.escaped = false
  1595  				mod = ModAlt
  1596  			}
  1597  			res = append(res, NewEventKey(KeyRune, rune(by), mod))
  1598  			continue
  1599  		}
  1600  
  1601  		// well we have some partial data, wait until we get
  1602  		// some more
  1603  		break
  1604  	}
  1605  
  1606  	return res
  1607  }
  1608  
  1609  func (t *tScreen) mainLoop(stopQ chan struct{}) {
  1610  	defer t.wg.Done()
  1611  	buf := &bytes.Buffer{}
  1612  	for {
  1613  		select {
  1614  		case <-stopQ:
  1615  			return
  1616  		case <-t.quit:
  1617  			return
  1618  		case <-t.resizeQ:
  1619  			t.Lock()
  1620  			t.cx = -1
  1621  			t.cy = -1
  1622  			t.resize()
  1623  			t.cells.Invalidate()
  1624  			t.draw()
  1625  			t.Unlock()
  1626  			continue
  1627  		case <-t.keytimer.C:
  1628  			// If the timer fired, and the current time
  1629  			// is after the expiration of the escape sequence,
  1630  			// then we assume the escape sequence reached its
  1631  			// conclusion, and process the chunk independently.
  1632  			// This lets us detect conflicts such as a lone ESC.
  1633  			if buf.Len() > 0 {
  1634  				if time.Now().After(t.keyexpire) {
  1635  					t.scanInput(buf, true)
  1636  				}
  1637  			}
  1638  			if buf.Len() > 0 {
  1639  				if !t.keytimer.Stop() {
  1640  					select {
  1641  					case <-t.keytimer.C:
  1642  					default:
  1643  					}
  1644  				}
  1645  				t.keytimer.Reset(time.Millisecond * 50)
  1646  			}
  1647  		case chunk := <-t.keychan:
  1648  			buf.Write(chunk)
  1649  			t.keyexpire = time.Now().Add(time.Millisecond * 50)
  1650  			t.scanInput(buf, false)
  1651  			if !t.keytimer.Stop() {
  1652  				select {
  1653  				case <-t.keytimer.C:
  1654  				default:
  1655  				}
  1656  			}
  1657  			if buf.Len() > 0 {
  1658  				t.keytimer.Reset(time.Millisecond * 50)
  1659  			}
  1660  		}
  1661  	}
  1662  }
  1663  
  1664  func (t *tScreen) inputLoop(stopQ chan struct{}) {
  1665  
  1666  	defer t.wg.Done()
  1667  	for {
  1668  		select {
  1669  		case <-stopQ:
  1670  			return
  1671  		default:
  1672  		}
  1673  		chunk := make([]byte, 128)
  1674  		n, e := t.tty.Read(chunk)
  1675  		switch e {
  1676  		case nil:
  1677  		default:
  1678  			t.Lock()
  1679  			running := t.running
  1680  			t.Unlock()
  1681  			if running {
  1682  				_ = t.PostEvent(NewEventError(e))
  1683  			}
  1684  			return
  1685  		}
  1686  		if n > 0 {
  1687  			t.keychan <- chunk[:n]
  1688  		}
  1689  	}
  1690  }
  1691  
  1692  func (t *tScreen) Sync() {
  1693  	t.Lock()
  1694  	t.cx = -1
  1695  	t.cy = -1
  1696  	if !t.fini {
  1697  		t.resize()
  1698  		t.clear = true
  1699  		t.cells.Invalidate()
  1700  		t.draw()
  1701  	}
  1702  	t.Unlock()
  1703  }
  1704  
  1705  func (t *tScreen) CharacterSet() string {
  1706  	return t.charset
  1707  }
  1708  
  1709  func (t *tScreen) RegisterRuneFallback(orig rune, fallback string) {
  1710  	t.Lock()
  1711  	t.fallback[orig] = fallback
  1712  	t.Unlock()
  1713  }
  1714  
  1715  func (t *tScreen) UnregisterRuneFallback(orig rune) {
  1716  	t.Lock()
  1717  	delete(t.fallback, orig)
  1718  	t.Unlock()
  1719  }
  1720  
  1721  func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
  1722  
  1723  	if enc := t.encoder; enc != nil {
  1724  		nb := make([]byte, 6)
  1725  		ob := make([]byte, 6)
  1726  		num := utf8.EncodeRune(ob, r)
  1727  
  1728  		enc.Reset()
  1729  		dst, _, err := enc.Transform(nb, ob[:num], true)
  1730  		if dst != 0 && err == nil && nb[0] != '\x1A' {
  1731  			return true
  1732  		}
  1733  	}
  1734  	// Terminal fallbacks always permitted, since we assume they are
  1735  	// basically nearly perfect renditions.
  1736  	if _, ok := t.acs[r]; ok {
  1737  		return true
  1738  	}
  1739  	if !checkFallbacks {
  1740  		return false
  1741  	}
  1742  	if _, ok := t.fallback[r]; ok {
  1743  		return true
  1744  	}
  1745  	return false
  1746  }
  1747  
  1748  func (t *tScreen) HasMouse() bool {
  1749  	return len(t.mouse) != 0
  1750  }
  1751  
  1752  func (t *tScreen) HasKey(k Key) bool {
  1753  	if k == KeyRune {
  1754  		return true
  1755  	}
  1756  	return t.keyexist[k]
  1757  }
  1758  
  1759  func (t *tScreen) SetSize(w, h int) {
  1760  	if t.setWinSize != "" {
  1761  		t.TPuts(t.ti.TParm(t.setWinSize, w, h))
  1762  	}
  1763  	t.cells.Invalidate()
  1764  	t.resize()
  1765  }
  1766  
  1767  func (t *tScreen) Resize(int, int, int, int) {}
  1768  
  1769  func (t *tScreen) Suspend() error {
  1770  	t.disengage()
  1771  	return nil
  1772  }
  1773  
  1774  func (t *tScreen) Resume() error {
  1775  	return t.engage()
  1776  }
  1777  
  1778  // engage is used to place the terminal in raw mode and establish screen size, etc.
  1779  // Think of this is as tcell "engaging" the clutch, as it's going to be driving the
  1780  // terminal interface.
  1781  func (t *tScreen) engage() error {
  1782  	t.Lock()
  1783  	defer t.Unlock()
  1784  	if t.tty == nil {
  1785  		return ErrNoScreen
  1786  	}
  1787  	t.tty.NotifyResize(func() {
  1788  		select {
  1789  		case t.resizeQ <- true:
  1790  		default:
  1791  		}
  1792  	})
  1793  	if t.running {
  1794  		return errors.New("already engaged")
  1795  	}
  1796  	if err := t.tty.Start(); err != nil {
  1797  		return err
  1798  	}
  1799  	t.running = true
  1800  	if w, h, err := t.tty.WindowSize(); err == nil && w != 0 && h != 0 {
  1801  		t.cells.Resize(w, h)
  1802  	}
  1803  	stopQ := make(chan struct{})
  1804  	t.stopQ = stopQ
  1805  	t.enableMouse(t.mouseFlags)
  1806  	t.enablePasting(t.pasteEnabled)
  1807  
  1808  	ti := t.ti
  1809  	t.TPuts(ti.EnterCA)
  1810  	t.TPuts(ti.EnterKeypad)
  1811  	t.TPuts(ti.HideCursor)
  1812  	t.TPuts(ti.EnableAcs)
  1813  	t.TPuts(ti.Clear)
  1814  
  1815  	t.wg.Add(2)
  1816  	go t.inputLoop(stopQ)
  1817  	go t.mainLoop(stopQ)
  1818  	return nil
  1819  }
  1820  
  1821  // disengage is used to release the terminal back to support from the caller.
  1822  // Think of this as tcell disengaging the clutch, so that another application
  1823  // can take over the terminal interface.  This restores the TTY mode that was
  1824  // present when the application was first started.
  1825  func (t *tScreen) disengage() {
  1826  
  1827  	t.Lock()
  1828  	if !t.running {
  1829  		t.Unlock()
  1830  		return
  1831  	}
  1832  	t.running = false
  1833  	stopQ := t.stopQ
  1834  	close(stopQ)
  1835  	_ = t.tty.Drain()
  1836  	t.Unlock()
  1837  
  1838  	t.tty.NotifyResize(nil)
  1839  	// wait for everything to shut down
  1840  	t.wg.Wait()
  1841  
  1842  	// shutdown the screen and disable special modes (e.g. mouse and bracketed paste)
  1843  	ti := t.ti
  1844  	t.cells.Resize(0, 0)
  1845  	t.TPuts(ti.ShowCursor)
  1846  	if t.cursorStyles != nil && t.cursorStyle != CursorStyleDefault {
  1847  		t.TPuts(t.cursorStyles[t.cursorStyle])
  1848  	}
  1849  	t.TPuts(ti.ResetFgBg)
  1850  	t.TPuts(ti.AttrOff)
  1851  	t.TPuts(ti.Clear)
  1852  	t.TPuts(ti.ExitCA)
  1853  	t.TPuts(ti.ExitKeypad)
  1854  	t.enableMouse(0)
  1855  	t.enablePasting(false)
  1856  
  1857  	_ = t.tty.Stop()
  1858  }
  1859  
  1860  // Beep emits a beep to the terminal.
  1861  func (t *tScreen) Beep() error {
  1862  	t.writeString(string(byte(7)))
  1863  	return nil
  1864  }
  1865  
  1866  // finalize is used to at application shutdown, and restores the terminal
  1867  // to it's initial state.  It should not be called more than once.
  1868  func (t *tScreen) finalize() {
  1869  	t.disengage()
  1870  	_ = t.tty.Close()
  1871  }
  1872  

View as plain text