Kitty Graphics Protocol in OCaml
terminal graphics ocaml

restructure types

+69 -99
+2 -2
example/anim_test.ml
··· 22 22 let image_id = 500 in 23 23 24 24 (* Clear any existing image *) 25 - send (K.Command.delete ~quiet:`Errors_only (`All_visible_and_free)) ~data:""; 25 + send (K.Command.delete ~free:true ~quiet:`Errors_only `All_visible) ~data:""; 26 26 27 27 (* Step 1: Transmit base frame (red) *) 28 28 let red_frame = solid_color_rgba ~width ~height ~r:255 ~g:0 ~b:0 ~a:255 in ··· 93 93 ~data:""; 94 94 95 95 (* Clean up *) 96 - send (K.Command.delete ~quiet:`Errors_only (`All_visible_and_free)) ~data:""; 96 + send (K.Command.delete ~free:true ~quiet:`Errors_only `All_visible) ~data:""; 97 97 print_endline "Done."
example/anim_test.mli

This is a binary file and will not be displayed.

example/debug_anim.mli

This is a binary file and will not be displayed.

example/test_output.mli

This is a binary file and will not be displayed.

+2 -2
example/tiny_anim.ml
··· 24 24 let image_id = 999 in 25 25 26 26 (* Clear any existing images *) 27 - send (K.Command.delete ~quiet:`Errors_only (`All_visible_and_free)) ~data:""; 27 + send (K.Command.delete ~free:true ~quiet:`Errors_only `All_visible) ~data:""; 28 28 29 29 (* Step 1: Transmit base frame (red) - matching Go's sequence *) 30 30 let red_frame = solid_color_rgba ~width ~height ~r:255 ~g:0 ~b:0 ~a:255 in ··· 104 104 ~data:""; 105 105 106 106 (* Clean up *) 107 - send (K.Command.delete ~quiet:`Errors_only (`All_visible_and_free)) ~data:""; 107 + send (K.Command.delete ~free:true ~quiet:`Errors_only `All_visible) ~data:""; 108 108 print_endline "Done."
example/tiny_anim.mli

This is a binary file and will not be displayed.

-10
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 - 23 13 (* Configuration modules *) 24 14 module Placement = Kgp_placement 25 15 module Frame = Kgp_frame
+9 -5
lib/kgp.mli
··· 211 211 (** {2 Deletion} 212 212 213 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. *) 214 + By default, only placements are removed and image data is retained 215 + for potential reuse. Use [~free:true] to also release the image data. *) 216 216 217 - val delete : ?quiet:Quiet.t -> Delete.t -> command 217 + val delete : ?free:bool -> ?quiet:Quiet.t -> Delete.t -> command 218 218 (** Delete images or placements. 219 219 220 220 See {!Delete} for the full list of deletion targets. 221 221 222 + @param free If true, also free the image data from memory (default: false). 223 + Without [~free:true], only placements are removed and the image data 224 + can be reused for new placements. 225 + 222 226 Examples: 223 227 {[ 224 - (* Delete all visible images *) 228 + (* Delete all visible images, keep data *) 225 229 Kgp.delete `All_visible 226 230 227 231 (* Delete specific image, keeping data for reuse *) 228 232 Kgp.delete (`By_id (42, None)) 229 233 230 234 (* Delete specific image and free its data *) 231 - Kgp.delete (`By_id_and_free (42, None)) 235 + Kgp.delete ~free:true (`By_id (42, None)) 232 236 233 237 (* Delete all placements at a specific cell *) 234 238 Kgp.delete (`At_cell (10, 5))
+16 -15
lib/kgp_command.ml
··· 22 22 image_number : int option; 23 23 placement : Kgp_placement.t option; 24 24 delete : Kgp_delete.t option; 25 + delete_free : bool; 25 26 frame : Kgp_frame.t option; 26 27 animation : Kgp_animation.t option; 27 28 compose : Kgp_compose.t option; ··· 42 43 image_number = None; 43 44 placement = None; 44 45 delete = None; 46 + delete_free = false; 45 47 frame = None; 46 48 animation = None; 47 49 compose = None; ··· 86 88 let display ?image_id ?image_number ?placement ?quiet () = 87 89 { (make `Display) with image_id; image_number; placement; quiet } 88 90 89 - let delete ?quiet del = { (make `Delete) with quiet; delete = Some del } 91 + let delete ?(free = false) ?quiet del = 92 + { (make `Delete) with quiet; delete = Some del; delete_free = free } 90 93 91 94 let frame ?image_id ?image_number ?format ?transmission ?compression ?width 92 95 ?height ?quiet ~frame () = ··· 162 165 kv_int_if w 'C' ~default:0 (Some (Kgp_cursor.to_int c))); 163 166 if Kgp_placement.unicode_placeholder p then kv_int w 'U' 1 164 167 165 - let write_delete w (d : Kgp_delete.t) = 166 - kv_char w 'd' (Kgp_delete.to_char d); 168 + let write_delete w ~free (d : Kgp_delete.t) = 169 + kv_char w 'd' (Kgp_delete.to_char ~free d); 167 170 match d with 168 - | `By_id (id, pid) | `By_id_and_free (id, pid) -> 171 + | `By_id (id, pid) -> 169 172 kv_int w 'i' id; 170 173 kv_int_opt w 'p' pid 171 - | `By_number (n, pid) | `By_number_and_free (n, pid) -> 174 + | `By_number (n, pid) -> 172 175 kv_int w 'I' n; 173 176 kv_int_opt w 'p' pid 174 - | `At_cell (x, y) | `At_cell_and_free (x, y) -> 177 + | `At_cell (x, y) -> 175 178 kv_int w 'x' x; 176 179 kv_int w 'y' y 177 - | `At_cell_z (x, y, z) | `At_cell_z_and_free (x, y, z) -> 180 + | `At_cell_z (x, y, z) -> 178 181 kv_int w 'x' x; 179 182 kv_int w 'y' y; 180 183 kv_int w 'z' z 181 - | `By_column c | `By_column_and_free c -> kv_int w 'x' c 182 - | `By_row r | `By_row_and_free r -> kv_int w 'y' r 183 - | `By_z_index z | `By_z_index_and_free z -> kv_int w 'z' z 184 - | `By_id_range (min_id, max_id) | `By_id_range_and_free (min_id, max_id) -> 184 + | `By_column c -> kv_int w 'x' c 185 + | `By_row r -> kv_int w 'y' r 186 + | `By_z_index z -> kv_int w 'z' z 187 + | `By_id_range (min_id, max_id) -> 185 188 kv_int w 'x' min_id; 186 189 kv_int w 'y' max_id 187 - | `All_visible | `All_visible_and_free | `At_cursor | `At_cursor_and_free 188 - | `Frames | `Frames_and_free -> 189 - () 190 + | `All_visible | `At_cursor | `Frames -> () 190 191 191 192 let write_frame w (f : Kgp_frame.t) = 192 193 kv_int_opt w 'x' (Kgp_frame.x f); ··· 255 256 kv_int_opt w 'I' cmd.image_number; 256 257 (* Complex options *) 257 258 cmd.placement |> Option.iter (write_placement w); 258 - cmd.delete |> Option.iter (write_delete w); 259 + cmd.delete |> Option.iter (write_delete w ~free:cmd.delete_free); 259 260 cmd.frame |> Option.iter (write_frame w); 260 261 cmd.animation |> Option.iter (write_animation w); 261 262 cmd.compose |> Option.iter (write_compose w);
+6 -2
lib/kgp_command.mli
··· 62 62 63 63 (** {1 Deletion} *) 64 64 65 - val delete : ?quiet:Kgp_quiet.t -> Kgp_delete.t -> t 66 - (** Delete images or placements. *) 65 + val delete : ?free:bool -> ?quiet:Kgp_quiet.t -> Kgp_delete.t -> t 66 + (** Delete images or placements. 67 + 68 + @param free If true, also free the image data from memory (default: false). 69 + Without [~free:true], only placements are removed and the image data 70 + can be reused for new placements. *) 67 71 68 72 (** {1 Animation} *) 69 73
+18 -35
lib/kgp_delete.ml
··· 1 1 type t = 2 2 [ `All_visible 3 - | `All_visible_and_free 4 3 | `By_id of int * int option 5 - | `By_id_and_free of int * int option 6 4 | `By_number of int * int option 7 - | `By_number_and_free of int * int option 8 5 | `At_cursor 9 - | `At_cursor_and_free 10 6 | `At_cell of int * int 11 - | `At_cell_and_free of int * int 12 7 | `At_cell_z of int * int * int 13 - | `At_cell_z_and_free of int * int * int 14 8 | `By_column of int 15 - | `By_column_and_free of int 16 9 | `By_row of int 17 - | `By_row_and_free of int 18 10 | `By_z_index of int 19 - | `By_z_index_and_free of int 20 11 | `By_id_range of int * int 21 - | `By_id_range_and_free of int * int 22 - | `Frames 23 - | `Frames_and_free ] 12 + | `Frames ] 24 13 25 - let to_char : t -> char = function 26 - | `All_visible -> 'a' 27 - | `All_visible_and_free -> 'A' 28 - | `By_id _ -> 'i' 29 - | `By_id_and_free _ -> 'I' 30 - | `By_number _ -> 'n' 31 - | `By_number_and_free _ -> 'N' 32 - | `At_cursor -> 'c' 33 - | `At_cursor_and_free -> 'C' 34 - | `At_cell _ -> 'p' 35 - | `At_cell_and_free _ -> 'P' 36 - | `At_cell_z _ -> 'q' 37 - | `At_cell_z_and_free _ -> 'Q' 38 - | `By_column _ -> 'x' 39 - | `By_column_and_free _ -> 'X' 40 - | `By_row _ -> 'y' 41 - | `By_row_and_free _ -> 'Y' 42 - | `By_z_index _ -> 'z' 43 - | `By_z_index_and_free _ -> 'Z' 44 - | `By_id_range _ -> 'r' 45 - | `By_id_range_and_free _ -> 'R' 46 - | `Frames -> 'f' 47 - | `Frames_and_free -> 'F' 14 + let to_char ~free : t -> char = 15 + let base = function 16 + | `All_visible -> 'a' 17 + | `By_id _ -> 'i' 18 + | `By_number _ -> 'n' 19 + | `At_cursor -> 'c' 20 + | `At_cell _ -> 'p' 21 + | `At_cell_z _ -> 'q' 22 + | `By_column _ -> 'x' 23 + | `By_row _ -> 'y' 24 + | `By_z_index _ -> 'z' 25 + | `By_id_range _ -> 'r' 26 + | `Frames -> 'f' 27 + in 28 + fun t -> 29 + let c = base t in 30 + if free then Char.uppercase_ascii c else c
+16 -28
lib/kgp_delete.mli
··· 25 25 26 26 {2 Placements vs Image Data} 27 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. 28 + Each deletion type can optionally free image data (controlled by the 29 + [~free] parameter in the delete command): 30 + - {b Without free}: Removes placements only. The image data remains in 31 + memory and can be displayed again later. (Protocol: lowercase char) 32 + - {b With free}: Removes placements AND frees the image data. The 33 + image cannot be displayed again without retransmitting. (Protocol: 34 + uppercase char) 37 35 38 36 {2 Placement IDs} 39 37 ··· 50 48 {2 Virtual Placements} 51 49 52 50 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. *) 51 + by: [{`By_id}], [{`By_number}], and [{`By_id_range}]. Other deletion 52 + commands do not affect virtual placements. *) 56 53 57 54 type t = 58 55 [ `All_visible 59 - | `All_visible_and_free 60 56 | `By_id of int * int option 61 - | `By_id_and_free of int * int option 62 57 | `By_number of int * int option 63 - | `By_number_and_free of int * int option 64 58 | `At_cursor 65 - | `At_cursor_and_free 66 59 | `At_cell of int * int 67 - | `At_cell_and_free of int * int 68 60 | `At_cell_z of int * int * int 69 - | `At_cell_z_and_free of int * int * int 70 61 | `By_column of int 71 - | `By_column_and_free of int 72 62 | `By_row of int 73 - | `By_row_and_free of int 74 63 | `By_z_index of int 75 - | `By_z_index_and_free of int 76 64 | `By_id_range of int * int 77 - | `By_id_range_and_free of int * int 78 - | `Frames 79 - | `Frames_and_free ] 65 + | `Frames ] 80 66 (** Deletion target specification. 81 67 82 68 {b Screen-based:} ··· 98 84 {b Animation:} 99 85 - [`Frames] - Animation frames only (not the base image) 100 86 101 - All variants have an [_and_free] version that also releases image data. *) 87 + Use the [~free] parameter in the delete command to also release 88 + image data from memory. *) 102 89 103 - val to_char : t -> char 90 + val to_char : free:bool -> t -> char 104 91 (** Convert to protocol character for the delete command. 105 92 106 - Returns the character used in the [d=] control data key. Lowercase 107 - for placement-only deletion, uppercase for deletion with data free. *) 93 + Returns the character used in the [d=] control data key. 94 + @param free If true, returns uppercase (frees data); if false, 95 + returns lowercase (keeps data). *)