mirror of
https://github.com/apognu/tuigreet.git
synced 2024-10-27 00:53:45 +03:00
Miscellaneous refactoring.
This commit is contained in:
parent
b178fa318b
commit
b4e587a903
@ -12,7 +12,10 @@ use getopts::{Matches, Options};
|
||||
use i18n_embed::DesktopLanguageRequester;
|
||||
use tokio::{
|
||||
net::UnixStream,
|
||||
sync::{RwLock, RwLockWriteGuard},
|
||||
sync::{
|
||||
oneshot::{Receiver, Sender},
|
||||
RwLock, RwLockWriteGuard,
|
||||
},
|
||||
};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
@ -81,8 +84,8 @@ pub struct Greeter {
|
||||
|
||||
pub working: bool,
|
||||
pub done: bool,
|
||||
#[default(Ok(()))]
|
||||
pub exit: Result<(), AuthStatus>,
|
||||
pub exit_tx: Option<Sender<AuthStatus>>,
|
||||
pub exit_rx: Option<Receiver<AuthStatus>>,
|
||||
}
|
||||
|
||||
impl Drop for Greeter {
|
||||
@ -117,6 +120,10 @@ 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
|
||||
}
|
||||
|
179
src/ipc.rs
179
src/ipc.rs
@ -8,105 +8,124 @@ use tokio::sync::{
|
||||
|
||||
use crate::{info::write_last_username, AuthStatus, Greeter, Mode};
|
||||
|
||||
type IpcChannel = (Arc<Mutex<Receiver<Request>>>, Arc<Mutex<Sender<Request>>>);
|
||||
#[derive(Clone)]
|
||||
pub struct Ipc(Arc<IpcHandle>);
|
||||
|
||||
pub fn new_ipc() -> IpcChannel {
|
||||
let (net_tx, net_rx) = tokio::sync::mpsc::channel::<Request>(10);
|
||||
|
||||
(Arc::new(Mutex::new(net_rx)), Arc::new(Mutex::new(net_tx)))
|
||||
pub struct IpcHandle {
|
||||
tx: RwLock<Sender<Request>>,
|
||||
rx: Mutex<Receiver<Request>>,
|
||||
}
|
||||
|
||||
pub async fn handle(greeter: Arc<RwLock<Greeter>>, net_tx: Arc<Mutex<Sender<Request>>>, net_rx: Arc<Mutex<Receiver<Request>>>) -> Result<(), Box<dyn Error>> {
|
||||
let request = net_rx.lock().await.recv().await;
|
||||
impl Ipc {
|
||||
pub fn new() -> Ipc {
|
||||
let (tx, rx) = tokio::sync::mpsc::channel::<Request>(10);
|
||||
|
||||
if let Some(request) = request {
|
||||
let stream = {
|
||||
let greeter = greeter.read().await;
|
||||
|
||||
greeter.stream.as_ref().unwrap().clone()
|
||||
};
|
||||
|
||||
let response = {
|
||||
request.write_to(&mut *stream.write().await).await?;
|
||||
|
||||
Response::read_from(&mut *stream.write().await).await?
|
||||
};
|
||||
|
||||
parse_response(&mut *greeter.write().await, response, net_tx).await?;
|
||||
Ipc(Arc::new(IpcHandle {
|
||||
tx: RwLock::new(tx),
|
||||
rx: Mutex::new(rx),
|
||||
}))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub async fn send(&self, request: Request) {
|
||||
let _ = self.0.tx.read().await.send(request).await;
|
||||
}
|
||||
|
||||
async fn parse_response(mut greeter: &mut Greeter, response: Response, net_tx: Arc<Mutex<Sender<Request>>>) -> Result<(), Box<dyn Error>> {
|
||||
match response {
|
||||
Response::AuthMessage { auth_message_type, auth_message } => match auth_message_type {
|
||||
AuthMessageType::Secret => {
|
||||
greeter.mode = Mode::Password;
|
||||
greeter.working = false;
|
||||
greeter.secret = true;
|
||||
greeter.set_prompt(&auth_message);
|
||||
}
|
||||
pub async fn next(&mut self) -> Option<Request> {
|
||||
self.0.rx.lock().await.recv().await
|
||||
}
|
||||
|
||||
AuthMessageType::Visible => {
|
||||
greeter.mode = Mode::Password;
|
||||
greeter.working = false;
|
||||
greeter.secret = false;
|
||||
greeter.set_prompt(&auth_message);
|
||||
}
|
||||
pub async fn handle(&mut self, greeter: Arc<RwLock<Greeter>>) -> Result<(), Box<dyn Error>> {
|
||||
let request = self.next().await;
|
||||
|
||||
AuthMessageType::Error => greeter.message = Some(auth_message),
|
||||
if let Some(request) = request {
|
||||
let stream = {
|
||||
let greeter = greeter.read().await;
|
||||
|
||||
AuthMessageType::Info => {
|
||||
if let Some(message) = &mut greeter.message {
|
||||
message.push_str(&auth_message.trim_end());
|
||||
message.push('\n');
|
||||
} else {
|
||||
greeter.message = Some(auth_message.trim_end().to_string());
|
||||
greeter.stream.as_ref().unwrap().clone()
|
||||
};
|
||||
|
||||
let response = {
|
||||
request.write_to(&mut *stream.write().await).await?;
|
||||
|
||||
Response::read_from(&mut *stream.write().await).await?
|
||||
};
|
||||
|
||||
self.parse_response(&mut *greeter.write().await, response).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn parse_response(&mut self, mut greeter: &mut Greeter, response: Response) -> Result<(), Box<dyn Error>> {
|
||||
match response {
|
||||
Response::AuthMessage { auth_message_type, auth_message } => match auth_message_type {
|
||||
AuthMessageType::Secret => {
|
||||
greeter.mode = Mode::Password;
|
||||
greeter.working = false;
|
||||
greeter.secret = true;
|
||||
greeter.set_prompt(&auth_message);
|
||||
}
|
||||
|
||||
AuthMessageType::Visible => {
|
||||
greeter.mode = Mode::Password;
|
||||
greeter.working = false;
|
||||
greeter.secret = false;
|
||||
greeter.set_prompt(&auth_message);
|
||||
}
|
||||
|
||||
AuthMessageType::Error => greeter.message = Some(auth_message),
|
||||
|
||||
AuthMessageType::Info => {
|
||||
if let Some(message) = &mut greeter.message {
|
||||
message.push_str(&auth_message.trim_end());
|
||||
message.push('\n');
|
||||
} else {
|
||||
greeter.message = Some(auth_message.trim_end().to_string());
|
||||
|
||||
if let Some(message) = &mut greeter.message {
|
||||
message.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
self.send(Request::PostAuthMessageResponse { response: None }).await;
|
||||
}
|
||||
},
|
||||
|
||||
Response::Success => {
|
||||
if greeter.done {
|
||||
if greeter.remember {
|
||||
write_last_username(&greeter.username);
|
||||
}
|
||||
|
||||
crate::exit(&mut greeter, AuthStatus::Success).await;
|
||||
} else if let Some(command) = &greeter.command {
|
||||
greeter.done = true;
|
||||
|
||||
self.send(Request::StartSession { cmd: vec![command.clone()] }).await;
|
||||
}
|
||||
}
|
||||
|
||||
Response::Error { error_type, description } => {
|
||||
Ipc::cancel(&mut greeter).await;
|
||||
|
||||
match error_type {
|
||||
ErrorType::AuthError => {
|
||||
greeter.message = Some(fl!("failed"));
|
||||
}
|
||||
|
||||
ErrorType::Error => {
|
||||
greeter.message = Some(description);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = net_tx.lock().await.send(Request::PostAuthMessageResponse { response: None }).await;
|
||||
}
|
||||
},
|
||||
|
||||
Response::Success => {
|
||||
if greeter.done {
|
||||
if greeter.remember {
|
||||
write_last_username(&greeter.username);
|
||||
}
|
||||
|
||||
crate::exit(&mut greeter, AuthStatus::Success).await;
|
||||
} else if let Some(command) = &greeter.command {
|
||||
greeter.done = true;
|
||||
|
||||
let _ = net_tx.lock().await.send(Request::StartSession { cmd: vec![command.clone()] }).await;
|
||||
greeter.reset().await;
|
||||
}
|
||||
}
|
||||
|
||||
Response::Error { error_type, description } => {
|
||||
cancel(&mut greeter).await;
|
||||
|
||||
match error_type {
|
||||
ErrorType::AuthError => {
|
||||
greeter.message = Some(fl!("failed"));
|
||||
}
|
||||
|
||||
ErrorType::Error => {
|
||||
greeter.message = Some(description);
|
||||
}
|
||||
}
|
||||
|
||||
greeter.reset().await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cancel(greeter: &mut Greeter) {
|
||||
let _ = Request::CancelSession.write_to(&mut *greeter.stream().await).await;
|
||||
pub async fn cancel(greeter: &mut Greeter) {
|
||||
let _ = Request::CancelSession.write_to(&mut *greeter.stream().await).await;
|
||||
}
|
||||
}
|
||||
|
@ -3,23 +3,23 @@ use std::{error::Error, sync::Arc};
|
||||
use greetd_ipc::Request;
|
||||
use system_shutdown::{reboot, shutdown};
|
||||
use termion::event::Key;
|
||||
use tokio::sync::{mpsc::Sender, Mutex, RwLock};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{
|
||||
event::{Event, Events},
|
||||
info::write_last_session,
|
||||
ipc::cancel,
|
||||
ipc::Ipc,
|
||||
ui::{PowerOption, POWER_OPTIONS},
|
||||
Greeter, Mode,
|
||||
};
|
||||
|
||||
pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, net_tx: Arc<Mutex<Sender<Request>>>) -> Result<(), Box<dyn Error>> {
|
||||
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 {
|
||||
let mut greeter = greeter.write().await;
|
||||
|
||||
match input {
|
||||
Key::Esc => {
|
||||
cancel(&mut greeter).await;
|
||||
Ipc::cancel(&mut greeter).await;
|
||||
greeter.reset().await;
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, net_tx:
|
||||
greeter.working = true;
|
||||
greeter.message = None;
|
||||
|
||||
let _ = net_tx.lock().await.send(Request::CreateSession { username: greeter.username.clone() }).await;
|
||||
ipc.send(Request::CreateSession { username: greeter.username.clone() }).await;
|
||||
greeter.answer = String::new();
|
||||
}
|
||||
|
||||
@ -108,9 +108,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, net_tx:
|
||||
greeter.working = true;
|
||||
greeter.message = None;
|
||||
|
||||
let _ = net_tx
|
||||
.lock()
|
||||
.await
|
||||
ipc
|
||||
.send(Request::PostAuthMessageResponse {
|
||||
response: Some(greeter.answer.clone()),
|
||||
})
|
||||
@ -172,7 +170,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, events: &mut Events, net_tx:
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
Key::Ctrl('x') => {
|
||||
use crate::config::AuthStatus;
|
||||
use crate::greeter::AuthStatus;
|
||||
|
||||
crate::exit(&mut greeter, AuthStatus::Cancel).await;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
macro_rules! fl {
|
||||
($message_id:literal) => {{
|
||||
i18n_embed_fl::fl!($crate::MESSAGES, $message_id)
|
||||
i18n_embed_fl::fl!($crate::ui::MESSAGES, $message_id)
|
||||
}};
|
||||
|
||||
($message_id:literal, $($args:expr),*) => {{
|
||||
i18n_embed_fl::fl!($crate::MESSAGES, $message_id, $($args), *)
|
||||
i18n_embed_fl::fl!($crate::ui::MESSAGES, $message_id, $($args), *)
|
||||
}};
|
||||
}
|
||||
|
51
src/main.rs
51
src/main.rs
@ -4,8 +4,8 @@ extern crate smart_default;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod config;
|
||||
mod event;
|
||||
mod greeter;
|
||||
mod info;
|
||||
mod ipc;
|
||||
mod keyboard;
|
||||
@ -14,34 +14,12 @@ mod ui;
|
||||
use std::{error::Error, io, process, sync::Arc};
|
||||
|
||||
use greetd_ipc::Request;
|
||||
use i18n_embed::{
|
||||
fluent::{fluent_language_loader, FluentLanguageLoader},
|
||||
DesktopLanguageRequester, LanguageLoader,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use rust_embed::RustEmbed;
|
||||
use termion::raw::IntoRawMode;
|
||||
use tokio::sync::RwLock;
|
||||
use tui::{backend::TermionBackend, Terminal};
|
||||
|
||||
pub use self::config::*;
|
||||
use self::{event::Events, ipc::new_ipc};
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "contrib/locales"]
|
||||
struct Localizations;
|
||||
|
||||
lazy_static! {
|
||||
static ref MESSAGES: FluentLanguageLoader = {
|
||||
let locales = Localizations;
|
||||
let loader = fluent_language_loader!();
|
||||
loader.load_languages(&locales, &[loader.fallback_language()]).unwrap();
|
||||
|
||||
let _ = i18n_embed::select(&loader, &locales, &DesktopLanguageRequester::requested_languages());
|
||||
|
||||
loader
|
||||
};
|
||||
}
|
||||
pub use self::greeter::*;
|
||||
use self::{event::Events, ipc::Ipc};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@ -64,43 +42,48 @@ async fn run() -> Result<(), Box<dyn Error>> {
|
||||
terminal.clear()?;
|
||||
|
||||
let mut events = Events::new().await;
|
||||
|
||||
let (ipc_rx, ipc_tx) = new_ipc();
|
||||
let ipc = Ipc::new();
|
||||
|
||||
if greeter.remember && !greeter.username.is_empty() {
|
||||
let _ = ipc_tx.lock().await.send(Request::CreateSession { username: greeter.username.clone() }).await;
|
||||
ipc.send(Request::CreateSession { username: greeter.username.clone() }).await;
|
||||
}
|
||||
|
||||
let greeter = Arc::new(RwLock::new(greeter));
|
||||
|
||||
tokio::task::spawn({
|
||||
let greeter = greeter.clone();
|
||||
let (ipc_rx, ipc_tx) = (ipc_rx.clone(), ipc_tx.clone());
|
||||
let mut ipc = ipc.clone();
|
||||
|
||||
async move {
|
||||
loop {
|
||||
let _ = ipc::handle(greeter.clone(), ipc_tx.clone(), ipc_rx.clone()).await;
|
||||
let _ = ipc.handle(greeter.clone()).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
greeter.read().await.exit?;
|
||||
if let Some(ref mut rx) = greeter.write().await.exit_rx {
|
||||
if let Ok(status) = rx.try_recv() {
|
||||
return Err(status.into());
|
||||
}
|
||||
}
|
||||
|
||||
ui::draw(greeter.clone(), &mut terminal).await?;
|
||||
keyboard::handle(greeter.clone(), &mut events, ipc_tx.clone()).await?;
|
||||
keyboard::handle(greeter.clone(), &mut events, ipc.clone()).await?;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn exit(mut greeter: &mut Greeter, status: AuthStatus) {
|
||||
match status {
|
||||
AuthStatus::Success => {}
|
||||
AuthStatus::Cancel | AuthStatus::Failure => ipc::cancel(&mut greeter).await,
|
||||
AuthStatus::Cancel | AuthStatus::Failure => Ipc::cancel(&mut greeter).await,
|
||||
}
|
||||
|
||||
clear_screen();
|
||||
|
||||
greeter.exit = Err(status);
|
||||
if let Some(tx) = greeter.exit_tx.take() {
|
||||
let _ = tx.send(status);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_screen() {
|
||||
|
22
src/ui/i18n.rs
Normal file
22
src/ui/i18n.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use i18n_embed::{
|
||||
fluent::{fluent_language_loader, FluentLanguageLoader},
|
||||
DesktopLanguageRequester, LanguageLoader,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "contrib/locales"]
|
||||
struct Localizations;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref MESSAGES: FluentLanguageLoader = {
|
||||
let locales = Localizations;
|
||||
let loader = fluent_language_loader!();
|
||||
loader.load_languages(&locales, &[loader.fallback_language()]).unwrap();
|
||||
|
||||
let _ = i18n_embed::select(&loader, &locales, &DesktopLanguageRequester::requested_languages());
|
||||
|
||||
loader
|
||||
};
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
mod command;
|
||||
mod i18n;
|
||||
mod power;
|
||||
mod prompt;
|
||||
mod sessions;
|
||||
@ -24,7 +25,10 @@ use tui::{
|
||||
|
||||
use crate::{info::capslock_status, ui::util::titleize, Greeter, Mode};
|
||||
|
||||
pub use self::power::{Option as PowerOption, OPTIONS as POWER_OPTIONS};
|
||||
pub use self::{
|
||||
i18n::MESSAGES,
|
||||
power::{Option as PowerOption, OPTIONS as POWER_OPTIONS},
|
||||
};
|
||||
|
||||
const TITLEBAR_INDEX: usize = 1;
|
||||
const STATUSBAR_INDEX: usize = 3;
|
||||
|
Loading…
Reference in New Issue
Block a user