diff --git a/src/main.rs b/src/main.rs index 0d6e6e6..46e5a19 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,17 @@ use std::cmp; +use std::fs::read_to_string; use chrono::prelude::*; -use hyprland::data::Workspaces; -use hyprland::event_listener; -use hyprland::shared::{HyprData, WorkspaceType}; +use hyprland::data::{Client, Workspace, Workspaces}; +use hyprland::event_listener::{self, AsyncEventListener}; +use hyprland::shared::{HyprData, HyprDataActiveOptional, HyprDataVec}; use iced::font::Weight; -use iced::widget::{container, horizontal_space, row, text, Text}; +use iced::futures::SinkExt; +use iced::stream; +use iced::widget::{container, horizontal_space, row, text}; use iced::{ time, Background, Border, Color, Element, Event, Font, Length, Shadow, Subscription, Task, - Theme, Vector, + Vector, }; use iced_layershell::build_pattern::{application, MainSettings}; @@ -16,6 +19,7 @@ use iced_layershell::reexport::{Anchor, KeyboardInteractivity}; use iced_layershell::settings::LayerShellSettings; use iced_layershell::to_layer_message; use iced_runtime::futures::event; +use miette::{IntoDiagnostic, Result}; use sysinfo::{Disks, System}; use tracing::level_filters::LevelFilter; use tracing::{debug, error}; @@ -57,9 +61,6 @@ fn main() -> iced_layershell::Result { application(Panel::namespace, Panel::update, Panel::view) .subscription(Panel::subscription) - .subscription(|_| { - time::every(time::Duration::from_millis(1000)).map(|_| Message::Time(Local::now())) - }) .settings(settings) .style(Panel::style) .run_with(Panel::new) @@ -70,12 +71,13 @@ struct Panel { time: String, cpu: String, memory: String, - battery: String, + battery: Option, disk: String, system: System, disks: Disks, - workspaces: Workspaces, + workspaces: Vec, active_workspace: i32, + active_window: String, apps: Vec, } @@ -85,7 +87,9 @@ enum Message { Launch(usize), Time(DateTime), IcedEvent(Event), - WorkspaceChange(WorkspaceType), + WorkspaceChange(Vec), + ActiveWorkspaceChange(event_listener::WorkspaceEventData), + ActiveWindowChange(String), } impl Panel { @@ -94,18 +98,27 @@ impl Panel { error!("Couldn't get hyprland info: {}", e); e }); + let active_window = Client::get_active().unwrap().unwrap().title; + let battery = match Battery::get() { + Ok(b) => Some(b), + Err(e) => { + error!("{e}"); + None + } + }; ( Self { system: System::new_all(), disks: Disks::new_with_refreshed_list(), - workspaces: workspaces.unwrap(), + workspaces: workspaces.unwrap().to_vec(), active_workspace: 1, time: String::new(), cpu: String::new(), memory: String::new(), - battery: String::new(), disk: String::new(), apps: vec![String::new()], + active_window, + battery, }, Task::none(), ) @@ -129,6 +142,13 @@ impl Panel { convert((disk.total_space() - disk.available_space()) as f64) ); }; + self.battery = match Battery::get() { + Ok(b) => Some(b), + Err(e) => { + error!("{e}"); + None + } + }; // let used_space = 393; // self.disk = format!("󰋊 {}", used_space); self.cpu = format!(" {}%", self.system.global_cpu_usage().round()); @@ -149,26 +169,113 @@ impl Panel { Message::IcedEvent(event) => { debug!(?event) } + Message::WorkspaceChange(workspaces) => { + // debug!(?workspaces); + self.workspaces = workspaces; + } + Message::ActiveWorkspaceChange(data) => { + // debug!(?data); + self.active_workspace = data.id; + } + Message::ActiveWindowChange(w) => { + self.active_window = w; + } _ => unreachable!(), - Message::WorkspaceChange(workspace_type) => todo!(), } Task::none() } fn subscription(&self) -> Subscription { - event::listen().map(Message::IcedEvent) + let iced_events = event::listen().map(Message::IcedEvent); + let timed_events = + time::every(time::Duration::from_millis(1000)).map(|_| Message::Time(Local::now())); + let hyprland_events = self.hyprland_subscription(); + Subscription::batch([iced_events, timed_events, hyprland_events]) } - // fn hyprland_subscription(&self) -> Subscription { - // let mut event_listener = event_listener::EventListener::new(); - // event_listener.add_workspace_changed_handler(|x| Message::WorkspaceChange(x)); - // Subscription::run(|| event_listener.start_listener()) - // } + fn hyprland_subscription(&self) -> Subscription { + Subscription::run(|| { + stream::channel(1, |mut sender| async move { + let mut workspaces = Workspaces::get_async() + .await + .map_err(|e| { + error!("Couldn't get hyprland info: {}", e); + e + }) + .unwrap() + .to_vec(); + workspaces.sort_by(|a, b| a.id.cmp(&b.id)); + sender + .send(Message::WorkspaceChange(workspaces)) + .await + .unwrap_or_else(|err| { + eprintln!("Trying to send workspaces failed with err: {err}"); + }); + if let Ok(window) = Client::get_active_async().await { + if let Some(w) = window { + sender + .send(Message::ActiveWindowChange(w.title.trim().to_owned())) + .await + .unwrap_or_else(|e| { + error!("Trying to send window failed with err: {e}"); + }); + } + } + + let mut listener = AsyncEventListener::new(); + + let senderx = sender.clone(); + listener.add_active_window_changed_handler(move |data| { + let mut sender = senderx.clone(); + Box::pin(async move { + if let Some(data) = data { + sender + .send(Message::ActiveWindowChange(data.title)) + .await + .unwrap(); + } + }) + }); + + let senderx = sender.clone(); + listener.add_workspace_changed_handler(move |data| { + let mut sender = senderx.clone(); + Box::pin(async move { + sender + .send(Message::ActiveWorkspaceChange(data)) + .await + .unwrap_or_else(|e| error!("Error in sending: {e}")); + let mut workspaces = Workspaces::get_async() + .await + .map_err(|e| { + error!("Couldn't get hyprland info: {}", e); + e + }) + .unwrap() + .to_vec(); + workspaces.sort_by(|a, b| a.id.cmp(&b.id)); + sender + .send(Message::WorkspaceChange(workspaces)) + .await + .unwrap_or_else(|err| { + eprintln!("Trying to send workspaces failed with err: {err}"); + }); + }) + }); + + listener + .start_listener_async() + .await + .expect("Failed to listen for hyprland events"); + }) + }) + } fn view(&self) -> Element { let workspaces: Vec> = self .workspaces .iter() + .filter(|w| w.name != "special:special") .map(|w| { text!("{}", { match w.id { @@ -195,17 +302,39 @@ impl Panel { }) .collect(); let workspaces = row(workspaces).spacing(20).padding([0, 5]); + + let window = text!("{}", self.active_window).color(Color::parse("#57c7ff").unwrap()); let disk = text!("{}", self.disk).color(Color::parse("#ff9f43").unwrap()); let mem = text!("{}", self.memory).color(Color::parse("#f3f99d").unwrap()); let cpu = text!("{}", self.cpu).color(Color::parse("#57c7ff").unwrap()); let clock = text!("{}", self.time); let clock = clock.color(Color::parse("#5af78e").unwrap()); + let battery_icon = if let Some(battery) = &self.battery { + match battery.status { + BatteryStatus::Charging => "󱟦", + BatteryStatus::Charged => "󱟢", + BatteryStatus::Draining => "󱟤", + } + } else { + "" + }; + + let battery = text!( + "{} {}", + battery_icon, + if let Some(battery) = &self.battery { + battery.capacity.to_string() + } else { + "".to_string() + } + ) + .color(Color::parse("#ff6ac1").unwrap()); + let row = row!( - container(workspaces), - container(horizontal_space()), + container(row!(workspaces, window, horizontal_space()).spacing(10)), container(clock), - container(row!(horizontal_space(), disk, cpu, mem).spacing(5)) + container(row!(horizontal_space(), disk, cpu, mem, battery).spacing(5)) ) .spacing(5) .padding([0, 20]); @@ -263,3 +392,37 @@ pub fn convert(num: f64) -> String { let unit = units[exponent as usize]; format!("{}{} {}", negative, pretty_bytes, unit) } + +#[derive(Debug)] +struct Battery { + capacity: u8, + status: BatteryStatus, +} + +impl Battery { + fn get() -> Result { + let capacity = read_to_string("/sys/class/power_supply/BAT1/capacity") + .into_diagnostic()? + .trim() + .parse() + .into_diagnostic()?; + let status = match read_to_string("/sys/class/power_supply/BAT1/status") + .into_diagnostic()? + .trim() + { + "Not charging" => BatteryStatus::Charged, + "Discharging" => BatteryStatus::Draining, + "Charging" => BatteryStatus::Charging, + _ => BatteryStatus::Draining, + }; + + Ok(Self { capacity, status }) + } +} + +#[derive(Debug)] +enum BatteryStatus { + Charging, + Charged, + Draining, +}