diff --git a/Cargo.lock b/Cargo.lock index 6f563be..816b5e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "anyhow" version = "1.0.102" @@ -418,6 +468,65 @@ dependencies = [ "windows-link", ] +[[package]] +name = "clap" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_complete" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19c9f1dde76b736e3681f28cec9d5a61299cbaae0fce80a68e43724ad56031eb" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_derive" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "clap_mangen" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e30ffc187e2e3aeafcd1c6e2aa416e29739454c0ccaa419226d5ecd181f2d78" +dependencies = [ + "clap", + "roff", +] + [[package]] name = "clipboard-win" version = "5.4.1" @@ -468,6 +577,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + [[package]] name = "combine" version = "4.6.7" @@ -1468,6 +1583,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itoa" version = "1.0.17" @@ -2104,6 +2225,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "orbclient" version = "0.3.51" @@ -2261,6 +2388,20 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "ppd" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c9336eeb6b2ea4e61900f44b539ca0d095593a19ba8e831825c30e906aa475c" +dependencies = [ + "clap", + "clap_complete", + "clap_mangen", + "serde", + "thiserror 2.0.18", + "zbus", +] + [[package]] name = "presser" version = "0.3.1" @@ -2404,6 +2545,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "roff" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf2048e0e979efb2ca7b91c4f1a8d77c91853e9b987c94c555668a8994915ad" + [[package]] name = "roxmltree" version = "0.20.0" @@ -3000,6 +3147,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.22.0" @@ -3310,6 +3463,7 @@ dependencies = [ "chrono", "iced", "iced_layershell", + "ppd", "tokio", "winit", "zbus", diff --git a/Cargo.toml b/Cargo.toml index 539476c..c46a44f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" chrono = "0.4.44" iced = { version = "0.14.0", default-features = false, features = ["wgpu", "wayland", "tokio"] } iced_layershell = { version = "0.15.0", default-features = false } +ppd = "0.1.7" tokio = { version = "1.50.0", features = ["time"] } winit = { version = "0.30.12", default-features = false, features = ["wayland"] } zbus = "5.14.0" diff --git a/src/app.rs b/src/app.rs index 12a8041..b8ba2a1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -19,9 +19,9 @@ impl App { pub fn new() -> Self { Self { widgets: vec![ - Box::new(ClockWidget::new()), - Box::new(Spacer::new(iced::Length::Fill)), Box::new(ShutdownWidget::new()), + Box::new(Spacer::new(iced::Length::Fixed(5.))), + Box::new(ClockWidget::new()), Box::new(Spacer::new(iced::Length::Fill)), Box::new(BatteryWidget::new()), ], diff --git a/src/widget.rs b/src/widget.rs index 17beceb..f200259 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -3,6 +3,7 @@ use iced::Subscription; use iced::Task; use iced_layershell::to_layer_message; +use crate::widgets::power_management::PowerManagement; use crate::widgets::powerbutton::ShutdownEvents; pub trait PanelWidget { @@ -20,4 +21,5 @@ pub enum Message { Battery(Option), Time, ShutdownEvent(ShutdownEvents), + PowerManagement(PowerManagement), } diff --git a/src/widgets/focused_window.rs b/src/widgets/focused_window.rs new file mode 100644 index 0000000..e4ffa18 --- /dev/null +++ b/src/widgets/focused_window.rs @@ -0,0 +1,69 @@ +use iced::{Subscription, Task, futures::SinkExt}; +use niri_ipc::{Event, Request, Response, Window, socket::Socket}; + +use crate::widget::{Message, PanelWidget}; + +pub struct FocusedWindowWidget { + focused_window: Option, +} + +impl FocusedWindowWidget { + pub fn new() -> Self { + Self { + focused_window: None, + } + } +} + +impl PanelWidget for FocusedWindowWidget { + fn update(&mut self, message: &Message) -> iced::Task { + let Message::FocusChanged(i) = message else { + return Task::none(); + }; + + self.focused_window = *i; + Task::none() + } + + fn subscribe(&self) -> Subscription { + Subscription::run(|| { + iced::stream::channel(16, async move |mut tx| { + use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; + use tokio::net::UnixStream; + + let socket_path = std::env::var("NIRI_SOCKET").unwrap(); + let mut stream = UnixStream::connect(socket_path).await.unwrap(); + + // Send the EventStream request as newline-delimited JSON + let request = serde_json::to_string(&niri_ipc::Request::EventStream).unwrap(); + stream + .write_all(format!("{request}\n").as_bytes()) + .await + .unwrap(); + + let mut reader = BufReader::new(stream); + let mut line = String::new(); + + loop { + line.clear(); + reader.read_line(&mut line).await.unwrap(); + + // First line back is the Reply, subsequent lines are Events + if let Ok(event) = serde_json::from_str::(line.trim()) + && let niri_ipc::Event::WindowFocusChanged { id } = event + { + let _ = tx.send(Message::FocusChanged(id)).await; + } + } + }) + }) + } + fn view(&self, _id: iced::window::Id) -> iced::Element<'_, Message> { + iced::widget::text!( + "{}", + self.focused_window + .map_or("None".into(), |f| format!("{}", f)) + ) + .into() + } +} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 22184f7..15c857e 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,4 +1,5 @@ pub mod battery; pub mod clock; +pub mod power_management; pub mod powerbutton; pub mod spacer; diff --git a/src/widgets/power_management.rs b/src/widgets/power_management.rs new file mode 100644 index 0000000..6c0615e --- /dev/null +++ b/src/widgets/power_management.rs @@ -0,0 +1,52 @@ +use iced::{Subscription, Task}; +use ppd::PpdProxy; +use zbus::connection::Connection; + +use crate::widget::{Message, PanelWidget}; + +pub struct PowerManagementWidget { + window: Option, + current_mode: usize, + names: Vec>, +} + +#[derive(Debug, Clone)] +pub enum PowerManagement { + ToggleWindow, + SetMode(SetMode), +} + +#[derive(Debug, Clone)] +pub enum SetMode { + Saver, + Medium, + Performant, +} + +impl PowerManagementWidget { + pub fn new() -> Self { + let conn = Connection::system().await.unwrap(); + } +} + +impl PanelWidget for PowerManagementWidget { + fn update(&mut self, message: &crate::widget::Message) -> iced::Task { + let Message::PowerManagement(msg) = message else { + return Task::none(); + }; + + Task::none() + } + + fn subscribe(&self) -> iced::Subscription { + Subscription::none() + } + + fn view(&self, id: iced::window::Id) -> iced::Element<'_, crate::widget::Message> { + todo!() + } + + fn has_window(&self, id: iced::window::Id) -> bool { + self.window == Some(id) + } +}