func inputUntilClosed(in io.Reader) (<-chan vt100.Command, <-chan error) { buf := bufio.NewReaderSize(in, 512) cmds, errs := make(chan vt100.Command, 10), make(chan error) go func() { for { cmd, err := vt100.Decode(buf) if err == io.EOF { close(cmds) close(errs) return } if err != nil { errs <- err continue } cmds <- cmd } }() return cmds, errs }
func main() { flag.Parse() origAttr, err := makeRaw(os.Stdout.Fd()) if err != nil { panic(err) } defer func() { if err := tcsetattr(os.Stdout.Fd(), origAttr); err != nil { glog.Error(err) // Nothing much we can do about this error. } }() c := exec.Command("nethack") c.Env = append(c.Env, os.Environ()...) // I have no idea if this is right, but it seems to work. c.Env = append(c.Env, "TERM=xterm") pty, err := pty.Start(c) if err != nil { panic(err) } v := vt{vt100.NewVT100(24, 80), new(sync.Mutex)} vtexport.Export("/debug/vt100", v.VT100, v) server := http.Server{ Addr: fmt.Sprintf(":%d", port), Handler: http.DefaultServeMux, } vReaderRaw, vWriter := io.Pipe() vReader := bufio.NewReader(vReaderRaw) dupOut, err := ioutil.TempFile("", "nh_output.txt") if err != nil { panic(err) } exit := make(chan struct{}, 0) go func() { for { _, err := io.Copy(pty, os.Stdin) if err != nil { if err != io.EOF { glog.Error(err) } return } } }() go func() { defer func() { exit <- struct{}{} }() multi := io.MultiWriter(os.Stdout, dupOut, vWriter) for { _, err := io.Copy(multi, pty) if err != nil { if err != io.EOF { glog.Error(err) } return } } }() go func() { for { cmd, err := vt100.Decode(vReader) if err == nil { v.Lock() err = v.Process(cmd) v.Unlock() } if err == nil { continue } if _, isUnsupported := err.(vt100.UnsupportedError); isUnsupported { // This gets exported through an expvar. continue } if err != io.EOF { glog.Error(err) } return } }() downServer, err := httpdown.HTTP{ StopTimeout: time.Second * 5, }.ListenAndServe(&server) if err != nil { panic(err) } <-exit downServer.Stop() downServer.Wait() }