func handleWinEvents(win *acme.Win) { for ev := range win.EventChan() { if ev.C2 == 'x' || ev.C2 == 'X' { if ev.Flag&2 != 0 { ev.Q0 = ev.OrigQ0 ev.Q1 = ev.OrigQ1 } win.WriteEvent(ev) if fs := strings.Fields(string(ev.Text)); len(fs) > 0 && fs[0] == "Del" { os.Exit(0) } } } }
// We would use win.ReadAll except for a bug in acme // where it crashes when reading trying to read more // than the negotiated 9P message size. func readBody(win *acme.Win) ([]byte, error) { var body []byte buf := make([]byte, 8000) for { n, err := win.Read("body", buf) if err == io.EOF { break } if err != nil { return nil, err } body = append(body, buf[0:n]...) } return body, nil }
func showCal(win *acme.Win) { cmd := exec.Command("9", "cal") out, err := cmd.StdoutPipe() if err != nil { panic(err) } go cmd.Start() if _, err := io.Copy(dataWriter{win}, out); err != nil { panic(err) } if err := cmd.Wait(); err != nil { panic(err) } win.Ctl("clean") }
// We would use io.Copy except for a bug in acme // where it crashes when reading trying to read more // than the negotiated 9P message size. func copyBody(w io.Writer, win *acme.Win) error { buf := make([]byte, 8000) for { n, err := win.Read("body", buf) if err == io.EOF { break } if err != nil { return err } if _, err := w.Write(buf[0:n]); err != nil { return fmt.Errorf("write error: %v", err) } } return nil }
func writeData(win *acme.Win, data []byte) error { if len(data) == 0 { _, err := win.Write("data", nil) return err } for len(data) > 0 { d := data if len(d) > 8000 { d = trimIncompleteRune(d[0:8000]) } n, err := win.Write("data", d) if err != nil { return err } data = data[n:] } return nil }
func events(w *acme.Win) <-chan string { c := make(chan string, 10) go func() { for e := range w.EventChan() { switch e.C2 { case 'x', 'X': // execute if string(e.Text) == "Del" { w.Ctl("delete") } w.WriteEvent(e) case 'l', 'L': // look w.Ctl("clean") c <- string(e.Text) } } w.CloseFiles() close(c) }() return c }
func blinker(w *acme.Win) chan bool { c := make(chan bool) go func() { t := time.NewTicker(300 * time.Millisecond) defer t.Stop() dirty := false for { select { case <-t.C: dirty = !dirty if dirty { w.Ctl("dirty") } else { w.Ctl("clean") } case <-c: if dirty { w.Ctl("clean") } return } } }() return c }
func main() { log.SetFlags(0) log.SetPrefix("Run: ") file := os.Getenv("samfile") if file == "" { log.Fatal("not running in acme") } id, _ := strconv.Atoi(os.Getenv("winid")) wfile, err := acme.Open(id, nil) if err != nil { log.Fatal(err) } wfile.Ctl("put") wfile.CloseFiles() wname := "/go/run/" + strings.TrimSuffix(path.Base(file), ".go") windows, _ := acme.Windows() var w *acme.Win for _, info := range windows { if info.Name == wname { ww, err := acme.Open(info.ID, nil) if err != nil { log.Fatal(err) } ww.Addr(",") ww.Write("data", nil) w = ww break } } if w == nil { ww, err := acme.New() if err != nil { log.Fatal(err) } ww.Name(wname) w = ww } w.Ctl("clean") defer w.Ctl("clean") cmd := exec.Command("go", append([]string{"run", os.Getenv("samfile")}, os.Args[1:]...)...) cmd.Stdout = bodyWriter{w} cmd.Stderr = cmd.Stdout cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} err = cmd.Start() if err != nil { w.Fprintf("body", "error starting command: %v\n", err) return } //stop := blinker(w) w.Ctl("cleartag") w.Fprintf("tag", " Kill Stack") done := make(chan bool) go func() { err := cmd.Wait() if err != nil { w.Fprintf("body", "\nerror running command: %v\n", err) } //stop <- true done <- true }() deleted := make(chan bool, 1) go func() { for e := range w.EventChan() { if e.C2 == 'x' || e.C2 == 'X' { switch string(e.Text) { case "Del": select { case deleted <- true: default: } syscall.Kill(-cmd.Process.Pid, 2) continue case "Kill": syscall.Kill(-cmd.Process.Pid, 2) continue case "Stack": syscall.Kill(-cmd.Process.Pid, 3) continue } w.WriteEvent(e) } } }() <-done w.Ctl("cleartag") select { case <-deleted: w.Ctl("delete") default: } }
func clear(win *acme.Win) { win.Addr("0,$") if _, err := win.Write("data", []byte{}); err != nil { panic(err) } }