Simplify icat code and write to STDOUT rather than the terminal device
The terminal device is now used only for detection.
This commit is contained in:
parent
2205bf4426
commit
3743ae50e7
121
tools/cmd/icat/detect.go
Normal file
121
tools/cmd/icat/detect.go
Normal file
@ -0,0 +1,121 @@
|
||||
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||
|
||||
package icat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"kitty/tools/tui/graphics"
|
||||
"kitty/tools/tui/loop"
|
||||
"kitty/tools/utils"
|
||||
"kitty/tools/utils/shm"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
func DetectSupport(timeout time.Duration) (memory, files, direct bool, err error) {
|
||||
var direct_query_id, file_query_id, memory_query_id uint32
|
||||
lp, e := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
||||
if e != nil {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
print_error := func(format string, args ...any) {
|
||||
lp.Println(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
lp.OnInitialize = func() (string, error) {
|
||||
var iid uint32
|
||||
lp.AddTimer(timeout, false, func(loop.IdType) error {
|
||||
return fmt.Errorf("Timed out waiting for a response form the terminal: %w", os.ErrDeadlineExceeded)
|
||||
})
|
||||
|
||||
g := func(t graphics.GRT_t, payload string) uint32 {
|
||||
iid += 1
|
||||
g1 := &graphics.GraphicsCommand{}
|
||||
g1.SetTransmission(t).SetAction(graphics.GRT_action_query).SetImageId(iid).SetDataWidth(1).SetDataHeight(1).SetFormat(
|
||||
graphics.GRT_format_rgb).SetDataSize(uint64(len(payload)))
|
||||
g1.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(payload))
|
||||
return iid
|
||||
}
|
||||
|
||||
direct_query_id = g(graphics.GRT_transmission_direct, "123")
|
||||
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())
|
||||
tf.Write([]byte{1, 2, 3})
|
||||
tf.Close()
|
||||
} else {
|
||||
print_error("Failed to create temporary file for data transfer, file based transfer is disabled. Error: %v", err)
|
||||
}
|
||||
sf, err := shm.CreateTemp("icat-", 3)
|
||||
if err == nil {
|
||||
memory_query_id = g(graphics.GRT_transmission_sharedmem, sf.Name())
|
||||
shm_files_to_delete = append(shm_files_to_delete, sf)
|
||||
copy(sf.Slice(), []byte{1, 2, 3})
|
||||
sf.Close()
|
||||
} else {
|
||||
var ens *shm.ErrNotSupported
|
||||
if !errors.As(err, &ens) {
|
||||
print_error("Failed to create SHM for data transfer, memory based transfer is disabled. Error: %v", err)
|
||||
}
|
||||
}
|
||||
lp.QueueWriteString("\x1b[c")
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
lp.OnEscapeCode = func(etype loop.EscapeCodeType, payload []byte) (err error) {
|
||||
switch etype {
|
||||
case loop.CSI:
|
||||
if len(payload) > 3 && payload[0] == '?' && payload[len(payload)-1] == 'c' {
|
||||
lp.Quit(0)
|
||||
return nil
|
||||
}
|
||||
case loop.APC:
|
||||
g := graphics.GraphicsCommandFromAPC(payload)
|
||||
if g != nil {
|
||||
if g.ResponseMessage() == "OK" {
|
||||
switch g.ImageId() {
|
||||
case direct_query_id:
|
||||
direct = true
|
||||
case file_query_id:
|
||||
files = true
|
||||
case memory_query_id:
|
||||
memory = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
lp.OnKeyEvent = func(event *loop.KeyEvent) error {
|
||||
if event.MatchesPressOrRepeat("ctrl+c") {
|
||||
event.Handled = true
|
||||
print_error("Waiting for response from terminal, aborting now could lead to corruption")
|
||||
}
|
||||
if event.MatchesPressOrRepeat("ctrl+z") {
|
||||
event.Handled = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err = lp.Run()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ds := lp.DeathSignalName()
|
||||
if ds != "" {
|
||||
fmt.Println("Killed by signal: ", ds)
|
||||
lp.KillIfSignalled()
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
package icat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
@ -16,11 +15,12 @@ import (
|
||||
"kitty/tools/tty"
|
||||
"kitty/tools/tui"
|
||||
"kitty/tools/tui/graphics"
|
||||
"kitty/tools/tui/loop"
|
||||
"kitty/tools/utils"
|
||||
"kitty/tools/utils/images"
|
||||
"kitty/tools/utils/shm"
|
||||
"kitty/tools/utils/style"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
@ -30,7 +30,6 @@ type Place struct {
|
||||
}
|
||||
|
||||
var opts *Options
|
||||
var lp *loop.Loop
|
||||
var place *Place
|
||||
var z_index int32
|
||||
var remove_alpha *images.NRGBColor
|
||||
@ -48,15 +47,17 @@ var transfer_by_file, transfer_by_memory, transfer_by_stream transfer_mode
|
||||
|
||||
var temp_files_to_delete []string
|
||||
var shm_files_to_delete []shm.MMap
|
||||
var direct_query_id, file_query_id, memory_query_id uint32
|
||||
var stderr_is_tty bool
|
||||
var query_in_flight bool
|
||||
var stream_response string
|
||||
var files_channel chan input_arg
|
||||
var output_channel chan *image_data
|
||||
var num_of_items int
|
||||
var keep_going *atomic.Bool
|
||||
var screen_size loop.ScreenSize
|
||||
var screen_size *unix.Winsize
|
||||
|
||||
func send_output(imgd *image_data) {
|
||||
output_channel <- imgd
|
||||
}
|
||||
|
||||
func parse_mirror() (err error) {
|
||||
flip = opts.Mirror == "both" || opts.Mirror == "vertical"
|
||||
@ -128,143 +129,8 @@ func parse_place() (err error) {
|
||||
}
|
||||
|
||||
func print_error(format string, args ...any) {
|
||||
if lp == nil || !stderr_is_tty {
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
fmt.Fprintln(os.Stderr)
|
||||
} else {
|
||||
lp.QueueWriteString("\r")
|
||||
lp.ClearToEndOfLine()
|
||||
for _, line := range utils.Splitlines(fmt.Sprintf(format, args...)) {
|
||||
lp.Println(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func on_detect_timeout(timer_id loop.IdType) error {
|
||||
if query_in_flight {
|
||||
return fmt.Errorf("Timed out waiting for a response form the terminal")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
lp.AddTimer(time.Duration(opts.DetectionTimeout*float64(time.Second)), false, on_detect_timeout)
|
||||
g := func(t graphics.GRT_t, payload string) uint32 {
|
||||
iid += 1
|
||||
g1 := &graphics.GraphicsCommand{}
|
||||
g1.SetTransmission(t).SetAction(graphics.GRT_action_query).SetImageId(iid).SetDataWidth(1).SetDataHeight(1).SetFormat(
|
||||
graphics.GRT_format_rgb).SetDataSize(uint64(len(payload)))
|
||||
g1.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(payload))
|
||||
return iid
|
||||
}
|
||||
keep_going.Store(true)
|
||||
screen_size = sz
|
||||
if !opts.DetectSupport && num_of_items > 0 {
|
||||
num_workers := utils.Max(1, utils.Min(num_of_items, runtime.NumCPU()))
|
||||
for i := 0; i < num_workers; i++ {
|
||||
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.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())
|
||||
tf.Write([]byte{1, 2, 3})
|
||||
tf.Close()
|
||||
} else {
|
||||
transfer_by_file = unsupported
|
||||
print_error("Failed to create temporary file for data transfer, file based transfer is disabled. Error: %v", err)
|
||||
}
|
||||
sf, err := shm.CreateTemp("icat-", 3)
|
||||
if err == nil {
|
||||
memory_query_id = g(graphics.GRT_transmission_sharedmem, sf.Name())
|
||||
shm_files_to_delete = append(shm_files_to_delete, sf)
|
||||
copy(sf.Slice(), []byte{1, 2, 3})
|
||||
sf.Close()
|
||||
} else {
|
||||
transfer_by_memory = unsupported
|
||||
var ens *shm.ErrNotSupported
|
||||
if !errors.As(err, &ens) {
|
||||
print_error("Failed to create SHM for data transfer, memory based transfer is disabled. Error: %v", err)
|
||||
}
|
||||
}
|
||||
lp.QueueWriteString("\x1b[c")
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func on_query_finished() (err error) {
|
||||
query_in_flight = false
|
||||
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.DetectSupport {
|
||||
switch {
|
||||
case transfer_by_memory == supported:
|
||||
print_error("memory")
|
||||
case transfer_by_file == supported:
|
||||
print_error("file")
|
||||
default:
|
||||
print_error("stream")
|
||||
}
|
||||
quit_loop()
|
||||
return
|
||||
}
|
||||
return on_wakeup()
|
||||
}
|
||||
|
||||
func on_query_response(g *graphics.GraphicsCommand) (err error) {
|
||||
var tm *transfer_mode
|
||||
switch g.ImageId() {
|
||||
case direct_query_id:
|
||||
tm = &transfer_by_stream
|
||||
case file_query_id:
|
||||
tm = &transfer_by_file
|
||||
case memory_query_id:
|
||||
tm = &transfer_by_memory
|
||||
}
|
||||
if g.ResponseMessage() == "OK" {
|
||||
*tm = supported
|
||||
} else {
|
||||
*tm = unsupported
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func on_escape_code(etype loop.EscapeCodeType, payload []byte) (err error) {
|
||||
switch etype {
|
||||
case loop.CSI:
|
||||
if len(payload) > 3 && payload[0] == '?' && payload[len(payload)-1] == 'c' {
|
||||
return on_query_finished()
|
||||
}
|
||||
case loop.APC:
|
||||
g := graphics.GraphicsCommandFromAPC(payload)
|
||||
if g != nil {
|
||||
if query_in_flight {
|
||||
return on_query_response(g)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
fmt.Fprintln(os.Stderr)
|
||||
}
|
||||
|
||||
func on_finalize() string {
|
||||
@ -281,55 +147,6 @@ func on_finalize() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
var errors_occurred bool = false
|
||||
|
||||
func quit_loop() {
|
||||
if errors_occurred {
|
||||
lp.Quit(1)
|
||||
} else {
|
||||
lp.Quit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func on_wakeup() error {
|
||||
if query_in_flight {
|
||||
return nil
|
||||
}
|
||||
have_more := true
|
||||
for have_more {
|
||||
select {
|
||||
case imgd := <-output_channel:
|
||||
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)
|
||||
} else {
|
||||
transmit_image(imgd)
|
||||
}
|
||||
default:
|
||||
have_more = false
|
||||
}
|
||||
}
|
||||
if num_of_items <= 0 && !query_in_flight {
|
||||
quit_loop()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func on_key_event(event *loop.KeyEvent) error {
|
||||
if event.MatchesPressOrRepeat("ctrl+c") {
|
||||
event.Handled = true
|
||||
if query_in_flight {
|
||||
print_error("Waiting for response from terminal, aborting now could lead to corruption")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Aborted by user")
|
||||
}
|
||||
if event.MatchesPressOrRepeat("ctrl+z") {
|
||||
event.Handled = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {
|
||||
opts = o
|
||||
err = parse_place()
|
||||
@ -349,28 +166,30 @@ func main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {
|
||||
return 1, err
|
||||
}
|
||||
stderr_is_tty = tty.IsTerminal(os.Stderr.Fd())
|
||||
t, err := tty.OpenControllingTerm()
|
||||
if err != nil {
|
||||
return 1, fmt.Errorf("Failed to open controlling terminal with error: %w", err)
|
||||
}
|
||||
screen_size, err = t.GetSize()
|
||||
if err != nil {
|
||||
return 1, fmt.Errorf("Failed to query terminal using TIOCGWINSZ with error: %w", err)
|
||||
}
|
||||
|
||||
if opts.PrintWindowSize {
|
||||
t, err := tty.OpenControllingTerm()
|
||||
if err != nil {
|
||||
return 1, fmt.Errorf("Failed to open controlling terminal with error: %w", err)
|
||||
}
|
||||
sz, err := t.GetSize()
|
||||
if err != nil {
|
||||
return 1, fmt.Errorf("Failed to query terminal using TIOCGWINSZ with error: %w", err)
|
||||
}
|
||||
fmt.Printf("%dx%d", sz.Xpixel, sz.Ypixel)
|
||||
fmt.Printf("%dx%d", screen_size.Xpixel, screen_size.Ypixel)
|
||||
return 0, nil
|
||||
}
|
||||
if opts.Clear {
|
||||
cc := &graphics.GraphicsCommand{}
|
||||
cc.SetAction(graphics.GRT_action_delete).SetDelete(graphics.GRT_free_visible)
|
||||
cc.WriteWithPayloadTo(os.Stdout, nil)
|
||||
}
|
||||
if screen_size.Xpixel == 0 || screen_size.Ypixel == 0 {
|
||||
return 1, fmt.Errorf("Terminal does not support reporting screen sizes in pixels, use a terminal such as kitty, WezTerm, Konsole, etc. that does.")
|
||||
}
|
||||
|
||||
temp_files_to_delete = make([]string, 0, 8)
|
||||
shm_files_to_delete = make([]shm.MMap, 0, 8)
|
||||
lp, err = loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lp.OnInitialize = on_initialize
|
||||
lp.OnFinalize = on_finalize
|
||||
lp.OnEscapeCode = on_escape_code
|
||||
lp.OnWakeup = on_wakeup
|
||||
items, err := process_dirs(args...)
|
||||
if err != nil {
|
||||
return 1, err
|
||||
@ -385,18 +204,55 @@ func main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {
|
||||
num_of_items = len(items)
|
||||
output_channel = make(chan *image_data, 1)
|
||||
keep_going = &atomic.Bool{}
|
||||
keep_going.Store(true)
|
||||
if !opts.DetectSupport && num_of_items > 0 {
|
||||
num_workers := utils.Max(1, utils.Min(num_of_items, runtime.NumCPU()))
|
||||
for i := 0; i < num_workers; i++ {
|
||||
go run_worker()
|
||||
}
|
||||
}
|
||||
|
||||
err = lp.Run()
|
||||
defer on_finalize()
|
||||
|
||||
if opts.TransferMode == "detect" || opts.DetectSupport {
|
||||
memory, files, direct, err := DetectSupport(time.Duration(opts.DetectionTimeout * float64(time.Second)))
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
if !direct {
|
||||
return 1, fmt.Errorf("This terminal does not support the graphics protocol use a terminal such as kitty, WezTerm or Konsole that does")
|
||||
}
|
||||
if memory {
|
||||
transfer_by_memory = supported
|
||||
} else {
|
||||
transfer_by_memory = unsupported
|
||||
}
|
||||
if files {
|
||||
transfer_by_file = supported
|
||||
} else {
|
||||
transfer_by_file = unsupported
|
||||
}
|
||||
}
|
||||
if opts.DetectSupport {
|
||||
if transfer_by_memory == supported {
|
||||
print_error("memory")
|
||||
} else if transfer_by_file == supported {
|
||||
print_error("files")
|
||||
} else {
|
||||
print_error("stream")
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
for num_of_items > 0 {
|
||||
imgd := <-output_channel
|
||||
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)
|
||||
} else {
|
||||
transmit_image(imgd)
|
||||
}
|
||||
}
|
||||
keep_going.Store(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ds := lp.DeathSignalName()
|
||||
if ds != "" {
|
||||
fmt.Println("Killed by signal: ", ds)
|
||||
lp.KillIfSignalled()
|
||||
return
|
||||
}
|
||||
if opts.Hold {
|
||||
fmt.Print("\r")
|
||||
if opts.Place != "" {
|
||||
|
||||
@ -194,21 +194,16 @@ func set_basic_metadata(imgd *image_data) {
|
||||
if imgd.frames == nil {
|
||||
imgd.frames = make([]*image_frame, 0, 32)
|
||||
}
|
||||
imgd.available_width = int(screen_size.WidthPx)
|
||||
imgd.available_width = int(screen_size.Xpixel)
|
||||
imgd.available_height = 10 * imgd.canvas_height
|
||||
if place != nil {
|
||||
imgd.available_width = place.width * int(screen_size.CellWidth)
|
||||
imgd.available_height = place.height * int(screen_size.CellHeight)
|
||||
imgd.available_width = place.width * int(screen_size.Xpixel) / int(screen_size.Col)
|
||||
imgd.available_height = place.height * int(screen_size.Ypixel) / int(screen_size.Row)
|
||||
}
|
||||
imgd.needs_scaling = imgd.canvas_width > imgd.available_width || imgd.canvas_height > imgd.available_height || opts.ScaleUp
|
||||
imgd.needs_conversion = imgd.needs_scaling || remove_alpha != nil || flip || flop || imgd.format_uppercase != "PNG"
|
||||
}
|
||||
|
||||
func send_output(imgd *image_data) {
|
||||
output_channel <- imgd
|
||||
lp.WakeupMainThread()
|
||||
}
|
||||
|
||||
func report_error(source_name, msg string, err error) {
|
||||
imgd := image_data{source_name: source_name, err: fmt.Errorf("%s: %w", msg, err)}
|
||||
send_output(&imgd)
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"kitty/tools/tui/graphics"
|
||||
"kitty/tools/tui/loop"
|
||||
"kitty/tools/utils"
|
||||
"kitty/tools/utils/shm"
|
||||
)
|
||||
@ -95,7 +96,7 @@ func transmit_shm(imgd *image_data, frame_num int, frame *image_frame) (err erro
|
||||
gc := gc_for_image(imgd, frame_num, frame)
|
||||
gc.SetTransmission(graphics.GRT_transmission_sharedmem)
|
||||
gc.SetDataSize(uint64(data_size))
|
||||
gc.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(mmap.Name()))
|
||||
gc.WriteWithPayloadTo(os.Stdout, utils.UnsafeStringToBytes(mmap.Name()))
|
||||
mmap.Close()
|
||||
|
||||
return nil
|
||||
@ -141,7 +142,7 @@ func transmit_file(imgd *image_data, frame_num int, frame *image_frame) (err err
|
||||
if data_size > 0 {
|
||||
gc.SetDataSize(uint64(data_size))
|
||||
}
|
||||
gc.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(fname))
|
||||
gc.WriteWithPayloadTo(os.Stdout, utils.UnsafeStringToBytes(fname))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -159,7 +160,7 @@ func transmit_stream(imgd *image_data, frame_num int, frame *image_frame) (err e
|
||||
}
|
||||
}
|
||||
gc := gc_for_image(imgd, frame_num, frame)
|
||||
gc.WriteWithPayloadToLoop(lp, data)
|
||||
gc.WriteWithPayloadTo(os.Stdout, data)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -179,15 +180,15 @@ func calculate_in_cell_x_offset(width, cell_width int) int {
|
||||
}
|
||||
|
||||
func place_cursor(imgd *image_data) {
|
||||
cw := int(screen_size.CellWidth)
|
||||
cw := int(screen_size.Xpixel) / int(int(screen_size.Col))
|
||||
imgd.cell_x_offset = calculate_in_cell_x_offset(imgd.canvas_width, cw)
|
||||
num_of_cells_needed := int(math.Ceil(float64(imgd.canvas_width) / float64(cw)))
|
||||
if place == nil {
|
||||
switch opts.Align {
|
||||
case "center":
|
||||
imgd.move_x_by = (int(screen_size.WidthCells) - num_of_cells_needed) / 2
|
||||
imgd.move_x_by = (int(screen_size.Col) - num_of_cells_needed) / 2
|
||||
case "right":
|
||||
imgd.move_x_by = (int(screen_size.WidthCells) - num_of_cells_needed)
|
||||
imgd.move_x_by = (int(screen_size.Col) - num_of_cells_needed)
|
||||
}
|
||||
} else {
|
||||
imgd.move_to.x = place.left + 1
|
||||
@ -242,12 +243,12 @@ func transmit_image(imgd *image_data) {
|
||||
}
|
||||
}
|
||||
place_cursor(imgd)
|
||||
lp.QueueWriteString("\r")
|
||||
fmt.Print("\r")
|
||||
if imgd.move_x_by > 0 {
|
||||
lp.MoveCursorHorizontally(imgd.move_x_by)
|
||||
fmt.Printf("\x1b[%dC", imgd.move_x_by)
|
||||
}
|
||||
if imgd.move_to.x > 0 {
|
||||
lp.MoveCursorTo(imgd.move_to.x, imgd.move_to.y)
|
||||
fmt.Printf(loop.MoveCursorToTemplate, imgd.move_to.y, imgd.move_to.x)
|
||||
}
|
||||
frame_control_cmd := graphics.GraphicsCommand{}
|
||||
frame_control_cmd.SetAction(graphics.GRT_action_animate).SetImageNumber(imgd.image_number)
|
||||
@ -272,20 +273,20 @@ func transmit_image(imgd *image_data) {
|
||||
case opts.Loop > 0:
|
||||
c.SetNumberOfLoops(uint64(opts.Loop) + 1)
|
||||
}
|
||||
c.WriteWithPayloadToLoop(lp, nil)
|
||||
c.WriteWithPayloadTo(os.Stdout, nil)
|
||||
case 1:
|
||||
c := frame_control_cmd
|
||||
c.SetAnimationControl(2) // set animation to loading mode
|
||||
c.WriteWithPayloadToLoop(lp, nil)
|
||||
c.WriteWithPayloadTo(os.Stdout, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_animated {
|
||||
c := frame_control_cmd
|
||||
c.SetAnimationControl(3) // set animation to normal mode
|
||||
c.WriteWithPayloadToLoop(lp, nil)
|
||||
c.WriteWithPayloadTo(os.Stdout, nil)
|
||||
}
|
||||
if imgd.move_to.x == 0 {
|
||||
lp.Println() // ensure cursor is on new line
|
||||
fmt.Println() // ensure cursor is on new line
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,9 +261,11 @@ func (self *Loop) SetCursorVisible(visible bool) {
|
||||
}
|
||||
}
|
||||
|
||||
const MoveCursorToTemplate = "\x1b[%d;%dH"
|
||||
|
||||
func (self *Loop) MoveCursorTo(x, y int) {
|
||||
if x > 0 && y > 0 {
|
||||
self.QueueWriteString(fmt.Sprintf("\x1b[%d;%dH", y, x))
|
||||
self.QueueWriteString(fmt.Sprintf(MoveCursorToTemplate, y, x))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user