init: pristine aerc 0.20.0 source
This commit is contained in:
+177
@@ -0,0 +1,177 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"sync/atomic"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/config"
|
||||
"git.sr.ht/~rjarry/aerc/lib/log"
|
||||
"git.sr.ht/~rjarry/aerc/lib/ui"
|
||||
"git.sr.ht/~rockorager/vaxis"
|
||||
"git.sr.ht/~rockorager/vaxis/widgets/term"
|
||||
)
|
||||
|
||||
type HasTerminal interface {
|
||||
Terminal() *Terminal
|
||||
}
|
||||
|
||||
type Terminal struct {
|
||||
closed int32
|
||||
visible int32 // visible if >0
|
||||
cmd *exec.Cmd
|
||||
ctx *ui.Context
|
||||
focus bool
|
||||
vterm *term.Model
|
||||
running bool
|
||||
|
||||
OnClose func(err error)
|
||||
OnEvent func(event vaxis.Event) bool
|
||||
OnStart func()
|
||||
OnTitle func(title string)
|
||||
}
|
||||
|
||||
func NewTerminal(cmd *exec.Cmd) (*Terminal, error) {
|
||||
term := &Terminal{
|
||||
cmd: cmd,
|
||||
vterm: term.New(),
|
||||
visible: 1,
|
||||
}
|
||||
term.vterm.OSC8 = config.General.EnableOSC8
|
||||
term.vterm.TERM = config.General.Term
|
||||
return term, nil
|
||||
}
|
||||
|
||||
func (term *Terminal) Close() {
|
||||
term.closeErr(nil)
|
||||
}
|
||||
|
||||
// TODO: replace with atomic.Bool when min go version will have it (1.19+)
|
||||
const closed int32 = 1
|
||||
|
||||
func (term *Terminal) isClosed() bool {
|
||||
return atomic.LoadInt32(&term.closed) == closed
|
||||
}
|
||||
|
||||
func (term *Terminal) closeErr(err error) {
|
||||
if atomic.SwapInt32(&term.closed, closed) == closed {
|
||||
return
|
||||
}
|
||||
if term.vterm != nil {
|
||||
// Stop receiving events
|
||||
term.vterm.Detach()
|
||||
term.vterm.Close()
|
||||
if term.ctx != nil {
|
||||
term.ctx.HideCursor()
|
||||
}
|
||||
}
|
||||
if term.OnClose != nil {
|
||||
term.OnClose(err)
|
||||
}
|
||||
ui.Invalidate()
|
||||
}
|
||||
|
||||
func (term *Terminal) Destroy() {
|
||||
// If we destroy, we don't want to call the OnClose callback
|
||||
term.OnClose = nil
|
||||
term.closeErr(nil)
|
||||
}
|
||||
|
||||
func (term *Terminal) Invalidate() {
|
||||
ui.Invalidate()
|
||||
}
|
||||
|
||||
func (term *Terminal) Draw(ctx *ui.Context) {
|
||||
if ctx.Width() == 0 || ctx.Height() == 0 {
|
||||
return
|
||||
}
|
||||
term.ctx = ctx
|
||||
if !term.running && term.cmd != nil {
|
||||
term.vterm.Attach(term.HandleEvent)
|
||||
w, h := ctx.Window().Size()
|
||||
if err := term.vterm.StartWithSize(term.cmd, w, h); err != nil {
|
||||
log.Errorf("error running terminal: %v", err)
|
||||
term.closeErr(err)
|
||||
return
|
||||
}
|
||||
term.running = true
|
||||
if term.OnStart != nil {
|
||||
term.OnStart()
|
||||
}
|
||||
}
|
||||
term.vterm.Draw(ctx.Window())
|
||||
}
|
||||
|
||||
func (term *Terminal) Show(visible bool) {
|
||||
if visible {
|
||||
atomic.StoreInt32(&term.visible, 1)
|
||||
} else {
|
||||
atomic.StoreInt32(&term.visible, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (term *Terminal) Terminal() *Terminal {
|
||||
return term
|
||||
}
|
||||
|
||||
func (term *Terminal) MouseEvent(localX int, localY int, event vaxis.Event) {
|
||||
ev, ok := event.(vaxis.Mouse)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if term.OnEvent != nil {
|
||||
term.OnEvent(ev)
|
||||
}
|
||||
if term.isClosed() {
|
||||
return
|
||||
}
|
||||
ev.Row = localY
|
||||
ev.Col = localX
|
||||
term.vterm.Update(ev)
|
||||
}
|
||||
|
||||
func (term *Terminal) Focus(focus bool) {
|
||||
if term.isClosed() {
|
||||
return
|
||||
}
|
||||
term.focus = focus
|
||||
if term.focus {
|
||||
term.vterm.Focus()
|
||||
} else {
|
||||
term.vterm.Blur()
|
||||
}
|
||||
}
|
||||
|
||||
// HandleEvent is used to watch the underlying terminal events
|
||||
func (t *Terminal) HandleEvent(ev vaxis.Event) {
|
||||
if t.isClosed() {
|
||||
return
|
||||
}
|
||||
switch ev := ev.(type) {
|
||||
case vaxis.Redraw:
|
||||
if atomic.LoadInt32(&t.visible) > 0 {
|
||||
ui.Invalidate()
|
||||
}
|
||||
case term.EventTitle:
|
||||
if t.OnTitle != nil {
|
||||
t.OnTitle(string(ev))
|
||||
}
|
||||
case term.EventClosed:
|
||||
t.Close()
|
||||
ui.Invalidate()
|
||||
case term.EventBell:
|
||||
aerc.Beep()
|
||||
}
|
||||
}
|
||||
|
||||
func (term *Terminal) Event(event vaxis.Event) bool {
|
||||
if term.OnEvent != nil {
|
||||
if term.OnEvent(event) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if term.isClosed() {
|
||||
return false
|
||||
}
|
||||
term.vterm.Update(event)
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user