Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored event handling for power commands and exit signal. #116

Merged
merged 1 commit into from
Nov 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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