diff --git a/Cargo.lock b/Cargo.lock index 63d24b0..31390f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,18 +109,18 @@ checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index 08fdcbd..339c697 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # A [river-status](https://codeberg.org/river/river) client -## Original credit to this program goes to [snakedye / Bryan][1] at [https://gitlab.com/snakedye/ristate][2] +## Original credit for this program goes to [snakedye / Bryan][1] at [https://gitlab.com/snakedye/ristate][2] [1]: https://gitlab.com/snakedye [2]: https://gitlab.com/snakedye/ristate diff --git a/src/main.rs b/src/main.rs index 5284486..52db659 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,70 +11,18 @@ use wayland_client::protocol::wl_registry::WlRegistry; use std::collections::{BTreeMap, HashMap}; use wayland_client::protocol::{wl_output, wl_output::WlOutput, wl_seat, wl_seat::WlSeat}; -use wayland_client::{Attached, DispatchData, Display, GlobalEvent, GlobalImplementor, GlobalManager, Main}; +use wayland_client::{Attached, DispatchData, Display, GlobalEvent, GlobalManager, Main}; -#[derive(Debug)] +#[derive(Debug, Default)] struct Flags { - tags: bool, - titles: bool, - urgency: bool, - viewtags: bool, - layout: bool, - mode: bool, - focused: bool, - gtk_nums: bool, + once: bool, seat: Option, } -impl Flags { - fn default() -> Flags { - Flags { - tags: false, - titles: false, - urgency: false, - viewtags: false, - layout: false, - mode: false, - focused: false, - gtk_nums: false, - seat: None, - } - } -} -#[derive(Clone)] +#[derive(Clone, Default)] struct Tags(u32); -struct Env { - flags: Flags, - // I don't know very much about wayland, and it shows - output_id_map: HashMap, - output_names: BTreeMap, - focused: Option, - layout: Option, - titles: Option>, - mode: Option, - tags: Option>, - urgency: Option>, - viewtags: Option>>, - status_manager: Option>, -} - -impl Env { - fn translate_output_map( - &self, input: &HashMap - ) -> HashMap { - let mut output = HashMap::::new(); - output.reserve(input.len()); - for (id, val) in input { - if let Some(name) = self.output_names.get(&id) { - output.insert(name.clone(), val.clone()); - } - } - output - } -} - impl Serialize for Tags { fn serialize(&self, serializer: S) -> Result where @@ -90,119 +38,135 @@ impl Serialize for Tags { } } + +#[derive(Clone, serde::Serialize, Default)] +struct OutputGeometry { + x: i32, + y: i32, + width: i32, + height: i32, + scale: i32, + refresh: f32, +} + +#[derive(Clone, Default, serde::Serialize)] +struct Output { + #[serde(skip_serializing)] + name: String, + title: String, + geometry: OutputGeometry, + focused_tags: Tags, + urgent_tags: Tags, + populated_tags: Vec, + // for serde serialization + focused: bool, + gdk_id: u32, +} + +#[derive(Default)] +struct ReadStatus { + wl_output: bool, + wl_seat: bool, + river_seat: bool, + river_output: bool, +} + +struct Env { + status_manager: Option>, + flags: Flags, + read_status: ReadStatus, + // I don't know very much about wayland, and it shows + output_id_map: HashMap, + layout: Option, + mode: Option, + focused_output: Option, + outputs: BTreeMap, +} + +impl Env { + fn new() -> Env { + Env { + status_manager: None, + flags: configuration(), + read_status: Default::default(), + output_id_map: Default::default(), + layout: None, + mode: None, + focused_output: None, + outputs: Default::default(), + } + } + + fn fmt(&self) { + println!("{}", serde_json::to_string(self).unwrap()); + } + + fn get_output(&mut self, id: u32) -> &mut Output { + if !self.outputs.contains_key(&id) { + self.outputs.insert(id, Default::default()); + } + self.outputs.get_mut(&id).unwrap() + } + + fn is_empty(&self) -> bool { + self.layout.is_none() && self.mode.is_none() && self.outputs.len() == 0 + } + + fn is_first_read_done(&self) -> bool{ + self.read_status.wl_output && self.read_status.wl_seat && + self.read_status.river_seat && self.read_status.river_output + } +} + impl Serialize for Env { fn serialize(&self, serializer: S) -> Result where S: Serializer { - let mut state = serializer.serialize_struct("Env", 8)?; + let mut state = serializer.serialize_struct("Env", 9)?; if let Some(layout) = self.layout.as_ref() { state.serialize_field("layout", layout)?; } else { state.skip_field("layout")?; } - if let Some(titles) = self.titles.as_ref() { - state.serialize_field("titles", &self.translate_output_map(titles))?; - } else { - state.skip_field("titles")?; - } if let Some(mode) = self.mode.as_ref() { state.serialize_field("mode", mode)?; } else { state.skip_field("mode")?; } - // kind of inelegant - if self.flags.focused && self.focused.is_some() && - self.output_names.contains_key(&self.focused.unwrap()) { - state.serialize_field( - "focused", &self.output_names[&self.focused.unwrap()])?; + let mut outputs: HashMap = HashMap::new(); + let mut gdk_id = 0; + for (output_id, output) in &self.outputs { + let mut new = output.clone(); + new.focused = if let Some(focused_id) = self.focused_output { + *output_id == focused_id } else { - state.skip_field("focused")?; - } - if let Some(tags) = self.tags.as_ref() { - state.serialize_field("tags", &self.translate_output_map(tags))?; - } else { - state.skip_field("tags")?; - } - if let Some(urgency) = self.urgency.as_ref() { - state.serialize_field("urgency", &self.translate_output_map(urgency))?; - } else { - state.skip_field("urgency")?; - } - if let Some(viewtags) = self.viewtags.as_ref() { - state.serialize_field("viewtags", &self.translate_output_map(viewtags))?; - } else { - state.skip_field("viewtags")?; - } - if self.flags.gtk_nums { - let mut name_id_map = HashMap::<&str, u32>::new(); - name_id_map.reserve(self.output_names.len()); - let mut id = 0; - for (key, value) in &self.output_names { - name_id_map.insert(value, id); - id += 1; - } - state.serialize_field("gtk_ids", &name_id_map)?; - } else { - state.skip_field("gtk_ids")?; + false + }; + new.gdk_id = gdk_id; + outputs.insert(new.name.clone(), new); + gdk_id += 1; } + state.serialize_field("outputs", &outputs)?; state.end() } } -impl Env { - fn new() -> Env { - let flags = configuration(); - Env { - layout: None, - mode: None, - focused: None, - titles: flags.titles.then(Default::default), - tags: flags.tags.then(Default::default), - urgency: flags.urgency.then(Default::default), - viewtags: flags.viewtags.then(Default::default), - status_manager: None, - flags, - output_id_map: Default::default(), - output_names: Default::default(), - } - } - - fn fmt(&self) { - if self.titles.is_some() - || self.tags.is_some() - || self.mode.is_some() - || self.urgency.is_some() - || self.viewtags.is_some() - || self.layout.is_some() - || self.focused.is_some() - || self.flags.gtk_nums - { - println!("{}", serde_json::to_string(self).unwrap()); - } - } -} - fn handle_river_seat_status( _status: Main, event: zriver_seat_status_v1::Event, mut env: DispatchData ) { if let Some(env) = env.get::() { + env.read_status.river_seat = true; match event { zriver_seat_status_v1::Event::FocusedOutput { output } => { - if env.flags.focused || env.flags.titles { - env.focused = Some(output.as_ref().id()); - } + env.focused_output = Some(output.as_ref().id()); }, zriver_seat_status_v1::Event::FocusedView { title } => { - if env.flags.titles && env.focused.is_some() { - if let Some(titles) = &mut env.titles { - titles.insert(env.focused.unwrap(), title); - } + if let Some(focused_output) = env.focused_output { + env.get_output(focused_output).title = title; } }, zriver_seat_status_v1::Event::Mode { name } => { - if env.flags.mode { - env.mode = Some(name); - } + env.mode = Some(name); }, _ => {}, } @@ -212,22 +176,21 @@ fn handle_river_seat_status( fn handle_seat_event( seat: Main, event: wl_seat::Event, mut env: DispatchData ) { - match event { - wl_seat::Event::Name { name } => { - if let Some(env) = env.get::() { - if (env.flags.titles || env.flags.mode || env.flags.focused) - && (env.flags.seat.is_none() - || name.eq(env.flags.seat.as_ref().unwrap())) - { - if let Some(status_manager) = &env.status_manager { - let seat_status = - status_manager.get_river_seat_status(&seat); - seat_status.quick_assign(handle_river_seat_status); + if let Some(env) = env.get::() { + env.read_status.wl_seat = true; + match event { + wl_seat::Event::Name { name } => { + if env.flags.seat.is_none() + || name == *env.flags.seat.as_ref().unwrap() { + if let Some(status_manager) = &env.status_manager { + let seat_status = + status_manager.get_river_seat_status(&seat); + seat_status.quick_assign(handle_river_seat_status); + } } - } - } + }, + _ => {}, } - _ => {} } } @@ -236,50 +199,33 @@ fn handle_river_output_status( mut env: DispatchData ) { if let Some(env) = env.get::() { + env.read_status.river_output = true; let output_id = output.as_ref().id(); match event { zriver_output_status_v1::Event::FocusedTags { tags: focused_tags, - } => { - if let Some(tags) = &mut env.tags { - tags.insert(output_id, Tags(focused_tags)); - } - } + } => env.get_output(output_id).focused_tags = Tags(focused_tags), zriver_output_status_v1::Event::ViewTags { tags, - } => { - if let Some(viewtags) = &mut env.viewtags { - let tags: Vec = tags[0..] - .chunks(4) - .map(|s| { - let buf = [s[0], s[1], s[2], s[3]]; - let tagmask = u32::from_le_bytes(buf); - for i in 0..32 { - if 1 << i == tagmask { - return 1 + i; - } - } - 0 - }) - .collect(); - viewtags.insert(output_id, tags); - } - } + } => env.get_output(output_id).populated_tags = tags[0..] + .chunks(4) + .map(|s| { + let buf = [s[0], s[1], s[2], s[3]]; + let tagmask = u32::from_le_bytes(buf); + for i in 0..32 { + if 1 << i == tagmask { + return 1 + i; + } + } + 0 + }).collect(), zriver_output_status_v1::Event::UrgentTags { tags, - } => { - if let Some(urgency) = &mut env.urgency { - urgency.insert(output_id, Tags(tags)); - } - } + } => env.get_output(output_id).urgent_tags = Tags(tags), zriver_output_status_v1::Event::LayoutName { name, - } => { - if env.flags.layout { - env.layout = Some(name); - } - } - _ => {} + } => env.layout = Some(name), + _ => {}, } } @@ -288,11 +234,13 @@ fn handle_river_output_status( fn handle_output_event( output: Main, event: wl_output::Event, mut env: DispatchData ) { + let output_id = output.as_ref().id(); if let Some(env) = env.get::() { + env.read_status.wl_output = true; match event { wl_output::Event::Geometry { - x: _, - y: _, + x, + y, physical_width: _, physical_height: _, subpixel: _, @@ -307,13 +255,23 @@ fn handle_output_event( handle_river_output_status(&output, event, env); }); } - } + let entry = env.get_output(output_id); + entry.geometry.x = x; + entry.geometry.y = y; + }, + wl_output::Event::Mode { flags: _, width, height, refresh } => { + let entry = env.get_output(output_id); + entry.geometry.width = width; + entry.geometry.height = height; + entry.geometry.refresh = refresh as f32 / 1000.0f32; + }, wl_output::Event::Name { name, - } => { - env.output_names.insert(output.as_ref().id(), name); - } - _ => {} + } => env.get_output(output_id).name = name, + wl_output::Event::Scale { + factor + } => env.get_output(output_id).geometry.scale = factor, + _ => {}, } } } @@ -347,19 +305,7 @@ fn global_manager_callback( GlobalEvent::Removed { id, interface: _ } => { if let Some(env) = env.get::() { if let Some(oid) = env.output_id_map.remove(&id) { - env.output_names.remove(&oid); - if let Some(titles) = &mut env.titles { - titles.remove(&oid); - } - if let Some(tags) = &mut env.tags { - tags.remove(&oid); - } - if let Some(urgency) = &mut env.urgency { - urgency.remove(&oid); - } - if let Some(viewtags) = &mut env.viewtags { - viewtags.remove(&oid); - } + env.outputs.remove(&oid); } } } @@ -374,25 +320,12 @@ fn configuration() -> Flags { match args.next() { Some(flag) => match flag.as_str() { "--seat" | "-s" => default.seat = args.next(), - "--urgency" | "-u" => default.urgency = true, - "--title" | "-w" => default.titles = true, - "--mode" | "-m" => default.mode = true, - "--focused" | "-f" => default.focused = true, - "--tags" | "-t" => default.tags = true, - "--layout" | "-l" => default.layout = true, - "--view-tags" | "-vt" => default.viewtags = true, - "--gtk-ids" | "-g" => default.gtk_nums = true, + "--once" | "-o" => default.once = true, "--help" | "-h" => { - print!("Usage: ristate [option]\n\n"); - print!(" --tag | -t the focused tag of each output\n"); - print!(" --titles | -w the titles of focused views\n"); - print!(" --mode | -m the current input mode\n"); - print!(" --layout | -l display the name of the layout\n"); - print!(" --focused | -f the name of the focused output\n"); - print!(" --urgency | -u tags with urgent views on them\n"); - print!(" --view-tags | -vt the tags with views on them\n"); - print!(" --gtk-ids | -g attempt to map output names to their gtk id\n"); - print!(" --seat | -s select the seat\n"); + println!("Usage: ristate [option]\n"); + println!(" --help | -h print this message and exit"); + println!(" --once | -o print the current status and exit"); + println!(" --seat | -s select the seat"); std::process::exit(0); } _ => {} @@ -400,11 +333,6 @@ fn configuration() -> Flags { None => break, } } - if !default.urgency && !default.titles && !default.mode && !default.focused - && !default.tags && !default.layout && !default.viewtags && !default.gtk_nums { - println!("error: You must specify at least one thing to listen for!"); - std::process::exit(1); - } default } @@ -431,6 +359,11 @@ fn main() { ); }) .unwrap(); - env.fmt(); + if !env.is_empty() && env.is_first_read_done() { + env.fmt(); + if env.flags.once { + std::process::exit(0); + } + } } }