mirror of
https://github.com/apognu/tuigreet.git
synced 2024-09-11 07:25:29 +03:00
Refactored event handling for power commands and exit signal.
This commit is contained in:
parent
551a08af75
commit
45e98216c4
46
src/event.rs
46
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<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();
|
||||
|
||||
loop {
|
||||
let render = render_interval.tick();
|
||||
let event = stream.next().fuse();
|
||||
async move {
|
||||
let mut stream = EventStream::new();
|
||||
let mut render_interval = tokio::time::interval(Duration::from_secs_f64(1.0 / FRAME_RATE));
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = 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()
|
||||
}
|
||||
}
|
||||
|
@ -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<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,
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
@ -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> {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
@ -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;
|
||||
|
32
src/main.rs
32
src/main.rs
@ -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();
|
||||
@ -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() {
|
||||
@ -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());
|
||||
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user