summary refs log tree commit diff stats
path: root/widgets/terminal.go
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/terminal.go')
-rw-r--r--widgets/terminal.go136
1 files changed, 135 insertions, 1 deletions
diff --git a/widgets/terminal.go b/widgets/terminal.go
index 07b0f45..29a619c 100644
--- a/widgets/terminal.go
+++ b/widgets/terminal.go
@@ -12,6 +12,81 @@ import (
 	"github.com/kr/pty"
 )
 
+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)
+	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++ {
+		keyMap[tcell.Key(int(tcell.KeyF1)+i)] =
+			directKey(vterm.Key(int(vterm.KeyFunction0) + i))
+	}
+	keyMap[tcell.KeyTAB] = directKey(vterm.KeyTab)
+	keyMap[tcell.KeyESC] = directKey(vterm.KeyEscape)
+	keyMap[tcell.KeyDEL] = directKey(vterm.KeyBackspace)
+}
+
 type Terminal struct {
 	closed       bool
 	cmd          *exec.Cmd
@@ -43,11 +118,14 @@ func NewTerminal(cmd *exec.Cmd) (*Terminal, error) {
 		for {
 			n, err := term.pty.Read(buf)
 			if err != nil {
-				term.Close(err)
+				// These are generally benine errors when the process exits
+				term.Close(nil)
+				return
 			}
 			n, err = term.vterm.Write(buf[:n])
 			if err != nil {
 				term.Close(err)
+				return
 			}
 			term.Invalidate()
 		}
@@ -87,6 +165,25 @@ func NewTerminal(cmd *exec.Cmd) (*Terminal, error) {
 	return term, nil
 }
 
+func (term *Terminal) flushTerminal() {
+	buf := make([]byte, 2048)
+	for {
+		n, err := term.vterm.Read(buf)
+		if err != nil {
+			term.Close(err)
+			return
+		}
+		if n == 0 {
+			break
+		}
+		n, err = term.pty.Write(buf[:n])
+		if err != nil {
+			term.Close(err)
+			return
+		}
+	}
+}
+
 func (term *Terminal) Close(err error) {
 	term.err = err
 	if term.vterm != nil {
@@ -193,7 +290,44 @@ func (term *Terminal) Focus(focus bool) {
 	term.resetCursor()
 }
 
+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)
+}
+
 func (term *Terminal) Event(event tcell.Event) bool {
+	switch event := event.(type) {
+	case *tcell.EventKey:
+		if event.Key() == tcell.KeyRune {
+			term.vterm.KeyboardUnichar(
+				event.Rune(), convertMods(event.Modifiers()))
+		} else {
+			if key, ok := keyMap[event.Key()]; ok {
+				if key.Key == vterm.KeyNone {
+					term.vterm.KeyboardUnichar(
+						key.Rune, key.Mod)
+				} else if key.Mod == vterm.ModNone {
+					term.vterm.KeyboardKey(key.Key,
+						convertMods(event.Modifiers()))
+				} else {
+					term.vterm.KeyboardKey(key.Key, key.Mod)
+				}
+			}
+		}
+		term.flushTerminal()
+	}
 	return false
 }