| 223 |
223 |
|
}
|
| 224 |
224 |
|
|
| 225 |
225 |
|
/// The whole `setvtrgb` table, in the file format that tool reads: three
|
| 226 |
|
- |
/// whitespace-separated lines of sixteen decimal values, all reds then all
|
|
226 |
+ |
/// lines of sixteen comma-separated decimal values, all reds then all
|
| 227 |
227 |
|
/// greens then all blues.
|
| 228 |
228 |
|
///
|
|
229 |
+ |
/// **Comma, not whitespace.** kbd's `setvtrgb` parses each line with a
|
|
230 |
+ |
/// comma-delimited scan and rejects anything else with "Insufficient number
|
|
231 |
+ |
/// of fields", which is a parse failure before it ever opens a console.
|
|
232 |
+ |
/// This emitted spaces from the day it was written, inherited from the
|
|
233 |
+ |
/// `vtrgb.py` it replaced, so `alloy-vtrgb.service` failed on every boot of
|
|
234 |
+ |
/// every image and the console palette has never once been applied. Found
|
|
235 |
+ |
/// 2026-08-03 by booting a server image, where that service is the only
|
|
236 |
+ |
/// themed surface there is. Verified both ways against the shipped kbd: the
|
|
237 |
+ |
/// space form dies at parsing, the comma form parses and reaches the
|
|
238 |
+ |
/// console.
|
|
239 |
+ |
///
|
| 229 |
240 |
|
/// The kernel cmdline (`vt.default_*`, via [`Palette::vt_channel`]) and
|
| 230 |
241 |
|
/// this file are the same sixteen colors applied twice — once by the kernel
|
| 231 |
242 |
|
/// before userspace, once by `alloy-vtrgb.service` after. They were
|
| 246 |
257 |
|
.to_string()
|
| 247 |
258 |
|
})
|
| 248 |
259 |
|
.collect();
|
| 249 |
|
- |
out.push_str(&row.join(" "));
|
|
260 |
+ |
out.push_str(&row.join(","));
|
| 250 |
261 |
|
out.push('\n');
|
| 251 |
262 |
|
}
|
| 252 |
263 |
|
Ok(out)
|
| 748 |
759 |
|
assert_eq!(p.ansi(15).unwrap().to_hex(), "#f0ece4");
|
| 749 |
760 |
|
}
|
| 750 |
761 |
|
|
| 751 |
|
- |
// The table skelgen emits has to be the one vtrgb.py emitted, because the
|
| 752 |
|
- |
// greeter's look was tuned against it. This is that script's output for
|
| 753 |
|
- |
// Akari Dawn, captured before it was deleted.
|
|
762 |
+ |
// The values are the ones vtrgb.py emitted, because the greeter's look was
|
|
763 |
+ |
// tuned against them. The separator is not: that script wrote spaces, this
|
|
764 |
+ |
// inherited them, and `setvtrgb` reads commas, so the table it produced was
|
|
765 |
+ |
// rejected at parse time on every boot for as long as either existed. The
|
|
766 |
+ |
// test that used to live here pinned the whole string including the
|
|
767 |
+ |
// spaces, which is how a bug gets a guard pointed the wrong way.
|
| 754 |
768 |
|
#[test]
|
| 755 |
|
- |
fn the_vtrgb_table_matches_what_the_retired_script_produced() {
|
| 756 |
|
- |
let want = "26 106 58 176 48 128 48 237 81 138 58 176 48 128 48 240\n\
|
| 757 |
|
- |
24 40 88 120 64 96 88 231 75 69 88 120 64 96 88 236\n\
|
| 758 |
|
- |
22 40 48 64 80 128 88 222 69 48 48 64 80 128 88 228\n";
|
|
769 |
+ |
fn the_vtrgb_table_carries_the_tuned_values_in_the_format_setvtrgb_reads() {
|
|
770 |
+ |
let want = "26,106,58,176,48,128,48,237,81,138,58,176,48,128,48,240\n\
|
|
771 |
+ |
24,40,88,120,64,96,88,231,75,69,88,120,64,96,88,236\n\
|
|
772 |
+ |
22,40,48,64,80,128,88,222,69,48,48,64,80,128,88,228\n";
|
| 759 |
773 |
|
assert_eq!(dawn().vtrgb_table().unwrap(), want);
|
| 760 |
774 |
|
}
|
| 761 |
775 |
|
|
|
776 |
+ |
// The property behind that literal, stated so a future edit to the values
|
|
777 |
+ |
// cannot quietly reintroduce the separator bug: three lines, sixteen
|
|
778 |
+ |
// comma-separated fields each, every field a decimal byte. This is
|
|
779 |
+ |
// `setvtrgb`'s documented format and the whole of what its parser accepts.
|
|
780 |
+ |
#[test]
|
|
781 |
+ |
fn every_vtrgb_line_is_sixteen_comma_separated_bytes() {
|
|
782 |
+ |
for palette in [dawn(), night()] {
|
|
783 |
+ |
let table = palette.vtrgb_table().unwrap();
|
|
784 |
+ |
let lines: Vec<&str> = table.lines().collect();
|
|
785 |
+ |
assert_eq!(lines.len(), 3, "{table}");
|
|
786 |
+ |
for line in lines {
|
|
787 |
+ |
assert!(!line.contains(' '), "a space would fail the parse: {line}");
|
|
788 |
+ |
let fields: Vec<&str> = line.split(',').collect();
|
|
789 |
+ |
assert_eq!(fields.len(), 16, "{line}");
|
|
790 |
+ |
assert!(
|
|
791 |
+ |
fields.iter().all(|f| f.parse::<u8>().is_ok()),
|
|
792 |
+ |
"every field is a decimal byte: {line}"
|
|
793 |
+ |
);
|
|
794 |
+ |
}
|
|
795 |
+ |
}
|
|
796 |
+ |
}
|
|
797 |
+ |
|
| 762 |
798 |
|
fn night() -> Palette {
|
| 763 |
799 |
|
let dir = makeover::bundled_themes_dir().expect("makeover bundles its themes");
|
| 764 |
800 |
|
let theme =
|