From 198f374a18b5c8a0e21f7d41125393aa89523f5c Mon Sep 17 00:00:00 2001 From: cashmere Date: Tue, 21 Jul 2026 14:07:42 +0200 Subject: mango: show focused window next to tags, fix mmsg lookup, 0.15.x compat - new focused-window block ({icon} {app_id} - {title}) driven by the active_client field of mmsg watch all-monitors (mangowm >= 0.14) - resolve program icons via freedesktop icon themes/.desktop files (new icon module, cairo png feature, cached per app_id) - new show_focused_window config option (default true) - run mmsg from PATH instead of hardcoded /usr/bin/mmsg (broken on NixOS) - handle null active_client fields (no focused client) without failing to parse the whole event - drop stale TODO comment and unused variable in mango provider - add .justfile run recipe exporting MANGO_INSTANCE_SIGNATURE --- src/bar.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) (limited to 'src/bar.rs') diff --git a/src/bar.rs b/src/bar.rs index 237c46a..102978d 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -14,7 +14,7 @@ use crate::protocol::*; use crate::shared_state::SharedState; use crate::state::State; use crate::text::{self, ComputedText, RenderOptions}; -use crate::wm_info_provider::Tag; +use crate::wm_info_provider::{FocusedWindow, Tag}; pub struct Bar { pub output: Output, @@ -33,10 +33,12 @@ pub struct Bar { tags: Vec, layout_name: Option, mode_name: Option, + focused_window: Option, tags_btns: ButtonManager, tags_computed: Vec<(u32, ColorPair, ComputedText)>, layout_name_computed: Option, mode_computed: Option, + focused_window_computed: Option, } #[derive(Debug, PartialEq)] @@ -79,10 +81,12 @@ impl Bar { tags: Vec::new(), layout_name: None, mode_name: None, + focused_window: None, tags_btns: Default::default(), tags_computed: Vec::new(), layout_name_computed: None, mode_computed: None, + focused_window_computed: None, } } @@ -112,6 +116,11 @@ impl Bar { self.mode_computed = None; } + pub fn set_focused_window(&mut self, focused_window: Option) { + self.focused_window = focused_window; + self.focused_window_computed = None; + } + pub fn click( &mut self, conn: &mut Connection, @@ -287,6 +296,59 @@ impl Bar { } } + // Display focused window + if ss.config.show_focused_window + && let Some(focused_window) = &self.focused_window + { + if let Some(icon) = ss.icon_cache.get(&focused_window.app_id, self.height) { + let icon_size = height_f - 4.0; + let scale = icon_size / (icon.width() as f64).max(icon.height() as f64); + let icon_w = icon.width() as f64 * scale; + let icon_h = icon.height() as f64 * scale; + cairo_ctx.save().unwrap(); + cairo_ctx.translate( + offset_left + (icon_size - icon_w) / 2.0, + (height_f - icon_h) / 2.0, + ); + cairo_ctx.scale(scale, scale); + cairo_ctx.set_source_surface(icon, 0.0, 0.0).unwrap(); + cairo_ctx.paint().unwrap(); + cairo_ctx.restore().unwrap(); + offset_left += icon_size + 5.0; + } + let text = self.focused_window_computed.get_or_insert_with(|| { + let label = if focused_window.title.is_empty() { + focused_window.app_id.clone() + } else { + format!("{} - {}", focused_window.app_id, focused_window.title) + }; + ComputedText::new( + &label, + text::Attributes { + font: &ss.config.font, + padding_left: 10.0, + padding_right: 10.0, + min_width: None, + align: Default::default(), + markup: false, + }, + ) + }); + text.render( + &cairo_ctx, + RenderOptions { + x_offset: offset_left, + bar_height: height_f, + fg_color: ss.config.tag_inactive_fg, + bg_color: None, + r_left: 0.0, + r_right: 0.0, + overlap: 0.0, + }, + ); + offset_left += text.width; + } + // Display mode if ss.config.show_mode { if let Some(mode) = &self.mode_name { -- cgit v1.3.1