···11+# This file is generated by dune, edit dune-project instead
22+opam-version: "2.0"
33+synopsis: "OCaml implementation of the Kitty terminal graphics protocol"
44+description:
55+ "Library for rendering images in terminals that support the Kitty graphics protocol. Supports image transmission, display, animation, Unicode placeholders, and terminal capability detection."
66+maintainer: ["Anil Madhavapeddy <anil@recoil.org>"]
77+authors: ["Anil Madhavapeddy"]
88+license: "ISC"
99+homepage: "https://tangled.org/@anil.recoil.org/ocaml-kgp"
1010+bug-reports: "https://tangled.org/@anil.recoil.org/ocaml-kgp/issues"
1111+depends: [
1212+ "dune" {>= "3.20"}
1313+ "ocaml" {>= "4.14.0"}
1414+ "cmdliner"
1515+ "base64"
1616+ "odoc" {with-doc}
1717+]
1818+build: [
1919+ ["dune" "subst"] {dev}
2020+ [
2121+ "dune"
2222+ "build"
2323+ "-p"
2424+ name
2525+ "-j"
2626+ jobs
2727+ "@install"
2828+ "@runtest" {with-test}
2929+ "@doc" {with-doc}
3030+ ]
3131+]
3232+x-maintenance-intent: ["(latest)"]
+8-7
lib-cli/kgp_cli.ml
···11111212let graphics_term =
1313 let doc = "Force graphics output enabled, ignoring terminal detection." in
1414- let enable = Arg.info ["g"; "graphics"] ~doc ~docs:graphics_docs in
1414+ let enable = Arg.info [ "g"; "graphics" ] ~doc ~docs:graphics_docs in
1515 let doc = "Disable graphics output, use text placeholders instead." in
1616- let disable = Arg.info ["no-graphics"] ~doc ~docs:graphics_docs in
1616+ let disable = Arg.info [ "no-graphics" ] ~doc ~docs:graphics_docs in
1717 let doc = "Force tmux passthrough mode for graphics." in
1818- let tmux = Arg.info ["tmux"] ~doc ~docs:graphics_docs in
1818+ let tmux = Arg.info [ "tmux" ] ~doc ~docs:graphics_docs in
1919 let choose enable disable tmux : Kgp.Terminal.graphics_mode =
2020 if enable then `Enabled
2121 else if disable then `Disabled
2222 else if tmux then `Tmux
2323 else `Auto
2424 in
2525- Term.(const choose
2626- $ Arg.(value & flag enable)
2727- $ Arg.(value & flag disable)
2828- $ Arg.(value & flag tmux))
2525+ Term.(
2626+ const choose
2727+ $ Arg.(value & flag enable)
2828+ $ Arg.(value & flag disable)
2929+ $ Arg.(value & flag tmux))
+5-4
lib-cli/kgp_cli.mli
···5566(** Cmdliner Support for Kitty Graphics Protocol
7788- This module provides Cmdliner terms for configuring graphics output mode
99- in CLI applications. It allows users to override auto-detection with
88+ This module provides Cmdliner terms for configuring graphics output mode in
99+ CLI applications. It allows users to override auto-detection with
1010 command-line flags.
11111212 {2 Usage}
···4242 - [--tmux]: Force tmux passthrough mode
4343 - (default): Auto-detect based on terminal environment
44444545- The term evaluates to a {!Kgp.Terminal.graphics_mode} value which can
4646- be passed to {!Kgp.Terminal.supports_graphics} or {!Kgp.Terminal.resolve_mode}. *)
4545+ The term evaluates to a {!Kgp.Terminal.graphics_mode} value which can be
4646+ passed to {!Kgp.Terminal.supports_graphics} or {!Kgp.Terminal.resolve_mode}.
4747+*)
47484849val graphics_docs : string
4950(** Section name for graphics options in help output ("GRAPHICS OPTIONS").
···5566(** Kitty Terminal Graphics Protocol
7788- This library implements the Kitty terminal graphics protocol, allowing
99- OCaml programs to display images in terminals that support the protocol
1010- (Kitty, WezTerm, Konsole, Ghostty, etc.).
88+ This library implements the Kitty terminal graphics protocol, allowing OCaml
99+ programs to display images in terminals that support the protocol (Kitty,
1010+ WezTerm, Konsole, Ghostty, etc.).
11111212 {1 Protocol Overview}
1313···16161717 - No requirement for terminal emulators to understand image formats
1818 - Pixel-level positioning of graphics
1919- - Integration with text (graphics can be drawn below/above text with alpha blending)
1919+ - Integration with text (graphics can be drawn below/above text with alpha
2020+ blending)
2021 - Automatic scrolling with text
2122 - Animation support with frame deltas for efficiency
2223···72737374 (* Display at different positions *)
7475 let cmd = Kgp.display ~image_id:1 () in
7575- Kgp.write buf cmd ~data:"";
7676+ Kgp.write buf cmd ~data:""
7677 ]}
77787879 {2 Protocol Reference}
79808080- See {{:https://sw.kovidgoyal.net/kitty/graphics-protocol/} Kitty Graphics Protocol}
8181- for the full specification. *)
8181+ See
8282+ {{:https://sw.kovidgoyal.net/kitty/graphics-protocol/} Kitty Graphics
8383+ Protocol} for the full specification. *)
82848385(** {1 Type Modules} *)
8486···106108107109(** {2 Image Transmission}
108110109109- Images can be transmitted to the terminal for storage and later display.
110110- The terminal assigns storage and responds with success or failure.
111111+ Images can be transmitted to the terminal for storage and later display. The
112112+ terminal assigns storage and responds with success or failure.
111113112114 For large images, the library automatically handles chunked transmission
113115 (splitting data into 4096-byte base64-encoded chunks). *)
···130132 The image is stored by the terminal and can be displayed later using
131133 {!val:display} with the same [image_id].
132134133133- @param image_id Unique identifier (1-4294967295) for later reference.
134134- If specified, the terminal responds with success/failure.
135135- @param image_number Alternative to [image_id] where the terminal assigns
136136- a unique ID and returns it in the response. Useful when multiple
137137- programs share the terminal.
135135+ @param image_id
136136+ Unique identifier (1-4294967295) for later reference. If specified, the
137137+ terminal responds with success/failure.
138138+ @param image_number
139139+ Alternative to [image_id] where the terminal assigns a unique ID and
140140+ returns it in the response. Useful when multiple programs share the
141141+ terminal.
138142 @param format Pixel format of the data. Default is [`Rgba32].
139143 @param transmission How data is sent. Default is [`Direct] (inline).
140144 @param compression Compression applied to data. Default is [`None].
···160164 command
161165(** Transmit image data and display it immediately.
162166163163- Combines transmission and display in a single command. The image is
164164- rendered at the current cursor position unless placement options
165165- specify otherwise.
167167+ Combines transmission and display in a single command. The image is rendered
168168+ at the current cursor position unless placement options specify otherwise.
166169167170 See {!transmit} for parameter descriptions. The [placement] parameter
168171 controls display position and scaling. *)
···177180 command
178181(** Query terminal support without storing the image.
179182180180- Performs the same validation as {!transmit} but does not store the
181181- image. Useful for testing whether the terminal supports the graphics
182182- protocol and specific formats.
183183+ Performs the same validation as {!transmit} but does not store the image.
184184+ Useful for testing whether the terminal supports the graphics protocol and
185185+ specific formats.
183186184187 To detect graphics support, send a query and check for a response:
185188 {[
···191194192195(** {2 Display}
193196194194- Previously transmitted images can be displayed multiple times at
195195- different positions with different cropping and scaling options. *)
197197+ Previously transmitted images can be displayed multiple times at different
198198+ positions with different cropping and scaling options. *)
196199197200val display :
198201 ?image_id:int ->
···203206 command
204207(** Display a previously transmitted image.
205208206206- The image is rendered at the current cursor position. Use [placement]
207207- to control cropping, scaling, z-index, and other display options.
209209+ The image is rendered at the current cursor position. Use [placement] to
210210+ control cropping, scaling, z-index, and other display options.
208211209209- Each display creates a "placement" of the image. Multiple placements
210210- of the same image share the underlying image data.
212212+ Each display creates a "placement" of the image. Multiple placements of the
213213+ same image share the underlying image data.
211214212215 @param image_id ID of a previously transmitted image.
213213- @param image_number Image number (acts on the newest image with this number).
216216+ @param image_number
217217+ Image number (acts on the newest image with this number).
214218 @param placement Display configuration (position, size, z-index, etc.). *)
215219216220(** {2 Deletion}
217221218218- Images and placements can be deleted to free terminal resources.
219219- By default, only placements are removed and image data is retained
220220- for potential reuse. Use [~free:true] to also release the image data. *)
222222+ Images and placements can be deleted to free terminal resources. By default,
223223+ only placements are removed and image data is retained for potential reuse.
224224+ Use [~free:true] to also release the image data. *)
221225222226val delete : ?free:bool -> ?quiet:Quiet.t -> Delete.t -> command
223227(** Delete images or placements.
224228225229 See {!Delete} for the full list of deletion targets.
226230227227- @param free If true, also free the image data from memory (default: false).
228228- Without [~free:true], only placements are removed and the image data
229229- can be reused for new placements.
231231+ @param free
232232+ If true, also free the image data from memory (default: false). Without
233233+ [~free:true], only placements are removed and the image data can be reused
234234+ for new placements.
230235231236 Examples:
232237 {[
233238 (* Delete all visible images, keep data *)
234239 Kgp.delete `All_visible
235235-236236- (* Delete specific image, keeping data for reuse *)
237237- Kgp.delete (`By_id (42, None))
238238-239239- (* Delete specific image and free its data *)
240240- Kgp.delete ~free:true (`By_id (42, None))
241241-242242- (* Delete all placements at a specific cell *)
243243- Kgp.delete (`At_cell (10, 5))
240240+ (* Delete specific image, keeping data for reuse *)
241241+ Kgp.delete
242242+ (`By_id (42, None))
243243+ (* Delete specific image and free its data *)
244244+ Kgp.delete ~free:true
245245+ (`By_id (42, None))
246246+ (* Delete all placements at a specific cell *)
247247+ Kgp.delete
248248+ (`At_cell (10, 5))
244249 ]} *)
245250246251(** {2 Animation}
···249254 Animations are created by first transmitting a base image, then adding
250255 frames with optional delta encoding for efficiency.
251256252252- Frame numbers are 1-based: frame 1 is the root (base) image, frame 2
253253- is the first added frame, etc. *)
257257+ Frame numbers are 1-based: frame 1 is the root (base) image, frame 2 is the
258258+ first added frame, etc. *)
254259255260val frame :
256261 ?image_id:int ->
···266271 command
267272(** Transmit animation frame data.
268273269269- Adds a new frame to an existing image or edits an existing frame.
270270- The frame can be a full image or a partial update (rectangle).
274274+ Adds a new frame to an existing image or edits an existing frame. The frame
275275+ can be a full image or a partial update (rectangle).
271276272272- Use {!Frame.make} to configure the frame's position, timing, and
273273- composition options.
277277+ Use {!Frame.make} to configure the frame's position, timing, and composition
278278+ options.
274279275280 @param frame Frame configuration including timing and composition. *)
276281277282val animate :
278278- ?image_id:int ->
279279- ?image_number:int ->
280280- ?quiet:Quiet.t ->
281281- Animation.t ->
282282- command
283283+ ?image_id:int -> ?image_number:int -> ?quiet:Quiet.t -> Animation.t -> command
283284(** Control animation playback.
284285285286 For terminal-driven animation:
286287 {[
287288 (* Start infinite loop animation *)
288288- Kgp.animate ~image_id:1 (Animation.set_state ~loops:1 `Run)
289289-290290- (* Stop animation *)
291291- Kgp.animate ~image_id:1 (Animation.set_state `Stop)
292292-293293- (* Change frame timing *)
294294- Kgp.animate ~image_id:1 (Animation.set_gap ~frame:3 ~gap_ms:100)
289289+ Kgp.animate ~image_id:1
290290+ (Animation.set_state ~loops:1 `Run)
291291+ (* Stop animation *)
292292+ Kgp.animate ~image_id:1
293293+ (Animation.set_state `Stop)
294294+ (* Change frame timing *)
295295+ Kgp.animate ~image_id:1
296296+ (Animation.set_gap ~frame:3 ~gap_ms:100)
295297 ]}
296298297299 For client-driven animation:
···301303 ]} *)
302304303305val compose :
304304- ?image_id:int ->
305305- ?image_number:int ->
306306- ?quiet:Quiet.t ->
307307- Compose.t ->
308308- command
306306+ ?image_id:int -> ?image_number:int -> ?quiet:Quiet.t -> Compose.t -> command
309307(** Compose animation frames.
310308311311- Copies a rectangular region from one frame onto another. Useful for
312312- building complex frames from simpler components.
309309+ Copies a rectangular region from one frame onto another. Useful for building
310310+ complex frames from simpler components.
313311314312 {[
315313 (* Copy a 50x50 region from frame 2 to frame 5 *)
316316- let comp = Compose.make
317317- ~source_frame:2 ~dest_frame:5
318318- ~width:50 ~height:50
319319- ~source_x:10 ~source_y:10
320320- ~dest_x:20 ~dest_y:20 () in
314314+ let comp =
315315+ Compose.make ~source_frame:2 ~dest_frame:5 ~width:50 ~height:50
316316+ ~source_x:10 ~source_y:10 ~dest_x:20 ~dest_y:20 ()
317317+ in
321318 Kgp.compose ~image_id:1 comp
322319 ]} *)
323320324321(** {2 Output}
325322326326- Commands are serialized to escape sequences that can be written
327327- to the terminal. *)
323323+ Commands are serialized to escape sequences that can be written to the
324324+ terminal. *)
328325329326val write : Buffer.t -> command -> data:string -> unit
330327(** Write the command to a buffer.
331328332329 The [data] parameter contains the raw image/frame data (before base64
333333- encoding). Pass an empty string for commands that don't include payload
334334- data (like {!val:display}, {!val:delete}, {!val:animate}).
330330+ encoding). Pass an empty string for commands that don't include payload data
331331+ (like {!val:display}, {!val:delete}, {!val:animate}).
335332336333 The library handles base64 encoding and chunking automatically. *)
337334338335val to_string : command -> data:string -> string
339336(** Convert command to a string.
340337341341- Convenience wrapper around {!write} that returns the serialized
342342- command as a string. *)
338338+ Convenience wrapper around {!write} that returns the serialized command as a
339339+ string. *)
343340344341val write_tmux : Buffer.t -> command -> data:string -> unit
345342(** Write the command to a buffer with tmux passthrough support.
346343347347- If running inside tmux (detected via [TMUX] environment variable),
348348- wraps the graphics command in a DCS passthrough sequence so it
349349- reaches the underlying terminal. Otherwise, behaves like {!write}.
344344+ If running inside tmux (detected via [TMUX] environment variable), wraps the
345345+ graphics command in a DCS passthrough sequence so it reaches the underlying
346346+ terminal. Otherwise, behaves like {!write}.
350347351348 Requires tmux 3.3+ with [allow-passthrough] enabled. *)
352349353350val to_string_tmux : command -> data:string -> string
354351(** Convert command to a string with tmux passthrough support.
355352356356- Convenience wrapper around {!write_tmux}. If running inside tmux,
357357- wraps the output for passthrough. Otherwise, behaves like {!to_string}. *)
353353+ Convenience wrapper around {!write_tmux}. If running inside tmux, wraps the
354354+ output for passthrough. Otherwise, behaves like {!to_string}. *)
358355359356(** {1 Response} *)
360357···366363module Detect = Kgp_detect
367364368365module Tmux = Kgp_tmux
369369-(** Tmux passthrough support. Provides functions to detect if running
370370- inside tmux and to wrap escape sequences for passthrough. *)
366366+(** Tmux passthrough support. Provides functions to detect if running inside
367367+ tmux and to wrap escape sequences for passthrough. *)
371368372369module Terminal = Kgp_terminal
373370(** Terminal environment detection. Provides functions to detect terminal
374371 capabilities, pager mode, and resolve graphics output mode. *)
375375-
+29-26
lib/kgp_animation.mli
···5566(** Animation Control
7788- Operations for controlling animation playback. The protocol supports
99- both terminal-driven and client-driven animation modes.
88+ Operations for controlling animation playback. The protocol supports both
99+ terminal-driven and client-driven animation modes.
10101111 {2 Protocol Overview}
1212···24242525 {[
2626 (* Start infinite loop *)
2727- Kgp.animate ~image_id:1 (Animation.set_state ~loops:1 `Run)
2828-2929- (* Run 3 times then stop *)
3030- Kgp.animate ~image_id:1 (Animation.set_state ~loops:4 `Run)
3131-3232- (* Stop animation *)
3333- Kgp.animate ~image_id:1 (Animation.set_state `Stop)
2727+ Kgp.animate ~image_id:1
2828+ (Animation.set_state ~loops:1 `Run)
2929+ (* Run 3 times then stop *)
3030+ Kgp.animate ~image_id:1
3131+ (Animation.set_state ~loops:4 `Run)
3232+ (* Stop animation *)
3333+ Kgp.animate ~image_id:1
3434+ (Animation.set_state `Stop)
3435 ]}
35363637 {2 Client-Driven Animation}
···52535354 {[
5455 (* Slow down frame 3 *)
5555- Kgp.animate ~image_id:1 (Animation.set_gap ~frame:3 ~gap_ms:200)
5656-5757- (* Make frame 5 instant/gapless *)
5858- Kgp.animate ~image_id:1 (Animation.set_gap ~frame:5 ~gap_ms:(-1))
5656+ Kgp.animate ~image_id:1
5757+ (Animation.set_gap ~frame:3 ~gap_ms:200)
5858+ (* Make frame 5 instant/gapless *)
5959+ Kgp.animate ~image_id:1
6060+ (Animation.set_gap ~frame:5 ~gap_ms:(-1))
5961 ]}
60626163 {2 Loop Counting}
···7173 | `Set_current of int ]
7274(** Animation control operations.
73757474- - [`Set_state (state, loops)] - Set animation playback state with
7575- optional loop count.
7676+ - [`Set_state (state, loops)] - Set animation playback state with optional
7777+ loop count.
7678 - [`Set_gap (frame, gap_ms)] - Set the delay for a specific frame.
7779 - [`Set_current frame] - Jump to a specific frame (1-based). *)
78807981val set_state : ?loops:int -> Kgp_animation_state.t -> t
8082(** Set animation playback state.
81838282- @param loops Loop count: 0 = ignored, 1 = infinite, n > 1 = (n-1) loops.
8383- Protocol key: [v].
8484+ @param loops
8585+ Loop count: 0 = ignored, 1 = infinite, n > 1 = (n-1) loops. Protocol key:
8686+ [v].
8487 @param state The target playback state.
85888689 Examples:
8790 {[
8888- set_state `Run (* Run with current loop setting *)
8989- set_state ~loops:1 `Run (* Run infinitely *)
9090- set_state ~loops:3 `Run (* Run twice, then stop *)
9191- set_state `Stop (* Pause animation *)
9292- set_state `Loading (* Run, wait for more frames at end *)
9191+ set_state `Run (* Run with current loop setting *) set_state ~loops:1 `Run
9292+ (* Run infinitely *) set_state ~loops:3 `Run
9393+ (* Run twice, then stop *) set_state `Stop (* Pause animation *)
9494+ set_state `Loading (* Run, wait for more frames at end *)
9395 ]} *)
94969597val set_gap : frame:int -> gap_ms:int -> t
9698(** Set the gap (delay) for a specific frame.
979998100 @param frame 1-based frame number to modify. Protocol key: [r].
9999- @param gap_ms Delay in milliseconds before next frame. Negative values
100100- create gapless frames (not displayed, instant skip). Protocol key: [z].
101101+ @param gap_ms
102102+ Delay in milliseconds before next frame. Negative values create gapless
103103+ frames (not displayed, instant skip). Protocol key: [z].
101104102105 Note: Frame 1 is the root/base image. Use 2+ for added frames. *)
103106···106109107110 @param frame 1-based frame number to display. Protocol key: [c].
108111109109- Used for client-driven animation where the application controls
110110- frame advancement rather than the terminal. *)
112112+ Used for client-driven animation where the application controls frame
113113+ advancement rather than the terminal. *)
+1-4
lib/kgp_animation_state.ml
···5566type t = [ `Stop | `Loading | `Run ]
7788-let to_int : t -> int = function
99- | `Stop -> 1
1010- | `Loading -> 2
1111- | `Run -> 3
88+let to_int : t -> int = function `Stop -> 1 | `Loading -> 2 | `Run -> 3
+25-25
lib/kgp_animation_state.mli
···991010 {2 Protocol Details}
11111212- The animation state is specified via the [s] key in the control data
1313- when using action [a=a]:
1212+ The animation state is specified via the [s] key in the control data when
1313+ using action [a=a]:
1414 - [s=1]: stop animation
1515 - [s=2]: run in loading mode
1616 - [s=3]: run normally
···19192020 The protocol supports two animation approaches:
21212222- {b Terminal-driven animation}: The terminal automatically advances
2323- frames based on the gap (delay) specified for each frame. Use
2424- [{`Run}] or [{`Loading}] states.
2222+ {b Terminal-driven animation}: The terminal automatically advances frames
2323+ based on the gap (delay) specified for each frame. Use [{`Run}] or
2424+ [{`Loading}] states.
25252626- {b Client-driven animation}: The client manually sets the current
2727- frame using [Kgp.Animation.set_current_frame]. Use [{`Stop}] state
2828- to prevent automatic advancement.
2626+ {b Client-driven animation}: The client manually sets the current frame
2727+ using [Kgp.Animation.set_current_frame]. Use [{`Stop}] state to prevent
2828+ automatic advancement.
29293030 {2 Stop State}
31313232- [{`Stop}] halts automatic frame advancement. The animation freezes
3333- on the current frame. Use this when:
3232+ [{`Stop}] halts automatic frame advancement. The animation freezes on the
3333+ current frame. Use this when:
3434 - Implementing client-driven animation
3535 - Pausing an animation
3636 - Displaying a static frame from an animated image
37373838 {2 Loading State}
39394040- [{`Loading}] runs the animation but waits for new frames when reaching
4141- the end instead of looping. Use this when:
4040+ [{`Loading}] runs the animation but waits for new frames when reaching the
4141+ end instead of looping. Use this when:
4242 - Streaming animation frames progressively
4343 - Building an animation while displaying it
4444 - The animation is not yet complete
45454646 {2 Run State}
47474848- [{`Run}] runs the animation normally, looping back to the first frame
4949- after the last. The loop count can be controlled via the [loops]
5050- parameter in [Kgp.Animation.set_state]. *)
4848+ [{`Run}] runs the animation normally, looping back to the first frame after
4949+ the last. The loop count can be controlled via the [loops] parameter in
5050+ [Kgp.Animation.set_state]. *)
51515252type t = [ `Stop | `Loading | `Run ]
5353(** Animation playback states.
54545555- - [`Stop] - Halt animation playback. The animation freezes on the
5656- current frame and does not advance automatically.
5757- - [`Loading] - Run animation but wait for new frames at end. When
5858- the last frame is reached, the animation pauses until more frames
5959- are added, then continues.
6060- - [`Run] - Run animation normally and loop. After the last frame,
6161- playback returns to the first frame (or stops after the specified
6262- number of loops). *)
5555+ - [`Stop] - Halt animation playback. The animation freezes on the current
5656+ frame and does not advance automatically.
5757+ - [`Loading] - Run animation but wait for new frames at end. When the last
5858+ frame is reached, the animation pauses until more frames are added, then
5959+ continues.
6060+ - [`Run] - Run animation normally and loop. After the last frame, playback
6161+ returns to the first frame (or stops after the specified number of loops).
6262+*)
63636464val to_int : t -> int
6565(** Convert to protocol integer.
66666767- Returns 1 for [`Stop], 2 for [`Loading], or 3 for [`Run].
6868- These values are used in the [s=] control data key. *)
6767+ Returns 1 for [`Stop], 2 for [`Loading], or 3 for [`Run]. These values are
6868+ used in the [s=] control data key. *)
+8-9
lib/kgp_command.ml
···167167 kv_int_opt w 'p' (Kgp_placement.placement_id p);
168168 Kgp_placement.cursor p
169169 |> Option.iter (fun c ->
170170- kv_int_if w 'C' ~default:0 (Some (Kgp_cursor.to_int c)));
170170+ kv_int_if w 'C' ~default:0 (Some (Kgp_cursor.to_int c)));
171171 if Kgp_placement.unicode_placeholder p then kv_int w 'U' 1
172172173173let write_delete w ~free (d : Kgp_delete.t) =
···202202 kv_int_opt w 'z' (Kgp_frame.gap_ms f);
203203 Kgp_frame.composition f
204204 |> Option.iter (fun c ->
205205- kv_int_if w 'X' ~default:0 (Some (Kgp_composition.to_int c)));
205205+ kv_int_if w 'X' ~default:0 (Some (Kgp_composition.to_int c)));
206206 kv_int32_opt w 'Y' (Kgp_frame.background_color f)
207207208208let write_animation w : Kgp_animation.t -> unit = function
···226226 kv_int_opt w 'Y' (Kgp_compose.source_y c);
227227 Kgp_compose.composition c
228228 |> Option.iter (fun comp ->
229229- kv_int_if w 'C' ~default:0 (Some (Kgp_composition.to_int comp)))
229229+ kv_int_if w 'C' ~default:0 (Some (Kgp_composition.to_int comp)))
230230231231let write_control_data buf cmd =
232232 let w = kv_writer buf in
···235235 (* Quiet - only if non-default *)
236236 cmd.quiet
237237 |> Option.iter (fun q ->
238238- kv_int_if w 'q' ~default:0 (Some (Kgp_quiet.to_int q)));
238238+ kv_int_if w 'q' ~default:0 (Some (Kgp_quiet.to_int q)));
239239 (* Format *)
240240- cmd.format
241241- |> Option.iter (fun f -> kv_int w 'f' (Kgp_format.to_int f));
240240+ cmd.format |> Option.iter (fun f -> kv_int w 'f' (Kgp_format.to_int f));
242241 (* Transmission - only for transmit/frame actions, always include t=d for compatibility *)
243242 (match cmd.action with
244243 | `Transmit | `Transmit_and_display | `Frame -> (
···249248 (* Compression *)
250249 cmd.compression
251250 |> Option.iter (fun c ->
252252- Kgp_compression.to_char c |> Option.iter (kv_char w 'o'));
251251+ Kgp_compression.to_char c |> Option.iter (kv_char w 'o'));
253252 (* Dimensions *)
254253 kv_int_opt w 's' cmd.width;
255254 kv_int_opt w 'v' cmd.height;
···316315 let inner_buf = Buffer.create 1024 in
317316 write inner_buf cmd ~data;
318317 Kgp_tmux.write_wrapped buf (Buffer.contents inner_buf)
319319- end else
320320- write buf cmd ~data
318318+ end
319319+ else write buf cmd ~data
321320322321let to_string_tmux cmd ~data =
323322 let buf = Buffer.create 1024 in
+10-13
lib/kgp_command.mli
···7070val delete : ?free:bool -> ?quiet:Kgp_quiet.t -> Kgp_delete.t -> t
7171(** Delete images or placements.
72727373- @param free If true, also free the image data from memory (default: false).
7474- Without [~free:true], only placements are removed and the image data
7575- can be reused for new placements. *)
7373+ @param free
7474+ If true, also free the image data from memory (default: false). Without
7575+ [~free:true], only placements are removed and the image data can be reused
7676+ for new placements. *)
76777778(** {1 Animation} *)
7879···99100(** Control animation playback. *)
100101101102val compose :
102102- ?image_id:int ->
103103- ?image_number:int ->
104104- ?quiet:Kgp_quiet.t ->
105105- Kgp_compose.t ->
106106- t
103103+ ?image_id:int -> ?image_number:int -> ?quiet:Kgp_quiet.t -> Kgp_compose.t -> t
107104(** Compose animation frames. *)
108105109106(** {1 Output} *)
···117114val write_tmux : Buffer.t -> t -> data:string -> unit
118115(** Write the command to a buffer with tmux passthrough wrapping.
119116120120- If running inside tmux (detected via [TMUX] environment variable),
121121- wraps the graphics command in a DCS passthrough sequence. Otherwise,
122122- behaves like {!write}. *)
117117+ If running inside tmux (detected via [TMUX] environment variable), wraps the
118118+ graphics command in a DCS passthrough sequence. Otherwise, behaves like
119119+ {!write}. *)
123120124121val to_string_tmux : t -> data:string -> string
125122(** Convert command to a string with tmux passthrough wrapping.
126123127127- If running inside tmux, wraps the output for passthrough.
128128- Otherwise, behaves like {!to_string}. *)
124124+ If running inside tmux, wraps the output for passthrough. Otherwise, behaves
125125+ like {!to_string}. *)
+27-29
lib/kgp_compose.mli
···10101111 {2 Protocol Overview}
12121313- Frame composition uses action [a=c] to copy a rectangular region from
1414- one frame onto another. This is useful for:
1313+ Frame composition uses action [a=c] to copy a rectangular region from one
1414+ frame onto another. This is useful for:
15151616 - Building frames from reusable sprite components
1717 - Applying partial updates to existing frames
···19192020 {2 Coordinate System}
21212222- All coordinates are in pixels, relative to the top-left corner of
2323- the respective frame:
2222+ All coordinates are in pixels, relative to the top-left corner of the
2323+ respective frame:
24242525 - [source_x], [source_y]: Top-left of rectangle in source frame
2626 - [dest_x], [dest_y]: Top-left of destination in target frame
···38383939 The terminal responds with errors for:
4040 - [ENOENT]: Source or destination frame doesn't exist
4141- - [EINVAL]: Rectangle out of bounds, or source equals destination
4242- with overlapping regions
4141+ - [EINVAL]: Rectangle out of bounds, or source equals destination with
4242+ overlapping regions
4343 - [ENOSPC]: Not enough storage after composition
44444545 {2 Example}
46464747 {[
4848 (* Copy a 32x32 sprite from frame 2 to frame 5 *)
4949- let comp = Compose.make
5050- ~source_frame:2 ~dest_frame:5
5151- ~width:32 ~height:32
5252- ~source_x:0 ~source_y:0 (* From top-left of source *)
5353- ~dest_x:100 ~dest_y:50 () (* To position in dest *)
4949+ let comp =
5050+ Compose.make ~source_frame:2 ~dest_frame:5 ~width:32 ~height:32
5151+ ~source_x:0 ~source_y:0 (* From top-left of source *)
5252+ ~dest_x:100 ~dest_y:50 () (* To position in dest *)
5453 in
5554 Kgp.compose ~image_id:1 comp
5655 ]} *)
···7271 t
7372(** Create a composition operation.
74737575- @param source_frame 1-based frame number to copy from. Required.
7676- Protocol key: [r].
7777- @param dest_frame 1-based frame number to copy onto. Required.
7878- Protocol key: [c].
7979- @param width Width of rectangle in pixels. Default is full frame.
8080- Protocol key: [w].
8181- @param height Height of rectangle in pixels. Default is full frame.
8282- Protocol key: [h].
8383- @param source_x Left edge of source rectangle (default 0).
8484- Protocol key: [X].
8585- @param source_y Top edge of source rectangle (default 0).
8686- Protocol key: [Y].
8787- @param dest_x Left edge of destination position (default 0).
8888- Protocol key: [x].
8989- @param dest_y Top edge of destination position (default 0).
9090- Protocol key: [y].
9191- @param composition Blending mode. Default is alpha blending.
9292- Protocol key: [C]. *)
7474+ @param source_frame
7575+ 1-based frame number to copy from. Required. Protocol key: [r].
7676+ @param dest_frame
7777+ 1-based frame number to copy onto. Required. Protocol key: [c].
7878+ @param width
7979+ Width of rectangle in pixels. Default is full frame. Protocol key: [w].
8080+ @param height
8181+ Height of rectangle in pixels. Default is full frame. Protocol key: [h].
8282+ @param source_x
8383+ Left edge of source rectangle (default 0). Protocol key: [X].
8484+ @param source_y Top edge of source rectangle (default 0). Protocol key: [Y].
8585+ @param dest_x
8686+ Left edge of destination position (default 0). Protocol key: [x].
8787+ @param dest_y
8888+ Top edge of destination position (default 0). Protocol key: [y].
8989+ @param composition
9090+ Blending mode. Default is alpha blending. Protocol key: [C]. *)
93919492(** {1 Field Accessors} *)
9593
+1-3
lib/kgp_composition.ml
···5566type t = [ `Alpha_blend | `Overwrite ]
7788-let to_int : t -> int = function
99- | `Alpha_blend -> 0
1010- | `Overwrite -> 1
88+let to_int : t -> int = function `Alpha_blend -> 0 | `Overwrite -> 1
+7-7
lib/kgp_composition.mli
···991010 {2 Protocol Details}
11111212- The composition mode is specified via the [X] key in the control data
1313- (for animation frames) or the [C] key (for frame composition operations):
1212+ The composition mode is specified via the [X] key in the control data (for
1313+ animation frames) or the [C] key (for frame composition operations):
1414 - Value 0 or omitted: alpha blending (default)
1515 - Value 1: simple overwrite/replacement
1616···4141 - [`Alpha_blend] - Full alpha blending (default). Source pixels are
4242 composited onto the destination using standard Porter-Duff "over"
4343 compositing based on the source alpha channel.
4444- - [`Overwrite] - Simple pixel replacement. Source pixels completely
4545- replace destination pixels, ignoring alpha values. Faster but no
4646- transparency support. *)
4444+ - [`Overwrite] - Simple pixel replacement. Source pixels completely replace
4545+ destination pixels, ignoring alpha values. Faster but no transparency
4646+ support. *)
47474848val to_int : t -> int
4949(** Convert to protocol integer.
50505151- Returns 0 for [`Alpha_blend] or 1 for [`Overwrite].
5252- These values are used in the [X=] or [C=] control data keys. *)
5151+ Returns 0 for [`Alpha_blend] or 1 for [`Overwrite]. These values are used in
5252+ the [X=] or [C=] control data keys. *)
+1-3
lib/kgp_compression.ml
···5566type t = [ `None | `Zlib ]
7788-let to_char : t -> char option = function
99- | `None -> None
1010- | `Zlib -> Some 'z'
88+let to_char : t -> char option = function `None -> None | `Zlib -> Some 'z'
+8-8
lib/kgp_compression.mli
···1313 - No [o] key means no compression
1414 - [o=z] means zlib (RFC 1950 DEFLATE) compression
15151616- Compression is applied to the raw pixel/PNG data {i before} base64
1717- encoding. The terminal decompresses after base64 decoding.
1616+ Compression is applied to the raw pixel/PNG data {i before} base64 encoding.
1717+ The terminal decompresses after base64 decoding.
18181919 {2 When to Use Compression}
2020···3131 {2 PNG with Compression}
32323333 When using both [{`Png}] format and [{`Zlib}] compression, the [size]
3434- parameter must be specified with the original (uncompressed) PNG size.
3535- The terminal needs this to allocate the correct buffer for decompression. *)
3434+ parameter must be specified with the original (uncompressed) PNG size. The
3535+ terminal needs this to allocate the correct buffer for decompression. *)
36363737type t = [ `None | `Zlib ]
3838(** Compression options.
39394040 - [`None] - Raw uncompressed data. No [o=] key is sent.
4141- - [`Zlib] - RFC 1950 zlib/DEFLATE compression. Data is compressed
4242- before base64 encoding and decompressed by the terminal. *)
4141+ - [`Zlib] - RFC 1950 zlib/DEFLATE compression. Data is compressed before
4242+ base64 encoding and decompressed by the terminal. *)
43434444val to_char : t -> char option
4545(** Convert to protocol character.
46464747- Returns [None] for [`None] (no key sent), or [Some 'z'] for [`Zlib].
4848- When [Some c] is returned, [o=c] is added to the control data. *)
4747+ Returns [None] for [`None] (no key sent), or [Some 'z'] for [`Zlib]. When
4848+ [Some c] is returned, [o=c] is added to the control data. *)
+1-3
lib/kgp_cursor.ml
···5566type t = [ `Move | `Static ]
7788-let to_int : t -> int = function
99- | `Move -> 0
1010- | `Static -> 1
88+let to_int : t -> int = function `Move -> 0 | `Static -> 1
+11-11
lib/kgp_cursor.mli
···2121 - Right by the number of columns the image occupies
2222 - Down by the number of rows the image occupies
23232424- This matches how the cursor moves after printing text, allowing images
2525- to flow naturally with text content.
2424+ This matches how the cursor moves after printing text, allowing images to
2525+ flow naturally with text content.
26262727 {2 Static Cursor}
2828···34343535 {2 Relative Placements}
36363737- Note: When using relative placements (positioning images relative to
3838- other placements), the cursor never moves regardless of this setting. *)
3737+ Note: When using relative placements (positioning images relative to other
3838+ placements), the cursor never moves regardless of this setting. *)
39394040type t = [ `Move | `Static ]
4141(** Cursor movement behavior.
42424343- - [`Move] - Advance cursor past the displayed image (default).
4444- Cursor moves right by the number of columns and down by the
4545- number of rows occupied by the image.
4646- - [`Static] - Keep cursor at its original position. The image
4747- is displayed but cursor position is unchanged. *)
4343+ - [`Move] - Advance cursor past the displayed image (default). Cursor moves
4444+ right by the number of columns and down by the number of rows occupied by
4545+ the image.
4646+ - [`Static] - Keep cursor at its original position. The image is displayed
4747+ but cursor position is unchanged. *)
48484949val to_int : t -> int
5050(** Convert to protocol integer.
51515252- Returns 0 for [`Move] or 1 for [`Static].
5353- These values are used in the [C=] control data key. *)
5252+ Returns 0 for [`Move] or 1 for [`Static]. These values are used in the [C=]
5353+ control data key. *)
+24-23
lib/kgp_delete.mli
···991010 {2 Protocol Details}
11111212- Deletion is performed with action [a=d] and the [d] key specifies
1313- the deletion type. The [d] key uses single characters:
1212+ Deletion is performed with action [a=d] and the [d] key specifies the
1313+ deletion type. The [d] key uses single characters:
14141515 {v
1616 | Char | Meaning |
···30303131 {2 Placements vs Image Data}
32323333- Each deletion type can optionally free image data (controlled by the
3434- [~free] parameter in the delete command):
3333+ Each deletion type can optionally free image data (controlled by the [~free]
3434+ parameter in the delete command):
3535 - {b Without free}: Removes placements only. The image data remains in
3636 memory and can be displayed again later. (Protocol: lowercase char)
3737- - {b With free}: Removes placements AND frees the image data. The
3838- image cannot be displayed again without retransmitting. (Protocol:
3939- uppercase char)
3737+ - {b With free}: Removes placements AND frees the image data. The image
3838+ cannot be displayed again without retransmitting. (Protocol: uppercase
3939+ char)
40404141 {2 Placement IDs}
42424343 When deleting by image ID or number, an optional placement ID can be
4444- specified to delete only a specific placement. If [None], all placements
4545- of that image are deleted.
4444+ specified to delete only a specific placement. If [None], all placements of
4545+ that image are deleted.
46464747 {2 Coordinate-Based Deletion}
48484949- For [{`At_cell}] and [{`At_cell_z}], coordinates are 0-based cell
5050- positions (not pixel positions). Only placements that intersect the
5151- specified cell are deleted.
4949+ For [{`At_cell}] and [{`At_cell_z}], coordinates are 0-based cell positions
5050+ (not pixel positions). Only placements that intersect the specified cell are
5151+ deleted.
52525353 {2 Virtual Placements}
54545555- Virtual placements (used for Unicode placeholder mode) are only affected
5656- by: [{`By_id}], [{`By_number}], and [{`By_id_range}]. Other deletion
5757- commands do not affect virtual placements. *)
5555+ Virtual placements (used for Unicode placeholder mode) are only affected by:
5656+ [{`By_id}], [{`By_number}], and [{`By_id_range}]. Other deletion commands do
5757+ not affect virtual placements. *)
58585959type t =
6060 [ `All_visible
···8080 - [`By_z_index z] - All placements with z-index z
81818282 {b ID-based:}
8383- - [`By_id (id, placement_id)] - By image ID. If [placement_id] is
8484- [Some p], only that specific placement; if [None], all placements.
8585- - [`By_number (n, placement_id)] - By image number (newest image
8686- with that number). Placement ID works as above.
8383+ - [`By_id (id, placement_id)] - By image ID. If [placement_id] is [Some p],
8484+ only that specific placement; if [None], all placements.
8585+ - [`By_number (n, placement_id)] - By image number (newest image with that
8686+ number). Placement ID works as above.
8787 - [`By_id_range (min, max)] - All images with IDs in range [min..max]
88888989 {b Animation:}
9090 - [`Frames] - Animation frames only (not the base image)
91919292- Use the [~free] parameter in the delete command to also release
9393- image data from memory. *)
9292+ Use the [~free] parameter in the delete command to also release image data
9393+ from memory. *)
94949595val to_char : free:bool -> t -> char
9696(** Convert to protocol character for the delete command.
97979898 Returns the character used in the [d=] control data key.
9999- @param free If true, returns uppercase (frees data); if false,
100100- returns lowercase (keeps data). *)
9999+ @param free
100100+ If true, returns uppercase (frees data); if false, returns lowercase
101101+ (keeps data). *)
+1-4
lib/kgp_format.ml
···5566type t = [ `Rgba32 | `Rgb24 | `Png ]
7788-let to_int : t -> int = function
99- | `Rgba32 -> 32
1010- | `Rgb24 -> 24
1111- | `Png -> 100
88+let to_int : t -> int = function `Rgba32 -> 32 | `Rgb24 -> 24 | `Png -> 100
+13-13
lib/kgp_format.mli
···1717 {2 Raw Pixel Formats}
18181919 For [{`Rgb24}] and [{`Rgba32}], the data consists of raw pixel values in
2020- row-major order (left-to-right, top-to-bottom). The image dimensions must
2121- be specified via the [width] and [height] parameters.
2020+ row-major order (left-to-right, top-to-bottom). The image dimensions must be
2121+ specified via the [width] and [height] parameters.
22222323 - [{`Rgb24}]: 3 bytes per pixel in sRGB color space (red, green, blue)
2424 - [{`Rgba32}]: 4 bytes per pixel (red, green, blue, alpha)
···2828 For [{`Png}], the data is a complete PNG image. The terminal extracts
2929 dimensions from PNG metadata, so [width] and [height] are optional.
30303131- When using both PNG format and zlib compression, you must also specify
3232- the [size] parameter with the uncompressed PNG data size. *)
3131+ When using both PNG format and zlib compression, you must also specify the
3232+ [size] parameter with the uncompressed PNG data size. *)
33333434type t = [ `Rgba32 | `Rgb24 | `Png ]
3535(** Image data formats.
36363737- - [`Rgba32] - 32-bit RGBA (4 bytes per pixel). Default format.
3838- Pixels are ordered red, green, blue, alpha. Alpha of 255 is fully
3939- opaque, 0 is fully transparent.
4040- - [`Rgb24] - 24-bit RGB (3 bytes per pixel). No alpha channel;
4141- pixels are fully opaque. More compact than RGBA for opaque images.
3737+ - [`Rgba32] - 32-bit RGBA (4 bytes per pixel). Default format. Pixels are
3838+ ordered red, green, blue, alpha. Alpha of 255 is fully opaque, 0 is fully
3939+ transparent.
4040+ - [`Rgb24] - 24-bit RGB (3 bytes per pixel). No alpha channel; pixels are
4141+ fully opaque. More compact than RGBA for opaque images.
4242 - [`Png] - PNG encoded data. The terminal decodes the PNG internally.
4343- Supports all PNG color types and bit depths. Most convenient format
4444- as dimensions are embedded in the data. *)
4343+ Supports all PNG color types and bit depths. Most convenient format as
4444+ dimensions are embedded in the data. *)
45454646val to_int : t -> int
4747(** Convert to protocol integer value.
48484949- Returns 24 for [`Rgb24], 32 for [`Rgba32], or 100 for [`Png].
5050- These values are used in the [f=] control data key. *)
4949+ Returns 24 for [`Rgb24], 32 for [`Rgba32], or 100 for [`Png]. These values
5050+ are used in the [f=] control data key. *)
···5566(** Animation Frame Configuration
7788- Configuration for adding or editing animation frames. Frames can be
99- full images or partial updates (rectangles), with options for timing
1010- and composition.
88+ Configuration for adding or editing animation frames. Frames can be full
99+ images or partial updates (rectangles), with options for timing and
1010+ composition.
11111212 {2 Protocol Overview}
13131414- Animations are created by:
1515- 1. Transmitting a base image (becomes frame 1)
1616- 2. Adding frames using the frame action ([a=f])
1717- 3. Controlling playback with animation commands
1414+ Animations are created by: 1. Transmitting a base image (becomes frame 1) 2.
1515+ Adding frames using the frame action ([a=f]) 3. Controlling playback with
1616+ animation commands
18171918 Frame numbers are 1-based:
2019 - Frame 1: The original/base image
···22212322 {2 Frame Positioning}
24232525- For partial frame updates, [x] and [y] specify where the new pixel
2626- data is placed within the frame canvas:
2424+ For partial frame updates, [x] and [y] specify where the new pixel data is
2525+ placed within the frame canvas:
2726 - [x]: Left edge position in pixels (default 0)
2827 - [y]: Top edge position in pixels (default 0)
29283030- The frame data dimensions come from the [width] and [height] parameters
3131- of the frame command.
2929+ The frame data dimensions come from the [width] and [height] parameters of
3030+ the frame command.
32313332 {2 Frame Canvas}
34333534 Each frame needs a background canvas to composite onto. Options:
36353737- {b Solid color background} ([background_color]):
3838- Use a 32-bit RGBA color. Format: [0xRRGGBBAA] where AA is alpha.
3939- Default is 0 (transparent black).
3636+ {b Solid color background} ([background_color]): Use a 32-bit RGBA color.
3737+ Format: [0xRRGGBBAA] where AA is alpha. Default is 0 (transparent black).
40384141- {b Copy from existing frame} ([base_frame]):
4242- Use another frame as the starting canvas. Specified as 1-based frame
4343- number. The base frame's pixels are copied, then new data is composited.
3939+ {b Copy from existing frame} ([base_frame]): Use another frame as the
4040+ starting canvas. Specified as 1-based frame number. The base frame's pixels
4141+ are copied, then new data is composited.
44424543 {2 Editing Existing Frames}
4644···51495250 {2 Frame Timing}
53515454- The [gap_ms] parameter controls the delay before transitioning to
5555- the next frame:
5252+ The [gap_ms] parameter controls the delay before transitioning to the next
5353+ frame:
5654 - Positive value: Delay in milliseconds
5755 - Zero: Ignored (keeps existing gap)
5858- - Negative value: "Gapless" frame - not displayed, used as a base
5959- for other frames
5656+ - Negative value: "Gapless" frame - not displayed, used as a base for other
5757+ frames
60586161- Default gap for new frames is 40ms. The root frame (frame 1) has
6262- a default gap of 0ms.
5959+ Default gap for new frames is 40ms. The root frame (frame 1) has a default
6060+ gap of 0ms.
63616462 {2 Composition Mode}
65636666- The [composition] parameter controls how new pixel data is blended
6767- onto the canvas. *)
6464+ The [composition] parameter controls how new pixel data is blended onto the
6565+ canvas. *)
68666967type t
7068(** Animation frame configuration. Opaque type; use {!make} to construct. *)
···8179 t
8280(** Create a frame specification.
83818484- @param x Left edge where frame data is placed in pixels (default 0).
8585- Protocol key: [x].
8686- @param y Top edge where frame data is placed in pixels (default 0).
8787- Protocol key: [y].
8888- @param base_frame 1-based frame number to use as background canvas.
8989- Frame 1 is the root image. Protocol key: [c].
9090- @param edit_frame 1-based frame number to edit instead of creating new.
9191- If 0 or unset, a new frame is created. Protocol key: [r].
9292- @param gap_ms Delay before next frame in milliseconds. Negative values
9393- create gapless frames. Protocol key: [z].
9494- @param composition How to blend new pixels onto the canvas.
9595- Default is alpha blending. Protocol key: [X].
9696- @param background_color 32-bit RGBA background color when not using
9797- a base frame. Format: [0xRRGGBBAA]. Protocol key: [Y]. *)
8282+ @param x
8383+ Left edge where frame data is placed in pixels (default 0). Protocol key:
8484+ [x].
8585+ @param y
8686+ Top edge where frame data is placed in pixels (default 0). Protocol key:
8787+ [y].
8888+ @param base_frame
8989+ 1-based frame number to use as background canvas. Frame 1 is the root
9090+ image. Protocol key: [c].
9191+ @param edit_frame
9292+ 1-based frame number to edit instead of creating new. If 0 or unset, a new
9393+ frame is created. Protocol key: [r].
9494+ @param gap_ms
9595+ Delay before next frame in milliseconds. Negative values create gapless
9696+ frames. Protocol key: [z].
9797+ @param composition
9898+ How to blend new pixels onto the canvas. Default is alpha blending.
9999+ Protocol key: [X].
100100+ @param background_color
101101+ 32-bit RGBA background color when not using a base frame. Format:
102102+ [0xRRGGBBAA]. Protocol key: [Y]. *)
9810399104val empty : t
100105(** Empty frame spec with all defaults.
101106102102- Creates a new frame with transparent black background, composited
103103- at position (0, 0) with default timing (40ms gap). *)
107107+ Creates a new frame with transparent black background, composited at
108108+ position (0, 0) with default timing (40ms gap). *)
104109105110(** {1 Field Accessors} *)
106111
+44-36
lib/kgp_placement.mli
···2323 - [source_x], [source_y]: Top-left corner in pixels (default: 0, 0)
2424 - [source_width], [source_height]: Size in pixels (default: full image)
25252626- The displayed area is the intersection of this rectangle with the
2727- actual image bounds. This allows cropping images without modifying
2828- the original data.
2626+ The displayed area is the intersection of this rectangle with the actual
2727+ image bounds. This allows cropping images without modifying the original
2828+ data.
29293030 {2 Cell-Based Sizing}
3131···3333 - [columns]: Number of columns to span (width in cells)
3434 - [rows]: Number of rows to span (height in cells)
35353636- If both are specified, the source rectangle is scaled to fit.
3737- If only one is specified, the other is computed to maintain aspect ratio.
3838- If neither is specified, the image is displayed at natural size.
3636+ If both are specified, the source rectangle is scaled to fit. If only one is
3737+ specified, the other is computed to maintain aspect ratio. If neither is
3838+ specified, the image is displayed at natural size.
39394040 {2 Pixel Offsets}
4141···5151 - Positive values: drawn above text
5252 - Zero: drawn at text level
5353 - Negative values: drawn below text
5454- - Values < INT32_MIN/2 (-1,073,741,824): drawn under cells with
5555- non-default background colors
5454+ - Values < INT32_MIN/2 (-1,073,741,824): drawn under cells with non-default
5555+ background colors
56565757- Overlapping images with the same z-index are ordered by image ID
5858- (lower ID draws first/underneath).
5757+ Overlapping images with the same z-index are ordered by image ID (lower ID
5858+ draws first/underneath).
59596060 {2 Placement IDs}
6161···6565 - Deleting specific placements
6666 - Moving placements by resending with same image_id + placement_id
67676868- If [placement_id] is 0 or unspecified, each display creates an
6969- independent placement. *)
6868+ If [placement_id] is 0 or unspecified, each display creates an independent
6969+ placement. *)
70707171type t
7272(** Placement configuration. Opaque type; use {!make} to construct. *)
···8888 t
8989(** Create a placement configuration.
90909191- @param source_x Left edge of source rectangle in pixels (default 0).
9292- Protocol key: [x].
9393- @param source_y Top edge of source rectangle in pixels (default 0).
9494- Protocol key: [y].
9595- @param source_width Width of source rectangle in pixels.
9696- Default is the full image width. Protocol key: [w].
9797- @param source_height Height of source rectangle in pixels.
9898- Default is the full image height. Protocol key: [h].
9999- @param cell_x_offset X offset within the first cell in pixels.
100100- Must be smaller than cell width. Protocol key: [X].
101101- @param cell_y_offset Y offset within the first cell in pixels.
102102- Must be smaller than cell height. Protocol key: [Y].
103103- @param columns Number of columns to display over. Image is scaled
104104- to fit. Protocol key: [c].
105105- @param rows Number of rows to display over. Image is scaled to fit.
106106- Protocol key: [r].
107107- @param z_index Stacking order. Positive = above text, negative = below.
108108- Protocol key: [z].
109109- @param placement_id Unique ID (1-4294967295) for this placement.
110110- Allows updating/deleting specific placements. Protocol key: [p].
9191+ @param source_x
9292+ Left edge of source rectangle in pixels (default 0). Protocol key: [x].
9393+ @param source_y
9494+ Top edge of source rectangle in pixels (default 0). Protocol key: [y].
9595+ @param source_width
9696+ Width of source rectangle in pixels. Default is the full image width.
9797+ Protocol key: [w].
9898+ @param source_height
9999+ Height of source rectangle in pixels. Default is the full image height.
100100+ Protocol key: [h].
101101+ @param cell_x_offset
102102+ X offset within the first cell in pixels. Must be smaller than cell width.
103103+ Protocol key: [X].
104104+ @param cell_y_offset
105105+ Y offset within the first cell in pixels. Must be smaller than cell
106106+ height. Protocol key: [Y].
107107+ @param columns
108108+ Number of columns to display over. Image is scaled to fit. Protocol key:
109109+ [c].
110110+ @param rows
111111+ Number of rows to display over. Image is scaled to fit. Protocol key: [r].
112112+ @param z_index
113113+ Stacking order. Positive = above text, negative = below. Protocol key:
114114+ [z].
115115+ @param placement_id
116116+ Unique ID (1-4294967295) for this placement. Allows updating/deleting
117117+ specific placements. Protocol key: [p].
111118 @param cursor Cursor movement policy after display.
112112- @param unicode_placeholder If true, creates a virtual (invisible)
113113- placement for Unicode placeholder mode. Protocol key: [U=1]. *)
119119+ @param unicode_placeholder
120120+ If true, creates a virtual (invisible) placement for Unicode placeholder
121121+ mode. Protocol key: [U=1]. *)
114122115123val empty : t
116124(** Empty placement with all defaults.
117125118118- Equivalent to [make ()]. The image displays at natural size at the
119119- current cursor position with default z-index (0). *)
126126+ Equivalent to [make ()]. The image displays at natural size at the current
127127+ cursor position with default z-index (0). *)
120128121129(** {1 Field Accessors} *)
122130
+14-14
lib/kgp_quiet.mli
···2020 - On success: [ESC _Gi=ID;OK ESC]
2121 - On failure: [ESC _Gi=ID;ECODE:message ESC]
22222323- Response processing requires reading from the terminal, which can be
2424- complex in some applications.
2323+ Response processing requires reading from the terminal, which can be complex
2424+ in some applications.
25252626 {2 Use Cases}
27272828- [{`Noisy}] (default): Use when you need to verify operations succeeded
2929- or want to handle errors programmatically.
2828+ [{`Noisy}] (default): Use when you need to verify operations succeeded or
2929+ want to handle errors programmatically.
30303131 [{`Errors_only}]: Use when you want to detect failures but don't need
3232 confirmation of success. Reduces response traffic.
33333434- [{`Silent}]: Use in fire-and-forget scenarios like shell scripts or
3535- when the application cannot easily read terminal responses. Also useful
3636- for high-frequency animation updates where response processing would
3737- add latency. *)
3434+ [{`Silent}]: Use in fire-and-forget scenarios like shell scripts or when the
3535+ application cannot easily read terminal responses. Also useful for
3636+ high-frequency animation updates where response processing would add
3737+ latency. *)
38383939type t = [ `Noisy | `Errors_only | `Silent ]
4040(** Response suppression levels.
41414242 - [`Noisy] - Send all responses including OK confirmations (default).
4343 Required for detecting success and getting assigned image IDs.
4444- - [`Errors_only] - Suppress OK responses, only send error messages.
4545- Useful when success is expected but errors should be caught.
4646- - [`Silent] - Suppress all responses including errors. Useful for
4747- shell scripts or when response handling is not possible. *)
4444+ - [`Errors_only] - Suppress OK responses, only send error messages. Useful
4545+ when success is expected but errors should be caught.
4646+ - [`Silent] - Suppress all responses including errors. Useful for shell
4747+ scripts or when response handling is not possible. *)
48484949val to_int : t -> int
5050(** Convert to protocol integer.
51515252- Returns 0 for [`Noisy], 1 for [`Errors_only], or 2 for [`Silent].
5353- These values are used in the [q=] control data key. *)
5252+ Returns 0 for [`Noisy], 1 for [`Errors_only], or 2 for [`Silent]. These
5353+ values are used in the [q=] control data key. *)
+1-1
lib/kgp_response.ml
···2020 else
2121 String.index_opt t.message ':'
2222 |> Option.fold ~none:(Some t.message) ~some:(fun i ->
2323- Some (String.sub t.message 0 i))
2323+ Some (String.sub t.message 0 i))
24242525let image_id t = t.image_id
2626let image_number t = t.image_number
+27-31
lib/kgp_terminal.ml
···88type graphics_mode = [ `Auto | `Enabled | `Disabled | `Tmux ]
991010let is_kitty () =
1111- Option.is_some (Sys.getenv_opt "KITTY_WINDOW_ID") ||
1212- (match Sys.getenv_opt "TERM" with
1313- | Some term -> String.lowercase_ascii term = "xterm-kitty"
1414- | None -> false) ||
1515- (match Sys.getenv_opt "TERM_PROGRAM" with
1616- | Some prog -> String.lowercase_ascii prog = "kitty"
1717- | None -> false)
1111+ Option.is_some (Sys.getenv_opt "KITTY_WINDOW_ID")
1212+ || (match Sys.getenv_opt "TERM" with
1313+ | Some term -> String.lowercase_ascii term = "xterm-kitty"
1414+ | None -> false)
1515+ ||
1616+ match Sys.getenv_opt "TERM_PROGRAM" with
1717+ | Some prog -> String.lowercase_ascii prog = "kitty"
1818+ | None -> false
18191920let is_wezterm () =
2020- Option.is_some (Sys.getenv_opt "WEZTERM_PANE") ||
2121- (match Sys.getenv_opt "TERM_PROGRAM" with
2222- | Some prog -> String.lowercase_ascii prog = "wezterm"
2323- | None -> false)
2121+ Option.is_some (Sys.getenv_opt "WEZTERM_PANE")
2222+ ||
2323+ match Sys.getenv_opt "TERM_PROGRAM" with
2424+ | Some prog -> String.lowercase_ascii prog = "wezterm"
2525+ | None -> false
24262527let is_ghostty () =
2626- Option.is_some (Sys.getenv_opt "GHOSTTY_RESOURCES_DIR") ||
2727- (match Sys.getenv_opt "TERM_PROGRAM" with
2828- | Some prog -> String.lowercase_ascii prog = "ghostty"
2929- | None -> false)
3030-3131-let is_graphics_terminal () =
3232- is_kitty () || is_wezterm () || is_ghostty ()
2828+ Option.is_some (Sys.getenv_opt "GHOSTTY_RESOURCES_DIR")
2929+ ||
3030+ match Sys.getenv_opt "TERM_PROGRAM" with
3131+ | Some prog -> String.lowercase_ascii prog = "ghostty"
3232+ | None -> false
33333434+let is_graphics_terminal () = is_kitty () || is_wezterm () || is_ghostty ()
3435let is_tmux () = Kgp_tmux.is_active ()
3535-3636-let is_interactive () =
3737- Unix.isatty Unix.stdout
3636+let is_interactive () = Unix.isatty Unix.stdout
38373938let is_pager () =
4039 (* Not interactive = likely piped to pager *)
4141- not (is_interactive ()) ||
4040+ (not (is_interactive ()))
4141+ ||
4242 (* PAGER set and not in a known graphics terminal *)
4343 (Option.is_some (Sys.getenv_opt "PAGER") && not (is_graphics_terminal ()))
4444···4747 | `Enabled -> `Graphics
4848 | `Tmux -> `Tmux
4949 | `Auto ->
5050- if is_pager () || not (is_interactive ()) then
5151- `Placeholder
5252- else if is_tmux () then
5353- (* Inside tmux - use passthrough if underlying terminal supports graphics *)
5454- if is_graphics_terminal () then `Tmux
5050+ if is_pager () || not (is_interactive ()) then `Placeholder
5151+ else if is_tmux () then
5252+ (* Inside tmux - use passthrough if underlying terminal supports graphics *)
5353+ if is_graphics_terminal () then `Tmux else `Placeholder
5454+ else if is_graphics_terminal () then `Graphics
5555 else `Placeholder
5656- else if is_graphics_terminal () then
5757- `Graphics
5858- else
5959- `Placeholder
60566157let supports_graphics mode =
6258 match resolve_mode mode with
+8-9
lib/kgp_tmux.ml
···5566(* Tmux Passthrough Support - Implementation *)
7788-let is_active () =
99- Option.is_some (Sys.getenv_opt "TMUX")
88+let is_active () = Option.is_some (Sys.getenv_opt "TMUX")
1091110let write_wrapped buf s =
1211 (* DCS passthrough prefix: ESC P tmux ; *)
1312 Buffer.add_string buf "\027Ptmux;";
1413 (* Double all ESC characters in the content *)
1515- String.iter (fun c ->
1616- if c = '\027' then Buffer.add_string buf "\027\027"
1717- else Buffer.add_char buf c
1818- ) s;
1414+ String.iter
1515+ (fun c ->
1616+ if c = '\027' then Buffer.add_string buf "\027\027"
1717+ else Buffer.add_char buf c)
1818+ s;
1919 (* DCS terminator: ESC \ *)
2020 Buffer.add_string buf "\027\\"
21212222let wrap_always s =
2323- let buf = Buffer.create (String.length s * 2 + 10) in
2323+ let buf = Buffer.create ((String.length s * 2) + 10) in
2424 write_wrapped buf s;
2525 Buffer.contents buf
26262727-let wrap s =
2828- if is_active () then wrap_always s else s
2727+let wrap s = if is_active () then wrap_always s else s
+20-19
lib/kgp_tmux.mli
···5566(** Tmux Passthrough Support
7788- Support for passing graphics protocol escape sequences through tmux
99- to the underlying terminal emulator.
88+ Support for passing graphics protocol escape sequences through tmux to the
99+ underlying terminal emulator.
10101111 {2 Background}
12121313 When running inside tmux, graphics protocol escape sequences need to be
1414- wrapped in a DCS (Device Control String) passthrough sequence so that
1515- tmux forwards them to the actual terminal (kitty, wezterm, ghostty, etc.)
1616- rather than interpreting them itself.
1414+ wrapped in a DCS (Device Control String) passthrough sequence so that tmux
1515+ forwards them to the actual terminal (kitty, wezterm, ghostty, etc.) rather
1616+ than interpreting them itself.
17171818 The passthrough format is:
1919 - Prefix: [ESC P tmux ;]
···2323 {2 Requirements}
24242525 For tmux passthrough to work:
2626- - tmux version 3.3 or later
2727- - [allow-passthrough] must be enabled in tmux.conf:
2828- {v set -g allow-passthrough on v}
2626+ {ul
2727+ {- tmux version 3.3 or later }
2828+ {- [allow-passthrough] must be enabled in tmux.conf:
2929+ {v set -g allow-passthrough on v}
3030+ }
3131+ }
29323033 {2 Usage}
3134···3336 if Kgp.Tmux.is_active () then
3437 let wrapped = Kgp.Tmux.wrap graphics_command in
3538 print_string wrapped
3636- else
3737- print_string graphics_command
3939+ else print_string graphics_command
3840 ]} *)
39414042val is_active : unit -> bool
4143(** Detect if we are running inside tmux.
42444343- Returns [true] if the [TMUX] environment variable is set,
4444- indicating the process is running inside a tmux session. *)
4545+ Returns [true] if the [TMUX] environment variable is set, indicating the
4646+ process is running inside a tmux session. *)
45474648val wrap : string -> string
4749(** Wrap an escape sequence for tmux passthrough.
48504949- Takes a graphics protocol escape sequence and wraps it in the
5050- tmux DCS passthrough format:
5151+ Takes a graphics protocol escape sequence and wraps it in the tmux DCS
5252+ passthrough format:
5153 - Adds [ESC P tmux ;] prefix
5254 - Doubles all ESC characters in the content
5355 - Adds [ESC] suffix
···5759val wrap_always : string -> string
5860(** Wrap an escape sequence for tmux passthrough unconditionally.
59616060- Like {!wrap} but always applies the wrapping, regardless of
6161- whether we are inside tmux. Useful when you want to pre-generate
6262- tmux-compatible output. *)
6262+ Like {!wrap} but always applies the wrapping, regardless of whether we are
6363+ inside tmux. Useful when you want to pre-generate tmux-compatible output. *)
63646465val write_wrapped : Buffer.t -> string -> unit
6566(** Write a wrapped escape sequence directly to a buffer.
66676767- More efficient than {!wrap_always} when building output in a buffer,
6868- as it avoids allocating an intermediate string. *)
6868+ More efficient than {!wrap_always} when building output in a buffer, as it
6969+ avoids allocating an intermediate string. *)
+11-11
lib/kgp_transmission.mli
···16161717 {2 Direct Transmission}
18181919- [{`Direct}] sends data inline within the escape sequence itself. The data
2020- is base64-encoded in the payload section. This is the simplest method and
2121- works over any connection (including SSH).
1919+ [{`Direct}] sends data inline within the escape sequence itself. The data is
2020+ base64-encoded in the payload section. This is the simplest method and works
2121+ over any connection (including SSH).
22222323 For images larger than 4096 bytes (after base64 encoding), the data is
2424 automatically split into chunks using the [m=] key:
···27272828 {2 File Transmission}
29293030- [{`File}] tells the terminal to read data from a file path. The path is
3131- sent base64-encoded in the payload. Additional parameters:
3030+ [{`File}] tells the terminal to read data from a file path. The path is sent
3131+ base64-encoded in the payload. Additional parameters:
3232 - [S=] specifies the number of bytes to read
3333 - [O=] specifies the byte offset to start reading from
34343535- File transmission only works when the terminal and client share a
3636- filesystem (i.e., local terminals, not SSH).
3535+ File transmission only works when the terminal and client share a filesystem
3636+ (i.e., local terminals, not SSH).
37373838- Security: The terminal will refuse to read device files, sockets, or
3939- files in sensitive locations like [/proc], [/sys], or [/dev].
3838+ Security: The terminal will refuse to read device files, sockets, or files
3939+ in sensitive locations like [/proc], [/sys], or [/dev].
40404141 {2 Temporary File Transmission}
4242···5353 - [`Direct] - Data is sent inline in the escape sequence (base64-encoded).
5454 Works over any connection including SSH. Automatic chunking for large
5555 images.
5656- - [`File] - Terminal reads from a file path sent in the payload.
5757- Only works when terminal and client share a filesystem.
5656+ - [`File] - Terminal reads from a file path sent in the payload. Only works
5757+ when terminal and client share a filesystem.
5858 - [`Tempfile] - Like [`File] but terminal deletes the file after reading.
5959 File must be in a recognized temporary directory. *)
6060
···5566(** Kitty Graphics Protocol Unicode Placeholders
7788- Support for invisible Unicode placeholder characters that encode
99- image position metadata for accessibility and compatibility.
88+ Support for invisible Unicode placeholder characters that encode image
99+ position metadata for accessibility and compatibility.
10101111 {2 Image ID Requirements}
1212···2727 - High byte (bits 24-31) is non-zero
2828 - Middle bytes (bits 8-23) are non-zero
29293030- This ensures the foreground color encoding and diacritic encoding
3131- work correctly. Uses [Random] internally. *)
3030+ This ensures the foreground color encoding and diacritic encoding work
3131+ correctly. Uses [Random] internally. *)
32323333val write :
3434 Buffer.t ->
···4040 unit
4141(** Write placeholder characters to a buffer.
42424343- @param image_id Should be generated with {!next_image_id} for correct rendering.
4444- @param placement_id Optional placement ID for multiple placements of same image.
4343+ @param image_id
4444+ Should be generated with {!next_image_id} for correct rendering.
4545+ @param placement_id
4646+ Optional placement ID for multiple placements of same image.
4547 @param rows Number of rows in the placeholder grid.
4648 @param cols Number of columns in the placeholder grid. *)
4749