use std::{
process::Command,
rc::Rc,
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use nix::{
libc,
sys::{signal, wait},
};
use structopt::StructOpt;
use xidlehook_core::{
modules::{StopAt, Xcb},
timers::CmdTimer,
Module, Xidlehook,
};
static EXITED: AtomicBool = AtomicBool::new(false);
extern "C" fn exit_handler(_signo: libc::c_int) {
EXITED.store(true, Ordering::SeqCst);
}
extern "C" fn sigchld_handler(_signo: libc::c_int) {
let _ = wait::waitpid(None, Some(wait::WaitPidFlag::WNOHANG));
}
#[derive(StructOpt, Debug)]
pub struct Opt {
#[structopt(long)]
pub once: bool,
#[structopt(long)]
pub not_when_fullscreen: bool,
#[cfg(feature = "pulse")]
#[structopt(long)]
pub not_when_audio: bool,
#[structopt(long, required = true, value_names = &["duration", "command", "canceller"])]
pub timer: Vec<String>,
}
fn main() -> xidlehook_core::Result<()> {
env_logger::init();
let opt = Opt::from_args();
let xcb = Rc::new(Xcb::new()?);
let mut timers = Vec::new();
let mut iter = opt.timer.iter().peekable();
while iter.peek().is_some() {
let duration: u64 = match iter.next().unwrap().parse() {
Ok(duration) => duration,
Err(err) => {
eprintln!("error: failed to parse duration as number: {}", err);
return Ok(());
},
};
timers.push(CmdTimer {
time: Duration::from_secs(duration),
activation: Some(command(iter.next().unwrap())),
abortion: iter.next().filter(|s| !s.is_empty()).map(|s| command(&s)),
..CmdTimer::default()
});
}
let mut modules: Vec<Box<dyn Module>> = Vec::new();
if opt.once {
modules.push(Box::new(StopAt::completion()));
}
if opt.not_when_fullscreen {
modules.push(Box::new(Rc::clone(&xcb).not_when_fullscreen()));
}
#[cfg(feature = "pulse")]
{
if opt.not_when_audio {
modules.push(Box::new(xidlehook_core::modules::NotWhenAudio::new()?))
}
}
let xidlehook = Xidlehook::new(timers).register(modules);
unsafe {
for &(signal, handler) in &[
(
signal::Signal::SIGINT,
exit_handler as extern "C" fn(libc::c_int),
),
(
signal::Signal::SIGCHLD,
sigchld_handler as extern "C" fn(libc::c_int),
),
] {
signal::sigaction(
signal,
&signal::SigAction::new(
signal::SigHandler::Handler(handler),
signal::SaFlags::empty(),
signal::SigSet::empty(),
),
)?;
}
}
xidlehook.main_sync(&xcb, || EXITED.load(Ordering::SeqCst))?;
Ok(())
}
fn command(cmd: &str) -> Command {
let mut command = Command::new("/bin/sh");
command.arg("-c").arg(cmd);
command
}