diff options
| -rw-r--r-- | Cargo.toml | 3 | ||||
| -rw-r--r-- | src/shared_state.rs | 4 | ||||
| -rw-r--r-- | src/state.rs | 1 | ||||
| -rw-r--r-- | src/wm_info_provider.rs | 10 | ||||
| -rw-r--r-- | src/wm_info_provider/mango.rs | 127 |
5 files changed, 144 insertions, 1 deletions
@@ -23,10 +23,11 @@ clap = { version = "4.3", default-features = false, features = ["derive", "std", libc = "0.2" [features] -default = ["river", "niri", "hyprland"] +default = ["river", "mango"] river = [] niri = [] hyprland = [] +mango = [] [profile.release] lto = "fat" diff --git a/src/shared_state.rs b/src/shared_state.rs index 015fa08..17752d7 100644 --- a/src/shared_state.rs +++ b/src/shared_state.rs @@ -36,4 +36,8 @@ impl SharedState { pub fn get_niri(&mut self) -> Option<&mut wm_info_provider::NiriInfoProvider> { self.downcast_provider() } + #[cfg(feature = "mango")] + pub fn get_mango(&mut self) -> Option<&mut wm_info_provider::MangoInfoProvider> { + self.downcast_provider() + } } diff --git a/src/state.rs b/src/state.rs index 263f38c..8b0d2a7 100644 --- a/src/state.rs +++ b/src/state.rs @@ -208,6 +208,7 @@ impl State { } pub fn tags_updated(&mut self, conn: &mut Connection<Self>, output: Option<WlOutput>) { + println!("tags-updated"); self.for_each_bar(output, |bar, ss| { bar.set_tags(ss.wm_info_provider.get_tags(&bar.output)); bar.frame(conn, ss); diff --git a/src/wm_info_provider.rs b/src/wm_info_provider.rs index 2beff89..d598234 100644 --- a/src/wm_info_provider.rs +++ b/src/wm_info_provider.rs @@ -27,6 +27,11 @@ mod niri; #[cfg(feature = "niri")] pub use niri::*; +#[cfg(feature = "mango")] +mod mango; +#[cfg(feature = "mango")] +pub use mango::*; + pub trait WmInfoProvider: Any { fn register(&self, _: &mut EventLoop) {} @@ -70,6 +75,11 @@ pub fn bind(conn: &mut Connection<State>, config: &WmConfig) -> Box<dyn WmInfoPr return Box::new(niri); } + #[cfg(feature = "mango")] + if let Some(mango) = MangoInfoProvider::new() { + return Box::new(mango); + } + Box::new(DummyInfoProvider) } diff --git a/src/wm_info_provider/mango.rs b/src/wm_info_provider/mango.rs new file mode 100644 index 0000000..82b511c --- /dev/null +++ b/src/wm_info_provider/mango.rs @@ -0,0 +1,127 @@ +#![allow(clippy::collapsible_else_if)] + +use std::io; +use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd}; +use std::os::unix::net::UnixStream; +use std::process::{Command, Stdio}; + +use super::*; +use crate::event_loop; +use crate::utils::read_to_vec; + +pub struct MangoInfoProvider { + mmsg_sock: UnixStream, + mmsg_buf: Vec<u8>, + all_tags: Vec<MangoTagsContainer>, +} + +#[derive(serde::Deserialize, Debug, Clone)] +struct MangoTag { + index: u32, + is_active: bool, + is_urgent: bool, + client_count: u32, +} + +#[derive(serde::Deserialize, Debug, Clone)] +struct MangoTagsContainer { + monitor: String, + tags: Vec<MangoTag>, +} + +#[derive(serde::Deserialize, Debug)] +struct MangoIpc { + all_tags: Vec<MangoTagsContainer>, +} + +impl MangoInfoProvider { + pub fn new() -> Option<Self> { + let child = Command::new("/usr/bin/mmsg") + .arg("watch") + .arg("all-tags") + .stdout(Stdio::piped()) + .spawn().ok()?; + let stdout = child.stdout.expect("couldn't get stdout"); + let sock = unsafe { UnixStream::from_raw_fd(stdout.into_raw_fd()) }; + sock.set_nonblocking(true).ok()?; + Some(Self { + mmsg_sock: sock, + mmsg_buf: Vec::new(), + all_tags: Vec::new(), + }) + } + fn next_event(&mut self) -> io::Result<MangoIpc> { + loop { + if let Some(i) = memchr::memchr(b'\n', &self.mmsg_buf) { + let event = String::from_utf8_lossy(&self.mmsg_buf[..i]).into_owned(); + self.mmsg_buf.drain(..=i); + return Ok(serde_json::from_str(&event)?); + } + if read_to_vec(&self.mmsg_sock, &mut self.mmsg_buf)? == 0 { + return Err(io::Error::new( + io::ErrorKind::BrokenPipe, + "mmsg child process disconnected", + )); + } + } + } +} + +impl WmInfoProvider for MangoInfoProvider { + fn register(&self, ev: &mut EventLoop) { + ev.register_with_fd(self.mmsg_sock.as_raw_fd(), |ctx| { + loop { + match ctx.state.shared_state.get_mango().unwrap().next_event() { + Ok(event) => { + ctx.state.shared_state.get_mango().unwrap().all_tags = event.all_tags; + ctx.state.tags_updated(ctx.conn, None); + } + Err(e) if e.kind() == io::ErrorKind::WouldBlock => break, + Err(e) => { + ctx.state.set_error(ctx.conn, "mango", e); + return Ok(event_loop::Action::Unregister); + } + } + } + Ok(event_loop::Action::Keep) + }); + } + fn get_tags(&self, output: &Output) -> Vec<Tag> { + self.all_tags + .iter() + .find(|c| c.monitor == output.name) + .map_or_else( + || { + eprintln!("Couldn't find tags for monitor {}", output.name); + Vec::new() + }, + |container| { + container + .tags + .iter() + .enumerate() + .map(|(i, mango_tag)| Tag { + id: mango_tag.index, + name: (i + 1).to_string(), + is_focused: mango_tag.is_active, + is_active: mango_tag.client_count > 0, + is_urgent: mango_tag.is_urgent, + }) + .collect() + }, + ) + } + fn get_mode_name(&self, _: &Output) -> Option<String> { + None + } + + fn click_on_tag( + &mut self, + _conn: &mut Connection<State>, + _output: &Output, + _seat: WlSeat, + _tag_id: Option<u32>, + _btn: PointerBtn, + ) { + } +} |
