Compare commits
1 Commits
master
...
better-log
Author | SHA1 | Date | |
---|---|---|---|
ffd4d9a2c2 |
64
Cargo.lock
generated
64
Cargo.lock
generated
@ -33,12 +33,6 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "backlight"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a1dd1f0fb2c34052222f8937bd7ab412c664e8436e2c775d1b64836974889f21"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.50"
|
version = "0.3.50"
|
||||||
@ -279,9 +273,9 @@ version = "0.1.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.21",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.41",
|
"syn 1.0.58",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -476,6 +470,12 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numtoa"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e521b6adefa0b2c1fa5d2abdf9a5216288686fe6146249215d884c0e5ab320b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc"
|
name = "objc"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
@ -537,9 +537,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.21"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid 0.2.1",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
@ -574,7 +574,7 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.21",
|
"proc-macro2 1.0.24",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -632,8 +632,9 @@ dependencies = [
|
|||||||
name = "simple-osd-brightness"
|
name = "simple-osd-brightness"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backlight",
|
|
||||||
"simple-osd-common",
|
"simple-osd-common",
|
||||||
|
"sysfs-class",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -714,11 +715,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.41"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b"
|
checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.21",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"unicode-xid 0.2.1",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
@ -738,12 +739,41 @@ version = "0.12.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.21",
|
"proc-macro2 1.0.24",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.41",
|
"syn 1.0.58",
|
||||||
"unicode-xid 0.2.1",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sysfs-class"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e1bbcf869732c45a77898f7f61ed6d411dfc37613517e444842f58d428856d1"
|
||||||
|
dependencies = [
|
||||||
|
"numtoa",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.24",
|
||||||
|
"quote 1.0.7",
|
||||||
|
"syn 1.0.58",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
|
@ -8,13 +8,13 @@ use std::io;
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use osd::notify::{OSD, Urgency};
|
|
||||||
use osd::config::Config;
|
use osd::config::Config;
|
||||||
|
use osd::notify::{Urgency, OSD};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
enum Threshold {
|
enum Threshold {
|
||||||
Percentage(i32),
|
Percentage(i32),
|
||||||
Minutes(i32)
|
Minutes(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
@ -22,17 +22,21 @@ enum State {
|
|||||||
Low,
|
Low,
|
||||||
Critical,
|
Critical,
|
||||||
Charging,
|
Charging,
|
||||||
Normal
|
Normal,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn threshold_sane(thresh: Threshold) -> Option<Threshold> {
|
fn threshold_sane(thresh: Threshold) -> Option<Threshold> {
|
||||||
match thresh {
|
match thresh {
|
||||||
Threshold::Percentage(p) => {
|
Threshold::Percentage(p) => {
|
||||||
if p < 0 || p > 100 { return None; }
|
if p < 0 || p > 100 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
Some(thresh)
|
Some(thresh)
|
||||||
},
|
}
|
||||||
Threshold::Minutes(m) => {
|
Threshold::Minutes(m) => {
|
||||||
if m < 0 { return None; }
|
if m < 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
Some(thresh)
|
Some(thresh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,9 +50,12 @@ fn parse_threshold(thresh: String) -> Option<Threshold> {
|
|||||||
let parsed = s.parse();
|
let parsed = s.parse();
|
||||||
|
|
||||||
match last {
|
match last {
|
||||||
Some('%') => parsed.map(Threshold::Percentage).ok().and_then(threshold_sane),
|
Some('%') => parsed
|
||||||
|
.map(Threshold::Percentage)
|
||||||
|
.ok()
|
||||||
|
.and_then(threshold_sane),
|
||||||
Some('m') => parsed.map(Threshold::Minutes).ok().and_then(threshold_sane),
|
Some('m') => parsed.map(Threshold::Minutes).ok().and_then(threshold_sane),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,11 +65,17 @@ mod parse_threshold_tests {
|
|||||||
use super::Threshold;
|
use super::Threshold;
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_percentage() {
|
fn parses_percentage() {
|
||||||
assert_eq!(parse_threshold("15%".to_string()), Some(Threshold::Percentage(15)));
|
assert_eq!(
|
||||||
|
parse_threshold("15%".to_string()),
|
||||||
|
Some(Threshold::Percentage(15))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_minutes() {
|
fn parses_minutes() {
|
||||||
assert_eq!(parse_threshold("10m".to_string()), Some(Threshold::Minutes(10)));
|
assert_eq!(
|
||||||
|
parse_threshold("10m".to_string()),
|
||||||
|
Some(Threshold::Minutes(10))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn fails_on_incorrect_percentage() {
|
fn fails_on_incorrect_percentage() {
|
||||||
@ -100,16 +113,19 @@ fn format_duration(duration: f32) -> String {
|
|||||||
let minutes = (d % 3600) / 60;
|
let minutes = (d % 3600) / 60;
|
||||||
let seconds = d % 60;
|
let seconds = d % 60;
|
||||||
|
|
||||||
|
|
||||||
if hours > 0 {
|
if hours > 0 {
|
||||||
s.push_str(&format!("{}h", hours));
|
s.push_str(&format!("{}h", hours));
|
||||||
}
|
}
|
||||||
if minutes > 0 {
|
if minutes > 0 {
|
||||||
if hours > 0 { s.push(' '); }
|
if hours > 0 {
|
||||||
|
s.push(' ');
|
||||||
|
}
|
||||||
s.push_str(&format!("{}m", minutes));
|
s.push_str(&format!("{}m", minutes));
|
||||||
}
|
}
|
||||||
if seconds > 0 {
|
if seconds > 0 {
|
||||||
if hours > 0 || minutes > 0 { s.push(' '); }
|
if hours > 0 || minutes > 0 {
|
||||||
|
s.push(' ');
|
||||||
|
}
|
||||||
s.push_str(&format!("{}s", seconds));
|
s.push_str(&format!("{}s", seconds));
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
@ -158,8 +174,10 @@ fn main() -> battery::Result<()> {
|
|||||||
let low_threshold_str = config.get_default("threshold", "low", String::from("30m"));
|
let low_threshold_str = config.get_default("threshold", "low", String::from("30m"));
|
||||||
let critical_threshold_str = config.get_default("threshold", "critical", String::from("10m"));
|
let critical_threshold_str = config.get_default("threshold", "critical", String::from("10m"));
|
||||||
|
|
||||||
let low_threshold = parse_threshold(low_threshold_str).expect("Low threshold is incorrect: must be either a percentage or minutes");
|
let low_threshold = parse_threshold(low_threshold_str)
|
||||||
let critical_threshold = parse_threshold(critical_threshold_str).expect("Critical threshold is incorrect: must be either a percentage or minutes");
|
.expect("Low threshold is incorrect: must be either a percentage or minutes");
|
||||||
|
let critical_threshold = parse_threshold(critical_threshold_str)
|
||||||
|
.expect("Critical threshold is incorrect: must be either a percentage or minutes");
|
||||||
|
|
||||||
let refresh_interval = config.get_default("default", "refresh interval", 30);
|
let refresh_interval = config.get_default("default", "refresh interval", 30);
|
||||||
|
|
||||||
@ -182,7 +200,6 @@ fn main() -> battery::Result<()> {
|
|||||||
let mut state: State;
|
let mut state: State;
|
||||||
let mut last_state: State = State::Normal;
|
let mut last_state: State = State::Normal;
|
||||||
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
state = match battery.state() {
|
state = match battery.state() {
|
||||||
battery::State::Charging => State::Charging,
|
battery::State::Charging => State::Charging,
|
||||||
@ -192,12 +209,36 @@ fn main() -> battery::Result<()> {
|
|||||||
let tte = battery.time_to_empty().map(|q| q.value).unwrap_or(0.) as i32 / 60;
|
let tte = battery.time_to_empty().map(|q| q.value).unwrap_or(0.) as i32 / 60;
|
||||||
println!("{:?}, {:?}", soc, tte);
|
println!("{:?}, {:?}", soc, tte);
|
||||||
let low = match low_threshold {
|
let low = match low_threshold {
|
||||||
Threshold::Percentage(p) => if soc <= p { State::Low } else { State::Normal },
|
Threshold::Percentage(p) => {
|
||||||
Threshold::Minutes(m) => if tte <= m { State::Low } else { State::Normal }
|
if soc <= p {
|
||||||
|
State::Low
|
||||||
|
} else {
|
||||||
|
State::Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Threshold::Minutes(m) => {
|
||||||
|
if tte <= m {
|
||||||
|
State::Low
|
||||||
|
} else {
|
||||||
|
State::Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match critical_threshold {
|
match critical_threshold {
|
||||||
Threshold::Percentage(p) => if soc <= p { State::Critical } else { low },
|
Threshold::Percentage(p) => {
|
||||||
Threshold::Minutes(m) => if tte <= m { State::Critical } else { low }
|
if soc <= p {
|
||||||
|
State::Critical
|
||||||
|
} else {
|
||||||
|
low
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Threshold::Minutes(m) => {
|
||||||
|
if tte <= m {
|
||||||
|
State::Critical
|
||||||
|
} else {
|
||||||
|
low
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -206,25 +247,34 @@ fn main() -> battery::Result<()> {
|
|||||||
match state {
|
match state {
|
||||||
State::Charging => {
|
State::Charging => {
|
||||||
if let Some(ttf) = battery.time_to_full() {
|
if let Some(ttf) = battery.time_to_full() {
|
||||||
osd.title = Some(format!("Charging, {} until full", format_duration(ttf.value)));
|
osd.title = Some(format!(
|
||||||
|
"Charging, {} until full",
|
||||||
|
format_duration(ttf.value)
|
||||||
|
));
|
||||||
osd.urgency = Urgency::Low;
|
osd.urgency = Urgency::Low;
|
||||||
osd.update().unwrap();
|
osd.update().unwrap();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
State::Low => {
|
State::Low => {
|
||||||
if let Some(tte) = battery.time_to_empty() {
|
if let Some(tte) = battery.time_to_empty() {
|
||||||
osd.title = Some(format!("Low battery, {} remaining", format_duration(tte.value)));
|
osd.title = Some(format!(
|
||||||
|
"Low battery, {} remaining",
|
||||||
|
format_duration(tte.value)
|
||||||
|
));
|
||||||
osd.urgency = Urgency::Normal;
|
osd.urgency = Urgency::Normal;
|
||||||
osd.update().unwrap();
|
osd.update().unwrap();
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
State::Normal | State::Critical => { }
|
State::Normal | State::Critical => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if state == State::Critical {
|
if state == State::Critical {
|
||||||
if let Some(tte) = battery.time_to_empty() {
|
if let Some(tte) = battery.time_to_empty() {
|
||||||
osd.title = Some(format!("Critically low battery, {} remaining", format_duration(tte.value)));
|
osd.title = Some(format!(
|
||||||
|
"Critically low battery, {} remaining",
|
||||||
|
format_duration(tte.value)
|
||||||
|
));
|
||||||
osd.urgency = Urgency::Critical;
|
osd.urgency = Urgency::Critical;
|
||||||
osd.update().unwrap();
|
osd.update().unwrap();
|
||||||
};
|
};
|
||||||
|
@ -4,14 +4,13 @@
|
|||||||
extern crate blurz;
|
extern crate blurz;
|
||||||
extern crate simple_osd_common as osd;
|
extern crate simple_osd_common as osd;
|
||||||
|
|
||||||
use blurz::bluetooth_session::BluetoothSession;
|
|
||||||
use blurz::bluetooth_adapter::BluetoothAdapter;
|
use blurz::bluetooth_adapter::BluetoothAdapter;
|
||||||
|
use blurz::bluetooth_session::BluetoothSession;
|
||||||
|
|
||||||
use osd::config::Config;
|
use osd::config::Config;
|
||||||
use osd::notify::{OSD, Urgency};
|
use osd::notify::{Urgency, OSD};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
||||||
let mut config = Config::new("bluetooth");
|
let mut config = Config::new("bluetooth");
|
||||||
|
|
||||||
let refresh_interval = config.get_default("default", "refresh interval", 15);
|
let refresh_interval = config.get_default("default", "refresh interval", 15);
|
||||||
|
@ -8,4 +8,5 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
simple-osd-common = { version = "0.1", path = "../common" }
|
simple-osd-common = { version = "0.1", path = "../common" }
|
||||||
backlight = "0.1.1"
|
sysfs-class = "0.1.2"
|
||||||
|
thiserror = "1.0"
|
@ -1,35 +1,60 @@
|
|||||||
// This is free and unencumbered software released into the public domain.
|
// This is free and unencumbered software released into the public domain.
|
||||||
// balsoft 2020
|
// balsoft 2020
|
||||||
|
|
||||||
extern crate backlight;
|
|
||||||
extern crate simple_osd_common as osd;
|
extern crate simple_osd_common as osd;
|
||||||
|
extern crate sysfs_class;
|
||||||
|
|
||||||
use osd::config::Config;
|
use osd::config::Config;
|
||||||
use osd::notify::{OSD, OSDContents, OSDProgressText};
|
use osd::notify::{OSDContents, OSDProgressText, OSD};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use log;
|
||||||
|
use sysfs_class::{Backlight, Brightness, SysClass};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use backlight::Brightness;
|
#[derive(Debug, Error)]
|
||||||
|
pub enum BrightnessError {
|
||||||
|
#[error("Failed to initialite backlight (possibly invalid backend): {0:?}")]
|
||||||
|
BacklightInitError(std::io::Error),
|
||||||
|
#[error("Failed to get maximum brightness: {0:?}")]
|
||||||
|
BacklightMaxBrightnessError(std::io::Error),
|
||||||
|
#[error("Failed to get brightness: {0:?}")]
|
||||||
|
BacklightBrightnessError(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn brightness_daemon() -> Result<(), BrightnessError> {
|
||||||
let mut config = Config::new("brightness");
|
let mut config = Config::new("brightness");
|
||||||
|
|
||||||
let refresh_interval = config.get_default("default", "refresh interval", 1);
|
let refresh_interval = config.get_default("default", "refresh interval", 1);
|
||||||
|
|
||||||
let brightness = Brightness::default();
|
let backend = config.get_default(
|
||||||
|
"default",
|
||||||
|
"backlight backend",
|
||||||
|
String::from("intel_backlight"),
|
||||||
|
);
|
||||||
|
|
||||||
let m = brightness.get_max_brightness().unwrap() as f32;
|
let brightness: Backlight = SysClass::from_path(&PathBuf::from(backend))
|
||||||
|
.map_err(BrightnessError::BacklightInitError)?;
|
||||||
|
|
||||||
|
let m = brightness
|
||||||
|
.max_brightness()
|
||||||
|
.map(|b| b as f32)
|
||||||
|
.map_err(BrightnessError::BacklightMaxBrightnessError)?;
|
||||||
|
|
||||||
let mut osd = OSD::new();
|
let mut osd = OSD::new();
|
||||||
osd.title = Some(String::from("Screen brightness"));
|
osd.title = Some(String::from("Screen brightness"));
|
||||||
|
|
||||||
let mut b : f32;
|
let mut b: f32;
|
||||||
|
|
||||||
let mut last_b : f32 = 0.;
|
let mut last_b: f32 = 0.;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
b = brightness.get_brightness().unwrap() as f32;
|
b = brightness
|
||||||
|
.brightness()
|
||||||
|
.map(|b| b as f32)
|
||||||
|
.map_err(BrightnessError::BacklightBrightnessError)?;
|
||||||
|
|
||||||
if (b - last_b).abs() > 0.1 {
|
if (b - last_b).abs() > 0.1 {
|
||||||
osd.contents = OSDContents::Progress(b/m, OSDProgressText::Percentage);
|
osd.contents = OSDContents::Progress(b / m, OSDProgressText::Percentage);
|
||||||
osd.update().unwrap();
|
osd.update().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,3 +63,13 @@ fn main() {
|
|||||||
std::thread::sleep(std::time::Duration::from_secs(refresh_interval))
|
std::thread::sleep(std::time::Duration::from_secs(refresh_interval))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
match brightness_daemon() {
|
||||||
|
Ok(_) => { },
|
||||||
|
Err(err) => {
|
||||||
|
error!(err);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
extern crate simple_osd_common as osd;
|
extern crate simple_osd_common as osd;
|
||||||
|
|
||||||
use osd::notify::{OSD, OSDContents, OSDProgressText, Urgency};
|
|
||||||
use osd::config::Config;
|
use osd::config::Config;
|
||||||
use std::time::Duration;
|
use osd::notify::{OSDContents, OSDProgressText, Urgency, OSD};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut config = Config::new("simple-example");
|
let mut config = Config::new("simple-example");
|
||||||
@ -12,9 +12,13 @@ fn main() {
|
|||||||
|
|
||||||
println!("Value of foo is {}", foo);
|
println!("Value of foo is {}", foo);
|
||||||
|
|
||||||
let example_no_default = config.get::<i32>("example section", "example variable with no default");
|
let example_no_default =
|
||||||
|
config.get::<i32>("example section", "example variable with no default");
|
||||||
|
|
||||||
println!("Value of example variable with no default is {:?}", example_no_default);
|
println!(
|
||||||
|
"Value of example variable with no default is {:?}",
|
||||||
|
example_no_default
|
||||||
|
);
|
||||||
|
|
||||||
let refresh_interval = config.get_default("default", "refresh interval", 1);
|
let refresh_interval = config.get_default("default", "refresh interval", 1);
|
||||||
|
|
||||||
@ -26,13 +30,15 @@ fn main() {
|
|||||||
let mut percentage = 0.;
|
let mut percentage = 0.;
|
||||||
|
|
||||||
let mut osd_progress_bar_percentage = OSD::new();
|
let mut osd_progress_bar_percentage = OSD::new();
|
||||||
osd_progress_bar_percentage.title = Some("A progress bar showing important percentage!".to_string());
|
osd_progress_bar_percentage.title =
|
||||||
|
Some("A progress bar showing important percentage!".to_string());
|
||||||
|
|
||||||
let eta = 15.;
|
let eta = 15.;
|
||||||
let mut elapsed = 0.;
|
let mut elapsed = 0.;
|
||||||
|
|
||||||
let mut osd_progress_bar_text = OSD::new();
|
let mut osd_progress_bar_text = OSD::new();
|
||||||
osd_progress_bar_text.title = Some("Nuclear warhead launch in progress, time left:".to_string());
|
osd_progress_bar_text.title =
|
||||||
|
Some("Nuclear warhead launch in progress, time left:".to_string());
|
||||||
osd_progress_bar_text.urgency = Urgency::Low;
|
osd_progress_bar_text.urgency = Urgency::Low;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -40,9 +46,13 @@ fn main() {
|
|||||||
|
|
||||||
elapsed = (elapsed + refresh_interval as f32) % eta;
|
elapsed = (elapsed + refresh_interval as f32) % eta;
|
||||||
|
|
||||||
osd_progress_bar_percentage.contents = OSDContents::Progress(percentage, OSDProgressText::Percentage);
|
osd_progress_bar_percentage.contents =
|
||||||
|
OSDContents::Progress(percentage, OSDProgressText::Percentage);
|
||||||
|
|
||||||
osd_progress_bar_text.contents = OSDContents::Progress(elapsed / eta, OSDProgressText::Text(Some(format!("{}s / {}s", elapsed, eta))));
|
osd_progress_bar_text.contents = OSDContents::Progress(
|
||||||
|
elapsed / eta,
|
||||||
|
OSDProgressText::Text(Some(format!("{}s / {}s", elapsed, eta))),
|
||||||
|
);
|
||||||
|
|
||||||
osd_simple.update();
|
osd_simple.update();
|
||||||
osd_progress_bar_percentage.update();
|
osd_progress_bar_percentage.update();
|
||||||
@ -50,5 +60,4 @@ fn main() {
|
|||||||
|
|
||||||
sleep(Duration::from_secs(refresh_interval));
|
sleep(Duration::from_secs(refresh_interval));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,29 @@
|
|||||||
// This is free and unencumbered software released into the public domain.
|
// This is free and unencumbered software released into the public domain.
|
||||||
// balsoft 2020
|
// balsoft 2020
|
||||||
|
|
||||||
extern crate notify_rust;
|
|
||||||
extern crate xdg;
|
|
||||||
extern crate configparser;
|
extern crate configparser;
|
||||||
extern crate dbus;
|
extern crate dbus;
|
||||||
|
extern crate notify_rust;
|
||||||
|
extern crate xdg;
|
||||||
|
|
||||||
pub static APPNAME: &str = "simple-osd";
|
pub static APPNAME: &str = "simple-osd";
|
||||||
|
|
||||||
pub mod config {
|
pub mod config {
|
||||||
use configparser::ini::Ini;
|
use configparser::ini::Ini;
|
||||||
|
use std::fs::{metadata, File};
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
use std::fs::{File, metadata};
|
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::default::Default;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::default::Default;
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
config_path: Option<String>,
|
config_path: Option<String>,
|
||||||
config: Ini
|
config: Ini,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|
||||||
pub fn new(name: &'static str) -> Config {
|
pub fn new(name: &'static str) -> Config {
|
||||||
let mut config = Ini::new();
|
let mut config = Ini::new();
|
||||||
|
|
||||||
@ -33,7 +32,10 @@ pub mod config {
|
|||||||
let config_path_option = xdg_dirs.place_config_file(name).ok();
|
let config_path_option = xdg_dirs.place_config_file(name).ok();
|
||||||
|
|
||||||
if let Some(config_path_buf) = config_path_option.clone() {
|
if let Some(config_path_buf) = config_path_option.clone() {
|
||||||
if metadata(config_path_buf.clone()).map(|m| m.is_file()).unwrap_or(false) {
|
if metadata(config_path_buf.clone())
|
||||||
|
.map(|m| m.is_file())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
let _ = config.load(config_path_buf.to_str().unwrap());
|
let _ = config.load(config_path_buf.to_str().unwrap());
|
||||||
} else {
|
} else {
|
||||||
let _ = File::create(config_path_buf);
|
let _ = File::create(config_path_buf);
|
||||||
@ -42,32 +44,42 @@ pub mod config {
|
|||||||
|
|
||||||
let config_path = config_path_option.map(|p| p.to_str().unwrap().to_string());
|
let config_path = config_path_option.map(|p| p.to_str().unwrap().to_string());
|
||||||
|
|
||||||
Config { config, config_path }
|
Config {
|
||||||
|
config,
|
||||||
|
config_path,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<T>(&mut self, section: &str, key: &str) -> Option<T>
|
pub fn get<T>(&mut self, section: &str, key: &str) -> Option<T>
|
||||||
where
|
where
|
||||||
T: FromStr,
|
T: FromStr,
|
||||||
<T as FromStr>::Err: Debug
|
<T as FromStr>::Err: Debug,
|
||||||
{
|
{
|
||||||
self.config.get(section, key).map(|s: String| { s.parse().unwrap() }).or_else(|| {
|
self.config
|
||||||
self.config.set(section, key, None);
|
.get(section, key)
|
||||||
self.config_path.as_ref().map(|path| self.config.write(path.as_str()));
|
.map(|s: String| s.parse().unwrap())
|
||||||
None
|
.or_else(|| {
|
||||||
})
|
self.config.set(section, key, None);
|
||||||
|
self.config_path
|
||||||
|
.as_ref()
|
||||||
|
.map(|path| self.config.write(path.as_str()));
|
||||||
|
None
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default<T>(&mut self, section: &str, key: &str, default: T) -> T
|
pub fn get_default<T>(&mut self, section: &str, key: &str, default: T) -> T
|
||||||
where
|
where
|
||||||
T: FromStr,
|
T: FromStr,
|
||||||
T: Display,
|
T: Display,
|
||||||
<T as FromStr>::Err: Debug
|
<T as FromStr>::Err: Debug,
|
||||||
{
|
{
|
||||||
let val: Option<T> = self.get(section, key);
|
let val: Option<T> = self.get(section, key);
|
||||||
|
|
||||||
val.unwrap_or_else(|| {
|
val.unwrap_or_else(|| {
|
||||||
self.config.set(section, key, Some(format!("{}", default)));
|
self.config.set(section, key, Some(format!("{}", default)));
|
||||||
self.config_path.as_ref().map(|path| self.config.write(path.as_str()));
|
self.config_path
|
||||||
|
.as_ref()
|
||||||
|
.map(|path| self.config.write(path.as_str()));
|
||||||
default
|
default
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -77,28 +89,28 @@ pub mod config {
|
|||||||
T: FromStr,
|
T: FromStr,
|
||||||
T: Display,
|
T: Display,
|
||||||
T: Default,
|
T: Default,
|
||||||
<T as FromStr>::Err: Debug
|
<T as FromStr>::Err: Debug,
|
||||||
{
|
{
|
||||||
self.get_default(section, key, T::default())
|
self.get_default(section, key, T::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub mod notify {
|
pub mod notify {
|
||||||
use notify_rust::{Notification, NotificationHandle};
|
|
||||||
use dbus::ffidisp::{Connection, BusType};
|
|
||||||
use std::thread;
|
|
||||||
pub use notify_rust::Urgency;
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use dbus::ffidisp::{BusType, Connection};
|
||||||
|
pub use notify_rust::Urgency;
|
||||||
|
use notify_rust::{Notification, NotificationHandle};
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
pub enum OSDProgressText {
|
pub enum OSDProgressText {
|
||||||
Percentage,
|
Percentage,
|
||||||
Text(Option<String>)
|
Text(Option<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum OSDContents {
|
pub enum OSDContents {
|
||||||
Simple(Option<String>),
|
Simple(Option<String>),
|
||||||
Progress(f32, OSDProgressText)
|
Progress(f32, OSDProgressText),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for OSDContents {
|
impl Default for OSDContents {
|
||||||
@ -110,7 +122,7 @@ pub mod notify {
|
|||||||
struct CustomHandle {
|
struct CustomHandle {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub connection: Connection,
|
pub connection: Connection,
|
||||||
pub notification: Notification
|
pub notification: Notification,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OSD {
|
pub struct OSD {
|
||||||
@ -135,7 +147,7 @@ pub mod notify {
|
|||||||
|
|
||||||
// Internal notification
|
// Internal notification
|
||||||
notification: Notification,
|
notification: Notification,
|
||||||
id: Option<u32>
|
id: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OSD {
|
impl OSD {
|
||||||
@ -155,22 +167,29 @@ pub mod notify {
|
|||||||
let notification = Notification::new();
|
let notification = Notification::new();
|
||||||
|
|
||||||
OSD {
|
OSD {
|
||||||
title: None, icon: None,
|
title: None,
|
||||||
|
icon: None,
|
||||||
contents: OSDContents::default(),
|
contents: OSDContents::default(),
|
||||||
urgency: Urgency::Normal, id: None,
|
urgency: Urgency::Normal,
|
||||||
|
id: None,
|
||||||
timeout,
|
timeout,
|
||||||
length, full, empty, start, end,
|
length,
|
||||||
notification
|
full,
|
||||||
|
empty,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
notification,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_fake_handle(id: u32, notification: Notification) -> NotificationHandle {
|
fn construct_fake_handle(id: u32, notification: Notification) -> NotificationHandle {
|
||||||
let h = CustomHandle
|
let h = CustomHandle {
|
||||||
{ id,
|
id,
|
||||||
connection: Connection::get_private(BusType::Session).unwrap(),
|
connection: Connection::get_private(BusType::Session).unwrap(),
|
||||||
notification };
|
notification,
|
||||||
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
let handle : NotificationHandle = std::mem::transmute(h);
|
let handle: NotificationHandle = std::mem::transmute(h);
|
||||||
handle
|
handle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,19 +223,21 @@ pub mod notify {
|
|||||||
s.push_str(((value * 100.) as i32).to_string().as_str());
|
s.push_str(((value * 100.) as i32).to_string().as_str());
|
||||||
|
|
||||||
s.push('%');
|
s.push('%');
|
||||||
},
|
}
|
||||||
OSDProgressText::Text(text) => {
|
OSDProgressText::Text(text) => {
|
||||||
if let Some(text) = text.as_ref() { s.push_str(text.as_str()) };
|
if let Some(text) = text.as_ref() {
|
||||||
|
s.push_str(text.as_str())
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Some(s)
|
Some(s)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.id.map(|i| self.notification.id(i));
|
self.id.map(|i| self.notification.id(i));
|
||||||
let handle = self.notification
|
let handle = self
|
||||||
|
.notification
|
||||||
.summary(self.title.as_deref().unwrap_or(""))
|
.summary(self.title.as_deref().unwrap_or(""))
|
||||||
.body(&text.unwrap_or_else(String::new))
|
.body(&text.unwrap_or_else(String::new))
|
||||||
.icon(self.icon.as_deref().unwrap_or(""))
|
.icon(self.icon.as_deref().unwrap_or(""))
|
||||||
@ -228,8 +249,10 @@ pub mod notify {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on_close<F: 'static>(&mut self, callback: F)
|
||||||
pub fn on_close<F: 'static>(&mut self, callback: F) where F: std::ops::FnOnce() + Send {
|
where
|
||||||
|
F: std::ops::FnOnce() + Send,
|
||||||
|
{
|
||||||
if let Some(id) = self.id {
|
if let Some(id) = self.id {
|
||||||
let notification = self.notification.clone();
|
let notification = self.notification.clone();
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
pub extern crate simple_osd_common as osd;
|
|
||||||
pub extern crate mpris;
|
pub extern crate mpris;
|
||||||
|
pub extern crate simple_osd_common as osd;
|
||||||
|
|
||||||
pub use std::sync::{Arc, Mutex};
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
pub use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use std::ops::{Deref};
|
use std::ops::Deref;
|
||||||
|
|
||||||
pub use osd::notify::{OSD, OSDContents, OSDProgressText};
|
|
||||||
pub use osd::config::Config;
|
pub use osd::config::Config;
|
||||||
|
pub use osd::notify::{OSDContents, OSDProgressText, OSD};
|
||||||
|
|
||||||
use mpris::{PlaybackStatus, PlayerFinder};
|
use mpris::{PlaybackStatus, PlayerFinder};
|
||||||
|
|
||||||
@ -44,12 +44,12 @@ fn format_artists(artists: Vec<&str>) -> Option<String> {
|
|||||||
v.reverse();
|
v.reverse();
|
||||||
|
|
||||||
if v.len() < 2 {
|
if v.len() < 2 {
|
||||||
return Some(v.pop()?.to_string())
|
return Some(v.pop()?.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
|
|
||||||
for _ in 0..v.len()-2 {
|
for _ in 0..v.len() - 2 {
|
||||||
s.push_str(v.pop()?);
|
s.push_str(v.pop()?);
|
||||||
s.push_str(", ")
|
s.push_str(", ")
|
||||||
}
|
}
|
||||||
@ -72,16 +72,24 @@ mod format_artists_test {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn one() {
|
fn one() {
|
||||||
assert_eq!(format_artists(["John Doe"].to_vec()), Some("John Doe".to_string()));
|
assert_eq!(
|
||||||
|
format_artists(["John Doe"].to_vec()),
|
||||||
|
Some("John Doe".to_string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn two() {
|
fn two() {
|
||||||
assert_eq!(format_artists(["John Doe", "Jane Doe"].to_vec()), Some("John Doe & Jane Doe".to_string()));
|
assert_eq!(
|
||||||
|
format_artists(["John Doe", "Jane Doe"].to_vec()),
|
||||||
|
Some("John Doe & Jane Doe".to_string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn many() {
|
fn many() {
|
||||||
assert_eq!(format_artists(["John Doe", "Jane Doe", "Chris P. Bacon", "Seymore Clevarge"].to_vec()),
|
assert_eq!(
|
||||||
Some("John Doe, Jane Doe, Chris P. Bacon & Seymore Clevarge".to_string()));
|
format_artists(["John Doe", "Jane Doe", "Chris P. Bacon", "Seymore Clevarge"].to_vec()),
|
||||||
|
Some("John Doe, Jane Doe, Chris P. Bacon & Seymore Clevarge".to_string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,56 +99,76 @@ mod volume_changes {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use pulse::mainloop::standard::Mainloop;
|
use pulse::context::subscribe::{subscription_masks, Facility, Operation};
|
||||||
use pulse::mainloop::standard::IterateResult;
|
|
||||||
use pulse::context::Context;
|
use pulse::context::Context;
|
||||||
use pulse::context::subscribe::{subscription_masks, Operation, Facility};
|
use pulse::mainloop::standard::IterateResult;
|
||||||
|
use pulse::mainloop::standard::Mainloop;
|
||||||
|
|
||||||
pub(super) struct VolumeMonitor {
|
pub(super) struct VolumeMonitor {
|
||||||
mainloop: Arc<Mutex<Mainloop>>,
|
mainloop: Arc<Mutex<Mainloop>>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
context: Arc<Mutex<Context>>
|
context: Arc<Mutex<Context>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VolumeMonitor {
|
impl VolumeMonitor {
|
||||||
pub fn new(config: Arc<Mutex<Config>>, trigger: Arc<Mutex<SystemTime>>, dismissed: Arc<Mutex<AtomicBool>>) -> VolumeMonitor {
|
pub fn new(
|
||||||
let mainloop = Arc::new(Mutex::new
|
config: Arc<Mutex<Config>>,
|
||||||
(Mainloop::new().expect("Failed to create mainloop")));
|
trigger: Arc<Mutex<SystemTime>>,
|
||||||
|
dismissed: Arc<Mutex<AtomicBool>>,
|
||||||
|
) -> VolumeMonitor {
|
||||||
|
let mainloop = Arc::new(Mutex::new(
|
||||||
|
Mainloop::new().expect("Failed to create mainloop"),
|
||||||
|
));
|
||||||
|
|
||||||
let context = Arc::new(Mutex::new(Context::new(
|
let context = Arc::new(Mutex::new(
|
||||||
mainloop.lock().unwrap().deref(), osd::APPNAME
|
Context::new(mainloop.lock().unwrap().deref(), osd::APPNAME)
|
||||||
).expect("Failed to create new context")));
|
.expect("Failed to create new context"),
|
||||||
|
));
|
||||||
|
|
||||||
context.lock().unwrap().connect(config.lock().unwrap().get::<String>("pulseaudio", "server").as_deref(), 0, None)
|
context
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.connect(
|
||||||
|
config
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get::<String>("pulseaudio", "server")
|
||||||
|
.as_deref(),
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
)
|
||||||
.expect("Failed to connect context");
|
.expect("Failed to connect context");
|
||||||
|
|
||||||
|
|
||||||
// Wait for context to be ready
|
// Wait for context to be ready
|
||||||
loop {
|
loop {
|
||||||
match mainloop.lock().unwrap().iterate(false) {
|
match mainloop.lock().unwrap().iterate(false) {
|
||||||
IterateResult::Quit(_) |
|
IterateResult::Quit(_) | IterateResult::Err(_) => {
|
||||||
IterateResult::Err(_) => {
|
|
||||||
panic!("Iterate state was not success, quitting...");
|
panic!("Iterate state was not success, quitting...");
|
||||||
},
|
}
|
||||||
IterateResult::Success(_) => {},
|
IterateResult::Success(_) => {}
|
||||||
}
|
}
|
||||||
match context.lock().unwrap().get_state() {
|
match context.lock().unwrap().get_state() {
|
||||||
pulse::context::State::Ready => { break; },
|
pulse::context::State::Ready => {
|
||||||
pulse::context::State::Failed |
|
break;
|
||||||
pulse::context::State::Unconnected |
|
}
|
||||||
pulse::context::State::Terminated => {
|
pulse::context::State::Failed
|
||||||
|
| pulse::context::State::Unconnected
|
||||||
|
| pulse::context::State::Terminated => {
|
||||||
panic!("Context state failed/terminated, quitting...");
|
panic!("Context state failed/terminated, quitting...");
|
||||||
},
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.lock().unwrap().subscribe(subscription_masks::SINK, |success| {
|
context
|
||||||
if ! success {
|
.lock()
|
||||||
eprintln!("failed to subscribe to events");
|
.unwrap()
|
||||||
return;
|
.subscribe(subscription_masks::SINK, |success| {
|
||||||
}
|
if !success {
|
||||||
});
|
eprintln!("failed to subscribe to events");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let subscribe_callback = move |facility, operation, _index| {
|
let subscribe_callback = move |facility, operation, _index| {
|
||||||
if facility == Some(Facility::Sink) && operation == Some(Operation::Changed) {
|
if facility == Some(Facility::Sink) && operation == Some(Operation::Changed) {
|
||||||
@ -149,17 +177,19 @@ mod volume_changes {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
context.lock().unwrap().set_subscribe_callback(Some(Box::new(subscribe_callback)));
|
context
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.set_subscribe_callback(Some(Box::new(subscribe_callback)));
|
||||||
|
|
||||||
VolumeMonitor { mainloop, context }
|
VolumeMonitor { mainloop, context }
|
||||||
}
|
}
|
||||||
pub fn tick(& self) {
|
pub fn tick(&self) {
|
||||||
match self.mainloop.lock().unwrap().iterate(false) {
|
match self.mainloop.lock().unwrap().iterate(false) {
|
||||||
IterateResult::Quit(_) |
|
IterateResult::Quit(_) | IterateResult::Err(_) => {
|
||||||
IterateResult::Err(_) => {
|
|
||||||
panic!("Iterate state was not success, quitting...");
|
panic!("Iterate state was not success, quitting...");
|
||||||
},
|
}
|
||||||
IterateResult::Success(_) => { },
|
IterateResult::Success(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,15 +205,28 @@ fn main() {
|
|||||||
|
|
||||||
let mut progress_tracker = player.track_progress(100).unwrap();
|
let mut progress_tracker = player.track_progress(100).unwrap();
|
||||||
|
|
||||||
let update_on_volume_change = config.lock().unwrap().get_default("default", "update on volume change", true);
|
let update_on_volume_change =
|
||||||
let timeout = config.lock().unwrap().get_default("default", "notification display time", 5);
|
config
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get_default("default", "update on volume change", true);
|
||||||
|
let timeout = config
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get_default("default", "notification display time", 5);
|
||||||
|
|
||||||
let trigger = Arc::new(Mutex::new(SystemTime::now()));
|
let trigger = Arc::new(Mutex::new(SystemTime::now()));
|
||||||
|
|
||||||
#[cfg(feature = "display_on_volume_changes")]
|
#[cfg(feature = "display_on_volume_changes")]
|
||||||
let vc = if update_on_volume_change {
|
let vc = if update_on_volume_change {
|
||||||
Some(volume_changes::VolumeMonitor::new(config.clone(), trigger.clone(), dismissed.clone()))
|
Some(volume_changes::VolumeMonitor::new(
|
||||||
} else { None };
|
config.clone(),
|
||||||
|
trigger.clone(),
|
||||||
|
dismissed.clone(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
drop(config);
|
drop(config);
|
||||||
|
|
||||||
@ -203,30 +246,49 @@ fn main() {
|
|||||||
dismissed.lock().unwrap().store(false, Ordering::Relaxed);
|
dismissed.lock().unwrap().store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = trigger.lock().unwrap().elapsed().unwrap_or_else(|_| Duration::from_secs(timeout + 1));
|
let elapsed = trigger
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.elapsed()
|
||||||
|
.unwrap_or_else(|_| Duration::from_secs(timeout + 1));
|
||||||
|
|
||||||
if elapsed.as_secs() < timeout && playback_status != PlaybackStatus::Stopped && ! dismissed.lock().unwrap().load(Ordering::Relaxed) {
|
if elapsed.as_secs() < timeout
|
||||||
|
&& playback_status != PlaybackStatus::Stopped
|
||||||
|
&& !dismissed.lock().unwrap().load(Ordering::Relaxed)
|
||||||
|
{
|
||||||
let metadata = progress.metadata();
|
let metadata = progress.metadata();
|
||||||
let artists = metadata.artists().and_then(format_artists).unwrap_or_else(|| "Unknown".to_string());
|
let artists = metadata
|
||||||
|
.artists()
|
||||||
|
.and_then(format_artists)
|
||||||
|
.unwrap_or_else(|| "Unknown".to_string());
|
||||||
let position = progress.position();
|
let position = progress.position();
|
||||||
let length = progress.length().unwrap_or_else(|| Duration::from_secs(100000000));
|
let length = progress
|
||||||
|
.length()
|
||||||
|
.unwrap_or_else(|| Duration::from_secs(100000000));
|
||||||
|
|
||||||
osd.title = Some(format!("{:?}: {} - {}", playback_status, title, artists));
|
osd.title = Some(format!("{:?}: {} - {}", playback_status, title, artists));
|
||||||
let ratio = position.as_secs_f32() / length.as_secs_f32();
|
let ratio = position.as_secs_f32() / length.as_secs_f32();
|
||||||
let text = format!("{} / {}", format_duration(position), format_duration(length));
|
let text = format!(
|
||||||
|
"{} / {}",
|
||||||
|
format_duration(position),
|
||||||
|
format_duration(length)
|
||||||
|
);
|
||||||
osd.contents = OSDContents::Progress(ratio, OSDProgressText::Text(Some(text)));
|
osd.contents = OSDContents::Progress(ratio, OSDProgressText::Text(Some(text)));
|
||||||
osd.timeout = 1;
|
osd.timeout = 1;
|
||||||
osd.icon = match playback_status {
|
osd.icon = match playback_status {
|
||||||
PlaybackStatus::Playing => Some("media-playback-start".to_string()),
|
PlaybackStatus::Playing => Some("media-playback-start".to_string()),
|
||||||
PlaybackStatus::Paused => Some("media-playback-pause".to_string()),
|
PlaybackStatus::Paused => Some("media-playback-pause".to_string()),
|
||||||
_ => None
|
_ => None,
|
||||||
};
|
};
|
||||||
osd.update().unwrap();
|
osd.update().unwrap();
|
||||||
if ! waiting_on_close {
|
if !waiting_on_close {
|
||||||
waiting_on_close = true;
|
waiting_on_close = true;
|
||||||
let dismissed_clone = dismissed.clone();
|
let dismissed_clone = dismissed.clone();
|
||||||
osd.on_close(move || {
|
osd.on_close(move || {
|
||||||
dismissed_clone.lock().unwrap().store(true, Ordering::Relaxed);
|
dismissed_clone
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.store(true, Ordering::Relaxed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -234,11 +296,12 @@ fn main() {
|
|||||||
osd.close()
|
osd.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
old_title = title;
|
old_title = title;
|
||||||
old_playback_status = playback_status;
|
old_playback_status = playback_status;
|
||||||
|
|
||||||
#[cfg(feature = "display_on_volume_changes")]
|
#[cfg(feature = "display_on_volume_changes")]
|
||||||
if let Some(v) = vc.as_ref() { v.tick() };
|
if let Some(v) = vc.as_ref() {
|
||||||
|
v.tick()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,15 @@ extern crate libpulse_binding as pulse;
|
|||||||
|
|
||||||
extern crate simple_osd_common as osd;
|
extern crate simple_osd_common as osd;
|
||||||
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use pulse::mainloop::standard::Mainloop;
|
|
||||||
use pulse::context::Context;
|
|
||||||
use osd::config::Config;
|
use osd::config::Config;
|
||||||
use osd::notify::{OSD, OSDContents, OSDProgressText};
|
use osd::notify::{OSDContents, OSDProgressText, OSD};
|
||||||
|
use pulse::context::Context;
|
||||||
|
use pulse::mainloop::standard::Mainloop;
|
||||||
|
|
||||||
use pulse::context::subscribe::{subscription_masks, Operation, Facility};
|
use pulse::context::subscribe::{subscription_masks, Facility, Operation};
|
||||||
|
|
||||||
use pulse::callbacks::ListResult;
|
use pulse::callbacks::ListResult;
|
||||||
use pulse::context::introspect::SinkInfo;
|
use pulse::context::introspect::SinkInfo;
|
||||||
@ -23,24 +23,29 @@ fn main() {
|
|||||||
|
|
||||||
let mut config = Config::new("pulseaudio");
|
let mut config = Config::new("pulseaudio");
|
||||||
|
|
||||||
let mut context = Context::new(
|
let mut context = Context::new(&mainloop, osd::APPNAME).expect("Failed to create new context");
|
||||||
&mainloop, osd::APPNAME
|
|
||||||
).expect("Failed to create new context");
|
|
||||||
|
|
||||||
context.connect(config.get::<String>("default", "server").as_deref(), 0, None)
|
context
|
||||||
|
.connect(
|
||||||
|
config.get::<String>("default", "server").as_deref(),
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
)
|
||||||
.expect("Failed to connect context");
|
.expect("Failed to connect context");
|
||||||
|
|
||||||
// Wait for context to be ready
|
// Wait for context to be ready
|
||||||
loop {
|
loop {
|
||||||
mainloop.iterate(false);
|
mainloop.iterate(false);
|
||||||
match context.get_state() {
|
match context.get_state() {
|
||||||
pulse::context::State::Ready => { break; },
|
pulse::context::State::Ready => {
|
||||||
pulse::context::State::Failed |
|
break;
|
||||||
pulse::context::State::Unconnected |
|
}
|
||||||
pulse::context::State::Terminated => {
|
pulse::context::State::Failed
|
||||||
|
| pulse::context::State::Unconnected
|
||||||
|
| pulse::context::State::Terminated => {
|
||||||
eprintln!("Context state failed/terminated, quitting...");
|
eprintln!("Context state failed/terminated, quitting...");
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,7 +53,7 @@ fn main() {
|
|||||||
eprintln!("connected");
|
eprintln!("connected");
|
||||||
|
|
||||||
context.subscribe(subscription_masks::SINK, |success| {
|
context.subscribe(subscription_masks::SINK, |success| {
|
||||||
if ! success {
|
if !success {
|
||||||
eprintln!("failed to subscribe to events");
|
eprintln!("failed to subscribe to events");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -56,7 +61,6 @@ fn main() {
|
|||||||
|
|
||||||
let introspector = context.introspect();
|
let introspector = context.introspect();
|
||||||
|
|
||||||
|
|
||||||
let osd = Rc::new(RefCell::new(OSD::new()));
|
let osd = Rc::new(RefCell::new(OSD::new()));
|
||||||
osd.borrow_mut().icon = Some(String::from("multimedia-volume-control"));
|
osd.borrow_mut().icon = Some(String::from("multimedia-volume-control"));
|
||||||
|
|
||||||
@ -66,7 +70,8 @@ fn main() {
|
|||||||
let sink_name = i.description.as_deref().unwrap_or("Unnamed sink");
|
let sink_name = i.description.as_deref().unwrap_or("Unnamed sink");
|
||||||
let muted_message = if i.mute { " [MUTED]" } else { "" };
|
let muted_message = if i.mute { " [MUTED]" } else { "" };
|
||||||
osd.borrow_mut().title = Some(format!("Volume on {}{}", sink_name, muted_message));
|
osd.borrow_mut().title = Some(format!("Volume on {}{}", sink_name, muted_message));
|
||||||
osd.borrow_mut().contents = OSDContents::Progress(volume.0 as f32 / 65536., OSDProgressText::Percentage);
|
osd.borrow_mut().contents =
|
||||||
|
OSDContents::Progress(volume.0 as f32 / 65536., OSDProgressText::Percentage);
|
||||||
osd.borrow_mut().update().unwrap();
|
osd.borrow_mut().update().unwrap();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user