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