From 08e7e01d0ec933d9e9d901774d94d0fbcb421111 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 17 Jul 2025 22:31:16 +0200 Subject: [PATCH] config: allow disabling the built-in bar --- jay-config/src/_private/client.rs | 10 +++++++ jay-config/src/_private/ipc.rs | 7 +++++ jay-config/src/lib.rs | 18 +++++++++++++ src/compositor.rs | 1 + src/config/handler.rs | 15 +++++++++++ src/renderer.rs | 7 ++--- src/state.rs | 4 +++ src/tree/container.rs | 13 ++++++++-- src/tree/output.rs | 33 ++++++++++++++++++++---- toml-config/src/config.rs | 3 +++ toml-config/src/config/parsers/action.rs | 3 +++ toml-config/src/config/parsers/config.rs | 3 +++ toml-config/src/lib.rs | 11 +++++--- toml-spec/spec/spec.generated.json | 9 ++++++- toml-spec/spec/spec.generated.md | 20 ++++++++++++++ toml-spec/spec/spec.yaml | 13 ++++++++++ 16 files changed, 156 insertions(+), 14 deletions(-) diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index 2a3c0710..218fe46f 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -950,6 +950,16 @@ impl ConfigClient { above } + pub fn set_show_bar(&self, show: bool) { + self.send(&ClientMessage::SetShowBar { show }); + } + + pub fn get_show_bar(&self) -> bool { + let res = self.send_with_response(&ClientMessage::GetShowBar); + get_response!(res, true, GetShowBar { show }); + show + } + pub fn set_show_float_pin_icon(&self, show: bool) { self.send(&ClientMessage::SetShowFloatPinIcon { show }); } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index 2e5d5bf2..9ca66f39 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -721,6 +721,10 @@ pub enum ClientMessage<'a> { GetContentType { window: Window, }, + SetShowBar { + show: bool, + }, + GetShowBar, } #[derive(Serialize, Deserialize, Debug)] @@ -950,6 +954,9 @@ pub enum Response { GetContentType { kind: ContentType, }, + GetShowBar { + show: bool, + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/lib.rs b/jay-config/src/lib.rs index 39b6f6cc..15926459 100644 --- a/jay-config/src/lib.rs +++ b/jay-config/src/lib.rs @@ -316,6 +316,24 @@ pub fn set_show_float_pin_icon(show: bool) { get!().set_show_float_pin_icon(show); } +/// Sets whether the built-in bar is shown. +/// +/// The default is `true`. +pub fn set_show_bar(show: bool) { + get!().set_show_bar(show) +} + +/// Returns whether the built-in bar is shown. +pub fn get_show_bar() -> bool { + get!(true).get_show_bar() +} + +/// Toggles whether the built-in bar is shown. +pub fn toggle_show_bar() { + let get = get!(); + get.set_show_bar(!get.get_show_bar()); +} + /// Sets a callback to run when this config is unloaded. /// /// Only one callback can be set at a time. If another callback is already set, it will be diff --git a/src/compositor.rs b/src/compositor.rs index 7dda9add..609dc995 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -351,6 +351,7 @@ fn start_compositor2( head_names: Default::default(), head_managers: Default::default(), head_managers_async: Default::default(), + show_bar: Cell::new(true), }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); diff --git a/src/config/handler.rs b/src/config/handler.rs index 0f34717c..63bd8ce7 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1330,6 +1330,19 @@ impl ConfigProxyHandler { }); } + fn handle_set_show_bar(&self, show: bool) { + self.state.show_bar.set(show); + for output in self.state.root.outputs.lock().values() { + output.on_spaces_changed(); + } + } + + fn handle_get_show_bar(&self) { + self.respond(Response::GetShowBar { + show: self.state.show_bar.get(), + }); + } + fn handle_set_show_float_pin_icon(&self, show: bool) { self.state.show_pin_icon.set(show); for stacked in self.state.root.stacked.iter() { @@ -2982,6 +2995,8 @@ impl ConfigProxyHandler { ClientMessage::GetContentType { window } => self .handle_get_content_type(window) .wrn("get_content_type")?, + ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show), + ClientMessage::GetShowBar => self.handle_get_show_bar(), } Ok(()) } diff --git a/src/renderer.rs b/src/renderer.rs index bcd1dde3..a36f28c0 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -87,8 +87,8 @@ impl Renderer<'_> { render_layer!(output.layers[0]); render_layer!(output.layers[1]); let non_exclusive_rect = output.non_exclusive_rect_rel.get(); - let (x, y) = non_exclusive_rect.translate_inv(x, y); - { + let (x, mut y) = non_exclusive_rect.translate_inv(x, y); + if self.state.show_bar.get() { let bar_bg = Rect::new_sized(0, 0, non_exclusive_rect.width(), th).unwrap(); let bar_bg = self.base.scale_rect(bar_bg); let bar_bg_abs = { @@ -166,9 +166,10 @@ impl Renderer<'_> { self.render_surface(&data.surface, rect.x1(), rect.y1(), Some(&bounds)); } } + y += th + 1; } if let Some(ws) = output.workspace.get() { - self.render_workspace(&ws, x, y + th + 1); + self.render_workspace(&ws, x, y); } } macro_rules! render_stacked { diff --git a/src/state.rs b/src/state.rs index 47747cf7..1fb6235c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -271,6 +271,7 @@ pub struct State { pub head_managers: CopyHashMap<(ClientId, JayHeadManagerSessionV1Id), Rc>, pub head_managers_async: AsyncQueue, + pub show_bar: Cell, } // impl Drop for State { @@ -1474,6 +1475,9 @@ impl State { } pub fn tray_icon_size(&self) -> i32 { + if !self.show_bar.get() { + return 0; + } (self.theme.sizes.title_height.get() - 2).max(0) } diff --git a/src/tree/container.rs b/src/tree/container.rs index dc774b31..b867d928 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -814,8 +814,13 @@ impl ContainerNode { let abs_y = self.abs_y1.get(); for (i, child) in self.children.iter().enumerate() { let rect = child.title_rect.get(); - if self.toplevel_data.visible.get() { - self.state.damage(rect.move_(abs_x, abs_y)); + if self.toplevel_data.visible.get() && !mono && split != ContainerSplit::Horizontal { + self.state.damage(Rect::new_sized_unchecked( + abs_x, + abs_y + rect.y1(), + cwidth, + rect.height() + 1, + )); } if i > 0 { let rect = if mono { @@ -852,6 +857,10 @@ impl ContainerNode { rd.underline_rects .push(Rect::new_sized(0, th, cwidth, 1).unwrap()); } + if self.toplevel_data.visible.get() && (mono || split == ContainerSplit::Horizontal) { + self.state + .damage(Rect::new_sized_unchecked(abs_x, abs_y, cwidth, th + 1)); + } rd.titles.remove_if(|_, v| v.is_empty()); } diff --git a/src/tree/output.rs b/src/tree/output.rs index b3cce86c..2da49494 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -496,6 +496,9 @@ impl OutputNode { fn update_render_data_phase1(self: &Rc) -> Rc { let on_completed = Rc::new(OnDropEvent::default()); + if !self.state.show_bar.get() { + return on_completed.event(); + } let Some(ctx) = self.state.render_ctx.get() else { return on_completed.event(); }; @@ -556,6 +559,10 @@ impl OutputNode { rd.attention_requested_workspaces.clear(); rd.captured_inactive_workspaces.clear(); rd.active_workspace = None; + if !self.state.show_bar.get() { + self.state.damage(rd.full_area); + return; + } let mut pos = 0; let theme = &self.state.theme; let th = theme.sizes.title_height.get(); @@ -766,7 +773,10 @@ impl OutputNode { width, height, )); - let y1 = y1 + th + 1; + let mut y1 = y1; + if self.state.show_bar.get() { + y1 += th + 1; + } let height = (y2 - y1).max(0); self.workspace_rect .set(Rect::new_sized_unchecked(x1, y1, width, height)); @@ -1068,6 +1078,9 @@ impl OutputNode { } fn button(self: Rc, id: PointerType) { + if !self.state.show_bar.get() { + return; + } let (x, y) = match self.pointer_positions.get(&id) { Some(p) => p, _ => return, @@ -1208,8 +1221,9 @@ impl OutputNode { if ws.fullscreen.is_some() { return None; } + let show_bar = self.state.show_bar.get(); let th = self.state.theme.sizes.title_height.get(); - if y_abs < rect.y1() + th + 1 { + if show_bar && y_abs < rect.y1() + th + 1 { let rd = &*self.render_data.borrow(); let (x, _) = rect.translate(x_abs, y_abs); let mut last_x2 = 0; @@ -1236,8 +1250,11 @@ impl OutputNode { }, }); } - let thp1 = self.state.theme.sizes.title_height.get() + 1; - let rect = Rect::new(rect.x1(), rect.y1() + thp1, rect.x2(), rect.y2())?; + let bar_height = match show_bar { + true => th + 1, + false => 0, + }; + let rect = Rect::new(rect.x1(), rect.y1() + bar_height, rect.x2(), rect.y2())?; if !rect.contains(x_abs, y_abs) { return None; } @@ -1256,6 +1273,9 @@ impl OutputNode { x_abs: i32, y_abs: i32, ) -> Option { + if !self.state.show_bar.get() { + return None; + } let rect = self.non_exclusive_rect.get(); if !rect.contains(x_abs, y_abs) { return None; @@ -1478,7 +1498,10 @@ impl Node for OutputNode { } return FindTreeResult::AcceptsInput; } - let bar_height = self.state.theme.sizes.title_height.get() + 1; + let bar_height = match self.state.show_bar.get() { + true => self.state.theme.sizes.title_height.get() + 1, + false => 0, + }; if usecase == FindTreeUsecase::SelectWorkspace { if y >= bar_height { y -= bar_height; diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index ec77f760..ac2bdccf 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -68,6 +68,8 @@ pub enum SimpleCommand { SetFloatPinned(bool), ToggleFloatPinned, KillClient, + ShowBar(bool), + ToggleBar, } #[derive(Debug, Clone)] @@ -483,6 +485,7 @@ pub struct Config { pub window_rules: Vec, pub pointer_revert_key: Option, pub use_hardware_cursor: Option, + pub show_bar: Option, } #[derive(Debug, Error)] diff --git a/toml-config/src/config/parsers/action.rs b/toml-config/src/config/parsers/action.rs index 618c0cb1..c0a39075 100644 --- a/toml-config/src/config/parsers/action.rs +++ b/toml-config/src/config/parsers/action.rs @@ -133,6 +133,9 @@ impl ActionParser<'_> { "unpin-float" => SetFloatPinned(false), "toggle-float-pinned" => ToggleFloatPinned, "kill-client" => KillClient, + "show-bar" => ShowBar(true), + "hide-bar" => ShowBar(false), + "toggle-bar" => ToggleBar, _ => { return Err( ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span) diff --git a/toml-config/src/config/parsers/config.rs b/toml-config/src/config/parsers/config.rs index 8b102fb0..07d61fcd 100644 --- a/toml-config/src/config/parsers/config.rs +++ b/toml-config/src/config/parsers/config.rs @@ -132,6 +132,7 @@ impl Parser for ConfigParser<'_> { window_rules_val, pointer_revert_key_str, use_hardware_cursor, + show_bar, ), ) = ext.extract(( ( @@ -179,6 +180,7 @@ impl Parser for ConfigParser<'_> { opt(val("windows")), recover(opt(str("pointer-revert-key"))), recover(opt(bol("use-hardware-cursor"))), + recover(opt(bol("show-bar"))), ), ))?; let mut keymap = None; @@ -494,6 +496,7 @@ impl Parser for ConfigParser<'_> { window_rules, pointer_revert_key, use_hardware_cursor: use_hardware_cursor.despan(), + show_bar: show_bar.despan(), }) } } diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 01bcb7fe..95191f34 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -35,12 +35,12 @@ use { logging::set_log_level, on_devices_enumerated, on_idle, on_unload, quit, reload, set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled, set_float_above_fullscreen, - set_idle, set_idle_grace_period, set_show_float_pin_icon, set_ui_drag_enabled, - set_ui_drag_threshold, + set_idle, set_idle_grace_period, set_show_bar, set_show_float_pin_icon, + set_ui_drag_enabled, set_ui_drag_threshold, status::{set_i3bar_separator, set_status, set_status_command, unset_status_command}, switch_to_vt, theme::{reset_colors, reset_font, reset_sizes, set_font}, - toggle_float_above_fullscreen, + toggle_float_above_fullscreen, toggle_show_bar, video::{ ColorSpace, Connector, DrmDevice, TransferFunction, connectors, drm_devices, on_connector_connected, on_connector_disconnected, on_graphics_initialized, @@ -154,6 +154,8 @@ impl Action { } SimpleCommand::ToggleFloatPinned => window_or_seat!(s, s.toggle_float_pinned()), SimpleCommand::KillClient => client_action!(c, c.kill()), + SimpleCommand::ShowBar(show) => B::new(move || set_show_bar(show)), + SimpleCommand::ToggleBar => B::new(toggle_show_bar), }, Action::Multi { actions } => { let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect(); @@ -1247,6 +1249,9 @@ fn load_config(initial_load: bool, persistent: &Rc) { if let Some(v) = config.use_hardware_cursor { persistent.seat.use_hardware_cursor(v); } + if let Some(v) = config.show_bar { + set_show_bar(v); + } } fn create_command(exec: &Exec) -> Command { diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index d7c7dc61..369a8835 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -883,6 +883,10 @@ "use-hardware-cursor": { "type": "boolean", "description": "Configures whether the default seat uses hardware cursors.\n\nThe default is `true`.\n" + }, + "show-bar": { + "type": "boolean", + "description": "Configures whether the built-in bar is shown.\n\nThe default is `true`.\n" } }, "required": [] @@ -1586,7 +1590,10 @@ "pin-float", "unpin-float", "toggle-float-pinned", - "kill-client" + "kill-client", + "show-bar", + "hide-bar", + "toggle-bar" ] }, "Status": { diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index 33327424..c4f5f56a 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -1794,6 +1794,14 @@ The table has the following fields: The value of this field should be a boolean. +- `show-bar` (optional): + + Configures whether the built-in bar is shown. + + The default is `true`. + + The value of this field should be a boolean. + ### `Connector` @@ -3597,6 +3605,18 @@ The string should have one of the following values: Within a window rule, it applies to the client of the window. Within a client rule it applies to the matched client. Has no effect otherwise. +- `show-bar`: + + Shows the built-in bar. + +- `hide-bar`: + + Hides the built-in bar. + +- `toggle-bar`: + + Toggles the built-in bar. + diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index efdb960c..76a142d3 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -854,6 +854,12 @@ SimpleActionName: Within a window rule, it applies to the client of the window. Within a client rule it applies to the matched client. Has no effect otherwise. + - value: show-bar + description: Shows the built-in bar. + - value: hide-bar + description: Hides the built-in bar. + - value: toggle-bar + description: Toggles the built-in bar. Color: @@ -2614,6 +2620,13 @@ Config: Configures whether the default seat uses hardware cursors. The default is `true`. + show-bar: + kind: boolean + required: false + description: | + Configures whether the built-in bar is shown. + + The default is `true`. Idle: