options.go

  1package kitty
  2
  3import (
  4	"encoding"
  5	"fmt"
  6	"strconv"
  7	"strings"
  8)
  9
 10var (
 11	_ encoding.TextMarshaler   = Options{}
 12	_ encoding.TextUnmarshaler = &Options{}
 13)
 14
 15// Options represents a Kitty Graphics Protocol options.
 16type Options struct {
 17	// Common options.
 18
 19	// Action (a=t) is the action to be performed on the image. Can be one of
 20	// [Transmit], [TransmitDisplay], [Query], [Put], [Delete], [Frame],
 21	// [Animate], [Compose].
 22	Action byte
 23
 24	// Quite mode (q=0) is the quiet mode. Can be either zero, one, or two
 25	// where zero is the default, 1 suppresses OK responses, and 2 suppresses
 26	// both OK and error responses.
 27	Quite byte
 28
 29	// Transmission options.
 30
 31	// ID (i=) is the image ID. The ID is a unique identifier for the image.
 32	// Must be a positive integer up to [math.MaxUint32].
 33	ID int
 34
 35	// PlacementID (p=) is the placement ID. The placement ID is a unique
 36	// identifier for the placement of the image. Must be a positive integer up
 37	// to [math.MaxUint32].
 38	PlacementID int
 39
 40	// Number (I=0) is the number of images to be transmitted.
 41	Number int
 42
 43	// Format (f=32) is the image format. One of [RGBA], [RGB], [PNG].
 44	Format int
 45
 46	// ImageWidth (s=0) is the transmitted image width.
 47	ImageWidth int
 48
 49	// ImageHeight (v=0) is the transmitted image height.
 50	ImageHeight int
 51
 52	// Compression (o=) is the image compression type. Can be [Zlib] or zero.
 53	Compression byte
 54
 55	// Transmission (t=d) is the image transmission type. Can be [Direct], [File],
 56	// [TempFile], or[SharedMemory].
 57	Transmission byte
 58
 59	// File is the file path to be used when the transmission type is [File].
 60	// If [Options.Transmission] is omitted i.e. zero and this is non-empty,
 61	// the transmission type is set to [File].
 62	File string
 63
 64	// Size (S=0) is the size to be read from the transmission medium.
 65	Size int
 66
 67	// Offset (O=0) is the offset byte to start reading from the transmission
 68	// medium.
 69	Offset int
 70
 71	// Chunk (m=) whether the image is transmitted in chunks. Can be either
 72	// zero or one. When true, the image is transmitted in chunks. Each chunk
 73	// must be a multiple of 4, and up to [MaxChunkSize] bytes. Each chunk must
 74	// have the m=1 option except for the last chunk which must have m=0.
 75	Chunk bool
 76
 77	// Display options.
 78
 79	// X (x=0) is the pixel X coordinate of the image to start displaying.
 80	X int
 81
 82	// Y (y=0) is the pixel Y coordinate of the image to start displaying.
 83	Y int
 84
 85	// Z (z=0) is the Z coordinate of the image to display.
 86	Z int
 87
 88	// Width (w=0) is the width of the image to display.
 89	Width int
 90
 91	// Height (h=0) is the height of the image to display.
 92	Height int
 93
 94	// OffsetX (X=0) is the OffsetX coordinate of the cursor cell to start
 95	// displaying the image. OffsetX=0 is the leftmost cell. This must be
 96	// smaller than the terminal cell width.
 97	OffsetX int
 98
 99	// OffsetY (Y=0) is the OffsetY coordinate of the cursor cell to start
100	// displaying the image. OffsetY=0 is the topmost cell. This must be
101	// smaller than the terminal cell height.
102	OffsetY int
103
104	// Columns (c=0) is the number of columns to display the image. The image
105	// will be scaled to fit the number of columns.
106	Columns int
107
108	// Rows (r=0) is the number of rows to display the image. The image will be
109	// scaled to fit the number of rows.
110	Rows int
111
112	// VirtualPlacement (U=0) whether to use virtual placement. This is used
113	// with Unicode [Placeholder] to display images.
114	VirtualPlacement bool
115
116	// DoNotMoveCursor (C=0) whether to move the cursor after displaying the
117	// image.
118	DoNotMoveCursor bool
119
120	// ParentID (P=0) is the parent image ID. The parent ID is the ID of the
121	// image that is the parent of the current image. This is used with Unicode
122	// [Placeholder] to display images relative to the parent image.
123	ParentID int
124
125	// ParentPlacementID (Q=0) is the parent placement ID. The parent placement
126	// ID is the ID of the placement of the parent image. This is used with
127	// Unicode [Placeholder] to display images relative to the parent image.
128	ParentPlacementID int
129
130	// Delete options.
131
132	// Delete (d=a) is the delete action. Can be one of [DeleteAll],
133	// [DeleteID], [DeleteNumber], [DeleteCursor], [DeleteFrames],
134	// [DeleteCell], [DeleteCellZ], [DeleteRange], [DeleteColumn], [DeleteRow],
135	// [DeleteZ].
136	Delete byte
137
138	// DeleteResources indicates whether to delete the resources associated
139	// with the image.
140	DeleteResources bool
141}
142
143// Options returns the options as a slice of a key-value pairs.
144func (o *Options) Options() (opts []string) {
145	opts = []string{}
146	if o.Format == 0 {
147		o.Format = RGBA
148	}
149
150	if o.Action == 0 {
151		o.Action = Transmit
152	}
153
154	if o.Delete == 0 {
155		o.Delete = DeleteAll
156	}
157
158	if o.Transmission == 0 {
159		if len(o.File) > 0 {
160			o.Transmission = File
161		} else {
162			o.Transmission = Direct
163		}
164	}
165
166	if o.Format != RGBA {
167		opts = append(opts, fmt.Sprintf("f=%d", o.Format))
168	}
169
170	if o.Quite > 0 {
171		opts = append(opts, fmt.Sprintf("q=%d", o.Quite))
172	}
173
174	if o.ID > 0 {
175		opts = append(opts, fmt.Sprintf("i=%d", o.ID))
176	}
177
178	if o.PlacementID > 0 {
179		opts = append(opts, fmt.Sprintf("p=%d", o.PlacementID))
180	}
181
182	if o.Number > 0 {
183		opts = append(opts, fmt.Sprintf("I=%d", o.Number))
184	}
185
186	if o.ImageWidth > 0 {
187		opts = append(opts, fmt.Sprintf("s=%d", o.ImageWidth))
188	}
189
190	if o.ImageHeight > 0 {
191		opts = append(opts, fmt.Sprintf("v=%d", o.ImageHeight))
192	}
193
194	if o.Transmission != Direct {
195		opts = append(opts, fmt.Sprintf("t=%c", o.Transmission))
196	}
197
198	if o.Size > 0 {
199		opts = append(opts, fmt.Sprintf("S=%d", o.Size))
200	}
201
202	if o.Offset > 0 {
203		opts = append(opts, fmt.Sprintf("O=%d", o.Offset))
204	}
205
206	if o.Compression == Zlib {
207		opts = append(opts, fmt.Sprintf("o=%c", o.Compression))
208	}
209
210	if o.VirtualPlacement {
211		opts = append(opts, "U=1")
212	}
213
214	if o.DoNotMoveCursor {
215		opts = append(opts, "C=1")
216	}
217
218	if o.ParentID > 0 {
219		opts = append(opts, fmt.Sprintf("P=%d", o.ParentID))
220	}
221
222	if o.ParentPlacementID > 0 {
223		opts = append(opts, fmt.Sprintf("Q=%d", o.ParentPlacementID))
224	}
225
226	if o.X > 0 {
227		opts = append(opts, fmt.Sprintf("x=%d", o.X))
228	}
229
230	if o.Y > 0 {
231		opts = append(opts, fmt.Sprintf("y=%d", o.Y))
232	}
233
234	if o.Z > 0 {
235		opts = append(opts, fmt.Sprintf("z=%d", o.Z))
236	}
237
238	if o.Width > 0 {
239		opts = append(opts, fmt.Sprintf("w=%d", o.Width))
240	}
241
242	if o.Height > 0 {
243		opts = append(opts, fmt.Sprintf("h=%d", o.Height))
244	}
245
246	if o.OffsetX > 0 {
247		opts = append(opts, fmt.Sprintf("X=%d", o.OffsetX))
248	}
249
250	if o.OffsetY > 0 {
251		opts = append(opts, fmt.Sprintf("Y=%d", o.OffsetY))
252	}
253
254	if o.Columns > 0 {
255		opts = append(opts, fmt.Sprintf("c=%d", o.Columns))
256	}
257
258	if o.Rows > 0 {
259		opts = append(opts, fmt.Sprintf("r=%d", o.Rows))
260	}
261
262	if o.Delete != DeleteAll || o.DeleteResources {
263		da := o.Delete
264		if o.DeleteResources {
265			da = da - ' ' // to uppercase
266		}
267
268		opts = append(opts, fmt.Sprintf("d=%c", da))
269	}
270
271	if o.Action != Transmit {
272		opts = append(opts, fmt.Sprintf("a=%c", o.Action))
273	}
274
275	return
276}
277
278// String returns the string representation of the options.
279func (o Options) String() string {
280	return strings.Join(o.Options(), ",")
281}
282
283// MarshalText returns the string representation of the options.
284func (o Options) MarshalText() ([]byte, error) {
285	return []byte(o.String()), nil
286}
287
288// UnmarshalText parses the options from the given string.
289func (o *Options) UnmarshalText(text []byte) error {
290	opts := strings.Split(string(text), ",")
291	for _, opt := range opts {
292		ps := strings.SplitN(opt, "=", 2)
293		if len(ps) != 2 || len(ps[1]) == 0 {
294			continue
295		}
296
297		switch ps[0] {
298		case "a":
299			o.Action = ps[1][0]
300		case "o":
301			o.Compression = ps[1][0]
302		case "t":
303			o.Transmission = ps[1][0]
304		case "d":
305			d := ps[1][0]
306			if d >= 'A' && d <= 'Z' {
307				o.DeleteResources = true
308				d = d + ' ' // to lowercase
309			}
310			o.Delete = d
311		case "i", "q", "p", "I", "f", "s", "v", "S", "O", "m", "x", "y", "z", "w", "h", "X", "Y", "c", "r", "U", "P", "Q":
312			v, err := strconv.Atoi(ps[1])
313			if err != nil {
314				continue
315			}
316
317			switch ps[0] {
318			case "i":
319				o.ID = v
320			case "q":
321				o.Quite = byte(v)
322			case "p":
323				o.PlacementID = v
324			case "I":
325				o.Number = v
326			case "f":
327				o.Format = v
328			case "s":
329				o.ImageWidth = v
330			case "v":
331				o.ImageHeight = v
332			case "S":
333				o.Size = v
334			case "O":
335				o.Offset = v
336			case "m":
337				o.Chunk = v == 0 || v == 1
338			case "x":
339				o.X = v
340			case "y":
341				o.Y = v
342			case "z":
343				o.Z = v
344			case "w":
345				o.Width = v
346			case "h":
347				o.Height = v
348			case "X":
349				o.OffsetX = v
350			case "Y":
351				o.OffsetY = v
352			case "c":
353				o.Columns = v
354			case "r":
355				o.Rows = v
356			case "U":
357				o.VirtualPlacement = v == 1
358			case "P":
359				o.ParentID = v
360			case "Q":
361				o.ParentPlacementID = v
362			}
363		}
364	}
365
366	return nil
367}