diff --git a/tools/cmd/icat/main.go b/tools/cmd/icat/main.go index 271537887..b89605c4d 100644 --- a/tools/cmd/icat/main.go +++ b/tools/cmd/icat/main.go @@ -147,6 +147,22 @@ func on_detect_timeout(timer_id loop.IdType) error { func on_initialize() (string, error) { var iid uint32 + sz, err := lp.ScreenSize() + if err != nil { + return "", fmt.Errorf("Failed to query terminal for screen size with error: %w", err) + } + if sz.WidthPx == 0 || sz.HeightPx == 0 { + return "", fmt.Errorf("Terminal does not support reporting screen sizes in pixels, use a terminal such as kitty, WezTerm, Konsole, etc. that does.") + } + if opts.Clear { + cc := &graphics.GraphicsCommand{} + cc.SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_free_visible) + cc.WriteWithPayloadToLoop(lp, nil) + } + if opts.TransferMode != "detect" { + return "", nil + } + query_in_flight = true lp.AddTimer(time.Duration(opts.DetectionTimeout*float64(time.Second)), false, on_detect_timeout) g := func(t graphics.GRT_t, payload string) uint32 { @@ -157,13 +173,6 @@ func on_initialize() (string, error) { g1.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(payload)) return iid } - sz, err := lp.ScreenSize() - if err != nil { - return "", fmt.Errorf("Failed to query terminal for screen size with error: %w", err) - } - if sz.WidthPx == 0 || sz.HeightPx == 0 { - return "", fmt.Errorf("Terminal does not support reporting screen sizes via the TIOCGWINSZ ioctl") - } keep_going.Store(true) screen_size = sz if !opts.DetectSupport && num_of_items > 0 { @@ -207,11 +216,6 @@ func on_query_finished() (err error) { if transfer_by_stream != supported { return fmt.Errorf("This terminal emulator does not support the graphics protocol, use a terminal emulator such as kitty that does support it") } - if opts.Clear { - cc := &graphics.GraphicsCommand{} - cc.SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_free_visible) - cc.WriteWithPayloadToLoop(lp, nil) - } if opts.DetectSupport { switch { case transfer_by_memory == supported: @@ -224,10 +228,7 @@ func on_query_finished() (err error) { quit_loop() return } - if num_of_items <= 0 { - quit_loop() - } - return + return on_wakeup() } func on_query_response(g *graphics.GraphicsCommand) (err error) { @@ -290,6 +291,9 @@ func quit_loop() { } func on_wakeup() error { + if query_in_flight { + return nil + } have_more := true for have_more { select { @@ -297,9 +301,9 @@ func on_wakeup() error { num_of_items-- if imgd.err != nil { print_error("Failed to process \x1b[31m%s\x1b[39m: %v\r\n", imgd.source_name, imgd.err) - continue + } else { + transmit_image(imgd) } - lp.QueueWriteString("Processed " + imgd.source_name + "\r\n") default: have_more = false } diff --git a/tools/cmd/icat/process_images.go b/tools/cmd/icat/process_images.go index ad2c9e82d..c6f42bfc7 100644 --- a/tools/cmd/icat/process_images.go +++ b/tools/cmd/icat/process_images.go @@ -135,14 +135,19 @@ func (self *opened_input) Release() { } } +type image_frame struct { + filename string + in_memory_bytes []byte + filename_is_temporary bool + width, height int +} + type image_data struct { canvas_width, canvas_height int format_uppercase string available_width, available_height int needs_scaling, needs_conversion bool - filename string - in_memory_bytes []byte - filename_is_temporary bool + frames []image_frame // for error reporting err error @@ -150,6 +155,7 @@ type image_data struct { } func set_basic_metadata(imgd *image_data) { + imgd.frames = make([]image_frame, 0, 32) imgd.available_width = int(screen_size.WidthPx) imgd.available_height = 10 * imgd.canvas_height if place != nil { @@ -173,12 +179,15 @@ func report_error(source_name, msg string, err error) { func make_output_from_input(imgd *image_data, f *opened_input) { bb, ok := f.file.(*BytesBuf) + frame := &imgd.frames[0] + frame.width = imgd.canvas_width + frame.height = imgd.canvas_height if ok { - imgd.in_memory_bytes = bb.data + frame.in_memory_bytes = bb.data } else { - imgd.filename = f.file.(*os.File).Name() + frame.filename = f.file.(*os.File).Name() if f.name_to_unlink != "" { - imgd.filename_is_temporary = true + frame.filename_is_temporary = true f.name_to_unlink = "" } } diff --git a/tools/cmd/icat/transmit.go b/tools/cmd/icat/transmit.go new file mode 100644 index 000000000..a54919c62 --- /dev/null +++ b/tools/cmd/icat/transmit.go @@ -0,0 +1,100 @@ +// License: GPLv3 Copyright: 2022, Kovid Goyal, + +package icat + +import ( + "errors" + "fmt" + "io" + "kitty/tools/tui/graphics" + "kitty/tools/utils/shm" + "os" +) + +var _ = fmt.Print + +func gc_for_image(imgd *image_data) *graphics.GraphicsCommand { +} + +func transmit_shm(imgd *image_data, frame_num int, frame *image_frame) error { + var data_size int + if frame.in_memory_bytes == nil { + f, err := os.Open(frame.filename) + if err != nil { + return err + } + defer f.Close() + sz, _ := f.Seek(0, io.SeekEnd) + f.Seek(0, io.SeekStart) + mmap, err := shm.CreateTemp("icat-*", uint64(sz)) + if err != nil { + return err + } + defer mmap.Close() + dest := mmap.Slice() + for len(dest) > 0 { + n, err := f.Read(dest) + dest = dest[n:] + if err != nil { + if errors.Is(err, io.EOF) { + break + } + mmap.Unlink() + return err + } + } + data_size = len(mmap.Slice()) - len(dest) + } else { + mmap, err := shm.CreateTemp("icat-*", uint64(len(frame.in_memory_bytes))) + if err != nil { + return err + } + defer mmap.Close() + data_size = copy(mmap.Slice(), frame.in_memory_bytes) + } +} + +func transmit_file(imgd *image_data, frame_num int, frame *image_frame) error { +} + +func transmit_stream(imgd *image_data, frame_num int, frame *image_frame) error { +} + +func transmit_image(imgd *image_data) { + defer func() { + for _, frame := range imgd.frames { + if frame.filename_is_temporary && frame.filename != "" { + os.Remove(frame.filename) + frame.filename = "" + } + frame.in_memory_bytes = nil + } + }() + var f func(*image_data, int, *image_frame) error + if opts.TransferMode != "detect" { + switch opts.TransferMode { + case "file": + f = transmit_file + case "memory": + f = transmit_shm + case "stream": + f = transmit_stream + return + } + } + if f == nil && transfer_by_memory == supported && imgd.frames[0].in_memory_bytes != nil { + f = transmit_shm + } + if f == nil && transfer_by_file == supported { + f = transmit_file + } + if f == nil { + f = transmit_stream + } + for frame_num, frame := range imgd.frames { + err := f(imgd, frame_num, &frame) + if err != nil { + print_error("Failed to transmit %s with error: %v", imgd.source_name, err) + } + } +}