mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-25 09:48:09 +02:00
Start work on proper TUI support
This commit is contained in:
135
tools/tui/loop.go
Normal file
135
tools/tui/loop.go
Normal file
@@ -0,0 +1,135 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user