mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-21 08:02:21 +00:00
* fix(windows): bundle Microsoft's ConPTY so panes can answer color queries
The in-box conhost swallows a pane process's OSC 11 background query: it
never reaches tty7's emulator and no reply is ever written back, so
applications that choose a light or dark UI from the terminal background
render a dark UI under a light theme.
tty7 already answers OSC 10/11/12 from the live theme, so nothing was
missing but a pseudoconsole that forwards the question. Microsoft ships one
as a redistributable, and portable-pty already prefers a sideloaded
conpty.dll over kernel32's, so this is packaging rather than code: the pair
goes beside tty7-app.exe, where the DLL search path finds it.
Measured on Windows 11 26200, same binary, only the pair added beside it:
in-box conhost: the terminal side never sees the query; the client times
out with no reply
bundled ConPTY: the terminal side sees ESC]11;?BEL and a real pane reads
back rgb:efef/f1f1/f5f5 under catppuccin_latte, which is
the preset's exact background
The two files are one supported unit, so the release verifier fails a
package that carries only one, a mismatched pair, or the MIT notice-less
DLL. They also join PORTABLE_MANAGED_ROOTS, without which the updater would
reject every portable archive that contains them; they are deliberately not
required by verify_portable_payload, since tty7 runs without them and a
packaging slip should fail the release rather than a user's update.
build.rs stages the pair beside cargo's output so a development build does
not quietly run on the in-box host, and the daemon logs which pseudoconsole
it got.
Closes #345
* fix(windows): restage the bundled ConPTY when it goes missing
Watching only the vendored sources meant a staged copy that left the target
directory stayed gone: the build script was cached, so it never ran again to
put it back, and the build silently fell back to the in-box conhost. Cargo
treats a rerun-if-changed path that does not exist as changed, so naming the
destinations makes the staging self-healing.
Found by deleting target/debug/conpty.dll and watching the next build not
bring it back.
---------
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
79 lines
3.1 KiB
Rust
79 lines
3.1 KiB
Rust
fn main() {
|
|
#[cfg(windows)]
|
|
{
|
|
println!("cargo:rerun-if-changed=assets/favicon.ico");
|
|
let mut res = winresource::WindowsResource::new();
|
|
res.set_icon("assets/favicon.ico");
|
|
if let Err(e) = res.compile() {
|
|
println!("cargo:warning=failed to embed Windows icon: {e}");
|
|
}
|
|
stage_bundled_conpty();
|
|
}
|
|
}
|
|
|
|
/// Put the bundled ConPTY beside cargo's output, the way the packaged app has
|
|
/// it beside `tty7-app.exe`.
|
|
///
|
|
/// `portable-pty` picks a sideloaded `conpty.dll` over the one in `kernel32`,
|
|
/// and finds it through the DLL search path — which starts at the directory of
|
|
/// the running executable, not the working directory. Without this a
|
|
/// development build silently runs on the in-box `conhost.exe` and loses the
|
|
/// `OSC 11` answer that packaged builds give (see `assets/windows/conpty`), so
|
|
/// the bug reappears only for people running from source.
|
|
///
|
|
/// Test executables live one directory deeper, so `deps/` gets a copy too.
|
|
#[cfg(windows)]
|
|
fn stage_bundled_conpty() {
|
|
use std::path::{Path, PathBuf};
|
|
|
|
println!("cargo:rerun-if-changed=assets/windows/conpty");
|
|
|
|
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
|
|
let vendored = match arch.as_str() {
|
|
"x86_64" => "x64",
|
|
// Every other Windows architecture is unreleased, so nothing is
|
|
// vendored for it and the in-box conhost stays in charge.
|
|
_ => return,
|
|
};
|
|
let source = PathBuf::from("assets/windows/conpty").join(vendored);
|
|
|
|
// .../target/<triple>?/<profile>/build/<pkg>-<hash>/out
|
|
let Some(target_dir) = std::env::var_os("OUT_DIR")
|
|
.map(PathBuf::from)
|
|
.and_then(|out| out.ancestors().nth(3).map(Path::to_path_buf))
|
|
else {
|
|
return;
|
|
};
|
|
|
|
for name in ["conpty.dll", "OpenConsole.exe"] {
|
|
let from = source.join(name);
|
|
for directory in [target_dir.clone(), target_dir.join("deps")] {
|
|
if !directory.is_dir() {
|
|
continue;
|
|
}
|
|
let to = directory.join(name);
|
|
// Watching the destination as well as the source is what makes the
|
|
// staging self-healing: cargo treats a path that no longer exists
|
|
// as changed, so a copy that gets cleaned out of the target
|
|
// directory comes back on the next build instead of staying gone
|
|
// behind a cached build script.
|
|
println!("cargo:rerun-if-changed={}", to.display());
|
|
// Re-copying would fail while a previously built tty7 is running,
|
|
// and the pair only changes when it is deliberately updated.
|
|
let same = std::fs::metadata(&to)
|
|
.ok()
|
|
.zip(std::fs::metadata(&from).ok())
|
|
.is_some_and(|(a, b)| a.len() == b.len());
|
|
if same {
|
|
continue;
|
|
}
|
|
if let Err(e) = std::fs::copy(&from, &to) {
|
|
println!(
|
|
"cargo:warning=could not stage {}: {e} — this build will use the in-box conhost",
|
|
to.display()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|