Gocui在寻呼机程序后中断

Gocui在寻呼机程序后中断

问题描述:

I have a program based on the gocui library, which has a keybinding with a function which generates text and sends it to the pager's stdin. But after closing the pager, all previous gocui interface becomes broken, but program is still working.

So how can I safely return from pager to the gocui mode and continue the program?

Simplified example, here.

package main

import (
    "fmt"
    "log"
    "os/exec"
    "os"
    "strings"
    "github.com/jroimartin/gocui"
    "time"
    "sync"
    "bytes"
)

var (
    done = make(chan struct{})
    wg  sync.WaitGroup
)

func main() {
    g, err := gocui.NewGui(gocui.OutputNormal)
    if err != nil {
        log.Panicln(err)
    }
    defer g.Close()

    g.SetManagerFunc(layout)

    if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
        log.Panicln(err)
    }
    if err := g.SetKeybinding("", gocui.KeySpace, gocui.ModNone, pager); err != nil {
        log.Panicln(err)
    }

    wg.Add(1)
    go showTime(g)

    if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
        log.Panicln(err)
    }

    wg.Wait()
}

func layout(g *gocui.Gui) error {
    maxX, _ := g.Size()
        if v, err := g.SetView("main", -1, -1, maxX/2-1, 4); err != nil {
        if err != gocui.ErrUnknownView {
            return err
        }
        fmt.Fprintln(v, time.Now().Format("2006-01-02 15:04:05.000"))
        v.Frame = false
    }

    return nil
}

func pager(g *gocui.Gui, v *gocui.View) error {
    var buf bytes.Buffer
    for i:=0; i < 10; i++ {
        buf.WriteString(fmt.Sprintf("%d -- %s %s
", i, "example", "text"))
    }

    cmd := exec.Command("less")
    cmd.Stdin = strings.NewReader(buf.String())
    cmd.Stdout = os.Stdout
    err := cmd.Run()

    return err
}

func showTime(g *gocui.Gui) {
    defer wg.Done()

    for {
        select {
        case <-done:
            return
        case <-time.After(1 * time.Second):
            t := time.Now()

            g.Update(func(g *gocui.Gui) error {
                v, err := g.View("main")
                if err != nil {
                    return err
                }
                v.Clear()
                fmt.Fprintln(v, t.Format("2006-01-02 15:04:05.000"))
                return nil
            })
        }
    }
}

func quit(g *gocui.Gui, v *gocui.View) error {
    return gocui.ErrQuit
}

Final example is below, it has additional things and in some places looks dirty, but it works in general.

package main

import (
    "fmt"
    "log"
    "os/exec"
    "os"
    "github.com/jroimartin/gocui"
    "time"
    "sync"
)

var (
    do_quit = make(chan int)
    do_pause = make(chan int)
    show_aux = false
    wg  sync.WaitGroup
)

func main() {
    for {
        // DEBUG: fmt.Println("start gui")

        g, err := gocui.NewGui(gocui.OutputNormal)
        if err != nil {
            log.Panicln(err)
        }
        defer g.Close()

        g.SetManagerFunc(layout)
        setKeys(g)

        wg.Add(1)
        go showTime(g)

        if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
            continue        // restart gui if something goes wrong
        }
        wg.Wait()
    }
}


func layout(g *gocui.Gui) error {
    maxX, _ := g.Size()

    if v, err := g.SetView("main", -1, -1, maxX/2-1, 4); err != nil {
        if err != gocui.ErrUnknownView {
            return err
        }
        fmt.Fprintf(v, "sys: %s
", time.Now().Format("2006-01-02 15:04:05.000"))
        v.Frame = false
    }

    return nil
}

func runPager(g *gocui.Gui, v *gocui.View) error {
    do_quit <-1
    g.Close()
    cmd := exec.Command("less", "/etc/sysctl.conf")
    cmd.Stdout = os.Stdout
    err := cmd.Run()

    return err
}

func runEditor(g *gocui.Gui, v *gocui.View) error {
    do_quit <-1
    g.Close()
    cmd := exec.Command("vi", "/tmp/strace.out")
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    err := cmd.Run()

    return err
}

func runPsql(g *gocui.Gui, v *gocui.View) error {
    do_quit <-1
    g.Close()
    cmd := exec.Command("psql", "-U", "postgres")
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    err := cmd.Run()

    return err
}

func showAux(g *gocui.Gui, _ *gocui.View) error {
    if !show_aux {
        maxX, maxY := g.Size()
        if v, err := g.SetView("aux", -1, 3*maxY/5-1, maxX-1, maxY-1); err != nil {
            if err != gocui.ErrUnknownView {
                return err
            }
            fmt.Fprintln(v, "")
            v.Frame = false
        }
    } else {
        g.DeleteView("aux")
    }
    show_aux = !show_aux
    return nil
}

func showTime(g *gocui.Gui) {
    var pause = false
    defer wg.Done()

    for {
        select {
        case <-do_pause:
            pause = !pause
        case <-do_quit:
            return
        case <-time.After(1 * time.Second):
            if pause { continue }

            g.Update(func(g *gocui.Gui) error {
                v, err := g.View("main")
                if err != nil {
                    return err
                }
                v.Clear()
                fmt.Fprintf(v, "sys: %s
", time.Now().Format("2006-01-02 15:04:05.000"))

                if show_aux {
                    v, err := g.View("aux")
                    if err != nil {
                        return err
                    }
                    v.Clear()
                    fmt.Fprintf(v, "aux: %s
", time.Now().Format("2006-01-02 15:04:05.000"))
                }
                return nil
            })
        }
    }
}

func doQuit(g *gocui.Gui, v *gocui.View) error {
    do_quit <- 1
    g.Close()
    os.Exit(0)
    return gocui.ErrQuit
}

func setKeys(g *gocui.Gui) {
    if err := g.SetKeybinding("", gocui.KeyCtrlQ, gocui.ModNone, doQuit); err != nil {
        log.Panicln(err)
    }
    if err := g.SetKeybinding("", 'c', gocui.ModNone, runPager); err != nil {
        log.Panicln(err)
    }
    if err := g.SetKeybinding("", 'e', gocui.ModNone, runEditor); err != nil {
        log.Panicln(err)
    }
    if err := g.SetKeybinding("", 'p', gocui.ModNone, runPsql); err != nil {
        log.Panicln(err)
    }
    if err := g.SetKeybinding("", 'b', gocui.ModNone, showAux); err != nil {
        log.Panicln(err)
    }
}