summaryrefslogtreecommitdiff
path: root/src/lib.rs
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/lib.rs
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/lib.rs')
-rw-r--r--src/lib.rs157
1 files changed, 152 insertions, 5 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)
+ }
+}