aboutsummaryrefslogtreecommitdiff
path: root/src/terminal/runtime.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-21 20:19:54 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-21 20:22:57 +0300
commit866aaf740e10fcfce169aaf81380ea0257fbe272 (patch)
treef815392b7c6b24a7c0de14e655bd635656383813 /src/terminal/runtime.rs
parentca579c402d5668560ca13059f82c03178be18b2a (diff)
refactor: separate server client boundaries
Diffstat (limited to 'src/terminal/runtime.rs')
-rw-r--r--src/terminal/runtime.rs312
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)
+ }
+}