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. *)