Skip to content

Commit

Permalink
make debugging output for Config contingent on verbose mode; provide …
Browse files Browse the repository at this point in the history
…reusable interface to terminal mode labels; fix terminal mode calculation on Windows
  • Loading branch information
apparebit committed Dec 23, 2024
1 parent 8c1703d commit 2bfeffa
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 126 deletions.
10 changes: 7 additions & 3 deletions crates/prettytty/examples/echo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::io::{Read, Write};

use prettytty::cmd::{Format, RequestColor, ResetStyle, SetForeground8, SetForegroundDefault};
use prettytty::err::report;
use prettytty::opt::Options;
use prettytty::util::WriteNicely;
use prettytty::Connection;

Expand All @@ -16,7 +17,9 @@ const GRAY: SetForeground8 = SetForeground8(244);
#[allow(unused_assignments)]
fn run() -> std::io::Result<()> {
// Access the terminal
let tty = Connection::open()?;
println!("\n");
let options = Options::verbose_default();
let tty = Connection::with_options(options)?;
let mut input = tty.input();
let mut output = tty.output();

Expand All @@ -26,7 +29,7 @@ fn run() -> std::io::Result<()> {
// Peek into terminal access
write!(
output,
"{}press ‹t› to query theme color, ‹q› to quit{}\r\n\r\n",
"\r\n\r\n{}press ‹t› to query theme color, ‹q› to quit{}\r\n\r\n",
Format::Bold,
Format::Bold.undo()
)?;
Expand Down Expand Up @@ -89,6 +92,7 @@ fn run() -> std::io::Result<()> {
// Handle user input.
if buffer.contains(&b'q') {
output.exec(ResetStyle)?;
output.println("\n\n")?;
break;
} else if buffer.contains(&b't') {
let mut entry = color_requests.next();
Expand All @@ -105,7 +109,7 @@ fn run() -> std::io::Result<()> {
drop(input);
drop(output);
drop(tty);
println!("\n\nbye bye!");
println!("bye bye!");

Ok(())
}
Expand Down
9 changes: 7 additions & 2 deletions crates/prettytty/src/conn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,16 @@ impl Connection {
.map_err(|e| Error::new(ErrorKind::ConnectionRefused, e))?;

let config = Config::read(connection.input())?;
println!("{:#?}", &config);
let verbose = options.verbose();
if verbose {
println!("terminal::config {:?}", &config);
}
let config = config.apply(&options).map_or_else(
|| Ok::<Option<Config>, Error>(None),
|reconfig| {
println!("{:#?}", &reconfig);
if verbose {
println!("terminal::reconfig {:?}", &reconfig);
}
reconfig.write(connection.output())?;
Ok(Some(config))
},
Expand Down
191 changes: 116 additions & 75 deletions crates/prettytty/src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,41 @@ impl RawConnection {

// ----------------------------------------------------------------------------------------------------------

#[derive(Clone, Copy, Debug)]
pub(crate) enum ModeGroup {
Input,
Output,
Control,
Local,
}

impl ModeGroup {
pub fn all() -> impl std::iter::Iterator<Item = ModeGroup> {
use self::ModeGroup::*;

std::iter::successors(
Some(Input),
|n| Some(match n {
Input => Output,
Output => Control,
Control => Local,
Local => return None,
})
)
}

pub fn name(&self) -> &'static str {
use self::ModeGroup::*;

match self {
Input => "input",
Output => "output",
Control => "control",
Local => "local"
}
}
}

/// A terminal configuration.
pub(crate) struct Config {
state: libc::termios,
Expand Down Expand Up @@ -145,94 +180,100 @@ impl Config {
.into_result()?;
Ok(())
}
}

impl std::fmt::Debug for Config {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Different Unix-like operating systems use differently sized modes,
// which is just fine with a macro.
/// Get labels for active modes in given group.
pub fn labels(&self, group: ModeGroup) -> Vec<&'static str> {
let mut labels = Vec::new();

macro_rules! maybe_add {
($labels:expr, $field:expr, $mask:expr, $label:expr) => {
($field:expr, $mask:expr, $label:expr) => {
if $field & $mask != 0 {
$labels.push($label);
labels.push($label);
}
};
}

// Input Modes
let mut input_labels = Vec::new();
for (label, mask) in [
("BRKINT", libc::BRKINT),
("ICRNL", libc::ICRNL),
("IGNBRK", libc::IGNBRK),
("IGNCR", libc::IGNCR),
("IGNPAR", libc::IGNPAR),
("INLCR", libc::INLCR),
("INPCK", libc::INPCK),
("ISTRIP", libc::ISTRIP),
("IXANY", libc::IXANY),
("IXOFF", libc::IXOFF),
("IXON", libc::IXON),
("PARMRK", libc::PARMRK),
] {
maybe_add!(input_labels, self.state.c_iflag, mask, label);
}
match group {
ModeGroup::Input => {
for (label, mask) in [
("BRKINT", libc::BRKINT),
("ICRNL", libc::ICRNL),
("IGNBRK", libc::IGNBRK),
("IGNCR", libc::IGNCR),
("IGNPAR", libc::IGNPAR),
("INLCR", libc::INLCR),
("INPCK", libc::INPCK),
("ISTRIP", libc::ISTRIP),
("IXANY", libc::IXANY),
("IXOFF", libc::IXOFF),
("IXON", libc::IXON),
("PARMRK", libc::PARMRK),
] {
maybe_add!(self.state.c_iflag, mask, label);
}
}
ModeGroup::Output => {
for (label, mask) in [
("OPOST", libc::OPOST),
("OCRNL", libc::OCRNL),
("ONOCR", libc::ONOCR),
("ONLRET", libc::ONLRET),
("OFILL", libc::OFILL),
("OFDEL", libc::OFDEL),
// Missing: NLDLY, CRDLY, TABDLY, BSDLY, VTDLY, FFDLY
] {
maybe_add!(self.state.c_oflag, mask, label);
}

// Output Modes
let mut output_labels = Vec::new();
for (label, mask) in [
("OPOST", libc::OPOST),
("OCRNL", libc::OCRNL),
("ONOCR", libc::ONOCR),
("ONLRET", libc::ONLRET),
("OFILL", libc::OFILL),
("OFDEL", libc::OFDEL),
// Missing: NLDLY, CRDLY, TABDLY, BSDLY, VTDLY, FFDLY
] {
maybe_add!(output_labels, self.state.c_oflag, mask, label);
}
ModeGroup::Control => {
maybe_add!(self.state.c_cflag, libc::CLOCAL, "CLOCAL");
maybe_add!(self.state.c_cflag, libc::CREAD, "CREAD");
match self.state.c_cflag & libc::CSIZE {
libc::CS5 => labels.push("CS5"),
libc::CS6 => labels.push("CS6"),
libc::CS7 => labels.push("CS7"),
libc::CS8 => labels.push("CS8"),
_ => (),
}
for (label, mask) in [
("CSTOPB", libc::CSTOPB),
("HUPCL", libc::HUPCL),
("PARENB", libc::PARENB),
("PARODD", libc::PARODD),
] {
maybe_add!(self.state.c_cflag, mask, label);
}
}
ModeGroup::Local => {
for (label, mask) in [
("ECHO", libc::ECHO),
("ECHOE", libc::ECHOE),
("ECHOK", libc::ECHOK),
("ECHONL", libc::ECHONL),
("ICANON", libc::ICANON),
("IEXTEN", libc::IEXTEN),
("ISIG", libc::ISIG),
("NOFLSH", libc::NOFLSH),
("TOSTOP", libc::TOSTOP),
] {
maybe_add!(self.state.c_lflag, mask, label);
}
}
}

// Control Modes
let mut control_labels = Vec::new();
maybe_add!(control_labels, self.state.c_cflag, libc::CLOCAL, "CLOCAL");
maybe_add!(control_labels, self.state.c_cflag, libc::CREAD, "CREAD");
match self.state.c_cflag & libc::CSIZE {
libc::CS5 => control_labels.push("CS5"),
libc::CS6 => control_labels.push("CS6"),
libc::CS7 => control_labels.push("CS7"),
libc::CS8 => control_labels.push("CS8"),
_ => (),
}
for (label, mask) in [
("CSTOPB", libc::CSTOPB),
("HUPCL", libc::HUPCL),
("PARENB", libc::PARENB),
("PARODD", libc::PARODD),
] {
maybe_add!(control_labels, self.state.c_cflag, mask, label);
}
labels
}
}

// Local Modes
let mut local_labels = Vec::new();
for (label, mask) in [
("ECHO", libc::ECHO),
("ECHOE", libc::ECHOE),
("ECHOK", libc::ECHOK),
("ECHONL", libc::ECHONL),
("ICANON", libc::ICANON),
("IEXTEN", libc::IEXTEN),
("ISIG", libc::ISIG),
("NOFLSH", libc::NOFLSH),
("TOSTOP", libc::TOSTOP),
] {
maybe_add!(local_labels, self.state.c_lflag, mask, label);
impl std::fmt::Debug for Config {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debugger = f.debug_struct("Config");
for group in ModeGroup::all() {
debugger.field(group.name(), &IdentList::new(self.labels(group)));
}

f.debug_struct("Config")
.field("input_mode", &IdentList::new(input_labels))
.field("output_mode", &IdentList::new(output_labels))
.field("control_mode", &IdentList::new(control_labels))
.field("local_mode", &IdentList::new(local_labels))
debugger
.field("vmin", &self.state.c_cc[libc::VMIN])
.field("vtime", &self.state.c_cc[libc::VTIME])
.finish()
Expand Down
Loading

0 comments on commit 2bfeffa

Please sign in to comment.