2019-09-22 11:15:39 -07:00
|
|
|
// Copyright 2019 The TCell Authors
|
2015-09-26 23:37:54 -07:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the license at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package tcell
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"sync"
|
2017-09-23 23:25:24 -07:00
|
|
|
"time"
|
2015-09-26 23:37:54 -07:00
|
|
|
"unicode/utf8"
|
2015-10-04 23:22:57 -07:00
|
|
|
|
|
|
|
"golang.org/x/text/transform"
|
2017-11-23 12:31:44 -08:00
|
|
|
|
|
|
|
"github.com/gdamore/tcell/terminfo"
|
2019-07-23 18:54:20 -07:00
|
|
|
|
|
|
|
// import the stock terminals
|
|
|
|
_ "github.com/gdamore/tcell/terminfo/base"
|
2015-09-26 23:37:54 -07:00
|
|
|
)
|
|
|
|
|
2015-10-05 15:46:51 -07:00
|
|
|
// NewTerminfoScreen returns a Screen that uses the stock TTY interface
|
|
|
|
// and POSIX termios, combined with a terminfo description taken from
|
|
|
|
// the $TERM environment variable. It returns an error if the terminal
|
|
|
|
// is not supported for any reason.
|
|
|
|
//
|
|
|
|
// For terminals that do not support dynamic resize events, the $LINES
|
|
|
|
// $COLUMNS environment variables can be set to the actual window size,
|
|
|
|
// otherwise defaults taken from the terminal database are used.
|
2015-09-26 23:37:54 -07:00
|
|
|
func NewTerminfoScreen() (Screen, error) {
|
2017-11-23 12:31:44 -08:00
|
|
|
ti, e := terminfo.LookupTerminfo(os.Getenv("TERM"))
|
2015-09-26 23:37:54 -07:00
|
|
|
if e != nil {
|
2019-09-22 11:15:39 -07:00
|
|
|
ti, e = loadDynamicTerminfo(os.Getenv("TERM"))
|
2019-06-12 23:34:37 -07:00
|
|
|
if e != nil {
|
|
|
|
return nil, e
|
|
|
|
}
|
|
|
|
terminfo.AddTerminfo(ti)
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
t := &tScreen{ti: ti}
|
|
|
|
|
2016-03-23 13:14:49 -07:00
|
|
|
t.keyexist = make(map[Key]bool)
|
2016-04-28 12:37:50 -07:00
|
|
|
t.keycodes = make(map[string]*tKeyCode)
|
2015-09-26 23:37:54 -07:00
|
|
|
if len(ti.Mouse) > 0 {
|
|
|
|
t.mouse = []byte(ti.Mouse)
|
|
|
|
}
|
|
|
|
t.prepareKeys()
|
2015-10-04 19:53:38 -07:00
|
|
|
t.buildAcsMap()
|
2015-10-07 00:56:18 -07:00
|
|
|
t.sigwinch = make(chan os.Signal, 10)
|
2015-10-14 19:09:00 -07:00
|
|
|
t.fallback = make(map[rune]string)
|
|
|
|
for k, v := range RuneFallbacks {
|
|
|
|
t.fallback[k] = v
|
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
|
|
|
|
return t, nil
|
|
|
|
}
|
|
|
|
|
2016-04-28 12:37:50 -07:00
|
|
|
// tKeyCode represents a combination of a key code and modifiers.
|
|
|
|
type tKeyCode struct {
|
|
|
|
key Key
|
|
|
|
mod ModMask
|
|
|
|
}
|
|
|
|
|
2015-09-26 23:37:54 -07:00
|
|
|
// tScreen represents a screen backed by a terminfo implementation.
|
|
|
|
type tScreen struct {
|
2017-11-23 12:31:44 -08:00
|
|
|
ti *terminfo.Terminfo
|
2015-10-27 23:28:26 -07:00
|
|
|
h int
|
|
|
|
w int
|
|
|
|
fini bool
|
|
|
|
cells CellBuffer
|
|
|
|
in *os.File
|
|
|
|
out *os.File
|
2019-03-19 03:31:05 -04:00
|
|
|
buffering bool // true if we are collecting writes to buf instead of sending directly to out
|
|
|
|
buf bytes.Buffer
|
2015-10-27 23:28:26 -07:00
|
|
|
curstyle Style
|
|
|
|
style Style
|
|
|
|
evch chan Event
|
|
|
|
sigwinch chan os.Signal
|
|
|
|
quit chan struct{}
|
|
|
|
indoneq chan struct{}
|
2016-03-23 13:14:49 -07:00
|
|
|
keyexist map[Key]bool
|
2016-04-28 12:37:50 -07:00
|
|
|
keycodes map[string]*tKeyCode
|
2017-09-23 23:25:24 -07:00
|
|
|
keychan chan []byte
|
|
|
|
keytimer *time.Timer
|
|
|
|
keyexpire time.Time
|
2015-10-27 23:28:26 -07:00
|
|
|
cx int
|
|
|
|
cy int
|
|
|
|
mouse []byte
|
|
|
|
clear bool
|
|
|
|
cursorx int
|
|
|
|
cursory int
|
|
|
|
tiosp *termiosPrivate
|
|
|
|
wasbtn bool
|
|
|
|
acs map[rune]string
|
|
|
|
charset string
|
|
|
|
encoder transform.Transformer
|
|
|
|
decoder transform.Transformer
|
|
|
|
fallback map[rune]string
|
|
|
|
colors map[Color]Color
|
|
|
|
palette []Color
|
|
|
|
truecolor bool
|
2016-04-28 00:44:30 -07:00
|
|
|
escaped bool
|
2016-04-29 06:08:51 -07:00
|
|
|
buttondn bool
|
2015-09-26 23:37:54 -07:00
|
|
|
|
|
|
|
sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) Init() error {
|
2015-10-05 01:27:55 -07:00
|
|
|
t.evch = make(chan Event, 10)
|
2015-10-02 21:49:13 -07:00
|
|
|
t.indoneq = make(chan struct{})
|
2017-09-23 23:25:24 -07:00
|
|
|
t.keychan = make(chan []byte, 10)
|
|
|
|
t.keytimer = time.NewTimer(time.Millisecond * 50)
|
2015-10-04 19:53:38 -07:00
|
|
|
t.charset = "UTF-8"
|
2015-10-04 23:22:57 -07:00
|
|
|
|
2015-11-04 17:05:24 -08:00
|
|
|
t.charset = getCharset()
|
2015-10-07 20:15:33 -07:00
|
|
|
if enc := GetEncoding(t.charset); enc != nil {
|
|
|
|
t.encoder = enc.NewEncoder()
|
|
|
|
t.decoder = enc.NewDecoder()
|
|
|
|
} else {
|
|
|
|
return ErrNoCharset
|
2015-10-04 23:22:57 -07:00
|
|
|
}
|
|
|
|
ti := t.ti
|
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
// environment overrides
|
|
|
|
w := ti.Columns
|
|
|
|
h := ti.Lines
|
|
|
|
if i, _ := strconv.Atoi(os.Getenv("LINES")); i != 0 {
|
|
|
|
h = i
|
|
|
|
}
|
|
|
|
if i, _ := strconv.Atoi(os.Getenv("COLUMNS")); i != 0 {
|
|
|
|
w = i
|
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
if e := t.termioInit(); e != nil {
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
2016-05-19 11:35:33 -07:00
|
|
|
if t.ti.SetFgBgRGB != "" || t.ti.SetFgRGB != "" || t.ti.SetBgRGB != "" {
|
2015-10-27 23:28:26 -07:00
|
|
|
t.truecolor = true
|
|
|
|
}
|
|
|
|
// A user who wants to have his themes honored can
|
|
|
|
// set this environment variable.
|
|
|
|
if os.Getenv("TCELL_TRUECOLOR") == "disable" {
|
|
|
|
t.truecolor = false
|
|
|
|
}
|
|
|
|
if !t.truecolor {
|
|
|
|
t.colors = make(map[Color]Color)
|
|
|
|
t.palette = make([]Color, t.Colors())
|
|
|
|
for i := 0; i < t.Colors(); i++ {
|
|
|
|
t.palette[i] = Color(i)
|
|
|
|
// identity map for our builtin colors
|
|
|
|
t.colors[Color(i)] = Color(i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.EnterCA)
|
|
|
|
t.TPuts(ti.HideCursor)
|
2015-10-14 14:10:01 -07:00
|
|
|
t.TPuts(ti.EnableAcs)
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.Clear)
|
2015-09-26 23:37:54 -07:00
|
|
|
|
|
|
|
t.quit = make(chan struct{})
|
2015-10-07 00:56:18 -07:00
|
|
|
|
|
|
|
t.Lock()
|
2015-09-26 23:37:54 -07:00
|
|
|
t.cx = -1
|
|
|
|
t.cy = -1
|
2015-10-02 21:49:13 -07:00
|
|
|
t.style = StyleDefault
|
2015-10-07 00:56:18 -07:00
|
|
|
t.cells.Resize(w, h)
|
2015-10-02 21:49:13 -07:00
|
|
|
t.cursorx = -1
|
|
|
|
t.cursory = -1
|
2015-10-07 00:56:18 -07:00
|
|
|
t.resize()
|
2015-10-06 00:14:18 -07:00
|
|
|
t.Unlock()
|
2015-10-07 00:56:18 -07:00
|
|
|
|
2017-09-23 23:25:24 -07:00
|
|
|
go t.mainLoop()
|
2015-09-26 23:37:54 -07:00
|
|
|
go t.inputLoop()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-28 12:37:50 -07:00
|
|
|
func (t *tScreen) prepareKeyMod(key Key, mod ModMask, val string) {
|
2015-09-26 23:37:54 -07:00
|
|
|
if val != "" {
|
2016-03-23 13:14:49 -07:00
|
|
|
// Do not overrride codes that already exist
|
|
|
|
if _, exist := t.keycodes[val]; !exist {
|
|
|
|
t.keyexist[key] = true
|
2016-04-28 12:37:50 -07:00
|
|
|
t.keycodes[val] = &tKeyCode{key: key, mod: mod}
|
2016-03-23 13:14:49 -07:00
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-28 12:37:50 -07:00
|
|
|
func (t *tScreen) prepareKey(key Key, val string) {
|
|
|
|
t.prepareKeyMod(key, ModNone, val)
|
|
|
|
}
|
|
|
|
|
2015-09-26 23:37:54 -07:00
|
|
|
func (t *tScreen) prepareKeys() {
|
|
|
|
ti := t.ti
|
|
|
|
t.prepareKey(KeyBackspace, ti.KeyBackspace)
|
|
|
|
t.prepareKey(KeyF1, ti.KeyF1)
|
|
|
|
t.prepareKey(KeyF2, ti.KeyF2)
|
|
|
|
t.prepareKey(KeyF3, ti.KeyF3)
|
|
|
|
t.prepareKey(KeyF4, ti.KeyF4)
|
|
|
|
t.prepareKey(KeyF5, ti.KeyF5)
|
|
|
|
t.prepareKey(KeyF6, ti.KeyF6)
|
|
|
|
t.prepareKey(KeyF7, ti.KeyF7)
|
|
|
|
t.prepareKey(KeyF8, ti.KeyF8)
|
|
|
|
t.prepareKey(KeyF9, ti.KeyF9)
|
|
|
|
t.prepareKey(KeyF10, ti.KeyF10)
|
|
|
|
t.prepareKey(KeyF11, ti.KeyF11)
|
|
|
|
t.prepareKey(KeyF12, ti.KeyF12)
|
|
|
|
t.prepareKey(KeyF13, ti.KeyF13)
|
|
|
|
t.prepareKey(KeyF14, ti.KeyF14)
|
|
|
|
t.prepareKey(KeyF15, ti.KeyF15)
|
|
|
|
t.prepareKey(KeyF16, ti.KeyF16)
|
|
|
|
t.prepareKey(KeyF17, ti.KeyF17)
|
|
|
|
t.prepareKey(KeyF18, ti.KeyF18)
|
|
|
|
t.prepareKey(KeyF19, ti.KeyF19)
|
|
|
|
t.prepareKey(KeyF20, ti.KeyF20)
|
2015-10-03 11:29:55 -07:00
|
|
|
t.prepareKey(KeyF21, ti.KeyF21)
|
|
|
|
t.prepareKey(KeyF22, ti.KeyF22)
|
|
|
|
t.prepareKey(KeyF23, ti.KeyF23)
|
|
|
|
t.prepareKey(KeyF24, ti.KeyF24)
|
|
|
|
t.prepareKey(KeyF25, ti.KeyF25)
|
|
|
|
t.prepareKey(KeyF26, ti.KeyF26)
|
|
|
|
t.prepareKey(KeyF27, ti.KeyF27)
|
|
|
|
t.prepareKey(KeyF28, ti.KeyF28)
|
|
|
|
t.prepareKey(KeyF29, ti.KeyF29)
|
|
|
|
t.prepareKey(KeyF30, ti.KeyF30)
|
|
|
|
t.prepareKey(KeyF31, ti.KeyF31)
|
|
|
|
t.prepareKey(KeyF32, ti.KeyF32)
|
|
|
|
t.prepareKey(KeyF33, ti.KeyF33)
|
|
|
|
t.prepareKey(KeyF34, ti.KeyF34)
|
|
|
|
t.prepareKey(KeyF35, ti.KeyF35)
|
|
|
|
t.prepareKey(KeyF36, ti.KeyF36)
|
|
|
|
t.prepareKey(KeyF37, ti.KeyF37)
|
|
|
|
t.prepareKey(KeyF38, ti.KeyF38)
|
|
|
|
t.prepareKey(KeyF39, ti.KeyF39)
|
|
|
|
t.prepareKey(KeyF40, ti.KeyF40)
|
|
|
|
t.prepareKey(KeyF41, ti.KeyF41)
|
|
|
|
t.prepareKey(KeyF42, ti.KeyF42)
|
|
|
|
t.prepareKey(KeyF43, ti.KeyF43)
|
|
|
|
t.prepareKey(KeyF44, ti.KeyF44)
|
|
|
|
t.prepareKey(KeyF45, ti.KeyF45)
|
|
|
|
t.prepareKey(KeyF46, ti.KeyF46)
|
|
|
|
t.prepareKey(KeyF47, ti.KeyF47)
|
|
|
|
t.prepareKey(KeyF48, ti.KeyF48)
|
|
|
|
t.prepareKey(KeyF49, ti.KeyF49)
|
|
|
|
t.prepareKey(KeyF50, ti.KeyF50)
|
|
|
|
t.prepareKey(KeyF51, ti.KeyF51)
|
|
|
|
t.prepareKey(KeyF52, ti.KeyF52)
|
|
|
|
t.prepareKey(KeyF53, ti.KeyF53)
|
|
|
|
t.prepareKey(KeyF54, ti.KeyF54)
|
|
|
|
t.prepareKey(KeyF55, ti.KeyF55)
|
|
|
|
t.prepareKey(KeyF56, ti.KeyF56)
|
|
|
|
t.prepareKey(KeyF57, ti.KeyF57)
|
|
|
|
t.prepareKey(KeyF58, ti.KeyF58)
|
|
|
|
t.prepareKey(KeyF59, ti.KeyF59)
|
|
|
|
t.prepareKey(KeyF60, ti.KeyF60)
|
|
|
|
t.prepareKey(KeyF61, ti.KeyF61)
|
|
|
|
t.prepareKey(KeyF62, ti.KeyF62)
|
|
|
|
t.prepareKey(KeyF63, ti.KeyF63)
|
|
|
|
t.prepareKey(KeyF64, ti.KeyF64)
|
2015-09-26 23:37:54 -07:00
|
|
|
t.prepareKey(KeyInsert, ti.KeyInsert)
|
|
|
|
t.prepareKey(KeyDelete, ti.KeyDelete)
|
|
|
|
t.prepareKey(KeyHome, ti.KeyHome)
|
|
|
|
t.prepareKey(KeyEnd, ti.KeyEnd)
|
|
|
|
t.prepareKey(KeyUp, ti.KeyUp)
|
|
|
|
t.prepareKey(KeyDown, ti.KeyDown)
|
|
|
|
t.prepareKey(KeyLeft, ti.KeyLeft)
|
|
|
|
t.prepareKey(KeyRight, ti.KeyRight)
|
|
|
|
t.prepareKey(KeyPgUp, ti.KeyPgUp)
|
|
|
|
t.prepareKey(KeyPgDn, ti.KeyPgDn)
|
|
|
|
t.prepareKey(KeyHelp, ti.KeyHelp)
|
2015-10-03 11:29:55 -07:00
|
|
|
t.prepareKey(KeyPrint, ti.KeyPrint)
|
|
|
|
t.prepareKey(KeyCancel, ti.KeyCancel)
|
|
|
|
t.prepareKey(KeyExit, ti.KeyExit)
|
|
|
|
t.prepareKey(KeyBacktab, ti.KeyBacktab)
|
2016-03-23 10:49:51 -07:00
|
|
|
|
2016-04-28 12:37:50 -07:00
|
|
|
t.prepareKeyMod(KeyRight, ModShift, ti.KeyShfRight)
|
|
|
|
t.prepareKeyMod(KeyLeft, ModShift, ti.KeyShfLeft)
|
|
|
|
t.prepareKeyMod(KeyUp, ModShift, ti.KeyShfUp)
|
|
|
|
t.prepareKeyMod(KeyDown, ModShift, ti.KeyShfDown)
|
|
|
|
t.prepareKeyMod(KeyHome, ModShift, ti.KeyShfHome)
|
|
|
|
t.prepareKeyMod(KeyEnd, ModShift, ti.KeyShfEnd)
|
|
|
|
|
|
|
|
t.prepareKeyMod(KeyRight, ModCtrl, ti.KeyCtrlRight)
|
|
|
|
t.prepareKeyMod(KeyLeft, ModCtrl, ti.KeyCtrlLeft)
|
|
|
|
t.prepareKeyMod(KeyUp, ModCtrl, ti.KeyCtrlUp)
|
|
|
|
t.prepareKeyMod(KeyDown, ModCtrl, ti.KeyCtrlDown)
|
|
|
|
t.prepareKeyMod(KeyHome, ModCtrl, ti.KeyCtrlHome)
|
|
|
|
t.prepareKeyMod(KeyEnd, ModCtrl, ti.KeyCtrlEnd)
|
|
|
|
|
|
|
|
t.prepareKeyMod(KeyRight, ModAlt, ti.KeyAltRight)
|
|
|
|
t.prepareKeyMod(KeyLeft, ModAlt, ti.KeyAltLeft)
|
|
|
|
t.prepareKeyMod(KeyUp, ModAlt, ti.KeyAltUp)
|
|
|
|
t.prepareKeyMod(KeyDown, ModAlt, ti.KeyAltDown)
|
|
|
|
t.prepareKeyMod(KeyHome, ModAlt, ti.KeyAltHome)
|
|
|
|
t.prepareKeyMod(KeyEnd, ModAlt, ti.KeyAltEnd)
|
|
|
|
|
2016-05-19 16:45:31 -07:00
|
|
|
t.prepareKeyMod(KeyRight, ModAlt, ti.KeyMetaRight)
|
|
|
|
t.prepareKeyMod(KeyLeft, ModAlt, ti.KeyMetaLeft)
|
|
|
|
t.prepareKeyMod(KeyUp, ModAlt, ti.KeyMetaUp)
|
|
|
|
t.prepareKeyMod(KeyDown, ModAlt, ti.KeyMetaDown)
|
|
|
|
t.prepareKeyMod(KeyHome, ModAlt, ti.KeyMetaHome)
|
|
|
|
t.prepareKeyMod(KeyEnd, ModAlt, ti.KeyMetaEnd)
|
|
|
|
|
|
|
|
t.prepareKeyMod(KeyRight, ModAlt|ModShift, ti.KeyAltShfRight)
|
|
|
|
t.prepareKeyMod(KeyLeft, ModAlt|ModShift, ti.KeyAltShfLeft)
|
|
|
|
t.prepareKeyMod(KeyUp, ModAlt|ModShift, ti.KeyAltShfUp)
|
|
|
|
t.prepareKeyMod(KeyDown, ModAlt|ModShift, ti.KeyAltShfDown)
|
|
|
|
t.prepareKeyMod(KeyHome, ModAlt|ModShift, ti.KeyAltShfHome)
|
|
|
|
t.prepareKeyMod(KeyEnd, ModAlt|ModShift, ti.KeyAltShfEnd)
|
|
|
|
|
|
|
|
t.prepareKeyMod(KeyRight, ModAlt|ModShift, ti.KeyMetaShfRight)
|
|
|
|
t.prepareKeyMod(KeyLeft, ModAlt|ModShift, ti.KeyMetaShfLeft)
|
|
|
|
t.prepareKeyMod(KeyUp, ModAlt|ModShift, ti.KeyMetaShfUp)
|
|
|
|
t.prepareKeyMod(KeyDown, ModAlt|ModShift, ti.KeyMetaShfDown)
|
|
|
|
t.prepareKeyMod(KeyHome, ModAlt|ModShift, ti.KeyMetaShfHome)
|
|
|
|
t.prepareKeyMod(KeyEnd, ModAlt|ModShift, ti.KeyMetaShfEnd)
|
|
|
|
|
|
|
|
t.prepareKeyMod(KeyRight, ModCtrl|ModShift, ti.KeyCtrlShfRight)
|
|
|
|
t.prepareKeyMod(KeyLeft, ModCtrl|ModShift, ti.KeyCtrlShfLeft)
|
|
|
|
t.prepareKeyMod(KeyUp, ModCtrl|ModShift, ti.KeyCtrlShfUp)
|
|
|
|
t.prepareKeyMod(KeyDown, ModCtrl|ModShift, ti.KeyCtrlShfDown)
|
|
|
|
t.prepareKeyMod(KeyHome, ModCtrl|ModShift, ti.KeyCtrlShfHome)
|
|
|
|
t.prepareKeyMod(KeyEnd, ModCtrl|ModShift, ti.KeyCtrlShfEnd)
|
2016-04-30 10:32:37 -04:00
|
|
|
|
2016-03-23 13:14:49 -07:00
|
|
|
// Sadly, xterm handling of keycodes is somewhat erratic. In
|
|
|
|
// particular, different codes are sent depending on application
|
|
|
|
// mode is in use or not, and the entries for many of these are
|
|
|
|
// simply absent from terminfo on many systems. So we insert
|
|
|
|
// a number of escape sequences if they are not already used, in
|
|
|
|
// order to have the widest correct usage. Note that prepareKey
|
|
|
|
// will not inject codes if the escape sequence is already known.
|
|
|
|
// We also only do this for terminals that have the application
|
|
|
|
// mode present.
|
|
|
|
|
|
|
|
// Cursor mode
|
|
|
|
if ti.EnterKeypad != "" {
|
|
|
|
t.prepareKey(KeyUp, "\x1b[A")
|
|
|
|
t.prepareKey(KeyDown, "\x1b[B")
|
|
|
|
t.prepareKey(KeyRight, "\x1b[C")
|
|
|
|
t.prepareKey(KeyLeft, "\x1b[D")
|
|
|
|
t.prepareKey(KeyEnd, "\x1b[F")
|
|
|
|
t.prepareKey(KeyHome, "\x1b[H")
|
|
|
|
t.prepareKey(KeyDelete, "\x1b[3~")
|
|
|
|
t.prepareKey(KeyHome, "\x1b[1~")
|
|
|
|
t.prepareKey(KeyEnd, "\x1b[4~")
|
|
|
|
t.prepareKey(KeyPgUp, "\x1b[5~")
|
|
|
|
t.prepareKey(KeyPgDn, "\x1b[6~")
|
|
|
|
|
|
|
|
// Application mode
|
|
|
|
t.prepareKey(KeyUp, "\x1bOA")
|
|
|
|
t.prepareKey(KeyDown, "\x1bOB")
|
|
|
|
t.prepareKey(KeyRight, "\x1bOC")
|
|
|
|
t.prepareKey(KeyLeft, "\x1bOD")
|
|
|
|
t.prepareKey(KeyHome, "\x1bOH")
|
|
|
|
}
|
|
|
|
|
2016-03-23 10:49:51 -07:00
|
|
|
outer:
|
2016-03-23 13:14:49 -07:00
|
|
|
// Add key mappings for control keys.
|
2016-03-23 10:49:51 -07:00
|
|
|
for i := 0; i < ' '; i++ {
|
2016-03-23 13:14:49 -07:00
|
|
|
// Do not insert direct key codes for ambiguous keys.
|
|
|
|
// For example, ESC is used for lots of other keys, so
|
|
|
|
// when parsing this we don't want to fast path handling
|
|
|
|
// of it, but instead wait a bit before parsing it as in
|
|
|
|
// isolation.
|
|
|
|
for esc := range t.keycodes {
|
|
|
|
if []byte(esc)[0] == byte(i) {
|
2016-03-23 10:49:51 -07:00
|
|
|
continue outer
|
|
|
|
}
|
|
|
|
}
|
2016-03-23 13:14:49 -07:00
|
|
|
|
|
|
|
t.keyexist[Key(i)] = true
|
2016-04-28 12:37:50 -07:00
|
|
|
|
|
|
|
mod := ModCtrl
|
|
|
|
switch Key(i) {
|
|
|
|
case KeyBS, KeyTAB, KeyESC, KeyCR:
|
|
|
|
// directly typeable- no control sequence
|
|
|
|
mod = ModNone
|
|
|
|
}
|
|
|
|
t.keycodes[string(rune(i))] = &tKeyCode{key: Key(i), mod: mod}
|
2016-03-23 10:49:51 -07:00
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) Fini() {
|
2015-10-06 00:14:18 -07:00
|
|
|
t.Lock()
|
2018-01-22 23:41:06 -07:00
|
|
|
defer t.Unlock()
|
2018-06-17 00:44:25 +02:00
|
|
|
|
|
|
|
ti := t.ti
|
2015-10-07 00:56:18 -07:00
|
|
|
t.cells.Resize(0, 0)
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.ShowCursor)
|
|
|
|
t.TPuts(ti.AttrOff)
|
|
|
|
t.TPuts(ti.Clear)
|
|
|
|
t.TPuts(ti.ExitCA)
|
|
|
|
t.TPuts(ti.ExitKeypad)
|
2015-10-04 12:30:50 -07:00
|
|
|
t.TPuts(ti.TParm(ti.MouseMode, 0))
|
2015-10-07 00:56:18 -07:00
|
|
|
t.curstyle = Style(-1)
|
|
|
|
t.clear = false
|
|
|
|
t.fini = true
|
|
|
|
|
2018-03-18 01:44:55 -06:00
|
|
|
select {
|
|
|
|
case <-t.quit:
|
|
|
|
// do nothing, already closed
|
|
|
|
|
|
|
|
default:
|
2015-10-06 00:14:18 -07:00
|
|
|
close(t.quit)
|
|
|
|
}
|
2018-06-17 00:44:25 +02:00
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
t.termioFini()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) SetStyle(style Style) {
|
|
|
|
t.Lock()
|
2015-10-06 00:14:18 -07:00
|
|
|
if !t.fini {
|
|
|
|
t.style = style
|
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
t.Unlock()
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) Clear() {
|
2016-05-23 14:55:11 -07:00
|
|
|
t.Fill(' ', t.style)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) Fill(r rune, style Style) {
|
2015-09-26 23:37:54 -07:00
|
|
|
t.Lock()
|
2015-10-06 00:14:18 -07:00
|
|
|
if !t.fini {
|
2016-05-23 14:55:11 -07:00
|
|
|
t.cells.Fill(r, style)
|
2015-10-06 00:14:18 -07:00
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
t.Unlock()
|
|
|
|
}
|
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
func (t *tScreen) SetContent(x, y int, mainc rune, combc []rune, style Style) {
|
2015-09-26 23:37:54 -07:00
|
|
|
t.Lock()
|
2015-10-07 00:56:18 -07:00
|
|
|
if !t.fini {
|
|
|
|
t.cells.SetContent(x, y, mainc, combc, style)
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
t.Unlock()
|
|
|
|
}
|
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
func (t *tScreen) GetContent(x, y int) (rune, []rune, Style, int) {
|
2015-10-02 21:49:13 -07:00
|
|
|
t.Lock()
|
2015-10-07 00:56:18 -07:00
|
|
|
mainc, combc, style, width := t.cells.GetContent(x, y)
|
2015-10-02 21:49:13 -07:00
|
|
|
t.Unlock()
|
2015-10-07 00:56:18 -07:00
|
|
|
return mainc, combc, style, width
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
func (t *tScreen) SetCell(x, y int, style Style, ch ...rune) {
|
|
|
|
if len(ch) > 0 {
|
|
|
|
t.SetContent(x, y, ch[0], ch[1:], style)
|
|
|
|
} else {
|
|
|
|
t.SetContent(x, y, ' ', nil, style)
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-05 14:30:36 -07:00
|
|
|
func (t *tScreen) encodeRune(r rune, buf []byte) []byte {
|
|
|
|
|
|
|
|
nb := make([]byte, 6)
|
|
|
|
ob := make([]byte, 6)
|
|
|
|
num := utf8.EncodeRune(ob, r)
|
|
|
|
ob = ob[:num]
|
2015-10-14 19:09:00 -07:00
|
|
|
dst := 0
|
|
|
|
var err error
|
|
|
|
if enc := t.encoder; enc != nil {
|
|
|
|
enc.Reset()
|
|
|
|
dst, _, err = enc.Transform(nb, ob, true)
|
|
|
|
}
|
|
|
|
if err != nil || dst == 0 || nb[0] == '\x1a' {
|
|
|
|
// Combining characters are elided
|
|
|
|
if len(buf) == 0 {
|
|
|
|
if acs, ok := t.acs[r]; ok {
|
|
|
|
buf = append(buf, []byte(acs)...)
|
|
|
|
} else if fb, ok := t.fallback[r]; ok {
|
|
|
|
buf = append(buf, []byte(fb)...)
|
|
|
|
} else {
|
|
|
|
buf = append(buf, '?')
|
2015-10-05 14:30:36 -07:00
|
|
|
}
|
|
|
|
}
|
2015-10-14 19:09:00 -07:00
|
|
|
} else {
|
|
|
|
buf = append(buf, nb[:dst]...)
|
2015-10-05 14:30:36 -07:00
|
|
|
}
|
2015-10-14 19:09:00 -07:00
|
|
|
|
2015-10-05 14:30:36 -07:00
|
|
|
return buf
|
|
|
|
}
|
|
|
|
|
2016-05-19 11:35:33 -07:00
|
|
|
func (t *tScreen) sendFgBg(fg Color, bg Color) {
|
2015-11-01 14:44:23 -08:00
|
|
|
ti := t.ti
|
2016-05-19 11:35:33 -07:00
|
|
|
if ti.Colors == 0 {
|
2015-11-01 14:44:23 -08:00
|
|
|
return
|
2016-05-19 11:35:33 -07:00
|
|
|
}
|
|
|
|
if t.truecolor {
|
|
|
|
if ti.SetFgBgRGB != "" &&
|
2016-05-19 16:45:31 -07:00
|
|
|
fg != ColorDefault && bg != ColorDefault {
|
2016-05-19 11:35:33 -07:00
|
|
|
r1, g1, b1 := fg.RGB()
|
|
|
|
r2, g2, b2 := bg.RGB()
|
|
|
|
t.TPuts(ti.TParm(ti.SetFgBgRGB,
|
|
|
|
int(r1), int(g1), int(b1),
|
|
|
|
int(r2), int(g2), int(b2)))
|
|
|
|
} else {
|
|
|
|
if fg != ColorDefault && ti.SetFgRGB != "" {
|
|
|
|
r, g, b := fg.RGB()
|
|
|
|
t.TPuts(ti.TParm(ti.SetFgRGB,
|
|
|
|
int(r), int(g), int(b)))
|
|
|
|
}
|
|
|
|
if bg != ColorDefault && ti.SetBgRGB != "" {
|
2016-11-25 16:26:21 +09:00
|
|
|
r, g, b := bg.RGB()
|
2016-05-19 11:35:33 -07:00
|
|
|
t.TPuts(ti.TParm(ti.SetBgRGB,
|
|
|
|
int(r), int(g), int(b)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if fg != ColorDefault {
|
2015-11-01 14:44:23 -08:00
|
|
|
if v, ok := t.colors[fg]; ok {
|
|
|
|
fg = v
|
2016-05-19 11:35:33 -07:00
|
|
|
} else {
|
|
|
|
v = FindColor(fg, t.palette)
|
2015-11-01 14:44:23 -08:00
|
|
|
t.colors[fg] = v
|
|
|
|
fg = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-19 11:35:33 -07:00
|
|
|
if bg != ColorDefault {
|
2015-11-01 14:44:23 -08:00
|
|
|
if v, ok := t.colors[bg]; ok {
|
|
|
|
bg = v
|
2016-05-19 11:35:33 -07:00
|
|
|
} else {
|
|
|
|
v = FindColor(bg, t.palette)
|
2015-11-01 14:44:23 -08:00
|
|
|
t.colors[bg] = v
|
|
|
|
bg = v
|
|
|
|
}
|
2016-05-19 11:35:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if ti.SetFgBg != "" && fg != ColorDefault && bg != ColorDefault {
|
|
|
|
t.TPuts(ti.TParm(ti.SetFgBg, int(fg), int(bg)))
|
|
|
|
} else {
|
|
|
|
if fg != ColorDefault && ti.SetFg != "" {
|
|
|
|
t.TPuts(ti.TParm(ti.SetFg, int(fg)))
|
|
|
|
}
|
|
|
|
if bg != ColorDefault && ti.SetBg != "" {
|
|
|
|
t.TPuts(ti.TParm(ti.SetBg, int(bg)))
|
|
|
|
}
|
2015-11-01 14:44:23 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
func (t *tScreen) drawCell(x, y int) int {
|
2015-10-02 21:49:13 -07:00
|
|
|
|
2015-09-26 23:37:54 -07:00
|
|
|
ti := t.ti
|
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
mainc, combc, style, width := t.cells.GetContent(x, y)
|
|
|
|
if !t.cells.Dirty(x, y) {
|
|
|
|
return width
|
|
|
|
}
|
|
|
|
|
2015-09-26 23:37:54 -07:00
|
|
|
if t.cy != y || t.cx != x {
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.TGoto(x, y))
|
2015-10-07 00:56:18 -07:00
|
|
|
t.cx = x
|
|
|
|
t.cy = y
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
2015-10-07 00:56:18 -07:00
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
if style == StyleDefault {
|
|
|
|
style = t.style
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
if style != t.curstyle {
|
|
|
|
fg, bg, attrs := style.Decompose()
|
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.AttrOff)
|
2015-10-27 16:40:24 -07:00
|
|
|
|
2016-05-19 11:35:33 -07:00
|
|
|
t.sendFgBg(fg, bg)
|
2015-09-28 22:28:41 -07:00
|
|
|
if attrs&AttrBold != 0 {
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.Bold)
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-09-28 22:28:41 -07:00
|
|
|
if attrs&AttrUnderline != 0 {
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.Underline)
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-09-28 22:28:41 -07:00
|
|
|
if attrs&AttrReverse != 0 {
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.Reverse)
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-09-28 22:28:41 -07:00
|
|
|
if attrs&AttrBlink != 0 {
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.Blink)
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-09-28 22:28:41 -07:00
|
|
|
if attrs&AttrDim != 0 {
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(ti.Dim)
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
t.curstyle = style
|
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
// now emit runes - taking care to not overrun width with a
|
2015-09-26 23:37:54 -07:00
|
|
|
// wide character, and to ensure that we emit exactly one regular
|
|
|
|
// character followed up by any residual combing characters
|
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
if width < 1 {
|
|
|
|
width = 1
|
|
|
|
}
|
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
var str string
|
2015-10-04 19:53:38 -07:00
|
|
|
|
2015-10-07 20:15:33 -07:00
|
|
|
buf := make([]byte, 0, 6)
|
2015-10-05 14:30:36 -07:00
|
|
|
|
2015-10-07 20:15:33 -07:00
|
|
|
buf = t.encodeRune(mainc, buf)
|
|
|
|
for _, r := range combc {
|
|
|
|
buf = t.encodeRune(r, buf)
|
|
|
|
}
|
2015-10-07 00:56:18 -07:00
|
|
|
|
2015-10-07 20:15:33 -07:00
|
|
|
str = string(buf)
|
|
|
|
if width > 1 && str == "?" {
|
|
|
|
// No FullWidth character support
|
|
|
|
str = "? "
|
|
|
|
t.cx = -1
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
2015-10-04 19:53:38 -07:00
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
// XXX: check for hazeltine not being able to display ~
|
|
|
|
|
|
|
|
if x > t.w-width {
|
|
|
|
// too wide to fit; emit a single space instead
|
2015-10-05 14:30:36 -07:00
|
|
|
width = 1
|
|
|
|
str = " "
|
|
|
|
}
|
2019-03-19 03:31:05 -04:00
|
|
|
t.writeString(str)
|
2015-10-07 00:56:18 -07:00
|
|
|
t.cx += width
|
|
|
|
t.cells.SetDirty(x, y, false)
|
2015-10-07 20:15:33 -07:00
|
|
|
if width > 1 {
|
|
|
|
t.cx = -1
|
|
|
|
}
|
2015-10-07 00:56:18 -07:00
|
|
|
|
|
|
|
return width
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) ShowCursor(x, y int) {
|
|
|
|
t.Lock()
|
2015-10-07 00:56:18 -07:00
|
|
|
t.cursorx = x
|
|
|
|
t.cursory = y
|
2015-10-02 21:49:13 -07:00
|
|
|
t.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) HideCursor() {
|
|
|
|
t.ShowCursor(-1, -1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) showCursor() {
|
|
|
|
|
|
|
|
x, y := t.cursorx, t.cursory
|
2015-10-07 00:56:18 -07:00
|
|
|
w, h := t.cells.Size()
|
|
|
|
if x < 0 || y < 0 || x >= w || y >= h {
|
|
|
|
t.hideCursor()
|
2015-09-26 23:37:54 -07:00
|
|
|
return
|
|
|
|
}
|
2016-05-18 19:57:23 -07:00
|
|
|
t.TPuts(t.ti.TGoto(x, y))
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(t.ti.ShowCursor)
|
2015-09-26 23:37:54 -07:00
|
|
|
t.cx = x
|
|
|
|
t.cy = y
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
|
2019-03-19 03:31:05 -04:00
|
|
|
// writeString sends a string to the terminal. The string is sent as-is and
|
|
|
|
// this function does not expand inline padding indications (of the form
|
|
|
|
// $<[delay]> where [delay] is msec). In order to have these expanded, use
|
|
|
|
// TPuts. If the screen is "buffering", the string is collected in a buffer,
|
|
|
|
// with the intention that the entire buffer be sent to the terminal in one
|
|
|
|
// write operation at some point later.
|
|
|
|
func (t *tScreen) writeString(s string) {
|
|
|
|
if t.buffering {
|
|
|
|
io.WriteString(&t.buf, s)
|
|
|
|
} else {
|
|
|
|
io.WriteString(t.out, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
func (t *tScreen) TPuts(s string) {
|
2019-03-19 03:31:05 -04:00
|
|
|
if t.buffering {
|
2019-07-23 18:54:20 -07:00
|
|
|
t.ti.TPuts(&t.buf, s)
|
2019-03-19 03:31:05 -04:00
|
|
|
} else {
|
2019-07-23 18:54:20 -07:00
|
|
|
t.ti.TPuts(t.out, s)
|
2019-03-19 03:31:05 -04:00
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) Show() {
|
|
|
|
t.Lock()
|
2015-10-06 00:14:18 -07:00
|
|
|
if !t.fini {
|
|
|
|
t.resize()
|
|
|
|
t.draw()
|
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
t.Unlock()
|
|
|
|
}
|
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
func (t *tScreen) clearScreen() {
|
2015-11-01 14:44:23 -08:00
|
|
|
fg, bg, _ := t.style.Decompose()
|
2016-05-19 11:35:33 -07:00
|
|
|
t.sendFgBg(fg, bg)
|
2015-10-02 21:49:13 -07:00
|
|
|
t.TPuts(t.ti.Clear)
|
|
|
|
t.clear = false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) hideCursor() {
|
|
|
|
// does not update cursor position
|
2015-10-07 00:56:18 -07:00
|
|
|
if t.ti.HideCursor != "" {
|
|
|
|
t.TPuts(t.ti.HideCursor)
|
|
|
|
} else {
|
|
|
|
// No way to hide cursor, stick it
|
|
|
|
// at bottom right of screen
|
|
|
|
t.cx, t.cy = t.cells.Size()
|
|
|
|
t.TPuts(t.ti.TGoto(t.cx, t.cy))
|
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) draw() {
|
|
|
|
// clobber cursor position, because we're gonna change it all
|
|
|
|
t.cx = -1
|
|
|
|
t.cy = -1
|
|
|
|
|
2019-03-19 03:31:05 -04:00
|
|
|
t.buf.Reset()
|
|
|
|
t.buffering = true
|
|
|
|
defer func() {
|
|
|
|
t.buffering = false
|
|
|
|
}()
|
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
// hide the cursor while we move stuff around
|
|
|
|
t.hideCursor()
|
|
|
|
|
|
|
|
if t.clear {
|
|
|
|
t.clearScreen()
|
|
|
|
}
|
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
for y := 0; y < t.h; y++ {
|
|
|
|
for x := 0; x < t.w; x++ {
|
|
|
|
width := t.drawCell(x, y)
|
2015-10-07 20:15:33 -07:00
|
|
|
if width > 1 {
|
|
|
|
if x+1 < t.w {
|
|
|
|
// this is necessary so that if we ever
|
|
|
|
// go back to drawing that cell, we
|
|
|
|
// actually will *draw* it.
|
|
|
|
t.cells.SetDirty(x+1, y, true)
|
|
|
|
}
|
|
|
|
}
|
2015-10-07 00:56:18 -07:00
|
|
|
x += width - 1
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// restore the cursor
|
|
|
|
t.showCursor()
|
2019-03-19 03:31:05 -04:00
|
|
|
|
|
|
|
t.buf.WriteTo(t.out)
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) EnableMouse() {
|
|
|
|
if len(t.mouse) != 0 {
|
2015-10-04 12:30:50 -07:00
|
|
|
t.TPuts(t.ti.TParm(t.ti.MouseMode, 1))
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) DisableMouse() {
|
|
|
|
if len(t.mouse) != 0 {
|
2015-10-04 12:30:50 -07:00
|
|
|
t.TPuts(t.ti.TParm(t.ti.MouseMode, 0))
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) Size() (int, int) {
|
|
|
|
t.Lock()
|
|
|
|
w, h := t.w, t.h
|
|
|
|
t.Unlock()
|
|
|
|
return w, h
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) resize() {
|
|
|
|
if w, h, e := t.getWinSize(); e == nil {
|
|
|
|
if w != t.w || h != t.h {
|
|
|
|
t.cx = -1
|
|
|
|
t.cy = -1
|
2015-10-02 21:49:13 -07:00
|
|
|
|
2015-10-07 00:56:18 -07:00
|
|
|
t.cells.Resize(w, h)
|
|
|
|
t.cells.Invalidate()
|
2015-10-02 21:49:13 -07:00
|
|
|
t.h = h
|
2015-10-07 00:56:18 -07:00
|
|
|
t.w = w
|
|
|
|
ev := NewEventResize(w, h)
|
|
|
|
t.PostEvent(ev)
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) Colors() int {
|
|
|
|
// this doesn't change, no need for lock
|
2015-10-27 23:28:26 -07:00
|
|
|
if t.truecolor {
|
|
|
|
return 1 << 24
|
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
return t.ti.Colors
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) PollEvent() Event {
|
|
|
|
select {
|
|
|
|
case <-t.quit:
|
|
|
|
return nil
|
|
|
|
case ev := <-t.evch:
|
|
|
|
return ev
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 19:09:00 -07:00
|
|
|
// vtACSNames is a map of bytes defined by terminfo that are used in
|
|
|
|
// the terminals Alternate Character Set to represent other glyphs.
|
|
|
|
// For example, the upper left corner of the box drawing set can be
|
|
|
|
// displayed by printing "l" while in the alternate character set.
|
|
|
|
// Its not quite that simple, since the "l" is the terminfo name,
|
|
|
|
// and it may be necessary to use a different character based on
|
|
|
|
// the terminal implementation (or the terminal may lack support for
|
|
|
|
// this altogether). See buildAcsMap below for detail.
|
|
|
|
var vtACSNames = map[byte]rune{
|
|
|
|
'+': RuneRArrow,
|
|
|
|
',': RuneLArrow,
|
|
|
|
'-': RuneUArrow,
|
|
|
|
'.': RuneDArrow,
|
|
|
|
'0': RuneBlock,
|
|
|
|
'`': RuneDiamond,
|
|
|
|
'a': RuneCkBoard,
|
|
|
|
'b': '␉', // VT100, Not defined by terminfo
|
|
|
|
'c': '␌', // VT100, Not defined by terminfo
|
|
|
|
'd': '␋', // VT100, Not defined by terminfo
|
|
|
|
'e': '␊', // VT100, Not defined by terminfo
|
|
|
|
'f': RuneDegree,
|
|
|
|
'g': RunePlMinus,
|
|
|
|
'h': RuneBoard,
|
|
|
|
'i': RuneLantern,
|
|
|
|
'j': RuneLRCorner,
|
|
|
|
'k': RuneURCorner,
|
|
|
|
'l': RuneULCorner,
|
|
|
|
'm': RuneLLCorner,
|
|
|
|
'n': RunePlus,
|
|
|
|
'o': RuneS1,
|
|
|
|
'p': RuneS3,
|
|
|
|
'q': RuneHLine,
|
|
|
|
'r': RuneS7,
|
|
|
|
's': RuneS9,
|
|
|
|
't': RuneLTee,
|
|
|
|
'u': RuneRTee,
|
|
|
|
'v': RuneBTee,
|
|
|
|
'w': RuneTTee,
|
|
|
|
'x': RuneVLine,
|
|
|
|
'y': RuneLEqual,
|
|
|
|
'z': RuneGEqual,
|
|
|
|
'{': RunePi,
|
|
|
|
'|': RuneNEqual,
|
|
|
|
'}': RuneSterling,
|
|
|
|
'~': RuneBullet,
|
|
|
|
}
|
|
|
|
|
|
|
|
// buildAcsMap builds a map of characters that we translate from Unicode to
|
2015-10-04 19:53:38 -07:00
|
|
|
// alternate character encodings. To do this, we use the standard VT100 ACS
|
|
|
|
// maps. This is only done if the terminal lacks support for Unicode; we
|
|
|
|
// always prefer to emit Unicode glyphs when we are able.
|
|
|
|
func (t *tScreen) buildAcsMap() {
|
|
|
|
acsstr := t.ti.AltChars
|
|
|
|
t.acs = make(map[rune]string)
|
|
|
|
for len(acsstr) > 2 {
|
2015-10-14 19:09:00 -07:00
|
|
|
srcv := acsstr[0]
|
2015-10-04 19:53:38 -07:00
|
|
|
dstv := string(acsstr[1])
|
2015-10-14 19:09:00 -07:00
|
|
|
if r, ok := vtACSNames[srcv]; ok {
|
|
|
|
t.acs[r] = t.ti.EnterAcs + dstv + t.ti.ExitAcs
|
2015-10-04 19:53:38 -07:00
|
|
|
}
|
|
|
|
acsstr = acsstr[2:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-30 09:33:45 -07:00
|
|
|
func (t *tScreen) PostEventWait(ev Event) {
|
|
|
|
t.evch <- ev
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) PostEvent(ev Event) error {
|
2015-10-05 01:27:55 -07:00
|
|
|
select {
|
|
|
|
case t.evch <- ev:
|
2015-10-30 09:33:45 -07:00
|
|
|
return nil
|
2015-10-05 01:27:55 -07:00
|
|
|
default:
|
2015-10-30 09:33:45 -07:00
|
|
|
return ErrEventQFull
|
2015-10-05 01:27:55 -07:00
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
|
2015-11-04 17:05:24 -08:00
|
|
|
func (t *tScreen) clip(x, y int) (int, int) {
|
|
|
|
w, h := t.cells.Size()
|
|
|
|
if x < 0 {
|
|
|
|
x = 0
|
|
|
|
}
|
|
|
|
if y < 0 {
|
|
|
|
y = 0
|
|
|
|
}
|
|
|
|
if x > w-1 {
|
|
|
|
x = w - 1
|
|
|
|
}
|
|
|
|
if y > h-1 {
|
|
|
|
y = h - 1
|
|
|
|
}
|
|
|
|
return x, y
|
|
|
|
}
|
|
|
|
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
// buildMouseEvent returns an event based on the supplied coordinates and button
|
|
|
|
// state. Note that the screen's mouse button state is updated based on the
|
|
|
|
// input to this function (i.e. it mutates the receiver).
|
|
|
|
func (t *tScreen) buildMouseEvent(x, y, btn int) *EventMouse {
|
2015-10-02 21:49:13 -07:00
|
|
|
|
|
|
|
// XTerm mouse events only report at most one button at a time,
|
|
|
|
// which may include a wheel button. Wheel motion events are
|
|
|
|
// reported as single impulses, while other button events are reported
|
|
|
|
// as separate press & release events.
|
|
|
|
|
|
|
|
button := ButtonNone
|
|
|
|
mod := ModNone
|
|
|
|
|
|
|
|
// Mouse wheel has bit 6 set, no release events. It should be noted
|
|
|
|
// that wheel events are sometimes misdelivered as mouse button events
|
|
|
|
// during a click-drag, so we debounce these, considering them to be
|
|
|
|
// button press events unless we see an intervening release event.
|
|
|
|
switch btn & 0x43 {
|
|
|
|
case 0:
|
|
|
|
button = Button1
|
|
|
|
t.wasbtn = true
|
|
|
|
case 1:
|
|
|
|
button = Button2
|
|
|
|
t.wasbtn = true
|
|
|
|
case 2:
|
|
|
|
button = Button3
|
|
|
|
t.wasbtn = true
|
|
|
|
case 3:
|
|
|
|
button = ButtonNone
|
|
|
|
t.wasbtn = false
|
|
|
|
case 0x40:
|
|
|
|
if !t.wasbtn {
|
|
|
|
button = WheelUp
|
|
|
|
} else {
|
|
|
|
button = Button1
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
case 0x41:
|
|
|
|
if !t.wasbtn {
|
|
|
|
button = WheelDown
|
|
|
|
} else {
|
|
|
|
button = Button2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-03 11:29:55 -07:00
|
|
|
if btn&0x4 != 0 {
|
2015-10-02 21:49:13 -07:00
|
|
|
mod |= ModShift
|
|
|
|
}
|
2015-10-03 11:29:55 -07:00
|
|
|
if btn&0x8 != 0 {
|
2016-05-19 16:45:31 -07:00
|
|
|
mod |= ModAlt
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
2015-10-03 11:29:55 -07:00
|
|
|
if btn&0x10 != 0 {
|
2015-10-02 21:49:13 -07:00
|
|
|
mod |= ModCtrl
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some terminals will report mouse coordinates outside the
|
|
|
|
// screen, especially with click-drag events. Clip the coordinates
|
|
|
|
// to the screen in that case.
|
2015-11-04 17:05:24 -08:00
|
|
|
x, y = t.clip(x, y)
|
|
|
|
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
return NewEventMouse(x, y, button, mod)
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// parseSgrMouse attempts to locate an SGR mouse record at the start of the
|
|
|
|
// buffer. It returns true, true if it found one, and the associated bytes
|
|
|
|
// be removed from the buffer. It returns true, false if the buffer might
|
|
|
|
// contain such an event, but more bytes are necessary (partial match), and
|
|
|
|
// false, false if the content is definitely *not* an SGR mouse record.
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
|
2015-10-02 21:49:13 -07:00
|
|
|
|
|
|
|
b := buf.Bytes()
|
|
|
|
|
|
|
|
var x, y, btn, state int
|
|
|
|
dig := false
|
|
|
|
neg := false
|
2016-04-29 06:08:51 -07:00
|
|
|
motion := false
|
2015-10-02 21:49:13 -07:00
|
|
|
i := 0
|
|
|
|
val := 0
|
|
|
|
|
|
|
|
for i = range b {
|
|
|
|
switch b[i] {
|
|
|
|
case '\x1b':
|
|
|
|
if state != 0 {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
state = 1
|
|
|
|
|
|
|
|
case '\x9b':
|
|
|
|
if state != 0 {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
state = 2
|
|
|
|
|
|
|
|
case '[':
|
|
|
|
if state != 1 {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
state = 2
|
|
|
|
|
|
|
|
case '<':
|
|
|
|
if state != 2 {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
val = 0
|
|
|
|
dig = false
|
|
|
|
neg = false
|
|
|
|
state = 3
|
|
|
|
|
|
|
|
case '-':
|
2015-10-07 20:15:33 -07:00
|
|
|
if state != 3 && state != 4 && state != 5 {
|
2015-10-02 21:49:13 -07:00
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
if dig || neg {
|
|
|
|
return false, false
|
|
|
|
}
|
2015-10-03 11:29:55 -07:00
|
|
|
neg = true // stay in state
|
2015-10-02 21:49:13 -07:00
|
|
|
|
|
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
|
|
if state != 3 && state != 4 && state != 5 {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
val *= 10
|
2015-10-03 11:29:55 -07:00
|
|
|
val += int(b[i] - '0')
|
|
|
|
dig = true // stay in state
|
2015-10-02 21:49:13 -07:00
|
|
|
|
|
|
|
case ';':
|
|
|
|
if neg {
|
|
|
|
val = -val
|
|
|
|
}
|
|
|
|
switch state {
|
|
|
|
case 3:
|
|
|
|
btn, val = val, 0
|
|
|
|
neg, dig, state = false, false, 4
|
|
|
|
case 4:
|
2016-04-18 17:32:02 -04:00
|
|
|
x, val = val-1, 0
|
2015-10-02 21:49:13 -07:00
|
|
|
neg, dig, state = false, false, 5
|
|
|
|
default:
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'm', 'M':
|
|
|
|
if state != 5 {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
if neg {
|
|
|
|
val = -val
|
|
|
|
}
|
2016-04-18 17:32:02 -04:00
|
|
|
y = val - 1
|
2015-10-02 21:49:13 -07:00
|
|
|
|
2016-04-29 06:08:51 -07:00
|
|
|
motion = (btn & 32) != 0
|
2015-10-02 21:49:13 -07:00
|
|
|
btn &^= 32
|
|
|
|
if b[i] == 'm' {
|
|
|
|
// mouse release, clear all buttons
|
|
|
|
btn |= 3
|
|
|
|
btn &^= 0x40
|
2016-04-29 06:08:51 -07:00
|
|
|
t.buttondn = false
|
|
|
|
} else if motion {
|
|
|
|
/*
|
|
|
|
* Some broken terminals appear to send
|
|
|
|
* mouse button one motion events, instead of
|
|
|
|
* encoding 35 (no buttons) into these events.
|
|
|
|
* We resolve these by looking for a non-motion
|
|
|
|
* event first.
|
|
|
|
*/
|
|
|
|
if !t.buttondn {
|
|
|
|
btn |= 3
|
|
|
|
btn &^= 0x40
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.buttondn = true
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
// consume the event bytes
|
|
|
|
for i >= 0 {
|
|
|
|
buf.ReadByte()
|
|
|
|
i--
|
|
|
|
}
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
*evs = append(*evs, t.buildMouseEvent(x, y, btn))
|
2015-10-02 21:49:13 -07:00
|
|
|
return true, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// incomplete & inconclusve at this point
|
|
|
|
return true, false
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseXtermMouse is like parseSgrMouse, but it parses a legacy
|
|
|
|
// X11 mouse record.
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
func (t *tScreen) parseXtermMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
|
2015-10-02 21:49:13 -07:00
|
|
|
|
|
|
|
b := buf.Bytes()
|
|
|
|
|
|
|
|
state := 0
|
|
|
|
btn := 0
|
|
|
|
x := 0
|
2015-10-03 11:29:55 -07:00
|
|
|
y := 0
|
2015-10-02 21:49:13 -07:00
|
|
|
|
|
|
|
for i := range b {
|
|
|
|
switch state {
|
|
|
|
case 0:
|
|
|
|
switch b[i] {
|
|
|
|
case '\x1b':
|
|
|
|
state = 1
|
|
|
|
case '\x9b':
|
|
|
|
state = 2
|
|
|
|
default:
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
if b[i] != '[' {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
state = 2
|
|
|
|
case 2:
|
|
|
|
if b[i] != 'M' {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
state++
|
|
|
|
case 3:
|
|
|
|
btn = int(b[i])
|
|
|
|
state++
|
|
|
|
case 4:
|
|
|
|
x = int(b[i]) - 32 - 1
|
|
|
|
state++
|
|
|
|
case 5:
|
|
|
|
y = int(b[i]) - 32 - 1
|
|
|
|
for i >= 0 {
|
|
|
|
buf.ReadByte()
|
|
|
|
i--
|
|
|
|
}
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
*evs = append(*evs, t.buildMouseEvent(x, y, btn))
|
2015-10-02 21:49:13 -07:00
|
|
|
return true, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true, false
|
|
|
|
}
|
|
|
|
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
func (t *tScreen) parseFunctionKey(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
|
2015-10-02 21:49:13 -07:00
|
|
|
b := buf.Bytes()
|
|
|
|
partial := false
|
2016-03-23 13:14:49 -07:00
|
|
|
for e, k := range t.keycodes {
|
|
|
|
esc := []byte(e)
|
2016-04-28 00:44:30 -07:00
|
|
|
if (len(esc) == 1) && (esc[0] == '\x1b') {
|
|
|
|
continue
|
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
if bytes.HasPrefix(b, esc) {
|
|
|
|
// matched
|
|
|
|
var r rune
|
|
|
|
if len(esc) == 1 {
|
|
|
|
r = rune(b[0])
|
|
|
|
}
|
2016-04-28 12:37:50 -07:00
|
|
|
mod := k.mod
|
|
|
|
if t.escaped {
|
|
|
|
mod |= ModAlt
|
|
|
|
t.escaped = false
|
|
|
|
}
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
*evs = append(*evs, NewEventKey(k.key, r, mod))
|
2015-10-02 21:49:13 -07:00
|
|
|
for i := 0; i < len(esc); i++ {
|
|
|
|
buf.ReadByte()
|
|
|
|
}
|
|
|
|
return true, true
|
|
|
|
}
|
|
|
|
if bytes.HasPrefix(esc, b) {
|
|
|
|
partial = true
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
return partial, false
|
|
|
|
}
|
|
|
|
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
func (t *tScreen) parseRune(buf *bytes.Buffer, evs *[]Event) (bool, bool) {
|
2015-10-02 21:49:13 -07:00
|
|
|
b := buf.Bytes()
|
|
|
|
if b[0] >= ' ' && b[0] <= 0x7F {
|
|
|
|
// printable ASCII easy to deal with -- no encodings
|
2016-04-28 00:44:30 -07:00
|
|
|
mod := ModNone
|
|
|
|
if t.escaped {
|
|
|
|
mod = ModAlt
|
|
|
|
t.escaped = false
|
|
|
|
}
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
*evs = append(*evs, NewEventKey(KeyRune, rune(b[0]), mod))
|
2015-10-05 14:30:36 -07:00
|
|
|
buf.ReadByte()
|
2015-10-02 21:49:13 -07:00
|
|
|
return true, true
|
|
|
|
}
|
2015-10-05 14:30:36 -07:00
|
|
|
|
|
|
|
if b[0] < 0x80 {
|
2015-10-07 20:15:33 -07:00
|
|
|
// Low numbered values are control keys, not runes.
|
2015-10-05 14:30:36 -07:00
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
|
2015-10-07 20:15:33 -07:00
|
|
|
utfb := make([]byte, 12)
|
|
|
|
for l := 1; l <= len(b); l++ {
|
|
|
|
t.decoder.Reset()
|
|
|
|
nout, nin, e := t.decoder.Transform(utfb, b[:l], true)
|
|
|
|
if e == transform.ErrShortSrc {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if nout != 0 {
|
|
|
|
r, _ := utf8.DecodeRune(utfb[:nout])
|
|
|
|
if r != utf8.RuneError {
|
2016-04-28 00:44:30 -07:00
|
|
|
mod := ModNone
|
|
|
|
if t.escaped {
|
|
|
|
mod = ModAlt
|
|
|
|
t.escaped = false
|
|
|
|
}
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
*evs = append(*evs, NewEventKey(KeyRune, r, mod))
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-10-07 20:15:33 -07:00
|
|
|
for nin > 0 {
|
|
|
|
buf.ReadByte()
|
|
|
|
nin--
|
2015-10-04 23:22:57 -07:00
|
|
|
}
|
2015-10-07 20:15:33 -07:00
|
|
|
return true, true
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
2015-10-05 14:30:36 -07:00
|
|
|
// Looks like potential escape
|
|
|
|
return true, false
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) scanInput(buf *bytes.Buffer, expire bool) {
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
evs := t.collectEventsFromInput(buf, expire)
|
|
|
|
|
|
|
|
for _, ev := range evs {
|
|
|
|
t.PostEventWait(ev)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an array of Events extracted from the supplied buffer. This is done
|
|
|
|
// while holding the screen's lock - the events can then be queued for
|
|
|
|
// application processing with the lock released.
|
|
|
|
func (t *tScreen) collectEventsFromInput(buf *bytes.Buffer, expire bool) []Event {
|
|
|
|
|
|
|
|
res := make([]Event, 0, 20)
|
2015-10-02 21:49:13 -07:00
|
|
|
|
2015-12-15 12:55:04 -08:00
|
|
|
t.Lock()
|
|
|
|
defer t.Unlock()
|
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
for {
|
|
|
|
b := buf.Bytes()
|
|
|
|
if len(b) == 0 {
|
|
|
|
buf.Reset()
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
return res
|
2015-10-02 21:49:13 -07:00
|
|
|
}
|
|
|
|
|
2015-09-26 23:37:54 -07:00
|
|
|
partials := 0
|
2015-10-02 21:49:13 -07:00
|
|
|
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
if part, comp := t.parseRune(buf, &res); comp {
|
2015-10-02 21:49:13 -07:00
|
|
|
continue
|
|
|
|
} else if part {
|
|
|
|
partials++
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
if part, comp := t.parseFunctionKey(buf, &res); comp {
|
2015-10-02 21:49:13 -07:00
|
|
|
continue
|
|
|
|
} else if part {
|
|
|
|
partials++
|
|
|
|
}
|
2015-09-28 22:28:41 -07:00
|
|
|
|
2015-10-04 12:30:50 -07:00
|
|
|
// Only parse mouse records if this term claims to have
|
|
|
|
// mouse support
|
2015-09-26 23:37:54 -07:00
|
|
|
|
2015-10-04 12:30:50 -07:00
|
|
|
if t.ti.Mouse != "" {
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
if part, comp := t.parseXtermMouse(buf, &res); comp {
|
2015-10-04 12:30:50 -07:00
|
|
|
continue
|
|
|
|
} else if part {
|
|
|
|
partials++
|
|
|
|
}
|
|
|
|
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
if part, comp := t.parseSgrMouse(buf, &res); comp {
|
2015-10-04 12:30:50 -07:00
|
|
|
continue
|
|
|
|
} else if part {
|
|
|
|
partials++
|
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
|
|
|
|
if partials == 0 || expire {
|
2016-04-28 00:44:30 -07:00
|
|
|
if b[0] == '\x1b' {
|
|
|
|
if len(b) == 1 {
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
res = append(res, NewEventKey(KeyEsc, 0, ModNone))
|
2016-04-28 00:44:30 -07:00
|
|
|
t.escaped = false
|
|
|
|
} else {
|
|
|
|
t.escaped = true
|
|
|
|
}
|
|
|
|
buf.ReadByte()
|
|
|
|
continue
|
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
// Nothing was going to match, or we timed out
|
|
|
|
// waiting for more data -- just deliver the characters
|
2015-10-07 20:15:33 -07:00
|
|
|
// to the app & let them sort it out. Possibly we
|
|
|
|
// should only do this for control characters like ESC.
|
2015-10-02 21:49:13 -07:00
|
|
|
by, _ := buf.ReadByte()
|
2016-04-28 00:44:30 -07:00
|
|
|
mod := ModNone
|
|
|
|
if t.escaped {
|
|
|
|
t.escaped = false
|
|
|
|
mod = ModAlt
|
|
|
|
}
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
res = append(res, NewEventKey(KeyRune, rune(by), mod))
|
2015-10-02 21:49:13 -07:00
|
|
|
continue
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
|
2015-10-02 21:49:13 -07:00
|
|
|
// well we have some partial data, wait until we get
|
|
|
|
// some more
|
|
|
|
break
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
Another attempt to fix limited length of paste.
I humbly submit this patch as another attempt to address the issue that tcell
will only paste up to 11 characters at a time. The problem is caused by the
fact that events (key, mouse, etc) constructed from the stream of raw input
characters are sent to the consuming application over a length-10 channel via
screen.PostEvent() which, if the channel is full, will drop what can't be sent
immediately. If the input stream grows rapidly e.g. because the user pasted a
large section of text into the running tcell application, then more than 10
events will likely be built from the chunk of input read by scanInput().
A blocking channel send is not used (i.e. PostEventWait() instead of
PostEvent()) because the channel send is issued from a call stack in which the
screen struct's lock is held. If the receiving application is not consuming
events, then callers to other screen APIs will block waiting for the screen's
lock. If the receiving application needs to call another screen API before
reading from the channel, a deadlock may occur if that required screen API
tries to take the screen's lock.
This patch collects events extracted from the input stream into a slice while
the lock is held, then after releasing the screen lock, writes them in order
to the event channel with PostEventWait(). I chose the blocking API to ensure
events aren't dropped, since sending the events outside of the lock-held scope
should remove the risk of a deadlock (unless I've missed something important!)
This patch is similar in spirit to that submitted by @soyking:
https://github.com/soyking/tcell/commit/9addd5bbe425a096257198e9832720dfd0e551a7.
I have not adjusted the windows cmd console screen because the paste problem
does not seem to be an issue in practice on that platform, at least according
to my testing.
2019-05-28 22:35:02 -04:00
|
|
|
|
|
|
|
return res
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
|
2017-09-23 23:25:24 -07:00
|
|
|
func (t *tScreen) mainLoop() {
|
2015-09-26 23:37:54 -07:00
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-t.quit:
|
2015-10-02 21:49:13 -07:00
|
|
|
close(t.indoneq)
|
2015-09-26 23:37:54 -07:00
|
|
|
return
|
|
|
|
case <-t.sigwinch:
|
2015-10-02 21:49:13 -07:00
|
|
|
t.Lock()
|
2015-10-07 00:56:18 -07:00
|
|
|
t.cx = -1
|
|
|
|
t.cy = -1
|
2015-09-26 23:37:54 -07:00
|
|
|
t.resize()
|
2015-10-07 00:56:18 -07:00
|
|
|
t.cells.Invalidate()
|
|
|
|
t.draw()
|
2015-10-02 21:49:13 -07:00
|
|
|
t.Unlock()
|
2015-09-26 23:37:54 -07:00
|
|
|
continue
|
2017-09-23 23:25:24 -07:00
|
|
|
case <-t.keytimer.C:
|
|
|
|
// If the timer fired, and the current time
|
|
|
|
// is after the expiration of the escape sequence,
|
|
|
|
// then we assume the escape sequence reached it's
|
|
|
|
// conclusion, and process the chunk independently.
|
|
|
|
// This lets us detect conflicts such as a lone ESC.
|
|
|
|
if buf.Len() > 0 {
|
|
|
|
if time.Now().After(t.keyexpire) {
|
|
|
|
t.scanInput(buf, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if buf.Len() > 0 {
|
|
|
|
if !t.keytimer.Stop() {
|
|
|
|
select {
|
|
|
|
case <-t.keytimer.C:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t.keytimer.Reset(time.Millisecond * 50)
|
|
|
|
}
|
|
|
|
case chunk := <-t.keychan:
|
|
|
|
buf.Write(chunk)
|
|
|
|
t.keyexpire = time.Now().Add(time.Millisecond * 50)
|
|
|
|
t.scanInput(buf, false)
|
|
|
|
if !t.keytimer.Stop() {
|
|
|
|
select {
|
|
|
|
case <-t.keytimer.C:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if buf.Len() > 0 {
|
|
|
|
t.keytimer.Reset(time.Millisecond * 50)
|
|
|
|
}
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
2017-09-23 23:25:24 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) inputLoop() {
|
|
|
|
|
|
|
|
for {
|
2017-11-24 15:55:02 -08:00
|
|
|
chunk := make([]byte, 128)
|
2015-09-26 23:37:54 -07:00
|
|
|
n, e := t.in.Read(chunk)
|
|
|
|
switch e {
|
|
|
|
case io.EOF:
|
|
|
|
case nil:
|
|
|
|
default:
|
2017-09-25 07:14:38 -07:00
|
|
|
t.PostEvent(NewEventError(e))
|
2015-09-26 23:37:54 -07:00
|
|
|
return
|
|
|
|
}
|
2017-09-23 23:25:24 -07:00
|
|
|
t.keychan <- chunk[:n]
|
2015-09-26 23:37:54 -07:00
|
|
|
}
|
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
|
|
|
|
func (t *tScreen) Sync() {
|
|
|
|
t.Lock()
|
2015-10-07 00:56:18 -07:00
|
|
|
t.cx = -1
|
|
|
|
t.cy = -1
|
|
|
|
if !t.fini {
|
|
|
|
t.resize()
|
|
|
|
t.clear = true
|
|
|
|
t.cells.Invalidate()
|
|
|
|
t.draw()
|
|
|
|
}
|
2015-10-02 21:49:13 -07:00
|
|
|
t.Unlock()
|
|
|
|
}
|
2015-10-04 19:53:38 -07:00
|
|
|
|
|
|
|
func (t *tScreen) CharacterSet() string {
|
|
|
|
return t.charset
|
|
|
|
}
|
2015-10-14 19:09:00 -07:00
|
|
|
|
|
|
|
func (t *tScreen) RegisterRuneFallback(orig rune, fallback string) {
|
|
|
|
t.Lock()
|
|
|
|
t.fallback[orig] = fallback
|
|
|
|
t.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) UnregisterRuneFallback(orig rune) {
|
|
|
|
t.Lock()
|
|
|
|
delete(t.fallback, orig)
|
|
|
|
t.Unlock()
|
|
|
|
}
|
2015-10-14 19:46:35 -07:00
|
|
|
|
|
|
|
func (t *tScreen) CanDisplay(r rune, checkFallbacks bool) bool {
|
|
|
|
|
|
|
|
if enc := t.encoder; enc != nil {
|
|
|
|
nb := make([]byte, 6)
|
|
|
|
ob := make([]byte, 6)
|
|
|
|
num := utf8.EncodeRune(ob, r)
|
|
|
|
|
|
|
|
enc.Reset()
|
|
|
|
dst, _, err := enc.Transform(nb, ob[:num], true)
|
|
|
|
if dst != 0 && err == nil && nb[0] != '\x1A' {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Terminal fallbacks always permitted, since we assume they are
|
|
|
|
// basically nearly perfect renditions.
|
|
|
|
if _, ok := t.acs[r]; ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if !checkFallbacks {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if _, ok := t.fallback[r]; ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2015-10-30 09:33:45 -07:00
|
|
|
|
2016-01-19 15:51:33 -08:00
|
|
|
func (t *tScreen) HasMouse() bool {
|
|
|
|
return len(t.mouse) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *tScreen) HasKey(k Key) bool {
|
|
|
|
if k == KeyRune {
|
|
|
|
return true
|
|
|
|
}
|
2016-03-23 13:14:49 -07:00
|
|
|
return t.keyexist[k]
|
2016-01-19 15:51:33 -08:00
|
|
|
}
|
|
|
|
|
2015-10-30 09:33:45 -07:00
|
|
|
func (t *tScreen) Resize(int, int, int, int) {}
|