diff --git a/commands/term-close.go b/commands/term-close.go new file mode 100644 index 0000000..38fcc27 --- /dev/null +++ b/commands/term-close.go @@ -0,0 +1,29 @@ +package commands + +import ( + "errors" + + "git.sr.ht/~sircmpwn/aerc2/lib/ui" + "git.sr.ht/~sircmpwn/aerc2/widgets" +) + +func init() { + Register("term-close", TermClose) +} + +func TermClose(aerc *widgets.Aerc, args []string) error { + if len(args) != 1 { + return errors.New("Usage: term-close") + } + grid, ok := aerc.SelectedTab().(*ui.Grid) + if !ok { + return errors.New("Error: not a terminal") + } + for _, child := range grid.Children() { + if term, ok := child.(*widgets.Terminal); ok { + term.Close(nil) + return nil + } + } + return errors.New("Error: not a terminal") +} diff --git a/commands/term.go b/commands/term.go index 7ce1947..976ce29 100644 --- a/commands/term.go +++ b/commands/term.go @@ -2,10 +2,12 @@ package commands import ( "os/exec" + "time" "git.sr.ht/~sircmpwn/aerc2/lib/ui" "git.sr.ht/~sircmpwn/aerc2/widgets" + "github.com/gdamore/tcell" "github.com/riywo/loginshell" ) @@ -32,13 +34,20 @@ func Term(aerc *widgets.Aerc, args []string) error { {ui.SIZE_WEIGHT, 1}, }) grid.AddChild(term).At(0, 1) - tab := aerc.NewTab(grid, "Terminal") + tab := aerc.NewTab(grid, args[1]) term.OnTitle = func(title string) { if title == "" { - title = "Terminal" + title = args[1] } tab.Name = title tab.Content.Invalidate() } + term.OnClose = func(err error) { + aerc.RemoveTab(grid) + if err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorRed, tcell.ColorWhite) + } + } return nil } diff --git a/lib/ui/tab.go b/lib/ui/tab.go index e41e906..32b195c 100644 --- a/lib/ui/tab.go +++ b/lib/ui/tab.go @@ -62,13 +62,13 @@ func (tabs *Tabs) Remove(content Drawable) { } /* Force the selected index into the existing range */ if tabs.Selected >= len(tabs.Tabs) { - tabs.Select(len(tabs.Tabs) - 1) + tabs.Select(tabs.Selected - 1) } tabs.TabStrip.Invalidate() } func (tabs *Tabs) Select(index int) { - if tabs.Selected >= len(tabs.Tabs) { + if index >= len(tabs.Tabs) { panic("Tried to set tab index to a non-existing element") } diff --git a/lib/ui/text.go b/lib/ui/text.go index d3f6c6b..b962166 100644 --- a/lib/ui/text.go +++ b/lib/ui/text.go @@ -20,7 +20,11 @@ type Text struct { } func NewText(text string) *Text { - return &Text{text: text} + return &Text{ + bg: tcell.ColorDefault, + fg: tcell.ColorDefault, + text: text, + } } func (t *Text) Text(text string) *Text { diff --git a/widgets/aerc.go b/widgets/aerc.go index a968ab1..49a61bd 100644 --- a/widgets/aerc.go +++ b/widgets/aerc.go @@ -139,12 +139,20 @@ func (aerc *Aerc) SelectedAccount() *AccountView { return acct } +func (aerc *Aerc) SelectedTab() ui.Drawable { + return aerc.tabs.Tabs[aerc.tabs.Selected].Content +} + func (aerc *Aerc) NewTab(drawable ui.Drawable, name string) *ui.Tab { tab := aerc.tabs.Add(drawable, name) aerc.tabs.Select(len(aerc.tabs.Tabs) - 1) return tab } +func (aerc *Aerc) RemoveTab(tab ui.Drawable) { + aerc.tabs.Remove(tab) +} + func (aerc *Aerc) NextTab() { next := aerc.tabs.Selected + 1 if next >= len(aerc.tabs.Tabs) { diff --git a/widgets/terminal.go b/widgets/terminal.go index bd84ab4..7726273 100644 --- a/widgets/terminal.go +++ b/widgets/terminal.go @@ -20,12 +20,14 @@ type Terminal struct { cursorPos vterm.Pos cursorShown bool damage []vterm.Rect + err error focus bool onInvalidate func(d ui.Drawable) pty *os.File start chan interface{} vterm *vterm.VTerm + OnClose func(err error) OnTitle func(title string) } @@ -41,11 +43,11 @@ func NewTerminal(cmd *exec.Cmd) (*Terminal, error) { for { n, err := term.pty.Read(buf) if err != nil { - term.Close() + term.Close(err) } n, err = term.vterm.Write(buf[:n]) if err != nil { - term.Close() + term.Close(err) } term.Invalidate() } @@ -79,14 +81,24 @@ func NewTerminal(cmd *exec.Cmd) (*Terminal, error) { return term, nil } -func (term *Terminal) Close() { - if term.closed { - return +func (term *Terminal) Close(err error) { + term.err = err + if term.vterm != nil { + term.vterm.Close() + term.vterm = nil + } + if term.pty != nil { + term.pty.Close() + term.pty = nil + } + if term.cmd != nil && term.cmd.Process != nil { + term.cmd.Process.Kill() + term.cmd = nil + } + if !term.closed && term.OnClose != nil { + term.OnClose(err) } term.closed = true - term.vterm.Close() - term.pty.Close() - term.cmd.Process.Kill() } func (term *Terminal) OnInvalidate(cb func(d ui.Drawable)) { @@ -100,6 +112,15 @@ func (term *Terminal) Invalidate() { } func (term *Terminal) Draw(ctx *ui.Context) { + if term.closed { + if term.err != nil { + ui.NewText(term.err.Error()).Strategy(ui.TEXT_CENTER).Draw(ctx) + } else { + ui.NewText("Terminal closed").Strategy(ui.TEXT_CENTER).Draw(ctx) + } + return + } + winsize := pty.Winsize{ Cols: uint16(ctx.Width()), Rows: uint16(ctx.Height()), @@ -110,16 +131,13 @@ func (term *Terminal) Draw(ctx *ui.Context) { tty, err := pty.StartWithSize(term.cmd, &winsize) term.pty = tty if err != nil { - term.Close() + term.Close(err) return } term.start <- nil } term.ctx = ctx // gross - if term.closed { - return - } rows, cols, err := pty.Getsize(term.pty) if err != nil {