summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPhilipp Gesang <phg@phi-gamma.net>2021-11-15 20:50:15 +0100
committerPhilipp Gesang <phg@phi-gamma.net>2021-11-15 20:14:17 +0100
commit695355b4882707a9f7ed0917d5188471f67f47b1 (patch)
tree72633650e4187d6c023c9f01c0d8ef5692d997f9 /src
parent3fcadd24b57ef31164b2410be869e998119db56c (diff)
downloadvtcol-695355b4882707a9f7ed0917d5188471f67f47b1.tar.gz
move console handling into lib
All operations are now exposed through wrappers that are member functions of ``vtcol::Console``.
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs157
-rw-r--r--src/vtcol.rs109
2 files changed, 162 insertions, 104 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 4030884..99fc821 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,11 +5,26 @@ use std::{convert::TryFrom,
os::unix::io::{AsRawFd, RawFd},
path::{Path, PathBuf}};
+trait IsMinusOne
+{
+ fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+ ($($t:ident)*) => ($(impl IsMinusOne for $t {
+ fn is_minus_one(&self) -> bool {
+ *self == -1
+ }
+ })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
/** Convenience syscall wrapper based on its namesake found in the sadly
private ``std::sys::unix`` library. */
-fn cvt(t: libc::c_int) -> io::Result<libc::c_int>
+fn cvt<T: IsMinusOne>(t: T) -> io::Result<T>
{
- if t == -1 {
+ if t.is_minus_one() {
Err(Error::last_os_error())
} else {
Ok(t)
@@ -18,7 +33,7 @@ fn cvt(t: libc::c_int) -> io::Result<libc::c_int>
/** Convenience syscall wrapper based on its namesake found in the sadly
private ``std::sys::unix`` library. */
-fn cvt_r(f: &mut dyn FnMut() -> libc::c_int) -> io::Result<libc::c_int>
+fn cvt_r<T: IsMinusOne>(f: &mut dyn FnMut() -> T) -> io::Result<T>
{
loop {
match cvt((*f)()) {
@@ -53,10 +68,10 @@ 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 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 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];
@@ -570,3 +585,135 @@ pub fn ioctl_gio_cmap<F: AsRawFd>(fd: &F) -> io::Result<Palette>
.map(|_| ())?;
Ok(pal)
}
+
+const CONSOLE_PATHS: [&str; 6] = [
+ "/proc/self/fd/0",
+ "/dev/tty",
+ "/dev/tty0",
+ "/dev/vc/0",
+ "/dev/systty",
+ "/dev/console",
+];
+
+const CONTROL_CLEAR: &[u8] = b"\x1b[2J";
+const CONTROL_CURSOR: &[u8] = b"\x1b[1;1H";
+
+pub struct Console(libc::c_int);
+
+impl Console
+{
+ fn from_fd(fd: libc::c_int) -> io::Result<Self>
+ {
+ if unsafe { libc::isatty(fd) } == 0 {
+ return Err(Error::last_os_error());
+ }
+
+ let mut tty_type: libc::c_char = 0;
+
+ let _ = cvt_r(&mut || unsafe {
+ ioctl(fd, KDGKBTYPE, &mut tty_type as *mut _)
+ })?;
+
+ /* Sanity check. */
+ if tty_type != KB_101 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!(
+ "console {} exhibiting weird behavior; bailing out",
+ fd
+ ),
+ ));
+ }
+
+ Ok(Self(fd))
+ }
+
+ fn from_path<P: AsRef<Path>>(path: P) -> io::Result<Self>
+ {
+ let p =
+ std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
+ let fd = cvt_r(&mut || unsafe {
+ libc::open(p.as_ptr(), libc::O_RDWR | libc::O_NOCTTY, 0)
+ })?;
+ Self::from_fd(fd)
+ }
+
+ /** Try and obtain a handle referring to the console we’re running in. */
+ pub fn current() -> io::Result<Self>
+ {
+ for path in CONSOLE_PATHS.iter() {
+ let path = std::path::Path::new(path);
+ if let Ok(con) = Self::from_path(path) {
+ return Ok(con);
+ }
+ }
+
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("could not retrieve fd for any of the search paths"),
+ ))
+ }
+
+ fn write(&self, buf: &[u8]) -> io::Result<()>
+ {
+ let len = buf.len() as libc::size_t;
+
+ if cvt_r(&mut || unsafe {
+ libc::write(self.0, buf.as_ptr() as *const libc::c_void, len)
+ })? != len as isize
+ {
+ Err(Error::last_os_error())
+ } else {
+ Ok(())
+ }
+ }
+
+ pub fn clear(&self) -> io::Result<()>
+ {
+ self.write(CONTROL_CLEAR)?;
+ self.write(CONTROL_CURSOR)?;
+
+ Ok(())
+ }
+
+ /** Read the current palette. */
+ #[inline]
+ pub fn current_palette(&self) -> io::Result<Palette>
+ {
+ ioctl_gio_cmap(&self.as_raw_fd())
+ }
+
+ /** Tell the kernel to use the specified palette on the console. */
+ #[inline]
+ pub fn apply_palette(&self, pal: &Palette) -> io::Result<()>
+ {
+ ioctl_pio_cmap(&self.as_raw_fd(), pal)
+ }
+
+ /** Read the current palette and determine the scheme. */
+ #[inline]
+ pub fn current_scheme(&self) -> io::Result<Scheme>
+ {
+ Ok(Scheme::from(self.current_palette()?))
+ }
+
+ /** Convert ``scm`` into a ``Palette`` and tell the kernel to use it. */
+ #[inline]
+ pub fn apply_scheme(&self, scm: &Scheme) -> io::Result<()>
+ {
+ self.apply_palette(&Palette::from(scm))
+ }
+}
+
+impl AsRawFd for Console
+{
+ fn as_raw_fd(&self) -> RawFd { self.0 }
+}
+
+impl fmt::Display for Console
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
+ {
+ write!(f, "Console(fd={})", self.0)
+ }
+}
diff --git a/src/vtcol.rs b/src/vtcol.rs
index 0cc713c..d3399fc 100644
--- a/src/vtcol.rs
+++ b/src/vtcol.rs
@@ -1,10 +1,9 @@
pub mod lib;
-use vtcol::{Fd, Palette, Scheme};
+use vtcol::{Console, Palette, Scheme};
use anyhow::{anyhow, Result};
-use std::{io::{self, BufWriter, Error},
- os::unix::io::AsRawFd,
+use std::{io::{self, BufWriter},
sync::atomic::{AtomicBool, Ordering}};
static VERBOSITY: AtomicBool = AtomicBool::new(false);
@@ -220,15 +219,12 @@ impl<'a> Job
fn set_scheme(scheme: Scheme) -> Result<()>
{
- let pal = Palette::from(&scheme);
- vrb!("Using palette:");
- vrb!("{}", pal);
- let fd = get_console_fd()?;
- vrb!("console fd: {}", fd);
+ let con = Console::current()?;
+ vrb!("console fd: {}", con);
- vtcol::ioctl_pio_cmap(&fd, &pal)?;
+ con.apply_scheme(&scheme)?;
+ con.clear()?;
- clear_term(&fd)?;
vrb!("successfully enabled scheme {:?}", scheme);
/* It’s fine to leak the fd, the kernel will clean up anyways. */
Ok(())
@@ -236,11 +232,10 @@ impl<'a> Job
fn get_scheme() -> Result<()>
{
- let fd = get_console_fd()?;
+ let fd = Console::current()?;
vrb!("console fd: {}", fd);
- let pal = vtcol::ioctl_gio_cmap(&fd)?;
- let scm = Scheme::from(pal);
+ let scm = fd.current_scheme()?;
vrb!("active scheme:");
println!("{}", scm);
@@ -253,12 +248,10 @@ impl<'a> Job
*/
fn toggle_scheme(one: Scheme, two: Scheme) -> Result<()>
{
- let fd = get_console_fd()?;
+ let fd = Console::current()?;
vrb!("console fd: {}", fd);
- let pal = vtcol::ioctl_gio_cmap(&fd)?;
-
- if pal == Palette::from(&one) {
+ if fd.current_palette()? == Palette::from(&one) {
Self::set_scheme(two)
} else {
Self::set_scheme(one)
@@ -266,88 +259,6 @@ impl<'a> Job
}
} /* [impl Job] */
-const CONSOLE_PATHS: [&str; 6] = [
- "/proc/self/fd/0",
- "/dev/tty",
- "/dev/tty0",
- "/dev/vc/0",
- "/dev/systty",
- "/dev/console",
-];
-
-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 | libc::O_NOCTTY, 0) } {
- -1 => None,
- fd => {
- vrb!(" *> got fd");
- if unsafe { libc::isatty(fd) } == 0 {
- vrb!(" *> not a tty");
- return None;
- }
-
- let mut tty_type: libc::c_char = 0;
-
- let res = unsafe {
- libc::ioctl(fd, vtcol::KDGKBTYPE, &mut tty_type as *mut _)
- };
- if res < 0 {
- vrb!(" *> ioctl failed");
- return None;
- }
-
- if tty_type != vtcol::KB_101 {
- return None;
- }
-
- Some(Fd::from(fd))
- },
- }
-}
-
-fn get_console_fd() -> Result<Fd>
-{
- for path in CONSOLE_PATHS.iter() {
- vrb!("trying path: {:?}", path);
- let path = std::path::Path::new(path);
- if let Some(fd) = fd_of_path(path) {
- vrb!(" * Success!");
- return Ok(fd);
- }
- }
- Err(anyhow!("could not retrieve fd for any of the search paths"))
-}
-
-fn write_to_term(fd: &Fd, buf: &str) -> Result<()>
-{
- let len = buf.len() as libc::size_t;
-
- if unsafe {
- libc::write(fd.as_raw_fd(), buf.as_ptr() as *const libc::c_void, len)
- } != len as isize
- {
- Err(anyhow!(
- "failed to write {} B to fd {}: {}",
- len,
- fd,
- Error::last_os_error()
- ))
- } else {
- Ok(())
- }
-}
-
-fn clear_term(fd: &Fd) -> Result<()>
-{
- let clear = "\x1b[2J";
- let cursor = "\x1b[1;1H";
- write_to_term(fd, clear)?;
- write_to_term(fd, cursor)?;
-
- Ok(())
-}
-
fn main() -> Result<()>
{
let job = Job::from_argv()?;