Kitty Graphics Protocol in OCaml
terminal
graphics
ocaml
1(* Terminal Environment Detection *)
2
3type graphics_mode = [ `Auto | `Enabled | `Disabled | `Tmux ]
4
5let is_kitty () =
6 Option.is_some (Sys.getenv_opt "KITTY_WINDOW_ID") ||
7 (match Sys.getenv_opt "TERM" with
8 | Some term -> String.lowercase_ascii term = "xterm-kitty"
9 | None -> false) ||
10 (match Sys.getenv_opt "TERM_PROGRAM" with
11 | Some prog -> String.lowercase_ascii prog = "kitty"
12 | None -> false)
13
14let is_wezterm () =
15 Option.is_some (Sys.getenv_opt "WEZTERM_PANE") ||
16 (match Sys.getenv_opt "TERM_PROGRAM" with
17 | Some prog -> String.lowercase_ascii prog = "wezterm"
18 | None -> false)
19
20let is_ghostty () =
21 Option.is_some (Sys.getenv_opt "GHOSTTY_RESOURCES_DIR") ||
22 (match Sys.getenv_opt "TERM_PROGRAM" with
23 | Some prog -> String.lowercase_ascii prog = "ghostty"
24 | None -> false)
25
26let is_graphics_terminal () =
27 is_kitty () || is_wezterm () || is_ghostty ()
28
29let is_tmux () = Kgp_tmux.is_active ()
30
31let is_interactive () =
32 Unix.isatty Unix.stdout
33
34let is_pager () =
35 (* Not interactive = likely piped to pager *)
36 not (is_interactive ()) ||
37 (* PAGER set and not in a known graphics terminal *)
38 (Option.is_some (Sys.getenv_opt "PAGER") && not (is_graphics_terminal ()))
39
40let resolve_mode = function
41 | `Disabled -> `Placeholder
42 | `Enabled -> `Graphics
43 | `Tmux -> `Tmux
44 | `Auto ->
45 if is_pager () || not (is_interactive ()) then
46 `Placeholder
47 else if is_tmux () then
48 (* Inside tmux - use passthrough if underlying terminal supports graphics *)
49 if is_graphics_terminal () then `Tmux
50 else `Placeholder
51 else if is_graphics_terminal () then
52 `Graphics
53 else
54 `Placeholder
55
56let supports_graphics mode =
57 match resolve_mode mode with
58 | `Graphics | `Tmux -> true
59 | `Placeholder -> false