mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-09-20 02:57:19 +03:00
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
// 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) {
|
|
temp_files_to_delete := make([]string, 0, 8)
|
|
shm_files_to_delete := make([]shm.MMap, 0, 8)
|
|
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...))
|
|
}
|
|
|
|
defer func() {
|
|
if len(temp_files_to_delete) > 0 && transfer_by_file != supported {
|
|
for _, name := range temp_files_to_delete {
|
|
os.Remove(name)
|
|
}
|
|
}
|
|
if len(shm_files_to_delete) > 0 && transfer_by_memory != supported {
|
|
for _, name := range shm_files_to_delete {
|
|
name.Unlink()
|
|
}
|
|
}
|
|
}()
|
|
|
|
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
|
|
}
|