status bar
git clone https://git.lucas.co/cce-status-interface.git
refactor: remove the layout-mode menu and viewport plumbing
The menu's viewport-layout / apply-mode-sharing commands were already
no-op stubs compositor-side; with viewport tags removed it loses its
target vocabulary entirely. Gone: the layout-indicator click menu
(SetMode/ToggleApplyAll rows, apply_all/active_viewport menu state),
LayoutBounds plumbing through every StatusModule signature, the
viewport status subscription and field, and the
get_closest_viewport / get_active_viewport_from_camera helpers. The
mode text stays visible next to the focused title; modes remain
changeable via mode-next and ccectl mode.
Co-Authored-By: Claude Fable 5 <[email protected]>
CLAUDE.md | 22 +++----
src/cloud.rs | 5 --
src/listeners.rs | 1 -
src/main.rs | 179 ++++---------------------------------------------------
src/modules.rs | 19 +-----
5 files changed, 22 insertions(+), 204 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index 8621654..db80cd4 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -73,11 +73,9 @@ in `new()` — which tasks run depends on the selected module, so a clock proces
listen to tray D-Bus, etc.:
- **Compositor status feed** (`spawn_status_listener`): connects to
- `/tmp/cce-status[-interface]-{WAYLAND_DISPLAY}.sock`, subscribes to `viewport`,
- `layout`, `title`, `modifiers` (line-oriented, auto-reconnects every 1s). The
- `viewport` payload is JSON carrying only the active viewport number
- (`{"active": N}`), read at menu-open time for the layout menu's target —
- nothing renders it (the old viewport tabs are gone).
+ `/tmp/cce-status[-interface]-{WAYLAND_DISPLAY}.sock`, subscribes to `layout`,
+ `title`, `modifiers`, `dismiss` (line-oriented, auto-reconnects every 1s).
+ (The old `viewport` topic is gone with the viewport-tag feature.)
- **System stats** (`spawn_system_stats`): `/proc/stat`, `/proc/meminfo`,
`/sys/class/power_supply/BAT*`, `/sys/class/backlight`, and `pactl` for volume/mute.
- **Tray** (`spawn_status_tray`): a full StatusNotifierItem/Watcher host over `zbus`,
@@ -87,9 +85,8 @@ listen to tray D-Bus, etc.:
`/tmp/cce-status-interface-switcher-{WAYLAND_DISPLAY}.sock`; a line on it fires
`SwitcherTriggered`.
-Outbound actions shell out to `ccectl` (`view <viewport>`, `windows --json`,
-`focus-window`, `viewport-layout`, `window-switcher`, `status-hide-mode`,
-`adjust-position-mode`), resolved from `~/.local/bin` first (`get_ccectl_cmd`).
+Outbound actions shell out to `ccectl` (`windows --json`, `focus-window`,
+`window-switcher`, `status-hide-mode`, `adjust-position-mode`), resolved from `~/.local/bin` first (`get_ccectl_cmd`).
`ccectl windows --json` returns one JSON object per line; the text format is kept only
as a parse fallback for older compositors (`parse_ccectl_window_any_line` handles
both). Keyboard alt-tab switching is delegated to the compositor
@@ -109,9 +106,8 @@ segment thicker than the bar as expanded: frozen slot, no size enforcement,
raised above overlapped windows; the bar must reset its own height on close.
**No cce-cloud popups remain in this app**: the window picker (window-module
-title click → `MenuReady` rows of `Ccectl(["focus-window", id])`) and the
-layout-mode menu (layout-indicator click → `SetMode` rows + the in-place
-`ToggleApplyAll` checkbox row) are in-surface menus too. Menu width sizes to
+click → `MenuReady` rows of `Ccectl(["focus-window", id])`) is an in-surface
+menu too. Menu width sizes to
the longest row label. Expanded segments stack in the compositor's popups
layer (cce-fx@74a0f75) so click-away-close works across the whole surface,
including the strip band over neighboring segments.
@@ -159,8 +155,8 @@ mtime in `tick()`, so there is no reload event to wire up.
starts the same segment drag as adjust-position mode (snap to an edge on release,
persisted to `layout.status_bar.<module>` in config.kdl). This app never sees those
clicks and no longer tracks the super key.
-- The layout indicator opens the layout-mode menu; tray icons left-click activate /
- right-click open their DBusMenu.
+- Tray icons left-click activate / right-click open their DBusMenu. (The old
+ layout-mode menu and viewport tabs are gone with the viewport-tag feature.)
- `ToggleHideModules` / `ToggleAdjustPositionMode` mirror their state to the compositor
via `ccectl status-hide-mode|adjust-position-mode true|false`; the adjust-mode state
is read back with `ccectl adjust-position-mode query` (the compositor is the single
diff --git a/src/cloud.rs b/src/cloud.rs
index f7f2766..5e43dd7 100644
--- a/src/cloud.rs
+++ b/src/cloud.rs
@@ -113,11 +113,6 @@ pub(crate) enum MenuRowAction {
Dispatch(CustomEvent),
/// Run ccectl with these args, detached (window picker rows).
Ccectl(Vec<String>),
- /// Apply a window mode (the layout menu): honors the menu's live
- /// apply-to-all toggle and captured viewport at click time.
- SetMode(String),
- /// Flip the layout menu's apply-to-all toggle in place (stays open).
- ToggleApplyAll,
/// Non-interactive (separators).
Inert,
}
diff --git a/src/listeners.rs b/src/listeners.rs
index 33921fe..bd9baa8 100644
--- a/src/listeners.rs
+++ b/src/listeners.rs
@@ -35,7 +35,6 @@ pub(crate) async fn spawn_status_listener(sub: &'static str, sender: calloop::ch
log::debug!("[status-listener] received '{}' update: '{}'", sub, val);
if !val.is_empty() {
let ev = match sub {
- "viewport" => CustomEvent::ViewportUpdated(val.clone()),
"layout" => CustomEvent::LayoutUpdated(val.clone()),
"title" => CustomEvent::TitleUpdated(val.clone()),
// Click-away-close: the payload is the app_id of
diff --git a/src/main.rs b/src/main.rs
index f642c07..a5b6df6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -54,14 +54,6 @@ pub struct TrayIconBounds {
pub dbus_id: Option<String>,
}
-#[derive(Debug, Clone)]
-pub struct LayoutBounds {
- pub x: f32,
- pub y: f32,
- pub w: f32,
- pub h: f32,
-}
-
#[derive(Debug, Clone)]
pub struct SystemStats {
pub clock: String,
@@ -101,7 +93,6 @@ fn stats_signature(module: Option<&str>, s: &SystemStats) -> Option<String> {
#[derive(Debug, Clone)]
pub(crate) enum CustomEvent {
- ViewportUpdated(String),
LayoutUpdated(String),
TitleUpdated(String),
SystemStatsUpdated(SystemStats),
@@ -133,10 +124,6 @@ struct ModuleContextMenu {
/// to; None for the bar's own module menu.
tray_target: Option<(String, String)>,
min_w: f32,
- /// The layout menu's "apply to all sharing mode" toggle.
- apply_all: bool,
- /// Viewport captured when the layout menu opened (SetMode target).
- active_viewport: i32,
hovered: Option<usize>,
/// Menu box in surface-local logical coords, set by `rebuild_layout`.
rect: (f32, f32, f32, f32),
@@ -254,7 +241,6 @@ pub struct ModuleBounds {
struct StatusApp {
// Status State
- viewport: String,
layout: String,
title: String,
stats: Option<SystemStats>,
@@ -262,7 +248,6 @@ struct StatusApp {
cursor_pos: (f64, f64),
hovered_tray_item: Option<String>,
tray_item_bounds: Vec<TrayIconBounds>,
- layout_bounds: Option<LayoutBounds>,
font_system: FontSystem,
status_bar: cce_ui::widget::Adapted<cce_ui::widget::StatusBar>,
@@ -389,7 +374,6 @@ impl StatusApp {
self.status_bar.set_bg_color(self.current_bg_color);
}
- self.layout_bounds = None;
let is_single = self.selected_module_name.is_some();
let margin_padding = if is_single { 6.0 } else { 12.0 };
@@ -446,7 +430,6 @@ impl StatusApp {
&mut self.text_prims,
&mut self.rects,
&mut self.overlay_rects,
- &mut self.layout_bounds,
&self.tray_items,
&mut self.tray_item_bounds,
box_bg_color,
@@ -527,7 +510,6 @@ impl StatusApp {
&mut self.text_prims,
&mut self.rects,
&mut self.overlay_rects,
- &mut self.layout_bounds,
&self.tray_items,
&mut self.tray_item_bounds,
box_bg_color,
@@ -696,7 +678,7 @@ impl StatusApp {
// ONE continuous box in the module's own fill, spanning
// the strip band and the menu — the module box literally
// grows into the menu. Inserted at the front so the
- // module's strip content (tray icons, viewport tabs)
+ // module's strip content (tray icons, labels)
// renders on top of its band.
self.rounded_boxes.insert(0, RoundedBox {
x: plate_x,
@@ -887,50 +869,6 @@ impl StatusApp {
}
}
-fn get_closest_viewport(x: f64, y: f64) -> i32 {
- let centers = [(0.0, 0.0), (2000.0, 0.0), (0.0, 2000.0), (2000.0, 2000.0)];
- let mut min_dist = f64::MAX;
- let mut best_tag = 1;
- for (i, &(cx, cy)) in centers.iter().enumerate() {
- let dx = x - cx;
- let dy = y - cy;
- let dist = dx * dx + dy * dy;
- if dist < min_dist {
- min_dist = dist;
- best_tag = (i + 1) as i32;
- }
- }
- best_tag
-}
-
-fn get_active_viewport_from_camera(viewport_json: &str) -> u32 {
- let mut text = viewport_json.to_string();
- if let Ok(val) = serde_json::from_str::<serde_json::Value>(viewport_json) {
- // Current compositors send the active viewport explicitly alongside
- // the tab markup; the Pan-text parse below is the fallback for the
- // old camera-state payload.
- if let Some(a) = val.get("active").and_then(|v| v.as_u64()) {
- if (1..=4).contains(&a) {
- return a as u32;
- }
- }
- if let Some(t) = val.get("text").and_then(|v| v.as_str()) {
- text = t.to_string();
- }
- }
- if let Some(pan_idx) = text.find("Pan: (") {
- let coords_str = &text[pan_idx + "Pan: (".len()..];
- if let Some(end_idx) = coords_str.find(")") {
- let parts: Vec<&str> = coords_str[..end_idx].split(',').collect();
- if parts.len() == 2 {
- let pan_x = parts[0].trim().parse::<f64>().unwrap_or(0.0);
- let pan_y = parts[1].trim().parse::<f64>().unwrap_or(0.0);
- return get_closest_viewport(pan_x, pan_y) as u32;
- }
- }
- }
- 1
-}
fn get_module_side(name: &str) -> Side {
let content = std::fs::read_to_string(cce_ui::config::get_config_path()).unwrap_or_default();
@@ -1133,7 +1071,6 @@ impl cce_ui::engine::Application for StatusApp {
left_modules.push(module);
if has_window {
- tokio::spawn(spawn_status_listener("viewport", sender.clone()));
tokio::spawn(spawn_status_listener("layout", sender.clone()));
tokio::spawn(spawn_status_listener("title", sender.clone()));
}
@@ -1159,7 +1096,6 @@ impl cce_ui::engine::Application for StatusApp {
let font_system = cce_ui::create_font_system();
let mut app = Self {
- viewport: String::new(),
layout: String::new(),
title: String::new(),
stats: if has_stats { Some(get_initial_stats()) } else { None },
@@ -1167,7 +1103,6 @@ impl cce_ui::engine::Application for StatusApp {
cursor_pos: (0.0, 0.0),
hovered_tray_item: None,
tray_item_bounds: Vec::new(),
- layout_bounds: None,
font_system,
status_bar: cce_ui::widget::StatusBar::new(),
rects: Vec::new(),
@@ -1225,17 +1160,10 @@ impl cce_ui::engine::Application for StatusApp {
fn update(&mut self, msg: Self::Message, needs_rebuild: &mut bool, _exit: &mut bool) {
// Default to redrawing; the high-frequency push events below clear this when
// their value is unchanged, so a once-a-second stats poll (or a repeated
- // viewport/title push) no longer forces a redraw — and the compositor's
+ // title push) no longer forces a redraw — and the compositor's
// whole-backdrop blur re-bake — every time.
let mut changed = true;
match msg {
- CustomEvent::ViewportUpdated(t) => {
- // Nothing renders the viewport payload (the tabs are gone);
- // it is only read at menu-open time for the layout menu's
- // active viewport, so a push never redraws.
- changed = false;
- self.viewport = t;
- }
CustomEvent::LayoutUpdated(l) => {
changed = self.layout != l;
self.layout = l;
@@ -1275,8 +1203,6 @@ impl cce_ui::engine::Application for StatusApp {
page: 0,
tray_target: None,
min_w,
- apply_all: false,
- active_viewport: 0,
hovered: None,
rect: (0.0, 0.0, 0.0, 0.0),
row_bounds: Vec::new(),
@@ -1296,8 +1222,6 @@ impl cce_ui::engine::Application for StatusApp {
page: 0,
tray_target: Some((destination, menu_path)),
min_w: 260.0,
- apply_all: false,
- active_viewport: 0,
hovered: None,
rect: (0.0, 0.0, 0.0, 0.0),
row_bounds: Vec::new(),
@@ -1575,32 +1499,6 @@ impl cce_ui::engine::Application for StatusApp {
});
self.menu_closing = true;
}
- Some(MenuRowAction::SetMode(mode)) => {
- let args = if menu.apply_all {
- vec!["apply-mode-sharing".to_string(), mode]
- } else {
- vec![
- "viewport-layout".to_string(),
- menu.active_viewport.to_string(),
- mode,
- ]
- };
- std::thread::spawn(move || {
- let _ = std::process::Command::new(get_ccectl_cmd())
- .args(&args)
- .spawn();
- });
- self.menu_closing = true;
- }
- Some(MenuRowAction::ToggleApplyAll) => {
- menu.apply_all = !menu.apply_all;
- let mark = if menu.apply_all { "[x]" } else { "[ ]" };
- if let Some(page) = menu.pages.get_mut(menu.page) {
- if let Some(row) = page.rows.get_mut(i) {
- row.label = format!("{} Apply to all sharing mode", mark);
- }
- }
- }
_ => {}
}
}
@@ -1763,8 +1661,6 @@ impl cce_ui::engine::Application for StatusApp {
page: 0,
tray_target: None,
min_w: 190.0,
- apply_all: false,
- active_viewport: 0,
hovered: None,
rect: (0.0, 0.0, 0.0, 0.0),
row_bounds: Vec::new(),
@@ -1776,70 +1672,19 @@ impl cce_ui::engine::Application for StatusApp {
}
if button == MouseButton::Left {
- log::debug!("[viewport-click] Mouse left click at logical: ({}, {})", cx, cy);
-
- // Check if layout mode was clicked
- let mut clicked_layout = false;
- if let Some(ref bounds) = self.layout_bounds {
- if cx >= bounds.x as f64 && cx <= (bounds.x + bounds.w) as f64
- && cy >= bounds.y as f64 && cy <= (bounds.y + bounds.h) as f64 {
- clicked_layout = true;
- }
- }
-
- if clicked_layout {
- log::debug!("[layout-click] opening in-surface layout menu");
- if self.context_menu.is_none() {
- self.menu_anim = 0.0;
- }
- self.menu_closing = false;
- let mode_row = |label: &str| MenuRow {
- label: label.to_string(),
- enabled: true,
- separator: false,
- action: MenuRowAction::SetMode(label.to_lowercase()),
- };
- let rows = vec![
- MenuRow {
- label: "[ ] Apply to all sharing mode".to_string(),
- enabled: true,
- separator: false,
- action: MenuRowAction::ToggleApplyAll,
- },
- mode_row("Cascade"),
- mode_row("Grid"),
- mode_row("Fullscreen"),
- mode_row("Floating"),
- mode_row("Popup"),
- ];
- self.context_menu = Some(ModuleContextMenu {
- pages: vec![MenuPage { title: "Window Mode".to_string(), rows }],
- page: 0,
- tray_target: None,
- min_w: 240.0,
- apply_all: false,
- active_viewport: get_active_viewport_from_camera(&self.viewport) as i32,
- hovered: None,
- rect: (0.0, 0.0, 0.0, 0.0),
- row_bounds: Vec::new(),
- });
- self.needs_rebuild = true;
- *needs_rebuild = true;
- } else {
- let mut clicked_window = false;
- for mb in &self.module_bounds {
- if mb.name == "window" {
- if coord >= mb.x && coord <= (mb.x + mb.w) {
- clicked_window = true;
- break;
- }
+ let mut clicked_window = false;
+ for mb in &self.module_bounds {
+ if mb.name == "window" {
+ if coord >= mb.x && coord <= (mb.x + mb.w) {
+ clicked_window = true;
+ break;
}
}
+ }
- if clicked_window {
- log::debug!("[window-click] Window module clicked, opening window picker");
- self.trigger_switcher(false);
- }
+ if clicked_window {
+ log::debug!("[window-click] Window module clicked, opening window picker");
+ self.trigger_switcher(false);
}
}
}
diff --git a/src/modules.rs b/src/modules.rs
index 874490b..4d6f1d4 100644
--- a/src/modules.rs
+++ b/src/modules.rs
@@ -4,7 +4,7 @@ use cce_ui::color;
use cce_ui::widget::StyledLabel as Label;
use crate::{
- RectWidget, RoundedBox, LayoutBounds, SystemStats, TrayItem,
+ RectWidget, RoundedBox, SystemStats, TrayItem,
TrayIconBounds, make_text_buffer,
};
@@ -49,7 +49,6 @@ pub trait StatusModule {
text_prims: &mut Vec<crate::TextPrim>,
rects: &mut Vec<RectWidget>,
overlay_rects: &mut Vec<RectWidget>,
- layout_bounds: &mut Option<LayoutBounds>,
tray_items: &HashMap<String, TrayItem>,
tray_item_bounds: &mut Vec<TrayIconBounds>,
box_bg_color: Option<[f32; 4]>,
@@ -162,7 +161,6 @@ impl StatusModule for WindowModule {
text_prims: &mut Vec<crate::TextPrim>,
_rects: &mut Vec<RectWidget>,
_overlay_rects: &mut Vec<RectWidget>,
- layout_bounds: &mut Option<LayoutBounds>,
_tray_items: &HashMap<String, TrayItem>,
_tray_item_bounds: &mut Vec<TrayIconBounds>,
box_bg_color: Option<[f32; 4]>,
@@ -196,14 +194,7 @@ impl StatusModule for WindowModule {
if has_layout {
let layout_label = Label::new_with_family(font_system, layout, font_size, normal_color, font_family);
- let w = layout_label.w;
crate::draw_label(text_prims, layout_label, cur_x, y_pos);
- *layout_bounds = Some(LayoutBounds {
- x: cur_x - 2.0,
- y: 0.0,
- w: w + 4.0,
- h: bar_h,
- });
}
} else if title == "(none)" {
// Dim chip signalling that no window has keyboard focus — the
@@ -271,7 +262,6 @@ impl StatusModule for ClockModule {
text_prims: &mut Vec<crate::TextPrim>,
_rects: &mut Vec<RectWidget>,
_overlay_rects: &mut Vec<RectWidget>,
- _layout_bounds: &mut Option<LayoutBounds>,
_tray_items: &HashMap<String, TrayItem>,
_tray_item_bounds: &mut Vec<TrayIconBounds>,
_box_bg_color: Option<[f32; 4]>,
@@ -326,7 +316,6 @@ impl StatusModule for BatteryModule {
text_prims: &mut Vec<crate::TextPrim>,
_rects: &mut Vec<RectWidget>,
_overlay_rects: &mut Vec<RectWidget>,
- _layout_bounds: &mut Option<LayoutBounds>,
_tray_items: &HashMap<String, TrayItem>,
_tray_item_bounds: &mut Vec<TrayIconBounds>,
_box_bg_color: Option<[f32; 4]>,
@@ -388,7 +377,6 @@ impl StatusModule for VolumeModule {
text_prims: &mut Vec<crate::TextPrim>,
_rects: &mut Vec<RectWidget>,
overlay_rects: &mut Vec<RectWidget>,
- _layout_bounds: &mut Option<LayoutBounds>,
_tray_items: &HashMap<String, TrayItem>,
_tray_item_bounds: &mut Vec<TrayIconBounds>,
_box_bg_color: Option<[f32; 4]>,
@@ -463,7 +451,6 @@ impl StatusModule for BrightnessModule {
text_prims: &mut Vec<crate::TextPrim>,
_rects: &mut Vec<RectWidget>,
_overlay_rects: &mut Vec<RectWidget>,
- _layout_bounds: &mut Option<LayoutBounds>,
_tray_items: &HashMap<String, TrayItem>,
_tray_item_bounds: &mut Vec<TrayIconBounds>,
_box_bg_color: Option<[f32; 4]>,
@@ -528,7 +515,6 @@ impl StatusModule for MemoryModule {
text_prims: &mut Vec<crate::TextPrim>,
_rects: &mut Vec<RectWidget>,
_overlay_rects: &mut Vec<RectWidget>,
- _layout_bounds: &mut Option<LayoutBounds>,
_tray_items: &HashMap<String, TrayItem>,
_tray_item_bounds: &mut Vec<TrayIconBounds>,
_box_bg_color: Option<[f32; 4]>,
@@ -583,7 +569,6 @@ impl StatusModule for CpuModule {
text_prims: &mut Vec<crate::TextPrim>,
_rects: &mut Vec<RectWidget>,
_overlay_rects: &mut Vec<RectWidget>,
- _layout_bounds: &mut Option<LayoutBounds>,
_tray_items: &HashMap<String, TrayItem>,
_tray_item_bounds: &mut Vec<TrayIconBounds>,
_box_bg_color: Option<[f32; 4]>,
@@ -638,7 +623,6 @@ impl StatusModule for TrayModule {
text_prims: &mut Vec<crate::TextPrim>,
_rects: &mut Vec<RectWidget>,
overlay_rects: &mut Vec<RectWidget>,
- _layout_bounds: &mut Option<LayoutBounds>,
tray_items: &HashMap<String, TrayItem>,
tray_item_bounds: &mut Vec<TrayIconBounds>,
_box_bg_color: Option<[f32; 4]>,
@@ -849,7 +833,6 @@ impl StatusModule for LightSourceModule {
_text_prims: &mut Vec<crate::TextPrim>,
_rects: &mut Vec<RectWidget>,
_overlay_rects: &mut Vec<RectWidget>,
- _layout_bounds: &mut Option<LayoutBounds>,
_tray_items: &HashMap<String, TrayItem>,
_tray_item_bounds: &mut Vec<TrayIconBounds>,
box_bg_color: Option<[f32; 4]>,