diff options
| author | MaxVerevkin <maxxverrr@gmail.com> | 2023-01-31 23:39:16 +0200 |
|---|---|---|
| committer | MaxVerevkin <maxxverrr@gmail.com> | 2023-01-31 23:39:16 +0200 |
| commit | e8c64f46d9a8e79afb13874813fa6a739025d28c (patch) | |
| tree | d4a5ce6354b5fe42ee91064e018ff0547f451d19 /src | |
| parent | 3200ac24848dd8820ad65b5ec83dc741999b1581 (diff) | |
| download | i3bar-mango-e8c64f46d9a8e79afb13874813fa6a739025d28c.tar.gz | |
abstract over tag and layout name provider
Diffstat (limited to 'src')
| -rw-r--r-- | src/bar.rs | 167 | ||||
| -rw-r--r-- | src/button_manager.rs | 2 | ||||
| -rw-r--r-- | src/color.rs | 8 | ||||
| -rw-r--r-- | src/main.rs | 2 | ||||
| -rw-r--r-- | src/shared_state.rs | 6 | ||||
| -rw-r--r-- | src/state.rs | 59 | ||||
| -rw-r--r-- | src/tags.rs | 48 | ||||
| -rw-r--r-- | src/wm_info_provider.rs | 58 | ||||
| -rw-r--r-- | src/wm_info_provider/river.rs | 164 |
9 files changed, 332 insertions, 182 deletions
@@ -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()) + } +} |
