Majorly rework
This commit is contained in:
parent
17735ba956
commit
2f628d6a2c
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -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",
|
||||
|
@ -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
|
||||
|
355
src/main.rs
355
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<String>,
|
||||
}
|
||||
|
||||
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<u32, u32>,
|
||||
output_names: BTreeMap<u32, String>,
|
||||
focused: Option<u32>,
|
||||
layout: Option<String>,
|
||||
titles: Option<HashMap<u32, String>>,
|
||||
mode: Option<String>,
|
||||
tags: Option<HashMap<u32, Tags>>,
|
||||
urgency: Option<HashMap<u32, Tags>>,
|
||||
viewtags: Option<HashMap<u32, Vec<u32>>>,
|
||||
status_manager: Option<Main<ZriverStatusManagerV1>>,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
fn translate_output_map<V: Clone>(
|
||||
&self, input: &HashMap<u32, V>
|
||||
) -> HashMap<String, V> {
|
||||
let mut output = HashMap::<String, V>::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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<u32>,
|
||||
// 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<Main<ZriverStatusManagerV1>>,
|
||||
flags: Flags,
|
||||
read_status: ReadStatus,
|
||||
// I don't know very much about wayland, and it shows
|
||||
output_id_map: HashMap<u32, u32>,
|
||||
layout: Option<String>,
|
||||
mode: Option<String>,
|
||||
focused_output: Option<u32>,
|
||||
outputs: BTreeMap<u32, Output>,
|
||||
}
|
||||
|
||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<String, Output> = 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<ZriverSeatStatusV1>, event: zriver_seat_status_v1::Event,
|
||||
mut env: DispatchData
|
||||
) {
|
||||
if let Some(env) = env.get::<Env>() {
|
||||
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);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
@ -212,23 +176,22 @@ fn handle_river_seat_status(
|
||||
fn handle_seat_event(
|
||||
seat: Main<WlSeat>, event: wl_seat::Event, mut env: DispatchData
|
||||
) {
|
||||
if let Some(env) = env.get::<Env>() {
|
||||
env.read_status.wl_seat = true;
|
||||
match event {
|
||||
wl_seat::Event::Name { name } => {
|
||||
if let Some(env) = env.get::<Env>() {
|
||||
if (env.flags.titles || env.flags.mode || env.flags.focused)
|
||||
&& (env.flags.seat.is_none()
|
||||
|| name.eq(env.flags.seat.as_ref().unwrap()))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_river_output_status(
|
||||
@ -236,20 +199,15 @@ fn handle_river_output_status(
|
||||
mut env: DispatchData
|
||||
) {
|
||||
if let Some(env) = env.get::<Env>() {
|
||||
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<u32> = tags[0..]
|
||||
} => env.get_output(output_id).populated_tags = tags[0..]
|
||||
.chunks(4)
|
||||
.map(|s| {
|
||||
let buf = [s[0], s[1], s[2], s[3]];
|
||||
@ -260,26 +218,14 @@ fn handle_river_output_status(
|
||||
}
|
||||
}
|
||||
0
|
||||
})
|
||||
.collect();
|
||||
viewtags.insert(output_id, tags);
|
||||
}
|
||||
}
|
||||
}).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<WlOutput>, event: wl_output::Event, mut env: DispatchData
|
||||
) {
|
||||
let output_id = output.as_ref().id();
|
||||
if let Some(env) = env.get::<Env>() {
|
||||
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::<Env>() {
|
||||
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 <string> 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 <string> 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();
|
||||
if !env.is_empty() && env.is_first_read_done() {
|
||||
env.fmt();
|
||||
if env.flags.once {
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user