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(** Animation Control
7
8 Operations for controlling animation playback. The protocol supports both
9 terminal-driven and client-driven animation modes.
10
11 {2 Protocol Overview}
12
13 Animation control uses action [a=a] with various keys:
14 - [s]: Set playback state (1=stop, 2=loading, 3=run)
15 - [c]: Set current frame (1-based frame number)
16 - [r]: Target frame number for gap changes
17 - [z]: Frame gap/delay in milliseconds
18 - [v]: Loop count
19
20 {2 Terminal-Driven Animation}
21
22 The terminal automatically advances frames based on each frame's gap
23 (delay). To start terminal-driven animation:
24
25 {[
26 (* Start infinite loop *)
27 Kgp.animate ~image_id:1
28 (Animation.set_state ~loops:1 `Run)
29 (* Run 3 times then stop *)
30 Kgp.animate ~image_id:1
31 (Animation.set_state ~loops:4 `Run)
32 (* Stop animation *)
33 Kgp.animate ~image_id:1
34 (Animation.set_state `Stop)
35 ]}
36
37 {2 Client-Driven Animation}
38
39 The client manually controls which frame is displayed:
40
41 {[
42 (* Display specific frame *)
43 Kgp.animate ~image_id:1 (Animation.set_current_frame 5)
44
45 (* Advance to next frame in application logic *)
46 let next_frame = (current_frame mod total_frames) + 1 in
47 Kgp.animate ~image_id:1 (Animation.set_current_frame next_frame)
48 ]}
49
50 {2 Modifying Frame Timing}
51
52 Frame gaps can be changed during playback:
53
54 {[
55 (* Slow down frame 3 *)
56 Kgp.animate ~image_id:1
57 (Animation.set_gap ~frame:3 ~gap_ms:200)
58 (* Make frame 5 instant/gapless *)
59 Kgp.animate ~image_id:1
60 (Animation.set_gap ~frame:5 ~gap_ms:(-1))
61 ]}
62
63 {2 Loop Counting}
64
65 The [loops] parameter in {!set_state}:
66 - 0: Ignored (doesn't change loop setting)
67 - 1: Infinite loop
68 - n > 1: Loop (n-1) times, then stop *)
69
70type t =
71 [ `Set_state of Kgp_animation_state.t * int option
72 | `Set_gap of int * int
73 | `Set_current of int ]
74(** Animation control operations.
75
76 - [`Set_state (state, loops)] - Set animation playback state with optional
77 loop count.
78 - [`Set_gap (frame, gap_ms)] - Set the delay for a specific frame.
79 - [`Set_current frame] - Jump to a specific frame (1-based). *)
80
81val set_state : ?loops:int -> Kgp_animation_state.t -> t
82(** Set animation playback state.
83
84 @param loops
85 Loop count: 0 = ignored, 1 = infinite, n > 1 = (n-1) loops. Protocol key:
86 [v].
87 @param state The target playback state.
88
89 Examples:
90 {[
91 set_state `Run (* Run with current loop setting *) set_state ~loops:1 `Run
92 (* Run infinitely *) set_state ~loops:3 `Run
93 (* Run twice, then stop *) set_state `Stop (* Pause animation *)
94 set_state `Loading (* Run, wait for more frames at end *)
95 ]} *)
96
97val set_gap : frame:int -> gap_ms:int -> t
98(** Set the gap (delay) for a specific frame.
99
100 @param frame 1-based frame number to modify. Protocol key: [r].
101 @param gap_ms
102 Delay in milliseconds before next frame. Negative values create gapless
103 frames (not displayed, instant skip). Protocol key: [z].
104
105 Note: Frame 1 is the root/base image. Use 2+ for added frames. *)
106
107val set_current_frame : int -> t
108(** Make a specific frame the current displayed frame.
109
110 @param frame 1-based frame number to display. Protocol key: [c].
111
112 Used for client-driven animation where the application controls frame
113 advancement rather than the terminal. *)