summaryrefslogtreecommitdiff
path: root/src/vtcol.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/vtcol.rs')
-rw-r--r--src/vtcol.rs1204
1 files changed, 939 insertions, 265 deletions
diff --git a/src/vtcol.rs b/src/vtcol.rs
index 9a78d2f..baf5ae3 100644
--- a/src/vtcol.rs
+++ b/src/vtcol.rs
@@ -1,6 +1,10 @@
+#![allow(clippy::option_map_unit_fn)]
+
pub mod lib;
-use vtcol::{Console, Fade, Palette, Scheme};
+#[cfg(feature = "gui")] pub mod edit;
+
+use vtcol::{Console, Fade, KbLedFlags, KbLedState, Palette, Scheme};
use anyhow::{anyhow, Result};
use std::{io::{self, BufWriter},
@@ -18,15 +22,240 @@ macro_rules! vrb {
)}
}
-/* struct Job -- Runtime parameters.
- */
+/** Helper for choosing the console. Defaults to the current one if
+none is explicitly supplied. */
+#[inline]
+fn open_console(path: Option<&str>) -> io::Result<Console>
+{
+ path.map(Console::from_path).unwrap_or_else(Console::current)
+}
+
+/** Trait for subcommands to implement. */
+trait Run
+{
+ fn run(self, console: Option<String>) -> Result<()>;
+}
+
#[derive(Debug)]
-enum Job
+enum LedJob
+{
+ /** Get keyboard LED state. */
+ Get(bool),
+ /** Revert to normal. */
+ Revert,
+ /** Set keyboard LED state of all leds at once. */
+ Set(KbLedState),
+ /** Set keyboard LED state of individual LEDs. */
+ SetIndividual(Option<bool>, Option<bool>, Option<bool>),
+}
+
+impl Run for LedJob
+{
+ fn run(self, console: Option<String>) -> Result<()>
+ {
+ match self {
+ Self::Get(raw) => Self::get(console, raw),
+ Self::Revert => Self::revert(console),
+ Self::Set(st) => Self::set(console, st),
+ Self::SetIndividual(c, n, s) =>
+ Self::set_individual(console, c, n, s),
+ }
+ }
+} /* [impl Run for LedJob] */
+
+impl LedJob
+{
+ /** Get the keyboard LED state. */
+ fn get(con: Option<String>, raw: bool) -> Result<()>
+ {
+ let fd = open_console(con.as_deref())?;
+ vrb!("console fd: {}", fd);
+
+ let leds = KbLedState::get(&fd)?;
+
+ if raw {
+ println!("{}", u8::from(leds));
+ } else {
+ println!("{}", leds);
+ }
+
+ Ok(())
+ }
+
+ /** Set the keyboard LED state. */
+ fn set(con: Option<String>, st: KbLedState) -> Result<()>
+ {
+ let fd = open_console(con.as_deref())?;
+ vrb!("console fd: {}", fd);
+
+ st.set(&fd)?;
+ vrb!("applied");
+
+ Ok(())
+ }
+
+ /** Set the state of the given LEDs using the current state as base. */
+ fn set_individual(
+ con: Option<String>,
+ cap: Option<bool>,
+ num: Option<bool>,
+ scr: Option<bool>,
+ ) -> Result<()>
+ {
+ let fd = open_console(con.as_deref())?;
+ vrb!("console fd: {}", fd);
+
+ let mut st = KbLedState::get(&fd)?;
+
+ cap.map(|b| st.set_cap(b));
+ num.map(|b| st.set_num(b));
+ scr.map(|b| st.set_scr(b));
+
+ st.set(&fd)?;
+ vrb!("applied");
+
+ Ok(())
+ }
+
+ /** Revert the keyboard LED state. */
+ fn revert(con: Option<String>) -> Result<()>
+ {
+ let fd = open_console(con.as_deref())?;
+ vrb!("console fd: {}", fd);
+
+ KbLedState::revert(&fd)?;
+ vrb!("reverted");
+
+ Ok(())
+ }
+} /* [impl LedJob] */
+
+#[derive(Debug)]
+enum FlagTarget
+{
+ Current,
+ Default,
+ Both,
+}
+
+#[derive(Debug)]
+enum FlagJob
+{
+ /** Get keyboard flags. */
+ Get(bool),
+ /** Set all keyboard flags at once. */
+ Set(KbLedFlags),
+ /** Set keyboard LED state of individual LEDs. */
+ SetIndividual(FlagTarget, Option<bool>, Option<bool>, Option<bool>),
+}
+
+impl FlagJob
+{
+ /** Get the keyboard flags (modifier locks). */
+ fn get(con: Option<String>, raw: bool) -> Result<()>
+ {
+ let fd = open_console(con.as_deref())?;
+ vrb!("console fd: {}", fd);
+
+ let leds = KbLedFlags::get(&fd)?;
+
+ if raw {
+ println!("{}", u8::from(leds));
+ } else {
+ println!("{}", leds);
+ }
+
+ Ok(())
+ }
+
+ /** Set the keyboard flags. */
+ fn set(con: Option<String>, st: KbLedFlags) -> Result<()>
+ {
+ let fd = open_console(con.as_deref())?;
+ vrb!("console fd: {}", fd);
+
+ st.set(&fd)?;
+ vrb!("applied");
+
+ Ok(())
+ }
+
+ /** Set / unset keyboard flags using the current as base. */
+ fn set_individual(
+ con: Option<String>,
+ target: FlagTarget,
+ cap: Option<bool>,
+ num: Option<bool>,
+ scr: Option<bool>,
+ ) -> Result<()>
+ {
+ let fd = open_console(con.as_deref())?;
+ vrb!("console fd: {}", fd);
+
+ let mut st = KbLedFlags::get(&fd)?;
+
+ if matches!(target, FlagTarget::Default | FlagTarget::Both) {
+ cap.map(|b| st.set_default_cap(b));
+ num.map(|b| st.set_default_num(b));
+ scr.map(|b| st.set_default_scr(b));
+ }
+
+ if matches!(target, FlagTarget::Current | FlagTarget::Both) {
+ cap.map(|b| st.set_cap(b));
+ num.map(|b| st.set_num(b));
+ scr.map(|b| st.set_scr(b));
+ }
+
+ st.set(&fd)?;
+ vrb!("applied");
+
+ Ok(())
+ }
+} /*[impl FlagJob] */
+
+impl Run for FlagJob
+{
+ fn run(self, console: Option<String>) -> Result<()>
+ {
+ match self {
+ Self::Get(raw) => Self::get(console, raw),
+ Self::Set(flags) => Self::set(console, flags),
+ Self::SetIndividual(dflt, c, n, s) =>
+ Self::set_individual(console, dflt, c, n, s),
+ }
+ }
+} /* [impl Run for FlagJob] */
+
+#[derive(Debug)]
+enum KbJob
+{
+ /** LED state ops. */
+ Leds(LedJob),
+ /** Flags. */
+ Flags(FlagJob),
+}
+
+impl Run for KbJob
+{
+ fn run(self, console: Option<String>) -> Result<()>
+ {
+ match self {
+ Self::Leds(leds) => leds.run(console),
+ Self::Flags(flags) => flags.run(console),
+ }
+ }
+} /* [impl Run for KbJob] */
+
+#[derive(Debug)]
+enum ColorJob
{
/** List available schemes. */
List,
/** Dump a scheme. */
Dump(Scheme),
+ /** Launch scheme editor. */
+ #[cfg(feature = "gui")]
+ Edit(Option<String>, Scheme),
/** Switch to color scheme. */
Set(Scheme),
/** Get currently active scheme. */
@@ -37,253 +266,34 @@ enum Job
Fade(Option<Scheme>, Scheme, Duration, u8, bool),
}
-impl<'a> Job
+impl Run for ColorJob
{
- pub fn from_argv() -> Result<Job>
+ fn run(self, console: Option<String>) -> Result<()>
{
- use clap::{App, Arg, SubCommand};
-
- let app = App::new(clap::crate_name!())
- .version(clap::crate_version!())
- .author(clap::crate_authors!())
- .about(clap::crate_description!())
- .subcommand(
- SubCommand::with_name("dump")
- .about("dump a color scheme")
- .arg(
- Arg::with_name("scheme")
- .help("name of the scheme")
- .required(false)
- .value_name("NAME")
- .takes_value(true),
- )
- .arg(
- Arg::with_name("base64")
- .short("b")
- .long("base64")
- .value_name("DATA")
- .help("base64 encoded binary input")
- .required(false)
- .takes_value(true),
- ),
- )
- .subcommand(
- SubCommand::with_name("list").about("list available schemes"),
- )
- .subcommand(
- SubCommand::with_name("set")
- .about("apply color scheme to current terminal")
- .arg(
- Arg::with_name("scheme")
- .value_name("NAME")
- .help("predefined color scheme")
- .takes_value(true)
- .conflicts_with("file"),
- )
- .arg(
- Arg::with_name("file")
- .short("f")
- .long("file")
- .value_name("PATH")
- .help("apply scheme from file")
- .takes_value(true),
- )
- .arg(
- Arg::with_name("base64")
- .short("b")
- .long("base64")
- .help("base64 encoded binary input")
- .value_name("DATA")
- .required(false)
- .takes_value(true),
- ),
- )
- .subcommand(
- SubCommand::with_name("get")
- .about("get current color scheme")
- .arg(
- Arg::with_name("base64")
- .short("b")
- .long("base64")
- .help("base64 encoded binary output")
- .required(false)
- .takes_value(false),
- ),
- )
- .subcommand(
- SubCommand::with_name("toggle")
- .about("toggle between two schemes")
- .arg(
- Arg::with_name("one")
- .value_name("NAME1")
- .help("predefined color scheme")
- .takes_value(true),
- )
- .arg(
- Arg::with_name("two")
- .value_name("NAME2")
- .help("predefined color scheme")
- .takes_value(true),
- ),
- )
- .subcommand(
- SubCommand::with_name("fade")
- .about("fade from one scheme to another")
- .arg(
- Arg::with_name("from")
- .short("f")
- .long("from")
- .value_name("NAME1")
- .help("initial color scheme (default: current)")
- .takes_value(true),
- )
- .arg(
- Arg::with_name("to")
- .short("t")
- .long("to")
- .value_name("NAME2")
- .help("final color scheme")
- .takes_value(true)
- .required(true),
- )
- .arg(
- Arg::with_name("ms")
- .value_name("MS")
- .short("m")
- .long("ms")
- .help("how long (in ms) the fade should take")
- .takes_value(true),
- )
- .arg(
- Arg::with_name("clear")
- .short("c")
- .long("clear")
- .help("clear terminal on each fade step")
- .takes_value(false),
- )
- .arg(
- Arg::with_name("frequency")
- .value_name("HZ")
- .short("h")
- .long("frequency")
- .help("rate (HZ/s) of intermediate scheme changes")
- .takes_value(true),
- ),
- )
- .arg(
- Arg::with_name("verbose")
- .short("v")
- .long("verbose")
- .help("enable extra diagnostics")
- .takes_value(false),
- );
-
- let matches = app.get_matches();
-
- if matches.is_present("verbose") {
- VERBOSITY.store(true, Ordering::SeqCst);
- }
-
- match matches.subcommand() {
- ("dump", Some(subm)) => {
- if let Some(b64) = subm.value_of("base64") {
- let scheme = Scheme::from_base64(b64)?;
- return Ok(Self::Dump(scheme));
- }
- if let Some(name) = subm.value_of("scheme") {
- let scm = Scheme::from(name);
- return Ok(Self::Dump(scm));
- }
- Err(anyhow!("dump requires an argument"))
- },
- ("list", _) => Ok(Self::List),
- ("set", Some(subm)) => {
- if let Some(b64) = subm.value_of("base64") {
- let scheme = Scheme::from_base64(&b64)?;
- return Ok(Self::Set(scheme));
- }
- let scheme = match subm.value_of("scheme") {
- Some("-") => Self::read_scheme_from_stdin(),
- Some(name) => {
- vrb!("pick predefined scheme [{}]", name);
- Scheme::from(name)
- },
- None =>
- match subm.value_of("file") {
- None | Some("-") => Self::read_scheme_from_stdin(),
- Some(fname) => {
- vrb!(
- "read custom scheme from file [{}]",
- fname
- );
- Scheme::from_path(fname)
- },
- },
- };
- Ok(Self::Set(scheme))
- },
- ("get", Some(subm)) => Ok(Self::Get(subm.is_present("base64"))),
- ("toggle", Some(subm)) => {
- match (subm.value_of("one"), subm.value_of("two")) {
- (Some(one), Some(two)) => {
- vrb!("toggle schemes [{}] and [{}]", one, two);
- Ok(Self::Toggle(Scheme::from(one), Scheme::from(two)))
- },
- _ =>
- Err(anyhow!(
- "please supply two schemes to toggle between"
- )),
- }
- },
- ("fade", Some(subm)) => {
- let dur: u64 = if let Some(ms) = subm.value_of("ms") {
- ms.parse()?
- } else {
- DEFAULT_FADE_DURATION_MS
- };
- let hz: u8 = if let Some(ms) = subm.value_of("frequency") {
- ms.parse()?
- } else {
- DEFAULT_FADE_UPDATE_HZ
- };
- let clear = subm.is_present("clear");
- let dur = Duration::from_millis(dur);
-
- match (subm.value_of("from"), subm.value_of("to")) {
- (_, None) =>
- Err(anyhow!("please supply color scheme to fade to")),
- (from, Some(to)) =>
- Ok(Self::Fade(
- from.map(Scheme::from),
- Scheme::from(to),
- dur,
- hz,
- clear,
- )),
- }
- },
- (junk, _) =>
- Err(anyhow!(
- "invalid subcommand [{}]; try ``{} --help``",
- junk,
- clap::crate_name!()
- )),
+ match self {
+ Self::Dump(scm) => Self::dump(scm),
+ #[cfg(feature = "gui")]
+ Self::Edit(name, scm) => Self::edit(name, scm),
+ Self::List => Self::list(),
+ Self::Set(scm) => Self::set(console, scm),
+ Self::Get(b64) => Self::get(console, b64),
+ Self::Toggle(one, two) => Self::toggle(console, one, two),
+ Self::Fade(from, to, ms, hz, clear) =>
+ Self::fade(console, from, to, ms, hz, clear),
}
}
+} /* [impl Run for ColorJob] */
- fn list_schemes()
+impl ColorJob
+{
+ fn list() -> Result<()>
{
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()
+ Ok(())
}
fn dump(scm: Scheme) -> Result<()>
@@ -318,37 +328,31 @@ impl<'a> Job
}
}
- fn run(self) -> Result<()>
+ #[cfg(feature = "gui")]
+ fn edit(name: Option<String>, scm: Scheme) -> Result<()>
{
- match self {
- Self::Dump(scm) => Self::dump(scm)?,
- Self::List => Self::list_schemes(),
- Self::Set(scm) => Self::set_scheme(scm)?,
- Self::Get(b64) => Self::get_scheme(b64)?,
- Self::Toggle(one, two) => Self::toggle_scheme(one, two)?,
- Self::Fade(from, to, ms, hz, clear) =>
- Self::fade(from, to, ms, hz, clear)?,
- }
+ vrb!("Launching color scheme editor for scheme {}", scm);
+ let editor = crate::edit::Edit::new(name, scm);
- Ok(())
+ editor.run()
}
- fn set_scheme(scheme: Scheme) -> Result<()>
+ fn set(con: Option<String>, scheme: Scheme) -> Result<()>
{
- let con = Console::current()?;
- vrb!("console fd: {}", con);
+ let fd = open_console(con.as_deref())?;
+ vrb!("console fd: {}", fd);
- con.apply_scheme(&scheme)?;
- con.clear()?;
+ fd.apply_scheme(&scheme)?;
+ fd.clear()?;
vrb!("successfully enabled scheme {:?}", scheme);
/* It’s fine to leak the fd, the kernel will clean up anyways. */
Ok(())
}
- fn get_scheme(b64: bool) -> Result<()>
+ fn get(con: Option<String>, b64: bool) -> Result<()>
{
- let fd = Console::current()?;
+ let fd = open_console(con.as_deref())?;
vrb!("console fd: {}", fd);
let scm = fd.current_scheme()?;
@@ -365,15 +369,18 @@ impl<'a> Job
/** Toggle between two schemes. Defaults to ``one`` in case neither scheme
is active.
*/
- fn toggle_scheme(one: Scheme, two: Scheme) -> Result<()>
+ fn toggle(con: Option<String>, one: Scheme, two: Scheme) -> Result<()>
{
- let fd = Console::current()?;
- vrb!("console fd: {}", fd);
+ let pal = {
+ let fd = open_console(con.as_deref())?;
+ vrb!("console fd: {}", fd);
+ fd.current_palette()?
+ };
- if fd.current_palette()? == Palette::try_from(&one)? {
- Self::set_scheme(two)
+ if pal == Palette::try_from(&one)? {
+ Self::set(con, two)
} else {
- Self::set_scheme(one)
+ Self::set(con, one)
}
}
@@ -381,6 +388,7 @@ impl<'a> Job
If ``from`` is ``None``, the current palette is used as starting point. */
fn fade(
+ con: Option<String>,
from: Option<Scheme>,
to: Scheme,
dur: Duration,
@@ -388,7 +396,7 @@ impl<'a> Job
clear: bool,
) -> Result<()>
{
- let fd = Console::current()?;
+ let fd = open_console(con.as_deref())?;
vrb!("console fd: {}", fd);
let from = if let Some(from) = from {
@@ -403,6 +411,672 @@ impl<'a> Job
fade.commence(&fd)?;
Ok(())
}
+} /* [impl ColorJob] */
+
+/** Subcommand and runtime parameters. */
+#[derive(Debug)]
+enum Subcmd
+{
+ /** Console palette ops. */
+ Colors(ColorJob),
+ /** Keyboard LED ops. */
+ Kb(KbJob),
+}
+
+#[derive(Debug)]
+struct Job(Option<String>, Subcmd);
+
+impl<'a> Job
+{
+ pub fn from_argv() -> Result<Job>
+ {
+ use clap::{App, Arg, ArgSettings, SubCommand};
+
+ let app = App::new(clap::crate_name!())
+ .version(clap::crate_version!())
+ .author(clap::crate_authors!())
+ .about(clap::crate_description!())
+ .arg(
+ Arg::with_name("verbose")
+ .set(ArgSettings::Global)
+ .short("v")
+ .long("verbose")
+ .help("enable extra diagnostics")
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("console")
+ .set(ArgSettings::Global)
+ .short("C")
+ .long("console")
+ .help(
+ "path to the console device to operate on [default: \
+ current console]",
+ )
+ .takes_value(true),
+ )
+ .subcommand(
+ SubCommand::with_name("colors")
+ .about("operations on the console palette")
+ .subcommand(
+ SubCommand::with_name("dump")
+ .about("dump a color scheme")
+ .arg(
+ Arg::with_name("scheme")
+ .help("name of the scheme")
+ .required(false)
+ .value_name("NAME")
+ .takes_value(true),
+ )
+ .arg(
+ Arg::with_name("base64")
+ .short("b")
+ .long("base64")
+ .value_name("DATA")
+ .help("base64 encoded binary input")
+ .required(false)
+ .takes_value(true),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("edit")
+ .about("launch a graphical color scheme editor")
+ .arg(
+ Arg::with_name("scheme")
+ .help("name of the scheme to edit")
+ .required(false)
+ .value_name("NAME")
+ .takes_value(true),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("list")
+ .about("list builtin schemes"),
+ )
+ .subcommand(
+ SubCommand::with_name("set")
+ .about("apply color scheme to current terminal")
+ .arg(
+ Arg::with_name("scheme")
+ .value_name("NAME")
+ .help("predefined color scheme")
+ .takes_value(true)
+ .conflicts_with("file"),
+ )
+ .arg(
+ Arg::with_name("file")
+ .short("f")
+ .long("file")
+ .value_name("PATH")
+ .help("apply scheme from file")
+ .takes_value(true),
+ )
+ .arg(
+ Arg::with_name("base64")
+ .short("b")
+ .long("base64")
+ .help("base64 encoded binary input")
+ .value_name("DATA")
+ .required(false)
+ .takes_value(true),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("get")
+ .about("get current color scheme")
+ .arg(
+ Arg::with_name("base64")
+ .short("b")
+ .long("base64")
+ .help("base64 encoded binary output")
+ .required(false)
+ .takes_value(false),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("toggle")
+ .about("toggle between two schemes")
+ .arg(
+ Arg::with_name("one")
+ .value_name("NAME1")
+ .help("predefined color scheme")
+ .takes_value(true),
+ )
+ .arg(
+ Arg::with_name("two")
+ .value_name("NAME2")
+ .help("predefined color scheme")
+ .takes_value(true),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("fade")
+ .about("fade from one scheme to another")
+ .arg(
+ Arg::with_name("from")
+ .short("f")
+ .long("from")
+ .value_name("NAME1")
+ .help(
+ "initial color scheme (default: \
+ current)",
+ )
+ .takes_value(true),
+ )
+ .arg(
+ Arg::with_name("to")
+ .short("t")
+ .long("to")
+ .value_name("NAME2")
+ .help("final color scheme")
+ .takes_value(true)
+ .required(true),
+ )
+ .arg(
+ Arg::with_name("ms")
+ .value_name("MS")
+ .short("m")
+ .long("ms")
+ .help(
+ "how long (in ms) the fade should take",
+ )
+ .takes_value(true),
+ )
+ .arg(
+ Arg::with_name("clear")
+ .short("c")
+ .long("clear")
+ .help("clear terminal on each fade step")
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("frequency")
+ .value_name("HZ")
+ .short("h")
+ .long("frequency")
+ .help(
+ "rate (HZ/s) of intermediate scheme \
+ changes",
+ )
+ .takes_value(true),
+ ),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("kb")
+ .about("keyboard controls")
+ .subcommand(
+ SubCommand::with_name("leds")
+ .about("operations regarding keyboard LEDs")
+ .subcommand(
+ SubCommand::with_name("get")
+ .about("get LED state")
+ .arg(
+ Arg::with_name("u8")
+ .short("8")
+ .long("u8")
+ .help("output raw state as integer")
+ .takes_value(false),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("set")
+ .about("set LED state")
+ .arg(
+ Arg::with_name("u8")
+ .short("8")
+ .long("u8")
+ .help(
+ "provide desired state as \
+ integer",
+ )
+ .takes_value(true)
+ .required_unless_one(&[
+ "revert", "caps", "num",
+ "scroll",
+ ])
+ .conflicts_with_all(&[
+ "revert", "caps", "num",
+ "scroll",
+ ])
+ .value_name("STATE"),
+ )
+ .arg(
+ Arg::with_name("caps")
+ .short("c")
+ .long("caps")
+ .help("[de]activate Caps Lock LED")
+ .takes_value(true)
+ .possible_values(&["on", "off"])
+ .conflicts_with_all(&[
+ "revert", "u8",
+ ])
+ .value_name("STATE"),
+ )
+ .arg(
+ Arg::with_name("num")
+ .short("n")
+ .long("num")
+ .help("[de]activate Num Lock LED")
+ .takes_value(true)
+ .possible_values(&["on", "off"])
+ .conflicts_with_all(&[
+ "revert", "u8",
+ ])
+ .value_name("STATE"),
+ )
+ .arg(
+ Arg::with_name("scroll")
+ .short("s")
+ .long("scroll")
+ .help(
+ "[de]activate Scroll Lock LED",
+ )
+ .takes_value(true)
+ .possible_values(&["on", "off"])
+ .conflicts_with_all(&[
+ "revert", "u8",
+ ])
+ .value_name("STATE"),
+ )
+ .arg(
+ Arg::with_name("revert")
+ .short("r")
+ .long("revert")
+ .help("revert LED state to normal")
+ .takes_value(false)
+ .conflicts_with_all(&[
+ "u8", "caps", "num", "scroll",
+ ]),
+ ),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("flags")
+ .about(
+ "operations regarding keyboard flags \
+ (modifier locks)",
+ )
+ .subcommand(
+ SubCommand::with_name("get")
+ .about("get flags")
+ .arg(
+ Arg::with_name("u8")
+ .short("8")
+ .long("u8")
+ .help("output raw flags as integer")
+ .takes_value(false),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("set")
+ .about("set flags")
+ .arg(
+ Arg::with_name("u8")
+ .short("8")
+ .long("u8")
+ .help(
+ "provide desired flags as
+ integer",
+ )
+ .takes_value(true)
+ .required_unless_one(&[
+ "caps", "num", "scroll",
+ ])
+ .conflicts_with_all(&[
+ "dflt", "caps", "num", "scroll",
+ ])
+ .value_name("STATE"),
+ )
+ .arg(
+ Arg::with_name("dflt")
+ .short("d")
+ .long("default")
+ .help(
+ "operate on defaults, not the \
+ current state",
+ )
+ .conflicts_with("both")
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("both")
+ .short("b")
+ .long("both")
+ .help(
+ "operate on both defaults and \
+ current state",
+ )
+ .takes_value(false),
+ )
+ .arg(
+ Arg::with_name("caps")
+ .short("c")
+ .long("caps")
+ .help("[de]activate Caps Lock flag")
+ .takes_value(true)
+ .possible_values(&["on", "off"])
+ .conflicts_with("u8")
+ .value_name("STATE"),
+ )
+ .arg(
+ Arg::with_name("num")
+ .short("n")
+ .long("num")
+ .help("[de]activate Num Lock flag")
+ .takes_value(true)
+ .possible_values(&["on", "off"])
+ .conflicts_with("u8")
+ .value_name("STATE"),
+ )
+ .arg(
+ Arg::with_name("scroll")
+ .short("s")
+ .long("scroll")
+ .help(
+ "[de]activate Scroll Lock flag",
+ )
+ .takes_value(true)
+ .possible_values(&["on", "off"])
+ .conflicts_with("u8")
+ .value_name("STATE"),
+ ),
+ ),
+ ),
+ );
+
+ let matches = app.get_matches();
+
+ if matches.is_present("verbose") {
+ VERBOSITY.store(true, Ordering::SeqCst);
+ }
+
+ let con = matches.value_of("console").map(String::from);
+
+ match matches.subcommand() {
+ ("colors", Some(subm)) =>
+ match subm.subcommand() {
+ ("dump", Some(subm)) => {
+ if let Some(b64) = subm.value_of("base64") {
+ let scheme = Scheme::from_base64(b64)?;
+ return Ok(Self(
+ con,
+ Subcmd::Colors(ColorJob::Dump(scheme)),
+ ));
+ }
+ if let Some(name) = subm.value_of("scheme") {
+ let scm = Scheme::from(name);
+ return Ok(Self(
+ con,
+ Subcmd::Colors(ColorJob::Dump(scm)),
+ ));
+ }
+ Err(anyhow!("dump requires an argument"))
+ },
+ ("edit", Some(subm)) => {
+ #[cfg(feature = "gui")]
+ if let Some(name) = subm.value_of("scheme") {
+ let scm = Scheme::from(name);
+ Ok(Self(
+ con,
+ Subcmd::Colors(ColorJob::Edit(
+ Some(name.to_string()),
+ scm,
+ )),
+ ))
+ } else {
+ Ok(Self(
+ con,
+ Subcmd::Colors(ColorJob::Edit(
+ None,
+ Scheme::from("default"),
+ )),
+ ))
+ }
+ #[cfg(not(feature = "gui"))]
+ {
+ let _ = subm; /* silence warn(unused_variables) */
+ Err(anyhow!(
+ "the ``edit'' subcommand requires vtcol to be \
+ built with the the ``gui'' feature"
+ ))
+ }
+ },
+ ("list", _) =>
+ Ok(Self(con, Subcmd::Colors(ColorJob::List))),
+ ("set", Some(subm)) => {
+ if let Some(b64) = subm.value_of("base64") {
+ let scheme = Scheme::from_base64(b64)?;
+ return Ok(Self(
+ con,
+ Subcmd::Colors(ColorJob::Set(scheme)),
+ ));
+ }
+ let scheme = match subm.value_of("scheme") {
+ Some("-") => Self::read_scheme_from_stdin(),
+ Some(name) => {
+ vrb!("pick predefined scheme [{}]", name);
+ Scheme::from(name)
+ },
+ None =>
+ match subm.value_of("file") {
+ None | Some("-") =>
+ Self::read_scheme_from_stdin(),
+ Some(fname) => {
+ vrb!(
+ "read custom scheme from file [{}]",
+ fname
+ );
+ Scheme::from_path(fname)
+ },
+ },
+ };
+ Ok(Self(con, Subcmd::Colors(ColorJob::Set(scheme))))
+ },
+ ("get", Some(subm)) =>
+ Ok(Self(
+ con,
+ Subcmd::Colors(ColorJob::Get(
+ subm.is_present("base64"),
+ )),
+ )),
+ ("toggle", Some(subm)) => {
+ match (subm.value_of("one"), subm.value_of("two")) {
+ (Some(one), Some(two)) => {
+ vrb!("toggle schemes [{}] and [{}]", one, two);
+ Ok(Self(
+ con,
+ Subcmd::Colors(ColorJob::Toggle(
+ Scheme::from(one),
+ Scheme::from(two),
+ )),
+ ))
+ },
+ _ =>
+ Err(anyhow!(
+ "please supply two schemes to toggle \
+ between"
+ )),
+ }
+ },
+ ("fade", Some(subm)) => {
+ let dur: u64 = if let Some(ms) = subm.value_of("ms") {
+ ms.parse()?
+ } else {
+ DEFAULT_FADE_DURATION_MS
+ };
+ let hz: u8 =
+ if let Some(ms) = subm.value_of("frequency") {
+ ms.parse()?
+ } else {
+ DEFAULT_FADE_UPDATE_HZ
+ };
+ let clear = subm.is_present("clear");
+ let dur = Duration::from_millis(dur);
+
+ match (subm.value_of("from"), subm.value_of("to")) {
+ (_, None) =>
+ Err(anyhow!(
+ "please supply color scheme to fade to"
+ )),
+ (from, Some(to)) =>
+ Ok(Self(
+ con,
+ Subcmd::Colors(ColorJob::Fade(
+ from.map(Scheme::from),
+ Scheme::from(to),
+ dur,
+ hz,
+ clear,
+ )),
+ )),
+ }
+ },
+ (junk, _) =>
+ Err(anyhow!(
+ "invalid sub-subcommand to colors: [{}]; try ``{} \
+ colors --help``",
+ junk,
+ clap::crate_name!()
+ )),
+ },
+
+ ("kb", Some(subm)) =>
+ match subm.subcommand() {
+ ("leds", Some(subm)) =>
+ match subm.subcommand() {
+ ("get", Some(subm)) => {
+ let raw = subm.is_present("u8");
+ Ok(Self(
+ con,
+ Subcmd::Kb(KbJob::Leds(LedJob::Get(raw))),
+ ))
+ },
+ ("set", Some(subm)) => {
+ if subm.is_present("revert") {
+ return Ok(Self(
+ con,
+ Subcmd::Kb(KbJob::Leds(LedJob::Revert)),
+ ));
+ }
+ if let Some(st) = subm.value_of("u8") {
+ let st: u8 = st.parse()?;
+ let st = KbLedState::try_from(st)?;
+ return Ok(Self(
+ con,
+ Subcmd::Kb(KbJob::Leds(LedJob::Set(
+ st,
+ ))),
+ ));
+ }
+ let cap =
+ subm.value_of("caps").map(|a| a == "on");
+ let num =
+ subm.value_of("num").map(|a| a == "on");
+ let scr =
+ subm.value_of("scroll").map(|a| a == "on");
+ Ok(Self(
+ con,
+ Subcmd::Kb(KbJob::Leds(
+ LedJob::SetIndividual(cap, num, scr),
+ )),
+ ))
+ },
+ (leds_junk, _) =>
+ Err(anyhow!(
+ "invalid sub-sub-subcommand to kb leds: \
+ [{}]; try ``{} kb leds --help``",
+ leds_junk,
+ clap::crate_name!()
+ )),
+ },
+ ("flags", Some(subm)) =>
+ match subm.subcommand() {
+ ("get", Some(subm)) => {
+ let raw = subm.is_present("u8");
+ Ok(Self(
+ con,
+ Subcmd::Kb(KbJob::Flags(FlagJob::Get(raw))),
+ ))
+ },
+ ("set", Some(subm)) => {
+ if let Some(st) = subm.value_of("u8") {
+ let st: u8 = st.parse()?;
+ let st = KbLedFlags::try_from(st)?;
+ return Ok(Self(
+ con,
+ Subcmd::Kb(KbJob::Flags(FlagJob::Set(
+ st,
+ ))),
+ ));
+ }
+
+ let target = if subm.is_present("both") {
+ FlagTarget::Both
+ } else if subm.is_present("dflt") {
+ FlagTarget::Default
+ } else {
+ FlagTarget::Current
+ };
+
+ let cap =
+ subm.value_of("caps").map(|a| a == "on");
+ let num =
+ subm.value_of("num").map(|a| a == "on");
+ let scr =
+ subm.value_of("scroll").map(|a| a == "on");
+ Ok(Self(
+ con,
+ Subcmd::Kb(KbJob::Flags(
+ FlagJob::SetIndividual(
+ target, cap, num, scr,
+ ),
+ )),
+ ))
+ },
+ (flags_junk, _) =>
+ Err(anyhow!(
+ "invalid sub-sub-subcommand to kb flags: \
+ [{}]; try ``{} kb flags --help``",
+ flags_junk,
+ clap::crate_name!()
+ )),
+ },
+
+ (kb_junk, _) =>
+ Err(anyhow!(
+ "invalid sub-subcommand to kb [{}]; try ``{} kb \
+ --help``",
+ kb_junk,
+ clap::crate_name!()
+ )),
+ },
+ (junk, _) =>
+ Err(anyhow!(
+ "invalid subcommand [{}]; try ``{} --help``",
+ junk,
+ clap::crate_name!()
+ )),
+ }
+ }
+
+ fn read_scheme_from_stdin() -> Scheme
+ {
+ vrb!("Go ahead, type your color scheme …");
+ vrb!("vtcol>");
+ Scheme::from_stdin()
+ }
+
+ fn run(self) -> Result<()>
+ {
+ let Job(con, cmd) = self;
+ match cmd {
+ Subcmd::Colors(cols) => cols.run(con)?,
+ Subcmd::Kb(kb) => kb.run(con)?,
+ }
+
+ Ok(())
+ }
} /* [impl Job] */
fn main() -> Result<()>