keyfork-crossterm: add FdTerminal struct to manage non-default terminals
This commit is contained in:
parent
6825ac9cea
commit
f6b41fce5f
|
@ -1427,7 +1427,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "keyfork-crossterm"
|
||||
version = "0.27.0"
|
||||
version = "0.27.1"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"bitflags 2.4.1",
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
//!
|
||||
//! For manual execution control check out [crossterm::queue](../macro.queue.html).
|
||||
|
||||
use std::{fmt, io};
|
||||
use std::{fmt, io, os};
|
||||
|
||||
#[cfg(windows)]
|
||||
use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer};
|
||||
|
@ -92,6 +92,57 @@ use serde::{Deserialize, Serialize};
|
|||
#[cfg(windows)]
|
||||
use winapi::um::wincon::ENABLE_WRAP_AT_EOL_OUTPUT;
|
||||
|
||||
pub trait TerminalIoctl {
|
||||
fn enable_raw_mode(&mut self) -> io::Result<()>;
|
||||
|
||||
fn disable_raw_mode(&mut self) -> io::Result<()>;
|
||||
|
||||
fn size(&self) -> io::Result<(u16, u16)>;
|
||||
|
||||
fn window_size(&self) -> io::Result<WindowSize>;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub struct FdTerminal {
|
||||
fd: i32,
|
||||
stored_termios: Option<libc::termios>,
|
||||
}
|
||||
|
||||
impl<T> From<T> for FdTerminal where T: os::fd::AsRawFd {
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
fd: value.as_raw_fd(),
|
||||
stored_termios: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl TerminalIoctl for FdTerminal {
|
||||
fn enable_raw_mode(&mut self) -> io::Result<()> {
|
||||
if self.stored_termios.is_none() {
|
||||
let termios = sys::fd_enable_raw_mode(self.fd)?;
|
||||
let _ = self.stored_termios.insert(termios);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable_raw_mode(&mut self) -> io::Result<()> {
|
||||
if let Some(termios) = self.stored_termios.take() {
|
||||
sys::fd_disable_raw_mode(self.fd, termios)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn size(&self) -> io::Result<(u16, u16)> {
|
||||
sys::fd_size(self.fd)
|
||||
}
|
||||
|
||||
fn window_size(&self) -> io::Result<WindowSize> {
|
||||
sys::fd_window_size(self.fd)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(no_inline)]
|
||||
use crate::Command;
|
||||
use crate::{csi, impl_display};
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
pub use self::unix::supports_keyboard_enhancement;
|
||||
#[cfg(unix)]
|
||||
pub(crate) use self::unix::{
|
||||
disable_raw_mode, enable_raw_mode, is_raw_mode_enabled, size, window_size,
|
||||
disable_raw_mode, enable_raw_mode, fd_disable_raw_mode, fd_enable_raw_mode, fd_size,
|
||||
fd_window_size, is_raw_mode_enabled, size, window_size,
|
||||
};
|
||||
#[cfg(windows)]
|
||||
#[cfg(feature = "events")]
|
||||
|
|
|
@ -34,6 +34,21 @@ impl From<winsize> for WindowSize {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fd_window_size(fd: i32) -> io::Result<WindowSize> {
|
||||
let mut size = winsize {
|
||||
ws_row: 0,
|
||||
ws_col: 0,
|
||||
ws_xpixel: 0,
|
||||
ws_ypixel: 0,
|
||||
};
|
||||
|
||||
if wrap_with_result(unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut size) }).is_ok() {
|
||||
return Ok(size.into());
|
||||
}
|
||||
|
||||
Err(std::io::Error::last_os_error())
|
||||
}
|
||||
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub(crate) fn window_size() -> io::Result<WindowSize> {
|
||||
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
|
||||
|
@ -59,6 +74,10 @@ pub(crate) fn window_size() -> io::Result<WindowSize> {
|
|||
Err(std::io::Error::last_os_error().into())
|
||||
}
|
||||
|
||||
pub(crate) fn fd_size(fd: i32) -> io::Result<(u16, u16)> {
|
||||
fd_window_size(fd).map(|WindowSize { rows, columns, .. }| (columns, rows))
|
||||
}
|
||||
|
||||
#[allow(clippy::useless_conversion)]
|
||||
pub(crate) fn size() -> io::Result<(u16, u16)> {
|
||||
if let Ok(window_size) = window_size() {
|
||||
|
@ -68,6 +87,15 @@ pub(crate) fn size() -> io::Result<(u16, u16)> {
|
|||
tput_size().ok_or_else(|| std::io::Error::last_os_error().into())
|
||||
}
|
||||
|
||||
pub(crate) fn fd_enable_raw_mode(fd: i32) -> io::Result<Termios> {
|
||||
let mut ios = get_terminal_attr(fd)?;
|
||||
let original_mode_ios = ios;
|
||||
|
||||
raw_terminal_attr(&mut ios);
|
||||
set_terminal_attr(fd, &ios)?;
|
||||
Ok(original_mode_ios)
|
||||
}
|
||||
|
||||
pub(crate) fn enable_raw_mode() -> io::Result<()> {
|
||||
let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock();
|
||||
|
||||
|
@ -89,6 +117,11 @@ pub(crate) fn enable_raw_mode() -> io::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn fd_disable_raw_mode(fd: i32, termios: Termios) -> io::Result<()> {
|
||||
set_terminal_attr(fd, &termios)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset the raw mode.
|
||||
///
|
||||
/// More precisely, reset the whole termios mode to what it was before the first call
|
||||
|
|
|
@ -2,11 +2,11 @@ use std::io::{stdin, stdout};
|
|||
|
||||
use keyfork_prompt::{
|
||||
validators::{mnemonic, Validator},
|
||||
PromptManager,
|
||||
Terminal,
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut mgr = PromptManager::new(stdin(), stdout())?;
|
||||
let mut mgr = Terminal::new(stdin(), stdout())?;
|
||||
let transport_validator = mnemonic::MnemonicSetValidator {
|
||||
word_lengths: [9, 24],
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ use keyfork_crossterm::{
|
|||
cursor,
|
||||
event::{read, DisableBracketedPaste, EnableBracketedPaste, Event, KeyCode, KeyModifiers},
|
||||
style::{Print, PrintStyledContent, Stylize},
|
||||
terminal,
|
||||
terminal::{self, TerminalIoctl, FdTerminal},
|
||||
tty::IsTty,
|
||||
QueueableCommand,
|
||||
};
|
||||
|
@ -44,12 +44,13 @@ pub enum Message {
|
|||
Data(String),
|
||||
}
|
||||
|
||||
pub struct PromptManager<R, W> {
|
||||
pub struct Terminal<R, W> {
|
||||
read: BufReader<R>,
|
||||
write: W,
|
||||
terminal: FdTerminal,
|
||||
}
|
||||
|
||||
impl<R, W> PromptManager<R, W>
|
||||
impl<R, W> Terminal<R, W>
|
||||
where
|
||||
R: Read + Sized,
|
||||
W: Write + AsRawFd + Sized,
|
||||
|
@ -60,6 +61,7 @@ where
|
|||
}
|
||||
Ok(Self {
|
||||
read: BufReader::new(read_handle),
|
||||
terminal: FdTerminal::from(write_handle.as_raw_fd()),
|
||||
write: write_handle,
|
||||
})
|
||||
}
|
||||
|
@ -140,7 +142,7 @@ where
|
|||
}
|
||||
terminal.flush()?;
|
||||
|
||||
let (mut cols, mut _rows) = terminal::size()?;
|
||||
let (mut cols, mut _rows) = self.terminal.size()?;
|
||||
let mut input = String::new();
|
||||
|
||||
loop {
|
||||
|
@ -290,7 +292,7 @@ where
|
|||
}
|
||||
terminal.flush()?;
|
||||
|
||||
let (mut cols, mut _rows) = terminal::size()?;
|
||||
let (mut cols, mut _rows) = self.terminal.size()?;
|
||||
|
||||
let mut passphrase = String::new();
|
||||
loop {
|
||||
|
@ -334,7 +336,7 @@ where
|
|||
let mut terminal = RawMode::new(&mut terminal)?;
|
||||
|
||||
loop {
|
||||
let (cols, rows) = terminal::size()?;
|
||||
let (cols, rows) = self.terminal.size()?;
|
||||
|
||||
terminal
|
||||
.queue(terminal::Clear(terminal::ClearType::All))?
|
||||
|
@ -402,8 +404,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub type DefaultPromptManager = PromptManager<Stdin, Stderr>;
|
||||
pub type DefaultTerminal = Terminal<Stdin, Stderr>;
|
||||
|
||||
pub fn default_prompt_manager() -> Result<DefaultPromptManager> {
|
||||
PromptManager::new(stdin(), stderr())
|
||||
pub fn default_terminal() -> Result<DefaultTerminal> {
|
||||
Terminal::new(stdin(), stderr())
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{
|
|||
os::fd::AsRawFd,
|
||||
};
|
||||
|
||||
use keyfork_crossterm::terminal;
|
||||
use keyfork_crossterm::terminal::{FdTerminal, TerminalIoctl};
|
||||
|
||||
use crate::Result;
|
||||
|
||||
|
@ -12,16 +12,18 @@ where
|
|||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
write: &'a mut W,
|
||||
terminal: FdTerminal,
|
||||
}
|
||||
|
||||
// TODO: fork crossterm to allow using FD from as_raw_fd()
|
||||
impl<'a, W> RawMode<'a, W>
|
||||
where
|
||||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
pub(crate) fn new(write_handle: &'a mut W) -> Result<Self> {
|
||||
terminal::enable_raw_mode()?;
|
||||
let mut terminal = FdTerminal::from(write_handle.as_raw_fd());
|
||||
terminal.enable_raw_mode()?;
|
||||
Ok(Self {
|
||||
terminal,
|
||||
write: write_handle,
|
||||
})
|
||||
}
|
||||
|
@ -72,6 +74,6 @@ where
|
|||
W: Write + AsRawFd + Sized,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
terminal::disable_raw_mode().unwrap();
|
||||
self.terminal.disable_raw_mode().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use keyfork_mnemonic_util::{Mnemonic, Wordlist};
|
|||
use keyfork_prompt::{
|
||||
qrencode,
|
||||
validators::{mnemonic::MnemonicSetValidator, Validator},
|
||||
Message as PromptMessage, PromptManager,
|
||||
Message as PromptMessage, Terminal,
|
||||
};
|
||||
use sha2::Sha256;
|
||||
use sharks::{Share, Sharks};
|
||||
|
@ -43,7 +43,7 @@ pub(crate) const HUNK_OFFSET: usize = 2;
|
|||
/// The function may panic if it is given payloads generated using a version of Keyfork that is
|
||||
/// incompatible with the currently running version.
|
||||
pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut pm = PromptManager::new(stdin(), stdout())?;
|
||||
let mut pm = Terminal::new(stdin(), stdout())?;
|
||||
let wordlist = Wordlist::default();
|
||||
|
||||
let mut iter_count = None;
|
||||
|
|
|
@ -19,7 +19,7 @@ use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationEr
|
|||
use keyfork_prompt::{
|
||||
qrencode,
|
||||
validators::{mnemonic::MnemonicSetValidator, Validator},
|
||||
Error as PromptError, Message as PromptMessage, PromptManager,
|
||||
Error as PromptError, Message as PromptMessage, Terminal,
|
||||
};
|
||||
use openpgp::{
|
||||
armor::{Kind, Writer},
|
||||
|
@ -409,7 +409,7 @@ pub fn decrypt(
|
|||
metadata: &EncryptedMessage,
|
||||
encrypted_messages: &[EncryptedMessage],
|
||||
) -> Result<()> {
|
||||
let mut pm = PromptManager::new(stdin(), stdout())?;
|
||||
let mut pm = Terminal::new(stdin(), stdout())?;
|
||||
let wordlist = Wordlist::default();
|
||||
let validator = MnemonicSetValidator {
|
||||
word_lengths: [9, 24],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use keyfork_prompt::{Error as PromptError, DefaultPromptManager, default_prompt_manager};
|
||||
use keyfork_prompt::{Error as PromptError, DefaultTerminal, default_terminal};
|
||||
|
||||
use super::openpgp::{
|
||||
self,
|
||||
|
@ -25,7 +25,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
|
|||
pub struct Keyring {
|
||||
full_certs: Vec<Cert>,
|
||||
root: Option<Cert>,
|
||||
pm: DefaultPromptManager,
|
||||
pm: DefaultTerminal,
|
||||
}
|
||||
|
||||
impl Keyring {
|
||||
|
@ -33,7 +33,7 @@ impl Keyring {
|
|||
Ok(Self {
|
||||
full_certs: certs.as_ref().to_vec(),
|
||||
root: Default::default(),
|
||||
pm: default_prompt_manager()?,
|
||||
pm: default_terminal()?,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use keyfork_prompt::{
|
||||
default_prompt_manager,
|
||||
default_terminal,
|
||||
validators::{PinValidator, Validator},
|
||||
DefaultPromptManager, Error as PromptError, Message,
|
||||
DefaultTerminal, Error as PromptError, Message,
|
||||
};
|
||||
|
||||
use super::openpgp::{
|
||||
|
@ -69,7 +69,7 @@ fn format_name(input: impl AsRef<str>) -> String {
|
|||
pub struct SmartcardManager {
|
||||
current_card: Option<Card<Open>>,
|
||||
root: Option<Cert>,
|
||||
pm: DefaultPromptManager,
|
||||
pm: DefaultTerminal,
|
||||
pin_cache: HashMap<Fingerprint, String>,
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ impl SmartcardManager {
|
|||
Ok(Self {
|
||||
current_card: None,
|
||||
root: None,
|
||||
pm: default_prompt_manager()?,
|
||||
pm: default_terminal()?,
|
||||
pin_cache: Default::default(),
|
||||
})
|
||||
}
|
||||
|
@ -99,9 +99,8 @@ impl SmartcardManager {
|
|||
if let Some(c) = PcscBackend::cards(None)?.next().transpose()? {
|
||||
break c;
|
||||
}
|
||||
self.pm.prompt_message(&Message::Text(
|
||||
"No smart card was found".to_string(),
|
||||
))?;
|
||||
self.pm
|
||||
.prompt_message(&Message::Text("No smart card was found".to_string()))?;
|
||||
};
|
||||
let mut card = Card::<Open>::new(card_backend).map_err(Error::OpenSmartCard)?;
|
||||
let transaction = card.transaction().map_err(Error::Transaction)?;
|
||||
|
|
|
@ -12,7 +12,7 @@ use keyfork_derive_util::{
|
|||
};
|
||||
use keyfork_prompt::{
|
||||
validators::{PinValidator, Validator},
|
||||
Message, PromptManager,
|
||||
Message, Terminal,
|
||||
};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -102,7 +102,7 @@ fn factory_reset_current_card(
|
|||
|
||||
fn generate_shard_secret(threshold: u8, max: u8, keys_per_shard: u8) -> Result<()> {
|
||||
let seed = keyfork_entropy::generate_entropy_of_size(256 / 8)?;
|
||||
let mut pm = PromptManager::new(std::io::stdin(), std::io::stderr())?;
|
||||
let mut pm = Terminal::new(std::io::stdin(), std::io::stderr())?;
|
||||
let mut certs = vec![];
|
||||
let mut seen_cards: HashSet<String> = HashSet::new();
|
||||
let stdout = std::io::stdout();
|
||||
|
|
Loading…
Reference in New Issue