diff --git a/cmd/kakiko/main.go b/cmd/kakiko/main.go index 2aa6d03..15e9c80 100644 --- a/cmd/kakiko/main.go +++ b/cmd/kakiko/main.go @@ -2,123 +2,13 @@ package main import ( "os" - "os/exec" - "os/signal" - "syscall" - "github.com/creack/pty" - - "tea.kareha.org/lab/kakiko/internal/console" + "tea.kareha.org/lab/kakiko/internal/fep" + "tea.kareha.org/lab/kakiko/internal/skk" ) -var defaultCommand []string = []string{ - "/bin/sh", -} - func main() { - var cmd []string - if len(os.Args) > 1 { - cmd = os.Args[1:] - } else { - cmd = defaultCommand - } - var c *exec.Cmd - if len(cmd) < 2 { - c = exec.Command(cmd[0]) - } else { - c = exec.Command(cmd[0], cmd[1:]...) - } - - ptmx, err := pty.Start(c) - if err != nil { - panic(err) - } - - ch := make(chan os.Signal, 1) - signal.Notify(ch, syscall.SIGWINCH) - - go func() { - for range ch { - rows, cols, err := pty.Getsize(os.Stdin) - if err != nil { - panic(err) - } - pty.Setsize(ptmx, &pty.Winsize{ - Rows: uint16(rows - 1), - Cols: uint16(cols), - }) - } - }() - rows, cols, err := pty.Getsize(os.Stdin) - if err != nil { - panic(err) - } - pty.Setsize(ptmx, &pty.Winsize{ - Rows: uint16(rows - 1), - Cols: uint16(cols), - }) - - console.ScrollRange(0, rows-1) - defer console.ScrollRange(0, rows) - - console.Clear() - console.HomeCursor() - console.Raw() - defer func() { - console.Clear() - console.HomeCursor() - console.Cooked() - console.ShowCursor() - }() - - go func() { - for { - b := make([]byte, 1) - n, err := os.Stdin.Read(b) - if err != nil { - return - } - if n == 0 { - continue - } - - processed := skkProcess(b) - - _, err = ptmx.Write(processed) - if err != nil { - return - } - } - }() - - go func() { - c.Wait() - os.Exit(0) - }() - - buf := make([]byte, 1024) - for { - n, err := ptmx.Read(buf) - if err != nil { - return - } - - os.Stdout.Write(buf[:n]) - - drawStatus() - } -} - -func drawStatus() { - _, h := console.Size() - console.SaveCursor() - console.HideCursor() - console.MoveCursor(0, h-1) - console.Print("Hello, World!") - console.ShowCursor() - console.LoadCursor() -} - -func skkProcess(b []byte) []byte { - return b + f := fep.Init(os.Args, skk.Process, skk.Status) + defer f.Finish() + f.Main() } diff --git a/internal/console/console.go b/internal/console/console.go index 0f70b36..e6287e1 100644 --- a/internal/console/console.go +++ b/internal/console/console.go @@ -26,6 +26,7 @@ func Cooked() { panic("invalid state") } term.Restore(int(os.Stdin.Fd()), state) + state = nil } func Clear() { @@ -75,3 +76,7 @@ func LoadCursor() { func ScrollRange(top, bottom int) { fmt.Printf("\x1b[%d;%dr", top+1, bottom) } + +func ClearLine() { + fmt.Print("\x1b[K") +} diff --git a/internal/fep/fep.go b/internal/fep/fep.go new file mode 100644 index 0000000..31f6497 --- /dev/null +++ b/internal/fep/fep.go @@ -0,0 +1,138 @@ +package fep + +import ( + "os" + "os/exec" + "os/signal" + "syscall" + + "github.com/creack/pty" + + "tea.kareha.org/lab/kakiko/internal/console" +) + +type Process func(b []byte) []byte +type Status func() string + +const defaultCommand = "/bin/sh" +const bufferSize = 1024 + +type FEP struct { + fd *os.File + process Process + status Status +} + +func (f *FEP) updateSize() { + rows, cols, err := pty.Getsize(os.Stdin) + if err != nil { + panic(err) + } + pty.Setsize(f.fd, &pty.Winsize{ + Rows: uint16(rows - 1), + Cols: uint16(cols), + }) +} + +func Init(args []string, process Process, status Status) *FEP { + var command string = defaultCommand + var arguments []string + if len(args) > 1 { + command = args[1] + } + if len(args) > 2 { + arguments = args[2:] + } + var c = exec.Command(command, arguments...) + fd, err := pty.Start(c) + if err != nil { + panic(err) + } + + f := &FEP{ + fd: fd, + process: process, + status: status, + } + + f.updateSize() + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGWINCH) + go func() { + for range ch { + f.updateSize() + } + }() + + _, h := console.Size() + console.ScrollRange(0, h-1) + + console.Clear() + console.HomeCursor() + console.Raw() + + go func() { + for { + b := make([]byte, 1) + n, err := os.Stdin.Read(b) + if err != nil { + return + } + if n == 0 { + continue + } + + processed := f.process(b) + + _, err = fd.Write(processed) + if err != nil { + return + } + } + }() + + go func() { + c.Wait() + os.Exit(0) + }() + + return f +} + +func (f *FEP) Finish() { + _, h := console.Size() + console.ScrollRange(0, h) + + console.Clear() + console.HomeCursor() + console.Cooked() + console.ShowCursor() +} + +func (f *FEP) drawStatus() { + _, h := console.Size() + console.SaveCursor() + console.HideCursor() + console.MoveCursor(0, h-1) + + status := f.status() + console.Print(status) + console.ClearLine() + + console.ShowCursor() + console.LoadCursor() +} + +func (f *FEP) Main() { + buf := make([]byte, bufferSize) + for { + n, err := f.fd.Read(buf) + if err != nil { + return + } + + os.Stdout.Write(buf[:n]) + + f.drawStatus() + } +} diff --git a/internal/skk/skk.go b/internal/skk/skk.go new file mode 100644 index 0000000..a9584e3 --- /dev/null +++ b/internal/skk/skk.go @@ -0,0 +1,18 @@ +package skk + +import ( + "fmt" +) + +// XXX dummy +var count int = 0 + +func Process(b []byte) []byte { + count++ // XXX dummy + return b +} + +func Status() string { + // XXX dummy + return fmt.Sprintf("Hello, World! (%d)", count) +}