1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Window-level tmux notification handlers.
//!
//! Covers window add, close, and rename events from the tmux control-mode
//! session. Each event maps to a corresponding tab operation.
use crate::app::window_state::WindowState;
use crate::tmux::TmuxWindowId;
impl WindowState {
/// Handle window add notification - creates a new tab
pub(crate) fn handle_tmux_window_add(&mut self, window_id: TmuxWindowId) {
crate::debug_info!("TMUX", "Window added: @{}", window_id);
// Check max tabs limit
if self.config.load().tabs.max_tabs > 0
&& self.tab_manager.tab_count() >= self.config.load().tabs.max_tabs
{
crate::debug_error!(
"TMUX",
"Cannot create tab for tmux window @{}: max_tabs limit ({}) reached",
window_id,
self.config.load().tabs.max_tabs
);
return;
}
// Get current grid size from renderer
let grid_size = self.renderer.as_ref().map(|r| r.grid_size());
// Create a new tab for this tmux window
match self.tab_manager.new_tab(
&self.config.load(),
std::sync::Arc::clone(&self.runtime),
false, // Don't inherit CWD from active tab for tmux
grid_size,
) {
Ok(tab_id) => {
crate::debug_info!(
"TMUX",
"Created tab {} for tmux window @{}",
tab_id,
window_id
);
// Register the mapping
self.tmux_state.tmux_sync.map_window(window_id, tab_id);
// Set initial title based on tmux window ID
// Note: These tabs are for displaying tmux windows, but the gateway tab
// is where the actual tmux control connection lives. We store the tmux pane ID
// on the tab so we know which pane to route input to.
if let Some(tab) = self.tab_manager.get_tab_mut(tab_id) {
// UX.md T8/M11: the placeholder must not say "tmux" on
// a par-mux path; the daemon's own name (or the
// attach's window-name swap) replaces it.
let prefix = if self.tmux_state.is_mux_attached() {
"par-mux"
} else {
"tmux"
};
tab.set_title(&format!("{} @{}", prefix, window_id));
// Note: Don't set tmux_gateway_active here - only the gateway tab is the gateway
// Start refresh task for the new tab
if let Some(window) = &self.window {
tab.start_refresh_task(
std::sync::Arc::clone(&self.runtime),
std::sync::Arc::clone(window),
self.config.load().rendering.max_fps,
self.config.load().power.inactive_tab_fps,
);
}
// Resize terminal to match current renderer dimensions
// try_lock: intentional — called during window-add handling in the sync event
// loop. On miss: the new tmux tab's terminal is not resized this frame; the
// size will be corrected on the next Resized event.
if let Some(renderer) = &self.renderer
&& let Ok(mut term) = tab.terminal.try_write()
{
let (cols, rows) = renderer.grid_size();
let size = renderer.size();
let width_px = size.width as usize;
let height_px = size.height as usize;
term.set_cell_dimensions(
renderer.cell_width() as u32,
renderer.cell_height() as u32,
);
if let Err(e) = term.resize_with_pixels(cols, rows, width_px, height_px) {
crate::debug_error!(
"TERMINAL",
"resize_with_pixels failed (tmux_window): {e}"
);
}
crate::debug_info!(
"TMUX",
"Resized tmux tab {} terminal to {}x{}",
tab_id,
cols,
rows
);
}
}
// The first daemon window tab displaces a session-restore
// placeholder: a restored mux window spawns one local
// shell tab only so a failed attach never leaves the
// window empty — once a real daemon tab exists that shell
// is dead weight beside it, so close it (fast path: PTY
// pre-killed) and keep only daemon tabs. No-op unless a
// restore marked a placeholder.
if let Some(placeholder) = self.tmux_state.mux_restore_placeholder_tab.take()
&& self.tab_manager.get_tab(placeholder).is_some()
{
let _ = self.tab_manager.close_tab_fast(placeholder);
}
}
Err(e) => {
crate::debug_error!(
"TMUX",
"Failed to create tab for tmux window @{}: {}",
window_id,
e
);
}
}
// Hide the gateway tab now that a tmux window tab exists
self.hide_gateway_tab();
}
}