Make block interface more robust and allow for multi-part blocks

master
Rasmus Rosengren 3 years ago
parent dba83eb64a
commit f3cd64ab56
Signed by: rsrp
GPG Key ID: A13BC7BC4F81CF5F
  1. 51
      src/blocks/cpu.rs
  2. 17
      src/blocks/memory.rs
  3. 185
      src/blocks/mod.rs
  4. 13
      src/blocks/time.rs
  5. 164
      src/main.rs
  6. 2
      src/protocol/i3bar_event.rs
  7. 4
      src/util.rs

@ -1,6 +1,13 @@
use std::sync::{atomic::AtomicBool, Arc};
use systemstat::Platform;
use crate::Block;
use crate::{
blocks::{BlockInfo, Color},
Block,
};
use super::{BlockEvent, BlockPartName, BlockUpdate};
pub struct CpuBlock {
pub id: usize,
@ -15,20 +22,46 @@ impl Block for CpuBlock {
"cpu"
}
fn run(
&mut self,
_event_r: flume::Receiver<crate::protocol::i3bar_event::I3BarEvent>,
update_s: flume::Sender<(usize, String)>,
) {
fn run(&mut self, _event_r: flume::Receiver<BlockEvent>, update_s: flume::Sender<BlockUpdate>) {
let on = Arc::new(AtomicBool::new(false));
{
let on = on.clone();
std::thread::spawn(move || loop {
let event = _event_r.recv().unwrap();
match event.part_name {
BlockPartName::Named(name) => {
if name == "icon" {
let on_value = on.load(std::sync::atomic::Ordering::Relaxed);
on.store(!on_value, std::sync::atomic::Ordering::Relaxed);
}
}
_ => (),
}
});
}
let sys = systemstat::System::new();
loop {
let cpu = sys.cpu_load_aggregate().unwrap();
std::thread::sleep(std::time::Duration::from_millis(1000));
let cpu = cpu.done().unwrap();
let load_percentage = 100.0 * cpu.user;
update_s
.send((self.id, format!(" Cpu: {:.2}% ", load_percentage)))
.unwrap();
let fg_color = if on.load(std::sync::atomic::Ordering::Relaxed) {
Color::Rgb(0, 255, 0)
} else {
Color::Rgb(0, 0, 255)
};
let update = BlockUpdate::Multi(vec![
BlockInfo::from_unnamed(String::from(" ")).build(),
BlockInfo::from_named(String::from("icon"), String::from("\u{f3fd}"))
.fg_color(fg_color)
.bg_color(Color::Rgb(255, 255, 0))
.build(),
BlockInfo::from_unnamed(format!("{:>5.2}% ", load_percentage))
.fg_color(Color::Rgb(0, 255, 0))
.build(),
]);
update_s.send(update).unwrap();
}
}
}

@ -1,6 +1,8 @@
use systemstat::Platform;
use crate::Block;
use crate::{blocks::BlockInfo, Block};
use super::{BlockEvent, BlockUpdate};
pub struct MemoryBlock {
pub id: usize,
@ -15,20 +17,17 @@ impl Block for MemoryBlock {
"memory"
}
fn run(
&mut self,
_event_r: flume::Receiver<crate::protocol::i3bar_event::I3BarEvent>,
update_s: flume::Sender<(usize, String)>,
) {
fn run(&mut self, _event_r: flume::Receiver<BlockEvent>, update_s: flume::Sender<BlockUpdate>) {
let sys = systemstat::System::new();
loop {
let mem = sys.memory().unwrap();
let bytes_used = systemstat::saturating_sub_bytes(mem.total, mem.free).as_u64();
let bytes_total = mem.total.as_u64();
let mem_percentage = 100.0 * bytes_used as f64 / bytes_total as f64;
update_s
.send((self.id, format!(" Mem: {:.2}% ", mem_percentage)))
.unwrap();
let update = BlockUpdate::Single(
BlockInfo::from_main(format!(" \u{f2db}{:>5.2}% ", mem_percentage)).build(),
);
update_s.send(update).unwrap();
std::thread::sleep(std::time::Duration::from_millis(1000));
}
}

@ -1,3 +1,188 @@
pub mod cpu;
pub mod memory;
pub mod time;
use std::ops::Deref;
use serde::Serialize;
use crate::protocol::i3bar_event::{I3BarEventButton, I3BarEventModifier};
pub trait Block {
fn id(&self) -> usize;
fn name(&self) -> &str;
fn run(&mut self, event_r: flume::Receiver<BlockEvent>, update_s: flume::Sender<BlockUpdate>);
}
#[derive(Clone, Debug)]
pub enum BlockUpdate {
Single(BlockInfo),
Multi(Vec<BlockInfo>),
}
#[derive(Clone, Debug, Default)]
pub struct BlockInfo {
pub part_name: BlockPartName,
pub full_text: String,
pub short_text: Option<String>,
pub fg_color: Color,
pub bg_color: Color,
pub border: Option<BlockBorder>,
pub min_width: Option<i32>,
pub urgent: Option<bool>,
}
impl BlockInfo {
pub fn from_main(full_text: String) -> BlockInfoBuilder {
BlockInfoBuilder::new(full_text, BlockPartName::Main)
}
pub fn from_named(name: String, full_text: String) -> BlockInfoBuilder {
BlockInfoBuilder::new(full_text, BlockPartName::Named(name))
}
pub fn from_unnamed(full_text: String) -> BlockInfoBuilder {
BlockInfoBuilder::new(full_text, BlockPartName::Unnamed)
}
}
pub struct BlockInfoBuilder {
inner: BlockInfo,
}
impl BlockInfoBuilder {
pub fn new(full_text: String, part_name: BlockPartName) -> Self {
Self {
inner: BlockInfo {
full_text,
part_name,
..Default::default()
},
}
}
pub fn short_text(mut self, short_text: Option<String>) -> Self {
self.inner.short_text = short_text;
self
}
pub fn fg_color(mut self, fg_color: Color) -> Self {
self.inner.fg_color = fg_color;
self
}
pub fn bg_color(mut self, bg_color: Color) -> Self {
self.inner.bg_color = bg_color;
self
}
pub fn border(mut self, border: Option<BlockBorder>) -> Self {
self.inner.border = border;
self
}
pub fn min_width(mut self, min_width: Option<i32>) -> Self {
self.inner.min_width = min_width;
self
}
pub fn urgent(mut self, urgent: Option<bool>) -> Self {
self.inner.urgent = urgent;
self
}
pub fn build(self) -> BlockInfo {
self.inner
}
}
impl Deref for BlockInfoBuilder {
type Target = BlockInfo;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Clone, Debug)]
pub enum BlockPartName {
Main,
Unnamed,
Named(String),
}
impl From<&str> for BlockPartName {
fn from(part_name_raw: &str) -> Self {
match part_name_raw {
"__main" => Self::Main,
"__unnamed" => Self::Unnamed,
s => Self::Named(s.to_owned()),
}
}
}
impl From<BlockPartName> for String {
fn from(block_part_name: BlockPartName) -> Self {
match block_part_name {
BlockPartName::Main => String::from("__main"),
BlockPartName::Unnamed => String::from("__unnamed"),
BlockPartName::Named(s) => s,
}
}
}
impl Default for BlockPartName {
fn default() -> Self {
Self::Main
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct BlockBorder {
pub color: Color,
pub border_top: Option<i32>,
pub top: Option<i32>,
pub right: Option<i32>,
pub bottom: Option<i32>,
pub left: Option<i32>,
}
#[derive(Clone, Copy, Debug)]
pub enum Color {
Default,
Rgb(u8, u8, u8),
Rgba(u8, u8, u8, u8),
}
impl Color {
pub fn to_hex(&self) -> Option<String> {
match *self {
Color::Default => None,
Color::Rgb(r, g, b) => Some(format!("#{:02x}{:02x}{:02x}", r, g, b)),
Color::Rgba(r, g, b, a) => Some(format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a)),
}
}
}
impl Default for Color {
fn default() -> Self {
Self::Default
}
}
#[derive(Clone, Debug)]
pub struct BlockEvent {
pub part_name: BlockPartName,
pub button: I3BarEventButton,
pub modifiers: Vec<I3BarEventModifier>,
}
pub trait BlockConfig: Block {
type Config: Serialize;
fn new(id: usize, block_config: Self::Config, shared_config: String) -> std::io::Result<Self>
where
Self: Sized;
}

@ -2,6 +2,8 @@ use chrono::Timelike;
use crate::Block;
use super::{BlockEvent, BlockInfo, BlockUpdate};
pub struct TimeBlock {
pub id: usize,
}
@ -15,15 +17,12 @@ impl Block for TimeBlock {
"time"
}
fn run(
&mut self,
_event_r: flume::Receiver<crate::protocol::i3bar_event::I3BarEvent>,
update_s: flume::Sender<(usize, String)>,
) {
fn run(&mut self, _event_r: flume::Receiver<BlockEvent>, update_s: flume::Sender<BlockUpdate>) {
loop {
let now = chrono::Local::now();
let time_string = now.format(" %a %d/%m %R ").to_string();
update_s.send((self.id, time_string)).unwrap();
let time_string = now.format(" \u{f017}%a %d/%m %R ").to_string();
let update = BlockUpdate::Single(BlockInfo::from_main(time_string).build());
update_s.send(update).unwrap();
let second = now.second() as u64;
std::thread::sleep(std::time::Duration::from_secs(60 - second));

@ -4,11 +4,12 @@ mod util;
use std::collections::HashMap;
use protocol::i3bar_event::I3BarEvent;
use crate::protocol::{
i3bar_block::{write_blocks, I3BarBlock},
i3bar_event::read_event,
use crate::{
blocks::{Block, BlockPartName, BlockUpdate},
protocol::{
i3bar_block::{write_blocks, I3BarBlock},
i3bar_event::read_event,
},
};
fn main() {
@ -27,20 +28,39 @@ fn main() {
// Start all the blocks
for mut block in blocks {
let block_id = block.id();
let (event_s, event_r) = flume::unbounded();
event_senders.insert(block.id(), event_s);
event_senders.insert(block_id, event_s);
let (block_update_s, block_update_r) = flume::unbounded();
let update_s = update_s.clone();
std::thread::spawn(move || loop {
let update = block_update_r.recv().unwrap();
update_s.send((block_id, update)).unwrap();
});
std::thread::spawn(move || {
block.run(event_r, update_s);
block.run(event_r, block_update_s);
});
}
// Receive events and send them to the correct blocks
std::thread::spawn(move || loop {
let event = read_event().unwrap();
let id = event.instance.parse().unwrap();
if let Some(event_sender) = event_senders.get(&id) {
event_sender.send(event).unwrap();
let (id, part_name) = event.instance.split_once(',').unwrap();
let id = id.parse().unwrap();
let part_name = part_name.into();
match part_name {
BlockPartName::Unnamed => (),
_ => {
if let Some(event_sender) = event_senders.get(&id) {
event_sender
.send(blocks::BlockEvent {
part_name: part_name.into(),
button: event.button,
modifiers: event.modifiers,
})
.unwrap();
}
}
}
});
@ -50,30 +70,108 @@ fn main() {
loop {
let (id, content) = update_r.recv().unwrap();
cache.insert(id, content);
let rendered_blocks = block_metas
.iter()
.rev()
.filter_map(|(id, name)| cache.get(id).map(|content| (id, name, content.clone())))
.map(|(id, name, content)| I3BarBlock {
full_text: content,
name: name.clone(),
instance: id.to_string(),
color: Some(String::from("#ffffff")),
..Default::default()
})
.collect();
write_blocks(rendered_blocks);
}
}
let mut rendered_blocks = Vec::new();
for (id, name) in block_metas.iter().rev() {
let block = match cache.get(id) {
Some(block) => block,
None => continue,
};
pub trait Block {
fn id(&self) -> usize;
match block {
BlockUpdate::Single(block) => {
match block.part_name {
BlockPartName::Main => (),
_ => panic!("Single part blocks cannot be named or unnamed"),
}
fn name(&self) -> &str;
let block_part_name: String = block.part_name.to_owned().into();
let instance_name = format!("{},{}", id, block_part_name);
let rendered_block = I3BarBlock {
name: name.to_owned(),
instance: instance_name,
full_text: block.full_text.to_owned(),
short_text: block.short_text.to_owned(),
color: block.fg_color.to_hex(),
background: block.bg_color.to_hex(),
border: block.border.and_then(|border| border.color.to_hex()),
border_top: block
.border
.and_then(|border| border.top)
.or_else(|| Some(0)),
border_right: block
.border
.and_then(|border| border.right)
.or_else(|| Some(0)),
border_bottom: block
.border
.and_then(|border| border.bottom)
.or_else(|| Some(0)),
border_left: block
.border
.and_then(|border| border.left)
.or_else(|| Some(0)),
min_width: block.min_width,
urgent: block.urgent,
..Default::default()
};
rendered_blocks.push(rendered_block);
}
BlockUpdate::Multi(blocks) => {
let blocks_len = blocks.len();
for (i, block) in blocks.into_iter().enumerate() {
if let BlockPartName::Main = block.part_name {
panic!("Multi part blocks cannot have main")
}
fn run(
&mut self,
event_r: flume::Receiver<I3BarEvent>,
update_s: flume::Sender<(usize, String)>,
);
let block_part_name: String = block.part_name.to_owned().into();
let instance_name = format!("{},{}", id, block_part_name);
let rendered_block = I3BarBlock {
name: name.to_owned(),
instance: instance_name,
full_text: block.full_text.to_owned(),
short_text: block.short_text.to_owned(),
color: block.fg_color.to_hex(),
background: block.bg_color.to_hex(),
border: block.border.and_then(|border| border.color.to_hex()),
border_top: block
.border
.and_then(|border| border.top)
.or_else(|| Some(0)),
border_right: block
.border
.and_then(|border| border.right)
.or_else(|| Some(0)),
border_bottom: block
.border
.and_then(|border| border.bottom)
.or_else(|| Some(0)),
border_left: block
.border
.and_then(|border| border.left)
.or_else(|| Some(0)),
min_width: block.min_width,
urgent: block.urgent,
separator: Some(i == blocks_len - 1),
separator_block_width: if i == blocks_len - 1 { None } else { Some(0) },
..Default::default()
};
rendered_blocks.push(rendered_block);
}
}
}
}
// let rendered_blocks = block_metas
// .iter()
// .rev()
// .filter_map(|(id, name)| cache.get(id).map(|content| (id, name, content.clone())))
// .map(|(id, name, content)| I3BarBlock {
// full_text: content,
// name: name.clone(),
// instance: id.to_string(),
// color: Some(String::from("#ffffff")),
// ..Default::default()
// })
// .collect();
write_blocks(rendered_blocks);
}
}

@ -25,7 +25,7 @@ pub fn read_event() -> std::io::Result<I3BarEvent> {
let slice = slice.trim_end_matches(|c| c != '}');
// If empty line, read another line
if slice == "" {
if slice.is_empty() {
return read_event();
}

@ -1,6 +1,6 @@
use std::io::Write;
pub fn _debug<'s, S1, S2>(loc: S1, content: S2)
pub fn _debug<S1, S2>(loc: S1, content: S2)
where
S1: AsRef<str>,
S2: AsRef<str>,
@ -8,7 +8,7 @@ where
let mut file = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open("/home/rosen/dev/i3status-simple/debug.log")
.open("/home/rosen/i3status-simple-debug.log")
.unwrap();
file.write_all(format!("[{:?}]: {:?}\n", loc.as_ref(), content.as_ref()).as_bytes())
.unwrap();

Loading…
Cancel
Save