Resolves the 6 differential gap groups in docs/rewrite/notes/review- rv9-codex.md (details in final-Z11.md): - ghostty_os::zig_fmt ports std.fmt.parseInt/parseUnsigned/parseFloat (underscores, 0x/0o/0b prefixes, hex floats, inf/nan) with the Zig std tests and replaces every int/float parse in config, keybind parameters, ssh-cache and desktop detection. - Interior NUL no longer panics in font names and ends C strings like Zig's backing bytes (fontconfig, C config getters, title actions, clipboard callback, relative paths, config-file, shaders, background image, GTK CSS). - GTK new-window arguments are trimmed of ASCII whitespace only. - Command tokenization uses a port of std.process.ArgIteratorGeneral (incl. CR/LF separators) with its std tests. - UTF-8 validation happens where Zig's field parsers do it, so error kinds match (metric modifiers, quick-terminal size, link, ...). A scripted differential run of 202 fields x ~220 hostile/valid values against /usr/bin/ghostty (+validate-config and +show-config: exit code, stdout, stderr) now has zero mismatches besides build-specific defaults (theme resources path, auto-update-channel, gtk-opengl-debug); it also surfaced and fixed background-blur failure state, NaN formatting and NaN/-0 finalize clamps. Regression tests: ghostty-config/tests/ zig_parity.rs. Test Plan: - cargo test --workspace --no-fail-fast (3456 passed, exit 0) - just check-macos (both), just check-windows, just check-wasm
195 lines
7.0 KiB
Rust
195 lines
7.0 KiB
Rust
/// Returns true if the program was launched from a desktop environment.
|
|
///
|
|
/// On Linux GTK, this returns true if the program was launched using the
|
|
/// desktop file. This also includes when `gtk-launch` is used because I
|
|
/// can't find a way to distinguish the two scenarios.
|
|
#[cfg(target_os = "macos")]
|
|
pub fn launched_from_desktop() -> bool {
|
|
// macOS apps launched from finder or `open` always have the init
|
|
// process as their parent.
|
|
macos_launched_from_desktop(false, std::env::var_os(MAC_LAUNCH_SOURCE_ENV), || {
|
|
rustix::process::getppid().map_or(0, rustix::process::Pid::as_raw_pid)
|
|
})
|
|
}
|
|
|
|
/// iPhone/iPad is always launched from the "desktop".
|
|
#[cfg(target_os = "ios")]
|
|
pub fn launched_from_desktop() -> bool {
|
|
true
|
|
}
|
|
|
|
/// See the other variants.
|
|
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
|
pub fn launched_from_desktop() -> bool {
|
|
// On Linux and BSD, GTK sets GIO_LAUNCHED_DESKTOP_FILE and
|
|
// GIO_LAUNCHED_DESKTOP_FILE_PID. We only check the latter to see if
|
|
// we match the PID and assume that if we do, we were launched from
|
|
// the desktop file. Pid comparing catches the scenario where
|
|
// another terminal was launched from a desktop file and then launches
|
|
// Ghostty and Ghostty inherits the env.
|
|
let Some(gio_pid_str) = std::env::var_os("GIO_LAUNCHED_DESKTOP_FILE_PID") else {
|
|
return false;
|
|
};
|
|
|
|
// (`std::process::id` is `getpid` / `GetCurrentProcessId`.)
|
|
let pid = std::process::id();
|
|
// (`parseInt(c_int, ..., 10)`.)
|
|
let Ok(gio_pid) = crate::zig_fmt::parse_int::<i32>(gio_pid_str.as_encoded_bytes(), 10) else {
|
|
return false;
|
|
};
|
|
|
|
i64::from(gio_pid) == i64::from(pid)
|
|
}
|
|
|
|
/// Environment variable set by the macOS app to tell libghostty how it was
|
|
/// launched ("app", "cli" or "zig_run").
|
|
pub const MAC_LAUNCH_SOURCE_ENV: &str = "GHOSTTY_MAC_LAUNCH_SOURCE";
|
|
|
|
/// `launched_from_desktop` on macOS. `artifact_is_lib` mirrors
|
|
/// `build_config.artifact == .lib`: when embedded as a library, the app
|
|
/// bundle may tell us via [`MAC_LAUNCH_SOURCE_ENV`] that it launched us even
|
|
/// though our parent isn't `launchd`.
|
|
#[cfg_attr(not(any(target_os = "macos", test)), allow(dead_code))]
|
|
fn macos_launched_from_desktop(
|
|
artifact_is_lib: bool,
|
|
source: Option<std::ffi::OsString>,
|
|
getppid: impl FnOnce() -> i32,
|
|
) -> bool {
|
|
if artifact_is_lib && let Some(source) = source {
|
|
// Source can be "app", "cli", or "zig_run". We assume its the
|
|
// desktop only if its "app".
|
|
if source == "app" {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
getppid() == 1
|
|
}
|
|
|
|
/// The libghostty variant of [`launched_from_desktop`] on macOS (honours the
|
|
/// `GHOSTTY_MAC_LAUNCH_SOURCE` environment variable).
|
|
#[cfg(target_os = "macos")]
|
|
pub fn launched_from_desktop_lib() -> bool {
|
|
macos_launched_from_desktop(true, std::env::var_os(MAC_LAUNCH_SOURCE_ENV), || {
|
|
rustix::process::getppid().map_or(0, rustix::process::Pid::as_raw_pid)
|
|
})
|
|
}
|
|
|
|
/// The list of desktop environments that we detect. New Linux desktop
|
|
/// environments should only be added to this list if there's a specific reason
|
|
/// to differentiate between `gnome` and `other`.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum DesktopEnvironment {
|
|
Gnome,
|
|
Macos,
|
|
Other,
|
|
Windows,
|
|
}
|
|
|
|
/// Detect what desktop environment we are running under. This is mainly used
|
|
/// on Linux and BSD to enable or disable certain features but there may be more uses in
|
|
/// the future.
|
|
pub fn desktop_environment() -> DesktopEnvironment {
|
|
if cfg!(windows) {
|
|
return DesktopEnvironment::Windows;
|
|
}
|
|
if cfg!(target_os = "macos") {
|
|
return DesktopEnvironment::Macos;
|
|
}
|
|
|
|
// Use $XDG_SESSION_DESKTOP to determine what DE we are using on Linux
|
|
// https://www.freedesktop.org/software/systemd/man/latest/pam_systemd.html#desktop=
|
|
if let Some(sd) = std::env::var_os("XDG_SESSION_DESKTOP") {
|
|
let sd = sd.to_string_lossy();
|
|
if sd.eq_ignore_ascii_case("gnome") {
|
|
return DesktopEnvironment::Gnome;
|
|
}
|
|
if sd.eq_ignore_ascii_case("gnome-xorg") {
|
|
return DesktopEnvironment::Gnome;
|
|
}
|
|
}
|
|
|
|
// If $XDG_SESSION_DESKTOP is not set, or doesn't match any known
|
|
// DE, check $XDG_CURRENT_DESKTOP. $XDG_CURRENT_DESKTOP is a
|
|
// colon-separated list of up to three desktop names, although we
|
|
// only look at the first.
|
|
// https://specifications.freedesktop.org/desktop-entry-spec/latest/recognized-keys.html
|
|
if let Some(cd) = std::env::var_os("XDG_CURRENT_DESKTOP") {
|
|
let cd = cd.to_string_lossy();
|
|
let first = cd.split(':').next().unwrap_or("");
|
|
if first.eq_ignore_ascii_case("gnome") {
|
|
return DesktopEnvironment::Gnome;
|
|
}
|
|
}
|
|
|
|
DesktopEnvironment::Other
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[cfg(unix)]
|
|
#[allow(dead_code)]
|
|
fn de(session: Option<&str>, current: Option<&str>) -> DesktopEnvironment {
|
|
temp_env::with_vars(
|
|
[
|
|
("XDG_SESSION_DESKTOP", session),
|
|
("XDG_CURRENT_DESKTOP", current),
|
|
],
|
|
desktop_environment,
|
|
)
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[cfg(not(target_os = "macos"))]
|
|
#[test]
|
|
fn desktop_environment_detection() {
|
|
use DesktopEnvironment::{Gnome, Other};
|
|
assert_eq!(de(Some("gnome"), None), Gnome);
|
|
assert_eq!(de(Some("gnome-xorg"), None), Gnome);
|
|
assert_eq!(de(Some("foobar"), None), Other);
|
|
assert_eq!(de(None, None), Other);
|
|
assert_eq!(de(None, Some("GNOME")), Gnome);
|
|
assert_eq!(de(None, Some("FOOBAR")), Other);
|
|
assert_eq!(de(None, Some("GNOME:Unity")), Gnome);
|
|
assert_eq!(de(None, Some("Unity:GNOME")), Other);
|
|
assert_eq!(de(Some("foobar"), Some("GNOME")), Gnome);
|
|
}
|
|
|
|
#[test]
|
|
fn macos_launch_detection() {
|
|
use std::ffi::OsString;
|
|
// Parent is launchd.
|
|
assert!(macos_launched_from_desktop(false, None, || 1));
|
|
assert!(!macos_launched_from_desktop(false, None, || 4242));
|
|
// The env var only counts for the lib artifact and only for "app".
|
|
let app = Some(OsString::from("app"));
|
|
assert!(macos_launched_from_desktop(true, app.clone(), || 4242));
|
|
assert!(!macos_launched_from_desktop(false, app, || 4242));
|
|
assert!(!macos_launched_from_desktop(
|
|
true,
|
|
Some(OsString::from("cli")),
|
|
|| 4242
|
|
));
|
|
}
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
#[test]
|
|
fn launched_from_desktop_pid_match() {
|
|
let pid = std::process::id().to_string();
|
|
temp_env::with_var("GIO_LAUNCHED_DESKTOP_FILE_PID", Some(pid), || {
|
|
assert!(launched_from_desktop());
|
|
});
|
|
temp_env::with_var("GIO_LAUNCHED_DESKTOP_FILE_PID", Some("1"), || {
|
|
assert!(!launched_from_desktop());
|
|
});
|
|
temp_env::with_var("GIO_LAUNCHED_DESKTOP_FILE_PID", Some("abc"), || {
|
|
assert!(!launched_from_desktop());
|
|
});
|
|
temp_env::with_var("GIO_LAUNCHED_DESKTOP_FILE_PID", None::<&str>, || {
|
|
assert!(!launched_from_desktop());
|
|
});
|
|
}
|
|
}
|