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