diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bar.rs | 22 | ||||
| -rw-r--r-- | src/shared_state.rs | 19 | ||||
| -rw-r--r-- | src/state.rs | 75 | ||||
| -rw-r--r-- | src/text.rs | 9 | ||||
| -rw-r--r-- | src/wm_info_provider.rs | 87 | ||||
| -rw-r--r-- | src/wm_info_provider/ext_workspace_unstable.rs | 110 | ||||
| -rw-r--r-- | src/wm_info_provider/river.rs | 82 |
7 files changed, 216 insertions, 188 deletions
@@ -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::WmInfo; +use crate::wm_info_provider::Tag; pub struct Bar { pub output: Output, @@ -29,7 +29,8 @@ pub struct Bar { pub viewport: WpViewport, pub fractional_scale: Option<WpFractionalScaleV1>, pub blocks_btns: ButtonManager<(Option<String>, Option<String>)>, - pub wm_info: WmInfo, + pub tags: Vec<Tag>, + pub layout_name: Option<String>, pub tags_btns: ButtonManager<String>, pub tags_computed: Vec<(ColorPair, ComputedText)>, pub layout_name_computed: Option<ComputedText>, @@ -42,10 +43,14 @@ pub struct ColorPair { } impl Bar { - pub fn set_wm_info(&mut self, info: WmInfo) { - self.wm_info = info; + pub fn set_tags(&mut self, tags: Vec<Tag>) { + self.tags = tags; self.tags_btns.clear(); self.tags_computed.clear(); + } + + pub fn set_layout_name(&mut self, layout_name: Option<String>) { + self.layout_name = layout_name; self.layout_name_computed = None; } @@ -59,8 +64,9 @@ impl Bar { _y: f64, ) -> anyhow::Result<()> { if let Some(tag) = self.tags_btns.click(x) { - ss.wm_info_provider - .click_on_tag(conn, self.output.wl, seat, tag, button); + if let Some(wm) = &mut ss.wm_info_provider { + wm.click_on_tag(conn, self.output.wl, seat, tag, button); + } } else if let Some((name, instance)) = self.blocks_btns.click(x) { if let Some(cmd) = &mut ss.status_cmd { cmd.send_click_event(&i3bar_protocol::Event { @@ -137,7 +143,7 @@ impl Bar { if self.tags_computed.is_empty() { let mut offset_left = 0.0; self.tags_btns.clear(); - for tag in &self.wm_info.tags { + for tag in &self.tags { let (bg, fg) = if tag.is_urgent { (ss.config.tag_urgent_bg, ss.config.tag_urgent_fg) } else if tag.is_focused { @@ -187,7 +193,7 @@ impl Bar { // Display layout name if ss.config.show_layout_name { - if let Some(layout_name) = &self.wm_info.layout_name { + if let Some(layout_name) = &self.layout_name { let text = self.layout_name_computed.get_or_insert_with(|| { ComputedText::new( layout_name, diff --git a/src/shared_state.rs b/src/shared_state.rs index 2e87749..936b29a 100644 --- a/src/shared_state.rs +++ b/src/shared_state.rs @@ -1,6 +1,8 @@ use crate::{ - blocks_cache::BlocksCache, config::Config, status_cmd::StatusCmd, - wm_info_provider::WmInfoProvider, + blocks_cache::BlocksCache, + config::Config, + status_cmd::StatusCmd, + wm_info_provider::{self, WmInfoProvider}, }; use wayrs_utils::shm_alloc::ShmAlloc; @@ -10,5 +12,16 @@ pub struct SharedState { pub config: Config, pub status_cmd: Option<StatusCmd>, pub blocks_cache: BlocksCache, - pub wm_info_provider: WmInfoProvider, + pub wm_info_provider: Option<Box<dyn WmInfoProvider>>, +} + +impl SharedState { + pub fn get_river(&mut self) -> Option<&mut wm_info_provider::RiverInfoProvider> { + self.wm_info_provider.as_mut()?.as_any().downcast_mut() + } + + #[cfg(feature = "hyprland")] + pub fn get_ewu(&mut self) -> Option<&mut wm_info_provider::ExtWorkspaceUnstable> { + self.wm_info_provider.as_mut()?.as_any().downcast_mut() + } } diff --git a/src/state.rs b/src/state.rs index dcf7fbe..3ace244 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,7 @@ use crate::blocks_cache::BlocksCache; use crate::output::Output; use crate::protocol::*; -use crate::wm_info_provider::*; +use crate::wm_info_provider; use std::fmt::Display; use std::os::unix::io::{AsRawFd, RawFd}; @@ -74,6 +74,8 @@ impl State { .map_err(|e| error = Err(e.into())) .ok(); + let wm_info_provider = wm_info_provider::bind(conn, globals, &config.wm); + let mut this = Self { wl_compositor, layer_shell: globals.bind(conn, 1..=4).unwrap(), @@ -97,7 +99,7 @@ impl State { config, status_cmd, blocks_cache: BlocksCache::default(), - wm_info_provider: WmInfoProvider::bind(conn, globals, wm_info_cb), + wm_info_provider, }, cursor_theme, @@ -146,9 +148,9 @@ impl State { return; } - self.shared_state - .wm_info_provider - .new_ouput(conn, output.wl); + if let Some(wm) = &mut self.shared_state.wm_info_provider { + wm.new_ouput(conn, output.wl); + } let surface = self.wl_compositor.create_surface(conn); @@ -178,7 +180,8 @@ impl State { fractional_scale, layer_surface, blocks_btns: Default::default(), - wm_info: Default::default(), + tags: Vec::new(), + layout_name: None, tags_btns: Default::default(), tags_computed: Vec::new(), layout_name_computed: None, @@ -193,11 +196,11 @@ impl State { fn drop_bar(&mut self, conn: &mut Connection<Self>, bar_index: usize) { let bar = self.bars.swap_remove(bar_index); + if let Some(wm) = &mut self.shared_state.wm_info_provider { + wm.output_removed(conn, bar.output.wl); + } bar.surface.destroy(conn); bar.layer_surface.destroy(conn); - self.shared_state - .wm_info_provider - .output_removed(conn, bar.output.wl); bar.output.destroy(conn); } @@ -211,6 +214,50 @@ impl State { } } } + + fn for_each_bar<F: FnMut(&mut Bar, &mut SharedState)>( + &mut self, + output: Option<WlOutput>, + mut f: F, + ) { + match output { + Some(output) => f( + self.bars + .iter_mut() + .find(|b| b.output.wl == output) + .unwrap(), + &mut self.shared_state, + ), + None => self + .bars + .iter_mut() + .for_each(|b| f(b, &mut self.shared_state)), + } + } + + pub fn tags_updated(&mut self, conn: &mut Connection<Self>, output: Option<WlOutput>) { + self.for_each_bar(output, |bar, ss| { + bar.set_tags( + ss.wm_info_provider + .as_mut() + .unwrap() + .get_tags(bar.output.wl), + ); + bar.request_frame(conn); + }); + } + + pub fn layout_name_updated(&mut self, conn: &mut Connection<Self>, output: Option<WlOutput>) { + self.for_each_bar(output, |bar, ss| { + bar.set_layout_name( + ss.wm_info_provider + .as_mut() + .unwrap() + .get_layout_name(bar.output.wl), + ); + bar.request_frame(conn); + }); + } } impl SeatHandler for State { @@ -433,16 +480,6 @@ fn fractional_scale_cb( } } -fn wm_info_cb(conn: &mut Connection<State>, state: &mut State, output: WlOutput, info: WmInfo) { - let bar = state - .bars - .iter_mut() - .find(|b| b.output.wl == output) - .unwrap(); - bar.set_wm_info(info); - bar.request_frame(conn); -} - #[derive(Debug, Default, Clone, Copy)] pub struct ScrollFrame { stop: bool, diff --git a/src/text.rs b/src/text.rs index bc1516d..3a824d8 100644 --- a/src/text.rs +++ b/src/text.rs @@ -34,20 +34,15 @@ pub struct Attributes<'a> { pub markup: bool, } -#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Deserialize, Default, Debug, Clone, Copy, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum Align { Right, + #[default] Left, Center, } -impl Default for Align { - fn default() -> Self { - Self::Left - } -} - #[derive(Clone, Debug, PartialEq)] pub struct ComputedText { pub width: f64, diff --git a/src/wm_info_provider.rs b/src/wm_info_provider.rs index 96d76db..128d3b9 100644 --- a/src/wm_info_provider.rs +++ b/src/wm_info_provider.rs @@ -1,6 +1,9 @@ +use std::any::Any; + use wayrs_client::global::*; use wayrs_client::Connection; +use crate::config::WmConfig; use crate::pointer_btn::PointerBtn; use crate::protocol::*; use crate::state::State; @@ -8,77 +11,45 @@ use crate::state::State; #[cfg(feature = "hyprland")] mod ext_workspace_unstable; #[cfg(feature = "hyprland")] -use ext_workspace_unstable::*; +pub use ext_workspace_unstable::*; mod river; -use river::*; - -pub enum WmInfoProvider { - None, - River(RiverInfoProvider), - #[cfg(feature = "hyprland")] - Ewu(ExtWorkspaceUnstable), -} - -pub type WmInfoCallback = fn(&mut Connection<State>, &mut State, WlOutput, WmInfo); +pub use river::*; -impl WmInfoProvider { - pub fn bind( - conn: &mut Connection<State>, - globals: &Globals, - callback: WmInfoCallback, - ) -> WmInfoProvider { - if let Some(river) = RiverInfoProvider::bind(conn, globals, callback) { - return Self::River(river); - } - - #[cfg(feature = "hyprland")] - if let Some(ext_wp_u) = ExtWorkspaceUnstable::bind(conn, globals, callback) { - return Self::Ewu(ext_wp_u); - } +pub trait WmInfoProvider { + fn new_ouput(&mut self, conn: &mut Connection<State>, output: WlOutput); + fn output_removed(&mut self, conn: &mut Connection<State>, output: WlOutput); - Self::None - } - - pub fn new_ouput(&mut self, conn: &mut Connection<State>, output: WlOutput) { - match self { - Self::None => (), - Self::River(x) => x.new_output(conn, output), - #[cfg(feature = "hyprland")] - Self::Ewu(x) => x.new_ouput(conn, output), - } - } + fn get_tags(&self, output: WlOutput) -> Vec<Tag>; + fn get_layout_name(&self, output: WlOutput) -> Option<String>; - pub fn output_removed(&mut self, conn: &mut Connection<State>, output: WlOutput) { - match self { - Self::None => (), - Self::River(x) => x.output_removed(conn, output), - #[cfg(feature = "hyprland")] - Self::Ewu(x) => x.output_removed(conn, output), - } - } - - pub fn click_on_tag( + fn click_on_tag( &mut self, conn: &mut Connection<State>, output: WlOutput, seat: WlSeat, tag: &str, btn: PointerBtn, - ) { - match self { - Self::None => (), - Self::River(x) => x.click_on_tag(conn, output, seat, tag, btn), - #[cfg(feature = "hyprland")] - Self::Ewu(x) => x.click_on_tag(conn, output, seat, tag, btn), - } - } + ); + + fn as_any(&mut self) -> &mut dyn Any; } -#[derive(Default, Debug)] -pub struct WmInfo { - pub layout_name: Option<String>, - pub tags: Vec<Tag>, +pub fn bind( + conn: &mut Connection<State>, + globals: &Globals, + config: &WmConfig, +) -> Option<Box<dyn WmInfoProvider>> { + if let Some(river) = RiverInfoProvider::bind(conn, globals, config) { + return Some(Box::new(river)); + } + + #[cfg(feature = "hyprland")] + if let Some(ext_wp_u) = ExtWorkspaceUnstable::bind(conn, globals) { + return Some(Box::new(ext_wp_u)); + } + + None } #[derive(Debug)] diff --git a/src/wm_info_provider/ext_workspace_unstable.rs b/src/wm_info_provider/ext_workspace_unstable.rs index 467be2b..8cb5855 100644 --- a/src/wm_info_provider/ext_workspace_unstable.rs +++ b/src/wm_info_provider/ext_workspace_unstable.rs @@ -10,7 +10,6 @@ pub struct ExtWorkspaceUnstable { manager: ZextWorkspaceManagerV1, groups: Vec<Group>, known_outputs: Vec<WlOutput>, - callback: WmInfoCallback, } #[derive(Debug)] @@ -29,37 +28,62 @@ struct Workspace { } impl ExtWorkspaceUnstable { - pub fn bind( - conn: &mut Connection<State>, - globals: &Globals, - callback: WmInfoCallback, - ) -> Option<Self> { + pub fn bind(conn: &mut Connection<State>, globals: &Globals) -> Option<Self> { Some(Self { manager: globals.bind_with_cb(conn, 1..=1, manager_cb).ok()?, groups: Vec::new(), known_outputs: Vec::new(), - callback, }) } } -impl ExtWorkspaceUnstable { - pub fn new_ouput(&mut self, _: &mut Connection<State>, output: WlOutput) { +impl WmInfoProvider for ExtWorkspaceUnstable { + fn new_ouput(&mut self, _conn: &mut Connection<State>, output: WlOutput) { self.known_outputs.push(output); } - pub fn output_removed(&mut self, _: &mut Connection<State>, output: WlOutput) { + fn output_removed(&mut self, _conn: &mut Connection<State>, output: WlOutput) { self.known_outputs.retain(|&o| o != output); for group in &mut self.groups { group.outputs.retain(|&id| id != output.id()); } } - pub fn click_on_tag( + fn get_tags(&self, output: WlOutput) -> Vec<Tag> { + let Some(group) = self + .groups + .iter() + .find(|g| g.outputs.contains(&output.id())) + else { + return Vec::new(); + }; + + let mut tags: Vec<_> = group + .workspaces + .iter() + .flat_map(|w| { + let name = w.name.clone()?; + Some(Tag { + name, + is_focused: w.is_focused, + is_active: true, + is_urgent: w.is_urgent, + }) + }) + .collect(); + tags.sort_unstable_by(|a, b| a.name.cmp(&b.name)); + tags + } + + fn get_layout_name(&self, _output: WlOutput) -> Option<String> { + None + } + + fn click_on_tag( &mut self, conn: &mut Connection<State>, - _: WlOutput, - _: WlSeat, + _output: WlOutput, + _seat: WlSeat, tag: &str, btn: PointerBtn, ) { @@ -78,62 +102,30 @@ impl ExtWorkspaceUnstable { ws.workspace_handle.activate(conn); self.manager.commit(conn); } + + fn as_any(&mut self) -> &mut dyn Any { + self + } } fn manager_cb( conn: &mut Connection<State>, - s: &mut State, + state: &mut State, _: ZextWorkspaceManagerV1, event: zext_workspace_manager_v1::Event, ) { - let WmInfoProvider::Ewu(state) = &mut s.shared_state.wm_info_provider else { - unreachable!() - }; + let ewu = state.shared_state.get_ewu().unwrap(); match event { zext_workspace_manager_v1::Event::WorkspaceGroup(group_handle) => { conn.set_callback_for(group_handle, group_cb); - state.groups.push(Group { + ewu.groups.push(Group { group_handle, workspaces: Vec::new(), outputs: Vec::new(), }); } - zext_workspace_manager_v1::Event::Done => { - let mut events = Vec::new(); - for &output in &state.known_outputs { - if let Some(group) = state - .groups - .iter() - .find(|g| g.outputs.contains(&output.id())) - { - let mut tags: Vec<_> = group - .workspaces - .iter() - .flat_map(|w| { - let name = w.name.clone()?; - Some(Tag { - name, - is_focused: w.is_focused, - is_active: true, - is_urgent: w.is_urgent, - }) - }) - .collect(); - tags.sort_unstable_by(|a, b| a.name.cmp(&b.name)); - events.push((output, tags)); - } - } - - let cb = state.callback; - for (output, tags) in events { - let info = WmInfo { - layout_name: None, - tags, - }; - (cb)(conn, s, output, info); - } - } + zext_workspace_manager_v1::Event::Done => state.tags_updated(conn, None), zext_workspace_manager_v1::Event::Finished => unreachable!(), } } @@ -144,10 +136,8 @@ fn group_cb( group_handle: ZextWorkspaceGroupHandleV1, event: zext_workspace_group_handle_v1::Event, ) { - let WmInfoProvider::Ewu(state) = &mut state.shared_state.wm_info_provider else { - unreachable!() - }; - let group = state + let ewu = state.shared_state.get_ewu().unwrap(); + let group = ewu .groups .iter_mut() .find(|g| g.group_handle == group_handle) @@ -179,10 +169,8 @@ fn workspace_cb( workspace_handle: ZextWorkspaceHandleV1, event: zext_workspace_handle_v1::Event, ) { - let WmInfoProvider::Ewu(state) = &mut state.shared_state.wm_info_provider else { - unreachable!() - }; - let group = state + let ewu = state.shared_state.get_ewu().unwrap(); + let group = ewu .groups .iter_mut() .find(|g| { diff --git a/src/wm_info_provider/river.rs b/src/wm_info_provider/river.rs index b5683ab..c9778bc 100644 --- a/src/wm_info_provider/river.rs +++ b/src/wm_info_provider/river.rs @@ -10,7 +10,7 @@ pub struct RiverInfoProvider { status_manager: ZriverStatusManagerV1, control: ZriverControlV1, output_statuses: Vec<OutputStatus>, - callback: WmInfoCallback, + max_tag: u8, } struct OutputStatus { @@ -26,19 +26,19 @@ impl RiverInfoProvider { pub fn bind( conn: &mut Connection<State>, globals: &Globals, - callback: WmInfoCallback, + config: &WmConfig, ) -> Option<Self> { Some(Self { status_manager: globals.bind(conn, 1..=4).ok()?, control: globals.bind(conn, 1..=1).ok()?, output_statuses: Vec::new(), - callback, + max_tag: config.river.max_tag, }) } } -impl RiverInfoProvider { - pub fn new_output(&mut self, conn: &mut Connection<State>, output: WlOutput) { +impl WmInfoProvider for RiverInfoProvider { + fn new_ouput(&mut self, conn: &mut Connection<State>, output: WlOutput) { let status = self.status_manager .get_river_output_status_with_cb(conn, output, output_status_cb); @@ -52,7 +52,7 @@ impl RiverInfoProvider { }); } - pub fn output_removed(&mut self, conn: &mut Connection<State>, output: WlOutput) { + fn output_removed(&mut self, conn: &mut Connection<State>, output: WlOutput) { let index = self .output_statuses .iter() @@ -62,10 +62,29 @@ impl RiverInfoProvider { output_status.status.destroy(conn); } - pub fn click_on_tag( + fn get_tags(&self, output: WlOutput) -> Vec<Tag> { + let Some(status) = self.output_statuses.iter().find(|s| s.output == output) else { + return Vec::new(); + }; + (1..=self.max_tag) + .map(|tag| Tag { + name: tag.to_string(), + is_focused: status.focused_tags & (1 << (tag - 1)) != 0, + is_active: status.active_tags & (1 << (tag - 1)) != 0, + is_urgent: status.urgent_tags & (1 << (tag - 1)) != 0, + }) + .collect() + } + + fn get_layout_name(&self, output: WlOutput) -> Option<String> { + let status = self.output_statuses.iter().find(|s| s.output == output)?; + status.layout_name.clone() + } + + fn click_on_tag( &mut self, conn: &mut Connection<State>, - _: WlOutput, + _output: WlOutput, seat: WlSeat, tag: &str, btn: PointerBtn, @@ -82,6 +101,10 @@ impl RiverInfoProvider { self.control .run_command_with_cb(conn, seat, river_command_cb); } + + fn as_any(&mut self) -> &mut dyn Any { + self + } } fn output_status_cb( @@ -90,45 +113,40 @@ fn output_status_cb( output_status: ZriverOutputStatusV1, event: zriver_output_status_v1::Event, ) { - let WmInfoProvider::River(river) = &mut state.shared_state.wm_info_provider else { - unreachable!() - }; - let max_tag = state.shared_state.config.wm.river.max_tag.min(32); - + let river = state.shared_state.get_river().unwrap(); let status = river .output_statuses .iter_mut() .find(|s| s.status == output_status) .unwrap(); + let output = status.output; use zriver_output_status_v1::Event; match event { - Event::FocusedTags(tags) => status.focused_tags = tags, + Event::FocusedTags(tags) => { + status.focused_tags = tags; + state.tags_updated(conn, Some(output)); + } Event::ViewTags(tags) => { status.active_tags = tags .chunks_exact(4) .map(|bytes| u32::from_ne_bytes(bytes.try_into().unwrap())) .fold(0, |a, b| a | b); + state.tags_updated(conn, Some(output)); + } + Event::UrgentTags(tags) => { + status.urgent_tags = tags; + state.tags_updated(conn, Some(output)); + } + Event::LayoutName(ln) => { + status.layout_name = Some(ln.to_string_lossy().into()); + state.layout_name_updated(conn, Some(output)); + } + Event::LayoutNameClear => { + status.layout_name = None; + state.layout_name_updated(conn, Some(output)); } - Event::UrgentTags(tags) => status.urgent_tags = tags, - Event::LayoutName(ln) => status.layout_name = Some(ln.to_string_lossy().into()), - Event::LayoutNameClear => status.layout_name = None, } - - let info = WmInfo { - layout_name: status.layout_name.clone(), - tags: (1..=max_tag) - .map(|tag| Tag { - name: tag.to_string(), - is_focused: status.focused_tags & (1 << (tag - 1)) != 0, - is_active: status.active_tags & (1 << (tag - 1)) != 0, - is_urgent: status.urgent_tags & (1 << (tag - 1)) != 0, - }) - .collect(), - }; - - let output = status.output; - (river.callback)(conn, state, output, info); } fn river_command_cb( |
