ratatui_unity/lib.rs
1//! # ratatui_unity
2//!
3//! A C ABI wrapper around [`ratatui`] that renders terminal UIs to RGB24
4//! pixel buffers, suitable for embedding in game engines (e.g. Unity).
5//!
6//! The crate is compiled as both `cdylib` and `staticlib` so that it can be
7//! consumed from any host capable of calling C functions. All public entry
8//! points are `extern "C"` and `#[no_mangle]`; there is no idiomatic Rust API.
9//!
10//! ## High-level flow
11//!
12//! 1. Create a terminal handle with [`ratatui_create`].
13//! 2. For each frame:
14//! - Call [`ratatui_begin_frame`] to reset per-frame state.
15//! - Build a layout tree with [`ratatui_split`] / [`ratatui_inner`].
16//! - Optionally set a style with [`ratatui_set_style`] before any
17//! widget call.
18//! - Queue widget commands (e.g. [`ratatui_block`], [`ratatui_paragraph`],
19//! [`ratatui_chart_begin`] / [`ratatui_chart_end`], …).
20//! - Call [`ratatui_end_frame`] (or [`ratatui_end_frame_hashed`]) to draw
21//! the queue and rasterize the cell grid into an RGB24 pixel buffer.
22//! 3. When done, call [`ratatui_destroy`] to release the handle.
23//!
24//! ## Memory & lifetime
25//!
26//! The handle returned by [`ratatui_create`] is an opaque pointer to a
27//! heap-allocated `TerminalState`. The pixel
28//! buffer pointer returned by `ratatui_end_frame*` is owned by the handle and
29//! is only valid until the next FFI call that mutates the handle. The caller
30//! must copy the bytes before issuing further calls if it wants to retain them.
31//!
32//! ## Safety
33//!
34//! All FFI entry points perform null-pointer checks on the handle and on any
35//! pointer argument they dereference. Strings are read as null-terminated
36//! `*const c_char`. Slices passed as `(ptr, len)` pairs must reference valid
37//! memory for the duration of the call.
38//!
39//! ## Source layout
40//!
41//! The C ABI surface lives in `ffi` (split into `lifecycle`, `layout`,
42//! `style`, `widgets`, `builders`, and shared `util` helpers) and is
43//! re-exported here. The non-FFI internals live in `terminal`, `commands`,
44//! `renderer`, `font`, and `color`.
45
46mod color;
47mod commands;
48mod ffi;
49mod font;
50mod renderer;
51mod terminal;
52
53pub use ffi::*;
54
55#[cfg(test)]
56mod tests {
57 // Panicking via unwrap/expect is the standard way to fail a test.
58 #![allow(clippy::unwrap_used)]
59 use super::*;
60 use crate::ffi::util::state_ref;
61 use std::ffi::CString;
62
63 /// Regression: 65536 tab-separated columns used to truncate `col_count`
64 /// to 0 in the u16 cast and panic with a division by zero — and, before
65 /// the column cap, stalled the layout solver for hours.
66 #[test]
67 fn table_with_more_than_u16_max_columns_does_not_panic() {
68 let handle = ratatui_create(10, 5, 14.0);
69 ratatui_begin_frame(handle);
70 let data = CString::new(vec!["h"; 65536].join("\t")).unwrap();
71 ratatui_table(handle, 0, data.as_ptr());
72 ratatui_end_frame(handle);
73 ratatui_destroy(handle);
74 }
75
76 /// Regression: invalid font bytes must be rejected without panicking
77 /// (the crate is built with `panic = "abort"` in release).
78 #[test]
79 fn set_custom_font_with_invalid_bytes_returns_zero() {
80 let handle = ratatui_create(10, 5, 14.0);
81 let bytes = [0u8; 16];
82 assert_eq!(ratatui_set_custom_font(handle, bytes.as_ptr(), bytes.len() as u32), 0);
83 ratatui_destroy(handle);
84 }
85
86 /// Regression: after a successful font swap the reported pixel dimensions
87 /// must match the rasterized buffer size.
88 #[test]
89 fn set_custom_font_resyncs_pixel_dimensions() {
90 let handle = ratatui_create(10, 5, 14.0);
91 let bytes = include_bytes!("../fonts/JetBrainsMono-Regular.ttf");
92 assert_eq!(
93 ratatui_set_custom_font(handle, bytes.as_ptr(), bytes.len() as u32),
94 1
95 );
96 let w = ratatui_pixel_width(handle);
97 let h = ratatui_pixel_height(handle);
98 ratatui_begin_frame(handle);
99 ratatui_end_frame(handle);
100 let state = state_ref(handle).unwrap();
101 assert_eq!(state.pixel_buffer.len(), w as usize * h as usize * 3);
102 ratatui_destroy(handle);
103 }
104}