kitty/tools/tui/loop.go
2022-11-14 15:41:49 +05:30

136 lines
2.5 KiB
Go

package tui
import (
"fmt"
"io"
"kitty/tools/tty"
"os"
"time"
)
type TerminalState struct {
alternate_screen, grab_mouse bool
}
type Loop struct {
controlling_term *tty.Term
keep_going bool
flush_write_buf bool
write_buf []byte
}
func CreateLoop() (*Loop, error) {
l := Loop{controlling_term: nil}
return &l, nil
}
func (self *Loop) Run() (err error) {
signal_read_file, signal_write_file, err := os.Pipe()
if err != nil {
return err
}
defer func() {
signal_read_file.Close()
signal_write_file.Close()
}()
sigchnl := make(chan os.Signal, 256)
reset_signals := notify_signals(sigchnl, SIGINT, SIGTERM, SIGTSTP, SIGHUP)
defer reset_signals()
go func() {
for {
s := <-sigchnl
if write_signal(signal_write_file, s) != nil {
break
}
}
}()
controlling_term, err := tty.OpenControllingTerm()
if err != nil {
return err
}
self.controlling_term = controlling_term
defer func() {
self.controlling_term.RestoreAndClose()
self.controlling_term = nil
}()
err = self.controlling_term.ApplyOperations(tty.TCSANOW, tty.SetRaw)
if err != nil {
return nil
}
var selector Select
selector.RegisterRead(int(signal_read_file.Fd()))
selector.RegisterRead(controlling_term.Fd())
self.keep_going = true
self.flush_write_buf = true
defer func() {
if self.flush_write_buf {
self.flush()
}
}()
for self.keep_going {
num_ready, err := selector.WaitForever()
if err != nil {
return fmt.Errorf("Failed to call select() with error: %w", err)
}
if num_ready == 0 {
continue
}
}
return nil
}
func (self *Loop) write() error {
if len(self.write_buf) == 0 || self.controlling_term == nil {
return nil
}
n, err := self.controlling_term.Write(self.write_buf)
if err != nil {
return err
}
if n == 0 {
return io.EOF
}
remainder := self.write_buf[n:]
if len(remainder) > 0 {
self.write_buf = self.write_buf[:len(remainder)]
copy(self.write_buf, remainder)
} else {
self.write_buf = self.write_buf[:0]
}
return nil
}
func (self *Loop) flush() error {
var selector Select
if self.controlling_term == nil {
return nil
}
selector.RegisterWrite(self.controlling_term.Fd())
deadline := time.Now().Add(2 * time.Second)
for len(self.write_buf) > 0 {
timeout := deadline.Sub(time.Now())
if timeout < 0 {
break
}
num_ready, err := selector.Wait(timeout)
if err != nil {
return err
}
if num_ready > 0 && selector.IsReadyToWrite(self.controlling_term.Fd()) {
err = self.write()
if err != nil {
return err
}
}
}
return nil
}