use std::{cmp, convert::TryFrom, io::Read, path::PathBuf, sync::Arc, thread, time::Duration};
use crossbeam_channel::{Receiver, Sender};
use diskit::Diskit;
use id3::Tag;
use legacytranslate::MessageHandler;
use nix::sys::sysinfo::sysinfo;
use signal_hook::consts::{SIGINT, SIGTERM, SIGUSR1, SIGUSR2};
use signal_hook::iterator::Signals;
use crate::{
commands::Command,
config::ArcConfig,
dbus::handle_mpris,
l10n::{
messages::{LogLevel, Message},
L10n,
},
unicode_reader::UnicodeReader,
};
fn input_handler<R>(
tx: &Sender<(Command, u32)>,
reader: fn() -> R,
l10n: L10n,
config: &Arc<ArcConfig>,
) where
R: Read,
{
l10n.write(Message::HelpNotice);
let mut summed = 0;
let mut current_num = 0;
#[allow(clippy::significant_drop_in_scrutinee)]
for c in UnicodeReader::new(reader())
{
let c = match c
{
Ok(c) => c,
Err(err) =>
{
summed += current_num;
current_num = 0;
l10n.write(Message::UnknownCommandBytes(err));
continue;
}
};
if ('0'..='9').contains(&c)
{
current_num *= 10;
current_num += (c as u8 - b'0') as u32;
continue;
}
summed += current_num;
current_num = 0;
if c == '?'
{
Command::show_help(l10n, config);
}
else if c == '\n'
{ }
else if let Some(Ok(command)) = config
.conffile
.command_characters
.iter()
.position(|&x| x == c)
.map(Command::try_from)
{
let _ = tx.send((command, cmp::max(summed + current_num, 1)));
summed = 0;
current_num = 0;
}
else
{
l10n.write(Message::UnknownCommandChar(c));
}
}
tx.send((Command::Quit, 1)).expect("Couldn't quit normally");
}
fn signal_handler(
tx: &Sender<(Command, u32)>,
mut signals: Signals,
config: &Arc<ArcConfig>,
l10n: L10n,
)
{
for sig in signals.forever()
{
l10n.write(Message::InSignalHandler(sig));
match sig
{
SIGINT | SIGTERM => tx.send((Command::Quit, 1)).unwrap(),
SIGUSR1 => config
.reading_paused
.store(true, std::sync::atomic::Ordering::SeqCst),
SIGUSR2 => config
.reading_paused
.store(false, std::sync::atomic::Ordering::SeqCst),
_ => l10n.write(Message::CaughtUnknownSignal),
}
}
}
fn mpris_handler<D>(
tx: &Sender<(Command, u32)>,
tx_control: &Sender<()>,
rx_paused: Receiver<bool>,
rx_path: Receiver<(PathBuf, Option<Tag>)>,
config: &Arc<ArcConfig>,
l10n: L10n,
diskit: D,
) where
D: Diskit + Send + 'static,
{
if config.conffile.enable_dbus
{
if let Err(e) = handle_mpris(tx, tx_control, rx_paused, rx_path, config, diskit)
{
l10n.write(Message::MprisHandlerError(e));
}
let _ = tx;
let _ = tx_control;
}
}
fn low_memory_handler(tx: &Sender<(Command, u32)>, config: &Arc<ArcConfig>, l10n: L10n)
{
let s = l10n.get(Message::MemoryTight);
loop
{
let not_enough_memory = sysinfo().map_or(true, |sysinfo| {
sysinfo.ram_unused() < config.conffile.minimum_ram
});
if not_enough_memory && !config.conffile.ignore_ram
{
let _ = tx.send((Command::Quit, 1));
l10n.write(Message::LiteralString(s, LogLevel::Error));
thread::sleep(Duration::from_secs(10));
break;
}
thread::sleep(Duration::from_millis(10));
}
}
#[allow(clippy::too_many_arguments)]
#[allow(clippy::module_name_repetitions)]
pub fn start_threads<D, R>(
tx: Sender<(Command, u32)>,
tx_control: Sender<()>,
rx_paused: Receiver<bool>,
rx_path: Receiver<(PathBuf, Option<Tag>)>,
signals: Signals,
reader: fn() -> R,
config: Arc<ArcConfig>,
l10n: L10n,
diskit: D,
) where
D: Diskit + Send + 'static,
R: Read + 'static,
{
let tx1 = tx.clone();
let tx2 = tx.clone();
let tx3 = tx.clone();
let tx4 = tx;
let config1 = config.clone();
let config2 = config.clone();
let config3 = config.clone();
let config4 = config;
let _ = thread::spawn(move || input_handler(&tx1, reader, l10n, &config4));
let _ = thread::spawn(move || signal_handler(&tx2, signals, &config1, l10n));
let _ = thread::spawn(move || {
mpris_handler(
&tx3,
&tx_control,
rx_paused,
rx_path,
&config2,
l10n,
diskit,
);
});
let _ = thread::spawn(move || low_memory_handler(&tx4, &config3, l10n));
}