Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7c407a995 | ||
|
|
9233128b7c | ||
|
|
b51fc8a1b0 | ||
|
|
0de3da162b | ||
|
|
510ad34183 |
Generated
+10
@@ -586,6 +586,14 @@ dependencies = [
|
|||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "timerfd"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.11.2"
|
version = "1.11.2"
|
||||||
@@ -723,6 +731,7 @@ dependencies = [
|
|||||||
"serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"timerfd 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wayland-protocols 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wayland-protocols 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@@ -818,6 +827,7 @@ dependencies = [
|
|||||||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||||
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||||
|
"checksum timerfd 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c9a7822e546fa39d0b5ae14a93a33903975b62af6597288aea77f0580a6abbe"
|
||||||
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
||||||
"checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2"
|
"checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2"
|
||||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ lazy_static = "1.4"
|
|||||||
rcalc_lib = { version = "0.9" }
|
rcalc_lib = { version = "0.9" }
|
||||||
rust-ini = "0.13"
|
rust-ini = "0.13"
|
||||||
shlex = "0.1"
|
shlex = "0.1"
|
||||||
|
timerfd = "1.0.0"
|
||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = { version = "1.0", optional = true }
|
serde_json = { version = "1.0", optional = true }
|
||||||
|
|||||||
+11
-1
@@ -58,6 +58,10 @@ pub enum Widget {
|
|||||||
font_size: f32,
|
font_size: f32,
|
||||||
length: u32,
|
length: u32,
|
||||||
},
|
},
|
||||||
|
NetworkManager {
|
||||||
|
font_size: f32,
|
||||||
|
length: u32,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget {
|
impl Widget {
|
||||||
@@ -91,7 +95,7 @@ impl Widget {
|
|||||||
.map(|x| x.unwrap())
|
.map(|x| x.unwrap())
|
||||||
.collect(),
|
.collect(),
|
||||||
)),
|
)),
|
||||||
Widget::Clock { font_size } => Some(widgets::clock::Clock::new(font_size, tx.clone())),
|
Widget::Clock { font_size } => Some(widgets::clock::Clock::new(font_size)),
|
||||||
Widget::Date { font_size } => Some(widgets::date::Date::new(font_size)),
|
Widget::Date { font_size } => Some(widgets::date::Date::new(font_size)),
|
||||||
Widget::Calendar {
|
Widget::Calendar {
|
||||||
font_size,
|
font_size,
|
||||||
@@ -141,6 +145,12 @@ impl Widget {
|
|||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Widget::NetworkManager { font_size, length } => {
|
||||||
|
match widgets::networkmanager::NetworkManager::new(font_size, length, tx.clone()) {
|
||||||
|
Ok(w) => Some(w),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+88
-43
@@ -5,8 +5,10 @@ use std::os::unix::io::AsRawFd;
|
|||||||
use std::os::unix::net::{UnixListener, UnixStream};
|
use std::os::unix::net::{UnixListener, UnixStream};
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
|
use chrono::{Duration, Local};
|
||||||
use nix::poll::{poll, PollFd, PollFlags};
|
use nix::poll::{poll, PollFd, PollFlags};
|
||||||
use os_pipe::pipe;
|
use os_pipe::pipe;
|
||||||
|
use timerfd::{SetTimeFlags, TimerFd, TimerState};
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod buffer;
|
mod buffer;
|
||||||
@@ -24,6 +26,7 @@ use app::{App, OutputMode};
|
|||||||
use cmd::Cmd;
|
use cmd::Cmd;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use configfmt::ConfigFmt;
|
use configfmt::ConfigFmt;
|
||||||
|
use widget::WaitContext;
|
||||||
|
|
||||||
enum Mode {
|
enum Mode {
|
||||||
Start,
|
Start,
|
||||||
@@ -186,50 +189,20 @@ fn main() {
|
|||||||
tx_pipe.write_all(&[0x1]).unwrap();
|
tx_pipe.write_all(&[0x1]).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
let ipc_queue = app.cmd_queue();
|
let mut timer = TimerFd::new().unwrap();
|
||||||
let _ = std::thread::Builder::new()
|
let ev_fd = PollFd::new(app.event_queue().get_connection_fd(), PollFlags::POLLIN);
|
||||||
.name("ipc_server".to_string())
|
let rx_fd = PollFd::new(rx_pipe.as_raw_fd(), PollFlags::POLLIN);
|
||||||
.spawn(move || loop {
|
let tm_fd = PollFd::new(timer.as_raw_fd(), PollFlags::POLLIN);
|
||||||
for stream in listener.incoming() {
|
let ipc_fd = PollFd::new(listener.as_raw_fd(), PollFlags::POLLIN);
|
||||||
match stream {
|
|
||||||
Ok(stream) => {
|
|
||||||
let client_queue = ipc_queue.clone();
|
|
||||||
let mut client_pipe = ipc_pipe.try_clone().unwrap();
|
|
||||||
let _ = std::thread::Builder::new()
|
|
||||||
.name("ipc_client".to_string())
|
|
||||||
.spawn(move || {
|
|
||||||
let r = BufReader::new(stream);
|
|
||||||
for line in r.lines() {
|
|
||||||
match line {
|
|
||||||
Ok(v) => match v.as_str() {
|
|
||||||
"kill" => {
|
|
||||||
client_queue.lock().unwrap().push_back(Cmd::Exit)
|
|
||||||
}
|
|
||||||
"toggle_visible" => client_queue
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push_back(Cmd::ToggleVisible),
|
|
||||||
v => eprintln!("unknown command: {}", v),
|
|
||||||
},
|
|
||||||
Err(_) => return,
|
|
||||||
}
|
|
||||||
client_pipe.write_all(&[0x1]).unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(_) => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut fds = [
|
|
||||||
PollFd::new(app.event_queue().get_connection_fd(), PollFlags::POLLIN),
|
|
||||||
PollFd::new(rx_pipe.as_raw_fd(), PollFlags::POLLIN),
|
|
||||||
];
|
|
||||||
|
|
||||||
app.cmd_queue().lock().unwrap().push_back(Cmd::Draw);
|
app.cmd_queue().lock().unwrap().push_back(Cmd::Draw);
|
||||||
|
|
||||||
let mut visible = !daemon;
|
let mut visible = !daemon;
|
||||||
|
let mut wait_ctx = WaitContext {
|
||||||
|
fds: Vec::new(),
|
||||||
|
target_time: None,
|
||||||
|
};
|
||||||
|
|
||||||
let q = app.cmd_queue();
|
let q = app.cmd_queue();
|
||||||
loop {
|
loop {
|
||||||
let cmd = q.lock().unwrap().pop_front();
|
let cmd = q.lock().unwrap().pop_front();
|
||||||
@@ -287,9 +260,35 @@ fn main() {
|
|||||||
None => {
|
None => {
|
||||||
app.flush_display();
|
app.flush_display();
|
||||||
|
|
||||||
poll(&mut fds, -1).unwrap();
|
wait_ctx.fds.clear();
|
||||||
|
wait_ctx.fds.push(ev_fd);
|
||||||
|
wait_ctx.fds.push(rx_fd);
|
||||||
|
wait_ctx.fds.push(ipc_fd);
|
||||||
|
wait_ctx.target_time = None;
|
||||||
|
|
||||||
if fds[0].revents().unwrap().contains(PollFlags::POLLIN) {
|
app.get_widget().wait(&mut wait_ctx);
|
||||||
|
|
||||||
|
if let Some(target_time) = wait_ctx.target_time {
|
||||||
|
let n = Local::now();
|
||||||
|
let sleep = if target_time > n {
|
||||||
|
target_time - n
|
||||||
|
} else {
|
||||||
|
Duration::seconds(0)
|
||||||
|
};
|
||||||
|
timer.set_state(
|
||||||
|
TimerState::Oneshot(sleep.to_std().unwrap()),
|
||||||
|
SetTimeFlags::Default,
|
||||||
|
);
|
||||||
|
wait_ctx.fds.push(tm_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
poll(&mut wait_ctx.fds, -1).unwrap();
|
||||||
|
|
||||||
|
if wait_ctx.fds[0]
|
||||||
|
.revents()
|
||||||
|
.unwrap()
|
||||||
|
.contains(PollFlags::POLLIN)
|
||||||
|
{
|
||||||
if let Some(guard) = app.event_queue().prepare_read() {
|
if let Some(guard) = app.event_queue().prepare_read() {
|
||||||
if let Err(e) = guard.read_events() {
|
if let Err(e) = guard.read_events() {
|
||||||
if e.kind() != ::std::io::ErrorKind::WouldBlock {
|
if e.kind() != ::std::io::ErrorKind::WouldBlock {
|
||||||
@@ -306,10 +305,56 @@ fn main() {
|
|||||||
.expect("Failed to dispatch all messages.");
|
.expect("Failed to dispatch all messages.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if fds[1].revents().unwrap().contains(PollFlags::POLLIN) {
|
if wait_ctx.fds[1]
|
||||||
|
.revents()
|
||||||
|
.unwrap()
|
||||||
|
.contains(PollFlags::POLLIN)
|
||||||
|
{
|
||||||
let mut v = [0x00];
|
let mut v = [0x00];
|
||||||
rx_pipe.read_exact(&mut v).unwrap();
|
rx_pipe.read_exact(&mut v).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if wait_ctx.fds[2]
|
||||||
|
.revents()
|
||||||
|
.unwrap()
|
||||||
|
.contains(PollFlags::POLLIN)
|
||||||
|
{
|
||||||
|
if let Ok((stream, _)) = listener.accept() {
|
||||||
|
let client_queue = q.clone();
|
||||||
|
let mut client_pipe = ipc_pipe.try_clone().unwrap();
|
||||||
|
let _ = std::thread::Builder::new()
|
||||||
|
.name("ipc_client".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
let r = BufReader::new(stream);
|
||||||
|
for line in r.lines() {
|
||||||
|
match line {
|
||||||
|
Ok(v) => match v.as_str() {
|
||||||
|
"kill" => {
|
||||||
|
client_queue.lock().unwrap().push_back(Cmd::Exit)
|
||||||
|
}
|
||||||
|
"toggle_visible" => client_queue
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push_back(Cmd::ToggleVisible),
|
||||||
|
v => eprintln!("unknown command: {}", v),
|
||||||
|
},
|
||||||
|
Err(_) => return,
|
||||||
|
}
|
||||||
|
client_pipe.write_all(&[0x1]).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if wait_ctx.target_time.is_some()
|
||||||
|
&& wait_ctx.fds[wait_ctx.fds.len() - 1]
|
||||||
|
.revents()
|
||||||
|
.unwrap()
|
||||||
|
.contains(PollFlags::POLLIN)
|
||||||
|
{
|
||||||
|
timer.read();
|
||||||
|
q.lock().unwrap().push_back(Cmd::Draw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use chrono::{DateTime, Local};
|
use chrono::{DateTime, Local};
|
||||||
|
use nix::poll::PollFd;
|
||||||
pub use smithay_client_toolkit::keyboard::{KeyState, ModifiersState};
|
pub use smithay_client_toolkit::keyboard::{KeyState, ModifiersState};
|
||||||
|
|
||||||
pub struct DrawContext<'a> {
|
pub struct DrawContext<'a> {
|
||||||
@@ -29,7 +30,25 @@ impl DrawReport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct WaitContext {
|
||||||
|
pub fds: Vec<PollFd>,
|
||||||
|
pub target_time: Option<DateTime<Local>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WaitContext {
|
||||||
|
pub fn set_time(&mut self, new_time: DateTime<Local>) {
|
||||||
|
if let Some(ot) = self.target_time {
|
||||||
|
if new_time < ot {
|
||||||
|
self.target_time = Some(new_time);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.target_time = Some(new_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Widget {
|
pub trait Widget {
|
||||||
|
fn wait(&mut self, ctx: &mut WaitContext);
|
||||||
fn enter(&mut self);
|
fn enter(&mut self);
|
||||||
fn leave(&mut self);
|
fn leave(&mut self);
|
||||||
fn size(&self) -> (u32, u32);
|
fn size(&self) -> (u32, u32);
|
||||||
@@ -62,6 +81,11 @@ impl VerticalLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for VerticalLayout {
|
impl Widget for VerticalLayout {
|
||||||
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||||
|
for child in &mut self.children {
|
||||||
|
child.wait(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
fn enter(&mut self) {
|
fn enter(&mut self) {
|
||||||
for child in &mut self.children {
|
for child in &mut self.children {
|
||||||
child.enter();
|
child.enter();
|
||||||
@@ -168,6 +192,11 @@ impl HorizontalLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for HorizontalLayout {
|
impl Widget for HorizontalLayout {
|
||||||
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||||
|
for child in &mut self.children {
|
||||||
|
child.wait(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
fn enter(&mut self) {
|
fn enter(&mut self) {
|
||||||
for child in &mut self.children {
|
for child in &mut self.children {
|
||||||
child.enter();
|
child.enter();
|
||||||
@@ -280,6 +309,9 @@ impl Margin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Margin {
|
impl Widget for Margin {
|
||||||
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||||
|
self.child.wait(ctx)
|
||||||
|
}
|
||||||
fn enter(&mut self) {
|
fn enter(&mut self) {
|
||||||
self.child.enter()
|
self.child.enter()
|
||||||
}
|
}
|
||||||
@@ -354,6 +386,9 @@ impl Fixed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Fixed {
|
impl Widget for Fixed {
|
||||||
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||||
|
self.child.wait(ctx)
|
||||||
|
}
|
||||||
fn enter(&mut self) {
|
fn enter(&mut self) {
|
||||||
self.child.enter()
|
self.child.enter()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
|
use crate::widget::WaitContext;
|
||||||
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||||
|
|
||||||
use alsa::mixer::{Mixer, Selem, SelemChannelId, SelemId};
|
use alsa::mixer::{Mixer, Selem, SelemChannelId, SelemId};
|
||||||
@@ -83,6 +84,7 @@ impl Alsa {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BarWidgetImpl for Alsa {
|
impl BarWidgetImpl for Alsa {
|
||||||
|
fn wait(&mut self, _: &mut WaitContext) {}
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"volume"
|
"volume"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::cmd::Cmd;
|
use crate::cmd::Cmd;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
|
use crate::widget::WaitContext;
|
||||||
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@@ -502,6 +503,7 @@ impl PulseAudio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BarWidgetImpl for PulseAudio {
|
impl BarWidgetImpl for PulseAudio {
|
||||||
|
fn wait(&mut self, _: &mut WaitContext) {}
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"volume"
|
"volume"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
|
use crate::widget::WaitContext;
|
||||||
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||||
|
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
@@ -93,6 +94,7 @@ impl Backlight {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BarWidgetImpl for Backlight {
|
impl BarWidgetImpl for Backlight {
|
||||||
|
fn wait(&mut self, _: &mut WaitContext) {}
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"backlight"
|
"backlight"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR};
|
use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR};
|
||||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
pub trait BarWidgetImpl {
|
pub trait BarWidgetImpl {
|
||||||
|
fn wait(&mut self, ctx: &mut WaitContext);
|
||||||
fn name(&self) -> &str;
|
fn name(&self) -> &str;
|
||||||
fn value(&self) -> f32;
|
fn value(&self) -> f32;
|
||||||
fn color(&self) -> Color;
|
fn color(&self) -> Color;
|
||||||
@@ -33,9 +34,9 @@ impl BarWidget {
|
|||||||
Box::new(BarWidget {
|
Box::new(BarWidget {
|
||||||
bar_impl: w,
|
bar_impl: w,
|
||||||
dirty: Arc::new(Mutex::new(true)),
|
dirty: Arc::new(Mutex::new(true)),
|
||||||
font: font,
|
font,
|
||||||
font_size: font_size as u32,
|
font_size: font_size as u32,
|
||||||
length: length,
|
length,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,21 +53,23 @@ impl BarWidget {
|
|||||||
|
|
||||||
Ok(Box::new(BarWidget {
|
Ok(Box::new(BarWidget {
|
||||||
bar_impl: im,
|
bar_impl: im,
|
||||||
dirty: dirty,
|
dirty,
|
||||||
font: font,
|
font,
|
||||||
font_size: font_size as u32,
|
font_size: font_size as u32,
|
||||||
length: length,
|
length,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for BarWidget {
|
impl Widget for BarWidget {
|
||||||
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||||
|
self.bar_impl.wait(ctx);
|
||||||
|
}
|
||||||
fn enter(&mut self) {}
|
fn enter(&mut self) {}
|
||||||
fn leave(&mut self) {}
|
fn leave(&mut self) {}
|
||||||
fn size(&self) -> (u32, u32) {
|
fn size(&self) -> (u32, u32) {
|
||||||
(self.length, self.font_size)
|
(self.length, self.font_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &mut DrawContext,
|
ctx: &mut DrawContext,
|
||||||
|
|||||||
+76
-53
@@ -1,10 +1,12 @@
|
|||||||
use crate::cmd::Cmd;
|
use crate::cmd::Cmd;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
|
use crate::widget::WaitContext;
|
||||||
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
|
||||||
|
use nix::poll::{PollFd, PollFlags};
|
||||||
|
|
||||||
use dbus;
|
use dbus;
|
||||||
|
|
||||||
@@ -28,21 +30,34 @@ fn get_upower_property(
|
|||||||
.append2(
|
.append2(
|
||||||
dbus::MessageItem::Str("org.freedesktop.UPower.Device".to_string()),
|
dbus::MessageItem::Str("org.freedesktop.UPower.Device".to_string()),
|
||||||
dbus::MessageItem::Str(property.to_string()),
|
dbus::MessageItem::Str(property.to_string()),
|
||||||
);;
|
);
|
||||||
|
|
||||||
con.send_with_reply_and_block(msg, 1000).map_err(|_| {
|
con.send_with_reply_and_block(msg, 1000).map_err(|_| {
|
||||||
::std::io::Error::new(::std::io::ErrorKind::Other, "could not send dbus message")
|
::std::io::Error::new(::std::io::ErrorKind::Other, "could not send dbus message")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UpowerBattery {
|
// The widget is created on a different thread than where it will be used, so
|
||||||
device_path: String,
|
// we need it to be Send. However, dbus::Connection has a void*, so it's not
|
||||||
inner: Arc<Mutex<UpowerBatteryInner>>,
|
// auto-derived. So, we make a small wrapper where we make it Send.
|
||||||
|
struct DbusConnection(dbus::Connection);
|
||||||
|
|
||||||
|
impl std::convert::AsRef<dbus::Connection> for DbusConnection {
|
||||||
|
fn as_ref(&self) -> &dbus::Connection {
|
||||||
|
return &self.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UpowerBatteryInner {
|
unsafe impl Send for DbusConnection {}
|
||||||
|
|
||||||
|
pub struct UpowerBattery {
|
||||||
|
device_path: String,
|
||||||
|
sender: Sender<Cmd>,
|
||||||
|
dirty: Arc<Mutex<bool>>,
|
||||||
state: UpowerBatteryState,
|
state: UpowerBatteryState,
|
||||||
capacity: f64,
|
capacity: f64,
|
||||||
|
con: DbusConnection,
|
||||||
|
watch: dbus::Watch,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum UpowerBatteryState {
|
enum UpowerBatteryState {
|
||||||
@@ -55,12 +70,29 @@ enum UpowerBatteryState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl UpowerBattery {
|
impl UpowerBattery {
|
||||||
pub fn from_device(device: &str) -> Result<Self, ::std::io::Error> {
|
pub fn from_device(
|
||||||
let device_path = format!("/org/freedesktop/UPower/devices/battery_{}", device);
|
dirty: Arc<Mutex<bool>>,
|
||||||
|
sender: Sender<Cmd>,
|
||||||
|
device: &str,
|
||||||
|
) -> Result<Self, ::std::io::Error> {
|
||||||
let con = dbus::Connection::get_private(dbus::BusType::System).map_err(|_| {
|
let con = dbus::Connection::get_private(dbus::BusType::System).map_err(|_| {
|
||||||
::std::io::Error::new(::std::io::ErrorKind::Other, "could not get dbus connection")
|
::std::io::Error::new(::std::io::ErrorKind::Other, "unable to open dbus")
|
||||||
})?;
|
})?;
|
||||||
|
let device_path = format!("/org/freedesktop/UPower/devices/battery_{}", device);
|
||||||
|
|
||||||
|
let rule = format!(
|
||||||
|
"type='signal',\
|
||||||
|
path='{}',\
|
||||||
|
interface='org.freedesktop.DBus.Properties',\
|
||||||
|
member='PropertiesChanged'",
|
||||||
|
&device_path
|
||||||
|
);
|
||||||
|
con.add_match(&rule).map_err(|_| {
|
||||||
|
::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"unable to add match rule to dbus connection",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
let upower_type: dbus::arg::Variant<u32> =
|
let upower_type: dbus::arg::Variant<u32> =
|
||||||
match get_upower_property(&con, &device_path, "Type")?.get1() {
|
match get_upower_property(&con, &device_path, "Type")?.get1() {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
@@ -111,12 +143,22 @@ impl UpowerBattery {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let fds = con.watch_fds();
|
||||||
|
if fds.len() != 1 {
|
||||||
|
return Err(::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"expected 1 watch fd from dbus",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(UpowerBattery {
|
Ok(UpowerBattery {
|
||||||
device_path,
|
device_path,
|
||||||
inner: Arc::new(Mutex::new(UpowerBatteryInner {
|
con: DbusConnection(con),
|
||||||
|
dirty,
|
||||||
|
sender,
|
||||||
capacity: capacity,
|
capacity: capacity,
|
||||||
state: state,
|
state: state,
|
||||||
})),
|
watch: fds[0],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,36 +168,25 @@ impl UpowerBattery {
|
|||||||
sender: Sender<Cmd>,
|
sender: Sender<Cmd>,
|
||||||
) -> Result<Box<BarWidget>, ::std::io::Error> {
|
) -> Result<Box<BarWidget>, ::std::io::Error> {
|
||||||
BarWidget::new(font_size, length, move |dirty| {
|
BarWidget::new(font_size, length, move |dirty| {
|
||||||
let d = UpowerBattery::from_device("BAT0")?;
|
let d = UpowerBattery::from_device(dirty, sender, "BAT0")?;
|
||||||
let path = d.device_path.clone();
|
Ok(Box::new(d))
|
||||||
let inner = d.inner.clone();
|
})
|
||||||
let _ = thread::Builder::new()
|
}
|
||||||
.name("battery_monitor".to_string())
|
}
|
||||||
.spawn(move || {
|
|
||||||
let con = dbus::Connection::get_private(dbus::BusType::System)
|
|
||||||
.expect("Failed to establish D-Bus connection.");
|
|
||||||
let rule = format!(
|
|
||||||
"type='signal',\
|
|
||||||
path='{}',\
|
|
||||||
interface='org.freedesktop.DBus.Properties',\
|
|
||||||
member='PropertiesChanged'",
|
|
||||||
path
|
|
||||||
);
|
|
||||||
|
|
||||||
// First we're going to get an (irrelevant) NameAcquired event.
|
impl BarWidgetImpl for UpowerBattery {
|
||||||
con.incoming(10_000).next();
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||||
|
for _ in self
|
||||||
con.add_match(&rule)
|
.con
|
||||||
.expect("Failed to add D-Bus match rule.");
|
.as_ref()
|
||||||
|
.watch_handle(self.watch.fd(), dbus::WatchEvent::Readable as u32)
|
||||||
loop {
|
{
|
||||||
if con.incoming(10_000).next().is_some() {
|
let capacity = get_upower_property(self.con.as_ref(), &self.device_path, "Percentage")
|
||||||
let capacity = get_upower_property(&con, &path, "Percentage")
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get1::<dbus::arg::Variant<f64>>()
|
.get1::<dbus::arg::Variant<f64>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0;
|
.0;
|
||||||
let state = match get_upower_property(&con, &path, "State")
|
let state = match get_upower_property(self.con.as_ref(), &self.device_path, "State")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get1::<dbus::arg::Variant<u32>>()
|
.get1::<dbus::arg::Variant<u32>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -169,33 +200,25 @@ impl UpowerBattery {
|
|||||||
6 => UpowerBatteryState::Discharging,
|
6 => UpowerBatteryState::Discharging,
|
||||||
_ => UpowerBatteryState::Unknown,
|
_ => UpowerBatteryState::Unknown,
|
||||||
};
|
};
|
||||||
let mut inner = inner.lock().unwrap();
|
self.state = state;
|
||||||
inner.state = state;
|
self.capacity = capacity;
|
||||||
inner.capacity = capacity;
|
*self.dirty.lock().unwrap() = true;
|
||||||
*dirty.lock().unwrap() = true;
|
self.sender.send(Cmd::Draw).unwrap();
|
||||||
sender.send(Cmd::Draw).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Box::new(d))
|
ctx.fds
|
||||||
})
|
.push(PollFd::new(self.watch.fd(), PollFlags::POLLIN));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl BarWidgetImpl for UpowerBattery {
|
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"battery"
|
"battery"
|
||||||
}
|
}
|
||||||
fn value(&self) -> f32 {
|
fn value(&self) -> f32 {
|
||||||
let inner = self.inner.lock().unwrap();
|
(self.capacity as f32) / 100.0
|
||||||
(inner.capacity as f32) / 100.0
|
|
||||||
}
|
}
|
||||||
fn color(&self) -> Color {
|
fn color(&self) -> Color {
|
||||||
let inner = self.inner.lock().unwrap();
|
match self.state {
|
||||||
match inner.state {
|
|
||||||
UpowerBatteryState::Discharging | UpowerBatteryState::Unknown => {
|
UpowerBatteryState::Discharging | UpowerBatteryState::Unknown => {
|
||||||
if inner.capacity > 10.0 {
|
if self.capacity > 10.0 {
|
||||||
Color::new(1.0, 1.0, 1.0, 1.0)
|
Color::new(1.0, 1.0, 1.0, 1.0)
|
||||||
} else {
|
} else {
|
||||||
Color::new(1.0, 0.5, 0.0, 1.0)
|
Color::new(1.0, 0.5, 0.0, 1.0)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::draw::{Font, DEJAVUSANS_MONO, ROBOTO_REGULAR};
|
use crate::draw::{Font, DEJAVUSANS_MONO, ROBOTO_REGULAR};
|
||||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||||
|
|
||||||
use chrono::{Date, Datelike, Local};
|
use chrono::{Date, Datelike, Local};
|
||||||
|
|
||||||
@@ -177,6 +177,7 @@ impl Calendar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Calendar {
|
impl Widget for Calendar {
|
||||||
|
fn wait(&mut self, _: &mut WaitContext) {}
|
||||||
fn enter(&mut self) {}
|
fn enter(&mut self) {}
|
||||||
fn leave(&mut self) {}
|
fn leave(&mut self) {}
|
||||||
|
|
||||||
|
|||||||
+10
-21
@@ -1,9 +1,6 @@
|
|||||||
use crate::cmd::Cmd;
|
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::draw::{Font, ROBOTO_REGULAR};
|
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
|
||||||
|
|
||||||
use chrono::{DateTime, Datelike, Duration, Local, Timelike};
|
use chrono::{DateTime, Datelike, Duration, Local, Timelike};
|
||||||
|
|
||||||
@@ -14,23 +11,7 @@ pub struct Clock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Clock {
|
impl Clock {
|
||||||
pub fn new(size: f32, ch: Sender<Cmd>) -> Box<Clock> {
|
pub fn new(size: f32) -> Box<Clock> {
|
||||||
let _ = std::thread::Builder::new()
|
|
||||||
.name("clock_ticker".to_string())
|
|
||||||
.spawn(move || loop {
|
|
||||||
let n = Local::now();
|
|
||||||
let target = (n + Duration::seconds(60))
|
|
||||||
.with_second(0)
|
|
||||||
.unwrap()
|
|
||||||
.with_nanosecond(0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let d = target - n;
|
|
||||||
|
|
||||||
std::thread::sleep(d.to_std().unwrap());
|
|
||||||
ch.send(Cmd::Draw).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut clock_cache = Font::new(&ROBOTO_REGULAR, size);
|
let mut clock_cache = Font::new(&ROBOTO_REGULAR, size);
|
||||||
clock_cache.add_str_to_cache("0123456789:");
|
clock_cache.add_str_to_cache("0123456789:");
|
||||||
|
|
||||||
@@ -45,6 +26,14 @@ impl Clock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Clock {
|
impl Widget for Clock {
|
||||||
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||||
|
let target = (self.cur_time + Duration::seconds(60))
|
||||||
|
.with_second(0)
|
||||||
|
.unwrap()
|
||||||
|
.with_nanosecond(0)
|
||||||
|
.unwrap();
|
||||||
|
ctx.set_time(target);
|
||||||
|
}
|
||||||
fn enter(&mut self) {}
|
fn enter(&mut self) {}
|
||||||
fn leave(&mut self) {}
|
fn leave(&mut self) {}
|
||||||
fn size(&self) -> (u32, u32) {
|
fn size(&self) -> (u32, u32) {
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::draw::{Font, ROBOTO_REGULAR};
|
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||||
|
|
||||||
use chrono::{DateTime, Datelike, Local};
|
use chrono::{DateTime, Datelike, Local};
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ impl Date {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Date {
|
impl Widget for Date {
|
||||||
|
fn wait(&mut self, _: &mut WaitContext) {}
|
||||||
fn enter(&mut self) {}
|
fn enter(&mut self) {}
|
||||||
fn leave(&mut self) {}
|
fn leave(&mut self) {}
|
||||||
fn size(&self) -> (u32, u32) {
|
fn size(&self) -> (u32, u32) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::cmd::Cmd;
|
|||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::desktop::{load_desktop_files, Desktop};
|
use crate::desktop::{load_desktop_files, Desktop};
|
||||||
use crate::draw::{Font, ROBOTO_REGULAR};
|
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||||
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@@ -192,6 +192,7 @@ fn wlcopy(s: &str) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Launcher {
|
impl Widget for Launcher {
|
||||||
|
fn wait(&mut self, _: &mut WaitContext) {}
|
||||||
fn enter(&mut self) {}
|
fn enter(&mut self) {}
|
||||||
fn leave(&mut self) {
|
fn leave(&mut self) {
|
||||||
self.input = "".to_string();
|
self.input = "".to_string();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ pub mod calendar;
|
|||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod date;
|
pub mod date;
|
||||||
pub mod launcher;
|
pub mod launcher;
|
||||||
|
pub mod networkmanager;
|
||||||
|
|
||||||
#[cfg(any(feature = "alsa-widget", feature = "pulseaudio-widget"))]
|
#[cfg(any(feature = "alsa-widget", feature = "pulseaudio-widget"))]
|
||||||
pub mod audio;
|
pub mod audio;
|
||||||
|
|||||||
@@ -0,0 +1,516 @@
|
|||||||
|
use crate::color::Color;
|
||||||
|
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||||
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, WaitContext, Widget};
|
||||||
|
use crate::cmd::Cmd;
|
||||||
|
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use nix::poll::{PollFd, PollFlags};
|
||||||
|
|
||||||
|
use dbus;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum NetworkState {
|
||||||
|
Unknown = 0,
|
||||||
|
Asleep = 10,
|
||||||
|
Disconnected = 20,
|
||||||
|
Disconnecting = 30,
|
||||||
|
Connecting = 40,
|
||||||
|
ConnectedLocal = 50,
|
||||||
|
ConnectedSite = 60,
|
||||||
|
ConnectedGlobal = 70,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for NetworkState {
|
||||||
|
fn from(id: u32) -> Self {
|
||||||
|
match id {
|
||||||
|
// https://developer.gnome.org/NetworkManager/unstable/nm-dbus-types.html#NMState
|
||||||
|
10 => NetworkState::Asleep,
|
||||||
|
20 => NetworkState::Disconnected,
|
||||||
|
30 => NetworkState::Disconnecting,
|
||||||
|
40 => NetworkState::Connecting,
|
||||||
|
50 => NetworkState::ConnectedLocal,
|
||||||
|
60 => NetworkState::ConnectedSite,
|
||||||
|
70 => NetworkState::ConnectedGlobal,
|
||||||
|
_ => NetworkState::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ActiveConnectionState {
|
||||||
|
Unknown,
|
||||||
|
Activating,
|
||||||
|
Activated,
|
||||||
|
Deactivating,
|
||||||
|
Deactivated,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for ActiveConnectionState {
|
||||||
|
fn from(id: u32) -> Self {
|
||||||
|
match id {
|
||||||
|
// https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMActiveConnectionState
|
||||||
|
1 => ActiveConnectionState::Activating,
|
||||||
|
2 => ActiveConnectionState::Activated,
|
||||||
|
3 => ActiveConnectionState::Deactivating,
|
||||||
|
4 => ActiveConnectionState::Deactivated,
|
||||||
|
_ => ActiveConnectionState::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum DeviceType {
|
||||||
|
Unknown,
|
||||||
|
Ethernet,
|
||||||
|
Wifi,
|
||||||
|
Modem,
|
||||||
|
Bridge,
|
||||||
|
TUN,
|
||||||
|
Wireguard,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for DeviceType {
|
||||||
|
fn from(id: u32) -> Self {
|
||||||
|
match id {
|
||||||
|
// https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMDeviceType
|
||||||
|
1 => DeviceType::Ethernet,
|
||||||
|
2 => DeviceType::Wifi,
|
||||||
|
8 => DeviceType::Modem,
|
||||||
|
13 => DeviceType::Bridge,
|
||||||
|
16 => DeviceType::TUN,
|
||||||
|
29 => DeviceType::Wireguard,
|
||||||
|
_ => DeviceType::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Ipv4Address {
|
||||||
|
address: Ipv4Addr,
|
||||||
|
prefix: u32,
|
||||||
|
gateway: Ipv4Addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ByteOrderSwap {
|
||||||
|
fn swap(&self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ByteOrderSwap for u32 {
|
||||||
|
fn swap(&self) -> u32 {
|
||||||
|
((self & 0x000000FF) << 24) | ((self & 0x0000FF00) << 8) | ((self & 0x00FF0000) >> 8) | ((self & 0xFF000000) >> 24)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<dbus::arg::Array<'a, u32, dbus::arg::Iter<'a>>> for Ipv4Address {
|
||||||
|
fn from(s: dbus::arg::Array<'a, u32, dbus::arg::Iter<'a>>) -> Ipv4Address {
|
||||||
|
let mut i = s.into_iter();
|
||||||
|
Ipv4Address {
|
||||||
|
address: Ipv4Addr::from(i.next().unwrap().swap()),
|
||||||
|
prefix: i.next().unwrap(),
|
||||||
|
gateway: Ipv4Addr::from(i.next().unwrap().swap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Ipv4Address {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}/{}", self.address, self.prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_nm_property<'a>(
|
||||||
|
con: &dbus::Connection,
|
||||||
|
obj: &str,
|
||||||
|
path: &dbus::Path<'a>,
|
||||||
|
property: &str,
|
||||||
|
) -> Result<dbus::Message, ::std::io::Error> {
|
||||||
|
let msg = dbus::Message::new_method_call(
|
||||||
|
obj,
|
||||||
|
path,
|
||||||
|
"org.freedesktop.DBus.Properties",
|
||||||
|
"Get",
|
||||||
|
)
|
||||||
|
.map_err(|_| {
|
||||||
|
::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not send make dbus method call",
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.append2(
|
||||||
|
dbus::MessageItem::Str("org.freedesktop.NetworkManager".to_string()),
|
||||||
|
dbus::MessageItem::Str(property.to_string()),
|
||||||
|
);
|
||||||
|
|
||||||
|
con.send_with_reply_and_block(msg, 1000).map_err(|_| {
|
||||||
|
::std::io::Error::new(::std::io::ErrorKind::Other, "could not send dbus message")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(con: &dbus::Connection) -> Result<NetworkState, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager", &"/org/freedesktop/NetworkManager".into(), "State").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve state",
|
||||||
|
))?;
|
||||||
|
let state: dbus::arg::Variant<u32> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(NetworkState::from(state.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn primary_connection(con: &dbus::Connection) -> Result<NmConnection, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager", &"/org/freedesktop/NetworkManager".into(), "PrimaryConnection").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not primary connection",
|
||||||
|
))?;
|
||||||
|
let primary_connection: dbus::arg::Variant<dbus::Path> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
if let Ok(conn) = primary_connection.0.as_cstr().to_str() {
|
||||||
|
if conn == "/" {
|
||||||
|
return Err(::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"no primary connection",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(NmConnection { path: primary_connection.0.clone() })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active_connections(con: &dbus::Connection) -> Result<Vec<NmConnection>, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager", &"/org/freedesktop/NetworkManager".into(), "ActiveConnections").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve active connections",
|
||||||
|
))?;
|
||||||
|
let active_connections: dbus::arg::Variant<dbus::arg::Array<dbus::Path, dbus::arg::Iter>> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(active_connections.0.into_iter().map(|x| NmConnection { path: x }).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct NmConnection<'a> {
|
||||||
|
path: dbus::Path<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NmConnection<'a> {
|
||||||
|
fn state(&self, con: &dbus::Connection) -> Result<ActiveConnectionState, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.Connection.Active", &self.path, "State").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve connection state",
|
||||||
|
))?;
|
||||||
|
let state: dbus::arg::Variant<u32> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(ActiveConnectionState::from(state.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self, con: &dbus::Connection) -> Result<String, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.Connection.Active", &self.path, "Id").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve connection ID",
|
||||||
|
))?;
|
||||||
|
let id: dbus::arg::Variant<String> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(id.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn devices(&self, con: &dbus::Connection) -> Result<Vec<NmDevice>, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.Connection.Active", &self.path, "Devices").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve connection devices",
|
||||||
|
))?;
|
||||||
|
let devices: dbus::arg::Variant<dbus::arg::Array<dbus::Path, dbus::arg::Iter>> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(devices.0.into_iter().map(|x| NmDevice { path: x }).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct NmDevice<'a> {
|
||||||
|
path: dbus::Path<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NmDevice<'a> {
|
||||||
|
fn device_type(&self, con: &dbus::Connection) -> Result<DeviceType, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.Device", &self.path, "DeviceType").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve device type",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let device_type: dbus::arg::Variant<u32> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(DeviceType::from(device_type.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ip4config(&self, con: &dbus::Connection) -> Result<NmIp4Config, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.Device", &self.path, "Ip4Config").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve ip4config",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let ip4config: dbus::arg::Variant<dbus::Path> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(NmIp4Config { path: ip4config.0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active_access_point(&self, con: &dbus::Connection) -> Result<NmAccessPoint, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.Device.Wireless", &self.path, "ActiveAccessPoint").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve device type",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let active_ap: dbus::arg::Variant<dbus::Path> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(NmAccessPoint { path: active_ap.0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct NmAccessPoint<'a> {
|
||||||
|
path: dbus::Path<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NmAccessPoint<'a> {
|
||||||
|
fn ssid(&self, con: &dbus::Connection) -> Result<String, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.AccessPoint", &self.path, "Ssid").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve ssid",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let ssid: dbus::arg::Variant<dbus::arg::Array<u8, dbus::arg::Iter>> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(std::str::from_utf8(&ssid.0.into_iter().collect::<Vec<u8>>())
|
||||||
|
.map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not parse ssid",
|
||||||
|
))?
|
||||||
|
.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strength(&self, con: &dbus::Connection) -> Result<u8, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.AccessPoint", &self.path, "Strength").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve access point strength",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let strength: dbus::arg::Variant<u8> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(strength.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frequency(&self, con: &dbus::Connection) -> Result<u32, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.AccessPoint", &self.path, "Frequency").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve access point frequency",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let frequency: dbus::arg::Variant<u32> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(frequency.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct NmIp4Config<'a> {
|
||||||
|
path: dbus::Path<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NmIp4Config<'a> {
|
||||||
|
fn addresses(&self, con: &dbus::Connection) -> Result<Vec<Ipv4Address>, ::std::io::Error> {
|
||||||
|
let m = get_nm_property(con, "org.freedesktop.NetworkManager.IP4Config", &self.path, "Addresses").map_err(|_| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not retrieve addresses",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let addresses: dbus::arg::Variant<dbus::arg::Array<dbus::arg::Array<u32, dbus::arg::Iter>, dbus::arg::Iter>> = m.get1().ok_or_else(|| ::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"could not read property",
|
||||||
|
))?;
|
||||||
|
Ok(addresses.0.into_iter().map(|addr| Ipv4Address::from(addr)).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The widget is created on a different thread than where it will be used, so
|
||||||
|
// we need it to be Send. However, dbus::Connection has a void*, so it's not
|
||||||
|
// auto-derived. So, we make a small wrapper where we make it Send.
|
||||||
|
struct DbusConnection(dbus::Connection);
|
||||||
|
|
||||||
|
impl std::convert::AsRef<dbus::Connection> for DbusConnection {
|
||||||
|
fn as_ref(&self) -> &dbus::Connection {
|
||||||
|
return &self.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for DbusConnection {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Device {
|
||||||
|
// name: String,
|
||||||
|
t: DeviceType,
|
||||||
|
ips: Vec<Ipv4Address>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Connection {
|
||||||
|
devices: Vec<Device>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NetworkManager {
|
||||||
|
con: DbusConnection,
|
||||||
|
watch: dbus::Watch,
|
||||||
|
|
||||||
|
font: RefCell<Font>,
|
||||||
|
font_size: u32,
|
||||||
|
state: NetworkState,
|
||||||
|
dirty: bool,
|
||||||
|
tx: Sender<Cmd>,
|
||||||
|
length: u32,
|
||||||
|
connections: Vec<Connection>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkManager {
|
||||||
|
fn update(&mut self) -> Result<(), ::std::io::Error> {
|
||||||
|
self.state = state(self.con.as_ref())?;
|
||||||
|
|
||||||
|
self.connections = active_connections(self.con.as_ref())?
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| {
|
||||||
|
let devices = c.devices(self.con.as_ref()).unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|d| {
|
||||||
|
let t = d.device_type(self.con.as_ref()).unwrap();
|
||||||
|
let ips = d.ip4config(self.con.as_ref()).unwrap().addresses(self.con.as_ref()).unwrap();
|
||||||
|
Device{
|
||||||
|
t,
|
||||||
|
ips,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Connection{
|
||||||
|
devices
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(font_size: f32, length: u32, tx: Sender<Cmd>) -> Result<Box<NetworkManager>, ::std::io::Error> {
|
||||||
|
let con = dbus::Connection::get_private(dbus::BusType::System).map_err(|_| {
|
||||||
|
::std::io::Error::new(::std::io::ErrorKind::Other, "unable to open dbus")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
|
||||||
|
let font = Font::new(&ROBOTO_REGULAR, font_size);
|
||||||
|
|
||||||
|
let fds = con.watch_fds();
|
||||||
|
if fds.len() != 1 {
|
||||||
|
return Err(::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"expected 1 watch fd from dbus",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nm = NetworkManager {
|
||||||
|
con: DbusConnection(con),
|
||||||
|
font: RefCell::new(font),
|
||||||
|
font_size: font_size as u32,
|
||||||
|
length,
|
||||||
|
state: NetworkState::Unknown,
|
||||||
|
dirty: true,
|
||||||
|
watch: fds[0],
|
||||||
|
tx,
|
||||||
|
connections: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
nm.update()?;
|
||||||
|
|
||||||
|
Ok(Box::new(nm))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for NetworkManager {
|
||||||
|
fn wait(&mut self, ctx: &mut WaitContext) {
|
||||||
|
let mut stuff = false;
|
||||||
|
for _ in self
|
||||||
|
.con
|
||||||
|
.as_ref()
|
||||||
|
.watch_handle(self.watch.fd(), dbus::WatchEvent::Readable as u32)
|
||||||
|
{
|
||||||
|
stuff = true;
|
||||||
|
}
|
||||||
|
if stuff {
|
||||||
|
self.update().unwrap();
|
||||||
|
self.dirty = true;
|
||||||
|
self.tx.send(Cmd::Draw).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fds
|
||||||
|
.push(PollFd::new(self.watch.fd(), PollFlags::POLLIN));
|
||||||
|
}
|
||||||
|
fn enter(&mut self) {}
|
||||||
|
fn leave(&mut self) {}
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
(self.length, self.font_size * self.connections.len() as u32)
|
||||||
|
}
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
|
let (width, height) = self.size();
|
||||||
|
{
|
||||||
|
if !self.dirty && !ctx.force {
|
||||||
|
return Ok(DrawReport::empty(width, height));
|
||||||
|
}
|
||||||
|
self.dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = &mut ctx.buf.subdimensions((pos.0, pos.1, width, height))?;
|
||||||
|
buf.memset(ctx.bg);
|
||||||
|
|
||||||
|
for con in &self.connections {
|
||||||
|
if con.devices.len() == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let d = &con.devices[0];
|
||||||
|
let c = Color::new(1.0, 1.0, 1.0, 1.0);
|
||||||
|
self.font.borrow_mut().auto_draw_text(buf, ctx.bg, &c, "network")?;
|
||||||
|
let bar_off = 5 * self.font_size;
|
||||||
|
self.font.borrow_mut().auto_draw_text(&mut buf.offset((bar_off, 0))?, ctx.bg, &c, &format!("{:?}", d))?;
|
||||||
|
}
|
||||||
|
Ok(DrawReport {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
damage: vec![buf.get_signed_bounds()],
|
||||||
|
full_damage: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_input(&mut self, _: u32, _: ModifiersState, _: KeyState, _: Option<String>) {}
|
||||||
|
fn mouse_click(&mut self, _: u32, _: (u32, u32)) {}
|
||||||
|
fn mouse_scroll(&mut self, _: (f64, f64), _: (u32, u32)) {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user