Kitty Graphics Protocol in OCaml
terminal graphics ocaml

docs

+1086 -95
+24
lib/kgp.ml
··· 10 10 module Delete = Kgp_delete 11 11 module Animation_state = Kgp_animation_state 12 12 13 + (* Type aliases *) 14 + type format = Format.t 15 + type transmission = Transmission.t 16 + type compression = Compression.t 17 + type quiet = Quiet.t 18 + type cursor = Cursor.t 19 + type composition = Composition.t 20 + type delete = Delete.t 21 + type animation_state = Animation_state.t 22 + 13 23 (* Configuration modules *) 14 24 module Placement = Kgp_placement 15 25 module Frame = Kgp_frame 16 26 module Animation = Kgp_animation 17 27 module Compose = Kgp_compose 28 + 29 + (* Command type and functions *) 30 + type command = Kgp_command.t 31 + 32 + let transmit = Kgp_command.transmit 33 + let transmit_and_display = Kgp_command.transmit_and_display 34 + let query = Kgp_command.query 35 + let display = Kgp_command.display 36 + let delete = Kgp_command.delete 37 + let frame = Kgp_command.frame 38 + let animate = Kgp_command.animate 39 + let compose = Kgp_command.compose 40 + let write = Kgp_command.write 41 + let to_string = Kgp_command.to_string 18 42 19 43 (* Core modules *) 20 44 module Command = Kgp_command
+302 -8
lib/kgp.mli
··· 4 4 OCaml programs to display images in terminals that support the protocol 5 5 (Kitty, WezTerm, Konsole, Ghostty, etc.). 6 6 7 - The protocol uses APC (Application Programming Command) escape sequences 8 - to transmit and display pixel graphics. Images can be transmitted as raw 9 - RGB/RGBA data or PNG, and displayed at specific positions with various 10 - placement options. 7 + {1 Protocol Overview} 8 + 9 + The Kitty Graphics Protocol is a flexible, performant protocol for rendering 10 + arbitrary pixel (raster) graphics in terminal emulators. Key features: 11 + 12 + - No requirement for terminal emulators to understand image formats 13 + - Pixel-level positioning of graphics 14 + - Integration with text (graphics can be drawn below/above text with alpha blending) 15 + - Automatic scrolling with text 16 + - Animation support with frame deltas for efficiency 17 + 18 + {2 Escape Sequence Format} 19 + 20 + All graphics commands use the Application Programming Command (APC) format: 21 + 22 + {v <ESC>_G<control data>;<payload><ESC>\ v} 23 + 24 + Where: 25 + - [ESC _G] is the APC start sequence (bytes 0x1B 0x5F 0x47) 26 + - Control data is comma-separated key=value pairs 27 + - Payload is base64-encoded binary data (RFC-4648) 28 + - [ESC] is the APC terminator (bytes 0x1B 0x5C) 29 + 30 + Most terminal emulators ignore unrecognized APC sequences, making the 31 + protocol safe to use even in unsupported terminals. 32 + 33 + {2 Terminal Responses} 34 + 35 + When an image ID is specified, the terminal responds: 36 + - On success: [ESC _Gi=ID;OK ESC] 37 + - On failure: [ESC _Gi=ID;error ESC] 38 + 39 + Common error codes include [ENOENT] (image not found), [EINVAL] (invalid 40 + parameter), and [ENOSPC] (storage quota exceeded). 41 + 42 + {2 Image Storage} 43 + 44 + Terminal emulators maintain a storage quota for images (typically ~320MB). 45 + When the quota is exceeded, older images are deleted to make room for new 46 + ones. Images without active placements are preferred for deletion. 47 + 48 + For animations, frame data is stored separately with a larger quota 49 + (typically 5x the base quota). 11 50 12 51 {2 Basic Usage} 13 52 14 53 {[ 15 54 (* Display a PNG image *) 16 55 let png_data = read_file "image.png" in 17 - let cmd = Kgp.Command.transmit_and_display ~format:`Png () in 56 + let cmd = Kgp.transmit_and_display ~format:`Png () in 18 57 let buf = Buffer.create 1024 in 19 - Kgp.Command.write buf cmd ~data:png_data; 58 + Kgp.write buf cmd ~data:png_data; 20 59 print_string (Buffer.contents buf) 21 60 ]} 22 61 62 + {[ 63 + (* Transmit an image, then display it multiple times *) 64 + let png_data = read_file "icon.png" in 65 + let cmd = Kgp.transmit ~image_id:1 ~format:`Png () in 66 + Kgp.write buf cmd ~data:png_data; 67 + 68 + (* Display at different positions *) 69 + let cmd = Kgp.display ~image_id:1 () in 70 + Kgp.write buf cmd ~data:""; 71 + ]} 72 + 23 73 {2 Protocol Reference} 24 74 25 75 See {{:https://sw.kovidgoyal.net/kitty/graphics-protocol/} Kitty Graphics Protocol} ··· 43 93 module Animation = Kgp_animation 44 94 module Compose = Kgp_compose 45 95 46 - (** {1 Command and Response} *) 96 + (** {1 Commands} *) 97 + 98 + type command = Kgp_command.t 99 + (** A graphics protocol command. Commands are built using the functions below 100 + and then serialized using {!write} or {!to_string}. *) 101 + 102 + (** {2 Image Transmission} 103 + 104 + Images can be transmitted to the terminal for storage and later display. 105 + The terminal assigns storage and responds with success or failure. 106 + 107 + For large images, the library automatically handles chunked transmission 108 + (splitting data into 4096-byte base64-encoded chunks). *) 109 + 110 + val transmit : 111 + ?image_id:int -> 112 + ?image_number:int -> 113 + ?format:Format.t -> 114 + ?transmission:Transmission.t -> 115 + ?compression:Compression.t -> 116 + ?width:int -> 117 + ?height:int -> 118 + ?size:int -> 119 + ?offset:int -> 120 + ?quiet:Quiet.t -> 121 + unit -> 122 + command 123 + (** Transmit image data without displaying. 124 + 125 + The image is stored by the terminal and can be displayed later using 126 + {!val:display} with the same [image_id]. 127 + 128 + @param image_id Unique identifier (1-4294967295) for later reference. 129 + If specified, the terminal responds with success/failure. 130 + @param image_number Alternative to [image_id] where the terminal assigns 131 + a unique ID and returns it in the response. Useful when multiple 132 + programs share the terminal. 133 + @param format Pixel format of the data. Default is [`Rgba32]. 134 + @param transmission How data is sent. Default is [`Direct] (inline). 135 + @param compression Compression applied to data. Default is [`None]. 136 + @param width Image width in pixels (required for raw RGB/RGBA formats). 137 + @param height Image height in pixels (required for raw RGB/RGBA formats). 138 + @param size Size in bytes when reading from file. 139 + @param offset Byte offset when reading from file. 140 + @param quiet Response suppression level. *) 141 + 142 + val transmit_and_display : 143 + ?image_id:int -> 144 + ?image_number:int -> 145 + ?format:Format.t -> 146 + ?transmission:Transmission.t -> 147 + ?compression:Compression.t -> 148 + ?width:int -> 149 + ?height:int -> 150 + ?size:int -> 151 + ?offset:int -> 152 + ?quiet:Quiet.t -> 153 + ?placement:Placement.t -> 154 + unit -> 155 + command 156 + (** Transmit image data and display it immediately. 157 + 158 + Combines transmission and display in a single command. The image is 159 + rendered at the current cursor position unless placement options 160 + specify otherwise. 161 + 162 + See {!transmit} for parameter descriptions. The [placement] parameter 163 + controls display position and scaling. *) 47 164 48 - module Command = Kgp_command 165 + val query : 166 + ?format:Format.t -> 167 + ?transmission:Transmission.t -> 168 + ?width:int -> 169 + ?height:int -> 170 + ?quiet:Quiet.t -> 171 + unit -> 172 + command 173 + (** Query terminal support without storing the image. 174 + 175 + Performs the same validation as {!transmit} but does not store the 176 + image. Useful for testing whether the terminal supports the graphics 177 + protocol and specific formats. 178 + 179 + To detect graphics support, send a query and check for a response: 180 + {[ 181 + (* Send query with a tiny 1x1 RGB image *) 182 + let cmd = Kgp.query ~format:`Rgb24 ~width:1 ~height:1 () in 183 + Kgp.write buf cmd ~data:"\x00\x00\x00" 184 + (* If terminal responds, it supports the protocol *) 185 + ]} *) 186 + 187 + (** {2 Display} 188 + 189 + Previously transmitted images can be displayed multiple times at 190 + different positions with different cropping and scaling options. *) 191 + 192 + val display : 193 + ?image_id:int -> 194 + ?image_number:int -> 195 + ?placement:Placement.t -> 196 + ?quiet:Quiet.t -> 197 + unit -> 198 + command 199 + (** Display a previously transmitted image. 200 + 201 + The image is rendered at the current cursor position. Use [placement] 202 + to control cropping, scaling, z-index, and other display options. 203 + 204 + Each display creates a "placement" of the image. Multiple placements 205 + of the same image share the underlying image data. 206 + 207 + @param image_id ID of a previously transmitted image. 208 + @param image_number Image number (acts on the newest image with this number). 209 + @param placement Display configuration (position, size, z-index, etc.). *) 210 + 211 + (** {2 Deletion} 212 + 213 + Images and placements can be deleted to free terminal resources. 214 + Lowercase delete commands remove placements but keep image data; 215 + uppercase variants also free the image data. *) 216 + 217 + val delete : ?quiet:Quiet.t -> Delete.t -> command 218 + (** Delete images or placements. 219 + 220 + See {!Delete} for the full list of deletion targets. 221 + 222 + Examples: 223 + {[ 224 + (* Delete all visible images *) 225 + Kgp.delete `All_visible 226 + 227 + (* Delete specific image, keeping data for reuse *) 228 + Kgp.delete (`By_id (42, None)) 229 + 230 + (* Delete specific image and free its data *) 231 + Kgp.delete (`By_id_and_free (42, None)) 232 + 233 + (* Delete all placements at a specific cell *) 234 + Kgp.delete (`At_cell (10, 5)) 235 + ]} *) 236 + 237 + (** {2 Animation} 238 + 239 + The protocol supports both client-driven and terminal-driven animations. 240 + Animations are created by first transmitting a base image, then adding 241 + frames with optional delta encoding for efficiency. 242 + 243 + Frame numbers are 1-based: frame 1 is the root (base) image, frame 2 244 + is the first added frame, etc. *) 245 + 246 + val frame : 247 + ?image_id:int -> 248 + ?image_number:int -> 249 + ?format:Format.t -> 250 + ?transmission:Transmission.t -> 251 + ?compression:Compression.t -> 252 + ?width:int -> 253 + ?height:int -> 254 + ?quiet:Quiet.t -> 255 + frame:Frame.t -> 256 + unit -> 257 + command 258 + (** Transmit animation frame data. 259 + 260 + Adds a new frame to an existing image or edits an existing frame. 261 + The frame can be a full image or a partial update (rectangle). 262 + 263 + Use {!Frame.make} to configure the frame's position, timing, and 264 + composition options. 265 + 266 + @param frame Frame configuration including timing and composition. *) 267 + 268 + val animate : 269 + ?image_id:int -> 270 + ?image_number:int -> 271 + ?quiet:Quiet.t -> 272 + Animation.t -> 273 + command 274 + (** Control animation playback. 275 + 276 + For terminal-driven animation: 277 + {[ 278 + (* Start infinite loop animation *) 279 + Kgp.animate ~image_id:1 (Animation.set_state ~loops:1 `Run) 280 + 281 + (* Stop animation *) 282 + Kgp.animate ~image_id:1 (Animation.set_state `Stop) 283 + 284 + (* Change frame timing *) 285 + Kgp.animate ~image_id:1 (Animation.set_gap ~frame:3 ~gap_ms:100) 286 + ]} 287 + 288 + For client-driven animation: 289 + {[ 290 + (* Manually advance to specific frame *) 291 + Kgp.animate ~image_id:1 (Animation.set_current_frame 5) 292 + ]} *) 293 + 294 + val compose : 295 + ?image_id:int -> 296 + ?image_number:int -> 297 + ?quiet:Quiet.t -> 298 + Compose.t -> 299 + command 300 + (** Compose animation frames. 301 + 302 + Copies a rectangular region from one frame onto another. Useful for 303 + building complex frames from simpler components. 304 + 305 + {[ 306 + (* Copy a 50x50 region from frame 2 to frame 5 *) 307 + let comp = Compose.make 308 + ~source_frame:2 ~dest_frame:5 309 + ~width:50 ~height:50 310 + ~source_x:10 ~source_y:10 311 + ~dest_x:20 ~dest_y:20 () in 312 + Kgp.compose ~image_id:1 comp 313 + ]} *) 314 + 315 + (** {2 Output} 316 + 317 + Commands are serialized to escape sequences that can be written 318 + to the terminal. *) 319 + 320 + val write : Buffer.t -> command -> data:string -> unit 321 + (** Write the command to a buffer. 322 + 323 + The [data] parameter contains the raw image/frame data (before base64 324 + encoding). Pass an empty string for commands that don't include payload 325 + data (like {!val:display}, {!val:delete}, {!val:animate}). 326 + 327 + The library handles base64 encoding and chunking automatically. *) 328 + 329 + val to_string : command -> data:string -> string 330 + (** Convert command to a string. 331 + 332 + Convenience wrapper around {!write} that returns the serialized 333 + command as a string. *) 334 + 335 + (** {1 Response} *) 336 + 49 337 module Response = Kgp_response 50 338 51 339 (** {1 Utilities} *) 52 340 53 341 module Unicode_placeholder = Kgp_unicode 54 342 module Detect = Kgp_detect 343 + 344 + (** {1 Low-level Access} *) 345 + 346 + module Command = Kgp_command 347 + (** Low-level command module. The command functions are also available 348 + at the top level of this module for convenience. *)
+92 -8
lib/kgp_animation.mli
··· 1 - (** Kitty Graphics Protocol Animation 1 + (** Animation Control 2 + 3 + Operations for controlling animation playback. The protocol supports 4 + both terminal-driven and client-driven animation modes. 5 + 6 + {2 Protocol Overview} 7 + 8 + Animation control uses action [a=a] with various keys: 9 + - [s]: Set playback state (1=stop, 2=loading, 3=run) 10 + - [c]: Set current frame (1-based frame number) 11 + - [r]: Target frame number for gap changes 12 + - [z]: Frame gap/delay in milliseconds 13 + - [v]: Loop count 2 14 3 - Animation control operations. *) 15 + {2 Terminal-Driven Animation} 16 + 17 + The terminal automatically advances frames based on each frame's gap 18 + (delay). To start terminal-driven animation: 19 + 20 + {[ 21 + (* Start infinite loop *) 22 + Kgp.animate ~image_id:1 (Animation.set_state ~loops:1 `Run) 23 + 24 + (* Run 3 times then stop *) 25 + Kgp.animate ~image_id:1 (Animation.set_state ~loops:4 `Run) 26 + 27 + (* Stop animation *) 28 + Kgp.animate ~image_id:1 (Animation.set_state `Stop) 29 + ]} 30 + 31 + {2 Client-Driven Animation} 32 + 33 + The client manually controls which frame is displayed: 34 + 35 + {[ 36 + (* Display specific frame *) 37 + Kgp.animate ~image_id:1 (Animation.set_current_frame 5) 38 + 39 + (* Advance to next frame in application logic *) 40 + let next_frame = (current_frame mod total_frames) + 1 in 41 + Kgp.animate ~image_id:1 (Animation.set_current_frame next_frame) 42 + ]} 43 + 44 + {2 Modifying Frame Timing} 45 + 46 + Frame gaps can be changed during playback: 47 + 48 + {[ 49 + (* Slow down frame 3 *) 50 + Kgp.animate ~image_id:1 (Animation.set_gap ~frame:3 ~gap_ms:200) 51 + 52 + (* Make frame 5 instant/gapless *) 53 + Kgp.animate ~image_id:1 (Animation.set_gap ~frame:5 ~gap_ms:(-1)) 54 + ]} 55 + 56 + {2 Loop Counting} 57 + 58 + The [loops] parameter in {!set_state}: 59 + - 0: Ignored (doesn't change loop setting) 60 + - 1: Infinite loop 61 + - n > 1: Loop (n-1) times, then stop *) 4 62 5 63 type t = 6 64 [ `Set_state of Kgp_animation_state.t * int option 7 65 | `Set_gap of int * int 8 66 | `Set_current of int ] 9 - (** Animation control operations. *) 67 + (** Animation control operations. 68 + 69 + - [`Set_state (state, loops)] - Set animation playback state with 70 + optional loop count. 71 + - [`Set_gap (frame, gap_ms)] - Set the delay for a specific frame. 72 + - [`Set_current frame] - Jump to a specific frame (1-based). *) 10 73 11 74 val set_state : ?loops:int -> Kgp_animation_state.t -> t 12 - (** Set animation state. 13 - @param loops Number of loops: 0 = ignored, 1 = infinite, n = n-1 loops *) 75 + (** Set animation playback state. 76 + 77 + @param loops Loop count: 0 = ignored, 1 = infinite, n > 1 = (n-1) loops. 78 + Protocol key: [v]. 79 + @param state The target playback state. 80 + 81 + Examples: 82 + {[ 83 + set_state `Run (* Run with current loop setting *) 84 + set_state ~loops:1 `Run (* Run infinitely *) 85 + set_state ~loops:3 `Run (* Run twice, then stop *) 86 + set_state `Stop (* Pause animation *) 87 + set_state `Loading (* Run, wait for more frames at end *) 88 + ]} *) 14 89 15 90 val set_gap : frame:int -> gap_ms:int -> t 16 91 (** Set the gap (delay) for a specific frame. 17 - @param frame 1-based frame number 18 - @param gap_ms Delay in milliseconds (negative = gapless) *) 92 + 93 + @param frame 1-based frame number to modify. Protocol key: [r]. 94 + @param gap_ms Delay in milliseconds before next frame. Negative values 95 + create gapless frames (not displayed, instant skip). Protocol key: [z]. 96 + 97 + Note: Frame 1 is the root/base image. Use 2+ for added frames. *) 19 98 20 99 val set_current_frame : int -> t 21 - (** Make a specific frame (1-based) the current displayed frame. *) 100 + (** Make a specific frame the current displayed frame. 101 + 102 + @param frame 1-based frame number to display. Protocol key: [c]. 103 + 104 + Used for client-driven animation where the application controls 105 + frame advancement rather than the terminal. *)
+55 -5
lib/kgp_animation_state.mli
··· 1 1 (** Animation Playback State 2 2 3 - Controls the playback state of animated images. *) 3 + Controls the playback state of animated images. 4 + 5 + {2 Protocol Details} 6 + 7 + The animation state is specified via the [s] key in the control data 8 + when using action [a=a]: 9 + - [s=1]: stop animation 10 + - [s=2]: run in loading mode 11 + - [s=3]: run normally 12 + 13 + {2 Animation Modes} 14 + 15 + The protocol supports two animation approaches: 16 + 17 + {b Terminal-driven animation}: The terminal automatically advances 18 + frames based on the gap (delay) specified for each frame. Use 19 + [{`Run}] or [{`Loading}] states. 20 + 21 + {b Client-driven animation}: The client manually sets the current 22 + frame using [Kgp.Animation.set_current_frame]. Use [{`Stop}] state 23 + to prevent automatic advancement. 24 + 25 + {2 Stop State} 26 + 27 + [{`Stop}] halts automatic frame advancement. The animation freezes 28 + on the current frame. Use this when: 29 + - Implementing client-driven animation 30 + - Pausing an animation 31 + - Displaying a static frame from an animated image 32 + 33 + {2 Loading State} 34 + 35 + [{`Loading}] runs the animation but waits for new frames when reaching 36 + the end instead of looping. Use this when: 37 + - Streaming animation frames progressively 38 + - Building an animation while displaying it 39 + - The animation is not yet complete 40 + 41 + {2 Run State} 42 + 43 + [{`Run}] runs the animation normally, looping back to the first frame 44 + after the last. The loop count can be controlled via the [loops] 45 + parameter in [Kgp.Animation.set_state]. *) 4 46 5 47 type t = [ `Stop | `Loading | `Run ] 6 48 (** Animation playback states. 7 49 8 - - [`Stop] - Halt animation playback 9 - - [`Loading] - Run animation but wait for new frames at end 10 - - [`Run] - Run animation normally and loop *) 50 + - [`Stop] - Halt animation playback. The animation freezes on the 51 + current frame and does not advance automatically. 52 + - [`Loading] - Run animation but wait for new frames at end. When 53 + the last frame is reached, the animation pauses until more frames 54 + are added, then continues. 55 + - [`Run] - Run animation normally and loop. After the last frame, 56 + playback returns to the first frame (or stops after the specified 57 + number of loops). *) 11 58 12 59 val to_int : t -> int 13 - (** Convert to protocol integer (1, 2, or 3). *) 60 + (** Convert to protocol integer. 61 + 62 + Returns 1 for [`Stop], 2 for [`Loading], or 3 for [`Run]. 63 + These values are used in the [s=] control data key. *)
+88 -4
lib/kgp_compose.mli
··· 1 - (** Kitty Graphics Protocol Compose 1 + (** Frame Composition 2 + 3 + Operations for compositing rectangular regions between animation frames. 4 + This allows building complex frames from simpler components. 5 + 6 + {2 Protocol Overview} 7 + 8 + Frame composition uses action [a=c] to copy a rectangular region from 9 + one frame onto another. This is useful for: 10 + 11 + - Building frames from reusable sprite components 12 + - Applying partial updates to existing frames 13 + - Creating complex animations efficiently 14 + 15 + {2 Coordinate System} 16 + 17 + All coordinates are in pixels, relative to the top-left corner of 18 + the respective frame: 2 19 3 - Frame composition operations. *) 20 + - [source_x], [source_y]: Top-left of rectangle in source frame 21 + - [dest_x], [dest_y]: Top-left of destination in target frame 22 + - [width], [height]: Size of the rectangle to copy 23 + 24 + If width/height are omitted, the entire source frame is used. 25 + 26 + {2 Composition Mode} 27 + 28 + The [composition] parameter controls blending: 29 + - [{`Alpha_blend}]: Standard alpha compositing (default) 30 + - [{`Overwrite}]: Direct pixel replacement 31 + 32 + {2 Error Conditions} 33 + 34 + The terminal responds with errors for: 35 + - [ENOENT]: Source or destination frame doesn't exist 36 + - [EINVAL]: Rectangle out of bounds, or source equals destination 37 + with overlapping regions 38 + - [ENOSPC]: Not enough storage after composition 39 + 40 + {2 Example} 41 + 42 + {[ 43 + (* Copy a 32x32 sprite from frame 2 to frame 5 *) 44 + let comp = Compose.make 45 + ~source_frame:2 ~dest_frame:5 46 + ~width:32 ~height:32 47 + ~source_x:0 ~source_y:0 (* From top-left of source *) 48 + ~dest_x:100 ~dest_y:50 () (* To position in dest *) 49 + in 50 + Kgp.compose ~image_id:1 comp 51 + ]} *) 4 52 5 53 type t 6 - (** Composition operation. *) 54 + (** Composition operation. Opaque type; use {!make} to construct. *) 7 55 8 56 val make : 9 57 source_frame:int -> ··· 17 65 ?composition:Kgp_composition.t -> 18 66 unit -> 19 67 t 20 - (** Compose a rectangle from one frame onto another. *) 68 + (** Create a composition operation. 69 + 70 + @param source_frame 1-based frame number to copy from. Required. 71 + Protocol key: [r]. 72 + @param dest_frame 1-based frame number to copy onto. Required. 73 + Protocol key: [c]. 74 + @param width Width of rectangle in pixels. Default is full frame. 75 + Protocol key: [w]. 76 + @param height Height of rectangle in pixels. Default is full frame. 77 + Protocol key: [h]. 78 + @param source_x Left edge of source rectangle (default 0). 79 + Protocol key: [X]. 80 + @param source_y Top edge of source rectangle (default 0). 81 + Protocol key: [Y]. 82 + @param dest_x Left edge of destination position (default 0). 83 + Protocol key: [x]. 84 + @param dest_y Top edge of destination position (default 0). 85 + Protocol key: [y]. 86 + @param composition Blending mode. Default is alpha blending. 87 + Protocol key: [C]. *) 21 88 22 89 (** {1 Field Accessors} *) 23 90 24 91 val source_frame : t -> int 92 + (** 1-based source frame number. *) 93 + 25 94 val dest_frame : t -> int 95 + (** 1-based destination frame number. *) 96 + 26 97 val width : t -> int option 98 + (** Width of rectangle in pixels. *) 99 + 27 100 val height : t -> int option 101 + (** Height of rectangle in pixels. *) 102 + 28 103 val source_x : t -> int option 104 + (** Left edge of source rectangle. *) 105 + 29 106 val source_y : t -> int option 107 + (** Top edge of source rectangle. *) 108 + 30 109 val dest_x : t -> int option 110 + (** Left edge of destination position. *) 111 + 31 112 val dest_y : t -> int option 113 + (** Top edge of destination position. *) 114 + 32 115 val composition : t -> Kgp_composition.t option 116 + (** Blending mode for composition. *)
+39 -4
lib/kgp_composition.mli
··· 1 1 (** Pixel Composition Mode 2 2 3 - Controls how pixels are blended when compositing images or animation frames. *) 3 + Controls how pixels are blended when compositing images or animation frames. 4 + 5 + {2 Protocol Details} 6 + 7 + The composition mode is specified via the [X] key in the control data 8 + (for animation frames) or the [C] key (for frame composition operations): 9 + - Value 0 or omitted: alpha blending (default) 10 + - Value 1: simple overwrite/replacement 11 + 12 + {2 Alpha Blending} 13 + 14 + [{`Alpha_blend}] performs standard alpha compositing using the source 15 + pixel's alpha channel. For each pixel: 16 + - If source alpha is 255 (opaque), source pixel replaces destination 17 + - If source alpha is 0 (transparent), destination pixel is unchanged 18 + - Otherwise, colors are blended proportionally 19 + 20 + This mode is essential for: 21 + - Transparent PNG images 22 + - Overlaying graphics on backgrounds 23 + - Anti-aliased edges and text 24 + 25 + {2 Overwrite Mode} 26 + 27 + [{`Overwrite}] simply replaces destination pixels with source pixels, 28 + ignoring the alpha channel. This is useful for: 29 + - Performance optimization when transparency isn't needed 30 + - Replacing rectangular regions entirely 31 + - Animation frames that completely replace the previous frame *) 4 32 5 33 type t = [ `Alpha_blend | `Overwrite ] 6 34 (** Composition modes. 7 35 8 - - [`Alpha_blend] - Full alpha blending (default) 9 - - [`Overwrite] - Simple pixel replacement *) 36 + - [`Alpha_blend] - Full alpha blending (default). Source pixels are 37 + composited onto the destination using standard Porter-Duff "over" 38 + compositing based on the source alpha channel. 39 + - [`Overwrite] - Simple pixel replacement. Source pixels completely 40 + replace destination pixels, ignoring alpha values. Faster but no 41 + transparency support. *) 10 42 11 43 val to_int : t -> int 12 - (** Convert to protocol integer (0 or 1). *) 44 + (** Convert to protocol integer. 45 + 46 + Returns 0 for [`Alpha_blend] or 1 for [`Overwrite]. 47 + These values are used in the [X=] or [C=] control data keys. *)
+35 -5
lib/kgp_compression.mli
··· 1 1 (** Data Compression 2 2 3 - Specifies compression applied to image data before transmission. *) 3 + Specifies compression applied to image data before transmission. 4 + 5 + {2 Protocol Details} 6 + 7 + Compression is specified via the [o] key in the control data: 8 + - No [o] key means no compression 9 + - [o=z] means zlib (RFC 1950 DEFLATE) compression 10 + 11 + Compression is applied to the raw pixel/PNG data {i before} base64 12 + encoding. The terminal decompresses after base64 decoding. 13 + 14 + {2 When to Use Compression} 15 + 16 + Zlib compression is beneficial for: 17 + - Large images with repetitive patterns 18 + - Screenshots and UI graphics 19 + - Images with large solid color regions 20 + 21 + It may not help (or could increase size) for: 22 + - Already-compressed PNG data 23 + - Photographic images with high entropy 24 + - Very small images (compression overhead) 25 + 26 + {2 PNG with Compression} 27 + 28 + When using both [{`Png}] format and [{`Zlib}] compression, the [size] 29 + parameter must be specified with the original (uncompressed) PNG size. 30 + The terminal needs this to allocate the correct buffer for decompression. *) 4 31 5 32 type t = [ `None | `Zlib ] 6 33 (** Compression options. 7 34 8 - - [`None] - Raw uncompressed data 9 - - [`Zlib] - RFC 1950 zlib compression *) 35 + - [`None] - Raw uncompressed data. No [o=] key is sent. 36 + - [`Zlib] - RFC 1950 zlib/DEFLATE compression. Data is compressed 37 + before base64 encoding and decompressed by the terminal. *) 10 38 11 39 val to_char : t -> char option 12 - (** Convert to protocol character. Returns [None] for no compression, 13 - [Some 'z'] for zlib. *) 40 + (** Convert to protocol character. 41 + 42 + Returns [None] for [`None] (no key sent), or [Some 'z'] for [`Zlib]. 43 + When [Some c] is returned, [o=c] is added to the control data. *)
+40 -4
lib/kgp_cursor.mli
··· 1 1 (** Cursor Movement Behavior 2 2 3 - Controls cursor position after displaying an image. *) 3 + Controls cursor position after displaying an image. 4 + 5 + {2 Protocol Details} 6 + 7 + Cursor movement is specified via the [C] key in the control data: 8 + - [C=0] or no [C] key: move cursor after display (default) 9 + - [C=1]: keep cursor in place (static) 10 + 11 + This key was added in Kitty 0.20.0. 12 + 13 + {2 Default Behavior} 14 + 15 + By default ([{`Move}]), after displaying an image the cursor advances: 16 + - Right by the number of columns the image occupies 17 + - Down by the number of rows the image occupies 18 + 19 + This matches how the cursor moves after printing text, allowing images 20 + to flow naturally with text content. 21 + 22 + {2 Static Cursor} 23 + 24 + With [{`Static}], the cursor remains at its original position. This is 25 + useful when: 26 + - Overlaying images on existing content 27 + - Positioning multiple images relative to the same starting point 28 + - Implementing custom cursor management 29 + 30 + {2 Relative Placements} 31 + 32 + Note: When using relative placements (positioning images relative to 33 + other placements), the cursor never moves regardless of this setting. *) 4 34 5 35 type t = [ `Move | `Static ] 6 36 (** Cursor movement behavior. 7 37 8 - - [`Move] - Advance cursor past the displayed image (default) 9 - - [`Static] - Keep cursor in place *) 38 + - [`Move] - Advance cursor past the displayed image (default). 39 + Cursor moves right by the number of columns and down by the 40 + number of rows occupied by the image. 41 + - [`Static] - Keep cursor at its original position. The image 42 + is displayed but cursor position is unchanged. *) 10 43 11 44 val to_int : t -> int 12 - (** Convert to protocol integer (0 or 1). *) 45 + (** Convert to protocol integer. 46 + 47 + Returns 0 for [`Move] or 1 for [`Static]. 48 + These values are used in the [C=] control data key. *)
+75 -15
lib/kgp_delete.mli
··· 1 1 (** Image Deletion Target 2 2 3 - Specifies which images or placements to delete. Each deletion type has 4 - two variants: one that only removes placements and one that also frees 5 - the underlying image data. *) 3 + Specifies which images or placements to delete. 4 + 5 + {2 Protocol Details} 6 + 7 + Deletion is performed with action [a=d] and the [d] key specifies 8 + the deletion type. The [d] key uses single characters: 9 + 10 + {v 11 + | Char | Meaning | 12 + |------|--------------------------------------------------------| 13 + | a/A | All placements visible on screen | 14 + | i/I | By image ID (with optional placement ID) | 15 + | n/N | By image number (newest with that number) | 16 + | c/C | At current cursor position | 17 + | p/P | At specific cell coordinates (x, y) | 18 + | q/Q | At specific cell with z-index (x, y, z) | 19 + | x/X | All in specific column | 20 + | y/Y | All in specific row | 21 + | z/Z | All with specific z-index | 22 + | r/R | By image ID range (min_id to max_id) | 23 + | f/F | Animation frames only | 24 + v} 25 + 26 + {2 Placements vs Image Data} 27 + 28 + Each deletion type has two variants: 29 + - {b Lowercase}: Removes placements only. The image data remains in 30 + memory and can be displayed again later. 31 + - {b Uppercase}: Removes placements AND frees the image data. The 32 + image cannot be displayed again without retransmitting. 33 + 34 + Example: [{`By_id (42, None)}] removes all placements of image 42 but 35 + keeps the data. [{`By_id_and_free (42, None)}] removes placements and 36 + frees the image data. 37 + 38 + {2 Placement IDs} 39 + 40 + When deleting by image ID or number, an optional placement ID can be 41 + specified to delete only a specific placement. If [None], all placements 42 + of that image are deleted. 43 + 44 + {2 Coordinate-Based Deletion} 45 + 46 + For [{`At_cell}] and [{`At_cell_z}], coordinates are 0-based cell 47 + positions (not pixel positions). Only placements that intersect the 48 + specified cell are deleted. 49 + 50 + {2 Virtual Placements} 51 + 52 + Virtual placements (used for Unicode placeholder mode) are only affected 53 + by: [{`By_id}], [{`By_id_and_free}], [{`By_number}], [{`By_number_and_free}], 54 + [{`By_id_range}], and [{`By_id_range_and_free}]. Other deletion commands 55 + do not affect virtual placements. *) 6 56 7 57 type t = 8 58 [ `All_visible ··· 29 79 | `Frames_and_free ] 30 80 (** Deletion target specification. 31 81 32 - - [`All_visible] - All visible placements 33 - - [`By_id (id, placement_id)] - By image ID and optional placement ID 34 - - [`By_number (n, placement_id)] - By image number and optional placement ID 35 - - [`At_cursor] - Placement at cursor position 36 - - [`At_cell (x, y)] - Placement at cell coordinates 37 - - [`At_cell_z (x, y, z)] - Placement at cell with specific z-index 38 - - [`By_column c] - All placements in column c 39 - - [`By_row r] - All placements in row r 82 + {b Screen-based:} 83 + - [`All_visible] - All placements currently visible on screen 84 + - [`At_cursor] - Placements at current cursor position 85 + - [`At_cell (x, y)] - Placements intersecting cell at column x, row y 86 + - [`At_cell_z (x, y, z)] - Like [`At_cell] but only with z-index z 87 + - [`By_column x] - All placements intersecting column x 88 + - [`By_row y] - All placements intersecting row y 40 89 - [`By_z_index z] - All placements with z-index z 41 - - [`By_id_range (min, max)] - All images with IDs in range 42 - - [`Frames] - Animation frames only 43 90 44 - The [_and_free] variants also release the image data from memory. *) 91 + {b ID-based:} 92 + - [`By_id (id, placement_id)] - By image ID. If [placement_id] is 93 + [Some p], only that specific placement; if [None], all placements. 94 + - [`By_number (n, placement_id)] - By image number (newest image 95 + with that number). Placement ID works as above. 96 + - [`By_id_range (min, max)] - All images with IDs in range [min..max] 97 + 98 + {b Animation:} 99 + - [`Frames] - Animation frames only (not the base image) 100 + 101 + All variants have an [_and_free] version that also releases image data. *) 45 102 46 103 val to_char : t -> char 47 - (** Convert to protocol character for the delete command. *) 104 + (** Convert to protocol character for the delete command. 105 + 106 + Returns the character used in the [d=] control data key. Lowercase 107 + for placement-only deletion, uppercase for deletion with data free. *)
+37 -5
lib/kgp_format.mli
··· 1 1 (** Image Data Format 2 2 3 - Specifies the pixel format of image data being transmitted. *) 3 + Specifies the pixel format of image data being transmitted to the terminal. 4 + 5 + {2 Protocol Details} 6 + 7 + The format is specified via the [f] key in the control data: 8 + - [f=24] for RGB (3 bytes per pixel) 9 + - [f=32] for RGBA (4 bytes per pixel, default) 10 + - [f=100] for PNG 11 + 12 + {2 Raw Pixel Formats} 13 + 14 + For [{`Rgb24}] and [{`Rgba32}], the data consists of raw pixel values in 15 + row-major order (left-to-right, top-to-bottom). The image dimensions must 16 + be specified via the [width] and [height] parameters. 17 + 18 + - [{`Rgb24}]: 3 bytes per pixel in sRGB color space (red, green, blue) 19 + - [{`Rgba32}]: 4 bytes per pixel (red, green, blue, alpha) 20 + 21 + {2 PNG Format} 22 + 23 + For [{`Png}], the data is a complete PNG image. The terminal extracts 24 + dimensions from PNG metadata, so [width] and [height] are optional. 25 + 26 + When using both PNG format and zlib compression, you must also specify 27 + the [size] parameter with the uncompressed PNG data size. *) 4 28 5 29 type t = [ `Rgba32 | `Rgb24 | `Png ] 6 30 (** Image data formats. 7 31 8 - - [`Rgba32] - 32-bit RGBA (4 bytes per pixel) 9 - - [`Rgb24] - 24-bit RGB (3 bytes per pixel) 10 - - [`Png] - PNG encoded data *) 32 + - [`Rgba32] - 32-bit RGBA (4 bytes per pixel). Default format. 33 + Pixels are ordered red, green, blue, alpha. Alpha of 255 is fully 34 + opaque, 0 is fully transparent. 35 + - [`Rgb24] - 24-bit RGB (3 bytes per pixel). No alpha channel; 36 + pixels are fully opaque. More compact than RGBA for opaque images. 37 + - [`Png] - PNG encoded data. The terminal decodes the PNG internally. 38 + Supports all PNG color types and bit depths. Most convenient format 39 + as dimensions are embedded in the data. *) 11 40 12 41 val to_int : t -> int 13 - (** Convert to protocol integer value (32, 24, or 100). *) 42 + (** Convert to protocol integer value. 43 + 44 + Returns 24 for [`Rgb24], 32 for [`Rgba32], or 100 for [`Png]. 45 + These values are used in the [f=] control data key. *)
+93 -11
lib/kgp_frame.mli
··· 1 - (** Kitty Graphics Protocol Frame 1 + (** Animation Frame Configuration 2 + 3 + Configuration for adding or editing animation frames. Frames can be 4 + full images or partial updates (rectangles), with options for timing 5 + and composition. 6 + 7 + {2 Protocol Overview} 2 8 3 - Animation frame configuration. *) 9 + Animations are created by: 10 + 1. Transmitting a base image (becomes frame 1) 11 + 2. Adding frames using the frame action ([a=f]) 12 + 3. Controlling playback with animation commands 13 + 14 + Frame numbers are 1-based: 15 + - Frame 1: The original/base image 16 + - Frame 2+: Added animation frames 17 + 18 + {2 Frame Positioning} 19 + 20 + For partial frame updates, [x] and [y] specify where the new pixel 21 + data is placed within the frame canvas: 22 + - [x]: Left edge position in pixels (default 0) 23 + - [y]: Top edge position in pixels (default 0) 24 + 25 + The frame data dimensions come from the [width] and [height] parameters 26 + of the frame command. 27 + 28 + {2 Frame Canvas} 29 + 30 + Each frame needs a background canvas to composite onto. Options: 31 + 32 + {b Solid color background} ([background_color]): 33 + Use a 32-bit RGBA color. Format: [0xRRGGBBAA] where AA is alpha. 34 + Default is 0 (transparent black). 35 + 36 + {b Copy from existing frame} ([base_frame]): 37 + Use another frame as the starting canvas. Specified as 1-based frame 38 + number. The base frame's pixels are copied, then new data is composited. 39 + 40 + {2 Editing Existing Frames} 41 + 42 + Instead of creating a new frame, you can edit an existing one: 43 + - Set [edit_frame] to the 1-based frame number 44 + - The frame itself becomes the canvas 45 + - New data is composited onto it 46 + 47 + {2 Frame Timing} 48 + 49 + The [gap_ms] parameter controls the delay before transitioning to 50 + the next frame: 51 + - Positive value: Delay in milliseconds 52 + - Zero: Ignored (keeps existing gap) 53 + - Negative value: "Gapless" frame - not displayed, used as a base 54 + for other frames 55 + 56 + Default gap for new frames is 40ms. The root frame (frame 1) has 57 + a default gap of 0ms. 58 + 59 + {2 Composition Mode} 60 + 61 + The [composition] parameter controls how new pixel data is blended 62 + onto the canvas. *) 4 63 5 64 type t 6 - (** Animation frame configuration. *) 65 + (** Animation frame configuration. Opaque type; use {!make} to construct. *) 7 66 8 67 val make : 9 68 ?x:int -> ··· 17 76 t 18 77 (** Create a frame specification. 19 78 20 - @param x Left edge where frame data is placed (pixels) 21 - @param y Top edge where frame data is placed (pixels) 22 - @param base_frame 1-based frame number to use as background canvas 23 - @param edit_frame 1-based frame number to edit (0 = new frame) 24 - @param gap_ms Delay before next frame in milliseconds 25 - @param composition How to blend pixels onto the canvas 26 - @param background_color 32-bit RGBA background when no base frame *) 79 + @param x Left edge where frame data is placed in pixels (default 0). 80 + Protocol key: [x]. 81 + @param y Top edge where frame data is placed in pixels (default 0). 82 + Protocol key: [y]. 83 + @param base_frame 1-based frame number to use as background canvas. 84 + Frame 1 is the root image. Protocol key: [c]. 85 + @param edit_frame 1-based frame number to edit instead of creating new. 86 + If 0 or unset, a new frame is created. Protocol key: [r]. 87 + @param gap_ms Delay before next frame in milliseconds. Negative values 88 + create gapless frames. Protocol key: [z]. 89 + @param composition How to blend new pixels onto the canvas. 90 + Default is alpha blending. Protocol key: [X]. 91 + @param background_color 32-bit RGBA background color when not using 92 + a base frame. Format: [0xRRGGBBAA]. Protocol key: [Y]. *) 27 93 28 94 val empty : t 29 - (** Empty frame spec with defaults. *) 95 + (** Empty frame spec with all defaults. 96 + 97 + Creates a new frame with transparent black background, composited 98 + at position (0, 0) with default timing (40ms gap). *) 30 99 31 100 (** {1 Field Accessors} *) 32 101 33 102 val x : t -> int option 103 + (** Left edge position for frame data in pixels. *) 104 + 34 105 val y : t -> int option 106 + (** Top edge position for frame data in pixels. *) 107 + 35 108 val base_frame : t -> int option 109 + (** 1-based frame number to use as background canvas. *) 110 + 36 111 val edit_frame : t -> int option 112 + (** 1-based frame number being edited (0 or None = new frame). *) 113 + 37 114 val gap_ms : t -> int option 115 + (** Delay before next frame in milliseconds. *) 116 + 38 117 val composition : t -> Kgp_composition.t option 118 + (** Pixel composition mode. *) 119 + 39 120 val background_color : t -> int32 option 121 + (** 32-bit RGBA background color. *)
+114 -16
lib/kgp_placement.mli
··· 1 - (** Kitty Graphics Protocol Placement 1 + (** Image Placement Configuration 2 + 3 + Configuration for where and how to display images. Placements control 4 + cropping, scaling, positioning, and layering of images. 5 + 6 + {2 Protocol Overview} 7 + 8 + When displaying an image, the protocol allows specifying: 9 + - Which part of the source image to display (source rectangle) 10 + - Where to display it (cell position and pixel offsets) 11 + - How large to display it (scaling to cell dimensions) 12 + - How it layers with other content (z-index) 13 + - Whether it can be referenced via Unicode placeholders 14 + 15 + {2 Source Rectangle} 16 + 17 + The source rectangle specifies which portion of the image to display: 18 + - [source_x], [source_y]: Top-left corner in pixels (default: 0, 0) 19 + - [source_width], [source_height]: Size in pixels (default: full image) 20 + 21 + The displayed area is the intersection of this rectangle with the 22 + actual image bounds. This allows cropping images without modifying 23 + the original data. 24 + 25 + {2 Cell-Based Sizing} 26 + 27 + Images are sized in terminal cells: 28 + - [columns]: Number of columns to span (width in cells) 29 + - [rows]: Number of rows to span (height in cells) 30 + 31 + If both are specified, the source rectangle is scaled to fit. 32 + If only one is specified, the other is computed to maintain aspect ratio. 33 + If neither is specified, the image is displayed at natural size. 2 34 3 - Configuration for where and how to display images. *) 35 + {2 Pixel Offsets} 36 + 37 + Fine-grained positioning within the starting cell: 38 + - [cell_x_offset]: Horizontal offset in pixels from cell left edge 39 + - [cell_y_offset]: Vertical offset in pixels from cell top edge 40 + 41 + These offsets must be smaller than the cell dimensions. 42 + 43 + {2 Z-Index Layering} 44 + 45 + The [z_index] controls vertical stacking: 46 + - Positive values: drawn above text 47 + - Zero: drawn at text level 48 + - Negative values: drawn below text 49 + - Values < INT32_MIN/2 (-1,073,741,824): drawn under cells with 50 + non-default background colors 51 + 52 + Overlapping images with the same z-index are ordered by image ID 53 + (lower ID draws first/underneath). 54 + 55 + {2 Placement IDs} 56 + 57 + Each placement can have a unique [placement_id] (1-4294967295). This 58 + enables: 59 + - Updating a specific placement without affecting others 60 + - Deleting specific placements 61 + - Moving placements by resending with same image_id + placement_id 62 + 63 + If [placement_id] is 0 or unspecified, each display creates an 64 + independent placement. *) 4 65 5 66 type t 6 - (** Placement configuration. *) 67 + (** Placement configuration. Opaque type; use {!make} to construct. *) 7 68 8 69 val make : 9 70 ?source_x:int -> ··· 22 83 t 23 84 (** Create a placement configuration. 24 85 25 - @param source_x Left edge of source rectangle in pixels (default 0) 26 - @param source_y Top edge of source rectangle in pixels (default 0) 27 - @param source_width Width of source rectangle (default: full width) 28 - @param source_height Height of source rectangle (default: full height) 29 - @param cell_x_offset X offset within the first cell in pixels 30 - @param cell_y_offset Y offset within the first cell in pixels 31 - @param columns Number of columns to display over (scales image) 32 - @param rows Number of rows to display over (scales image) 33 - @param z_index Stacking order (negative = under text) 34 - @param placement_id Unique ID for this placement 35 - @param cursor Cursor movement policy after display 36 - @param unicode_placeholder Create virtual placement for Unicode mode *) 86 + @param source_x Left edge of source rectangle in pixels (default 0). 87 + Protocol key: [x]. 88 + @param source_y Top edge of source rectangle in pixels (default 0). 89 + Protocol key: [y]. 90 + @param source_width Width of source rectangle in pixels. 91 + Default is the full image width. Protocol key: [w]. 92 + @param source_height Height of source rectangle in pixels. 93 + Default is the full image height. Protocol key: [h]. 94 + @param cell_x_offset X offset within the first cell in pixels. 95 + Must be smaller than cell width. Protocol key: [X]. 96 + @param cell_y_offset Y offset within the first cell in pixels. 97 + Must be smaller than cell height. Protocol key: [Y]. 98 + @param columns Number of columns to display over. Image is scaled 99 + to fit. Protocol key: [c]. 100 + @param rows Number of rows to display over. Image is scaled to fit. 101 + Protocol key: [r]. 102 + @param z_index Stacking order. Positive = above text, negative = below. 103 + Protocol key: [z]. 104 + @param placement_id Unique ID (1-4294967295) for this placement. 105 + Allows updating/deleting specific placements. Protocol key: [p]. 106 + @param cursor Cursor movement policy after display. 107 + @param unicode_placeholder If true, creates a virtual (invisible) 108 + placement for Unicode placeholder mode. Protocol key: [U=1]. *) 37 109 38 110 val empty : t 39 - (** Empty placement with all defaults. *) 111 + (** Empty placement with all defaults. 112 + 113 + Equivalent to [make ()]. The image displays at natural size at the 114 + current cursor position with default z-index (0). *) 40 115 41 116 (** {1 Field Accessors} *) 42 117 43 118 val source_x : t -> int option 119 + (** Left edge of source rectangle in pixels. *) 120 + 44 121 val source_y : t -> int option 122 + (** Top edge of source rectangle in pixels. *) 123 + 45 124 val source_width : t -> int option 125 + (** Width of source rectangle in pixels. *) 126 + 46 127 val source_height : t -> int option 128 + (** Height of source rectangle in pixels. *) 129 + 47 130 val cell_x_offset : t -> int option 131 + (** X offset within the first cell in pixels. *) 132 + 48 133 val cell_y_offset : t -> int option 134 + (** Y offset within the first cell in pixels. *) 135 + 49 136 val columns : t -> int option 137 + (** Number of columns to display over. *) 138 + 50 139 val rows : t -> int option 140 + (** Number of rows to display over. *) 141 + 51 142 val z_index : t -> int option 143 + (** Stacking order (z-index). *) 144 + 52 145 val placement_id : t -> int option 146 + (** Unique placement identifier. *) 147 + 53 148 val cursor : t -> Kgp_cursor.t option 149 + (** Cursor movement policy. *) 150 + 54 151 val unicode_placeholder : t -> bool 152 + (** Whether this is a virtual placement for Unicode mode. *)
+40 -5
lib/kgp_quiet.mli
··· 1 1 (** Response Suppression Level 2 2 3 - Controls which terminal responses are sent back to the application. *) 3 + Controls which terminal responses are sent back to the application. 4 + 5 + {2 Protocol Details} 6 + 7 + The quiet level is specified via the [q] key in the control data: 8 + - [q=0] or no [q] key: send all responses (default) 9 + - [q=1]: suppress OK responses, only send errors 10 + - [q=2]: suppress all responses 11 + 12 + {2 Terminal Responses} 13 + 14 + Normally, when an [image_id] is specified, the terminal responds: 15 + - On success: [ESC _Gi=ID;OK ESC] 16 + - On failure: [ESC _Gi=ID;ECODE:message ESC] 17 + 18 + Response processing requires reading from the terminal, which can be 19 + complex in some applications. 20 + 21 + {2 Use Cases} 22 + 23 + [{`Noisy}] (default): Use when you need to verify operations succeeded 24 + or want to handle errors programmatically. 25 + 26 + [{`Errors_only}]: Use when you want to detect failures but don't need 27 + confirmation of success. Reduces response traffic. 28 + 29 + [{`Silent}]: Use in fire-and-forget scenarios like shell scripts or 30 + when the application cannot easily read terminal responses. Also useful 31 + for high-frequency animation updates where response processing would 32 + add latency. *) 4 33 5 34 type t = [ `Noisy | `Errors_only | `Silent ] 6 35 (** Response suppression levels. 7 36 8 - - [`Noisy] - Send all responses (default) 9 - - [`Errors_only] - Suppress OK responses, only send errors 10 - - [`Silent] - Suppress all responses *) 37 + - [`Noisy] - Send all responses including OK confirmations (default). 38 + Required for detecting success and getting assigned image IDs. 39 + - [`Errors_only] - Suppress OK responses, only send error messages. 40 + Useful when success is expected but errors should be caught. 41 + - [`Silent] - Suppress all responses including errors. Useful for 42 + shell scripts or when response handling is not possible. *) 11 43 12 44 val to_int : t -> int 13 - (** Convert to protocol integer (0, 1, or 2). *) 45 + (** Convert to protocol integer. 46 + 47 + Returns 0 for [`Noisy], 1 for [`Errors_only], or 2 for [`Silent]. 48 + These values are used in the [q=] control data key. *)
+52 -5
lib/kgp_transmission.mli
··· 1 1 (** Data Transmission Method 2 2 3 - Specifies how image data is transmitted to the terminal. *) 3 + Specifies how image data is transmitted to the terminal. 4 + 5 + {2 Protocol Details} 6 + 7 + The transmission method is specified via the [t] key in the control data: 8 + - [t=d] for direct (inline) transmission (default) 9 + - [t=f] for regular file 10 + - [t=t] for temporary file (deleted after reading) 11 + 12 + {2 Direct Transmission} 13 + 14 + [{`Direct}] sends data inline within the escape sequence itself. The data 15 + is base64-encoded in the payload section. This is the simplest method and 16 + works over any connection (including SSH). 17 + 18 + For images larger than 4096 bytes (after base64 encoding), the data is 19 + automatically split into chunks using the [m=] key: 20 + - [m=1] indicates more chunks follow 21 + - [m=0] indicates the final chunk 22 + 23 + {2 File Transmission} 24 + 25 + [{`File}] tells the terminal to read data from a file path. The path is 26 + sent base64-encoded in the payload. Additional parameters: 27 + - [S=] specifies the number of bytes to read 28 + - [O=] specifies the byte offset to start reading from 29 + 30 + File transmission only works when the terminal and client share a 31 + filesystem (i.e., local terminals, not SSH). 32 + 33 + Security: The terminal will refuse to read device files, sockets, or 34 + files in sensitive locations like [/proc], [/sys], or [/dev]. 35 + 36 + {2 Temporary File Transmission} 37 + 38 + [{`Tempfile}] is like [{`File}] but the terminal deletes the file after 39 + reading. The file must be in a recognized temporary directory: 40 + - [/tmp] 41 + - [/dev/shm] 42 + - The [TMPDIR] environment variable location 43 + - Platform-specific temp directories containing [tty-graphics-protocol] *) 4 44 5 45 type t = [ `Direct | `File | `Tempfile ] 6 46 (** Transmission methods. 7 47 8 - - [`Direct] - Data is sent inline in the escape sequence 9 - - [`File] - Terminal reads from a file path 10 - - [`Tempfile] - Terminal reads and deletes a temporary file *) 48 + - [`Direct] - Data is sent inline in the escape sequence (base64-encoded). 49 + Works over any connection including SSH. Automatic chunking for large 50 + images. 51 + - [`File] - Terminal reads from a file path sent in the payload. 52 + Only works when terminal and client share a filesystem. 53 + - [`Tempfile] - Like [`File] but terminal deletes the file after reading. 54 + File must be in a recognized temporary directory. *) 11 55 12 56 val to_char : t -> char 13 - (** Convert to protocol character ('d', 'f', or 't'). *) 57 + (** Convert to protocol character. 58 + 59 + Returns ['d'] for [`Direct], ['f'] for [`File], or ['t'] for [`Tempfile]. 60 + These values are used in the [t=] control data key. *)