feat(os): port Ghostty's Linux OS helpers
Ports ZIG/src/os for Linux into ghostty-os: XDG dirs, home dir, hostname, env/PATH helpers, passwd lookup (via nix), shell detection, resources dir discovery, string encodings (printf-q, URL percent), systemd sd_notify, i18n via gettextrs, xdg-open, desktop/cgroup/kernel info, temp dirs, URI parsing (port of std.Uri incl. Ghostty's extra options), and a zbus-based Flatpak HostCommand implementation. The crate stays free of unsafe. Consequences, all tracked as TODO(integration) in docs/rewrite/notes/ghostty-os.md: - locale::ensure_locale is a no-op (setlocale/setenv are unsafe; GTK calls setlocale itself); setenv/unsetenv are not exposed. - passwd's Flatpak branch needs the termio pty and falls back to the sandbox entry for now. - FlatpakHostCommand's D-Bus path is untested (no portal here). Intentional fix: a leading '@' in NOTIFY_SOCKET is treated as an abstract socket. macOS/Windows/WASM modules are out of scope. Test Plan: - cargo test -p ghostty-os (88 tests) - cargo clippy -p ghostty-os --all-targets -- -D warnings
This commit is contained in:
1 parent
1388f117d3
commit
f8c5ce06d6
30 files changed
+3736
-1
No files matched your search
@@ -0,0 +1,112 @@
|
||||
/// 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.
|
||||
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;
|
||||
};
|
||||
|
||||
let pid = rustix::process::getpid().as_raw_nonzero().get();
|
||||
let Ok(gio_pid) = gio_pid_str.to_string_lossy().parse::<i32>() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
gio_pid == 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 {
|
||||
// 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::*;
|
||||
|
||||
fn de(session: Option<&str>, current: Option<&str>) -> DesktopEnvironment {
|
||||
temp_env::with_vars(
|
||||
[
|
||||
("XDG_SESSION_DESKTOP", session),
|
||||
("XDG_CURRENT_DESKTOP", current),
|
||||
],
|
||||
desktop_environment,
|
||||
)
|
||||
}
|
||||
|
||||
#[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 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());
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user