i3status_rs/themes/color/
sway.rs1use swayipc::ColorableBarPart;
32
33use std::sync::LazyLock;
34
35use crate::errors::*;
36
37#[cfg(not(test))]
38use std::{fs::read_to_string, os::unix::process::parent_id, path::Path};
39
40#[cfg(not(test))]
41static COLORS: LazyLock<Result<ColorableBarPart>> =
42 LazyLock::new(|| {
43 let mut swayipc_connection =
44 swayipc::Connection::new().error("Failed to open swayipc connection")?;
45
46 let mut current_pid = parent_id().to_string();
47 while current_pid != "1" {
48 let cmdline = read_to_string(Path::new("/proc").join(¤t_pid).join("cmdline"))
49 .or_error(|| format!("Failed to read /proc/{current_pid}/cmdline"))?;
50 let cmdline_parts: Vec<_> = cmdline.split('\0').collect();
51 if let Some(bar_id) = cmdline_parts
52 .iter()
53 .position(|&s| s == "-b" || s == "--bar_id")
54 .and_then(|pos| cmdline_parts.get(pos + 1))
55 {
56 return Ok(swayipc_connection
57 .get_bar_config(bar_id)
58 .error("Failed to get swaybar config")?
59 .colors);
60 }
61 let stat = read_to_string(Path::new("/proc").join(¤t_pid).join("stat"))
62 .or_error(|| format!("Failed to read /proc/{current_pid}/stat"))?;
63
64 current_pid =
65 stat.split(' ').nth(3).unwrap().parse().or_error(|| {
66 format!("Failed to parse parent PID from /proc/{current_pid}/stat")
67 })?;
68 }
69 Err(Error::new(
70 "Unable to find swaybar process in parent process tree",
71 ))
72 });
73
74#[cfg(test)]
75use tests::COLORS;
76
77pub fn get_color(name: &str) -> Result<String> {
78 COLORS.as_ref().map_err(Clone::clone).and_then(|colors| {
79 Ok(match name {
80 "background" => colors.background.clone(),
81 "statusline" => colors.statusline.clone(),
82 "separator" => colors.separator.clone(),
83 "focused_background" => colors.focused_background.clone(),
84 "focused_statusline" => colors.focused_statusline.clone(),
85 "focused_separator" => colors.focused_separator.clone(),
86 "focused_workspace_border" => colors.focused_workspace_border.clone(),
87 "focused_workspace_bg" => colors.focused_workspace_bg.clone(),
88 "focused_workspace_text" => colors.focused_workspace_text.clone(),
89 "inactive_workspace_border" => colors.inactive_workspace_border.clone(),
90 "inactive_workspace_bg" => colors.inactive_workspace_bg.clone(),
91 "inactive_workspace_text" => colors.inactive_workspace_text.clone(),
92 "active_workspace_border" => colors.active_workspace_border.clone(),
93 "active_workspace_bg" => colors.active_workspace_bg.clone(),
94 "active_workspace_text" => colors.active_workspace_text.clone(),
95 "urgent_workspace_border" => colors.urgent_workspace_border.clone(),
96 "urgent_workspace_bg" => colors.urgent_workspace_bg.clone(),
97 "urgent_workspace_text" => colors.urgent_workspace_text.clone(),
98 "binding_mode_border" => colors.binding_mode_border.clone(),
99 "binding_mode_bg" => colors.binding_mode_bg.clone(),
100 "binding_mode_text" => colors.binding_mode_text.clone(),
101 _ => {
102 return Err(Error::new(format!(
103 "color '{}' is not a valid sway color name",
104 name
105 )));
106 }
107 })
108 })
109}
110
111#[cfg(test)]
112mod tests {
113
114 use super::*;
115
116 pub(crate) static COLORS: LazyLock<Result<ColorableBarPart>> = LazyLock::new(|| {
117 let json_str = include_str!("../../../testdata/sway_colors.json");
118 serde_json::from_str(json_str).error("Failed to parse sway colors JSON")
119 });
120
121 #[test]
122 fn test_deserializing_xcolors() {
123 use crate::themes::color::*;
124 let mut parsed_color = "sway:separator".parse::<Color>().unwrap();
125 assert_eq!(
126 parsed_color,
127 Color::Rgba(Rgba {
128 r: 0x93,
129 g: 0xa1,
130 b: 0xa1,
131 a: 0xff
132 })
133 );
134 parsed_color = "sway:focused_workspace_border".parse::<Color>().unwrap();
135 assert_eq!(
136 parsed_color,
137 Color::Rgba(Rgba {
138 r: 0x00,
139 g: 0x88,
140 b: 0xcc,
141 a: 0xff,
142 })
143 );
144 }
145}