+use dbus::arg;
use dbus::blocking::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged;
use dbus::blocking::Connection;
use dbus::message::Message;
use std::collections::HashMap;
+use std::process::Command;
use std::sync::{Arc, Mutex};
+use std::thread;
use std::time::Duration;
+// Custom signal type impl
+// Mostly copied from library examples
+#[derive(Debug)]
+pub struct ComJacobCasperMailUnreadCount {
+ pub count: u32,
+}
+
+impl arg::AppendAll for ComJacobCasperMailUnreadCount {
+ fn append(&self, iter: &mut arg::IterAppend) {
+ arg::RefArg::append(&self.count, iter);
+ }
+}
+
+impl arg::ReadAll for ComJacobCasperMailUnreadCount {
+ fn read(iter: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
+ Ok(ComJacobCasperMailUnreadCount {
+ count: iter.read()?,
+ })
+ }
+}
+
+impl dbus::message::SignalArgs for ComJacobCasperMailUnreadCount {
+ const NAME: &'static str = "UnreadCount";
+ const INTERFACE: &'static str = "com.jacobcasper.Mail";
+}
+
+fn get_local_time_string() -> std::string::String {
+ use chrono::prelude::*;
+ let local_time = chrono::prelude::Local::now();
+ return format!(
+ "{}-{}-{:02} {}:{:02}",
+ local_time.year(),
+ local_time.month(),
+ local_time.day(),
+ local_time.hour(),
+ local_time.minute(),
+ );
+}
+
fn update_map(arc_map: Arc<Mutex<HashMap<String, String>>>, key: &str, val: &str) {
let clone_arc = arc_map.clone();
let mut map = clone_arc.lock().unwrap();
map.insert(String::from(key), String::from(val));
- for (k, v) in &*map {
- println! {"{}: {}", k.clone(), v.clone()};
- }
+}
+
+fn update_xsetroot(arc_map: Arc<Mutex<HashMap<String, String>>>) {
+ let clone_arc = arc_map.clone();
+ let map = clone_arc.lock().unwrap();
+ let _ = Command::new("xsetroot")
+ .arg("-name")
+ .arg(format!(
+ "[{playback_status} {title} - {artist}] | ✉ {unread_count} | {date_time}",
+ playback_status = map.get("playback_status").unwrap_or(&String::from("🔈")),
+ title = map.get("title").unwrap_or(&String::from("")),
+ artist = map.get("artist").unwrap_or(&String::from("")),
+ unread_count = map.get("unread_count").unwrap_or(&String::from("?")),
+ date_time = map.get("date_time").unwrap_or(&get_local_time_string()),
+ ))
+ .spawn();
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let arc_locked_xset_map = Arc::new(Mutex::new(HashMap::new()));
let conn = Connection::new_session()?;
- let proxy = conn.with_proxy(
+ let mail_proxy = conn.with_proxy(
+ "com.jacobcasper.Mail",
+ "/com/jacobcasper/Mail/Unread",
+ Duration::from_millis(5000),
+ );
+
+ let mail_match_map = arc_locked_xset_map.clone();
+ let _ = mail_proxy.match_signal(
+ move |m: ComJacobCasperMailUnreadCount, _: &Connection, _: &Message| {
+ update_map(
+ mail_match_map.clone(),
+ "unread_count",
+ m.count.to_string().as_str(),
+ );
+ update_xsetroot(mail_match_map.clone());
+ true
+ },
+ );
+
+ let spotify_proxy = conn.with_proxy(
"org.mpris.MediaPlayer2.spotify",
"/org/mpris/MediaPlayer2",
Duration::from_millis(5000),
);
let spotify_match_map = arc_locked_xset_map.clone();
- let _ = proxy.match_signal(
+ let _ = spotify_proxy.match_signal(
move |pc: PropertiesPropertiesChanged, _: &Connection, _: &Message| {
- pc.changed_properties["Metadata"]
- .0
- .as_iter()
- .and_then(|mut iter| {
- // Iterating over a RefArg goes key, value, key, value... Insane honestly.
- while let Some(key) = iter.next() {
- let key_str = key.as_str()?;
- let value = iter.next();
-
- match key_str {
- "xesam:artist" => {
- // Variant holding a variant that should just be a Vec<&str> I
- // believe. This is _the recommended_ way to do this by the author.
- let inner_value = value?.as_iter()?.next()?.as_iter()?.next()?;
- let artist = inner_value.as_str()?;
- update_map(spotify_match_map.clone(), "artist", artist);
- }
- "xesam:title" => {
- let title = value?.as_iter()?.next()?.as_str()?;
- update_map(spotify_match_map.clone(), "title", title);
+ match pc.changed_properties.get("Metadata") {
+ Some(variant) => {
+ variant.0.as_iter().and_then(|mut iter| {
+ // Iterating over a RefArg goes key, value, key, value... Insane honestly.
+ while let Some(key) = iter.next() {
+ let key_str = key.as_str()?;
+ let value = iter.next();
+
+ match key_str {
+ "xesam:artist" => {
+ // Variant holding a variant that should just be a Vec<&str> I
+ // believe. This is _the recommended_ way to do this by the author.
+ let inner_value =
+ value?.as_iter()?.next()?.as_iter()?.next()?;
+ let artist = inner_value.as_str()?;
+ update_map(spotify_match_map.clone(), "artist", artist);
+ }
+ "xesam:title" => {
+ let title = value?.as_iter()?.next()?.as_str()?;
+ update_map(spotify_match_map.clone(), "title", title);
+ }
+ _ => (),
}
- _ => (),
}
- }
- Some(())
- });
+ update_xsetroot(spotify_match_map.clone());
+ Some(())
+ });
+ }
+ None => (),
+ }
+ match &pc.changed_properties.get("PlaybackStatus") {
+ Some(variant) => {
+ let playback_status = &variant.0;
+ update_map(
+ spotify_match_map.clone(),
+ "playback_status",
+ match &*playback_status.as_str().unwrap_or("") {
+ "Playing" => "🔊",
+ _ => "🔈",
+ },
+ );
+ update_xsetroot(spotify_match_map.clone());
+ }
+ None => (),
+ }
true
},
);
+ let date_time_map = arc_locked_xset_map.clone();
+ let _ = thread::spawn(move || -> Result<(), std::time::SystemTimeError> {
+ let mut last_run_at = std::time::SystemTime::now();
+ loop {
+ let sys_time = std::time::SystemTime::now();
+ let elapsed_time = sys_time.duration_since(last_run_at)?;
+ if elapsed_time.as_secs() > 60 {
+ last_run_at = sys_time;
+ update_map(
+ date_time_map.clone(),
+ "date_time",
+ get_local_time_string().as_str(),
+ );
+ update_xsetroot(date_time_map.clone());
+ }
+ thread::sleep(Duration::from_millis(5000));
+ }
+ });
+
loop {
conn.process(Duration::from_millis(1000))?;
}