summaryrefslogtreecommitdiff
path: root/src/i3bar_protocol.rs
diff options
context:
space:
mode:
authorMax Verevkin <maxxverrr@gmail.com>2022-04-01 21:50:19 +0300
committerMax Verevkin <maxxverrr@gmail.com>2022-04-01 21:50:19 +0300
commit7bcaa341b8685ba411d206c1a89dc3ca77aa71bb (patch)
tree917dec11c47ce71fe9486e56f94015bd12c496b7 /src/i3bar_protocol.rs
downloadi3bar-mango-7bcaa341b8685ba411d206c1a89dc3ca77aa71bb.tar.gz
initial commit
Diffstat (limited to 'src/i3bar_protocol.rs')
-rw-r--r--src/i3bar_protocol.rs150
1 files changed, 150 insertions, 0 deletions
diff --git a/src/i3bar_protocol.rs b/src/i3bar_protocol.rs
new file mode 100644
index 0000000..d838fb1
--- /dev/null
+++ b/src/i3bar_protocol.rs
@@ -0,0 +1,150 @@
+use crate::color::Color;
+use crate::pointer_btn::PointerBtn;
+use crate::text::Align;
+use serde::Deserialize;
+use serde::Serialize;
+use std::io::{Error, ErrorKind, Result};
+
+#[derive(Deserialize, Default)]
+pub struct Block {
+ pub full_text: String,
+ #[serde(default)]
+ pub short_text: Option<String>,
+ #[serde(default)]
+ pub color: Option<Color>,
+ #[serde(default)]
+ pub background: Option<Color>,
+ #[serde(default)]
+ pub min_width: Option<MinWidth>,
+ #[serde(default)]
+ pub align: Option<Align>,
+ #[serde(default)]
+ pub name: Option<String>,
+ #[serde(default)]
+ pub instance: Option<String>,
+ #[serde(default = "def_sep")]
+ pub separator: bool,
+ #[serde(default = "def_sep_width")]
+ pub separator_block_width: u8,
+ #[serde(default)]
+ pub markup: Option<String>,
+}
+
+fn def_sep() -> bool {
+ true
+}
+
+fn def_sep_width() -> u8 {
+ 9
+}
+
+#[derive(Deserialize, Clone)]
+#[serde(untagged)]
+pub enum MinWidth {
+ Text(String),
+ Pixels(u32),
+}
+
+#[derive(Serialize, Default)]
+pub struct Event<'a> {
+ pub name: Option<&'a str>,
+ pub instance: Option<&'a str>,
+ pub button: PointerBtn,
+ // Not available on wayland
+ pub modifiers: Vec<()>,
+ // I see no reason to have these in the protocol, as a lot depends on font & pango markup
+ pub x: u8,
+ pub y: u8,
+ pub relative_x: u8,
+ pub relative_y: u8,
+ pub output_x: u8,
+ pub output_y: u8,
+ pub width: u8,
+ pub height: u8,
+}
+
+#[derive(Deserialize, Clone, Copy)]
+#[serde(deny_unknown_fields)]
+pub struct JsonHeader {
+ version: u8,
+ #[serde(default)]
+ #[allow(dead_code)]
+ stop_signal: i32,
+ #[serde(default)]
+ #[allow(dead_code)]
+ cont_signal: i32,
+ #[serde(default)]
+ click_events: bool,
+}
+
+pub enum Protocol {
+ Unknown,
+ PlainText(Option<String>),
+ JsonNotStarted(JsonHeader),
+ Json(JsonHeader, Option<Vec<Block>>),
+}
+
+impl Protocol {
+ pub fn process_line(&mut self, line: String) -> Result<()> {
+ match self {
+ Self::Unknown => {
+ if let Ok(header) = serde_json::from_str::<JsonHeader>(&line) {
+ if header.version == 1 {
+ *self = Protocol::JsonNotStarted(header);
+ return Ok(());
+ }
+ }
+ *self = Self::PlainText(Some(line))
+ }
+ Self::PlainText(s) => *s = Some(line),
+ Self::JsonNotStarted(header) => {
+ let line = line.trim();
+ if !line.is_empty() {
+ if !line.starts_with('[') && line.is_empty() {
+ return Err(Error::new(ErrorKind::InvalidData, "Expected '['"));
+ }
+ *self = Self::Json(*header, None);
+ self.process_line(line[1..].to_owned())?;
+ }
+ }
+ Self::Json(_, blocks) => {
+ let line = line.trim();
+ if !line.is_empty() {
+ if !line.ends_with(',') {
+ return Err(Error::new(ErrorKind::InvalidData, "Expected ','"));
+ }
+ *blocks = match serde_json::from_str::<Vec<Block>>(&line[..(line.len() - 1)]) {
+ Ok(b) => Some(b),
+ Err(e) => {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ format!("Invalid JSON: {e}"),
+ ))
+ }
+ };
+ }
+ }
+ }
+ Ok(())
+ }
+
+ pub fn get_blocks(&mut self) -> Option<Vec<Block>> {
+ match self {
+ Self::Unknown => None,
+ Self::PlainText(text) => Some(vec![Block {
+ full_text: text.take()?,
+ ..Default::default()
+ }]),
+ Self::JsonNotStarted(_) => None,
+ Self::Json(_, blocks) => blocks.take(),
+ }
+ }
+
+ pub fn supports_clicks(&self) -> bool {
+ match self {
+ Self::JsonNotStarted(h) => h.click_events,
+ Self::Json(h, _) => h.click_events,
+ _ => false,
+ }
+ }
+}