fix-deviant(config): accept absolute Windows theme paths
Intentional deviation from Ghostty 1.3.1 (DEVIANT_FIXES.md row 38). Zig's Theme.parseCLI (config/Config.zig:9788-9799) treats any value containing ',', '=' or ':' as a light/dark pair. Every absolute Windows path (C:\themes\x, \\?\C:\..., \\server\share\x) contains a colon, so Ghostty on Windows rejects all absolute theme paths. A value that is a single Windows absolute path is now taken as a theme path; the light:/dark: pair syntax still works, including with drive paths as values. Test Plan: - cargo test -p ghostty-config windows_absolute_path_detection theme_windows_absolute_path_and_pair_with_paths
This commit is contained in:
1 parent
5c5962ea95
commit
e4a6ee7bf4
2 files changed
+70
No files matched your search
@@ -974,6 +974,19 @@ impl ConfigField for Theme {
|
|||||||
// we're parsing a light/dark mode theme pair. Note that "=" isn't
|
// we're parsing a light/dark mode theme pair. Note that "=" isn't
|
||||||
// actually valid for setting a light/dark mode pair but I anticipate
|
// actually valid for setting a light/dark mode pair but I anticipate
|
||||||
// it'll be a common typo.
|
// it'll be a common typo.
|
||||||
|
//
|
||||||
|
// DEVIANT (Windows, see DEVIANT_FIXES.md): an absolute Windows path
|
||||||
|
// (`C:\themes\x`, `\\?\C:\...`, `\\server\share\x`) contains a
|
||||||
|
// colon, so Ghostty would always parse it as a malformed pair and
|
||||||
|
// reject every absolute theme path on Windows.
|
||||||
|
if cfg!(windows) && is_windows_absolute_path(input) {
|
||||||
|
let trimmed = crate::cli_args::trim_bytes(input);
|
||||||
|
*self = Theme {
|
||||||
|
light: EncodedOs::from_bytes(trimmed),
|
||||||
|
dark: EncodedOs::from_bytes(trimmed),
|
||||||
|
};
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
if input.iter().any(|b| matches!(b, b',' | b'=' | b':')) {
|
if input.iter().any(|b| matches!(b, b',' | b'=' | b':')) {
|
||||||
let mut light: Option<EncodedOs> = None;
|
let mut light: Option<EncodedOs> = None;
|
||||||
let mut dark: Option<EncodedOs> = None;
|
let mut dark: Option<EncodedOs> = None;
|
||||||
@@ -1017,6 +1030,17 @@ impl ConfigField for Theme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether `input` (after leading whitespace) is a drive-letter (`C:\` or
|
||||||
|
/// `C:/`) or UNC / device (`\\...`) absolute path.
|
||||||
|
fn is_windows_absolute_path(input: &[u8]) -> bool {
|
||||||
|
let input = crate::cli_args::trim_bytes(input);
|
||||||
|
match input {
|
||||||
|
[b'\\', b'\\', ..] => true,
|
||||||
|
[d, b':', b'\\' | b'/', ..] => d.is_ascii_alphabetic(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A duration, stored in nanoseconds.
|
/// A duration, stored in nanoseconds.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
pub struct Duration {
|
pub struct Duration {
|
||||||
@@ -1289,6 +1313,51 @@ config_packed! {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used, clippy::approx_constant)]
|
#[allow(clippy::unwrap_used, clippy::approx_constant)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn windows_absolute_path_detection() {
|
||||||
|
for p in [
|
||||||
|
r"C:\themes\x",
|
||||||
|
"d:/themes/x",
|
||||||
|
r"\\?\C:\t\x",
|
||||||
|
r"\\srv\share\x",
|
||||||
|
r" C:\x",
|
||||||
|
] {
|
||||||
|
assert!(is_windows_absolute_path(p.as_bytes()), "{p}");
|
||||||
|
}
|
||||||
|
for p in [
|
||||||
|
"foo",
|
||||||
|
"light:foo,dark:bar",
|
||||||
|
"light:C:\\x",
|
||||||
|
"/abs/x",
|
||||||
|
"C:",
|
||||||
|
"1:\\x",
|
||||||
|
"C:x",
|
||||||
|
] {
|
||||||
|
assert!(!is_windows_absolute_path(p.as_bytes()), "{p}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn theme_windows_absolute_path_and_pair_with_paths() {
|
||||||
|
let mut t = Theme::default();
|
||||||
|
let r = t.parse_bytes(Some(br"C:\themes\my theme"));
|
||||||
|
if cfg!(windows) {
|
||||||
|
r.unwrap();
|
||||||
|
assert_eq!(t.light.as_encoded(), r"C:\themes\my theme");
|
||||||
|
assert_eq!(t.dark, t.light);
|
||||||
|
// The pair syntax still works with drive paths as values.
|
||||||
|
let mut t = Theme::default();
|
||||||
|
// (Backslashes are escapes in the pair syntax: quote them.)
|
||||||
|
t.parse_bytes(Some(br#"light:"C:\\a",dark:"C:\\b""#))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(t.light.as_encoded(), r"C:\a");
|
||||||
|
assert_eq!(t.dark.as_encoded(), r"C:\b");
|
||||||
|
} else {
|
||||||
|
// Zig behaviour everywhere else: a colon means "pair".
|
||||||
|
assert!(r.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn quick_terminal_size_value_formats_nan_like_zig() {
|
fn quick_terminal_size_value_formats_nan_like_zig() {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ when this file was written.
|
|||||||
| 35 | Termio: PTY hangup drops unread output | `termio/Exec.zig` `ReadThread.threadMainPosix` (exits as soon as `POLL.HUP` is seen) | `POLLIN \| POLLHUP` can arrive together with unread final output (a Linux PTY check confirmed this); exiting immediately loses the tail of the child's output. | After HUP the read thread makes one more pass over the fd, so the remaining output is delivered before exit. | 6629fb0 | none (needs a live PTY; verified by RV18 on Linux) |
|
| 35 | Termio: PTY hangup drops unread output | `termio/Exec.zig` `ReadThread.threadMainPosix` (exits as soon as `POLL.HUP` is seen) | `POLLIN \| POLLHUP` can arrive together with unread final output (a Linux PTY check confirmed this); exiting immediately loses the tail of the child's output. | After HUP the read thread makes one more pass over the fd, so the remaining output is delivered before exit. | 6629fb0 | none (needs a live PTY; verified by RV18 on Linux) |
|
||||||
| 36 | Font (wasm/browser Canvas): atlas pixel format | `font/face/web_canvas.zig` (atlas write branch on `.rgba`) | The branch tests a stale `.rgba` format against an enum that contains `.bgra`, so BGRA atlases are never converted. | The BGRA atlas conversion is implemented. No claim of equivalence with a real browser (not testable here). | d89691b | `ghostty-font` web_canvas tests (compile/Node level) |
|
| 36 | Font (wasm/browser Canvas): atlas pixel format | `font/face/web_canvas.zig` (atlas write branch on `.rgba`) | The branch tests a stale `.rgba` format against an enum that contains `.bgra`, so BGRA atlases are never converted. | The BGRA atlas conversion is implemented. No claim of equivalence with a real browser (not testable here). | d89691b | `ghostty-font` web_canvas tests (compile/Node level) |
|
||||||
| 37 | Termio: subprocess cleanup | `termio/Exec.zig:1188-1224` (`getpgid`) | Loops forever while `getpgid(child) == our pgid`, i.e. also when the child died before `setsid` and is an unreaped zombie (its pgid stays ours), hanging the surface close. | Still waits without a cap (no 5 s limit) while the child is alive, but stops (no SIGHUP, nothing to kill) once `waitid(WNOWAIT)` shows the child exited. | uncommitted (P3) | `getpgid_gives_up_on_exited_child_without_setsid`, `getpgid_waits_for_late_setsid` (`ghostty-termio/src/exec.rs`) |
|
| 37 | Termio: subprocess cleanup | `termio/Exec.zig:1188-1224` (`getpgid`) | Loops forever while `getpgid(child) == our pgid`, i.e. also when the child died before `setsid` and is an unreaped zombie (its pgid stays ours), hanging the surface close. | Still waits without a cap (no 5 s limit) while the child is alive, but stops (no SIGHUP, nothing to kill) once `waitid(WNOWAIT)` shows the child exited. | uncommitted (P3) | `getpgid_gives_up_on_exited_child_without_setsid`, `getpgid_waits_for_late_setsid` (`ghostty-termio/src/exec.rs`) |
|
||||||
|
| 38 | Config (Windows): absolute theme paths | `config/Config.zig:9788-9799` (`Theme.parseCLI`) | Any value containing `,`, `=` or `:` is parsed as a `light:..,dark:..` pair, and a Windows absolute path (`C:\\themes\\x`, `\\\\?\\C:\\...`) always contains a colon, so `theme = <absolute path>` is rejected on Windows (found by running the config tests under Wine). | On Windows a value that starts like an absolute Windows path (drive letter + `:` + separator, or `\\\\`) is a single theme; the pair syntax is unchanged (quote backslashes). Other platforms are unchanged. | uncommitted (agent WINE) | `windows_absolute_path_detection`, `theme_windows_absolute_path_and_pair_with_paths` (`ghostty-config/src/types_b.rs`), `theme_loading` under Wine |
|
||||||
|
|
||||||
## Upstream quirks deliberately kept (not fixed)
|
## Upstream quirks deliberately kept (not fixed)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user