From 28f393bdbdd865702cc8c928607d43a05dc7e6b8 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 21 Mar 2019 19:50:54 -0400 Subject: [PATCH] Forward key events to child terminal --- go.mod | 2 +- go.sum | 4 ++ widgets/terminal.go | 136 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 140 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index c70fa24..48ca887 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module git.sr.ht/~sircmpwn/aerc2 require ( - git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190317210455-72d6c2838fbe + git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190321231430-3db654768b06 github.com/emersion/go-imap v1.0.0-beta.1 github.com/emersion/go-imap-idle v0.0.0-20180114101550-2af93776db6b github.com/emersion/go-sasl v0.0.0-20161116183048-7e096a0a6197 // indirect diff --git a/go.sum b/go.sum index 8ee8cab..48fbbc5 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,10 @@ git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190317205953-0674b071e8bf h1:6Ynr+M6lUn git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190317205953-0674b071e8bf/go.mod h1:hT88+cTemwwESbMptwC7O33qrJfQX0SgRWbXlndUS2c= git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190317210455-72d6c2838fbe h1:pg73OOcg6PZp+XUOsFaqGG4Heu3kYAJZHSaS6eRRmJk= git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190317210455-72d6c2838fbe/go.mod h1:hT88+cTemwwESbMptwC7O33qrJfQX0SgRWbXlndUS2c= +git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190321231235-12f4bd976b20 h1:5oQDpkPYOnIJZnbT5jgUhpRqOkHcbMLe2vmHHRcmcys= +git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190321231235-12f4bd976b20/go.mod h1:hT88+cTemwwESbMptwC7O33qrJfQX0SgRWbXlndUS2c= +git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190321231430-3db654768b06 h1:x9azyMxWqgTq4ARhNbYKHejt0PMhLi8J2XnuEsih9Qc= +git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190321231430-3db654768b06/go.mod h1:hT88+cTemwwESbMptwC7O33qrJfQX0SgRWbXlndUS2c= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emersion/go-imap v1.0.0-beta.1 h1:bTCaVlUnb5mKoW9lEukusxguSYYZPer+q0g5t+vw5X0= 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 }