summaryrefslogtreecommitdiff
path: root/src/vtcol.rs
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 /src/vtcol.rs
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.
Diffstat (limited to 'src/vtcol.rs')
-rw-r--r--src/vtcol.rs581
1 files changed, 48 insertions, 533 deletions
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()?;