diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 592 |
1 files changed, 567 insertions, 25 deletions
@@ -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 } |