Initial swayrbar impl

main
Tassilo Horn 2 years ago
parent 6a204e619e
commit 061c3589ac
  1. 192
      Cargo.lock
  2. 7
      Cargo.toml
  3. 58
      src/bar.rs
  4. 31
      src/bar/module.rs
  5. 62
      src/bar/module/date.rs
  6. 71
      src/bar/module/sysinfo.rs
  7. 105
      src/bar/module/window.rs
  8. 22
      src/bin/swayrbar.rs
  9. 71
      src/fmt_replace.rs
  10. 3
      src/lib.rs
  11. 62
      src/tree.rs

192
Cargo.lock generated

@ -40,6 +40,19 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi",
]
[[package]]
name = "clap"
version = "3.1.8"
@ -70,6 +83,57 @@ dependencies = [
"syn",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "crossbeam-channel"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
"cfg-if",
"lazy_static",
]
[[package]]
name = "directories"
version = "4.0.1"
@ -90,6 +154,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "env_logger"
version = "0.9.0"
@ -183,6 +253,59 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "ntapi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
dependencies = [
"winapi",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "os_str_bytes"
version = "6.0.0"
@ -270,6 +393,31 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.13"
@ -323,6 +471,12 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[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.136"
@ -360,6 +514,16 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "swaybar-types"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c0b435952b89d872f882cf7ae0756303ef68d310bfa44b9c8012fda88ae143"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "swayipc"
version = "3.0.0"
@ -386,6 +550,7 @@ dependencies = [
name = "swayr"
version = "0.16.1"
dependencies = [
"chrono",
"clap",
"directories",
"env_logger",
@ -396,7 +561,9 @@ dependencies = [
"rt-format",
"serde",
"serde_json",
"swaybar-types",
"swayipc",
"sysinfo",
"toml",
]
@ -411,6 +578,21 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "sysinfo"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f3c0db8e08e06cfd352a043bd0143498fb7d42783a6e941da83b55464eb27d2"
dependencies = [
"cfg-if",
"core-foundation-sys",
"libc",
"ntapi",
"once_cell",
"rayon",
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.3"
@ -446,6 +628,16 @@ dependencies = [
"syn",
]
[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "toml"
version = "0.5.8"

@ -22,9 +22,10 @@ rand = "0.8.4"
rt-format = "0.3.0"
log = "0.4"
env_logger = { version = "0.9.0", default-features = false, features = ["termcolor", "atty", "humantime"] } # without regex
[profile.dev]
lto = "thin"
swaybar-types = "3.0.0"
chrono = "0.4"
sysinfo = "0.23"
[profile.release]
lto = "thin"
strip = "symbols"

@ -0,0 +1,58 @@
// Copyright (C) 2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.
//! `swayrbar` lib.
use crate::bar::module::BarModuleFn;
use env_logger::Env;
use serde_json;
use std::thread;
pub mod module;
pub fn start() {
env_logger::Builder::from_env(Env::default().default_filter_or("warn"))
.init();
thread::spawn(handle_input);
let mods: Vec<Box<dyn BarModuleFn>> = vec![
crate::bar::module::window::BarModuleWindow::init(),
crate::bar::module::sysinfo::BarModuleSysInfo::init(),
crate::bar::module::date::BarModuleDate::init(),
];
generate_status(&mods);
}
pub fn handle_input() {
// TODO: Read stdin and react to click events.
}
pub fn generate_status(mods: &[Box<dyn BarModuleFn>]) {
println!("{{\"version\": 1}}");
// status_command should output an infinite array meaning we emit an
// opening [ and never the closing bracket.
println!("[");
loop {
let mut blocks = vec![];
for m in mods {
blocks.push(m.build());
}
let json = serde_json::to_string_pretty(&blocks)
.unwrap_or_else(|_| "".to_string());
println!("{},", json);
thread::sleep(std::time::Duration::from_secs(1));
}
}

@ -0,0 +1,31 @@
// Copyright (C) 2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.
use swaybar_types as s;
pub mod date;
pub mod sysinfo;
pub mod window;
pub trait BarModuleFn {
fn init() -> Box<dyn BarModuleFn>
where
Self: Sized;
fn name() -> String
where
Self: Sized;
fn instance(&self) -> String;
fn build(&self) -> s::Block;
}

@ -0,0 +1,62 @@
// Copyright (C) 2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.
//! The date `swayrbar` module.
use crate::bar::module::BarModuleFn;
use swaybar_types as s;
pub struct BarModuleDate {
pub instance: String,
}
impl BarModuleFn for BarModuleDate {
fn init() -> Box<dyn BarModuleFn> {
Box::new(BarModuleDate {
instance: "0".to_string(),
})
}
fn name() -> String {
String::from("date")
}
fn instance(&self) -> String {
self.instance.clone()
}
fn build(&self) -> s::Block {
let d = chrono::Local::now().format("%F %X").to_string();
s::Block {
name: Some(Self::name()),
instance: Some(self.instance.clone()),
full_text: d,
align: Some(s::Align::Right),
markup: Some(s::Markup::Pango),
short_text: None,
color: None,
background: None,
border: None,
border_top: None,
border_bottom: None,
border_left: None,
border_right: None,
min_width: None,
urgent: None,
separator: None,
separator_block_width: None,
}
}
}

@ -0,0 +1,71 @@
// Copyright (C) 2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.
//! The date `swayrbar` module.
use crate::bar::module::BarModuleFn;
use std::cell::RefCell;
use swaybar_types as s;
use sysinfo;
use sysinfo::SystemExt;
pub struct BarModuleSysInfo {
pub instance: String,
system: RefCell<sysinfo::System>,
}
impl BarModuleFn for BarModuleSysInfo {
fn init() -> Box<dyn BarModuleFn> {
Box::new(BarModuleSysInfo {
instance: "0".to_string(),
system: RefCell::new(sysinfo::System::new_all()),
})
}
fn name() -> String {
String::from("sysinfo")
}
fn instance(&self) -> String {
self.instance.clone()
}
fn build(&self) -> s::Block {
let x = String::from("{cpu_usage_avg}");
self.system.borrow_mut().refresh_specifics(
sysinfo::RefreshKind::new().with_cpu().with_memory(),
);
s::Block {
name: Some(Self::name()),
instance: Some(self.instance.clone()),
full_text: x,
align: Some(s::Align::Right),
markup: Some(s::Markup::Pango),
short_text: None,
color: None,
background: None,
border: None,
border_top: None,
border_bottom: None,
border_left: None,
border_right: None,
min_width: None,
urgent: None,
separator: None,
separator_block_width: None,
}
}
}

@ -0,0 +1,105 @@
// Copyright (C) 2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.
//! The window `swayrbar` module.
use crate::bar::module::BarModuleFn;
use std::cell::RefCell;
use swaybar_types as s;
use swayipc as ipc;
pub struct NodeIter<'a> {
stack: Vec<&'a ipc::Node>,
}
impl<'a> NodeIter<'a> {
fn new(node: &'a ipc::Node) -> NodeIter {
NodeIter { stack: vec![node] }
}
}
impl<'a> Iterator for NodeIter<'a> {
type Item = &'a ipc::Node;
fn next(&mut self) -> Option<Self::Item> {
if let Some(node) = self.stack.pop() {
for n in &node.floating_nodes {
self.stack.push(n);
}
for n in &node.nodes {
self.stack.push(n);
}
Some(node)
} else {
None
}
}
}
pub struct BarModuleWindow {
pub instance: String,
connection: RefCell<swayipc::Connection>,
}
impl BarModuleFn for BarModuleWindow {
fn init() -> Box<dyn BarModuleFn> {
Box::new(BarModuleWindow {
instance: "0".to_string(),
connection: RefCell::new(
ipc::Connection::new()
.expect("Couldn't get a sway IPC connection"),
),
})
}
fn name() -> String {
String::from("window")
}
fn instance(&self) -> String {
self.instance.clone()
}
fn build(&self) -> s::Block {
let x: String = match self.connection.borrow_mut().get_tree() {
Ok(root) => {
let o: Option<&ipc::Node> =
NodeIter::new(&root).find(|n| n.focused);
o.map(|w| w.name.clone().unwrap_or_default())
.unwrap_or_else(String::new)
}
Err(err) => format!("{}", err),
};
s::Block {
name: Some(Self::name()),
instance: Some(self.instance.clone()),
full_text: x,
align: Some(s::Align::Right),
markup: Some(s::Markup::Pango),
short_text: None,
color: None,
background: None,
border: None,
border_top: None,
border_bottom: None,
border_left: None,
border_right: None,
min_width: None,
urgent: None,
separator: None,
separator_block_width: None,
}
}
}

@ -0,0 +1,22 @@
// Copyright (C) 2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.
//! The `swayrbar` binary.
fn main() {
// TODO: We need a config file cmd line option so that each bar can have
// its own config.
swayr::bar::start();
}

@ -0,0 +1,71 @@
// Copyright (C) 2022 Tassilo Horn <tsdh@gnu.org>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.
use lazy_static::lazy_static;
lazy_static! {
pub static ref PLACEHOLDER_RX: regex::Regex = regex::Regex::new(
r"\{(?P<name>[^}:]+)(?::(?P<fmtstr>\{[^}]*\})(?P<clipstr>[^}]*))?\}"
)
.unwrap();
}
pub fn maybe_html_escape(do_it: bool, text: String) -> String {
if do_it {
text.replace('<', "&lt;")
.replace('>', "&gt;")
.replace('&', "&amp;")
} else {
text
}
}
macro_rules! fmt_replace {
( $fmt_str:expr, $html_escape:ident,
{ $( $($pat:pat)|+ => $exp:expr, )+ }
) => {
$crate::fmt_replace::PLACEHOLDER_RX
.replace_all($fmt_str, |caps: &regex::Captures| {
let value: String = match &caps["name"] {
$(
$( | $pat )+ => {
let val = $exp;
let fmt_str = caps.name("fmtstr")
.map_or("{}", |m| m.as_str());
let clipped_str = caps.name("clipstr")
.map_or("", |m| m.as_str());
$crate::fmt_replace::maybe_html_escape(
$html_escape,
crate::rtfmt::format(fmt_str, &val, clipped_str),
)
}
)+
_ => caps[0].to_string(),
};
value
}).into()
};
}
#[test]
fn foo() {
let foo = "{a}, {b}";
let html_escape = true;
let x: String = fmt_replace!(foo, html_escape, {
"a" => "1".to_string(),
"b" => "2".to_string(),
"c" => "3".to_owned(),
});
}

@ -23,10 +23,13 @@
//! window/workspace creations, deletions, and focus changes using sway's JSON
//! IPC interface. The client `swayr` offers subcommands, see `swayr --help`.
pub mod bar;
pub mod client;
pub mod cmds;
pub mod config;
pub mod demon;
#[macro_use]
pub mod fmt_replace;
pub mod layout;
pub mod rtfmt;
pub mod tree;

@ -16,7 +16,6 @@
//! Convenience data structures built from the IPC structs.
use crate::config;
use crate::rtfmt;
use crate::util;
use crate::util::DisplayFormat;
use lazy_static::lazy_static;
@ -432,20 +431,6 @@ pub fn get_tree<'a>(
lazy_static! {
static ref APP_NAME_AND_VERSION_RX: regex::Regex =
regex::Regex::new("(.+)(-[0-9.]+)").unwrap();
static ref PLACEHOLDER_RX: regex::Regex = regex::Regex::new(
r"\{(?P<name>[^}:]+)(?::(?P<fmtstr>\{[^}]*\})(?P<clipstr>[^}]*))?\}"
)
.unwrap();
}
fn maybe_html_escape(do_it: bool, text: String) -> String {
if do_it {
text.replace('<', "&lt;")
.replace('>', "&gt;")
.replace('&', "&amp;")
} else {
text
}
}
fn format_marks(marks: &[String]) -> String {
@ -515,36 +500,23 @@ impl DisplayFormat for DisplayNode<'_> {
.as_str(),
);
PLACEHOLDER_RX
.replace_all(&fmt, |caps: &regex::Captures| {
let value = match &caps["name"] {
"id" => self.node.id.to_string(),
"app_name" => self.node.get_app_name().to_string(),
"layout" => format!("{:?}", self.node.layout),
"name" | "title" => self.node.get_name().to_string(),
"output_name" => self
.tree
.get_parent_node_of_type(self.node.id, Type::Output)
.map_or("<no_output>", |w| w.get_name())
.to_string(),
"workspace_name" => self
.tree
.get_parent_node_of_type(self.node.id, Type::Workspace)
.map_or("<no_workspace>", |w| w.get_name())
.to_string(),
"marks" => format_marks(&self.node.marks),
_ => caps[0].to_string(),
};
let fmt_str = caps.name("fmtstr").map_or("{}", |m| m.as_str());
let clipped_str =
caps.name("clipstr").map_or("", |m| m.as_str());
maybe_html_escape(
html_escape,
rtfmt::format(fmt_str, &value, clipped_str),
)
})
.into()
fmt_replace!(&fmt, html_escape, {
"id" => self.node.id.to_string(),
"app_name" => self.node.get_app_name().to_string(),
"layout" => format!("{:?}", self.node.layout),
"name" | "title" => self.node.get_name().to_string(),
"output_name" => self
.tree
.get_parent_node_of_type(self.node.id, Type::Output)
.map_or("<no_output>", |w| w.get_name())
.to_string(),
"workspace_name" => self
.tree
.get_parent_node_of_type(self.node.id, Type::Workspace)
.map_or("<no_workspace>", |w| w.get_name())
.to_string(),
"marks" => format_marks(&self.node.marks),
})
}
fn get_indent_level(&self) -> usize {

Loading…
Cancel
Save