diff --git a/src/event.rs b/src/event.rs index 9d6782a..74f4138 100644 --- a/src/event.rs +++ b/src/event.rs @@ -2,48 +2,64 @@ use std::time::Duration; use crossterm::event::{Event as TermEvent, EventStream, KeyEvent}; use futures::{future::FutureExt, StreamExt}; -use tokio::sync::mpsc; +use tokio::{ + process::Command, + sync::mpsc::{self, Sender}, +}; + +use crate::AuthStatus; const FRAME_RATE: f64 = 2.0; pub enum Event { Key(KeyEvent), Render, + PowerCommand(Command), + Exit(AuthStatus), } pub struct Events { rx: mpsc::Receiver, + tx: mpsc::Sender, } impl Events { pub async fn new() -> Events { let (tx, rx) = mpsc::channel(10); - tokio::task::spawn(async move { - let mut stream = EventStream::new(); - let mut render_interval = tokio::time::interval(Duration::from_secs_f64(1.0 / FRAME_RATE)); + tokio::task::spawn({ + let tx = tx.clone(); + + async move { + let mut stream = EventStream::new(); + let mut render_interval = tokio::time::interval(Duration::from_secs_f64(1.0 / FRAME_RATE)); - loop { - let render = render_interval.tick(); - let event = stream.next().fuse(); + loop { + let render = render_interval.tick(); + let event = stream.next().fuse(); - tokio::select! { - event = event => { - if let Some(Ok(TermEvent::Key(event))) = event { - let _ = tx.send(Event::Key(event)).await; - let _ = tx.send(Event::Render).await; + tokio::select! { + event = event => { + if let Some(Ok(TermEvent::Key(event))) = event { + let _ = tx.send(Event::Key(event)).await; + let _ = tx.send(Event::Render).await; + } } - } - _ = render => { let _ = tx.send(Event::Render).await; }, + _ = render => { let _ = tx.send(Event::Render).await; }, + } } } }); - Events { rx } + Events { rx, tx } } pub async fn next(&mut self) -> Option { self.rx.recv().await } + + pub fn sender(&self) -> Sender { + self.tx.clone() + } } diff --git a/src/greeter.rs b/src/greeter.rs index 175c23f..84d0330 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -16,12 +16,12 @@ use getopts::{Matches, Options}; use i18n_embed::DesktopLanguageRequester; use tokio::{ net::UnixStream, - process::Command, - sync::{Notify, RwLock, RwLockWriteGuard}, + sync::{mpsc::Sender, RwLock, RwLockWriteGuard}, }; use zeroize::Zeroize; use crate::{ + event::Event, info::{ get_issue, get_last_session, get_last_session_path, get_last_user_name, get_last_user_session, get_last_user_session_path, get_last_user_username, get_min_max_uids, get_sessions, get_users, }, @@ -95,6 +95,7 @@ pub struct Greeter { pub config: Option, pub socket: String, pub stream: Option>>, + pub events: Option>, // Current mode of the application, will define what actions are permitted. pub mode: Mode, @@ -148,10 +149,6 @@ pub struct Greeter { // Menu for power options. pub powers: Menu, - // Power command that was selected. - pub power_command: Option, - // Channel to notify of a power command selection. - pub power_command_notify: Arc, // Whether to prefix the power commands with `setsid`. pub power_setsid: bool, @@ -170,9 +167,10 @@ impl Drop for Greeter { } impl Greeter { - pub async fn new() -> Self { + pub async fn new(events: Sender) -> Self { let mut greeter = Self::default(); + greeter.events = Some(events); greeter.set_locale(); greeter.powers = Menu { diff --git a/src/info.rs b/src/info.rs index 1e81433..f257156 100644 --- a/src/info.rs +++ b/src/info.rs @@ -159,7 +159,7 @@ pub fn delete_last_user_session_path(username: &str) { } pub fn delete_last_user_session(username: &str) { - let _ = fs::remove_file(&format!("{LAST_SESSION}-{username}")); + let _ = fs::remove_file(format!("{LAST_SESSION}-{username}")); } pub fn get_users(min_uid: u16, max_uid: u16) -> Vec { diff --git a/src/ipc.rs b/src/ipc.rs index a75fc72..e542fff 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -7,6 +7,7 @@ use tokio::sync::{ }; use crate::{ + event::Event, info::{delete_last_user_session, delete_last_user_session_path, write_last_user_session, write_last_user_session_path, write_last_username}, ui::sessions::{Session, SessionSource, SessionType}, AuthStatus, Greeter, Mode, @@ -125,7 +126,9 @@ impl Ipc { } } - crate::exit(greeter, AuthStatus::Success).await; + if let Some(ref sender) = greeter.events { + let _ = sender.send(Event::Exit(AuthStatus::Success)).await; + } } else { let command = greeter.session_source.command(greeter).map(str::to_string); diff --git a/src/keyboard.rs b/src/keyboard.rs index ddb7efe..f9603dc 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -45,9 +45,11 @@ pub async fn handle(greeter: Arc>, input: KeyEvent, ipc: Ipc) -> modifiers: KeyModifiers::CONTROL, .. } => { - use crate::greeter::AuthStatus; + use crate::{AuthStatus, Event}; - crate::exit(&mut greeter, AuthStatus::Cancel).await; + if let Some(ref sender) = greeter.events { + let _ = sender.send(Event::Exit(AuthStatus::Cancel)).await; + } } // Depending on the active screen, pressing Escape will either return to the @@ -259,7 +261,7 @@ pub async fn handle(greeter: Arc>, input: KeyEvent, ipc: Ipc) -> let power_command = greeter.powers.options.get(greeter.powers.selected).cloned(); if let Some(command) = power_command { - power(&mut greeter, command.action); + power(&mut greeter, command.action).await; } greeter.mode = greeter.previous_mode; diff --git a/src/main.rs b/src/main.rs index 98810be..d6aa1c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,7 +38,8 @@ async fn main() { } async fn run() -> Result<(), Box> { - let greeter = Greeter::new().await; + let mut events = Events::new().await; + let greeter = Greeter::new(events.sender()).await; let mut stdout = io::stdout(); register_panic_handler(); @@ -51,7 +52,6 @@ async fn run() -> Result<(), Box> { terminal.clear()?; - let mut events = Events::new().await; let ipc = Ipc::new(); if greeter.remember && !greeter.username.value.is_empty() { @@ -75,23 +75,6 @@ async fn run() -> Result<(), Box> { } }); - tokio::task::spawn({ - let greeter = greeter.clone(); - let notify = greeter.read().await.power_command_notify.clone(); - - async move { - loop { - notify.notified().await; - - let command = greeter.write().await.power_command.take(); - - if let Some(command) = command { - power::run(&greeter, command).await; - } - } - } - }); - loop { if let Some(status) = greeter.read().await.exit { return Err(status.into()); @@ -100,12 +83,21 @@ async fn run() -> Result<(), Box> { match events.next().await { Some(Event::Render) => ui::draw(greeter.clone(), &mut terminal).await?, Some(Event::Key(key)) => keyboard::handle(greeter.clone(), key, ipc.clone()).await?, + + Some(Event::Exit(status)) => { + crate::exit(&mut *greeter.write().await, status).await; + } + + Some(Event::PowerCommand(command)) => { + power::run(&greeter, command).await; + } + _ => {} } } } -pub async fn exit(greeter: &mut Greeter, status: AuthStatus) { +async fn exit(greeter: &mut Greeter, status: AuthStatus) { match status { AuthStatus::Success => {} AuthStatus::Cancel | AuthStatus::Failure => Ipc::cancel(greeter).await, diff --git a/src/power.rs b/src/power.rs index beac722..58a7a95 100644 --- a/src/power.rs +++ b/src/power.rs @@ -2,7 +2,7 @@ use std::{process::Stdio, sync::Arc}; use tokio::{process::Command, sync::RwLock}; -use crate::{ui::power::Power, Greeter, Mode}; +use crate::{event::Event, ui::power::Power, Greeter, Mode}; #[derive(SmartDefault, Clone, Copy, PartialEq, Eq, Hash)] pub enum PowerOption { @@ -11,7 +11,7 @@ pub enum PowerOption { Reboot, } -pub fn power(greeter: &mut Greeter, option: PowerOption) { +pub async fn power(greeter: &mut Greeter, option: PowerOption) { let command = match greeter.powers.options.iter().find(|opt| opt.action == option) { None => None, @@ -54,8 +54,9 @@ pub fn power(greeter: &mut Greeter, option: PowerOption) { command.stdout(Stdio::null()); command.stderr(Stdio::null()); - greeter.power_command = Some(command); - greeter.power_command_notify.notify_one(); + if let Some(ref sender) = greeter.events { + let _ = sender.send(Event::PowerCommand(command)).await; + } } }