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
|
package icat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -16,11 +15,12 @@ import (
|
|||||||
"kitty/tools/tty"
|
"kitty/tools/tty"
|
||||||
"kitty/tools/tui"
|
"kitty/tools/tui"
|
||||||
"kitty/tools/tui/graphics"
|
"kitty/tools/tui/graphics"
|
||||||
"kitty/tools/tui/loop"
|
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
"kitty/tools/utils/images"
|
"kitty/tools/utils/images"
|
||||||
"kitty/tools/utils/shm"
|
"kitty/tools/utils/shm"
|
||||||
"kitty/tools/utils/style"
|
"kitty/tools/utils/style"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
@ -30,7 +30,6 @@ type Place struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var opts *Options
|
var opts *Options
|
||||||
var lp *loop.Loop
|
|
||||||
var place *Place
|
var place *Place
|
||||||
var z_index int32
|
var z_index int32
|
||||||
var remove_alpha *images.NRGBColor
|
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 temp_files_to_delete []string
|
||||||
var shm_files_to_delete []shm.MMap
|
var shm_files_to_delete []shm.MMap
|
||||||
var direct_query_id, file_query_id, memory_query_id uint32
|
|
||||||
var stderr_is_tty bool
|
var stderr_is_tty bool
|
||||||
var query_in_flight bool
|
|
||||||
var stream_response string
|
var stream_response string
|
||||||
var files_channel chan input_arg
|
var files_channel chan input_arg
|
||||||
var output_channel chan *image_data
|
var output_channel chan *image_data
|
||||||
var num_of_items int
|
var num_of_items int
|
||||||
var keep_going *atomic.Bool
|
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) {
|
func parse_mirror() (err error) {
|
||||||
flip = opts.Mirror == "both" || opts.Mirror == "vertical"
|
flip = opts.Mirror == "both" || opts.Mirror == "vertical"
|
||||||
@ -128,143 +129,8 @@ func parse_place() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func print_error(format string, args ...any) {
|
func print_error(format string, args ...any) {
|
||||||
if lp == nil || !stderr_is_tty {
|
fmt.Fprintf(os.Stderr, format, args...)
|
||||||
fmt.Fprintf(os.Stderr, format, args...)
|
fmt.Fprintln(os.Stderr)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func on_finalize() string {
|
func on_finalize() string {
|
||||||
@ -281,55 +147,6 @@ func on_finalize() string {
|
|||||||
return ""
|
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) {
|
func main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {
|
||||||
opts = o
|
opts = o
|
||||||
err = parse_place()
|
err = parse_place()
|
||||||
@ -349,28 +166,30 @@ func main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {
|
|||||||
return 1, err
|
return 1, err
|
||||||
}
|
}
|
||||||
stderr_is_tty = tty.IsTerminal(os.Stderr.Fd())
|
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 {
|
if opts.PrintWindowSize {
|
||||||
t, err := tty.OpenControllingTerm()
|
fmt.Printf("%dx%d", screen_size.Xpixel, screen_size.Ypixel)
|
||||||
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)
|
|
||||||
return 0, nil
|
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)
|
temp_files_to_delete = make([]string, 0, 8)
|
||||||
shm_files_to_delete = make([]shm.MMap, 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...)
|
items, err := process_dirs(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
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)
|
num_of_items = len(items)
|
||||||
output_channel = make(chan *image_data, 1)
|
output_channel = make(chan *image_data, 1)
|
||||||
keep_going = &atomic.Bool{}
|
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)
|
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 {
|
if opts.Hold {
|
||||||
fmt.Print("\r")
|
fmt.Print("\r")
|
||||||
if opts.Place != "" {
|
if opts.Place != "" {
|
||||||
|
|||||||
@ -194,21 +194,16 @@ func set_basic_metadata(imgd *image_data) {
|
|||||||
if imgd.frames == nil {
|
if imgd.frames == nil {
|
||||||
imgd.frames = make([]*image_frame, 0, 32)
|
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
|
imgd.available_height = 10 * imgd.canvas_height
|
||||||
if place != nil {
|
if place != nil {
|
||||||
imgd.available_width = place.width * int(screen_size.CellWidth)
|
imgd.available_width = place.width * int(screen_size.Xpixel) / int(screen_size.Col)
|
||||||
imgd.available_height = place.height * int(screen_size.CellHeight)
|
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_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"
|
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) {
|
func report_error(source_name, msg string, err error) {
|
||||||
imgd := image_data{source_name: source_name, err: fmt.Errorf("%s: %w", msg, err)}
|
imgd := image_data{source_name: source_name, err: fmt.Errorf("%s: %w", msg, err)}
|
||||||
send_output(&imgd)
|
send_output(&imgd)
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"kitty/tools/tui/graphics"
|
"kitty/tools/tui/graphics"
|
||||||
|
"kitty/tools/tui/loop"
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
"kitty/tools/utils/shm"
|
"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 := gc_for_image(imgd, frame_num, frame)
|
||||||
gc.SetTransmission(graphics.GRT_transmission_sharedmem)
|
gc.SetTransmission(graphics.GRT_transmission_sharedmem)
|
||||||
gc.SetDataSize(uint64(data_size))
|
gc.SetDataSize(uint64(data_size))
|
||||||
gc.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(mmap.Name()))
|
gc.WriteWithPayloadTo(os.Stdout, utils.UnsafeStringToBytes(mmap.Name()))
|
||||||
mmap.Close()
|
mmap.Close()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -141,7 +142,7 @@ func transmit_file(imgd *image_data, frame_num int, frame *image_frame) (err err
|
|||||||
if data_size > 0 {
|
if data_size > 0 {
|
||||||
gc.SetDataSize(uint64(data_size))
|
gc.SetDataSize(uint64(data_size))
|
||||||
}
|
}
|
||||||
gc.WriteWithPayloadToLoop(lp, utils.UnsafeStringToBytes(fname))
|
gc.WriteWithPayloadTo(os.Stdout, utils.UnsafeStringToBytes(fname))
|
||||||
return nil
|
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 := gc_for_image(imgd, frame_num, frame)
|
||||||
gc.WriteWithPayloadToLoop(lp, data)
|
gc.WriteWithPayloadTo(os.Stdout, data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,15 +180,15 @@ func calculate_in_cell_x_offset(width, cell_width int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func place_cursor(imgd *image_data) {
|
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)
|
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)))
|
num_of_cells_needed := int(math.Ceil(float64(imgd.canvas_width) / float64(cw)))
|
||||||
if place == nil {
|
if place == nil {
|
||||||
switch opts.Align {
|
switch opts.Align {
|
||||||
case "center":
|
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":
|
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 {
|
} else {
|
||||||
imgd.move_to.x = place.left + 1
|
imgd.move_to.x = place.left + 1
|
||||||
@ -242,12 +243,12 @@ func transmit_image(imgd *image_data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
place_cursor(imgd)
|
place_cursor(imgd)
|
||||||
lp.QueueWriteString("\r")
|
fmt.Print("\r")
|
||||||
if imgd.move_x_by > 0 {
|
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 {
|
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 := graphics.GraphicsCommand{}
|
||||||
frame_control_cmd.SetAction(graphics.GRT_action_animate).SetImageNumber(imgd.image_number)
|
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:
|
case opts.Loop > 0:
|
||||||
c.SetNumberOfLoops(uint64(opts.Loop) + 1)
|
c.SetNumberOfLoops(uint64(opts.Loop) + 1)
|
||||||
}
|
}
|
||||||
c.WriteWithPayloadToLoop(lp, nil)
|
c.WriteWithPayloadTo(os.Stdout, nil)
|
||||||
case 1:
|
case 1:
|
||||||
c := frame_control_cmd
|
c := frame_control_cmd
|
||||||
c.SetAnimationControl(2) // set animation to loading mode
|
c.SetAnimationControl(2) // set animation to loading mode
|
||||||
c.WriteWithPayloadToLoop(lp, nil)
|
c.WriteWithPayloadTo(os.Stdout, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if is_animated {
|
if is_animated {
|
||||||
c := frame_control_cmd
|
c := frame_control_cmd
|
||||||
c.SetAnimationControl(3) // set animation to normal mode
|
c.SetAnimationControl(3) // set animation to normal mode
|
||||||
c.WriteWithPayloadToLoop(lp, nil)
|
c.WriteWithPayloadTo(os.Stdout, nil)
|
||||||
}
|
}
|
||||||
if imgd.move_to.x == 0 {
|
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) {
|
func (self *Loop) MoveCursorTo(x, y int) {
|
||||||
if x > 0 && y > 0 {
|
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