Work on implementing ttyio via TUI
This commit is contained in:
parent
0913b64c6b
commit
2ffba1c422
@ -5,6 +5,7 @@ package at
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@ -128,6 +129,34 @@ type rc_io_data struct {
|
||||
send_keypresses bool
|
||||
string_response_is_err bool
|
||||
timeout time.Duration
|
||||
|
||||
pending_chunks [][]byte
|
||||
}
|
||||
|
||||
func (self *rc_io_data) next_chunk(limit_size bool) (chunk []byte, err error) {
|
||||
if len(self.pending_chunks) > 0 {
|
||||
chunk = self.pending_chunks[0]
|
||||
copy(self.pending_chunks, self.pending_chunks[1:])
|
||||
self.pending_chunks = self.pending_chunks[:len(self.pending_chunks)-1]
|
||||
return
|
||||
}
|
||||
block, err := self.next_block(self.rc, self.serializer)
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
const limit = 2048
|
||||
if !limit_size || len(block) < limit {
|
||||
chunk = block
|
||||
return
|
||||
}
|
||||
chunk = block[:limit]
|
||||
block = block[limit:]
|
||||
for len(block) > 0 {
|
||||
self.pending_chunks = append(self.pending_chunks, block[:limit])
|
||||
block = block[limit:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func get_response(do_io func(io_data *rc_io_data) ([]byte, error), io_data *rc_io_data) (ans *Response, err error) {
|
||||
@ -137,6 +166,9 @@ func get_response(do_io func(io_data *rc_io_data) ([]byte, error), io_data *rc_i
|
||||
io_data.rc.Payload = nil
|
||||
io_data.rc.CancelAsync = true
|
||||
io_data.rc.NoResponse = true
|
||||
io_data.next_block = func(rc *utils.RemoteControlCmd, serializer serializer_func) ([]byte, error) {
|
||||
return serializer(rc)
|
||||
}
|
||||
_, err = do_io(io_data)
|
||||
}
|
||||
return
|
||||
|
||||
@ -63,7 +63,7 @@ func run_CMD_NAME(cmd *cobra.Command, args []string) (err error) {
|
||||
timeout: time.Duration(timeout * float64(time.Second)),
|
||||
string_response_is_err: STRING_RESPONSE_IS_ERROR,
|
||||
next_block: func(rc *utils.RemoteControlCmd, serializer serializer_func) ([]byte, error) {
|
||||
return make([]byte, 0), nil
|
||||
return serializer(rc)
|
||||
},
|
||||
}
|
||||
err = create_payload_CMD_NAME(&io_data, args)
|
||||
|
||||
@ -2,6 +2,79 @@
|
||||
|
||||
package at
|
||||
|
||||
func do_tty_io(io_data *rc_io_data) (serialized_response []byte, err error) {
|
||||
import (
|
||||
"kitty/tools/tui"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func do_chunked_io(io_data *rc_io_data) (serialized_response []byte, err error) {
|
||||
serialized_response = make([]byte, 0)
|
||||
loop, err := tui.CreateLoop()
|
||||
loop.NoAlternateScreen()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var last_received_data_at time.Time
|
||||
|
||||
check_for_timeout := func(loop *tui.Loop, timer_id tui.TimerId) error {
|
||||
if time.Now().Sub(last_received_data_at) > io_data.timeout {
|
||||
return os.ErrDeadlineExceeded
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
transition_to_read := func() {
|
||||
if io_data.rc.NoResponse {
|
||||
loop.Quit(0)
|
||||
}
|
||||
last_received_data_at = time.Now()
|
||||
loop.AddTimer(10*time.Millisecond, true, check_for_timeout)
|
||||
}
|
||||
|
||||
loop.OnReceivedData = func(loop *tui.Loop, data []byte) error {
|
||||
last_received_data_at = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
loop.OnInitialize = func(loop *tui.Loop) (string, error) {
|
||||
chunk, err := io_data.next_chunk(true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(chunk) == 0 {
|
||||
transition_to_read()
|
||||
} else {
|
||||
loop.QueueWriteBytes(chunk)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
loop.OnWriteComplete = func(loop *tui.Loop) error {
|
||||
chunk, err := io_data.next_chunk(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(chunk) == 0 {
|
||||
transition_to_read()
|
||||
} else {
|
||||
loop.QueueWriteBytes(chunk)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
loop.OnRCResponse = func(loop *tui.Loop, raw []byte) error {
|
||||
serialized_response = raw
|
||||
loop.Quit(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = loop.Run()
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func do_tty_io(io_data *rc_io_data) (serialized_response []byte, err error) {
|
||||
return do_chunked_io(io_data)
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"kitty/tools/tty"
|
||||
@ -75,7 +76,7 @@ type Loop struct {
|
||||
|
||||
// Called when the terminal has been fully setup. Any string returned is sent to
|
||||
// the terminal on shutdown
|
||||
OnInitialize func(loop *Loop) string
|
||||
OnInitialize func(loop *Loop) (string, error)
|
||||
|
||||
// Called when a key event happens
|
||||
OnKeyEvent func(loop *Loop, event *KeyEvent) error
|
||||
@ -88,6 +89,12 @@ type Loop struct {
|
||||
|
||||
// Called when writing is done
|
||||
OnWriteComplete func(loop *Loop) error
|
||||
|
||||
// Called when a response to an rc command is received
|
||||
OnRCResponse func(loop *Loop, data []byte) error
|
||||
|
||||
// Called when any input form tty is received
|
||||
OnReceivedData func(loop *Loop, data []byte) error
|
||||
}
|
||||
|
||||
func (self *Loop) update_screen_size() error {
|
||||
@ -146,6 +153,9 @@ func (self *Loop) handle_osc(raw []byte) error {
|
||||
}
|
||||
|
||||
func (self *Loop) handle_dcs(raw []byte) error {
|
||||
if self.OnRCResponse != nil && bytes.HasPrefix(raw, []byte("@kitty-cmd")) {
|
||||
return self.OnRCResponse(self, raw[len("@kitty-cmd"):])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -327,7 +337,10 @@ func (self *Loop) Run() (err error) {
|
||||
self.queue_write_to_tty(self.terminal_options.SetStateEscapeCodes())
|
||||
finalizer := ""
|
||||
if self.OnInitialize != nil {
|
||||
finalizer = self.OnInitialize(self)
|
||||
finalizer, err = self.OnInitialize(self)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@ -393,6 +406,12 @@ func (self *Loop) Run() (err error) {
|
||||
return err
|
||||
}
|
||||
if num_read > 0 {
|
||||
if self.OnReceivedData != nil {
|
||||
err = self.OnReceivedData(self, read_buf[:num_read])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = self.escape_code_parser.Parse(read_buf[:num_read])
|
||||
if err != nil {
|
||||
return err
|
||||
@ -419,6 +438,10 @@ func (self *Loop) QueueWriteString(data string) {
|
||||
self.queue_write_to_tty([]byte(data))
|
||||
}
|
||||
|
||||
func (self *Loop) QueueWriteBytes(data []byte) {
|
||||
self.queue_write_to_tty(data)
|
||||
}
|
||||
|
||||
func (self *Loop) ExitCode() int {
|
||||
return self.exit_code
|
||||
}
|
||||
|
||||
@ -27,9 +27,9 @@ func ReadPassword(prompt string, kill_if_signaled bool) (password string, err er
|
||||
return
|
||||
}
|
||||
|
||||
loop.OnInitialize = func(loop *Loop) string {
|
||||
loop.OnInitialize = func(loop *Loop) (string, error) {
|
||||
loop.QueueWriteString(prompt)
|
||||
return "\r\n"
|
||||
return "\r\n", nil
|
||||
}
|
||||
|
||||
loop.OnText = func(loop *Loop, text string, from_key_event bool, in_bracketed_paste bool) error {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user