Fixed tuigreet not exiting on success (#40).

Changed terminal backend to allow for async stdin reading without
blocking program termination.
This commit is contained in:
Antoine POPINEAU 2021-10-11 12:13:47 +02:00
parent 546d7fb75b
commit d2974d288b
No known key found for this signature in database
GPG Key ID: A78AC64694F84063
13 changed files with 288 additions and 140 deletions

213
Cargo.lock generated
View File

@ -108,6 +108,48 @@ dependencies = [
"libc",
]
[[package]]
name = "crossterm"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486d44227f71a1ef39554c0dc47e44b9f4139927c75043312690c3f476d1d788"
dependencies = [
"bitflags",
"crossterm_winapi",
"futures-core",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507"
dependencies = [
"winapi",
]
[[package]]
name = "dashmap"
version = "4.0.2"
@ -189,6 +231,100 @@ dependencies = [
"thiserror",
]
[[package]]
name = "futures"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d"
[[package]]
name = "futures-executor"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377"
[[package]]
name = "futures-macro"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb"
dependencies = [
"autocfg",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11"
[[package]]
name = "futures-task"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99"
[[package]]
name = "futures-util"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481"
dependencies = [
"autocfg",
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.4"
@ -494,12 +630,6 @@ dependencies = [
"libc",
]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "objc"
version = "0.2.7"
@ -605,6 +735,12 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.10"
@ -635,6 +771,18 @@ dependencies = [
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "1.0.29"
@ -708,15 +856,6 @@ dependencies = [
"bitflags",
]
[[package]]
name = "redox_termios"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
dependencies = [
"redox_syscall",
]
[[package]]
name = "regex"
version = "1.5.4"
@ -849,6 +988,27 @@ dependencies = [
"opaque-debug",
]
[[package]]
name = "signal-hook"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@ -858,6 +1018,12 @@ dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
[[package]]
name = "smallvec"
version = "1.7.0"
@ -904,18 +1070,6 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termion"
version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
dependencies = [
"libc",
"numtoa",
"redox_syscall",
"redox_termios",
]
[[package]]
name = "textwrap"
version = "0.14.2"
@ -1011,7 +1165,7 @@ checksum = "39c8ce4e27049eed97cfa363a5048b09d995e209994634a0efc26a14ab6c0c23"
dependencies = [
"bitflags",
"cassowary",
"termion",
"crossterm 0.20.0",
"unicode-segmentation",
"unicode-width",
]
@ -1021,6 +1175,8 @@ name = "tuigreet"
version = "0.7.1"
dependencies = [
"chrono",
"crossterm 0.21.0",
"futures",
"getopts",
"greetd_ipc",
"i18n-embed",
@ -1030,7 +1186,6 @@ dependencies = [
"rust-embed",
"rust-ini",
"smart-default",
"termion",
"textwrap",
"tokio",
"tui",

View File

@ -7,14 +7,15 @@ build = "build.rs"
[dependencies]
chrono = { version = "^0.4.19", features = ["unstable-locales"] }
crossterm = { version = "0.21.0", features = ["event-stream"] }
futures = "0.3.17"
getopts = "^0.2.21"
greetd_ipc = { version = "0.8.0", features = ["tokio-codec"] }
i18n-embed = { version = "^0.13.0", features = ["desktop-requester", "fluent-system"] }
i18n-embed-fl = "^0.6.0"
lazy_static = "^1.4.0"
nix = "^0.23.0"
termion = "^1.5.6"
tui = "^0.16.0"
tui = { version = "^0.16.0", default-features = false, features = ["crossterm"] }
rust-embed = "^6.2"
rust-ini = "^0.17.0"
smart-default = "0.6.0"

View File

@ -1,60 +1,45 @@
use std::{io, time::Duration};
use std::time::Duration;
use termion::{event::Key, input::TermRead};
use tokio::sync::mpsc;
use crossterm::event::{Event as TermEvent, EventStream, KeyEvent};
use futures::{future::FutureExt, StreamExt};
use tokio::{sync::mpsc, time};
pub enum Event<I> {
Input(I),
const TICK_RATE: u64 = 250;
pub enum Event {
Key(KeyEvent),
Tick,
}
pub struct Events {
rx: mpsc::Receiver<Event<Key>>,
}
#[derive(Debug, Clone, Copy)]
pub struct Config {
pub tick_rate: Duration,
}
impl Default for Config {
fn default() -> Config {
Config {
tick_rate: Duration::from_millis(250),
}
}
rx: mpsc::Receiver<Event>,
}
impl Events {
pub async fn new() -> Events {
let mut stream = EventStream::new();
let (tx, rx) = mpsc::channel(10);
{
let tx = tx.clone();
tokio::task::spawn(async move {
let stdin = io::stdin();
for key in stdin.keys().flatten() {
if tx.send(Event::Input(key)).await.is_err() {
return;
}
}
})
};
tokio::task::spawn(async move {
loop {
let _ = tx.send(Event::Tick).await;
let timeout = time::sleep(Duration::from_millis(TICK_RATE));
tokio::time::sleep(Duration::from_millis(250)).await;
tokio::select! {
event = stream.next().fuse() => {
if let Some(Ok(TermEvent::Key(event))) = event {
let _ = tx.send(Event::Key(event)).await;
}
}
_ = timeout => { let _ = tx.send(Event::Tick).await; },
}
}
});
Events { rx }
}
pub async fn next(&mut self) -> Option<Event<Key>> {
pub async fn next(&mut self) -> Option<Event> {
self.rx.recv().await
}
}

View File

@ -14,10 +14,7 @@ use i18n_embed::DesktopLanguageRequester;
use tokio::{
net::UnixStream,
process::Command,
sync::{
oneshot::{Receiver, Sender},
RwLock, RwLockWriteGuard,
},
sync::{RwLock, RwLockWriteGuard},
};
use zeroize::Zeroize;
@ -94,8 +91,7 @@ pub struct Greeter {
pub working: bool,
pub done: bool,
pub exit_tx: Option<Sender<AuthStatus>>,
pub exit_rx: Option<Receiver<AuthStatus>>,
pub exit: Option<AuthStatus>,
}
impl Drop for Greeter {
@ -130,10 +126,6 @@ impl Greeter {
greeter.selected_session = greeter.sessions.iter().position(|(_, command)| Some(command) == greeter.command.as_ref()).unwrap_or(0);
let (exit_tx, exit_rx) = tokio::sync::oneshot::channel::<AuthStatus>();
greeter.exit_tx = Some(exit_tx);
greeter.exit_rx = Some(exit_rx);
greeter
}

View File

@ -106,6 +106,7 @@ impl Ipc {
crate::exit(&mut greeter, AuthStatus::Success).await;
} else if let Some(command) = &greeter.command {
greeter.done = true;
greeter.mode = Mode::Processing;
#[cfg(not(debug_assertions))]
self.send(Request::StartSession { cmd: vec![command.clone()] }).await;

View File

@ -1,7 +1,7 @@
use std::{error::Error, sync::Arc};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use greetd_ipc::Request;
use termion::event::Key;
use tokio::sync::RwLock;
use crate::{
@ -14,19 +14,39 @@ use crate::{
};
pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, ipc: Ipc) -> Result<(), Box<dyn Error>> {
if let Some(Event::Input(input)) = events.next().await {
if let Some(Event::Key(input)) = events.next().await {
let mut greeter = greeter.write().await;
match input {
Key::Esc => {
KeyEvent {
code: KeyCode::Char('u'),
modifiers: KeyModifiers::CONTROL,
} => match greeter.mode {
Mode::Username => greeter.username = String::new(),
Mode::Password => greeter.answer = String::new(),
Mode::Command => greeter.new_command = String::new(),
_ => {}
},
#[cfg(debug_assertions)]
KeyEvent {
code: KeyCode::Char('x'),
modifiers: KeyModifiers::CONTROL,
} => {
use crate::greeter::AuthStatus;
crate::exit(&mut greeter, AuthStatus::Cancel).await;
}
KeyEvent { code: KeyCode::Esc, .. } => {
Ipc::cancel(&mut greeter).await;
greeter.reset().await;
}
Key::Left => greeter.cursor_offset -= 1,
Key::Right => greeter.cursor_offset += 1,
KeyEvent { code: KeyCode::Left, .. } => greeter.cursor_offset -= 1,
KeyEvent { code: KeyCode::Right, .. } => greeter.cursor_offset += 1,
Key::F(2) => {
KeyEvent { code: KeyCode::F(2), .. } => {
greeter.previous_mode = match greeter.mode {
Mode::Command | Mode::Sessions | Mode::Power => greeter.previous_mode,
_ => greeter.mode,
@ -36,7 +56,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, ipc: Ipc
greeter.mode = Mode::Command;
}
Key::F(3) => {
KeyEvent { code: KeyCode::F(3), .. } => {
greeter.previous_mode = match greeter.mode {
Mode::Command | Mode::Sessions | Mode::Power => greeter.previous_mode,
_ => greeter.mode,
@ -45,7 +65,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, ipc: Ipc
greeter.mode = Mode::Sessions;
}
Key::F(12) => {
KeyEvent { code: KeyCode::F(12), .. } => {
greeter.previous_mode = match greeter.mode {
Mode::Command | Mode::Sessions | Mode::Power => greeter.previous_mode,
_ => greeter.mode,
@ -54,7 +74,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, ipc: Ipc
greeter.mode = Mode::Power;
}
Key::Up => {
KeyEvent { code: KeyCode::Up, .. } => {
if let Mode::Sessions = greeter.mode {
if greeter.selected_session > 0 {
greeter.selected_session -= 1;
@ -68,7 +88,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, ipc: Ipc
}
}
Key::Down => {
KeyEvent { code: KeyCode::Down, .. } => {
if let Mode::Sessions = greeter.mode {
if greeter.selected_session < greeter.sessions.len() - 1 {
greeter.selected_session += 1;
@ -82,7 +102,10 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, ipc: Ipc
}
}
Key::Ctrl('a') => {
KeyEvent {
code: KeyCode::Char('a'),
modifiers: KeyModifiers::CONTROL,
} => {
let value = {
match greeter.mode {
Mode::Username => greeter.username.clone(),
@ -93,9 +116,12 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, ipc: Ipc
greeter.cursor_offset = -(value.chars().count() as i16);
}
Key::Ctrl('e') => greeter.cursor_offset = 0,
KeyEvent {
code: KeyCode::Char('e'),
modifiers: KeyModifiers::CONTROL,
} => greeter.cursor_offset = 0,
Key::Char('\n') | Key::Char('\t') => match greeter.mode {
KeyEvent { code: KeyCode::Enter, .. } | KeyEvent { code: KeyCode::Char('\t'), .. } => match greeter.mode {
Mode::Username => {
greeter.working = true;
greeter.message = None;
@ -158,23 +184,9 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, ipc: Ipc
Mode::Processing => {}
},
Key::Char(c) => insert_key(&mut greeter, c).await,
KeyEvent { code: KeyCode::Char(c), .. } => insert_key(&mut greeter, c).await,
Key::Backspace | Key::Delete => delete_key(&mut greeter, input).await,
Key::Ctrl('u') => match greeter.mode {
Mode::Username => greeter.username = String::new(),
Mode::Password => greeter.answer = String::new(),
Mode::Command => greeter.new_command = String::new(),
_ => {}
},
#[cfg(debug_assertions)]
Key::Ctrl('x') => {
use crate::greeter::AuthStatus;
crate::exit(&mut greeter, AuthStatus::Cancel).await;
}
KeyEvent { code: KeyCode::Backspace, .. } | KeyEvent { code: KeyCode::Delete, .. } => delete_key(&mut greeter, input.code).await,
_ => {}
}
@ -206,7 +218,7 @@ async fn insert_key(greeter: &mut Greeter, c: char) {
};
}
async fn delete_key(greeter: &mut Greeter, key: Key) {
async fn delete_key(greeter: &mut Greeter, key: KeyCode) {
let value = match greeter.mode {
Mode::Username => greeter.username.clone(),
Mode::Password => greeter.answer.clone(),
@ -215,8 +227,8 @@ async fn delete_key(greeter: &mut Greeter, key: Key) {
};
let index = match key {
Key::Backspace => (value.chars().count() as i16 + greeter.cursor_offset - 1) as usize,
Key::Delete => (value.chars().count() as i16 + greeter.cursor_offset) as usize,
KeyCode::Backspace => (value.chars().count() as i16 + greeter.cursor_offset - 1) as usize,
KeyCode::Delete => (value.chars().count() as i16 + greeter.cursor_offset) as usize,
_ => 0,
};
@ -233,7 +245,7 @@ async fn delete_key(greeter: &mut Greeter, key: Key) {
Mode::Sessions | Mode::Power | Mode::Processing => return,
};
if let Key::Delete = key {
if let KeyCode::Delete = key {
greeter.cursor_offset += 1;
}
}

View File

@ -14,10 +14,13 @@ mod ui;
use std::{error::Error, io, process, sync::Arc};
use crossterm::{
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen},
};
use greetd_ipc::Request;
use termion::raw::IntoRawMode;
use tokio::sync::RwLock;
use tui::{backend::TermionBackend, Terminal};
use tui::{backend::CrosstermBackend, Terminal};
pub use self::greeter::*;
use self::{event::Events, ipc::Ipc};
@ -36,8 +39,13 @@ async fn main() {
async fn run() -> Result<(), Box<dyn Error>> {
let greeter = Greeter::new().await;
let stdout = io::stdout().into_raw_mode()?;
let backend = TermionBackend::new(stdout);
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
terminal.clear()?;
@ -94,10 +102,8 @@ async fn run() -> Result<(), Box<dyn Error>> {
});
loop {
if let Some(ref mut rx) = greeter.write().await.exit_rx {
if let Ok(status) = rx.try_recv() {
return Err(status.into());
}
if let Some(status) = greeter.read().await.exit {
return Err(status.into());
}
ui::draw(greeter.clone(), &mut terminal).await?;
@ -112,14 +118,13 @@ pub async fn exit(mut greeter: &mut Greeter, status: AuthStatus) {
}
clear_screen();
let _ = disable_raw_mode();
if let Some(tx) = greeter.exit_tx.take() {
let _ = tx.send(status);
}
greeter.exit = Some(status);
}
pub fn clear_screen() {
let backend = TermionBackend::new(io::stdout());
let backend = CrosstermBackend::new(io::stdout());
if let Ok(mut terminal) = Terminal::new(backend) {
let _ = terminal.clear();
@ -130,7 +135,10 @@ pub fn clear_screen() {
pub fn log(msg: &str) {
use std::io::Write;
let time = chrono::Utc::now();
let mut file = std::fs::OpenOptions::new().create(true).append(true).open("/tmp/tuigreet.log").unwrap();
file.write_all(format!("{:?} - ", time).as_ref()).unwrap();
file.write_all(msg.as_ref()).unwrap();
file.write_all("\n".as_bytes()).unwrap();
}

View File

@ -1,8 +1,7 @@
use std::{error::Error, io};
use termion::raw::RawTerminal;
use tui::{
backend::TermionBackend,
backend::CrosstermBackend,
layout::{Constraint, Direction, Layout, Rect},
text::Span,
widgets::{Block, BorderType, Borders, Paragraph},
@ -12,7 +11,7 @@ use tui::{
use super::prompt_value;
use crate::{ui::util::*, Greeter};
pub fn draw(mut greeter: &mut Greeter, f: &mut Frame<'_, TermionBackend<RawTerminal<io::Stdout>>>) -> Result<(u16, u16), Box<dyn Error>> {
pub fn draw(mut greeter: &mut Greeter, f: &mut Frame<'_, CrosstermBackend<io::Stdout>>) -> Result<(u16, u16), Box<dyn Error>> {
let size = f.size();
let width = greeter.width();

View File

@ -13,10 +13,9 @@ use std::{
};
use chrono::prelude::*;
use termion::raw::RawTerminal;
use tokio::sync::RwLock;
use tui::{
backend::TermionBackend,
backend::CrosstermBackend,
layout::{Alignment, Constraint, Direction, Layout},
style::{Modifier, Style},
text::{Span, Spans},
@ -37,7 +36,7 @@ const STATUSBAR_INDEX: usize = 3;
const STATUSBAR_LEFT_INDEX: usize = 1;
const STATUSBAR_RIGHT_INDEX: usize = 2;
type Term = Terminal<TermionBackend<RawTerminal<io::Stdout>>>;
type Term = Terminal<CrosstermBackend<io::Stdout>>;
pub async fn draw(greeter: Arc<RwLock<Greeter>>, terminal: &mut Term) -> Result<(), Box<dyn Error>> {
let mut greeter = greeter.write().await;

View File

@ -1,9 +1,8 @@
use std::{error::Error, io};
use lazy_static::lazy_static;
use termion::raw::RawTerminal;
use tui::{
backend::TermionBackend,
backend::CrosstermBackend,
layout::Rect,
style::{Modifier, Style},
text::Span,
@ -23,7 +22,7 @@ lazy_static! {
};
}
pub fn draw(greeter: &mut Greeter, f: &mut Frame<'_, TermionBackend<RawTerminal<io::Stdout>>>) -> Result<(u16, u16), Box<dyn Error>> {
pub fn draw(greeter: &mut Greeter, f: &mut Frame<'_, CrosstermBackend<io::Stdout>>) -> Result<(u16, u16), Box<dyn Error>> {
let size = f.size();
let width = greeter.width();

View File

@ -1,8 +1,7 @@
use std::{error::Error, io};
use termion::raw::RawTerminal;
use tui::{
backend::TermionBackend,
backend::CrosstermBackend,
layout::{Alignment, Constraint, Direction, Layout, Rect},
text::Span,
widgets::{Block, BorderType, Borders, Paragraph},
@ -12,7 +11,7 @@ use tui::{
use super::util::*;
use crate::Greeter;
pub fn draw(greeter: &mut Greeter, f: &mut Frame<'_, TermionBackend<RawTerminal<io::Stdout>>>) -> Result<(u16, u16), Box<dyn Error>> {
pub fn draw(greeter: &mut Greeter, f: &mut Frame<'_, CrosstermBackend<io::Stdout>>) -> Result<(u16, u16), Box<dyn Error>> {
let size = f.size();
let width = greeter.width();

View File

@ -1,8 +1,7 @@
use std::{error::Error, io};
use termion::raw::RawTerminal;
use tui::{
backend::TermionBackend,
backend::CrosstermBackend,
layout::{Alignment, Constraint, Direction, Layout, Rect},
text::{Span, Text},
widgets::{Block, BorderType, Borders, Paragraph},
@ -16,7 +15,7 @@ const GREETING_INDEX: usize = 0;
const USERNAME_INDEX: usize = 1;
const ANSWER_INDEX: usize = 2;
pub fn draw(mut greeter: &mut Greeter, f: &mut Frame<'_, TermionBackend<RawTerminal<io::Stdout>>>) -> Result<(u16, u16), Box<dyn Error>> {
pub fn draw(mut greeter: &mut Greeter, f: &mut Frame<'_, CrosstermBackend<io::Stdout>>) -> Result<(u16, u16), Box<dyn Error>> {
let size = f.size();
let width = greeter.width();

View File

@ -1,8 +1,7 @@
use std::{error::Error, io};
use termion::raw::RawTerminal;
use tui::{
backend::TermionBackend,
backend::CrosstermBackend,
layout::Rect,
style::{Modifier, Style},
text::Span,
@ -13,7 +12,7 @@ use tui::{
use super::util::*;
use crate::Greeter;
pub fn draw(greeter: &mut Greeter, f: &mut Frame<'_, TermionBackend<RawTerminal<io::Stdout>>>) -> Result<(u16, u16), Box<dyn Error>> {
pub fn draw(greeter: &mut Greeter, f: &mut Frame<'_, CrosstermBackend<io::Stdout>>) -> Result<(u16, u16), Box<dyn Error>> {
let size = f.size();
let width = greeter.width();