fix: Windows core build, embedded init, byte-exact paths, version info

Closes the remaining core/lib/termio/GTK rows (final-Z2.md):
- ghostty-core compiles for Windows (Unix-only exec fields and file
  modes gated); checked with cargo check --target x86_64-pc-windows-gnu.
- Embedded init: ensure_locale, SIGPIPE ignored in ghostty_init for
  C/Swift hosts, Apple crash tags; the argv passed to ghostty_init feeds
  ghostty_config_load_cli_args; ghostty_config_load_file accepts
  non-UTF-8 paths.
- OSC 7 pwd, embedded surface cwd/command/env/initial input,
  ghostty_surface_text and GTK file drops keep raw bytes.
- XTVERSION, TERM_PROGRAM_VERSION, the About dialog and crash metadata
  use ghostty_build_info instead of hardcoded versions.

Test Plan:
- cargo test --workspace (3327 passed)
- cargo clippy --workspace --all-targets -- -D warnings
This commit is contained in:
ddidderr committed 2026-10-12 08:14:02 +02:00
1 parent 8519f1ba1b
commit ebefad5031
30 files changed
+863 -80

No files matched your search

+13 -10
View File
@@ -424,6 +424,7 @@ pub fn write_screen_temp_file(
loc: WriteScreenLoc,
emit: WriteScreenFormat,
) -> Result<String, CoreError> {
#[cfg(unix)]
use std::os::unix::fs::OpenOptionsExt as _;
// Create a temporary directory to store our scrollback.
@@ -432,12 +433,11 @@ pub fn write_screen_temp_file(
let file_path = tmp_dir.path().join(&filename);
let result = (|| -> Result<String, CoreError> {
let mut file = std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.mode(0o600)
.open(&file_path)?;
let mut opts = std::fs::OpenOptions::new();
opts.write(true).create(true).truncate(true);
#[cfg(unix)]
opts.mode(0o600);
let mut file = opts.open(&file_path)?;
file.write_all(contents.as_bytes())?;
file.flush()?;
Ok(std::fs::canonicalize(&file_path)?
@@ -1272,10 +1272,13 @@ mod tests {
assert!(path.ends_with("screen.txt"));
let read = std::fs::read_to_string(&path).unwrap();
assert_eq!(read, s);
let mode = std::os::unix::fs::PermissionsExt::mode(
&std::fs::metadata(&path).unwrap().permissions(),
);
assert_eq!(mode & 0o777, 0o600);
#[cfg(unix)]
{
let mode = std::os::unix::fs::PermissionsExt::mode(
&std::fs::metadata(&path).unwrap().permissions(),
);
assert_eq!(mode & 0o777, 0o600);
}
let _ = std::fs::remove_dir_all(std::path::Path::new(&path).parent().unwrap());
}
+13 -1
View File
@@ -215,6 +215,12 @@ impl Surface {
}
}
/// [`Surface::complete_clipboard_request`] for a paste of raw bytes
/// (Zig passes `[]const u8`; e.g. dropped non-UTF-8 file names).
pub fn complete_paste_bytes(&self, data: &[u8], confirmed: bool) -> Result<(), ClipboardError> {
self.complete_clipboard_paste(data, confirmed)
}
/// Paste text (also the target of `text_callback`).
pub(crate) fn complete_clipboard_paste(
&self,
@@ -390,7 +396,13 @@ impl Surface {
/// `textCallback`: text input that is pasted verbatim.
pub fn text_callback(&self, text: &str) -> Result<(), ClipboardError> {
self.text_callback_bytes(text.as_bytes())
}
/// [`Surface::text_callback`] for raw bytes (Zig takes `[]const u8`;
/// the embedded `ghostty_surface_text` forwards the host bytes as is).
pub fn text_callback_bytes(&self, text: &[u8]) -> Result<(), ClipboardError> {
let _crash = self.crash_guard();
self.complete_clipboard_paste(text.as_bytes(), true)
self.complete_clipboard_paste(text, true)
}
}
+24 -6
View File
@@ -471,10 +471,15 @@ impl Surface {
.and_then(|wd| wd.value())
.map(str::to_owned);
ecfg.resources_dir = global::state().resources_dir.host().map(str::to_owned);
ecfg.rt_pre_exec_info = ghostty_termio::command::RtPreExecInfo::init(&config);
ecfg.rt_post_fork_info = ghostty_termio::command::RtPostForkInfo::init(&config);
ecfg.create_scope = rt_surface.create_scope_fn();
ecfg.restore_rlimit = global::state().rlimits.nofile;
// The Windows exec backend has no pre-exec/post-fork hooks,
// cgroup scopes or rlimits (Zig: Linux/POSIX only).
#[cfg(unix)]
{
ecfg.rt_pre_exec_info = ghostty_termio::command::RtPreExecInfo::init(&config);
ecfg.rt_post_fork_info = ghostty_termio::command::RtPostForkInfo::init(&config);
ecfg.create_scope = rt_surface.create_scope_fn();
ecfg.restore_rlimit = global::state().rlimits.nofile;
}
let exec = Exec::new(ecfg)?;
let args = exec.args().to_vec();
@@ -935,7 +940,9 @@ impl Surface {
}
Message::PwdChange(w) => {
let pwd = String::from_utf8_lossy(w.slice()).into_owned();
// Keep the OSC 7 bytes (see `Pwd`): non-UTF-8 bytes are
// carried with the reversible byte escape.
let pwd = ghostty_os::osbytes::bytes_to_config_string(w.slice());
self.perform(RtAction::Pwd(Pwd { pwd }));
}
@@ -1364,7 +1371,14 @@ impl Surface {
pub fn pwd(&self) -> Option<String> {
let t = self.lock_terminal();
let pwd = t.get_pwd()?;
Some(String::from_utf8_lossy(pwd).into_owned())
Some(ghostty_os::osbytes::bytes_to_config_string(pwd))
}
/// The raw bytes of [`Surface::pwd`] (Zig returns the OSC 7 bytes as is).
#[must_use]
pub fn pwd_bytes(&self) -> Option<Vec<u8>> {
let t = self.lock_terminal();
t.get_pwd().map(<[u8]>::to_vec)
}
/// Returns the x/y coordinate of where the IME (Input Method Editor)
@@ -2055,6 +2069,10 @@ impl CoreSurface for Surface {
Surface::complete_clipboard_request(self, req, data, confirmed)
}
fn complete_paste_bytes(&self, data: &[u8], confirmed: bool) -> Result<(), ClipboardError> {
Surface::complete_paste_bytes(self, data, confirmed)
}
fn set_clipboard_read_allowed(&self, allow: bool) {
use ghostty_config::types_b::ClipboardAccess;
self.set_config_clipboard(
+1 -1
View File
@@ -1664,7 +1664,7 @@ impl Surface {
sel: link.selection,
trim: false,
});
let pwd = t.get_pwd().map(|p| String::from_utf8_lossy(p).into_owned());
let pwd = t.get_pwd().map(ghostty_os::osbytes::bytes_to_config_string);
resolve_path_for_opening(pwd.as_deref(), &str).unwrap_or(str)
}