summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElden <elden.samuel.n@gmail.com>2026-06-15 21:39:05 +0700
committerElden <elden.samuel.n@gmail.com>2026-06-15 21:39:05 +0700
commit165fc5ebe5365ec53bc2e06f5b682acf2fceb452 (patch)
tree76d0e0f7084ee4650cc1228580c563038d68b98c
parent73446cac559b10adf4beb5567a816d1be5273457 (diff)
downloadi3bar-mango-165fc5ebe5365ec53bc2e06f5b682acf2fceb452.tar.gz
added mangowm support
-rw-r--r--Cargo.toml3
-rw-r--r--src/shared_state.rs4
-rw-r--r--src/state.rs1
-rw-r--r--src/wm_info_provider.rs10
-rw-r--r--src/wm_info_provider/mango.rs127
5 files changed, 144 insertions, 1 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 8c06541..45a389d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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,
+ ) {
+ }
+}