From 2f404f36468453e12619757a8d112e525bc4ef15 Mon Sep 17 00:00:00 2001 From: Rasmus Rosengren Date: Fri, 27 Aug 2021 02:08:30 +0200 Subject: [PATCH] Initial commit, outputs bar that almost works --- .gitignore | 1 + Cargo.lock | 283 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 11 ++ src/main.rs | 124 ++++++++++++++++ src/protocol/i3bar_block.rs | 38 +++++ src/protocol/i3bar_event.rs | 90 ++++++++++++ src/protocol/mod.rs | 6 + 7 files changed, 553 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs create mode 100644 src/protocol/i3bar_block.rs create mode 100644 src/protocol/i3bar_event.rs create mode 100644 src/protocol/mod.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0a068ef --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,283 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bumpalo" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "flume" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24c3fd473b3a903a62609e413ed7538f99e10b665ecb502b5e481a95283f8ab4" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + +[[package]] +name = "futures-core" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99" + +[[package]] +name = "futures-sink" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53" + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "i3status-simple" +version = "0.1.0" +dependencies = [ + "flume", + "serde", + "serde_json", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "js-sys" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bf49d50e2961077d9c99f4b7997d770a1114f087c3c2e0069b36c13fc2979d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" + +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "nanorand" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729eb334247daa1803e0a094d0a5c55711b85571179f5ec6e53eccfdf7008958" +dependencies = [ + "getrandom", +] + +[[package]] +name = "pin-project" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "336b10da19a12ad094b59d870ebde26a45402e5b470add4b5fd03c5048a32127" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bfb1f86 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "i3status-simple" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +flume = "0.10" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..22dccd0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,124 @@ +use std::sync::mpsc; +use std::{collections::HashMap, io::Write}; + +use protocol::i3bar_event::I3BarEvent; + +use crate::protocol::{ + i3bar_block::{write_blocks, I3BarBlock}, + i3bar_event::read_event, +}; + +mod protocol; + +fn main() { + protocol::init(); + + let (event_s, event_r) = flume::unbounded(); + std::thread::spawn(move || loop { + let event = read_event().unwrap(); + event_s.send(event).unwrap(); + }); + + let blocks = vec![ + TestBlock { id: 0, n: 0 }, + TestBlock { id: 1, n: 1 }, + TestBlock { id: 2, n: 2 }, + TestBlock { id: 3, n: 3 }, + ]; + let (update_s, update_r) = flume::unbounded(); + let block_ids: Vec<_> = blocks.iter().map(|b| b.id()).collect(); + + for block in blocks { + let event_r = event_r.clone(); + let update_s = update_s.clone(); + std::thread::spawn(move || { + block.run(event_r, update_s); + }); + } + + let mut cache = HashMap::new(); + loop { + let (id, content) = update_r.recv().unwrap(); + cache.insert(id, content); + let rendered_blocks = block_ids + .iter() + .filter_map(|id| cache.get(id).map(|content| content.clone())) + .map(|content| I3BarBlock { + full_text: content, + color: Some(String::from("#ff0000")), + background: Some(String::from("#00ff00")), + border: Some(String::from("#0000ff")), + border_right: Some(16), + border_left: Some(16), + ..Default::default() + }) + .collect(); + write_blocks(rendered_blocks); + } +} + +pub trait Block { + fn id(&self) -> usize; + + fn name(&self) -> &str; + + fn run(self, event_r: flume::Receiver, update_s: flume::Sender<(usize, String)>); +} + +pub enum Event { + I3Bar(I3BarEvent), + Poll, +} + +struct TestBlock { + id: usize, + n: i32, +} + +impl Block for TestBlock { + fn id(&self) -> usize { + self.id + } + + fn name(&self) -> &str { + "test" + } + + fn run( + mut self, + event_r: flume::Receiver, + update_s: flume::Sender<(usize, String)>, + ) { + let (poll_s, poll_r) = flume::unbounded(); + std::thread::spawn(move || loop { + std::thread::sleep(std::time::Duration::from_millis(333)); + poll_s.send(()).unwrap(); + }); + + loop { + flume::Selector::new() + .recv(&event_r, |msg| msg.map(|msg| Event::I3Bar(msg))) + .recv(&poll_r, |_| Ok(Event::Poll)) + .wait() + .unwrap(); + self.n += 1; + update_s + .send((self.id(), format!(" hello {} ", self.n))) + .unwrap(); + } + } +} + +pub fn debug<'s, S1, S2>(loc: S1, content: S2) +where + S1: AsRef, + S2: AsRef, +{ + let mut file = std::fs::OpenOptions::new() + .append(true) + .create(true) + .open("/home/rosen/dev/i3status-simple/debug.log") + .unwrap(); + file.write_all(format!("[{:?}]: {:?}\n", loc.as_ref(), content.as_ref()).as_bytes()) + .unwrap(); +} diff --git a/src/protocol/i3bar_block.rs b/src/protocol/i3bar_block.rs new file mode 100644 index 0000000..6a8b834 --- /dev/null +++ b/src/protocol/i3bar_block.rs @@ -0,0 +1,38 @@ +use serde::Serialize; + +#[derive(Clone, Debug, Default, Serialize)] +pub struct I3BarBlock { + pub full_text: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub short_text: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub color: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub background: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub border: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub border_top: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub border_right: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub border_bottom: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub border_left: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub min_width: Option, + pub name: String, + pub instance: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub urgent: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub separator: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub separator_block_width: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub markup: Option, +} + +pub fn write_blocks(blocks: Vec) { + println!("{},", serde_json::to_string(&blocks).unwrap()); +} diff --git a/src/protocol/i3bar_event.rs b/src/protocol/i3bar_event.rs new file mode 100644 index 0000000..d9bed9b --- /dev/null +++ b/src/protocol/i3bar_event.rs @@ -0,0 +1,90 @@ +use serde::{de, Deserialize, Deserializer}; + +#[derive(Clone, Debug, Deserialize)] +pub struct I3BarEvent { + pub name: String, + pub instance: String, + pub x: i32, + pub y: i32, + #[serde(deserialize_with = "deserialize_mousebutton")] + pub button: I3BarEventButton, + pub relative_x: i32, + pub relative_y: i32, + pub output_x: i32, + pub output_y: i32, + pub width: i32, + pub height: i32, + pub modifiers: Vec, +} + +pub fn read_event() -> std::io::Result { + let mut line = String::new(); + std::io::stdin().read_line(&mut line)?; + + let slice = line.trim_start_matches(|c| c != '{'); + let slice = slice.trim_end_matches(|c| c != '}'); + + // If empty line, read another line + if slice == "" { + return read_event(); + } + + Ok(serde_json::from_str(slice).unwrap()) +} + +#[derive(Clone, Debug)] +pub enum I3BarEventButton { + LeftMouse, + MiddleMouse, + RightMouse, + ScrollUp, + ScrollDown, + ScrollRight, + ScrollLeft, +} + +fn deserialize_mousebutton<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + struct I3BarEventButtonVisitor; + + impl<'de> de::Visitor<'de> for I3BarEventButtonVisitor { + type Value = I3BarEventButton; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("u64") + } + + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + Ok(match v { + 1 => I3BarEventButton::LeftMouse, + 2 => I3BarEventButton::MiddleMouse, + 3 => I3BarEventButton::RightMouse, + 4 => I3BarEventButton::ScrollUp, + 5 => I3BarEventButton::ScrollDown, + 6 => I3BarEventButton::ScrollRight, + 7 => I3BarEventButton::ScrollLeft, + _ => { + return Err(de::Error::invalid_value( + de::Unexpected::Unsigned(v), + &"1-7", + )) + } + }) + } + } + + deserializer.deserialize_any(I3BarEventButtonVisitor) +} + +#[derive(Clone, Debug, Deserialize)] +pub enum I3BarEventModifier { + Mod1, + Mod4, + Control, + Shift, +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs new file mode 100644 index 0000000..6222db3 --- /dev/null +++ b/src/protocol/mod.rs @@ -0,0 +1,6 @@ +pub mod i3bar_block; +pub mod i3bar_event; + +pub fn init() { + println!(r#"{{ "version": 1, "click_events": true }}["#) +}