diff --git a/tools/cmd/icat/main.go b/tools/cmd/icat/main.go index b89605c4d..4d36ebb05 100644 --- a/tools/cmd/icat/main.go +++ b/tools/cmd/icat/main.go @@ -159,11 +159,6 @@ func on_initialize() (string, error) { 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 { iid += 1 @@ -181,9 +176,13 @@ func on_initialize() (string, error) { go run_worker() } } + if opts.TransferMode != "detect" { + return "", nil + } + query_in_flight = true direct_query_id = g(graphics.GRT_transmission_direct, "123") - tf, err := graphics.MakeTemp() + tf, err := graphics.CreateTempInRAM() if err == nil { file_query_id = g(graphics.GRT_transmission_tempfile, tf.Name()) temp_files_to_delete = append(temp_files_to_delete, tf.Name()) @@ -397,6 +396,10 @@ func main(cmd *cli.Command, o *Options, args []string) (rc int, err error) { return } if opts.Hold { + fmt.Print("\r") + if opts.Place != "" { + fmt.Println() + } tui.HoldTillEnter(false) } return 0, nil diff --git a/tools/cmd/icat/process_images.go b/tools/cmd/icat/process_images.go index c6f42bfc7..483ed2394 100644 --- a/tools/cmd/icat/process_images.go +++ b/tools/cmd/icat/process_images.go @@ -147,7 +147,8 @@ type image_data struct { format_uppercase string available_width, available_height int needs_scaling, needs_conversion bool - frames []image_frame + frames []*image_frame + image_number uint32 // for error reporting err error @@ -155,7 +156,7 @@ type image_data struct { } func set_basic_metadata(imgd *image_data) { - imgd.frames = make([]image_frame, 0, 32) + imgd.frames = make([]*image_frame, 0, 32) imgd.available_width = int(screen_size.WidthPx) imgd.available_height = 10 * imgd.canvas_height if place != nil { @@ -179,7 +180,8 @@ 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 := image_frame{} + imgd.frames = append(imgd.frames, &frame) frame.width = imgd.canvas_width frame.height = imgd.canvas_height if ok { diff --git a/tools/cmd/icat/transmit.go b/tools/cmd/icat/transmit.go index a54919c62..2bc0cc58b 100644 --- a/tools/cmd/icat/transmit.go +++ b/tools/cmd/icat/transmit.go @@ -3,32 +3,57 @@ package icat import ( + "bytes" "errors" "fmt" "io" "kitty/tools/tui/graphics" + "kitty/tools/utils" "kitty/tools/utils/shm" + "math/rand" "os" + "path/filepath" ) var _ = fmt.Print -func gc_for_image(imgd *image_data) *graphics.GraphicsCommand { +func gc_for_image(imgd *image_data, data_size, frame_num int, frame *image_frame) *graphics.GraphicsCommand { + gc := graphics.GraphicsCommand{} + gc.SetDataSize(uint64(data_size)).SetAction(graphics.GRT_action_transmit_and_display) + gc.SetDataWidth(uint64(frame.width)).SetDataHeight(uint64(frame.height)) + gc.SetQuiet(graphics.GRT_quiet_silent) + if z_index != 0 { + gc.SetZIndex(z_index) + } + if imgd.image_number != 0 { + gc.SetImageNumber(imgd.image_number) + } + switch imgd.format_uppercase { + case "PNG": + gc.SetFormat(graphics.GRT_format_png) + default: + gc.SetFormat(graphics.GRT_format_rgb) + case "RGBA": + gc.SetFormat(graphics.GRT_format_rgba) + } + + return &gc } -func transmit_shm(imgd *image_data, frame_num int, frame *image_frame) error { +func transmit_shm(imgd *image_data, frame_num int, frame *image_frame) (err error) { var data_size int + var mmap shm.MMap if frame.in_memory_bytes == nil { f, err := os.Open(frame.filename) if err != nil { - return err + return fmt.Errorf("Failed to open image data output file: %s with error: %w", frame.filename, err) } defer f.Close() sz, _ := f.Seek(0, io.SeekEnd) f.Seek(0, io.SeekStart) - mmap, err := shm.CreateTemp("icat-*", uint64(sz)) + mmap, err = shm.CreateTemp("icat-*", uint64(sz)) if err != nil { - return err + return fmt.Errorf("Failed to create a SHM file for transmission: %w", err) } defer mmap.Close() dest := mmap.Slice() @@ -40,24 +65,81 @@ func transmit_shm(imgd *image_data, frame_num int, frame *image_frame) error { break } mmap.Unlink() - return err + return fmt.Errorf("Failed to read data from image output data file: %w", err) } } data_size = len(mmap.Slice()) - len(dest) } else { - mmap, err := shm.CreateTemp("icat-*", uint64(len(frame.in_memory_bytes))) + mmap, err = shm.CreateTemp("icat-*", uint64(len(frame.in_memory_bytes))) if err != nil { - return err + return fmt.Errorf("Failed to create a SHM file for transmission: %w", err) } defer mmap.Close() data_size = copy(mmap.Slice(), frame.in_memory_bytes) } + gc := gc_for_image(imgd, data_size, frame_num, frame) + gc.SetTransmission(graphics.GRT_transmission_sharedmem) + gc.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(mmap.Name())) + + return nil } -func transmit_file(imgd *image_data, frame_num int, frame *image_frame) error { +func transmit_file(imgd *image_data, frame_num int, frame *image_frame) (err error) { + var data_size int + is_temp := false + fname := "" + if frame.in_memory_bytes == nil { + is_temp = frame.filename_is_temporary + fname, err = filepath.Abs(frame.filename) + if err != nil { + return fmt.Errorf("Failed to convert image data output file: %s to absolute path with error: %w", frame.filename, err) + } + frame.filename = "" // so it isnt deleted in cleanup + s, err := os.Stat(fname) + if err != nil { + return fmt.Errorf("Failed to stat() image data output file: %s with error: %w", fname, err) + } + data_size = int(s.Size()) + } else { + data_size = len(frame.in_memory_bytes) + f, err := graphics.CreateTempInRAM() + if err != nil { + return fmt.Errorf("Failed to create a temp file for image data transmission: %w", err) + } + _, err = bytes.NewBuffer(frame.in_memory_bytes).WriteTo(f) + f.Close() + if err != nil { + return fmt.Errorf("Failed to write image data to temp file for transmission: %w", err) + } + is_temp = true + fname = f.Name() + } + gc := gc_for_image(imgd, data_size, frame_num, frame) + if is_temp { + gc.SetTransmission(graphics.GRT_transmission_tempfile) + } else { + gc.SetTransmission(graphics.GRT_transmission_file) + } + gc.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(fname)) + return nil } -func transmit_stream(imgd *image_data, frame_num int, frame *image_frame) error { +func transmit_stream(imgd *image_data, frame_num int, frame *image_frame) (err error) { + data := frame.in_memory_bytes + if data == nil { + f, err := os.Open(frame.filename) + if err != nil { + return fmt.Errorf("Failed to open image data output file: %s with error: %w", frame.filename, err) + } + data, err = io.ReadAll(f) + f.Close() + if err != nil { + return fmt.Errorf("Failed to read data from image output data file: %w", err) + } + } + gc := gc_for_image(imgd, len(data), frame_num, frame) + gc.WriteWithPayloadToLoop(lp, data) + return nil } func transmit_image(imgd *image_data) { @@ -79,7 +161,6 @@ func transmit_image(imgd *image_data) { f = transmit_shm case "stream": f = transmit_stream - return } } if f == nil && transfer_by_memory == supported && imgd.frames[0].in_memory_bytes != nil { @@ -91,8 +172,11 @@ func transmit_image(imgd *image_data) { if f == nil { f = transmit_stream } + if len(imgd.frames) > 1 { + imgd.image_number = rand.Uint32() + } for frame_num, frame := range imgd.frames { - err := f(imgd, frame_num, &frame) + err := f(imgd, frame_num, frame) if err != nil { print_error("Failed to transmit %s with error: %v", imgd.source_name, err) } diff --git a/tools/tui/graphics/command.go b/tools/tui/graphics/command.go index 4d7eb6ef7..532218fcb 100644 --- a/tools/tui/graphics/command.go +++ b/tools/tui/graphics/command.go @@ -14,14 +14,25 @@ import ( "kitty/tools/tui/loop" "kitty/tools/utils" + "kitty/tools/utils/shm" ) var _ = fmt.Print -func MakeTemp() (*os.File, error) { +func CreateTemp() (*os.File, error) { return os.CreateTemp("", "tty-graphics-protocol-*") } +func CreateTempInRAM() (*os.File, error) { + if shm.SHM_DIR != "" { + f, err := os.CreateTemp(shm.SHM_DIR, "tty-graphics-protocol-*") + if err == nil { + return f, err + } + } + return CreateTemp() +} + // Enums {{{ type GRT_a int @@ -523,7 +534,7 @@ func (self *GraphicsCommand) AsAPC(payload []byte) string { } func (self *GraphicsCommand) WriteWithPayloadTo(o io.StringWriter, payload []byte) (err error) { - const compression_threshold = 1024 + const compression_threshold = 2048 if len(payload) == 0 { return self.serialize_to(o, "") } diff --git a/tools/utils/shm/specific_darwin.go b/tools/utils/shm/specific_darwin.go index ee7d44632..99222e952 100644 --- a/tools/utils/shm/specific_darwin.go +++ b/tools/utils/shm/specific_darwin.go @@ -4,3 +4,4 @@ package shm const SHM_NAME_MAX = 30 const SHM_REQUIRED_PREFIX = "" +const SHM_DIR = "" diff --git a/tools/utils/shm/specific_freebsd.go b/tools/utils/shm/specific_freebsd.go index ab218ded9..b2c7b6b88 100644 --- a/tools/utils/shm/specific_freebsd.go +++ b/tools/utils/shm/specific_freebsd.go @@ -4,3 +4,4 @@ package shm const SHM_NAME_MAX = 1023 const SHM_REQUIRED_PREFIX = "/" +const SHM_DIR = ""