Commit | Line | Data |
---|---|---|
12bd87b0 | 1 | use dbus::arg; |
baedbc7e JC |
2 | use dbus::blocking::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged; |
3 | use dbus::blocking::Connection; | |
4 | use dbus::message::Message; | |
1e52c28e | 5 | use std::collections::HashMap; |
42fef3a7 | 6 | use std::process::Command; |
1e52c28e | 7 | use std::sync::{Arc, Mutex}; |
bbc46eb1 | 8 | use std::thread; |
baedbc7e JC |
9 | use std::time::Duration; |
10 | ||
12bd87b0 JC |
11 | // Custom signal type impl |
12 | // Mostly copied from library examples | |
13 | #[derive(Debug)] | |
14 | pub struct ComJacobCasperMailUnreadCount { | |
15 | pub count: u32, | |
16 | } | |
17 | ||
18 | impl arg::AppendAll for ComJacobCasperMailUnreadCount { | |
19 | fn append(&self, iter: &mut arg::IterAppend) { | |
20 | arg::RefArg::append(&self.count, iter); | |
21 | } | |
22 | } | |
23 | ||
24 | impl arg::ReadAll for ComJacobCasperMailUnreadCount { | |
25 | fn read(iter: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> { | |
26 | Ok(ComJacobCasperMailUnreadCount { | |
27 | count: iter.read()?, | |
28 | }) | |
29 | } | |
30 | } | |
31 | ||
32 | impl dbus::message::SignalArgs for ComJacobCasperMailUnreadCount { | |
33 | const NAME: &'static str = "UnreadCount"; | |
34 | const INTERFACE: &'static str = "com.jacobcasper.Mail"; | |
35 | } | |
36 | ||
bbc46eb1 JC |
37 | fn get_local_time_string() -> std::string::String { |
38 | use chrono::prelude::*; | |
39 | let local_time = chrono::prelude::Local::now(); | |
40 | return format!( | |
6336104a | 41 | "{}-{}-{:02} {}:{:02}", |
bbc46eb1 JC |
42 | local_time.year(), |
43 | local_time.month(), | |
44 | local_time.day(), | |
45 | local_time.hour(), | |
46 | local_time.minute(), | |
47 | ); | |
48 | } | |
49 | ||
ddaab88f | 50 | fn update_map(arc_map: Arc<Mutex<HashMap<String, String>>>, key: &str, val: &str) { |
1e52c28e JC |
51 | let clone_arc = arc_map.clone(); |
52 | let mut map = clone_arc.lock().unwrap(); | |
ddaab88f | 53 | map.insert(String::from(key), String::from(val)); |
42fef3a7 JC |
54 | let _ = Command::new("xsetroot") |
55 | .arg("-name") | |
56 | .arg(format!( | |
7e7a1f74 | 57 | "[{playback_status} {title} - {artist}] | ✉ {unread_count} | {date_time}", |
6d3ac36b | 58 | playback_status = map.get("playback_status").unwrap_or(&String::from("🔈")), |
42fef3a7 JC |
59 | title = map.get("title").unwrap_or(&String::from("")), |
60 | artist = map.get("artist").unwrap_or(&String::from("")), | |
61 | unread_count = map.get("unread_count").unwrap_or(&String::from("?")), | |
bbc46eb1 | 62 | date_time = map.get("date_time").unwrap_or(&get_local_time_string()), |
42fef3a7 JC |
63 | )) |
64 | .spawn(); | |
1e52c28e JC |
65 | } |
66 | ||
baedbc7e | 67 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
1e52c28e | 68 | let arc_locked_xset_map = Arc::new(Mutex::new(HashMap::new())); |
baedbc7e JC |
69 | let conn = Connection::new_session()?; |
70 | ||
12bd87b0 JC |
71 | let mail_proxy = conn.with_proxy( |
72 | "com.jacobcasper.Mail", | |
73 | "/com/jacobcasper/Mail/Unread", | |
74 | Duration::from_millis(5000), | |
75 | ); | |
76 | ||
77 | let mail_match_map = arc_locked_xset_map.clone(); | |
78 | let _ = mail_proxy.match_signal( | |
79 | move |m: ComJacobCasperMailUnreadCount, _: &Connection, _: &Message| { | |
80 | update_map( | |
81 | mail_match_map.clone(), | |
82 | "unread_count", | |
83 | m.count.to_string().as_str(), | |
84 | ); | |
85 | true | |
86 | }, | |
87 | ); | |
88 | ||
89 | let spotify_proxy = conn.with_proxy( | |
baedbc7e JC |
90 | "org.mpris.MediaPlayer2.spotify", |
91 | "/org/mpris/MediaPlayer2", | |
92 | Duration::from_millis(5000), | |
93 | ); | |
94 | ||
16683530 | 95 | let spotify_match_map = arc_locked_xset_map.clone(); |
12bd87b0 | 96 | let _ = spotify_proxy.match_signal( |
1e52c28e | 97 | move |pc: PropertiesPropertiesChanged, _: &Connection, _: &Message| { |
be7f5d4d JC |
98 | match pc.changed_properties.get("Metadata") { |
99 | Some(variant) => { | |
100 | variant.0.as_iter().and_then(|mut iter| { | |
101 | // Iterating over a RefArg goes key, value, key, value... Insane honestly. | |
102 | while let Some(key) = iter.next() { | |
103 | let key_str = key.as_str()?; | |
104 | let value = iter.next(); | |
79b46cd5 | 105 | |
be7f5d4d JC |
106 | match key_str { |
107 | "xesam:artist" => { | |
108 | // Variant holding a variant that should just be a Vec<&str> I | |
109 | // believe. This is _the recommended_ way to do this by the author. | |
110 | let inner_value = | |
111 | value?.as_iter()?.next()?.as_iter()?.next()?; | |
112 | let artist = inner_value.as_str()?; | |
113 | update_map(spotify_match_map.clone(), "artist", artist); | |
114 | } | |
115 | "xesam:title" => { | |
116 | let title = value?.as_iter()?.next()?.as_str()?; | |
117 | update_map(spotify_match_map.clone(), "title", title); | |
118 | } | |
119 | _ => (), | |
79b46cd5 | 120 | } |
79b46cd5 | 121 | } |
be7f5d4d JC |
122 | Some(()) |
123 | }); | |
124 | } | |
125 | None => (), | |
126 | } | |
127 | match &pc.changed_properties.get("PlaybackStatus") { | |
128 | Some(variant) => { | |
129 | let playback_status = &variant.0; | |
130 | update_map( | |
131 | spotify_match_map.clone(), | |
132 | "playback_status", | |
133 | match &*playback_status.as_str().unwrap_or("") { | |
134 | "Playing" => "🔊", | |
135 | _ => "🔈", | |
136 | }, | |
137 | ); | |
138 | } | |
139 | None => (), | |
140 | } | |
baedbc7e JC |
141 | true |
142 | }, | |
143 | ); | |
144 | ||
bbc46eb1 JC |
145 | let date_time_map = arc_locked_xset_map.clone(); |
146 | let _ = thread::spawn(move || -> Result<(), std::time::SystemTimeError> { | |
147 | let mut last_run_at = std::time::SystemTime::now(); | |
148 | loop { | |
149 | let sys_time = std::time::SystemTime::now(); | |
150 | let elapsed_time = sys_time.duration_since(last_run_at)?; | |
151 | if elapsed_time.as_secs() > 60 { | |
152 | last_run_at = sys_time; | |
153 | update_map( | |
154 | date_time_map.clone(), | |
155 | "date_time", | |
156 | get_local_time_string().as_str(), | |
157 | ); | |
158 | } | |
159 | thread::sleep(Duration::from_millis(5000)); | |
160 | } | |
161 | }); | |
162 | ||
baedbc7e JC |
163 | loop { |
164 | conn.process(Duration::from_millis(1000))?; | |
165 | } | |
166 | } |