summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Gesang <phg@phi-gamma.net>2021-11-15 20:43:27 +0100
committerPhilipp Gesang <phg@phi-gamma.net>2021-11-15 20:14:17 +0100
commit10f04a80a878fd164ec01d4832f11c3cd63712eb (patch)
tree79012206579cc059eec7a7f8eef989e453832602
parentcc0eb0bce32255e95c1b86d9900209eee25a5951 (diff)
downloadvtcol-10f04a80a878fd164ec01d4832f11c3cd63712eb.tar.gz
lib: split out library
Not really a satisfactorily clean split as of yet but it’s a start. Error types have been changed from anyhow to io::Error in ``lib.rs`` so as to not force an error handling crate on downstream users.
-rw-r--r--Cargo.toml4
-rw-r--r--src/lib.rs528
-rw-r--r--src/vtcol.rs581
3 files changed, 580 insertions, 533 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 82eae4e..63c199b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,6 +19,10 @@ path = "src/vtcol.rs"
test = false
doc = false
+[lib]
+# doctests suck
+doctest = false
+
[dependencies]
clap = "2.33"
libc = "0.2"
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..47f650d
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,528 @@
+use libc::ioctl;
+use std::{convert::TryFrom,
+ fmt,
+ io::{self, BufWriter, Error, Write},
+ path::{Path, PathBuf}};
+
+pub type Fd = libc::c_int;
+
+const PALETTE_SIZE: usize = 16;
+const PALETTE_BYTES: usize = PALETTE_SIZE * 3; /* 16 * sizeof(int) */
+
+/* XXX: can we get these into ``libc``? */
+pub const KDGKBTYPE: libc::c_ulong = 0x4b33; /* kd.h */
+const GIO_CMAP: libc::c_ulong = 0x00004B70; /* kd.h */
+const PIO_CMAP: libc::c_ulong = 0x00004B71; /* kd.h */
+pub const KB_101: libc::c_char = 0x0002; /* kd.h */
+
+const RAW_COLEXPR_SIZE: usize = 6; /* e. g. 0xBADF00 */
+pub type RawPalette<'a> = [&'a str; PALETTE_SIZE];
+
+#[derive(Debug)]
+pub enum Color
+{
+ Black(bool),
+ Red(bool),
+ Green(bool),
+ Yellow(bool),
+ Blue(bool),
+ Magenta(bool),
+ Cyan(bool),
+ White(bool),
+}
+
+impl TryFrom<u8> for Color
+{
+ type Error = io::Error;
+
+ fn try_from(val: u8) -> io::Result<Self>
+ {
+ match val {
+ 0x00 => Ok(Color::Black(false)),
+ 0x01 => Ok(Color::Red(false)),
+ 0x02 => Ok(Color::Green(false)),
+ 0x03 => Ok(Color::Yellow(false)),
+ 0x04 => Ok(Color::Blue(false)),
+ 0x05 => Ok(Color::Magenta(false)),
+ 0x06 => Ok(Color::Cyan(false)),
+ 0x07 => Ok(Color::White(false)),
+
+ 0x08 => Ok(Color::Black(true)),
+ 0x09 => Ok(Color::Red(true)),
+ 0x0a => Ok(Color::Green(true)),
+ 0x0b => Ok(Color::Yellow(true)),
+ 0x0c => Ok(Color::Blue(true)),
+ 0x0d => Ok(Color::Magenta(true)),
+ 0x0e => Ok(Color::Cyan(true)),
+ 0x0f => Ok(Color::White(true)),
+
+ _ =>
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("invalid color value: {}", val),
+ )),
+ }
+ }
+} /* [impl TryFrom<u8> for Color] */
+
+impl Color
+{
+ fn format_brightness(b: bool, s: &str) -> String
+ {
+ if b {
+ "bright ".to_string() + s
+ } else {
+ s.to_string()
+ }
+ }
+} /* [impl Color] */
+
+impl fmt::Display for Color
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
+ {
+ let c = match *self {
+ Color::Black(b) => Color::format_brightness(b, "black"),
+ Color::Red(b) => Color::format_brightness(b, "red"),
+ Color::Green(b) => Color::format_brightness(b, "green"),
+ Color::Yellow(b) => Color::format_brightness(b, "yellow"),
+ Color::Blue(b) => Color::format_brightness(b, "blue"),
+ Color::Magenta(b) => Color::format_brightness(b, "magenta"),
+ Color::Cyan(b) => Color::format_brightness(b, "cyan"),
+ Color::White(b) => Color::format_brightness(b, "white"),
+ };
+
+ write!(f, "{}", c)
+ }
+} /* [impl fmt::Display for Color] */
+
+#[derive(Debug, Clone)]
+pub struct Builtin
+{
+ names: &'static [&'static str],
+ palette: &'static RawPalette<'static>,
+}
+
+/** Vanilla Linux colors. */
+const DEFAULT_COLORS: RawPalette = [
+ "000000", "aa0000", "00aa00", "aa5500", "0000aa", "aa00aa", "00aaaa",
+ "aaaaaa", "555555", "ff5555", "55ff55", "ffff55", "5555ff", "ff55ff",
+ "55ffff", "ffffff",
+];
+
+/** The dark (default) version of the Solarized scheme. */
+const SOLARIZED_COLORS_DARK: RawPalette = [
+ "002b36", "dc322f", "859900", "b58900", "268bd2", "d33682", "2aa198",
+ "eee8d5", "002b36", "cb4b16", "586e75", "657b83", "839496", "6c71c4",
+ "93a1a1", "fdf6e3",
+];
+
+/** The light version of the Solarized theme. */
+const SOLARIZED_COLORS_LIGHT: RawPalette = [
+ "eee8d5", "dc322f", "859900", "b58900", "268bd2", "d33682", "2aa198",
+ "073642", "fdf6e3", "cb4b16", "93a1a1", "839496", "657b83", "6c71c4",
+ "586e75", "002b36",
+];
+
+/** Bright green monochrome terminal. */
+const MONOCHROME_PHOSPHOR: RawPalette = [
+ "000000", "68fc68", "68fc68", "68fc68", "68fc68", "68fc68", "68fc68",
+ "68fc68", "68fc68", "68fc68", "68fc68", "68fc68", "68fc68", "68fc68",
+ "68fc68", "68fc68",
+];
+
+const DUMMY_COLORS: RawPalette = [
+ "000000", "ffffff", "000000", "ffffff", "000000", "ffffff", "000000",
+ "ffffff", "000000", "ffffff", "000000", "ffffff", "000000", "ffffff",
+ "000000", "ffffff",
+];
+
+pub const BUILTIN_SCHEMES: &[Builtin] = &[
+ Builtin::solarized(),
+ Builtin::solarized_light(),
+ Builtin::default(),
+ Builtin::phosphor(),
+];
+
+impl Builtin
+{
+ pub fn name(&self) -> &'static str { self.names.iter().next().unwrap() }
+
+ pub fn palette(&self) -> &RawPalette { self.palette }
+
+ const fn solarized() -> Self
+ {
+ Self {
+ names: &["solarized", "solarized_dark", "sd"],
+ palette: &SOLARIZED_COLORS_DARK,
+ }
+ }
+
+ const fn solarized_light() -> Self
+ {
+ Self {
+ names: &["solarized_light", "sl"],
+ palette: &SOLARIZED_COLORS_LIGHT,
+ }
+ }
+
+ const fn default() -> Self
+ {
+ Self {
+ names: &["default", "normal", "linux"],
+ palette: &DEFAULT_COLORS,
+ }
+ }
+
+ const fn phosphor() -> Self
+ {
+ Self { names: &["phosphor", "matrix"], palette: &MONOCHROME_PHOSPHOR }
+ }
+}
+
+impl TryFrom<&str> for Builtin
+{
+ type Error = io::Error;
+
+ fn try_from(name: &str) -> Result<Self, Self::Error>
+ {
+ for b in BUILTIN_SCHEMES {
+ if b.names.contains(&name) {
+ return Ok(b.clone());
+ }
+ }
+
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("no such builtin scheme: {}", name),
+ ))
+ }
+}
+
+impl<'a> fmt::Display for Builtin
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
+ {
+ write!(f, "{}", self.name())
+ }
+}
+
+#[derive(Debug)]
+pub enum Scheme
+{
+ /** One of the predefined schemes. */
+ Builtin(Builtin),
+ /** Custom ``Palette``. */
+ Palette(Palette),
+ /** Load from file. */
+ Custom(Option<PathBuf>),
+}
+
+impl<'a> fmt::Display for Scheme
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
+ {
+ match self {
+ Self::Builtin(b) => write!(f, "{}", b),
+ Self::Custom(None) => write!(f, "<read stdin>"),
+ Self::Custom(Some(fname)) => write!(f, "{}", fname.display()),
+ Self::Palette(pal) => write!(f, "palette: {}", pal),
+ }
+ }
+} /* [impl fmt::String for Scheme] */
+
+impl Scheme
+{
+ pub fn from_stdin() -> Self { Self::Custom(None) }
+
+ pub fn from_path<P: AsRef<Path>>(path: P) -> Self
+ {
+ Self::Custom(Some(path.as_ref().into()))
+ }
+} /* [impl Scheme] */
+
+/** Try to select one of the predefined schemes; if that fails,
+interpret the argument as a path. */
+impl From<&str> for Scheme
+{
+ fn from(name: &str) -> Scheme
+ {
+ Builtin::try_from(name)
+ .map(Self::Builtin)
+ .unwrap_or_else(|_| Self::from_path(name))
+ }
+}
+
+/** Try to match the palette against one of the predefined schemes. */
+impl From<Palette> for Scheme
+{
+ fn from(pal: Palette) -> Scheme
+ {
+ if pal == Palette::from(&DEFAULT_COLORS) {
+ return Self::Builtin(Builtin::default());
+ }
+ if pal == Palette::from(&SOLARIZED_COLORS_DARK) {
+ return Self::Builtin(Builtin::solarized());
+ }
+ if pal == Palette::from(&SOLARIZED_COLORS_LIGHT) {
+ return Self::Builtin(Builtin::solarized_light());
+ }
+ if pal == Palette::from(&MONOCHROME_PHOSPHOR) {
+ return Self::Builtin(Builtin::phosphor());
+ }
+
+ Self::Palette(pal)
+ }
+}
+
+fn nibble_of_char(chr: u8) -> u8
+{
+ match chr {
+ b'0'..=b'9' => chr - b'0',
+ b'a'..=b'f' => chr - b'a' + 10,
+ b'A'..=b'F' => chr - b'A' + 10,
+ _ => 0,
+ }
+}
+
+macro_rules! byte_of_hex {
+ ($ar:ident, $off:expr) => {
+ (nibble_of_char($ar[$off])) << 4 | nibble_of_char($ar[$off + 1]) as u8
+ };
+}
+
+fn rgb_of_hex_triplet(def: &str) -> (u8, u8, u8)
+{
+ let bytes = def.as_bytes();
+ let r: u8 = byte_of_hex!(bytes, 0);
+ let g: u8 = byte_of_hex!(bytes, 2);
+ let b: u8 = byte_of_hex!(bytes, 4);
+ (r, g, b)
+}
+
+#[derive(Eq, PartialEq, Clone)]
+pub struct Palette([u8; PALETTE_BYTES]);
+
+impl Palette
+{
+ /** Construct an all-zero ``Palette``. */
+ pub fn new() -> Self { Self([0u8; PALETTE_BYTES]) }
+
+ pub fn dummy() -> Self { Self::from(&DUMMY_COLORS) }
+
+ pub fn from_buffered_reader(reader: &mut dyn std::io::BufRead) -> Self
+ {
+ let mut pal_idx: usize = 0;
+ let mut pal: [u8; PALETTE_BYTES] = [0; PALETTE_BYTES];
+ let mut line: String = String::new();
+
+ while reader.read_line(&mut line).is_ok() {
+ let len = line.len();
+ if len == 0 {
+ break;
+ } else if len >= 8 {
+ if let Some(off) = line.find('#') {
+ if off != 0 {
+ /* Palette index specified, number prepended */
+ let parse_res: Result<usize, _> =
+ std::str::FromStr::from_str(&line[0..off]);
+ if let Ok(new_idx) = parse_res {
+ if new_idx < PALETTE_SIZE {
+ pal_idx = new_idx * 3;
+ }
+ }
+ }
+ let off = off + 1;
+ if off > len - 6 {
+ /* no room left for color definition after '#' char */
+ panic!("invalid color definition: {}", line);
+ }
+ let col = &line[off..(off + RAW_COLEXPR_SIZE)];
+
+ let (r, g, b) = rgb_of_hex_triplet(col);
+ pal[pal_idx] = r;
+ pal[pal_idx + 1] = g;
+ pal[pal_idx + 2] = b;
+ pal_idx = (pal_idx + 3) % PALETTE_BYTES;
+ }
+ }
+ line.truncate(0);
+ }
+
+ Self(pal)
+ }
+
+ #[allow(rustdoc::invalid_rust_codeblocks)]
+ /** Print palette in a text format that can be re-read back in.
+ Basically we print one hex rgb code per line prefixed with color
+ indices and the canonical names on the right:
+
+ 00 #002B36 black
+ 01 #DC322F red
+ 02 #859900 green
+ 03 #B58900 yellow
+ 04 #268BD2 blue
+ …
+ */
+ pub fn dump(&self, out: &mut BufWriter<io::Stdout>) -> io::Result<()>
+ {
+ let mut buf: [u8; 3] = [0u8, 0u8, 0u8];
+ for (i, col) in self.0.iter().enumerate() {
+ let idx: usize = i % 3;
+ buf[idx] = *col;
+ if idx == 2 {
+ let col = Color::try_from((i / 3) as u8)?;
+ out.write_all(
+ format!(
+ "{:02} #{:02.X}{:02.X}{:02.X} {}\n",
+ i / 3,
+ buf[0],
+ buf[1],
+ buf[2],
+ col,
+ )
+ .as_bytes(),
+ )?;
+ }
+ }
+
+ Ok(())
+ }
+
+ pub fn from_file(fname: &Path) -> Self
+ {
+ /* Check if file exists
+ */
+ let file = match std::fs::File::open(&fname) {
+ Err(e) => {
+ panic!("failed to open {} as file ({})", fname.display(), e)
+ },
+ Ok(f) => f,
+ };
+ let mut reader = std::io::BufReader::new(file);
+
+ /* Parse scheme file
+ */
+ Self::from_buffered_reader(&mut reader)
+ }
+
+ /* [Palette::from_file] */
+
+ pub fn from_stdin() -> Self
+ {
+ let mut reader = std::io::BufReader::new(std::io::stdin());
+
+ /* Parse scheme file
+ */
+ Self::from_buffered_reader(&mut reader)
+ } /* [Palette::from_stdin] */
+} /* [impl Palette] */
+
+impl fmt::Display for Palette
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
+ {
+ let mut i = 0;
+ while i < PALETTE_BYTES {
+ let _ = write!(f, "{}", if i == 0 { "(" } else { "\n " });
+ let r = self.0[i];
+ let g = self.0[i + 1];
+ let b = self.0[i + 2];
+ let _ = write!(
+ f,
+ "((r 0x{:02.X}) (g 0x{:02.X}) (b 0x{:02.x}))",
+ r, g, b
+ );
+ i += 3;
+ }
+ writeln!(f, ")")
+ }
+} /* [impl fmt::Display for Palette] */
+
+impl fmt::Debug for Palette
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
+ {
+ let mut i: u8 = 0_u8;
+ while (i as usize) < PALETTE_BYTES {
+ let r = self.0[i as usize];
+ let g = self.0[i as usize + 1];
+ let b = self.0[i as usize + 2];
+ let col = Color::try_from((i / 3) as u8)
+ .map(|c| format!("{}", c))
+ .unwrap_or_else(|_| "??".into());
+ let _ = writeln!(f, "{} => 0x{:02.X}{:02.X}{:02.X}", col, r, g, b);
+ i += 3_u8;
+ }
+ std::result::Result::Ok(())
+ }
+} /* [impl fmt::Debug for Palette] */
+
+/** Obtain a ``Palette`` from a ``Scheme``. */
+impl From<&Scheme> for Palette
+{
+ fn from(scm: &Scheme) -> Self
+ {
+ match scm {
+ Scheme::Builtin(Builtin { palette, .. }) => Self::from(*palette),
+ Scheme::Custom(None) => Self::from_stdin(),
+ Scheme::Custom(Some(ref fname)) => Self::from_file(fname),
+ Scheme::Palette(pal) => pal.clone(),
+ }
+ }
+}
+
+/** Obtain a ``Palette`` from a ``RawPalette``. */
+impl From<&RawPalette<'_>> for Palette
+{
+ fn from(colors: &RawPalette<'_>) -> Self
+ {
+ let mut idx: usize = 0;
+ let mut pal: [u8; PALETTE_BYTES] = [0; PALETTE_BYTES];
+
+ for def in colors.iter() {
+ let (r, g, b) = rgb_of_hex_triplet(*def);
+ pal[idx] = r;
+ pal[idx + 1] = g;
+ pal[idx + 2] = b;
+ //println!(">> {} -> {:X} {:X} {:X}", def, r, g, b);
+ idx += 3;
+ }
+
+ Self(pal)
+ }
+}
+
+pub fn ioctl_pio_cmap(fd: Fd, pal: &Palette) -> io::Result<()>
+{
+ if unsafe {
+ ioctl(
+ fd,
+ PIO_CMAP,
+ std::mem::transmute::<&Palette, *const libc::c_void>(&pal),
+ )
+ } < 0
+ {
+ Err(Error::last_os_error())
+ } else {
+ Ok(())
+ }
+}
+
+pub fn ioctl_gio_cmap(fd: Fd) -> io::Result<Palette>
+{
+ let mut pal = Palette::new();
+
+ if unsafe {
+ ioctl(
+ fd,
+ GIO_CMAP,
+ std::mem::transmute::<&mut Palette, *mut libc::c_void>(&mut pal),
+ )
+ } < 0
+ {
+ Err(Error::last_os_error())
+ } else {
+ Ok(pal)
+ }
+}
diff --git a/src/vtcol.rs b/src/vtcol.rs
index 5d073e7..707f96d 100644
--- a/src/vtcol.rs
+++ b/src/vtcol.rs
@@ -1,13 +1,11 @@
-#![allow(rustdoc::invalid_rust_codeblocks)]
+pub mod lib;
+
+use vtcol::{Palette, Scheme};
+
use anyhow::{anyhow, Result};
-use std::{convert::TryFrom,
- fmt,
- io::{self, BufWriter, Error, Write},
- path::{Path, PathBuf},
+use std::{io::{self, BufWriter, Error},
sync::atomic::{AtomicBool, Ordering}};
-type Fd = libc::c_int;
-
static VERBOSITY: AtomicBool = AtomicBool::new(false);
macro_rules! vrb {
@@ -16,234 +14,6 @@ macro_rules! vrb {
)}
}
-const PALETTE_SIZE: usize = 16;
-const PALETTE_BYTES: usize = PALETTE_SIZE * 3; /* 16 * sizeof(int) */
-
-const RAW_COLEXPR_SIZE: usize = 6; /* e. g. 0xBADF00 */
-
-type RawPalette<'a> = [&'a str; PALETTE_SIZE];
-
-/* XXX: can we get these into ``libc``? */
-const KDGKBTYPE: libc::c_int = 0x4b33; /* kd.h */
-const GIO_CMAP: libc::c_int = 0x00004B70; /* kd.h */
-const PIO_CMAP: libc::c_int = 0x00004B71; /* kd.h */
-const KB_101: libc::c_char = 0x0002; /* kd.h */
-const O_NOCTTY: libc::c_int = 0o0400; /* fcntl.h */
-
-#[derive(Debug)]
-enum Color
-{
- Black(bool),
- Red(bool),
- Green(bool),
- Yellow(bool),
- Blue(bool),
- Magenta(bool),
- Cyan(bool),
- White(bool),
-}
-
-impl TryFrom<u8> for Color
-{
- type Error = anyhow::Error;
-
- fn try_from(val: u8) -> Result<Self, Self::Error>
- {
- match val {
- 0x00 => Ok(Color::Black(false)),
- 0x01 => Ok(Color::Red(false)),
- 0x02 => Ok(Color::Green(false)),
- 0x03 => Ok(Color::Yellow(false)),
- 0x04 => Ok(Color::Blue(false)),
- 0x05 => Ok(Color::Magenta(false)),
- 0x06 => Ok(Color::Cyan(false)),
- 0x07 => Ok(Color::White(false)),
-
- 0x08 => Ok(Color::Black(true)),
- 0x09 => Ok(Color::Red(true)),
- 0x0a => Ok(Color::Green(true)),
- 0x0b => Ok(Color::Yellow(true)),
- 0x0c => Ok(Color::Blue(true)),
- 0x0d => Ok(Color::Magenta(true)),
- 0x0e => Ok(Color::Cyan(true)),
- 0x0f => Ok(Color::White(true)),
-
- _ => Err(anyhow!("invalid color value: {}", val)),
- }
- }
-} /* [impl TryFrom<u8> for Color] */
-
-impl Color
-{
- fn format_brightness(b: bool, s: &str) -> String
- {
- if b {
- "bright ".to_string() + s
- } else {
- s.to_string()
- }
- }
-} /* [impl Color] */
-
-impl fmt::Display for Color
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
- {
- let c = match *self {
- Color::Black(b) => Color::format_brightness(b, "black"),
- Color::Red(b) => Color::format_brightness(b, "red"),
- Color::Green(b) => Color::format_brightness(b, "green"),
- Color::Yellow(b) => Color::format_brightness(b, "yellow"),
- Color::Blue(b) => Color::format_brightness(b, "blue"),
- Color::Magenta(b) => Color::format_brightness(b, "magenta"),
- Color::Cyan(b) => Color::format_brightness(b, "cyan"),
- Color::White(b) => Color::format_brightness(b, "white"),
- };
-
- write!(f, "{}", c)
- }
-} /* [impl fmt::Display for Color] */
-
-#[derive(Debug, Clone)]
-struct Builtin
-{
- names: &'static [&'static str],
- palette: &'static RawPalette<'static>,
-}
-
-const BUILTIN_SCHEMES: &[Builtin] = &[
- Builtin::solarized(),
- Builtin::solarized_light(),
- Builtin::default(),
- Builtin::phosphor(),
-];
-
-impl Builtin
-{
- fn name(&self) -> &'static str { self.names.iter().next().unwrap() }
-
- const fn solarized() -> Self
- {
- Self {
- names: &["solarized", "solarized_dark", "sd"],
- palette: &SOLARIZED_COLORS_DARK,
- }
- }
-
- const fn solarized_light() -> Self
- {
- Self {
- names: &["solarized_light", "sl"],
- palette: &SOLARIZED_COLORS_LIGHT,
- }
- }
-
- const fn default() -> Self
- {
- Self {
- names: &["default", "normal", "linux"],
- palette: &DEFAULT_COLORS,
- }
- }
-
- const fn phosphor() -> Self
- {
- Self { names: &["phosphor", "matrix"], palette: &MONOCHROME_PHOSPHOR }
- }
-}
-
-impl TryFrom<&str> for Builtin
-{
- type Error = anyhow::Error;
-
- fn try_from(name: &str) -> Result<Self, Self::Error>
- {
- for b in BUILTIN_SCHEMES {
- if b.names.contains(&name) {
- return Ok(b.clone());
- }
- }
-
- Err(anyhow!("no such builtin: {}", name))
- }
-}
-
-impl<'a> fmt::Display for Builtin
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
- {
- write!(f, "{}", self.name())
- }
-}
-
-#[derive(Debug)]
-enum Scheme
-{
- /** One of the predefined schemes. */
- Builtin(Builtin),
- /** Custom ``Palette``. */
- Palette(Palette),
- /** Load from file. */
- Custom(Option<PathBuf>),
-}
-
-impl<'a> fmt::Display for Scheme
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
- {
- match self {
- Self::Builtin(b) => write!(f, "{}", b),
- Self::Custom(None) => write!(f, "<read stdin>"),
- Self::Custom(Some(fname)) => write!(f, "{}", fname.display()),
- Self::Palette(pal) => write!(f, "palette: {}", pal),
- }
- }
-} /* [impl fmt::String for Scheme] */
-
-impl Scheme
-{
- fn from_stdin() -> Self { Self::Custom(None) }
-
- fn from_path<P: AsRef<Path>>(path: P) -> Self
- {
- Self::Custom(Some(path.as_ref().into()))
- }
-} /* [impl Scheme] */
-
-/** Try to select one of the predefined schemes; if that fails,
-interpret the argument as a path. */
-impl From<&str> for Scheme
-{
- fn from(name: &str) -> Scheme
- {
- Builtin::try_from(name)
- .map(Self::Builtin)
- .unwrap_or_else(|_| Self::from_path(name))
- }
-}
-
-/** Try to match the palette against one of the predefined schemes. */
-impl From<Palette> for Scheme
-{
- fn from(pal: Palette) -> Scheme
- {
- if pal == Palette::from(&DEFAULT_COLORS) {
- return Self::Builtin(Builtin::default());
- }
- if pal == Palette::from(&SOLARIZED_COLORS_DARK) {
- return Self::Builtin(Builtin::solarized());
- }
- if pal == Palette::from(&SOLARIZED_COLORS_LIGHT) {
- return Self::Builtin(Builtin::solarized_light());
- }
- if pal == Palette::from(&MONOCHROME_PHOSPHOR) {
- return Self::Builtin(Builtin::phosphor());
- }
-
- Self::Palette(pal)
- }
-}
-
/* struct Job -- Runtime parameters.
*/
#[derive(Debug)]
@@ -346,14 +116,14 @@ impl<'a> Job
("list", _) => Ok(Self::List),
("set", Some(subm)) => {
let scheme = match subm.value_of("scheme") {
- Some("-") => Scheme::from_stdin(),
+ Some("-") => Self::read_scheme_from_stdin(),
Some(name) => {
vrb!("pick predefined scheme [{}]", name);
Scheme::from(name)
},
None =>
match subm.value_of("file") {
- None | Some("-") => Scheme::from_stdin(),
+ None | Some("-") => Self::read_scheme_from_stdin(),
Some(fname) => {
vrb!(
"read custom scheme from file [{}]",
@@ -389,24 +159,48 @@ impl<'a> Job
fn list_schemes()
{
- println!("{} color schemes available:", BUILTIN_SCHEMES.len());
- for s in BUILTIN_SCHEMES {
+ println!("{} color schemes available:", vtcol::BUILTIN_SCHEMES.len());
+ for s in vtcol::BUILTIN_SCHEMES {
println!(" * {}", s.name());
}
}
+ fn read_scheme_from_stdin() -> Scheme
+ {
+ vrb!("Go ahead, type your color scheme …");
+ vrb!("vtcol>");
+ Scheme::from_stdin()
+ }
+
fn dump(scm: Scheme) -> Result<()>
{
vrb!("Dumping color scheme {}", scm);
let mut out = BufWriter::new(io::stdout());
match scm {
- Scheme::Builtin(Builtin { palette, .. }) =>
- Palette::from(palette).dump(&mut out),
- Scheme::Custom(None) => Palette::from_stdin().dump(&mut out),
+ Scheme::Builtin(bltn) =>
+ Palette::from(bltn.palette()).dump(&mut out).map_err(|e| {
+ anyhow!(
+ "error loading builtin scheme {}: {}",
+ bltn.name(),
+ e
+ )
+ }),
+ Scheme::Custom(None) =>
+ Palette::from_stdin().dump(&mut out).map_err(|e| {
+ anyhow!("error loading palette from stdin: {}", e)
+ }),
Scheme::Custom(Some(fname)) =>
- Palette::from_file(&fname).dump(&mut out),
- Scheme::Palette(pal) => pal.dump(&mut out),
+ Palette::from_file(&fname).dump(&mut out).map_err(|e| {
+ anyhow!(
+ "error loading palette from file [{}]: {}",
+ fname.display(),
+ e
+ )
+ }),
+ Scheme::Palette(pal) =>
+ pal.dump(&mut out)
+ .map_err(|e| anyhow!("error dumping palette: {}", e)),
}
}
@@ -431,7 +225,7 @@ impl<'a> Job
let fd = get_console_fd()?;
vrb!("console fd: {}", fd);
- ioctl_pio_cmap(fd, &pal)?;
+ vtcol::ioctl_pio_cmap(fd, &pal)?;
clear_term(fd)?;
vrb!("successfully enabled scheme {:?}", scheme);
@@ -444,7 +238,7 @@ impl<'a> Job
let fd = get_console_fd()?;
vrb!("console fd: {}", fd);
- let pal = ioctl_gio_cmap(fd)?;
+ let pal = vtcol::ioctl_gio_cmap(fd)?;
let scm = Scheme::from(pal);
vrb!("active scheme:");
@@ -461,7 +255,7 @@ impl<'a> Job
let fd = get_console_fd()?;
vrb!("console fd: {}", fd);
- let pal = ioctl_gio_cmap(fd)?;
+ let pal = vtcol::ioctl_gio_cmap(fd)?;
if pal == Palette::from(&one) {
Self::set_scheme(two)
@@ -493,263 +287,10 @@ const CONSOLE_PATHS: [&str; 6] = [
"/dev/console",
];
-/** Vanilla Linux colors. */
-const DEFAULT_COLORS: RawPalette = [
- "000000", "aa0000", "00aa00", "aa5500", "0000aa", "aa00aa", "00aaaa",
- "aaaaaa", "555555", "ff5555", "55ff55", "ffff55", "5555ff", "ff55ff",
- "55ffff", "ffffff",
-];
-
-/** The dark (default) version of the Solarized scheme. */
-const SOLARIZED_COLORS_DARK: RawPalette = [
- "002b36", "dc322f", "859900", "b58900", "268bd2", "d33682", "2aa198",
- "eee8d5", "002b36", "cb4b16", "586e75", "657b83", "839496", "6c71c4",
- "93a1a1", "fdf6e3",
-];
-
-/** The light version of the Solarized theme. */
-const SOLARIZED_COLORS_LIGHT: RawPalette = [
- "eee8d5", "dc322f", "859900", "b58900", "268bd2", "d33682", "2aa198",
- "073642", "fdf6e3", "cb4b16", "93a1a1", "839496", "657b83", "6c71c4",
- "586e75", "002b36",
-];
-
-/** Bright green monochrome terminal. */
-const MONOCHROME_PHOSPHOR: RawPalette = [
- "000000", "68fc68", "68fc68", "68fc68", "68fc68", "68fc68", "68fc68",
- "68fc68", "68fc68", "68fc68", "68fc68", "68fc68", "68fc68", "68fc68",
- "68fc68", "68fc68",
-];
-
-const DUMMY_COLORS: RawPalette = [
- "000000", "ffffff", "000000", "ffffff", "000000", "ffffff", "000000",
- "ffffff", "000000", "ffffff", "000000", "ffffff", "000000", "ffffff",
- "000000", "ffffff",
-];
-
-fn nibble_of_char(chr: u8) -> u8
-{
- match chr {
- b'0'..=b'9' => chr - b'0',
- b'a'..=b'f' => chr - b'a' + 10,
- b'A'..=b'F' => chr - b'A' + 10,
- _ => 0,
- }
-}
-
-macro_rules! byte_of_hex {
- ($ar:ident, $off:expr) => {
- (nibble_of_char($ar[$off])) << 4 | nibble_of_char($ar[$off + 1]) as u8
- };
-}
-
-fn rgb_of_hex_triplet(def: &str) -> (u8, u8, u8)
-{
- let bytes = def.as_bytes();
- let r: u8 = byte_of_hex!(bytes, 0);
- let g: u8 = byte_of_hex!(bytes, 2);
- let b: u8 = byte_of_hex!(bytes, 4);
- (r, g, b)
-}
-
-#[derive(Eq, PartialEq, Clone)]
-pub struct Palette([u8; PALETTE_BYTES]);
-
-impl Palette
-{
- /** Construct an all-zero ``Palette``. */
- pub fn new() -> Self { Self([0u8; PALETTE_BYTES]) }
-
- pub fn dummy() -> Self { Self::from(&DUMMY_COLORS) }
-
- pub fn from_buffered_reader(reader: &mut dyn std::io::BufRead) -> Self
- {
- let mut pal_idx: usize = 0;
- let mut pal: [u8; PALETTE_BYTES] = [0; PALETTE_BYTES];
- let mut line: String = String::new();
-
- while reader.read_line(&mut line).is_ok() {
- let len = line.len();
- if len == 0 {
- break;
- } else if len >= 8 {
- if let Some(off) = line.find('#') {
- if off != 0 {
- /* Palette index specified, number prepended */
- let parse_res: Result<usize, _> =
- std::str::FromStr::from_str(&line[0..off]);
- if let Ok(new_idx) = parse_res {
- if new_idx < PALETTE_SIZE {
- pal_idx = new_idx * 3;
- }
- }
- }
- let off = off + 1;
- if off > len - 6 {
- /* no room left for color definition after '#' char */
- panic!("invalid color definition: {}", line);
- }
- let col = &line[off..(off + RAW_COLEXPR_SIZE)];
-
- let (r, g, b) = rgb_of_hex_triplet(col);
- pal[pal_idx] = r;
- pal[pal_idx + 1] = g;
- pal[pal_idx + 2] = b;
- pal_idx = (pal_idx + 3) % PALETTE_BYTES;
- }
- }
- line.truncate(0);
- }
-
- Self(pal)
- }
-
- /** Print palette in a text format that can be re-read back in.
- Basically we print one hex rgb code per line prefixed with color
- indices and the canonical names on the right:
-
- 00 #002B36 black
- 01 #DC322F red
- 02 #859900 green
- 03 #B58900 yellow
- 04 #268BD2 blue
- …
- */
- fn dump(&self, out: &mut BufWriter<io::Stdout>) -> Result<()>
- {
- let mut buf: [u8; 3] = [0u8, 0u8, 0u8];
- for (i, col) in self.0.iter().enumerate() {
- let idx: usize = i % 3;
- buf[idx] = *col;
- if idx == 2 {
- let col = Color::try_from((i / 3) as u8)?;
- out.write_all(
- format!(
- "{:02} #{:02.X}{:02.X}{:02.X} {}\n",
- i / 3,
- buf[0],
- buf[1],
- buf[2],
- col,
- )
- .as_bytes(),
- )?;
- }
- }
-
- Ok(())
- }
-
- pub fn from_file(fname: &Path) -> Self
- {
- /* Check if file exists
- */
- let file = match std::fs::File::open(&fname) {
- Err(e) => {
- panic!("failed to open {} as file ({})", fname.display(), e)
- },
- Ok(f) => f,
- };
- let mut reader = std::io::BufReader::new(file);
-
- /* Parse scheme file
- */
- Self::from_buffered_reader(&mut reader)
- }
-
- /* [Palette::from_file] */
-
- pub fn from_stdin() -> Self
- {
- vrb!("Go ahead, type your color scheme …");
- vrb!("vtcol>");
- let mut reader = std::io::BufReader::new(std::io::stdin());
-
- /* Parse scheme file
- */
- Self::from_buffered_reader(&mut reader)
- } /* [Palette::from_stdin] */
-} /* [impl Palette] */
-
-impl fmt::Display for Palette
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
- {
- let mut i = 0;
- while i < PALETTE_BYTES {
- let _ = write!(f, "{}", if i == 0 { "(" } else { "\n " });
- let r = self.0[i];
- let g = self.0[i + 1];
- let b = self.0[i + 2];
- let _ = write!(
- f,
- "((r 0x{:02.X}) (g 0x{:02.X}) (b 0x{:02.x}))",
- r, g, b
- );
- i += 3;
- }
- writeln!(f, ")")
- }
-} /* [impl fmt::Display for Palette] */
-
-impl fmt::Debug for Palette
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
- {
- let mut i: u8 = 0_u8;
- while (i as usize) < PALETTE_BYTES {
- let r = self.0[i as usize];
- let g = self.0[i as usize + 1];
- let b = self.0[i as usize + 2];
- let col = Color::try_from((i / 3) as u8)
- .map(|c| format!("{}", c))
- .unwrap_or_else(|_| "??".into());
- let _ = writeln!(f, "{} => 0x{:02.X}{:02.X}{:02.X}", col, r, g, b);
- i += 3_u8;
- }
- std::result::Result::Ok(())
- }
-} /* [impl fmt::Debug for Palette] */
-
-/** Obtain a ``Palette`` from a ``Scheme``. */
-impl From<&Scheme> for Palette
-{
- fn from(scm: &Scheme) -> Self
- {
- match scm {
- Scheme::Builtin(Builtin { palette, .. }) => Self::from(*palette),
- Scheme::Custom(None) => Self::from_stdin(),
- Scheme::Custom(Some(ref fname)) => Self::from_file(fname),
- Scheme::Palette(pal) => pal.clone(),
- }
- }
-}
-
-/** Obtain a ``Palette`` from a ``RawPalette``. */
-impl From<&RawPalette<'_>> for Palette
-{
- fn from(colors: &RawPalette<'_>) -> Self
- {
- let mut idx: usize = 0;
- let mut pal: [u8; PALETTE_BYTES] = [0; PALETTE_BYTES];
-
- for def in colors.iter() {
- let (r, g, b) = rgb_of_hex_triplet(*def);
- pal[idx] = r;
- pal[idx + 1] = g;
- pal[idx + 2] = b;
- //println!(">> {} -> {:X} {:X} {:X}", def, r, g, b);
- idx += 3;
- }
-
- Self(pal)
- }
-}
-
-fn fd_of_path(path: &std::path::Path) -> Option<Fd>
+fn fd_of_path(path: &std::path::Path) -> Option<vtcol::Fd>
{
let p = std::ffi::CString::new(path.to_str().unwrap()).unwrap();
- match unsafe { libc::open(p.as_ptr(), libc::O_RDWR | O_NOCTTY, 0) } {
+ match unsafe { libc::open(p.as_ptr(), libc::O_RDWR | libc::O_NOCTTY, 0) } {
-1 => None,
fd => {
vrb!(" *> got fd");
@@ -763,7 +304,7 @@ fn fd_of_path(path: &std::path::Path) -> Option<Fd>
let res = unsafe {
ioctl(
fd,
- KDGKBTYPE as libc::c_int,
+ vtcol::KDGKBTYPE as libc::c_int,
std::mem::transmute(&mut tty_type),
)
};
@@ -772,7 +313,7 @@ fn fd_of_path(path: &std::path::Path) -> Option<Fd>
return None;
}
- if tty_type != KB_101 {
+ if tty_type != vtcol::KB_101 {
return None;
}
@@ -781,7 +322,7 @@ fn fd_of_path(path: &std::path::Path) -> Option<Fd>
}
}
-fn get_console_fd() -> Result<Fd>
+fn get_console_fd() -> Result<vtcol::Fd>
{
for path in CONSOLE_PATHS.iter() {
vrb!("trying path: {:?}", path);
@@ -794,7 +335,7 @@ fn get_console_fd() -> Result<Fd>
Err(anyhow!("could not retrieve fd for any of the search paths"))
}
-fn write_to_term(fd: Fd, buf: &str) -> Result<()>
+fn write_to_term(fd: vtcol::Fd, buf: &str) -> Result<()>
{
let len = buf.len() as libc::size_t;
@@ -812,7 +353,7 @@ fn write_to_term(fd: Fd, buf: &str) -> Result<()>
}
}
-fn clear_term(fd: Fd) -> Result<()>
+fn clear_term(fd: vtcol::Fd) -> Result<()>
{
let clear = "\x1b[2J";
let cursor = "\x1b[1;1H";
@@ -822,32 +363,6 @@ fn clear_term(fd: Fd) -> Result<()>
Ok(())
}
-fn ioctl_pio_cmap(fd: Fd, pal: &Palette) -> Result<()>
-{
- if unsafe { ioctl(fd, PIO_CMAP, std::mem::transmute(pal)) } < 0 {
- Err(anyhow!(
- "ioctl(PIO_CMAP) failed to insert new palette: {}",
- Error::last_os_error()
- ))
- } else {
- Ok(())
- }
-}
-
-fn ioctl_gio_cmap(fd: Fd) -> Result<Palette>
-{
- let mut pal = Palette::new();
-
- if unsafe { ioctl(fd, GIO_CMAP, std::mem::transmute(&mut pal)) } < 0 {
- Err(anyhow!(
- "ioctl(GIO_CMAP) failed to get current palette: {}",
- Error::last_os_error()
- ))
- } else {
- Ok(pal)
- }
-}
-
fn main() -> Result<()>
{
let job = Job::from_argv()?;