fusabi_tui_scarab/lib.rs
1//! Scarab shared memory backend for Fusabi TUI.
2//!
3//! This crate provides integration between the Fusabi TUI framework and the
4//! Scarab terminal emulator's shared memory protocol. It enables zero-copy
5//! rendering of TUI applications directly to Scarab's terminal buffer.
6//!
7//! # Architecture
8//!
9//! Scarab uses a split-process architecture with shared memory for IPC:
10//! - **Daemon**: Headless server that owns PTY processes
11//! - **Client**: Bevy-based GUI that renders via shared memory
12//! - **Plugins**: Can run in either process with Fusabi scripting
13//!
14//! This crate provides:
15//! - `ScarabRenderer`: Renderer implementation that writes to shared memory
16//! - `SharedCell` / `SharedState`: Zero-copy types matching Scarab's protocol
17//! - Conversion utilities between Fusabi and Scarab types
18//! - Plugin trait for building TUI applications
19//!
20//! # Examples
21//!
22//! ## Basic Rendering
23//!
24//! ```no_run
25//! use fusabi_tui_scarab::renderer::ScarabRenderer;
26//! use fusabi_tui_render::renderer::Renderer;
27//! use fusabi_tui_core::buffer::Buffer;
28//! use fusabi_tui_core::layout::Rect;
29//! use fusabi_tui_core::style::{Style, Color};
30//!
31//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
32//! // Connect to Scarab's shared memory
33//! let mut renderer = ScarabRenderer::connect(None)?;
34//!
35//! // Create a buffer and draw to it
36//! let size = renderer.size()?;
37//! let mut buffer = Buffer::new(size);
38//!
39//! let style = Style::new().fg(Color::Green);
40//! buffer.set_string(0, 0, "Hello, Scarab!", style);
41//!
42//! // Render to shared memory
43//! renderer.draw(&buffer)?;
44//! renderer.flush()?;
45//! # Ok(())
46//! # }
47//! ```
48//!
49//! ## Plugin Development
50//!
51//! ```no_run
52//! use fusabi_tui_scarab::plugin::{TuiPlugin, PluginContext, RenderContext, InputEvent, Action};
53//! use fusabi_tui_scarab::error::Result;
54//! use fusabi_tui_core::buffer::Buffer;
55//! use fusabi_tui_core::style::{Style, Color};
56//!
57//! struct MyPlugin {
58//! counter: u32,
59//! }
60//!
61//! impl TuiPlugin for MyPlugin {
62//! fn on_init(&mut self, ctx: &PluginContext) -> Result<()> {
63//! println!("Plugin initialized!");
64//! Ok(())
65//! }
66//!
67//! fn on_render(&mut self, ctx: &RenderContext) -> Result<Buffer> {
68//! let mut buffer = Buffer::new(ctx.size);
69//! let style = Style::new().fg(Color::Cyan);
70//!
71//! let text = format!("Frame: {}", ctx.frame);
72//! buffer.set_string(0, 0, &text, style);
73//!
74//! Ok(buffer)
75//! }
76//!
77//! fn on_input(&mut self, event: InputEvent) -> Result<Action> {
78//! self.counter += 1;
79//! Ok(Action::Redraw)
80//! }
81//! }
82//! ```
83//!
84//! # Shared Memory Layout
85//!
86//! The shared memory region contains a `SharedState` structure with:
87//! - Sequence number for lock-free synchronization
88//! - Cursor position
89//! - 200x100 grid of cells (each 16 bytes)
90//!
91//! All structures use `#[repr(C)]` and `bytemuck::Pod` for zero-copy safety.
92//!
93//! # Features
94//!
95//! - `plugin`: Enables plugin system with JSON serialization (optional)
96
97#![warn(missing_docs)]
98#![warn(clippy::all)]
99
100pub mod convert;
101pub mod error;
102pub mod plugin;
103pub mod renderer;
104pub mod shared;
105
106// Re-export commonly used types
107pub use error::{Result, ScarabError};
108pub use renderer::ScarabRenderer;
109pub use shared::{SharedCell, SharedState, GRID_HEIGHT, GRID_WIDTH, SHMEM_PATH};
110
111#[cfg(feature = "plugin")]
112pub use plugin::{
113 Action, InputEvent, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent,
114 MouseEventKind, PluginContext, RenderContext, TuiPlugin,
115};
116
117/// Prelude module for convenient imports.
118pub mod prelude {
119 //! Convenient re-exports for common types and traits.
120
121 pub use crate::error::{Result, ScarabError};
122 pub use crate::renderer::ScarabRenderer;
123 pub use crate::shared::{SharedCell, SharedState};
124
125 #[cfg(feature = "plugin")]
126 pub use crate::plugin::{
127 Action, InputEvent, KeyCode, KeyEvent, PluginContext, RenderContext, TuiPlugin,
128 };
129
130 // Re-export core TUI types
131 pub use fusabi_tui_core::{
132 buffer::{Buffer, Cell},
133 layout::{Constraint, Direction, Layout, Rect},
134 style::{Color, Modifier, Style},
135 };
136
137 // Re-export renderer trait
138 pub use fusabi_tui_render::renderer::Renderer;
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_shared_cell_size() {
147 // Verify SharedCell is exactly 16 bytes (required by Scarab protocol)
148 assert_eq!(std::mem::size_of::<SharedCell>(), 16);
149 }
150
151 #[test]
152 fn test_shared_state_size() {
153 // Verify SharedState size matches expected layout
154 let expected = std::mem::size_of::<u64>() // sequence_number
155 + std::mem::size_of::<u8>() // dirty_flag
156 + std::mem::size_of::<u8>() // error_mode
157 + std::mem::size_of::<u16>() // cursor_x
158 + std::mem::size_of::<u16>() // cursor_y
159 + 2 // _padding2
160 + (std::mem::size_of::<SharedCell>() * shared::BUFFER_SIZE); // cells
161
162 assert_eq!(std::mem::size_of::<SharedState>(), expected);
163 }
164}