Set window options per window; label panes where programs cannot clobber them
Two bugs found deploying to the LattePanda, both of which v1 also had. Window options do not propagate from the session and new windows do not inherit them. `set-option -t <session> pane-border-status` quietly applies to whichever window happens to be current, so only ONE window ever got pane borders -- v1's rack display has had unlabeled borders on two of its three windows this whole time and nobody noticed, because the window that got them was the one usually on screen. This is the same trap that makes remain-on-exit useless here. pane-border-status, pane-border-format and allow-rename are now set per window in place(), and a test asserts every window has them. Pane labels no longer use `select-pane -T`. The pane *title* is writable by whatever runs in the pane: unifly probes for Kitty graphics support on startup and tmux consumed part of that probe as a title change, so the border read "Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA" instead of "unifly". Labels now live in a pane-scoped user option, @socktop_label, which no escape sequence can reach; pane-border-format falls back to the title if it is somehow unset. Also: `terminal:` now expands a leading ~/ like `binaries:` already did. A window manager's PATH rarely includes ~/.cargo/bin, so a full path is the usual answer there and should not have to be spelled out longhand. Build cost corrected from guesses to measurements on the LattePanda (Atom x5-Z8350, 4 cores, 1.9 GB, no swap, toolchain already present): 108 seconds, peak 1.1 GB, 103 MB target directory, 946 KB binary. The README said twenty minutes and the installer budgeted 600 MB; both were wrong. Integration tests now use one tmux session name each -- cargo runs them in parallel and they were tearing down each other's server state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+4
-1
@@ -228,7 +228,10 @@ fn run(cfg: &Config, no_touch: bool) -> Result<()> {
|
||||
Some(term) => {
|
||||
let mut argv = shell_words::split(term)
|
||||
.with_context(|| format!("cannot parse terminal: {term}"))?;
|
||||
let prog = argv.remove(0);
|
||||
// Same tilde expansion as `binaries:`. A window manager's PATH
|
||||
// rarely includes ~/.cargo/bin, so a full path is the usual answer
|
||||
// here and it should not have to be spelled out longhand.
|
||||
let prog = config::expand_tilde(&argv.remove(0));
|
||||
Command::new(&prog)
|
||||
.args(argv)
|
||||
.arg("-e")
|
||||
|
||||
+39
-11
@@ -80,6 +80,19 @@ impl Tmux {
|
||||
)
|
||||
}
|
||||
|
||||
/// Name a pane for the border.
|
||||
///
|
||||
/// NOT `select-pane -T`, which sets the pane *title* -- a value the program
|
||||
/// running in the pane can overwrite at any time with an OSC escape. unifly
|
||||
/// probes for Kitty graphics support on startup and tmux consumed part of
|
||||
/// that probe as a title change, so the border read
|
||||
/// `Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA` instead of "unifly". A pane-scoped user
|
||||
/// option is ours alone and no escape sequence can reach it.
|
||||
fn label(&self, pane: &str, title: &str) -> Result<()> {
|
||||
self.run(&["set-option", "-p", "-t", pane, "@socktop_label", title])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn zoomed(&self, window: &str) -> Result<bool> {
|
||||
Ok(self.run(&[
|
||||
"display-message",
|
||||
@@ -122,8 +135,30 @@ impl Tmux {
|
||||
])?
|
||||
};
|
||||
|
||||
// These are all WINDOW options, and new windows do not inherit them, so
|
||||
// they must be set per window rather than once on the session. Setting
|
||||
// a window option with `set-option -t <session>` silently applies it to
|
||||
// whichever window happens to be current -- the same trap that makes
|
||||
// `remain-on-exit` useless here, and the reason v1's pane borders only
|
||||
// ever appeared on one of its windows.
|
||||
//
|
||||
// allow-rename: a window created with -n has automatic-rename off, but a
|
||||
// program can still rename it with an escape sequence.
|
||||
for (opt, val) in [
|
||||
("allow-rename", "off"),
|
||||
("pane-border-status", "top"),
|
||||
(
|
||||
"pane-border-format",
|
||||
// Fall back to the pane title if the label is somehow unset, so
|
||||
// a pane is never nameless.
|
||||
" #{?#{@socktop_label},#{@socktop_label},#{pane_title}} ",
|
||||
),
|
||||
] {
|
||||
self.run(&["set-option", "-w", "-t", &window, opt, val])?;
|
||||
}
|
||||
|
||||
let first_pane = self.run(&["display-message", "-p", "-t", &window, "#{pane_id}"])?;
|
||||
self.run(&["select-pane", "-t", &first_pane, "-T", &cell.panes[0].title])?;
|
||||
self.label(&first_pane, &cell.panes[0].title)?;
|
||||
let mut panes = vec![first_pane];
|
||||
|
||||
for pane in &cell.panes[1..] {
|
||||
@@ -138,7 +173,7 @@ impl Tmux {
|
||||
"#{pane_id}",
|
||||
&Self::shell_command(&pane.command),
|
||||
])?;
|
||||
self.run(&["select-pane", "-t", &id, "-T", &pane.title])?;
|
||||
self.label(&id, &pane.title)?;
|
||||
panes.push(id);
|
||||
}
|
||||
|
||||
@@ -264,16 +299,9 @@ impl Multiplexer for Tmux {
|
||||
}
|
||||
*self.placed.borrow_mut() = placed;
|
||||
|
||||
// Only SESSION options below; window options are set per window in
|
||||
// place(), for the reason given there.
|
||||
let s = &self.session;
|
||||
self.run(&["set-option", "-t", s, "pane-border-status", "top"])?;
|
||||
self.run(&[
|
||||
"set-option",
|
||||
"-t",
|
||||
s,
|
||||
"pane-border-format",
|
||||
" #{pane_title} ",
|
||||
])?;
|
||||
|
||||
// Mouse mode MUST stay off. With it on, a touch swipe is also delivered
|
||||
// to tmux as a click-drag: dragging across a pane border resizes it and
|
||||
// taps reselect panes, both fighting the gesture layer. Exclusive evdev
|
||||
|
||||
Reference in New Issue
Block a user