summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bar.rs22
-rw-r--r--src/shared_state.rs19
-rw-r--r--src/state.rs75
-rw-r--r--src/text.rs9
-rw-r--r--src/wm_info_provider.rs87
-rw-r--r--src/wm_info_provider/ext_workspace_unstable.rs110
-rw-r--r--src/wm_info_provider/river.rs82
7 files changed, 216 insertions, 188 deletions
diff --git a/src/bar.rs b/src/bar.rs
index 5f24478..45fbc88 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::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(