diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-21 20:19:54 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-21 20:22:57 +0300 |
| commit | 866aaf740e10fcfce169aaf81380ea0257fbe272 (patch) | |
| tree | f815392b7c6b24a7c0de14e655bd635656383813 /src/terminal/runtime.rs | |
| parent | ca579c402d5668560ca13059f82c03178be18b2a (diff) | |
refactor: separate server client boundaries
Diffstat (limited to 'src/terminal/runtime.rs')
| -rw-r--r-- | src/terminal/runtime.rs | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/src/terminal/runtime.rs b/src/terminal/runtime.rs new file mode 100644 index 00000000..43c2467d --- /dev/null +++ b/src/terminal/runtime.rs @@ -0,0 +1,312 @@ +use std::sync::{atomic::AtomicBool, Arc}; + +use bytes::Bytes; +use ratatui::{layout::Rect, Frame}; +use tokio::sync::{mpsc, Notify}; + +use crate::events::AppEvent; +use crate::layout::PaneId; + +/// Live runtime for a server-owned terminal. +/// +/// The PTY implementation still delegates to the legacy pane runtime while the +/// migration proceeds, but production code now depends on this terminal-layer +/// type instead of the pane module's implementation detail. +pub struct TerminalRuntime(crate::pane::PaneRuntime); + +impl TerminalRuntime { + pub fn shutdown(self) { + self.0.shutdown(); + } + + pub fn spawn( + pane_id: PaneId, + rows: u16, + cols: u16, + cwd: std::path::PathBuf, + scrollback_limit_bytes: usize, + host_terminal_theme: crate::terminal_theme::TerminalTheme, + default_shell: &str, + events: mpsc::Sender<AppEvent>, + render_notify: Arc<Notify>, + render_dirty: Arc<AtomicBool>, + ) -> std::io::Result<Self> { + crate::pane::PaneRuntime::spawn( + pane_id, + rows, + cols, + cwd, + scrollback_limit_bytes, + host_terminal_theme, + default_shell, + events, + render_notify, + render_dirty, + ) + .map(Self) + } + + pub fn spawn_shell_command( + pane_id: PaneId, + rows: u16, + cols: u16, + cwd: std::path::PathBuf, + command: &str, + extra_env: &[(String, String)], + scrollback_limit_bytes: usize, + host_terminal_theme: crate::terminal_theme::TerminalTheme, + events: mpsc::Sender<AppEvent>, + render_notify: Arc<Notify>, + render_dirty: Arc<AtomicBool>, + ) -> std::io::Result<Self> { + crate::pane::PaneRuntime::spawn_shell_command( + pane_id, + rows, + cols, + cwd, + command, + extra_env, + scrollback_limit_bytes, + host_terminal_theme, + events, + render_notify, + render_dirty, + ) + .map(Self) + } + + pub fn spawn_argv_command( + pane_id: PaneId, + rows: u16, + cols: u16, + cwd: std::path::PathBuf, + argv: &[String], + scrollback_limit_bytes: usize, + host_terminal_theme: crate::terminal_theme::TerminalTheme, + events: mpsc::Sender<AppEvent>, + render_notify: Arc<Notify>, + render_dirty: Arc<AtomicBool>, + ) -> std::io::Result<Self> { + crate::pane::PaneRuntime::spawn_argv_command( + pane_id, + rows, + cols, + cwd, + argv, + scrollback_limit_bytes, + host_terminal_theme, + events, + render_notify, + render_dirty, + ) + .map(Self) + } + + pub fn apply_host_terminal_theme(&self, theme: crate::terminal_theme::TerminalTheme) { + self.0.apply_host_terminal_theme(theme); + } + + pub fn begin_graceful_release(&self, agent: crate::detect::Agent) { + self.0.begin_graceful_release(agent); + } + + pub fn resize(&self, rows: u16, cols: u16, cell_width_px: u32, cell_height_px: u32) { + self.0.resize(rows, cols, cell_width_px, cell_height_px); + } + + pub fn scroll_up(&self, lines: usize) { + self.0.scroll_up(lines); + } + + pub fn scroll_down(&self, lines: usize) { + self.0.scroll_down(lines); + } + + pub fn scroll_reset(&self) { + self.0.scroll_reset(); + } + + pub fn set_scroll_offset_from_bottom(&self, lines: usize) { + self.0.set_scroll_offset_from_bottom(lines); + } + + pub fn scroll_metrics(&self) -> Option<crate::pane::ScrollMetrics> { + self.0.scroll_metrics() + } + + pub fn input_state(&self) -> Option<crate::pane::InputState> { + self.0.input_state() + } + + pub fn cursor_state( + &self, + area: Rect, + show_cursor: bool, + ) -> Option<crate::pane::TerminalCursorState> { + self.0.cursor_state(area, show_cursor) + } + + pub fn visible_text(&self) -> String { + self.0.visible_text() + } + + pub fn visible_ansi(&self) -> String { + self.0.visible_ansi() + } + + pub fn recent_text(&self, lines: usize) -> String { + self.0.recent_text(lines) + } + + pub fn recent_ansi(&self, lines: usize) -> String { + self.0.recent_ansi(lines) + } + + pub fn recent_unwrapped_text(&self, lines: usize) -> String { + self.0.recent_unwrapped_text(lines) + } + + pub fn recent_unwrapped_ansi(&self, lines: usize) -> String { + self.0.recent_unwrapped_ansi(lines) + } + + pub fn extract_selection(&self, selection: &crate::selection::Selection) -> Option<String> { + self.0.extract_selection(selection) + } + + pub fn render(&self, frame: &mut Frame, area: Rect, show_cursor: bool) { + self.0.render(frame, area, show_cursor); + } + + pub fn visible_hyperlinks(&self, area: Rect) -> Vec<((u16, u16), String, String)> { + self.0.visible_hyperlinks(area) + } + + pub fn kitty_image_placements_with_data_filter<F>( + &self, + needs_data: F, + ) -> Vec<crate::ghostty::KittyImagePlacement> + where + F: FnMut(crate::ghostty::KittyImageDescriptor) -> bool, + { + self.0.kitty_image_placements_with_data_filter(needs_data) + } + + pub fn keyboard_protocol(&self) -> crate::input::KeyboardProtocol { + self.0.keyboard_protocol() + } + + pub fn encode_terminal_key(&self, key: crate::input::TerminalKey) -> Vec<u8> { + self.0.encode_terminal_key(key) + } + + pub async fn send_bytes(&self, bytes: Bytes) -> Result<(), mpsc::error::SendError<Bytes>> { + self.0.send_bytes(bytes).await + } + + pub fn try_send_bytes(&self, bytes: Bytes) -> Result<(), mpsc::error::TrySendError<Bytes>> { + self.0.try_send_bytes(bytes) + } + + pub async fn send_paste(&self, text: String) -> Result<(), mpsc::error::SendError<Bytes>> { + self.0.send_paste(text).await + } + + pub fn try_send_focus_event(&self, event: crate::ghostty::FocusEvent) -> bool { + self.0.try_send_focus_event(event) + } + + pub fn wheel_routing(&self) -> Option<crate::pane::WheelRouting> { + self.0.wheel_routing() + } + + pub fn encode_mouse_button( + &self, + kind: crossterm::event::MouseEventKind, + column: u16, + row: u16, + modifiers: crossterm::event::KeyModifiers, + ) -> Option<Vec<u8>> { + self.0.encode_mouse_button(kind, column, row, modifiers) + } + + pub fn encode_mouse_wheel( + &self, + kind: crossterm::event::MouseEventKind, + column: u16, + row: u16, + modifiers: crossterm::event::KeyModifiers, + ) -> Option<Vec<u8>> { + self.0.encode_mouse_wheel(kind, column, row, modifiers) + } + + pub fn encode_alternate_scroll( + &self, + kind: crossterm::event::MouseEventKind, + ) -> Option<Vec<u8>> { + self.0.encode_alternate_scroll(kind) + } + + pub fn cwd(&self) -> Option<std::path::PathBuf> { + self.0.cwd() + } +} + +#[cfg(test)] +impl TerminalRuntime { + pub(crate) fn current_size(&self) -> (u16, u16) { + self.0.current_size() + } + + pub(crate) fn test_with_channel(cols: u16, rows: u16) -> (Self, mpsc::Receiver<Bytes>) { + let (runtime, rx) = crate::pane::PaneRuntime::test_with_channel(cols, rows); + (Self(runtime), rx) + } + + pub(crate) fn test_with_channel_capacity( + cols: u16, + rows: u16, + capacity: usize, + ) -> (Self, mpsc::Receiver<Bytes>) { + let (runtime, rx) = + crate::pane::PaneRuntime::test_with_channel_capacity(cols, rows, capacity); + (Self(runtime), rx) + } + + pub(crate) fn test_with_screen_bytes(cols: u16, rows: u16, bytes: &[u8]) -> Self { + Self(crate::pane::PaneRuntime::test_with_screen_bytes( + cols, rows, bytes, + )) + } + + pub(crate) fn test_with_scrollback_bytes( + cols: u16, + rows: u16, + scrollback_limit_bytes: usize, + bytes: &[u8], + ) -> Self { + Self(crate::pane::PaneRuntime::test_with_scrollback_bytes( + cols, + rows, + scrollback_limit_bytes, + bytes, + )) + } + + pub(crate) fn test_with_channel_and_scrollback_bytes( + cols: u16, + rows: u16, + scrollback_limit_bytes: usize, + bytes: &[u8], + channel_capacity: usize, + ) -> (Self, mpsc::Receiver<Bytes>) { + let (runtime, rx) = crate::pane::PaneRuntime::test_with_channel_and_scrollback_bytes( + cols, + rows, + scrollback_limit_bytes, + bytes, + channel_capacity, + ); + (Self(runtime), rx) + } +} |
