2019-03-17 19:02:33 +01:00
|
|
|
package widgets
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2019-05-19 13:40:05 +02:00
|
|
|
"sync"
|
2020-05-06 16:25:11 +02:00
|
|
|
"syscall"
|
2019-03-17 19:02:33 +01:00
|
|
|
|
2021-11-05 10:19:46 +01:00
|
|
|
"git.sr.ht/~rjarry/aerc/lib/ui"
|
2022-03-22 09:52:27 +01:00
|
|
|
"git.sr.ht/~rjarry/aerc/logging"
|
2019-03-17 19:02:33 +01:00
|
|
|
|
2020-01-16 00:43:07 +01:00
|
|
|
"github.com/creack/pty"
|
2021-04-13 01:24:35 +02:00
|
|
|
vterm "github.com/ddevault/go-libvterm"
|
2020-11-30 23:07:03 +01:00
|
|
|
"github.com/gdamore/tcell/v2"
|
2019-03-17 19:02:33 +01:00
|
|
|
)
|
|
|
|
|
2019-03-22 00:50:54 +01:00
|
|
|
type vtermKey struct {
|
|
|
|
Key vterm.Key
|
|
|
|
Rune rune
|
|
|
|
Mod vterm.Modifier
|
|
|
|
}
|
|
|
|
|
|
|
|
var keyMap map[tcell.Key]vtermKey
|
|
|
|
|
|
|
|
func directKey(key vterm.Key) vtermKey {
|
|
|
|
return vtermKey{key, 0, vterm.ModNone}
|
|
|
|
}
|
|
|
|
|
|
|
|
func runeMod(r rune, mod vterm.Modifier) vtermKey {
|
|
|
|
return vtermKey{vterm.KeyNone, r, mod}
|
|
|
|
}
|
|
|
|
|
|
|
|
func keyMod(key vterm.Key, mod vterm.Modifier) vtermKey {
|
|
|
|
return vtermKey{key, 0, mod}
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
keyMap = make(map[tcell.Key]vtermKey)
|
|
|
|
keyMap[tcell.KeyCtrlSpace] = runeMod(' ', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlA] = runeMod('a', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlB] = runeMod('b', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlC] = runeMod('c', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlD] = runeMod('d', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlE] = runeMod('e', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlF] = runeMod('f', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlG] = runeMod('g', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlH] = runeMod('h', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlI] = runeMod('i', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlJ] = runeMod('j', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlK] = runeMod('k', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlL] = runeMod('l', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlM] = runeMod('m', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlN] = runeMod('n', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlO] = runeMod('o', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlP] = runeMod('p', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlQ] = runeMod('q', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlR] = runeMod('r', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlS] = runeMod('s', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlT] = runeMod('t', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlU] = runeMod('u', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlV] = runeMod('v', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlW] = runeMod('w', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlX] = runeMod('x', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlY] = runeMod('y', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlZ] = runeMod('z', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlBackslash] = runeMod('\\', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlCarat] = runeMod('^', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyCtrlUnderscore] = runeMod('_', vterm.ModCtrl)
|
|
|
|
keyMap[tcell.KeyEnter] = directKey(vterm.KeyEnter)
|
|
|
|
keyMap[tcell.KeyTab] = directKey(vterm.KeyTab)
|
2021-02-06 09:14:24 +01:00
|
|
|
keyMap[tcell.KeyBacktab] = keyMod(vterm.KeyTab, vterm.ModShift)
|
2019-03-22 00:50:54 +01:00
|
|
|
keyMap[tcell.KeyBackspace] = directKey(vterm.KeyBackspace)
|
|
|
|
keyMap[tcell.KeyEscape] = directKey(vterm.KeyEscape)
|
|
|
|
keyMap[tcell.KeyUp] = directKey(vterm.KeyUp)
|
|
|
|
keyMap[tcell.KeyDown] = directKey(vterm.KeyDown)
|
|
|
|
keyMap[tcell.KeyLeft] = directKey(vterm.KeyLeft)
|
|
|
|
keyMap[tcell.KeyRight] = directKey(vterm.KeyRight)
|
|
|
|
keyMap[tcell.KeyInsert] = directKey(vterm.KeyIns)
|
|
|
|
keyMap[tcell.KeyDelete] = directKey(vterm.KeyDel)
|
|
|
|
keyMap[tcell.KeyHome] = directKey(vterm.KeyHome)
|
|
|
|
keyMap[tcell.KeyEnd] = directKey(vterm.KeyEnd)
|
|
|
|
keyMap[tcell.KeyPgUp] = directKey(vterm.KeyPageUp)
|
|
|
|
keyMap[tcell.KeyPgDn] = directKey(vterm.KeyPageDown)
|
|
|
|
for i := 0; i < 64; i++ {
|
2022-07-31 22:16:40 +02:00
|
|
|
keyMap[tcell.Key(int(tcell.KeyF1)+i)] = directKey(vterm.Key(int(vterm.KeyFunction0) + i + 1))
|
2019-03-22 00:50:54 +01:00
|
|
|
}
|
|
|
|
keyMap[tcell.KeyTAB] = directKey(vterm.KeyTab)
|
|
|
|
keyMap[tcell.KeyESC] = directKey(vterm.KeyEscape)
|
|
|
|
keyMap[tcell.KeyDEL] = directKey(vterm.KeyBackspace)
|
|
|
|
}
|
|
|
|
|
2019-03-17 19:02:33 +01:00
|
|
|
type Terminal struct {
|
2019-04-27 18:47:59 +02:00
|
|
|
ui.Invalidatable
|
|
|
|
closed bool
|
|
|
|
cmd *exec.Cmd
|
|
|
|
ctx *ui.Context
|
|
|
|
cursorPos vterm.Pos
|
|
|
|
cursorShown bool
|
|
|
|
destroyed bool
|
|
|
|
err error
|
|
|
|
focus bool
|
|
|
|
pty *os.File
|
|
|
|
start chan interface{}
|
|
|
|
vterm *vterm.VTerm
|
2019-03-17 22:08:54 +01:00
|
|
|
|
2019-06-01 17:28:29 +02:00
|
|
|
damage []vterm.Rect // protected by damageMutex
|
2019-05-25 11:06:15 +02:00
|
|
|
damageMutex sync.Mutex
|
|
|
|
writeMutex sync.Mutex
|
2022-01-11 12:02:28 +01:00
|
|
|
readMutex sync.Mutex
|
2022-05-23 22:16:13 +02:00
|
|
|
closeMutex sync.Mutex
|
2019-05-19 13:40:05 +02:00
|
|
|
|
2019-03-17 22:23:53 +01:00
|
|
|
OnClose func(err error)
|
2019-05-11 20:15:29 +02:00
|
|
|
OnEvent func(event tcell.Event) bool
|
2019-03-30 16:58:24 +01:00
|
|
|
OnStart func()
|
2019-03-17 22:08:54 +01:00
|
|
|
OnTitle func(title string)
|
2019-03-17 19:02:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewTerminal(cmd *exec.Cmd) (*Terminal, error) {
|
2019-03-22 02:00:03 +01:00
|
|
|
term := &Terminal{
|
|
|
|
cursorShown: true,
|
|
|
|
}
|
2019-03-17 19:02:33 +01:00
|
|
|
term.cmd = cmd
|
2019-03-17 19:54:25 +01:00
|
|
|
term.vterm = vterm.New(24, 80)
|
2019-03-17 19:02:33 +01:00
|
|
|
term.vterm.SetUTF8(true)
|
2019-03-17 19:54:25 +01:00
|
|
|
term.start = make(chan interface{})
|
2019-03-22 02:00:03 +01:00
|
|
|
screen := term.vterm.ObtainScreen()
|
2019-03-17 19:02:33 +01:00
|
|
|
go func() {
|
2022-03-22 09:52:27 +01:00
|
|
|
defer logging.PanicHandler()
|
|
|
|
|
2019-03-17 19:54:25 +01:00
|
|
|
<-term.start
|
2019-03-22 02:00:03 +01:00
|
|
|
buf := make([]byte, 4096)
|
2019-03-17 19:02:33 +01:00
|
|
|
for {
|
|
|
|
n, err := term.pty.Read(buf)
|
2019-03-22 02:07:49 +01:00
|
|
|
if err != nil || term.closed {
|
2019-03-22 00:50:54 +01:00
|
|
|
// These are generally benine errors when the process exits
|
|
|
|
term.Close(nil)
|
|
|
|
return
|
2019-03-17 19:02:33 +01:00
|
|
|
}
|
2019-05-25 11:06:15 +02:00
|
|
|
term.writeMutex.Lock()
|
2022-03-09 22:48:00 +01:00
|
|
|
_, err = term.vterm.Write(buf[:n])
|
2019-05-25 11:06:15 +02:00
|
|
|
term.writeMutex.Unlock()
|
2019-03-17 19:02:33 +01:00
|
|
|
if err != nil {
|
2019-03-17 22:23:53 +01:00
|
|
|
term.Close(err)
|
2019-03-22 00:50:54 +01:00
|
|
|
return
|
2019-03-17 19:02:33 +01:00
|
|
|
}
|
2019-03-22 02:00:03 +01:00
|
|
|
screen.Flush()
|
2019-03-22 02:19:34 +01:00
|
|
|
term.flushTerminal()
|
2019-04-15 22:07:05 +02:00
|
|
|
term.invalidate()
|
2019-03-17 19:02:33 +01:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
screen.OnDamage = term.onDamage
|
|
|
|
screen.OnMoveCursor = term.onMoveCursor
|
2019-03-17 22:08:54 +01:00
|
|
|
screen.OnSetTermProp = term.onSetTermProp
|
2019-03-22 02:24:23 +01:00
|
|
|
screen.EnableAltScreen(true)
|
2019-03-17 19:02:33 +01:00
|
|
|
screen.Reset(true)
|
|
|
|
return term, nil
|
|
|
|
}
|
|
|
|
|
2019-03-22 00:50:54 +01:00
|
|
|
func (term *Terminal) flushTerminal() {
|
2019-03-22 02:00:03 +01:00
|
|
|
buf := make([]byte, 4096)
|
2019-03-22 00:50:54 +01:00
|
|
|
for {
|
2022-01-11 12:02:28 +01:00
|
|
|
term.readMutex.Lock()
|
2019-03-22 00:50:54 +01:00
|
|
|
n, err := term.vterm.Read(buf)
|
2022-01-11 12:02:28 +01:00
|
|
|
term.readMutex.Unlock()
|
2019-03-22 00:50:54 +01:00
|
|
|
if err != nil {
|
|
|
|
term.Close(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if n == 0 {
|
|
|
|
break
|
|
|
|
}
|
2022-03-09 22:48:00 +01:00
|
|
|
_, err = term.pty.Write(buf[:n])
|
2019-03-22 00:50:54 +01:00
|
|
|
if err != nil {
|
|
|
|
term.Close(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 22:23:53 +01:00
|
|
|
func (term *Terminal) Close(err error) {
|
2022-05-23 22:16:13 +02:00
|
|
|
term.closeMutex.Lock()
|
|
|
|
defer term.closeMutex.Unlock()
|
|
|
|
|
2019-03-30 16:58:24 +01:00
|
|
|
if term.closed {
|
|
|
|
return
|
|
|
|
}
|
2019-03-17 22:23:53 +01:00
|
|
|
term.err = err
|
|
|
|
if term.pty != nil {
|
|
|
|
term.pty.Close()
|
|
|
|
term.pty = nil
|
|
|
|
}
|
|
|
|
if term.cmd != nil && term.cmd.Process != nil {
|
2022-07-29 22:31:54 +02:00
|
|
|
err := term.cmd.Process.Kill()
|
|
|
|
if err != nil {
|
|
|
|
logging.Warnf("failed to kill process: %v", err)
|
|
|
|
}
|
|
|
|
err = term.cmd.Wait()
|
|
|
|
if err != nil {
|
|
|
|
logging.Warnf("failed for wait for process to terminate: %v", err)
|
|
|
|
}
|
2019-03-17 22:23:53 +01:00
|
|
|
term.cmd = nil
|
|
|
|
}
|
|
|
|
if !term.closed && term.OnClose != nil {
|
|
|
|
term.OnClose(err)
|
2019-03-17 19:02:33 +01:00
|
|
|
}
|
|
|
|
term.closed = true
|
2019-03-22 00:56:47 +01:00
|
|
|
term.ctx.HideCursor()
|
2019-03-17 19:02:33 +01:00
|
|
|
}
|
|
|
|
|
2019-03-30 21:29:52 +01:00
|
|
|
func (term *Terminal) Destroy() {
|
|
|
|
if term.destroyed {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if term.vterm != nil {
|
|
|
|
term.vterm.Close()
|
|
|
|
term.vterm = nil
|
|
|
|
}
|
|
|
|
if term.ctx != nil {
|
|
|
|
term.ctx.HideCursor()
|
|
|
|
}
|
|
|
|
term.destroyed = true
|
|
|
|
}
|
|
|
|
|
2019-03-17 19:02:33 +01:00
|
|
|
func (term *Terminal) Invalidate() {
|
2019-04-15 22:07:05 +02:00
|
|
|
if term.vterm != nil {
|
|
|
|
width, height := term.vterm.Size()
|
|
|
|
rect := vterm.NewRect(0, width, 0, height)
|
2019-05-25 11:06:15 +02:00
|
|
|
term.damageMutex.Lock()
|
2019-04-15 22:07:05 +02:00
|
|
|
term.damage = append(term.damage, *rect)
|
2019-05-25 11:06:15 +02:00
|
|
|
term.damageMutex.Unlock()
|
2019-04-15 22:07:05 +02:00
|
|
|
}
|
|
|
|
term.invalidate()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (term *Terminal) invalidate() {
|
2019-04-27 18:47:59 +02:00
|
|
|
term.DoInvalidate(term)
|
2019-03-17 19:02:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (term *Terminal) Draw(ctx *ui.Context) {
|
2019-03-30 21:29:52 +01:00
|
|
|
if term.destroyed {
|
2019-03-17 22:23:53 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-03-30 21:29:52 +01:00
|
|
|
term.ctx = ctx // gross
|
2019-03-22 02:23:30 +01:00
|
|
|
|
2019-03-30 21:29:52 +01:00
|
|
|
if !term.closed {
|
|
|
|
winsize := pty.Winsize{
|
|
|
|
Cols: uint16(ctx.Width()),
|
|
|
|
Rows: uint16(ctx.Height()),
|
|
|
|
}
|
2019-08-12 02:51:45 +02:00
|
|
|
if winsize.Cols == 0 || winsize.Rows == 0 || term.cmd == nil {
|
2019-07-13 17:13:48 +02:00
|
|
|
return
|
|
|
|
}
|
2019-03-17 19:54:25 +01:00
|
|
|
|
2019-03-30 21:29:52 +01:00
|
|
|
if term.pty == nil {
|
|
|
|
term.vterm.SetSize(ctx.Height(), ctx.Width())
|
2022-05-25 11:16:13 +02:00
|
|
|
|
|
|
|
term.closeMutex.Lock()
|
|
|
|
if term.cmd == nil {
|
|
|
|
term.closeMutex.Unlock()
|
|
|
|
return
|
|
|
|
}
|
2020-05-06 16:25:11 +02:00
|
|
|
tty, err := pty.StartWithAttrs(term.cmd, &winsize, &syscall.SysProcAttr{Setsid: true, Setctty: true, Ctty: 1})
|
2022-05-25 11:16:13 +02:00
|
|
|
term.closeMutex.Unlock()
|
|
|
|
|
2019-03-30 21:29:52 +01:00
|
|
|
term.pty = tty
|
|
|
|
if err != nil {
|
|
|
|
term.Close(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
term.start <- nil
|
|
|
|
if term.OnStart != nil {
|
|
|
|
term.OnStart()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-19 21:32:31 +01:00
|
|
|
ws, err := pty.GetsizeFull(term.pty)
|
2019-03-17 19:54:25 +01:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2022-01-19 21:32:31 +01:00
|
|
|
rows := int(ws.Rows)
|
|
|
|
cols := int(ws.Cols)
|
|
|
|
|
2019-03-30 21:29:52 +01:00
|
|
|
if ctx.Width() != cols || ctx.Height() != rows {
|
2019-05-25 11:06:15 +02:00
|
|
|
term.writeMutex.Lock()
|
2022-07-29 22:31:54 +02:00
|
|
|
err := pty.Setsize(term.pty, &winsize)
|
|
|
|
if err != nil {
|
|
|
|
logging.Warnf("failed to set terminal size: %v", err)
|
|
|
|
}
|
2019-03-30 21:29:52 +01:00
|
|
|
term.vterm.SetSize(ctx.Height(), ctx.Width())
|
2019-05-25 11:06:15 +02:00
|
|
|
term.writeMutex.Unlock()
|
2019-05-12 17:21:28 +02:00
|
|
|
rect := vterm.NewRect(0, ctx.Width(), 0, ctx.Height())
|
2019-05-25 11:06:15 +02:00
|
|
|
term.damageMutex.Lock()
|
2019-05-12 17:21:28 +02:00
|
|
|
term.damage = append(term.damage, *rect)
|
2019-05-25 11:06:15 +02:00
|
|
|
term.damageMutex.Unlock()
|
2019-03-30 21:29:52 +01:00
|
|
|
return
|
2019-03-30 16:58:24 +01:00
|
|
|
}
|
2019-03-17 19:54:25 +01:00
|
|
|
}
|
|
|
|
|
2019-03-17 19:02:33 +01:00
|
|
|
screen := term.vterm.ObtainScreen()
|
|
|
|
|
|
|
|
type coords struct {
|
|
|
|
x int
|
|
|
|
y int
|
|
|
|
}
|
|
|
|
|
|
|
|
// naive optimization
|
|
|
|
visited := make(map[coords]interface{})
|
|
|
|
|
2019-05-25 11:06:15 +02:00
|
|
|
term.damageMutex.Lock()
|
2019-03-17 19:02:33 +01:00
|
|
|
for _, rect := range term.damage {
|
|
|
|
for x := rect.StartCol(); x < rect.EndCol() && x < ctx.Width(); x += 1 {
|
2019-04-06 01:44:19 +02:00
|
|
|
for y := rect.StartRow(); y < rect.EndRow() && y < ctx.Height(); y += 1 {
|
2019-03-17 19:02:33 +01:00
|
|
|
|
|
|
|
coords := coords{x, y}
|
|
|
|
if _, ok := visited[coords]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
visited[coords] = nil
|
|
|
|
|
|
|
|
cell, err := screen.GetCellAt(y, x)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2019-03-17 19:54:25 +01:00
|
|
|
style := term.styleFromCell(cell)
|
2019-03-17 19:02:33 +01:00
|
|
|
ctx.Printf(x, y, style, "%s", string(cell.Chars()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-22 00:56:47 +01:00
|
|
|
|
2019-04-06 02:11:31 +02:00
|
|
|
term.damage = nil
|
2019-05-25 11:06:15 +02:00
|
|
|
term.damageMutex.Unlock()
|
2019-04-06 02:11:31 +02:00
|
|
|
|
2019-03-30 21:29:52 +01:00
|
|
|
if term.focus && !term.closed {
|
2019-03-22 02:28:51 +01:00
|
|
|
if !term.cursorShown {
|
|
|
|
ctx.HideCursor()
|
|
|
|
} else {
|
|
|
|
state := term.vterm.ObtainState()
|
|
|
|
row, col := state.GetCursorPos()
|
|
|
|
ctx.SetCursor(col, row)
|
|
|
|
}
|
2019-03-22 00:56:47 +01:00
|
|
|
}
|
2019-03-17 19:02:33 +01:00
|
|
|
}
|
|
|
|
|
2019-09-06 00:32:36 +02:00
|
|
|
func (term *Terminal) MouseEvent(localX int, localY int, event tcell.Event) {
|
2022-07-31 14:32:48 +02:00
|
|
|
if event, ok := event.(*tcell.EventMouse); ok {
|
2019-09-06 00:32:36 +02:00
|
|
|
if term.OnEvent != nil {
|
|
|
|
if term.OnEvent(event) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if term.closed {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 19:02:33 +01:00
|
|
|
func (term *Terminal) Focus(focus bool) {
|
2019-03-30 16:58:24 +01:00
|
|
|
if term.closed {
|
|
|
|
return
|
|
|
|
}
|
2019-03-17 19:02:33 +01:00
|
|
|
term.focus = focus
|
2019-03-22 02:34:12 +01:00
|
|
|
if term.ctx != nil {
|
|
|
|
if !term.focus {
|
|
|
|
term.ctx.HideCursor()
|
|
|
|
} else {
|
|
|
|
state := term.vterm.ObtainState()
|
|
|
|
row, col := state.GetCursorPos()
|
|
|
|
term.ctx.SetCursor(col, row)
|
2019-05-12 17:21:28 +02:00
|
|
|
term.Invalidate()
|
2019-03-22 02:34:12 +01:00
|
|
|
}
|
2019-03-22 02:28:51 +01:00
|
|
|
}
|
2019-03-17 19:02:33 +01:00
|
|
|
}
|
|
|
|
|
2019-03-22 00:50:54 +01:00
|
|
|
func convertMods(mods tcell.ModMask) vterm.Modifier {
|
|
|
|
var (
|
|
|
|
ret uint = 0
|
|
|
|
mask uint = uint(mods)
|
|
|
|
)
|
|
|
|
if mask&uint(tcell.ModShift) > 0 {
|
|
|
|
ret |= uint(vterm.ModShift)
|
|
|
|
}
|
|
|
|
if mask&uint(tcell.ModCtrl) > 0 {
|
|
|
|
ret |= uint(vterm.ModCtrl)
|
|
|
|
}
|
|
|
|
if mask&uint(tcell.ModAlt) > 0 {
|
|
|
|
ret |= uint(vterm.ModAlt)
|
|
|
|
}
|
|
|
|
return vterm.Modifier(ret)
|
|
|
|
}
|
|
|
|
|
2019-03-17 19:02:33 +01:00
|
|
|
func (term *Terminal) Event(event tcell.Event) bool {
|
2019-05-11 20:15:29 +02:00
|
|
|
if term.OnEvent != nil {
|
|
|
|
if term.OnEvent(event) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
2019-03-30 16:58:24 +01:00
|
|
|
if term.closed {
|
|
|
|
return false
|
|
|
|
}
|
2022-07-31 14:32:48 +02:00
|
|
|
if event, ok := event.(*tcell.EventKey); ok {
|
2019-03-22 00:50:54 +01:00
|
|
|
if event.Key() == tcell.KeyRune {
|
|
|
|
term.vterm.KeyboardUnichar(
|
|
|
|
event.Rune(), convertMods(event.Modifiers()))
|
2022-07-31 14:32:48 +02:00
|
|
|
} else if key, ok := keyMap[event.Key()]; ok {
|
|
|
|
switch {
|
|
|
|
case key.Key == vterm.KeyNone:
|
|
|
|
term.vterm.KeyboardUnichar(
|
|
|
|
key.Rune, key.Mod)
|
|
|
|
case key.Mod == vterm.ModNone:
|
|
|
|
term.vterm.KeyboardKey(key.Key,
|
|
|
|
convertMods(event.Modifiers()))
|
|
|
|
default:
|
|
|
|
term.vterm.KeyboardKey(key.Key, key.Mod)
|
2019-03-22 00:50:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
term.flushTerminal()
|
|
|
|
}
|
2019-03-17 19:02:33 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-03-17 19:54:25 +01:00
|
|
|
func (term *Terminal) styleFromCell(cell *vterm.ScreenCell) tcell.Style {
|
|
|
|
style := tcell.StyleDefault
|
|
|
|
|
2019-03-17 19:02:33 +01:00
|
|
|
background := cell.Bg()
|
|
|
|
foreground := cell.Fg()
|
2019-03-17 19:54:25 +01:00
|
|
|
|
2019-05-26 21:48:29 +02:00
|
|
|
var (
|
|
|
|
bg tcell.Color
|
|
|
|
fg tcell.Color
|
|
|
|
)
|
2022-07-31 14:32:48 +02:00
|
|
|
switch {
|
|
|
|
case background.IsDefaultBg():
|
2019-05-26 21:48:29 +02:00
|
|
|
bg = tcell.ColorDefault
|
2022-07-31 14:32:48 +02:00
|
|
|
case background.IsIndexed():
|
2020-11-30 23:07:03 +01:00
|
|
|
bg = tcell.Color(tcell.PaletteColor(int(background.GetIndex())))
|
2022-07-31 14:32:48 +02:00
|
|
|
case background.IsRgb():
|
2019-05-26 21:48:29 +02:00
|
|
|
r, g, b := background.GetRGB()
|
|
|
|
bg = tcell.NewRGBColor(int32(r), int32(g), int32(b))
|
|
|
|
}
|
2022-07-31 14:32:48 +02:00
|
|
|
switch {
|
|
|
|
case foreground.IsDefaultFg():
|
2019-05-26 21:48:29 +02:00
|
|
|
fg = tcell.ColorDefault
|
2022-07-31 14:32:48 +02:00
|
|
|
case foreground.IsIndexed():
|
2020-11-30 23:07:03 +01:00
|
|
|
fg = tcell.Color(tcell.PaletteColor(int(foreground.GetIndex())))
|
2022-07-31 14:32:48 +02:00
|
|
|
case foreground.IsRgb():
|
2019-05-26 21:48:29 +02:00
|
|
|
r, g, b := foreground.GetRGB()
|
|
|
|
fg = tcell.NewRGBColor(int32(r), int32(g), int32(b))
|
|
|
|
}
|
|
|
|
|
|
|
|
style = style.Background(bg).Foreground(fg)
|
2021-04-13 01:24:35 +02:00
|
|
|
attrs := cell.Attrs()
|
2019-03-17 19:54:25 +01:00
|
|
|
|
2021-04-13 01:24:35 +02:00
|
|
|
if attrs.Bold != 0 {
|
2019-03-17 19:54:25 +01:00
|
|
|
style = style.Bold(true)
|
|
|
|
}
|
2021-04-13 01:24:35 +02:00
|
|
|
if attrs.Italic != 0 {
|
2020-12-23 18:03:33 +01:00
|
|
|
style = style.Italic(true)
|
|
|
|
}
|
2021-04-13 01:24:35 +02:00
|
|
|
if attrs.Underline != 0 {
|
2019-03-17 19:54:25 +01:00
|
|
|
style = style.Underline(true)
|
|
|
|
}
|
2021-04-13 01:24:35 +02:00
|
|
|
if attrs.Blink != 0 {
|
2019-03-17 19:54:25 +01:00
|
|
|
style = style.Blink(true)
|
|
|
|
}
|
2021-04-13 01:24:35 +02:00
|
|
|
if attrs.Reverse != 0 {
|
2019-03-17 19:54:25 +01:00
|
|
|
style = style.Reverse(true)
|
|
|
|
}
|
2019-03-17 19:02:33 +01:00
|
|
|
return style
|
|
|
|
}
|
|
|
|
|
|
|
|
func (term *Terminal) onDamage(rect *vterm.Rect) int {
|
2019-05-25 11:06:15 +02:00
|
|
|
term.damageMutex.Lock()
|
2019-03-17 19:02:33 +01:00
|
|
|
term.damage = append(term.damage, *rect)
|
2019-05-25 11:06:15 +02:00
|
|
|
term.damageMutex.Unlock()
|
2019-04-15 22:07:05 +02:00
|
|
|
term.invalidate()
|
2019-03-17 19:02:33 +01:00
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func (term *Terminal) onMoveCursor(old *vterm.Pos,
|
2022-07-31 22:16:40 +02:00
|
|
|
pos *vterm.Pos, visible bool,
|
|
|
|
) int {
|
2019-03-22 02:00:03 +01:00
|
|
|
rows, cols, _ := pty.Getsize(term.pty)
|
|
|
|
if pos.Row() >= rows || pos.Col() >= cols {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
term.cursorPos = *pos
|
2019-04-15 22:07:05 +02:00
|
|
|
term.invalidate()
|
2019-03-17 19:02:33 +01:00
|
|
|
return 1
|
|
|
|
}
|
2019-03-17 22:08:54 +01:00
|
|
|
|
|
|
|
func (term *Terminal) onSetTermProp(prop int, val *vterm.VTermValue) int {
|
|
|
|
switch prop {
|
|
|
|
case vterm.VTERM_PROP_TITLE:
|
|
|
|
if term.OnTitle != nil {
|
|
|
|
term.OnTitle(val.String)
|
|
|
|
}
|
2019-03-22 02:00:03 +01:00
|
|
|
case vterm.VTERM_PROP_CURSORVISIBLE:
|
|
|
|
term.cursorShown = val.Boolean
|
2019-04-15 22:07:05 +02:00
|
|
|
term.invalidate()
|
2019-03-17 22:08:54 +01:00
|
|
|
}
|
|
|
|
return 1
|
|
|
|
}
|