decentral1se 1723025fbf
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
build: go 1.24
We were running behind and there were quite some deprecations to update.
This was mostly in the upstream copy/pasta package but seems quite
minimal.
2025-03-16 12:31:45 +01:00

368 lines
8.4 KiB
Go

package kitty
import (
"encoding"
"fmt"
"strconv"
"strings"
)
var (
_ encoding.TextMarshaler = Options{}
_ encoding.TextUnmarshaler = &Options{}
)
// Options represents a Kitty Graphics Protocol options.
type Options struct {
// Common options.
// Action (a=t) is the action to be performed on the image. Can be one of
// [Transmit], [TransmitDisplay], [Query], [Put], [Delete], [Frame],
// [Animate], [Compose].
Action byte
// Quite mode (q=0) is the quiet mode. Can be either zero, one, or two
// where zero is the default, 1 suppresses OK responses, and 2 suppresses
// both OK and error responses.
Quite byte
// Transmission options.
// ID (i=) is the image ID. The ID is a unique identifier for the image.
// Must be a positive integer up to [math.MaxUint32].
ID int
// PlacementID (p=) is the placement ID. The placement ID is a unique
// identifier for the placement of the image. Must be a positive integer up
// to [math.MaxUint32].
PlacementID int
// Number (I=0) is the number of images to be transmitted.
Number int
// Format (f=32) is the image format. One of [RGBA], [RGB], [PNG].
Format int
// ImageWidth (s=0) is the transmitted image width.
ImageWidth int
// ImageHeight (v=0) is the transmitted image height.
ImageHeight int
// Compression (o=) is the image compression type. Can be [Zlib] or zero.
Compression byte
// Transmission (t=d) is the image transmission type. Can be [Direct], [File],
// [TempFile], or[SharedMemory].
Transmission byte
// File is the file path to be used when the transmission type is [File].
// If [Options.Transmission] is omitted i.e. zero and this is non-empty,
// the transmission type is set to [File].
File string
// Size (S=0) is the size to be read from the transmission medium.
Size int
// Offset (O=0) is the offset byte to start reading from the transmission
// medium.
Offset int
// Chunk (m=) whether the image is transmitted in chunks. Can be either
// zero or one. When true, the image is transmitted in chunks. Each chunk
// must be a multiple of 4, and up to [MaxChunkSize] bytes. Each chunk must
// have the m=1 option except for the last chunk which must have m=0.
Chunk bool
// Display options.
// X (x=0) is the pixel X coordinate of the image to start displaying.
X int
// Y (y=0) is the pixel Y coordinate of the image to start displaying.
Y int
// Z (z=0) is the Z coordinate of the image to display.
Z int
// Width (w=0) is the width of the image to display.
Width int
// Height (h=0) is the height of the image to display.
Height int
// OffsetX (X=0) is the OffsetX coordinate of the cursor cell to start
// displaying the image. OffsetX=0 is the leftmost cell. This must be
// smaller than the terminal cell width.
OffsetX int
// OffsetY (Y=0) is the OffsetY coordinate of the cursor cell to start
// displaying the image. OffsetY=0 is the topmost cell. This must be
// smaller than the terminal cell height.
OffsetY int
// Columns (c=0) is the number of columns to display the image. The image
// will be scaled to fit the number of columns.
Columns int
// Rows (r=0) is the number of rows to display the image. The image will be
// scaled to fit the number of rows.
Rows int
// VirtualPlacement (U=0) whether to use virtual placement. This is used
// with Unicode [Placeholder] to display images.
VirtualPlacement bool
// DoNotMoveCursor (C=0) whether to move the cursor after displaying the
// image.
DoNotMoveCursor bool
// ParentID (P=0) is the parent image ID. The parent ID is the ID of the
// image that is the parent of the current image. This is used with Unicode
// [Placeholder] to display images relative to the parent image.
ParentID int
// ParentPlacementID (Q=0) is the parent placement ID. The parent placement
// ID is the ID of the placement of the parent image. This is used with
// Unicode [Placeholder] to display images relative to the parent image.
ParentPlacementID int
// Delete options.
// Delete (d=a) is the delete action. Can be one of [DeleteAll],
// [DeleteID], [DeleteNumber], [DeleteCursor], [DeleteFrames],
// [DeleteCell], [DeleteCellZ], [DeleteRange], [DeleteColumn], [DeleteRow],
// [DeleteZ].
Delete byte
// DeleteResources indicates whether to delete the resources associated
// with the image.
DeleteResources bool
}
// Options returns the options as a slice of a key-value pairs.
func (o *Options) Options() (opts []string) {
opts = []string{}
if o.Format == 0 {
o.Format = RGBA
}
if o.Action == 0 {
o.Action = Transmit
}
if o.Delete == 0 {
o.Delete = DeleteAll
}
if o.Transmission == 0 {
if len(o.File) > 0 {
o.Transmission = File
} else {
o.Transmission = Direct
}
}
if o.Format != RGBA {
opts = append(opts, fmt.Sprintf("f=%d", o.Format))
}
if o.Quite > 0 {
opts = append(opts, fmt.Sprintf("q=%d", o.Quite))
}
if o.ID > 0 {
opts = append(opts, fmt.Sprintf("i=%d", o.ID))
}
if o.PlacementID > 0 {
opts = append(opts, fmt.Sprintf("p=%d", o.PlacementID))
}
if o.Number > 0 {
opts = append(opts, fmt.Sprintf("I=%d", o.Number))
}
if o.ImageWidth > 0 {
opts = append(opts, fmt.Sprintf("s=%d", o.ImageWidth))
}
if o.ImageHeight > 0 {
opts = append(opts, fmt.Sprintf("v=%d", o.ImageHeight))
}
if o.Transmission != Direct {
opts = append(opts, fmt.Sprintf("t=%c", o.Transmission))
}
if o.Size > 0 {
opts = append(opts, fmt.Sprintf("S=%d", o.Size))
}
if o.Offset > 0 {
opts = append(opts, fmt.Sprintf("O=%d", o.Offset))
}
if o.Compression == Zlib {
opts = append(opts, fmt.Sprintf("o=%c", o.Compression))
}
if o.VirtualPlacement {
opts = append(opts, "U=1")
}
if o.DoNotMoveCursor {
opts = append(opts, "C=1")
}
if o.ParentID > 0 {
opts = append(opts, fmt.Sprintf("P=%d", o.ParentID))
}
if o.ParentPlacementID > 0 {
opts = append(opts, fmt.Sprintf("Q=%d", o.ParentPlacementID))
}
if o.X > 0 {
opts = append(opts, fmt.Sprintf("x=%d", o.X))
}
if o.Y > 0 {
opts = append(opts, fmt.Sprintf("y=%d", o.Y))
}
if o.Z > 0 {
opts = append(opts, fmt.Sprintf("z=%d", o.Z))
}
if o.Width > 0 {
opts = append(opts, fmt.Sprintf("w=%d", o.Width))
}
if o.Height > 0 {
opts = append(opts, fmt.Sprintf("h=%d", o.Height))
}
if o.OffsetX > 0 {
opts = append(opts, fmt.Sprintf("X=%d", o.OffsetX))
}
if o.OffsetY > 0 {
opts = append(opts, fmt.Sprintf("Y=%d", o.OffsetY))
}
if o.Columns > 0 {
opts = append(opts, fmt.Sprintf("c=%d", o.Columns))
}
if o.Rows > 0 {
opts = append(opts, fmt.Sprintf("r=%d", o.Rows))
}
if o.Delete != DeleteAll || o.DeleteResources {
da := o.Delete
if o.DeleteResources {
da = da - ' ' // to uppercase
}
opts = append(opts, fmt.Sprintf("d=%c", da))
}
if o.Action != Transmit {
opts = append(opts, fmt.Sprintf("a=%c", o.Action))
}
return
}
// String returns the string representation of the options.
func (o Options) String() string {
return strings.Join(o.Options(), ",")
}
// MarshalText returns the string representation of the options.
func (o Options) MarshalText() ([]byte, error) {
return []byte(o.String()), nil
}
// UnmarshalText parses the options from the given string.
func (o *Options) UnmarshalText(text []byte) error {
opts := strings.Split(string(text), ",")
for _, opt := range opts {
ps := strings.SplitN(opt, "=", 2)
if len(ps) != 2 || len(ps[1]) == 0 {
continue
}
switch ps[0] {
case "a":
o.Action = ps[1][0]
case "o":
o.Compression = ps[1][0]
case "t":
o.Transmission = ps[1][0]
case "d":
d := ps[1][0]
if d >= 'A' && d <= 'Z' {
o.DeleteResources = true
d = d + ' ' // to lowercase
}
o.Delete = d
case "i", "q", "p", "I", "f", "s", "v", "S", "O", "m", "x", "y", "z", "w", "h", "X", "Y", "c", "r", "U", "P", "Q":
v, err := strconv.Atoi(ps[1])
if err != nil {
continue
}
switch ps[0] {
case "i":
o.ID = v
case "q":
o.Quite = byte(v)
case "p":
o.PlacementID = v
case "I":
o.Number = v
case "f":
o.Format = v
case "s":
o.ImageWidth = v
case "v":
o.ImageHeight = v
case "S":
o.Size = v
case "O":
o.Offset = v
case "m":
o.Chunk = v == 0 || v == 1
case "x":
o.X = v
case "y":
o.Y = v
case "z":
o.Z = v
case "w":
o.Width = v
case "h":
o.Height = v
case "X":
o.OffsetX = v
case "Y":
o.OffsetY = v
case "c":
o.Columns = v
case "r":
o.Rows = v
case "U":
o.VirtualPlacement = v == 1
case "P":
o.ParentID = v
case "Q":
o.ParentPlacementID = v
}
}
}
return nil
}