use std::sync::{Arc, Mutex, mpsc};
use std::time::Instant;
use crossterm::event::{KeyCode, KeyModifiers};
use portable_pty::MasterPty;
use ratatui::prelude::Rect;
use chrono::Local;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub struct Pane {
pub master: Box<dyn MasterPty>,
pub child: Box<dyn portable_pty::Child>,
pub term: Arc<Mutex<vt100::Parser>>,
pub last_rows: u16,
pub last_cols: u16,
pub id: usize,
pub title: String,
pub child_pid: Option<u32>,
pub data_version: std::sync::Arc<std::sync::atomic::AtomicU64>,
pub last_title_check: Instant,
pub last_infer_title: Instant,
pub dead: bool,
pub vt_bridge_cache: Option<(Instant, bool)>,
}
#[derive(Clone, Copy, PartialEq)]
pub enum LayoutKind { Horizontal, Vertical }
pub enum Node {
Leaf(Pane),
Split { kind: LayoutKind, sizes: Vec<u16>, children: Vec<Node> },
}
pub struct Window {
pub root: Node,
pub active_path: Vec<usize>,
pub name: String,
pub id: usize,
pub activity_flag: bool,
pub bell_flag: bool,
pub silence_flag: bool,
pub last_output_time: std::time::Instant,
pub last_seen_version: u64,
pub manual_rename: bool,
}
#[derive(Clone)]
pub struct MenuItem {
pub name: String,
pub key: Option<char>,
pub command: String,
pub is_separator: bool,
}
#[derive(Clone)]
pub struct Menu {
pub title: String,
pub items: Vec<MenuItem>,
pub selected: usize,
pub x: Option<i16>,
pub y: Option<i16>,
}
#[derive(Clone)]
pub struct Hook {
pub name: String,
pub command: String,
}
pub struct PopupPty {
pub master: Box<dyn portable_pty::MasterPty>,
pub child: Box<dyn portable_pty::Child>,
pub term: std::sync::Arc<std::sync::Mutex<vt100::Parser>>,
}
pub struct PipePaneState {
pub pane_id: usize,
pub process: Option<std::process::Child>,
pub stdin: bool,
pub stdout: bool,
}
pub struct WaitChannel {
pub locked: bool,
pub waiters: Vec<mpsc::Sender<()>>,
}
pub enum Mode {
Passthrough,
Prefix { armed_at: Instant },
CommandPrompt { input: String, cursor: usize },
WindowChooser { selected: usize, tree: Vec<crate::session::TreeEntry> },
RenamePrompt { input: String },
RenameSessionPrompt { input: String },
CopyMode,
PaneChooser { opened_at: Instant },
MenuMode { menu: Menu },
PopupMode {
command: String,
output: String,
process: Option<std::process::Child>,
width: u16,
height: u16,
close_on_exit: bool,
popup_pty: Option<PopupPty>,
},
ConfirmMode {
prompt: String,
command: String,
input: String,
},
CopySearch {
input: String,
forward: bool,
},
ClockMode,
BufferChooser { selected: usize },
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SelectionMode { Char, Line, Rect }
#[derive(Debug, Clone, Copy)]
pub enum FocusDir { Left, Right, Up, Down }
pub struct AppState {
pub windows: Vec<Window>,
pub active_idx: usize,
pub mode: Mode,
pub escape_time_ms: u64,
pub repeat_time_ms: u64,
pub prefix_key: (KeyCode, KeyModifiers),
pub prediction_dimming: bool,
pub drag: Option<DragState>,
pub last_window_area: Rect,
pub mouse_enabled: bool,
pub paste_buffers: Vec<String>,
pub status_left: String,
pub status_right: String,
pub window_base_index: usize,
pub copy_anchor: Option<(u16,u16)>,
pub copy_pos: Option<(u16,u16)>,
pub copy_scroll_offset: usize,
pub copy_selection_mode: SelectionMode,
pub copy_search_query: String,
pub copy_search_matches: Vec<(u16, u16, u16)>,
pub copy_search_idx: usize,
pub copy_search_forward: bool,
pub copy_find_char_pending: Option<u8>,
pub display_map: Vec<(usize, Vec<usize>)>,
pub key_tables: std::collections::HashMap<String, Vec<Bind>>,
pub control_rx: Option<mpsc::Receiver<CtrlReq>>,
pub control_port: Option<u16>,
pub session_name: String,
pub attached_clients: usize,
pub created_at: chrono::DateTime<Local>,
pub next_win_id: usize,
pub next_pane_id: usize,
pub zoom_saved: Option<Vec<(Vec<usize>, Vec<u16>)>>,
pub sync_input: bool,
pub hooks: std::collections::HashMap<String, Vec<String>>,
pub wait_channels: std::collections::HashMap<String, WaitChannel>,
pub pipe_panes: Vec<PipePaneState>,
pub last_window_idx: usize,
pub last_pane_path: Vec<usize>,
pub tab_positions: Vec<(usize, u16, u16)>,
pub history_limit: usize,
pub display_time_ms: u64,
pub display_panes_time_ms: u64,
pub pane_base_index: usize,
pub focus_events: bool,
pub mode_keys: String,
pub status_visible: bool,
pub status_position: String,
pub status_style: String,
pub default_shell: String,
pub word_separators: String,
pub renumber_windows: bool,
pub automatic_rename: bool,
pub monitor_activity: bool,
pub visual_activity: bool,
pub remain_on_exit: bool,
pub aggressive_resize: bool,
pub set_titles: bool,
pub set_titles_string: String,
pub environment: std::collections::HashMap<String, String>,
pub pane_border_style: String,
pub pane_active_border_style: String,
pub window_status_format: String,
pub window_status_current_format: String,
pub window_status_separator: String,
pub window_status_style: String,
pub window_status_current_style: String,
pub window_status_activity_style: String,
pub window_status_bell_style: String,
pub window_status_last_style: String,
pub message_style: String,
pub message_command_style: String,
pub mode_style: String,
pub status_left_style: String,
pub status_right_style: String,
pub marked_pane: Option<(usize, usize)>,
pub monitor_silence: u64,
pub bell_action: String,
pub visual_bell: bool,
pub command_history: Vec<String>,
pub command_history_idx: usize,
pub status_interval: u64,
pub status_justify: String,
}
impl AppState {
pub fn new(session_name: String) -> Self {
Self {
windows: Vec::new(),
active_idx: 0,
mode: Mode::Passthrough,
escape_time_ms: 500,
repeat_time_ms: 500,
prefix_key: (crossterm::event::KeyCode::Char('b'), crossterm::event::KeyModifiers::CONTROL),
prediction_dimming: std::env::var("PSMUX_DIM_PREDICTIONS")
.map(|v| v != "0" && v.to_lowercase() != "false")
.unwrap_or(true),
drag: None,
last_window_area: Rect { x: 0, y: 0, width: 120, height: 30 },
mouse_enabled: true,
paste_buffers: Vec::new(),
status_left: "[#S] ".to_string(),
status_right: "\"#{=21:pane_title}\" %H:%M %d-%b-%y".to_string(),
window_base_index: 1,
copy_anchor: None,
copy_pos: None,
copy_scroll_offset: 0,
copy_selection_mode: SelectionMode::Char,
copy_search_query: String::new(),
copy_search_matches: Vec::new(),
copy_search_idx: 0,
copy_search_forward: true,
copy_find_char_pending: None,
display_map: Vec::new(),
key_tables: std::collections::HashMap::new(),
control_rx: None,
control_port: None,
session_name,
attached_clients: 0,
created_at: Local::now(),
next_win_id: 1,
next_pane_id: 1,
zoom_saved: None,
sync_input: false,
hooks: std::collections::HashMap::new(),
wait_channels: std::collections::HashMap::new(),
pipe_panes: Vec::new(),
last_window_idx: 0,
last_pane_path: Vec::new(),
tab_positions: Vec::new(),
history_limit: 2000,
display_time_ms: 750,
display_panes_time_ms: 1000,
pane_base_index: 0,
focus_events: false,
mode_keys: "emacs".to_string(),
status_visible: true,
status_position: "bottom".to_string(),
status_style: "bg=green,fg=black".to_string(),
default_shell: String::new(),
word_separators: " -_@".to_string(),
renumber_windows: false,
automatic_rename: true,
monitor_activity: false,
visual_activity: false,
remain_on_exit: false,
aggressive_resize: false,
set_titles: false,
set_titles_string: String::new(),
environment: std::collections::HashMap::new(),
pane_border_style: String::new(),
pane_active_border_style: "fg=green".to_string(),
window_status_format: "#I:#W#{?window_flags,#{window_flags}, }".to_string(),
window_status_current_format: "#I:#W#{?window_flags,#{window_flags}, }".to_string(),
window_status_separator: " ".to_string(),
window_status_style: String::new(),
window_status_current_style: String::new(),
window_status_activity_style: "reverse".to_string(),
window_status_bell_style: "reverse".to_string(),
window_status_last_style: String::new(),
message_style: "bg=yellow,fg=black".to_string(),
message_command_style: "bg=black,fg=yellow".to_string(),
mode_style: "bg=yellow,fg=black".to_string(),
status_left_style: String::new(),
status_right_style: String::new(),
marked_pane: None,
monitor_silence: 0,
bell_action: "any".to_string(),
visual_bell: false,
command_history: Vec::new(),
command_history_idx: 0,
status_interval: 15,
status_justify: "left".to_string(),
}
}
}
pub struct DragState {
pub split_path: Vec<usize>,
pub kind: LayoutKind,
pub index: usize,
pub start_x: u16,
pub start_y: u16,
pub left_initial: u16,
pub _right_initial: u16,
pub total_pixels: u16,
}
#[derive(Clone)]
pub enum Action {
DisplayPanes,
MoveFocus(FocusDir),
Command(String),
CommandChain(Vec<String>),
NewWindow,
SplitHorizontal,
SplitVertical,
KillPane,
NextWindow,
PrevWindow,
CopyMode,
Paste,
Detach,
RenameWindow,
WindowChooser,
ZoomPane,
}
#[derive(Clone)]
pub struct Bind { pub key: (KeyCode, KeyModifiers), pub action: Action, pub repeat: bool }
pub enum CtrlReq {
NewWindow(Option<String>, Option<String>, bool, Option<String>), SplitWindow(LayoutKind, Option<String>, bool, Option<String>, Option<u16>), KillPane,
CapturePane(mpsc::Sender<String>),
CapturePaneStyled(mpsc::Sender<String>),
FocusWindow(usize),
FocusPane(usize),
FocusPaneByIndex(usize),
SessionInfo(mpsc::Sender<String>),
CapturePaneRange(mpsc::Sender<String>, Option<u16>, Option<u16>),
ClientAttach,
ClientDetach,
DumpLayout(mpsc::Sender<String>),
DumpState(mpsc::Sender<String>),
SendText(String),
SendKey(String),
SendPaste(String),
ZoomPane,
CopyEnter,
CopyEnterPageUp,
CopyMove(i16, i16),
CopyAnchor,
CopyYank,
ClientSize(u16, u16),
FocusPaneCmd(usize),
FocusWindowCmd(usize),
MouseDown(u16,u16),
MouseDownRight(u16,u16),
MouseDownMiddle(u16,u16),
MouseDrag(u16,u16),
MouseUp(u16,u16),
MouseUpRight(u16,u16),
MouseUpMiddle(u16,u16),
MouseMove(u16,u16),
ScrollUp(u16, u16),
ScrollDown(u16, u16),
NextWindow,
PrevWindow,
RenameWindow(String),
ListWindows(mpsc::Sender<String>),
ListWindowsTmux(mpsc::Sender<String>),
ListWindowsFormat(mpsc::Sender<String>, String),
ListTree(mpsc::Sender<String>),
ToggleSync,
SetPaneTitle(String),
SendKeys(String, bool),
SendKeysX(String), SelectPane(String),
SelectWindow(usize),
ListPanes(mpsc::Sender<String>),
ListPanesFormat(mpsc::Sender<String>, String),
ListAllPanes(mpsc::Sender<String>),
ListAllPanesFormat(mpsc::Sender<String>, String),
KillWindow,
KillSession,
HasSession(mpsc::Sender<bool>),
RenameSession(String),
SwapPane(String),
ResizePane(String, u16),
SetBuffer(String),
ListBuffers(mpsc::Sender<String>),
ListBuffersFormat(mpsc::Sender<String>, String),
ShowBuffer(mpsc::Sender<String>),
ShowBufferAt(mpsc::Sender<String>, usize),
DeleteBuffer,
DisplayMessage(mpsc::Sender<String>, String),
LastWindow,
LastPane,
RotateWindow(bool),
DisplayPanes,
BreakPane,
JoinPane(usize),
RespawnPane,
BindKey(String, String, String, bool), UnbindKey(String),
ListKeys(mpsc::Sender<String>),
SetOption(String, String),
SetOptionQuiet(String, String, bool), SetOptionUnset(String), SetOptionAppend(String, String), ShowOptions(mpsc::Sender<String>),
SourceFile(String),
MoveWindow(Option<usize>),
SwapWindow(usize),
LinkWindow(String),
UnlinkWindow,
FindWindow(mpsc::Sender<String>, String),
MovePane(usize),
PipePane(String, bool, bool),
SelectLayout(String),
NextLayout,
ListClients(mpsc::Sender<String>),
SwitchClient(String),
LockClient,
RefreshClient,
SuspendClient,
CopyModePageUp,
ClearHistory,
SaveBuffer(String),
LoadBuffer(String),
SetEnvironment(String, String),
ShowEnvironment(mpsc::Sender<String>),
SetHook(String, String),
ShowHooks(mpsc::Sender<String>),
RemoveHook(String),
KillServer,
WaitFor(String, WaitForOp),
DisplayMenu(String, Option<i16>, Option<i16>),
DisplayPopup(String, u16, u16, bool),
ConfirmBefore(String, String),
ClockMode,
ResizePaneAbsolute(String, u16),
ShowOptionValue(mpsc::Sender<String>, String),
ChooseBuffer(mpsc::Sender<String>),
ServerInfo(mpsc::Sender<String>),
SendPrefix,
PrevLayout,
ResizeWindow(String, u16),
RespawnWindow,
FocusIn,
FocusOut,
}
pub static PTY_DATA_READY: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
#[derive(Clone, Copy)]
pub enum WaitForOp {
Wait,
Lock,
Signal,
Unlock,
}
#[derive(Debug, Clone, Default)]
pub struct ParsedTarget {
pub session: Option<String>,
pub window: Option<usize>,
pub pane: Option<usize>,
pub pane_is_id: bool,
pub window_is_id: bool,
}