summaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs592
1 files changed, 567 insertions, 25 deletions
diff --git a/src/lib.rs b/src/lib.rs
index c652565..8ecc4a6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::new_without_default)]
+
use std::{convert::TryFrom,
fmt,
io::{self, BufWriter, Error, Write},
@@ -46,28 +48,25 @@ fn cvt_r<T: IsMinusOne>(f: &mut dyn FnMut() -> T) -> io::Result<T>
/** Wrappers for ``ioctl_console(2)`` functionality. */
pub mod ioctl
{
- use super::{cvt_r, Palette};
+ use super::{cvt_r, KbLedFlags, KbLedState, KbLeds, Palette};
use libc::ioctl;
- use std::{io, os::unix::io::AsRawFd};
+ use std::{io::Result, os::unix::io::AsRawFd};
/* XXX: can we get these into ``libc``? */
- pub const KDGKBTYPE: libc::c_ulong = 0x4b33; /* kd.h */
- pub const GIO_CMAP: libc::c_ulong = 0x00004B70; /* kd.h */
- pub const PIO_CMAP: libc::c_ulong = 0x00004B71; /* kd.h */
- pub const KB_101: libc::c_char = 0x0002; /* kd.h */
-
- pub fn kdgkbtype<F: AsRawFd>(fd: &F) -> io::Result<libc::c_char>
- {
- let mut kb: libc::c_char = 0;
-
- let _ = cvt_r(&mut || unsafe {
- ioctl(fd.as_raw_fd(), KDGKBTYPE, &mut kb as *mut _)
- })?;
-
- Ok(kb)
- }
-
- pub fn pio_cmap<F: AsRawFd>(fd: &F, pal: &Palette) -> io::Result<()>
+ /* kd.h */
+ pub const KDGKBTYPE: libc::c_ulong = 0x4b33;
+ pub const GIO_CMAP: libc::c_ulong = 0x00004B70;
+ pub const PIO_CMAP: libc::c_ulong = 0x00004B71;
+ pub const KB_101: libc::c_char = 0x0002;
+ pub const KDGETLED: libc::c_ulong = 0x4b31;
+ pub const KDSETLED: libc::c_ulong = 0x4b32;
+
+ pub const KDGKBLED: libc::c_ulong = 0x4B64;
+ pub const KDSKBLED: libc::c_ulong = 0x4B65;
+ pub const KD_KBLED_STATE_MASK: libc::c_ulong = 0x07;
+ pub const KD_KBLED_DEFAULT_MASK: libc::c_ulong = 0x70;
+
+ pub fn pio_cmap<F: AsRawFd>(fd: &F, pal: &Palette) -> Result<()>
{
/* cvt_r because technically it can’t be ruled out that we hit EINTR. */
cvt_r(&mut || {
@@ -75,14 +74,14 @@ pub mod ioctl
ioctl(
fd.as_raw_fd(),
PIO_CMAP,
- std::mem::transmute::<&Palette, *const libc::c_void>(&pal),
+ std::mem::transmute::<&Palette, *const libc::c_void>(pal),
)
}
})
.map(|_| ())
}
- pub fn gio_cmap<F: AsRawFd>(fd: &F) -> io::Result<Palette>
+ pub fn gio_cmap<F: AsRawFd>(fd: &F) -> Result<Palette>
{
let mut pal = Palette::new();
@@ -101,6 +100,426 @@ pub mod ioctl
.map(|_| ())?;
Ok(pal)
}
+
+ pub fn kdgetled<F: AsRawFd>(fd: &F) -> Result<KbLedState>
+ {
+ let mut leds: libc::c_char = 0;
+
+ cvt_r(&mut || {
+ unsafe {
+ ioctl(
+ fd.as_raw_fd(),
+ KDGETLED,
+ std::mem::transmute::<&mut libc::c_char, *mut libc::c_void>(
+ &mut leds,
+ ),
+ )
+ }
+ })
+ .map(|_| ())?;
+
+ Ok(KbLedState(KbLeds::from(leds)))
+ }
+
+ /** If ``state`` is ``None`` it is taken to mean “revert to normal” as per
+ the man page:
+
+ KDSETLED
+ Set the LEDs. The LEDs are set to correspond to the lower three
+ bits of the unsigned long integer in argp. However, if a higher
+ order bit is set, the LEDs revert to normal: displaying the state
+ of the keyboard functions of caps lock, num lock, and scroll lock.
+ */
+ pub fn kdsetled<F: AsRawFd>(fd: &F, state: Option<KbLedState>)
+ -> Result<()>
+ {
+ let leds: libc::c_ulong = if let Some(state) = state {
+ state.into()
+ } else {
+ libc::c_ulong::MAX
+ };
+
+ cvt_r(&mut || {
+ unsafe {
+ ioctl(
+ fd.as_raw_fd(),
+ KDSETLED,
+ std::mem::transmute::<libc::c_ulong, *const libc::c_void>(
+ leds,
+ ),
+ )
+ }
+ })
+ .map(|_| ())?;
+
+ Ok(())
+ }
+
+ pub fn kdgkbled<F: AsRawFd>(fd: &F) -> Result<KbLedFlags>
+ {
+ let mut flags: libc::c_char = 0;
+
+ cvt_r(&mut || {
+ unsafe {
+ ioctl(
+ fd.as_raw_fd(),
+ KDGKBLED,
+ std::mem::transmute::<&mut libc::c_char, *mut libc::c_void>(
+ &mut flags,
+ ),
+ )
+ }
+ })
+ .map(|_| ())?;
+
+ KbLedFlags::try_from(flags as u8)
+ }
+
+ pub fn kdskbled<F: AsRawFd>(fd: &F, flags: KbLedFlags) -> Result<()>
+ {
+ let default = libc::c_ulong::from(flags.default);
+ let flags = libc::c_ulong::from(flags.flags) | (default << 4);
+
+ cvt_r(&mut || {
+ unsafe {
+ ioctl(
+ fd.as_raw_fd(),
+ KDSKBLED,
+ std::mem::transmute::<libc::c_ulong, *const libc::c_void>(
+ flags,
+ ),
+ )
+ }
+ })
+ .map(|_| ())
+ }
+
+ /* This should perhaps return unit as the kernel supposedly always returns
+ the constant ``KB_101``. */
+ pub fn kdgkbtype<F: AsRawFd>(fd: &F) -> Result<libc::c_char>
+ {
+ let mut kb: libc::c_char = 0;
+
+ let _ = cvt_r(&mut || unsafe {
+ ioctl(fd.as_raw_fd(), KDGKBTYPE, &mut kb as *mut _)
+ })?;
+
+ //assert_eq(kb, KB_101); /* XXX */
+ Ok(kb)
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+struct KbLeds(u8);
+
+impl KbLeds
+{
+ fn new(cap: bool, num: bool, scr: bool) -> Self
+ {
+ let mut state = 0u8;
+
+ state |= (cap as u8) << 2;
+ state |= (num as u8) << 1;
+ state |= scr as u8;
+
+ Self(state)
+ }
+
+ fn off() -> Self { Self(0) }
+
+ #[inline]
+ fn cap(&self) -> bool { (self.0 & 0x4) != 0 }
+
+ #[inline]
+ fn num(&self) -> bool { (self.0 & 0x2) != 0 }
+
+ #[inline]
+ fn scr(&self) -> bool { (self.0 & 0x1) != 0 }
+
+ #[inline]
+ fn set_cap(&mut self, set: bool)
+ {
+ let bit = (set as u8) << 2;
+ self.0 = (self.0 & !bit) | bit;
+ }
+
+ #[inline]
+ fn set_num(&mut self, set: bool)
+ {
+ let bit = (set as u8) << 1;
+ self.0 = (self.0 & !bit) | bit;
+ }
+
+ #[inline]
+ fn set_scr(&mut self, set: bool)
+ {
+ let bit = set as u8;
+ self.0 = (self.0 & !bit) | bit;
+ }
+}
+
+impl fmt::Display for KbLeds
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
+ {
+ write!(
+ f,
+ "caps: {}, num: {}, scroll: {}",
+ self.cap(),
+ self.num(),
+ self.scr()
+ )
+ }
+}
+
+impl From<libc::c_char> for KbLeds
+{
+ fn from(leds: libc::c_char) -> Self
+ {
+ Self::new(leds & 0x4 != 0, leds & 0x2 != 0, leds & 0x1 != 0)
+ }
+}
+
+impl From<KbLeds> for libc::c_ulong
+{
+ fn from(state: KbLeds) -> Self { state.0 as libc::c_ulong }
+}
+
+impl From<KbLeds> for u8
+{
+ fn from(state: KbLeds) -> Self { state.0 }
+}
+
+impl TryFrom<u8> for KbLeds
+{
+ type Error = io::Error;
+
+ fn try_from(val: u8) -> io::Result<Self>
+ {
+ if val <= 0b111 {
+ Ok(Self(val))
+ } else {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!(
+ "invalid raw led value: {:#b}; must not exceed 3 b",
+ val
+ ),
+ ))
+ }
+ }
+}
+
+#[cfg(test)]
+mod kb_led_state
+{
+ use super::KbLeds;
+
+ #[test]
+ fn create()
+ {
+ assert_eq!(0u8, KbLeds::new(false, false, false).into());
+ assert_eq!(1u8, KbLeds::new(false, false, true).into());
+ assert_eq!(2u8, KbLeds::new(false, true, false).into());
+ assert_eq!(4u8, KbLeds::new(true, false, false).into());
+ assert_eq!(6u8, KbLeds::new(true, true, false).into());
+
+ assert_eq!(0u8, KbLeds::from(0u8 as libc::c_char).into());
+ assert_eq!(1u8, KbLeds::from(1u8 as libc::c_char).into());
+ assert_eq!(2u8, KbLeds::from(2u8 as libc::c_char).into());
+ assert_eq!(4u8, KbLeds::from(4u8 as libc::c_char).into());
+ assert_eq!(6u8, KbLeds::from(6u8 as libc::c_char).into());
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct KbLedState(KbLeds);
+
+impl KbLedState
+{
+ pub fn new(cap: bool, num: bool, scr: bool) -> Self
+ {
+ Self(KbLeds::new(cap, num, scr))
+ }
+
+ #[inline]
+ pub fn get(con: &Console) -> io::Result<Self> { ioctl::kdgetled(con) }
+
+ #[inline]
+ pub fn set(&self, con: &Console) -> io::Result<()>
+ {
+ ioctl::kdsetled(con, Some(*self))
+ }
+
+ #[inline]
+ pub fn revert(con: &Console) -> io::Result<()>
+ {
+ ioctl::kdsetled(con, None)
+ }
+
+ #[inline]
+ pub fn cap(&self) -> bool { self.0.cap() }
+
+ #[inline]
+ pub fn num(&self) -> bool { self.0.num() }
+
+ #[inline]
+ pub fn scr(&self) -> bool { self.0.scr() }
+
+ #[inline]
+ pub fn set_cap(&mut self, set: bool) { self.0.set_cap(set) }
+
+ #[inline]
+ pub fn set_num(&mut self, set: bool) { self.0.set_num(set) }
+
+ #[inline]
+ pub fn set_scr(&mut self, set: bool) { self.0.set_scr(set) }
+}
+
+impl fmt::Display for KbLedState
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
+ {
+ write!(f, "{}", self.0)
+ }
+}
+
+impl From<libc::c_char> for KbLedState
+{
+ fn from(leds: libc::c_char) -> Self { Self(KbLeds(leds as u8)) }
+}
+
+impl From<KbLedState> for libc::c_ulong
+{
+ fn from(state: KbLedState) -> Self { state.0 .0 as libc::c_ulong }
+}
+
+impl From<KbLedState> for u8
+{
+ fn from(state: KbLedState) -> Self { state.0 .0 }
+}
+
+impl TryFrom<u8> for KbLedState
+{
+ type Error = io::Error;
+
+ fn try_from(val: u8) -> io::Result<Self>
+ {
+ Ok(Self(KbLeds::try_from(val)?))
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct KbLedFlags
+{
+ flags: KbLeds,
+ default: KbLeds,
+}
+
+impl KbLedFlags
+{
+ pub fn new_flags(cap: bool, num: bool, scr: bool) -> Self
+ {
+ let flags = KbLeds::new(cap, num, scr);
+ let default = KbLeds::off();
+ Self { flags, default }
+ }
+
+ pub fn new(
+ fcap: bool,
+ fnum: bool,
+ fscr: bool,
+ dcap: bool,
+ dnum: bool,
+ dscr: bool,
+ ) -> Self
+ {
+ let flags = KbLeds::new(fcap, fnum, fscr);
+ let default = KbLeds::new(dcap, dnum, dscr);
+ Self { flags, default }
+ }
+
+ #[inline]
+ pub fn get(con: &Console) -> io::Result<Self> { ioctl::kdgkbled(con) }
+
+ #[inline]
+ pub fn set(&self, con: &Console) -> io::Result<()>
+ {
+ ioctl::kdskbled(con, *self)
+ }
+
+ #[inline]
+ pub fn cap(&self) -> bool { self.flags.cap() }
+
+ #[inline]
+ pub fn num(&self) -> bool { self.flags.num() }
+
+ #[inline]
+ pub fn scr(&self) -> bool { self.flags.scr() }
+
+ #[inline]
+ pub fn default_cap(&self) -> bool { self.default.cap() }
+
+ #[inline]
+ pub fn default_num(&self) -> bool { self.default.num() }
+
+ #[inline]
+ pub fn default_scr(&self) -> bool { self.default.scr() }
+
+ #[inline]
+ pub fn set_cap(&mut self, set: bool) { self.flags.set_cap(set) }
+
+ #[inline]
+ pub fn set_num(&mut self, set: bool) { self.flags.set_num(set) }
+
+ #[inline]
+ pub fn set_scr(&mut self, set: bool) { self.flags.set_scr(set) }
+
+ #[inline]
+ pub fn set_default_cap(&mut self, set: bool) { self.default.set_cap(set) }
+
+ #[inline]
+ pub fn set_default_num(&mut self, set: bool) { self.default.set_num(set) }
+
+ #[inline]
+ pub fn set_default_scr(&mut self, set: bool) { self.default.set_scr(set) }
+}
+
+impl From<KbLedFlags> for u8
+{
+ fn from(state: KbLedFlags) -> Self
+ {
+ state.flags.0 | (state.default.0 << 0x4)
+ }
+}
+
+impl TryFrom<u8> for KbLedFlags
+{
+ type Error = io::Error;
+
+ /** From the manpage:
+
+ The low order three bits (mask 0x7) get the current flag state,
+ and the low order bits of the next nibble (mask 0x70) get the
+ default flag state.
+ */
+ fn try_from(val: u8) -> io::Result<Self>
+ {
+ let flags = val & (ioctl::KD_KBLED_STATE_MASK as u8);
+ let default = val & (ioctl::KD_KBLED_DEFAULT_MASK as u8) >> 4;
+ let flags = KbLeds::try_from(flags)?;
+ let default = KbLeds::try_from(default)?;
+
+ Ok(Self { flags, default })
+ }
+}
+
+impl fmt::Display for KbLedFlags
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
+ {
+ write!(f, "[flags: {}; default: {}]", self.flags, self.default)
+ }
}
#[derive(Debug)]
@@ -427,14 +846,17 @@ macro_rules! byte_of_hex {
};
}
-struct Rgb(u8, u8, u8);
+pub struct Rgb(pub u8, pub u8, pub u8);
impl Rgb
{
+ #[inline]
fn r(&self) -> u8 { self.0 }
+ #[inline]
fn g(&self) -> u8 { self.1 }
+ #[inline]
fn b(&self) -> u8 { self.2 }
}
@@ -452,6 +874,18 @@ impl TryFrom<&[u8; 6]> for Rgb
}
}
+impl From<[u8; 3]> for Rgb
+{
+ fn from(bytes: [u8; 3]) -> Self
+ {
+ let r = bytes[0];
+ let g = bytes[1];
+ let b = bytes[2];
+
+ Self(r, g, b)
+ }
+}
+
impl From<u32> for Rgb
{
fn from(rgb: u32) -> Self
@@ -604,11 +1038,12 @@ impl Palette
}
let mut res = Self::new();
- res.0.copy_from_slice(&b);
+ res.0.copy_from_slice(b);
Ok(res)
}
+ pub fn iter(&self) -> PaletteIterator { PaletteIterator::new(&self) }
/* [Palette::from_stdin] */
} /* [impl Palette] */
@@ -690,6 +1125,99 @@ impl From<&RawPalette> for Palette
}
}
+#[cfg(feature = "gui")]
+impl From<&[slint::Color]> for Palette
+{
+ fn from(colors: &[slint::Color]) -> Self
+ {
+ let mut idx: usize = 0;
+ let mut pal: [u8; PALETTE_BYTES] = [0; PALETTE_BYTES];
+
+ for &col in colors.iter() {
+ pal[idx] = col.red();
+ pal[idx + 1] = col.green();
+ pal[idx + 2] = col.blue();
+ idx += 3;
+ }
+
+ Self(pal)
+ }
+}
+
+/** Convert palette to the default text format so it can be parsed as a scheme. */
+impl Into<String> for Palette
+{
+ fn into(self) -> String
+ {
+ let mut acc = String::with_capacity(16 * 10);
+ for i in 0..PALETTE_SIZE {
+ let idx = i * 3;
+ let (r, g, b) = (self.0[idx], self.0[idx + 1], self.0[idx + 2]);
+ acc.push_str(&format!("{}#{:02.x}{:02.x}{:02.x}\n", i, r, g, b));
+ }
+ acc
+ }
+}
+
+#[test]
+fn palette_dump_as_text()
+{
+ let pal = Palette::from(&SOLARIZED_COLORS_DARK);
+ let txt = indoc::indoc! { r#"
+ 0#002b36
+ 1#dc322f
+ 2#859900
+ 3#b58900
+ 4#268bd2
+ 5#d33682
+ 6#2aa198
+ 7#eee8d5
+ 8#002b36
+ 9#cb4b16
+ 10#586e75
+ 11#657b83
+ 12#839496
+ 13#6c71c4
+ 14#93a1a1
+ 15#fdf6e3
+ "#};
+
+ let pal: String = pal.into();
+ assert_eq!(pal, txt);
+}
+
+pub struct PaletteIterator
+{
+ pal: Palette,
+ cur: usize,
+}
+
+impl PaletteIterator
+{
+ fn new(pal: &Palette) -> Self { Self { pal: pal.clone(), cur: 0 } }
+}
+
+impl Iterator for PaletteIterator
+{
+ type Item = Rgb;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ if self.cur >= PALETTE_SIZE {
+ None
+ } else {
+ let off = self.cur * 3;
+ let rgb = Rgb::from([
+ self.pal.0[off],
+ self.pal.0[off + 1],
+ self.pal.0[off + 2],
+ ]);
+ self.cur += 1;
+ Some(rgb)
+ }
+ }
+}
+
const CONSOLE_PATHS: [&str; 6] = [
"/proc/self/fd/0",
"/dev/tty",
@@ -728,7 +1256,7 @@ impl Console
Ok(fd)
}
- fn from_path<P: AsRef<Path>>(path: P) -> io::Result<Self>
+ pub fn from_path<P: AsRef<Path>>(path: P) -> io::Result<Self>
{
let p =
std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
@@ -750,7 +1278,7 @@ impl Console
Err(io::Error::new(
io::ErrorKind::Other,
- format!("could not retrieve fd for any of the search paths"),
+ String::from("could not retrieve fd for any of the search paths"),
))
}
@@ -805,6 +1333,20 @@ impl Console
}
}
+impl Drop for Console
+{
+ fn drop(&mut self)
+ {
+ if unsafe { libc::close(self.0) } == -1 {
+ eprintln!(
+ "Console: error closing fd {}: {}",
+ self.0,
+ Error::last_os_error()
+ );
+ }
+ }
+}
+
impl AsRawFd for Console
{
fn as_raw_fd(&self) -> RawFd { self.0 }