146 lines
4.6 KiB
Rust
146 lines
4.6 KiB
Rust
use cpal::{traits::{DeviceTrait, HostTrait, StreamTrait}, SampleFormat};
|
|
use futures_util::stream::StreamExt;
|
|
use std::collections::HashSet;
|
|
use swayipc_async::{Connection, Event, EventType, WindowChange};
|
|
|
|
#[tokio::main(flavor = "current_thread")]
|
|
async fn main() {
|
|
let sys_dbus = Box::leak(Box::new(zbus::Connection::system().await.unwrap()));
|
|
let _ses_dbus = Box::leak(Box::new(zbus::Connection::session().await.unwrap()));
|
|
|
|
let mut handlers = Vec::<Box<dyn SwayIpcHandler>>::new();
|
|
let mut panic = true;
|
|
for args in std::env::args().skip(1) {
|
|
handlers.push(match args.as_str() {
|
|
"system76-scheduler" => Box::new(System76::new(sys_dbus).await),
|
|
"empty-sound" => {
|
|
panic = false;
|
|
tokio::spawn(async {
|
|
play_empty_sound().await;
|
|
});
|
|
continue;
|
|
}
|
|
_ => panic!("handler not supported"),
|
|
})
|
|
}
|
|
if handlers.is_empty() {
|
|
if panic {
|
|
panic!("no handlers set up");
|
|
} else {
|
|
futures_util::future::pending::<()>().await;
|
|
}
|
|
}
|
|
|
|
let mut subs = HashSet::new();
|
|
for handler in &mut handlers {
|
|
handler.register(&mut subs);
|
|
}
|
|
let subs = subs.into_iter().collect::<Vec<_>>();
|
|
loop {
|
|
drop(start(&subs, &mut handlers).await);
|
|
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
|
}
|
|
}
|
|
|
|
async fn start(
|
|
subs: &[EventType],
|
|
handlers: &mut [Box<dyn SwayIpcHandler>],
|
|
) -> Result<(), swayipc_async::Error> {
|
|
let mut events = Connection::new().await?.subscribe(&subs).await?;
|
|
while let Some(event) = events.next().await {
|
|
match event {
|
|
Ok(event) => {
|
|
for handler in &mut *handlers {
|
|
handler.handle(&event);
|
|
}
|
|
}
|
|
Err(err) => match err {
|
|
swayipc_async::Error::Io(_)
|
|
| swayipc_async::Error::InvalidMagic(_)
|
|
| swayipc_async::Error::SubscriptionFailed(_) => return Err(err),
|
|
_ => {}
|
|
},
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
async fn play_empty_sound() {
|
|
let device = cpal::default_host()
|
|
.default_output_device()
|
|
.expect("no output device available");
|
|
let supported_config = device
|
|
.supported_output_configs()
|
|
.unwrap()
|
|
.find(|config| {
|
|
config.sample_format() == SampleFormat::F32
|
|
&& (config.min_sample_rate()..=config.max_sample_rate())
|
|
.contains(&cpal::SampleRate(44100))
|
|
&& config.channels() == 1
|
|
})
|
|
.unwrap()
|
|
.with_sample_rate(cpal::SampleRate(44100));
|
|
let config = supported_config.into();
|
|
let stream = Box::leak(Box::new(
|
|
device
|
|
.build_output_stream(
|
|
&config,
|
|
|data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
|
for sample in data.iter_mut() {
|
|
*sample = 1.0 / 1985.0;
|
|
}
|
|
},
|
|
|err| eprintln!("an error occurred on the output audio stream: {}", err),
|
|
None,
|
|
)
|
|
.unwrap(),
|
|
));
|
|
stream.play().unwrap();
|
|
futures_util::future::pending::<()>().await;
|
|
}
|
|
|
|
trait SwayIpcHandler {
|
|
fn register(&mut self, subs: &mut HashSet<EventType>);
|
|
fn handle(&mut self, event: &Event);
|
|
}
|
|
|
|
#[zbus::dbus_proxy(
|
|
interface = "com.system76.Scheduler",
|
|
default_service = "com.system76.Scheduler",
|
|
default_path = "/com/system76/Scheduler"
|
|
)]
|
|
pub trait System76Scheduler {
|
|
/// This process will have its process group prioritized over background processes
|
|
fn set_foreground_process(&mut self, pid: u32) -> zbus::fdo::Result<()>;
|
|
}
|
|
|
|
struct System76<'a> {
|
|
proxy: System76SchedulerProxy<'a>,
|
|
}
|
|
|
|
impl<'a> System76<'a> {
|
|
pub async fn new(dbus: &'a zbus::Connection) -> System76<'a> {
|
|
let proxy = System76SchedulerProxy::new(dbus).await.unwrap();
|
|
Self { proxy }
|
|
}
|
|
}
|
|
|
|
impl SwayIpcHandler for System76<'static> {
|
|
fn register(&mut self, subs: &mut HashSet<EventType>) {
|
|
subs.insert(EventType::Window);
|
|
}
|
|
fn handle(&mut self, event: &Event) {
|
|
if let Event::Window(window) = event {
|
|
if window.change != WindowChange::Focus {
|
|
return;
|
|
}
|
|
if let Some(pid) = window.container.pid.and_then(|x| u32::try_from(x).ok()) {
|
|
let mut proxy = self.proxy.clone();
|
|
tokio::spawn(async move {
|
|
drop(proxy.set_foreground_process(pid).await);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|