summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bar.rs167
-rw-r--r--src/button_manager.rs2
-rw-r--r--src/color.rs8
-rw-r--r--src/main.rs2
-rw-r--r--src/shared_state.rs6
-rw-r--r--src/state.rs59
-rw-r--r--src/tags.rs48
-rw-r--r--src/wm_info_provider.rs58
-rw-r--r--src/wm_info_provider/river.rs164
9 files changed, 332 insertions, 182 deletions
diff --git a/src/bar.rs b/src/bar.rs
index 5bfa020..09e76d3 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -1,11 +1,11 @@
use std::collections::BinaryHeap;
-use std::ffi::CString;
use pangocairo::cairo;
use wayrs_client::connection::Connection;
use crate::button_manager::ButtonManager;
+use crate::color::Color;
use crate::config::Config;
use crate::i3bar_protocol::{self, Block, MinWidth};
use crate::ord_adaptor::DefaultLess;
@@ -13,8 +13,8 @@ use crate::pointer_btn::PointerBtn;
use crate::protocol::*;
use crate::shared_state::SharedState;
use crate::state::{ComputedBlock, State};
-use crate::tags::{compute_tag_label, TagState, TagsInfo};
use crate::text::{self, ComputedText, RenderOptions};
+use crate::wm_info_provider::WmInfo;
pub struct Bar {
pub output: WlOutput,
@@ -27,24 +27,24 @@ pub struct Bar {
pub surface: WlSurface,
pub layer_surface: ZwlrLayerSurfaceV1,
pub blocks_btns: ButtonManager<(Option<String>, Option<String>)>,
- pub river_output_status: Option<ZriverOutputStatusV1>,
- pub river_control: Option<ZriverControlV1>,
- pub layout_name: Option<String>,
+ pub wm_info: WmInfo,
+ pub tags_btns: ButtonManager<String>,
+ pub tags_computed: Vec<(ColorPair, ComputedText)>,
pub layout_name_computed: Option<ComputedText>,
- pub tags_btns: ButtonManager,
- pub tags_info: TagsInfo,
- pub tags_computed: Vec<ComputedText>,
}
-impl Bar {
- pub fn has_tags_provider(&self) -> bool {
- // TODO: add more tags providers
- self.river_output_status.is_some()
- }
+#[derive(Debug, PartialEq)]
+pub struct ColorPair {
+ bg: Color,
+ fg: Color,
+}
- pub fn set_layout_name(&mut self, layout_name: Option<String>) {
+impl Bar {
+ pub fn set_wm_info(&mut self, info: WmInfo) {
+ self.wm_info = info;
+ self.tags_btns.clear();
+ self.tags_computed.clear();
self.layout_name_computed = None;
- self.layout_name = layout_name;
}
pub fn click(
@@ -57,26 +57,16 @@ impl Bar {
_y: f64,
) -> anyhow::Result<()> {
if let Some(tag) = self.tags_btns.click(x) {
- if let Some(river_control) = &self.river_control {
- let cmd = match button {
- PointerBtn::Left => wayrs_client::cstr!("set-focused-tags"),
- PointerBtn::Right => wayrs_client::cstr!("toggle-focused-tags"),
+ if let Some(wm_info_provider) = &mut ss.wm_info_provider {
+ match button {
+ PointerBtn::Left => {
+ wm_info_provider.left_click_on_tag(conn, self.output, seat, tag)
+ }
+ PointerBtn::Right => {
+ wm_info_provider.middle_click_on_tag(conn, self.output, seat, tag)
+ }
_ => return Ok(()),
- };
- river_control.add_argument(conn, cmd.into());
- river_control.add_argument(conn, CString::new((1u32 << tag).to_string()).unwrap());
- river_control.run_command_with_cb(
- conn,
- seat,
- |conn, state, _, event| match event {
- zriver_command_callback_v1::Event::Success(msg) => {
- info!("river_control: {msg:?}")
- }
- zriver_command_callback_v1::Event::Failure(msg) => {
- state.set_error(conn, msg.to_string_lossy())
- }
- },
- );
+ }
}
} else if let Some((name, instance)) = self.blocks_btns.click(x) {
if let Some(cmd) = &mut ss.status_cmd {
@@ -130,59 +120,61 @@ impl Bar {
cairo_ctx.paint().unwrap();
cairo_ctx.restore().unwrap();
- // Display tags
- let mut offset_left = 0.0;
- if self.has_tags_provider() {
- if self.tags_computed.is_empty() {
- // TODO make configurable
- for text in ["1", "2", "3", "4", "5", "6", "7", "8", "9"] {
- self.tags_computed
- .push(compute_tag_label(text, &ss.config, &cairo_ctx));
- }
- }
+ // Compute tags
+ if self.tags_computed.is_empty() {
+ let mut offset_left = 0.0;
self.tags_btns.clear();
- for (i, label) in self.tags_computed.iter().enumerate() {
- let state = self.tags_info.get_state(i);
- let (bg, fg) = match state {
- TagState::Urgent => (ss.config.tag_urgent_bg, ss.config.tag_urgent_fg),
- TagState::Focused => (ss.config.tag_focused_bg, ss.config.tag_focused_fg),
- TagState::Active => (ss.config.tag_bg, ss.config.tag_fg),
- TagState::Inactive => {
- if ss.config.hide_inactive_tags {
- continue;
- }
- (ss.config.tag_inactive_bg, ss.config.tag_inactive_fg)
- }
+ for tag in &self.wm_info.tags {
+ let (bg, fg) = if tag.is_urgent {
+ (ss.config.tag_urgent_bg, ss.config.tag_urgent_fg)
+ } else if tag.is_focused {
+ (ss.config.tag_focused_bg, ss.config.tag_focused_fg)
+ } else if tag.is_active {
+ (ss.config.tag_bg, ss.config.tag_fg)
+ } else if !ss.config.hide_inactive_tags {
+ (ss.config.tag_inactive_bg, ss.config.tag_inactive_fg)
+ } else {
+ continue;
};
- label.render(
- &cairo_ctx,
- RenderOptions {
- x_offset: offset_left,
- bar_height: height_f,
- fg_color: fg,
- bg_color: Some(bg),
- r_left: if i == 0 || self.tags_info.get_state(i.saturating_sub(1)) != state
- {
- ss.config.tags_r
- } else {
- 0.0
- },
- r_right: if i == 8 || self.tags_info.get_state(i + 1) != state {
- ss.config.tags_r
- } else {
- 0.0
- },
- overlap: 0.0,
- },
- );
- self.tags_btns.push(offset_left, label.width, i);
- offset_left += label.width;
+ let comp = compute_tag_label(&tag.name, &ss.config, &cairo_ctx);
+ self.tags_btns
+ .push(offset_left, comp.width, tag.name.clone());
+ offset_left += comp.width;
+ self.tags_computed.push((ColorPair { bg, fg }, comp));
}
}
+ // Display tags
+ let mut offset_left = 0.0;
+ for (i, label) in self.tags_computed.iter().enumerate() {
+ label.1.render(
+ &cairo_ctx,
+ RenderOptions {
+ x_offset: offset_left,
+ bar_height: height_f,
+ fg_color: label.0.fg,
+ bg_color: Some(label.0.bg),
+ r_left: if i == 0 || self.tags_computed[i - 1].0 != label.0 {
+ ss.config.tags_r
+ } else {
+ 0.0
+ },
+ r_right: if i + 1 == self.tags_computed.len()
+ || self.tags_computed[i + 1].0 != label.0
+ {
+ ss.config.tags_r
+ } else {
+ 0.0
+ },
+ overlap: 0.0,
+ },
+ );
+ offset_left += label.1.width;
+ }
+
// Display layout name
if ss.config.show_layout_name {
- if let Some(layout_name) = &self.layout_name {
+ if let Some(layout_name) = &self.wm_info.layout_name {
let text = self.layout_name_computed.get_or_insert_with(|| {
ComputedText::new(
layout_name,
@@ -457,3 +449,18 @@ fn render_blocks(
}
}
}
+
+pub fn compute_tag_label(label: &str, config: &Config, context: &cairo::Context) -> ComputedText {
+ ComputedText::new(
+ label,
+ text::Attributes {
+ font: &config.font.0,
+ padding_left: config.tags_padding,
+ padding_right: config.tags_padding,
+ min_width: None,
+ align: Default::default(),
+ markup: false,
+ },
+ context,
+ )
+}
diff --git a/src/button_manager.rs b/src/button_manager.rs
index e46a139..9981229 100644
--- a/src/button_manager.rs
+++ b/src/button_manager.rs
@@ -1,4 +1,4 @@
-#[derive(Default)]
+#[derive(Debug, Default)]
pub struct ButtonManager<T = usize>(Vec<(f64, f64, T)>);
impl<T> ButtonManager<T> {
diff --git a/src/color.rs b/src/color.rs
index 7305c24..7cb7964 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -13,11 +13,7 @@ pub struct Color {
impl Color {
pub fn apply(self, cr: &Context) {
- if self.alpha.is_nan() {
- cr.set_source_rgb(self.red, self.green, self.blue);
- } else {
- cr.set_source_rgba(self.red, self.green, self.blue, self.alpha);
- }
+ cr.set_source_rgba(self.red, self.green, self.blue, self.alpha);
}
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
@@ -25,7 +21,7 @@ impl Color {
red: r as f64 / 255.0,
green: g as f64 / 255.0,
blue: b as f64 / 255.0,
- alpha: if a == 255 { f64::NAN } else { a as f64 / 255.0 },
+ alpha: a as f64 / 255.0,
}
}
diff --git a/src/main.rs b/src/main.rs
index 7ef0acb..b1498e5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,9 +17,9 @@ mod protocol;
mod shared_state;
mod state;
mod status_cmd;
-mod tags;
mod text;
mod utils;
+mod wm_info_provider;
use wayrs_client::connection::Connection;
diff --git a/src/shared_state.rs b/src/shared_state.rs
index dd18606..fd4084e 100644
--- a/src/shared_state.rs
+++ b/src/shared_state.rs
@@ -1,4 +1,7 @@
-use crate::{config::Config, i3bar_protocol::Block, state::ComputedBlock, status_cmd::StatusCmd};
+use crate::{
+ config::Config, i3bar_protocol::Block, state::ComputedBlock, status_cmd::StatusCmd,
+ wm_info_provider::WmInfoProvider,
+};
use wayrs_shm_alloc::ShmAlloc;
pub struct SharedState {
@@ -7,4 +10,5 @@ pub struct SharedState {
pub status_cmd: Option<StatusCmd>,
pub blocks: Vec<Block>,
pub blocks_cache: Vec<ComputedBlock>,
+ pub wm_info_provider: Option<Box<dyn WmInfoProvider>>,
}
diff --git a/src/state.rs b/src/state.rs
index 3c1ab89..c283678 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,5 +1,6 @@
use crate::cursor::CursorTheme;
use crate::protocol::*;
+use crate::wm_info_provider::*;
use std::future::pending;
@@ -10,16 +11,13 @@ use wayrs_shm_alloc::{ShmAlloc, ShmAllocState};
use crate::{
bar::Bar, config::Config, i3bar_protocol::Block, pointer_btn::PointerBtn,
- shared_state::SharedState, status_cmd::StatusCmd, text::ComputedText,
+ shared_state::SharedState, status_cmd::StatusCmd, text::ComputedText, wm_info_provider,
};
pub struct State {
wl_compositor: WlCompositor,
layer_shell: ZwlrLayerShellV1,
- river_status_manager: Option<ZriverStatusManagerV1>,
- river_control: Option<ZriverControlV1>,
-
pub seats: Vec<Seat>,
pub bars: Vec<Bar>,
@@ -72,9 +70,6 @@ impl State {
.bind(conn, 1..=4)
.expect("could not bind layer_shell"),
- river_status_manager: globals.bind(conn, 1..=4).ok(),
- river_control: globals.bind(conn, 1..=1).ok(),
-
seats: Vec::new(),
bars: Vec::new(),
@@ -84,6 +79,7 @@ impl State {
status_cmd,
blocks: Vec::new(),
blocks_cache: Vec::new(),
+ wm_info_provider: wm_info_provider::bind_wayland(conn, globals, wm_info_cb),
},
cursor_theme: cursor_theme_ok.then_some(cursor_theme),
@@ -153,6 +149,10 @@ impl State {
.bind_with_cb(conn, 2..=4, wl_output_cb)
.expect("could not bind wl_output");
+ if let Some(wm_info_provider) = &mut self.shared_state.wm_info_provider {
+ wm_info_provider.new_outut(conn, output);
+ }
+
let surface = self.wl_compositor.create_surface(conn);
use zwlr_layer_shell_v1::Layer;
@@ -171,11 +171,6 @@ impl State {
// Note: layer_surface is commited when we receive the scale factor of this output
- let river_output_status = self
- .river_status_manager
- .as_ref()
- .map(|s| s.get_river_output_status_with_cb(conn, output, river_output_status_cb));
-
self.bars.push(Bar {
output,
output_reg_name: global.name,
@@ -187,13 +182,10 @@ impl State {
surface,
layer_surface,
blocks_btns: Default::default(),
- river_output_status,
- river_control: self.river_control,
- layout_name: None,
- layout_name_computed: None,
+ wm_info: Default::default(),
tags_btns: Default::default(),
- tags_info: Default::default(),
tags_computed: Vec::new(),
+ layout_name_computed: None,
});
}
@@ -217,8 +209,8 @@ impl State {
let bar = self.bars.swap_remove(bar_index);
bar.surface.destroy(conn);
bar.layer_surface.destroy(conn);
- if let Some(output_status) = bar.river_output_status {
- output_status.destroy(conn);
+ if let Some(wm_info_provider) = &mut self.shared_state.wm_info_provider {
+ wm_info_provider.output_removed(conn, bar.output);
}
if bar.output.version() >= 3 {
bar.output.release(conn);
@@ -476,32 +468,9 @@ fn wl_pointer_cb(
}
}
-fn river_output_status_cb(
- conn: &mut Connection<State>,
- state: &mut State,
- status: ZriverOutputStatusV1,
- event: zriver_output_status_v1::Event,
-) {
- let bar = state
- .bars
- .iter_mut()
- .find(|b| b.river_output_status == Some(status))
- .unwrap();
-
- use zriver_output_status_v1::Event;
- match event {
- Event::FocusedTags(tags) => bar.tags_info.focused = tags,
- Event::UrgentTags(tags) => bar.tags_info.urgent = tags,
- Event::LayoutName(name) => bar.set_layout_name(Some(name.into_string().unwrap())),
- Event::LayoutNameClear => bar.set_layout_name(None),
- Event::ViewTags(vt) => {
- bar.tags_info.active = vt
- .chunks_exact(4)
- .map(|bytes| u32::from_ne_bytes(bytes.try_into().unwrap()))
- .fold(0, |a, b| a | b);
- }
- }
-
+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 == output).unwrap();
+ bar.set_wm_info(info);
bar.request_frame(conn);
}
diff --git a/src/tags.rs b/src/tags.rs
deleted file mode 100644
index e3b87ab..0000000
--- a/src/tags.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use crate::config::Config;
-use crate::text::{Attributes, ComputedText};
-
-use pangocairo::cairo::Context;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum TagState {
- Urgent,
- Focused,
- Active,
- Inactive,
-}
-
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
-pub struct TagsInfo {
- pub focused: u32,
- pub urgent: u32,
- pub active: u32,
-}
-
-impl TagsInfo {
- pub fn get_state(self, tag: usize) -> TagState {
- if self.urgent >> tag & 1 == 1 {
- TagState::Urgent
- } else if self.focused >> tag & 1 == 1 {
- TagState::Focused
- } else if self.active >> tag & 1 == 1 {
- TagState::Active
- } else {
- TagState::Inactive
- }
- }
-}
-
-pub fn compute_tag_label(label: &str, config: &Config, context: &Context) -> ComputedText {
- ComputedText::new(
- label,
- Attributes {
- font: &config.font.0,
- padding_left: config.tags_padding,
- padding_right: config.tags_padding,
- min_width: None,
- align: Default::default(),
- markup: false,
- },
- context,
- )
-}
diff --git a/src/wm_info_provider.rs b/src/wm_info_provider.rs
new file mode 100644
index 0000000..b7854b1
--- /dev/null
+++ b/src/wm_info_provider.rs
@@ -0,0 +1,58 @@
+mod river;
+
+use std::any::Any;
+
+use wayrs_client::connection::Connection;
+use wayrs_client::global::*;
+
+use crate::protocol::*;
+use crate::state::State;
+
+pub type WmInfoCallback = fn(&mut Connection<State>, &mut State, WlOutput, WmInfo);
+
+pub trait WmInfoProvider {
+ fn as_any(&mut self) -> &mut dyn Any;
+
+ fn new_outut(&mut self, conn: &mut Connection<State>, output: WlOutput);
+
+ fn output_removed(&mut self, conn: &mut Connection<State>, output: WlOutput);
+
+ fn left_click_on_tag(
+ &mut self,
+ conn: &mut Connection<State>,
+ output: WlOutput,
+ seat: WlSeat,
+ tag: &str,
+ );
+
+ fn middle_click_on_tag(
+ &mut self,
+ conn: &mut Connection<State>,
+ output: WlOutput,
+ seat: WlSeat,
+ tag: &str,
+ );
+}
+
+#[derive(Default)]
+pub struct WmInfo {
+ pub layout_name: Option<String>,
+ pub tags: Vec<Tag>,
+}
+
+pub struct Tag {
+ pub name: String,
+ pub is_focused: bool,
+ pub is_active: bool,
+ pub is_urgent: bool,
+}
+
+pub fn bind_wayland(
+ conn: &mut Connection<State>,
+ globals: &Globals,
+ callback: WmInfoCallback,
+) -> Option<Box<dyn WmInfoProvider>> {
+ // TODO: add more providers
+ let river = river::RiverInfoProvider::bind(conn, globals, callback)?;
+ Some(Box::new(river))
+}
diff --git a/src/wm_info_provider/river.rs b/src/wm_info_provider/river.rs
new file mode 100644
index 0000000..dc42d71
--- /dev/null
+++ b/src/wm_info_provider/river.rs
@@ -0,0 +1,164 @@
+use std::any::Any;
+use std::ffi::CString;
+
+use wayrs_client::connection::Connection;
+use wayrs_client::global::*;
+
+use super::*;
+use crate::protocol::*;
+use crate::state::State;
+
+pub(super) struct RiverInfoProvider {
+ status_manager: ZriverStatusManagerV1,
+ control: ZriverControlV1,
+ output_statuses: Vec<OutputStatus>,
+ callback: WmInfoCallback,
+}
+
+struct OutputStatus {
+ output: WlOutput,
+ status: ZriverOutputStatusV1,
+ focused_tags: u32,
+ urgent_tags: u32,
+ active_tags: u32,
+ layout_name: Option<String>,
+}
+
+impl RiverInfoProvider {
+ pub(super) fn bind(
+ conn: &mut Connection<State>,
+ globals: &Globals,
+ callback: WmInfoCallback,
+ ) -> Option<Self> {
+ Some(Self {
+ status_manager: globals.bind(conn, 1..=4).ok()?,
+ control: globals.bind(conn, 1..=1).ok()?,
+ output_statuses: Vec::new(),
+ callback,
+ })
+ }
+}
+
+impl WmInfoProvider for RiverInfoProvider {
+ fn as_any(&mut self) -> &mut dyn Any {
+ self
+ }
+
+ fn new_outut(&mut self, conn: &mut Connection<State>, output: WlOutput) {
+ let status =
+ self.status_manager
+ .get_river_output_status_with_cb(conn, output, output_status_cb);
+ self.output_statuses.push(OutputStatus {
+ output,
+ status,
+ focused_tags: 0,
+ urgent_tags: 0,
+ active_tags: 0,
+ layout_name: None,
+ });
+ }
+
+ fn output_removed(&mut self, conn: &mut Connection<State>, output: WlOutput) {
+ let index = self
+ .output_statuses
+ .iter()
+ .position(|s| s.output == output)
+ .unwrap();
+ let output_status = self.output_statuses.swap_remove(index);
+ output_status.status.destroy(conn);
+ }
+
+ fn left_click_on_tag(
+ &mut self,
+ conn: &mut Connection<State>,
+ _: WlOutput,
+ seat: WlSeat,
+ tag: &str,
+ ) {
+ let tag: u32 = tag.parse().unwrap();
+ self.control
+ .add_argument(conn, CString::new("set-focused-tags").unwrap());
+ self.control
+ .add_argument(conn, CString::new((1u32 << (tag - 1)).to_string()).unwrap());
+ self.control
+ .run_command_with_cb(conn, seat, river_command_cb);
+ }
+
+ fn middle_click_on_tag(
+ &mut self,
+ conn: &mut Connection<State>,
+ _: WlOutput,
+ seat: WlSeat,
+ tag: &str,
+ ) {
+ let tag: u32 = tag.parse().unwrap();
+ self.control
+ .add_argument(conn, CString::new("toggle-focused-tags").unwrap());
+ self.control
+ .add_argument(conn, CString::new((1u32 << (tag - 1)).to_string()).unwrap());
+ self.control
+ .run_command_with_cb(conn, seat, river_command_cb);
+ }
+}
+
+fn output_status_cb(
+ conn: &mut Connection<State>,
+ state: &mut State,
+ output_status: ZriverOutputStatusV1,
+ event: zriver_output_status_v1::Event,
+) {
+ let river: &mut RiverInfoProvider = state
+ .shared_state
+ .wm_info_provider
+ .as_mut()
+ .unwrap()
+ .as_any()
+ .downcast_mut()
+ .unwrap();
+
+ let status = river
+ .output_statuses
+ .iter_mut()
+ .find(|s| s.status == output_status)
+ .unwrap();
+
+ use zriver_output_status_v1::Event;
+ match event {
+ Event::FocusedTags(tags) => status.focused_tags = tags,
+ 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);
+ }
+ 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..10)
+ .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(
+ conn: &mut Connection<State>,
+ state: &mut State,
+ _: ZriverCommandCallbackV1,
+ event: zriver_command_callback_v1::Event,
+) {
+ if let zriver_command_callback_v1::Event::Failure(msg) = event {
+ state.set_error(conn, msg.to_string_lossy())
+ }
+}