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