Kitty Graphics Protocol in OCaml
terminal
graphics
ocaml
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(* Tiny animation test - no chunking needed *)
7(* Uses 20x20 images which are ~1067 bytes base64 (well under 4096) *)
8
9module K = Kgp
10
11let solid_color_rgba ~width ~height ~r ~g ~b ~a =
12 let pixels = Bytes.create (width * height * 4) in
13 for i = 0 to (width * height) - 1 do
14 let idx = i * 4 in
15 Bytes.set pixels idx (Char.chr r);
16 Bytes.set pixels (idx + 1) (Char.chr g);
17 Bytes.set pixels (idx + 2) (Char.chr b);
18 Bytes.set pixels (idx + 3) (Char.chr a)
19 done;
20 Bytes.to_string pixels
21
22let send cmd ~data =
23 print_string (K.to_string cmd ~data);
24 flush stdout
25
26let () =
27 (* Use 20x20 to avoid chunking: 20*20*4 = 1600 bytes, base64 ~2134 bytes *)
28 let width, height = (20, 20) in
29 let image_id = 999 in
30
31 (* Clear any existing images *)
32 send (K.delete ~free:true ~quiet:`Errors_only `All_visible) ~data:"";
33
34 (* Step 1: Transmit base frame (red) *)
35 let red_frame = solid_color_rgba ~width ~height ~r:255 ~g:0 ~b:0 ~a:255 in
36 send
37 (K.transmit ~image_id ~format:`Rgba32 ~width ~height ~quiet:`Errors_only ())
38 ~data:red_frame;
39
40 (* Step 2: Add frame (orange) with 100ms gap *)
41 let orange_frame =
42 solid_color_rgba ~width ~height ~r:255 ~g:165 ~b:0 ~a:255
43 in
44 send
45 (K.frame ~image_id ~format:`Rgba32 ~width ~height
46 ~frame:(K.Frame.make ~gap_ms:100 ~composition:`Overwrite ())
47 ~quiet:`Errors_only ())
48 ~data:orange_frame;
49
50 (* Step 3: Add frame (yellow) *)
51 let yellow_frame =
52 solid_color_rgba ~width ~height ~r:255 ~g:255 ~b:0 ~a:255
53 in
54 send
55 (K.frame ~image_id ~format:`Rgba32 ~width ~height
56 ~frame:(K.Frame.make ~gap_ms:100 ~composition:`Overwrite ())
57 ~quiet:`Errors_only ())
58 ~data:yellow_frame;
59
60 (* Step 4: Add frame (green) *)
61 let green_frame = solid_color_rgba ~width ~height ~r:0 ~g:255 ~b:0 ~a:255 in
62 send
63 (K.frame ~image_id ~format:`Rgba32 ~width ~height
64 ~frame:(K.Frame.make ~gap_ms:100 ~composition:`Overwrite ())
65 ~quiet:`Errors_only ())
66 ~data:green_frame;
67
68 (* Step 5: Create placement *)
69 send
70 (K.display ~image_id
71 ~placement:
72 (K.Placement.make ~placement_id:1 ~cell_x_offset:0 ~cell_y_offset:0
73 ~cursor:`Static ())
74 ~quiet:`Errors_only ())
75 ~data:"";
76
77 (* Step 6: Start animation *)
78 send (K.animate ~image_id (K.Animation.set_state ~loops:1 `Run)) ~data:"";
79
80 print_endline "";
81 print_endline "Tiny animation (20x20) - Red -> Orange -> Yellow -> Green";
82 print_endline "This uses no chunking. Press Enter to stop...";
83 flush stdout;
84 let _ = read_line () in
85
86 (* Stop animation *)
87 send (K.animate ~image_id (K.Animation.set_state `Stop)) ~data:"";
88
89 (* Clean up *)
90 send (K.delete ~free:true ~quiet:`Errors_only `All_visible) ~data:"";
91 print_endline "Done."