Skip to content

Commit

Permalink
Refactored event handling for power commands and exit signal.
Browse files Browse the repository at this point in the history
  • Loading branch information
apognu committed Nov 11, 2023
1 parent 551a08a commit f54c171
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 51 deletions.
46 changes: 31 additions & 15 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Event>,
tx: mpsc::Sender<Event>,
}

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<Event> {
self.rx.recv().await
}

pub fn sender(&self) -> Sender<Event> {
self.tx.clone()
}
}
12 changes: 5 additions & 7 deletions src/greeter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -95,6 +95,7 @@ pub struct Greeter {
pub config: Option<Matches>,
pub socket: String,
pub stream: Option<Arc<RwLock<UnixStream>>>,
pub events: Option<Sender<Event>>,

// Current mode of the application, will define what actions are permitted.
pub mode: Mode,
Expand Down Expand Up @@ -148,10 +149,6 @@ pub struct Greeter {

// Menu for power options.
pub powers: Menu<Power>,
// Power command that was selected.
pub power_command: Option<Command>,
// Channel to notify of a power command selection.
pub power_command_notify: Arc<Notify>,
// Whether to prefix the power commands with `setsid`.
pub power_setsid: bool,

Expand All @@ -170,9 +167,10 @@ impl Drop for Greeter {
}

impl Greeter {
pub async fn new() -> Self {
pub async fn new(events: Sender<Event>) -> Self {
let mut greeter = Self::default();

greeter.events = Some(events);
greeter.set_locale();

greeter.powers = Menu {
Expand Down
2 changes: 1 addition & 1 deletion src/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<User> {
Expand Down
5 changes: 4 additions & 1 deletion src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down
8 changes: 5 additions & 3 deletions src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, 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
Expand Down Expand Up @@ -259,7 +261,7 @@ pub async fn handle(greeter: Arc<RwLock<Greeter>>, 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;
Expand Down
32 changes: 12 additions & 20 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ async fn main() {
}

async fn run() -> Result<(), Box<dyn Error>> {
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();
Expand All @@ -51,7 +52,6 @@ async fn run() -> Result<(), Box<dyn Error>> {

terminal.clear()?;

let mut events = Events::new().await;
let ipc = Ipc::new();

if greeter.remember && !greeter.username.value.is_empty() {
Expand All @@ -75,23 +75,6 @@ async fn run() -> Result<(), Box<dyn Error>> {
}
});

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());
Expand All @@ -100,12 +83,21 @@ async fn run() -> Result<(), Box<dyn Error>> {
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,
Expand Down
9 changes: 5 additions & 4 deletions src/power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,

Expand Down Expand Up @@ -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;
}
}
}

Expand Down

0 comments on commit f54c171

Please sign in to comment.