Add Ctl Socket, iterate on Idling daemon, update sysinfo crate, start on run menu

This commit is contained in:
jmb05
2026-10-30 16:49:21 +01:00
parent a6b601e3bd
commit 80b5cafb8c
17 changed files with 504 additions and 260 deletions

5
.gitignore vendored
View File

@@ -1,2 +1,7 @@
/target /target
/unused/ /unused/
/jmb-bar
/perf.data
/qodana.yaml
/.idea/dictionaries/project.xml
/.idea/inspectionProfiles/Project_Default.xml

1
.idea/jmb-bar.iml generated
View File

@@ -4,6 +4,7 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" /> <excludeFolder url="file://$MODULE_DIR$/target" />
<excludeFolder url="file://$MODULE_DIR$/unused" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

135
Cargo.lock generated
View File

@@ -98,7 +98,7 @@ dependencies = [
"russh", "russh",
"serde", "serde",
"serde_json", "serde_json",
"sysinfo", "sysinfo 0.39.6",
"tar", "tar",
"thiserror 2.0.21", "thiserror 2.0.21",
"tokio", "tokio",
@@ -1684,15 +1684,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "fsevent-sys"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.34" version = "0.3.34"
@@ -2496,7 +2487,7 @@ dependencies = [
"js-sys", "js-sys",
"log", "log",
"wasm-bindgen", "wasm-bindgen",
"windows-core 0.62.2", "windows-core 0.61.2",
] ]
[[package]] [[package]]
@@ -2737,7 +2728,7 @@ dependencies = [
"notify", "notify",
"rustsni", "rustsni",
"serde_json", "serde_json",
"sysinfo", "sysinfo 0.40.0",
"thiserror 2.0.21", "thiserror 2.0.21",
"time-format", "time-format",
"tokio", "tokio",
@@ -2748,8 +2739,8 @@ dependencies = [
[[package]] [[package]]
name = "jmb-logger-rs" name = "jmb-logger-rs"
version = "0.2.1" version = "0.2.2"
source = "git+https://github.com/jmb05/jmb-logger-rs#ea000f8c733b7e7854fdb123b509eb484bcec101" source = "git+https://github.com/jmb05/jmb-logger-rs#083f7b96a80ab43e37850735d2367e52c6b7f5b0"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"chrono", "chrono",
@@ -3382,27 +3373,28 @@ dependencies = [
[[package]] [[package]]
name = "notify" name = "notify"
version = "8.2.0" version = "8.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" checksum = "82f812702fbc75d35416d3f884e04925be7e9498f4eceea2e24ba34559284674"
dependencies = [ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
"fsevent-sys",
"inotify", "inotify",
"kqueue", "kqueue",
"libc", "libc",
"log", "log",
"mio", "mio",
"notify-types", "notify-types",
"objc2-core-foundation",
"objc2-core-services",
"walkdir", "walkdir",
"windows-sys 0.60.2", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
name = "notify-types" name = "notify-types"
version = "2.1.0" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" checksum = "0a437a9308232ce794b1e7fdf540d38ea37efc3c73492640c13b4c8568b8f108"
dependencies = [ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
] ]
@@ -3553,6 +3545,16 @@ dependencies = [
"objc2", "objc2",
] ]
[[package]]
name = "objc2-core-services"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583300ad934cba24ff5292aee751ecc070f7ca6b39a574cc21b7b5e588e06a0b"
dependencies = [
"libc",
"objc2-core-foundation",
]
[[package]] [[package]]
name = "objc2-encode" name = "objc2-encode"
version = "4.1.0" version = "4.1.0"
@@ -5349,6 +5351,21 @@ dependencies = [
"windows 0.62.2", "windows 0.62.2",
] ]
[[package]]
name = "sysinfo"
version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "110e37d98880fd804cd47509b90a8444f7aaaab655ae625e83cd442a8bc2ebf2"
dependencies = [
"libc",
"memchr",
"ntapi",
"objc2-core-foundation",
"objc2-io-kit",
"objc2-open-directory",
"windows 0.62.2",
]
[[package]] [[package]]
name = "system-configuration" name = "system-configuration"
version = "0.7.0" version = "0.7.0"
@@ -6492,15 +6509,6 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.61.2" version = "0.61.2"
@@ -6534,30 +6542,13 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6", "windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6", "windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6", "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6", "windows_i686_gnullvm",
"windows_i686_msvc 0.52.6", "windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6", "windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6", "windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6", "windows_x86_64_msvc 0.52.6",
] ]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link 0.2.1",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
]
[[package]] [[package]]
name = "windows-threading" name = "windows-threading"
version = "0.1.0" version = "0.1.0"
@@ -6588,12 +6579,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.48.5" version = "0.48.5"
@@ -6606,12 +6591,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.48.5" version = "0.48.5"
@@ -6624,24 +6603,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.48.5" version = "0.48.5"
@@ -6654,12 +6621,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.48.5" version = "0.48.5"
@@ -6672,12 +6633,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.48.5" version = "0.48.5"
@@ -6690,12 +6645,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.48.5" version = "0.48.5"
@@ -6708,12 +6657,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "1.0.4" version = "1.0.4"
@@ -6741,7 +6684,7 @@ dependencies = [
"serde", "serde",
"thiserror 2.0.21", "thiserror 2.0.21",
"windows 0.62.2", "windows 0.62.2",
"windows-core 0.62.2", "windows-core 0.61.2",
] ]
[[package]] [[package]]

View File

@@ -3,6 +3,14 @@ name = "jmb-bar"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[[bin]]
name = "jmb-bar"
path = "src/main.rs"
[[bin]]
name = "ctl"
path = "src/ctl.rs"
[dependencies] [dependencies]
grass = "0.13.4" grass = "0.13.4"
gtk = { version = "0.11.5", package = "gtk4", features = ["v4_24"] } gtk = { version = "0.11.5", package = "gtk4", features = ["v4_24"] }
@@ -17,8 +25,8 @@ tokio = { version = "1.53.3", features = ["rt", "time", "net", "sync", "rt-multi
futures-util = "0.3" futures-util = "0.3"
serde_json = "1.0.154" serde_json = "1.0.154"
crossbeam-channel = "0.5.17" crossbeam-channel = "0.5.17"
jmb-logger-rs = { git = "https://github.com/jmb05/jmb-logger-rs" } jmb-logger-rs = { version = "0.2.2", git = "https://github.com/jmb05/jmb-logger-rs" }
sysinfo = "0.39.6" sysinfo = "0.40.0"
all-smi = "0.28.1" all-smi = "0.28.1"
rustsni = "0.2.2" rustsni = "0.2.2"
thiserror = "2.0.21" thiserror = "2.0.21"

View File

@@ -1,5 +1,6 @@
pub mod audio; pub mod audio;
pub mod brightness_control; pub mod brightness_control;
mod commands;
pub(crate) mod idle; pub(crate) mod idle;
pub(crate) mod json_socket; pub(crate) mod json_socket;
pub mod network; pub mod network;
@@ -9,11 +10,13 @@ pub mod window_manager_hypr;
use crate::LOGGER; use crate::LOGGER;
use crate::backend::audio::{MediaBackend, MediaCommand}; use crate::backend::audio::{MediaBackend, MediaCommand};
use crate::backend::brightness_control::{BrightnessManager, BrightnessMessage}; use crate::backend::brightness_control::{BrightnessManager, BrightnessMessage};
use crate::backend::commands::ControlSocket;
use crate::backend::idle::{IdleDaemon, IdleMessage, IdleState}; use crate::backend::idle::{IdleDaemon, IdleMessage, IdleState};
use crate::backend::json_socket::{JsonSocketBackend, State}; use crate::backend::json_socket::{JsonSocketBackend, State};
use crate::backend::network::{NetworkBackendHandle, NetworkCommand, NetworkState}; use crate::backend::network::{NetworkBackendHandle, NetworkCommand, NetworkState};
use crate::backend::perf_stats::{Stats, StatsBackend}; use crate::backend::perf_stats::{Stats, StatsBackend};
use crate::backend::window_manager_hypr::{HyprlandComms, WindowManagerEvent}; use crate::backend::window_manager_hypr::{HyprlandComms, WindowManagerEvent};
use crate::ctl::ControlCommand;
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender};
use jmb_logger_rs::*; use jmb_logger_rs::*;
use std::fmt::Debug; use std::fmt::Debug;
@@ -31,7 +34,7 @@ pub trait BackendThread<M: Debug + Clone, D> {
if let Err(e) = tx.send(message.clone()) { if let Err(e) = tx.send(message.clone()) {
warn!( warn!(
&LOGGER, &LOGGER,
"Error sending message to frontend: {}", "Error sending message to frontend: {} (msg: {message:?})",
e.to_string() e.to_string()
); );
} }
@@ -72,8 +75,20 @@ pub struct Backend {
br_registrator: Registrator<BrightnessMessage>, br_registrator: Registrator<BrightnessMessage>,
stats_registrator: Registrator<Stats>, stats_registrator: Registrator<Stats>,
media_registrator: Registrator<MediaCommand>, media_registrator: Registrator<MediaCommand>,
tailscale_registrator: Registrator<State>, netbird_registrator: Registrator<State>,
idle_registrator: Registrator<IdleState>, idle_registrator: Registrator<IdleState>,
control_registrator: Registrator<ControlCommand>,
}
pub enum ChannelType {
Net,
Hypr,
Br,
Stats,
Media,
Idle,
Netbird,
Control,
} }
impl Backend { impl Backend {
@@ -86,6 +101,7 @@ impl Backend {
let media_registrator = MediaBackend::start(); let media_registrator = MediaBackend::start();
let tailscale_registrator = JsonSocketBackend::start(); let tailscale_registrator = JsonSocketBackend::start();
let idle_registrator = IdleDaemon::start(); let idle_registrator = IdleDaemon::start();
let control_registrator = ControlSocket::start();
debug!(&LOGGER, "Started Backend threads"); debug!(&LOGGER, "Started Backend threads");
Self { Self {
hypr_registrator, hypr_registrator,
@@ -93,47 +109,85 @@ impl Backend {
br_registrator, br_registrator,
stats_registrator, stats_registrator,
media_registrator, media_registrator,
tailscale_registrator, netbird_registrator: tailscale_registrator,
idle_registrator, idle_registrator,
control_registrator,
} }
} }
pub fn subscribe(&self) -> BackendChannels { pub fn subscribe(&self, channel_types: &[ChannelType]) -> BackendChannels {
let (net_rx, net_tx) = NetworkBackendHandle::subscribe(self.net_registrator.clone()); let mut channels = BackendChannels::default();
let (hypr_rx, _) = HyprlandComms::subscribe(self.hypr_registrator.clone()); for ch_type in channel_types {
let (br_rx, br_tx) = BrightnessManager::subscribe(self.br_registrator.clone()); match ch_type {
let (stats_rx, _) = StatsBackend::subscribe(self.stats_registrator.clone()); ChannelType::Net => {
let (media_rx, media_tx) = MediaBackend::subscribe(self.media_registrator.clone()); let (rx, tx) = NetworkBackendHandle::subscribe(self.net_registrator.clone());
let (tailscale_rx, _) = JsonSocketBackend::subscribe(self.tailscale_registrator.clone()); channels.net_tx = Some(tx);
let (idle_rx, idle_tx) = IdleDaemon::subscribe(self.idle_registrator.clone()); channels.net_rx = Some(rx);
BackendChannels { }
net_rx: Some(net_rx), ChannelType::Hypr => {
net_tx, channels.hypr_rx =
hypr_rx: Some(hypr_rx), Some(HyprlandComms::subscribe(self.hypr_registrator.clone()).0);
br_rx: Some(br_rx), }
br_tx, ChannelType::Br => {
stats_rx: Some(stats_rx), let (rx, tx) = BrightnessManager::subscribe(self.br_registrator.clone());
media_rx: Some(media_rx), channels.br_rx = Some(rx);
media_tx, channels.br_tx = Some(tx);
tailscale_rx: Some(tailscale_rx), }
idle_rx: Some(idle_rx), ChannelType::Stats => {
idle_tx, channels.stats_rx =
Some(StatsBackend::subscribe(self.stats_registrator.clone()).0);
}
ChannelType::Media => {
let (rx, tx) = MediaBackend::subscribe(self.media_registrator.clone());
channels.media_rx = Some(rx);
channels.media_tx = Some(tx);
}
ChannelType::Idle => {
let (rx, tx) = IdleDaemon::subscribe(self.idle_registrator.clone());
channels.idle_rx = Some(rx);
channels.idle_tx = Some(tx);
}
ChannelType::Netbird => {
channels.netbird_rx =
Some(JsonSocketBackend::subscribe(self.netbird_registrator.clone()).0);
}
ChannelType::Control => {
channels.ctl_rx =
Some(ControlSocket::subscribe(self.control_registrator.clone()).0);
}
}
} }
channels
}
pub fn subscribe_all(&self) -> BackendChannels {
self.subscribe(&[
ChannelType::Net,
ChannelType::Idle,
ChannelType::Netbird,
ChannelType::Br,
ChannelType::Stats,
ChannelType::Control,
ChannelType::Media,
ChannelType::Hypr,
])
} }
} }
#[derive(Default)]
pub struct BackendChannels { pub struct BackendChannels {
net_rx: Option<Receiver<NetworkState>>, net_rx: Option<Receiver<NetworkState>>,
net_tx: UnboundedSender<NetworkCommand>, net_tx: Option<UnboundedSender<NetworkCommand>>,
hypr_rx: Option<Receiver<WindowManagerEvent>>, hypr_rx: Option<Receiver<WindowManagerEvent>>,
br_rx: Option<Receiver<BrightnessMessage>>, br_rx: Option<Receiver<BrightnessMessage>>,
br_tx: Sender<BrightnessMessage>, br_tx: Option<Sender<BrightnessMessage>>,
stats_rx: Option<Receiver<Stats>>, stats_rx: Option<Receiver<Stats>>,
media_rx: Option<Receiver<MediaCommand>>, media_rx: Option<Receiver<MediaCommand>>,
media_tx: Sender<MediaCommand>, media_tx: Option<Sender<MediaCommand>>,
tailscale_rx: Option<Receiver<State>>, netbird_rx: Option<Receiver<State>>,
idle_rx: Option<Receiver<IdleState>>, idle_rx: Option<Receiver<IdleState>>,
idle_tx: Sender<IdleMessage>, idle_tx: Option<Sender<IdleMessage>>,
ctl_rx: Option<Receiver<ControlCommand>>,
} }
impl BackendChannels { impl BackendChannels {
@@ -150,7 +204,7 @@ impl BackendChannels {
self.stats_rx.take() self.stats_rx.take()
} }
pub fn take_netbird_rx(&mut self) -> Option<Receiver<State>> { pub fn take_netbird_rx(&mut self) -> Option<Receiver<State>> {
self.tailscale_rx.take() self.netbird_rx.take()
} }
pub fn take_media_rx(&mut self) -> Option<Receiver<MediaCommand>> { pub fn take_media_rx(&mut self) -> Option<Receiver<MediaCommand>> {
self.media_rx.take() self.media_rx.take()
@@ -158,16 +212,19 @@ impl BackendChannels {
pub fn take_idle_rx(&mut self) -> Option<Receiver<IdleState>> { pub fn take_idle_rx(&mut self) -> Option<Receiver<IdleState>> {
self.idle_rx.take() self.idle_rx.take()
} }
pub fn net_tx(&self) -> UnboundedSender<NetworkCommand> { pub fn take_ctl_rx(&mut self) -> Option<Receiver<ControlCommand>> {
self.ctl_rx.take()
}
pub fn net_tx(&self) -> Option<UnboundedSender<NetworkCommand>> {
self.net_tx.clone() self.net_tx.clone()
} }
pub fn br_tx(&self) -> Sender<BrightnessMessage> { pub fn br_tx(&self) -> Option<Sender<BrightnessMessage>> {
self.br_tx.clone() self.br_tx.clone()
} }
pub fn media_tx(&self) -> Sender<MediaCommand> { pub fn media_tx(&self) -> Option<Sender<MediaCommand>> {
self.media_tx.clone() self.media_tx.clone()
} }
pub fn idle_tx(&self) -> Sender<IdleMessage> { pub fn idle_tx(&self) -> Option<Sender<IdleMessage>> {
self.idle_tx.clone() self.idle_tx.clone()
} }
} }

View File

@@ -215,14 +215,19 @@ impl BackendThread<BrightnessMessage, Sender<BrightnessMessage>> for BrightnessM
} }
}); });
thread::spawn(move || { thread::spawn(move || {
let mut sleep_dur = 500;
loop { loop {
thread::sleep(Duration::from_millis(10_000)); if sleep_dur < 500 {
sleep_dur += 1;
}
thread::sleep(Duration::from_millis(sleep_dur));
let mut messages = vec![]; let mut messages = vec![];
for (monitor, data) in for (monitor, data) in
&mut instance.lock().expect("Couldn't get lock").display_cache &mut instance.lock().expect("Couldn't get lock").display_cache
{ {
if data.update() { if data.update() {
sleep_dur = 50;
messages.push(BrightnessMessage::Value(monitor.clone(), data.value, false)); messages.push(BrightnessMessage::Value(monitor.clone(), data.value, false));
} }
} }

62
src/backend/commands.rs Normal file
View File

@@ -0,0 +1,62 @@
use crate::LOGGER;
use crate::backend::{BackendThread, SimpleBackendThread};
use crate::ctl::{ControlCommand, SOCKET_PATH};
use crossbeam_channel::{Receiver, Sender};
use jmb_logger_rs::*;
use std::io::Read;
use std::net::Shutdown;
use std::os::unix::net::UnixListener;
use std::sync::{Arc, Mutex};
pub struct ControlSocket {
subscribers: Vec<Sender<ControlCommand>>,
}
impl SimpleBackendThread<ControlCommand> for ControlSocket {
fn subscribers(&self) -> &Vec<Sender<ControlCommand>> {
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<ControlCommand>>) {
let instance = Arc::new(Mutex::new(Self {
subscribers: Vec::new(),
}));
let instance_clone = instance.clone();
std::thread::spawn(move || {
while let Ok(sender) = registration_rx.recv() {
instance_clone.lock().unwrap().subscribers.push(sender);
}
});
std::thread::spawn(move || {
if std::fs::metadata(SOCKET_PATH).is_ok() {
if let Err(e) = std::fs::remove_file(SOCKET_PATH) {
error!(&LOGGER, "Error deleting old socket file: {e}");
return;
}
}
let socket = UnixListener::bind(SOCKET_PATH);
let Ok(socket) = socket else {
error!(&LOGGER, "Error creating socket {}", socket.err().unwrap());
return;
};
info!(&LOGGER, "Successfully started control socket");
loop {
if let Ok((mut stream, _addr)) = socket.accept() {
info!(&LOGGER, "Socket accepted connection");
let mut read = String::new();
let res = stream.read_to_string(&mut read);
if let Err(e) = res {
error!(&LOGGER, "Error reading: {e}");
let _ = stream.shutdown(Shutdown::Both);
continue;
}
if let Some(command) = ControlCommand::from_str(&read) {
instance.lock().unwrap().publish(command);
}
}
}
});
}
}

View File

@@ -6,7 +6,7 @@ use nmrs::raw::zbus::interface;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::{Duration, Instant};
use tokio::sync::watch; use tokio::sync::watch;
use tokio::time::sleep; use tokio::time::sleep;
use wayland_client::globals::registry_queue_init; use wayland_client::globals::registry_queue_init;
@@ -26,7 +26,7 @@ const LEGACY_OBJECT_PATH: &str = "/ScreenSaver";
struct WlState { struct WlState {
idle: Option<bool>, idle: Option<bool>,
tx: watch::Sender<IdleStateInner>, tx: watch::Sender<bool>,
} }
delegate_noop!(WlState: ignore WlSeat); delegate_noop!(WlState: ignore WlSeat);
@@ -53,14 +53,18 @@ impl Dispatch<ExtIdleNotificationV1, ()> for WlState {
_: &QueueHandle<Self>, _: &QueueHandle<Self>,
) { ) {
match event { match event {
Event::Idled => state.idle = Some(true), Event::Idled => {
Event::Resumed => state.idle = Some(false), println!("Idled: {:?}", Instant::now());
state.idle = Some(true);
}
Event::Resumed => {
println!("Resumed: {:?}", Instant::now());
state.idle = Some(false);
}
_ => {} _ => {}
} }
if let Some(idle) = state.idle { if let Some(idle) = state.idle {
state.tx.send_modify(|state| { state.tx.send_modify(|state| *state = idle);
state.wayland_idle = idle;
});
} }
} }
} }
@@ -84,20 +88,6 @@ struct Inhibitor {
_reason: String, _reason: String,
} }
#[derive(Clone, Copy, Debug)]
struct IdleStateInner {
dimmed: bool,
wayland_idle: bool,
dbus_inhibited: bool,
caffeine: bool,
}
impl IdleStateInner {
fn should_dim(&self) -> bool {
self.wayland_idle && !self.dbus_inhibited
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum IdleState { pub enum IdleState {
Init(Sender<IdleMessage>), Init(Sender<IdleMessage>),
@@ -107,20 +97,6 @@ pub enum IdleState {
Active, Active,
} }
impl From<IdleStateInner> for IdleState {
fn from(value: IdleStateInner) -> Self {
if value.should_dim() {
IdleState::Dimmed
} else if value.caffeine {
IdleState::Caffeine
} else if value.dbus_inhibited {
IdleState::Inhibited
} else {
IdleState::Active
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum IdleMessage { pub enum IdleMessage {
Inhibit, Inhibit,
@@ -170,15 +146,10 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
} }
} }
}); });
let (state_tx, state_rx) = watch::channel(IdleStateInner { let (wl_tx, wl_rx) = watch::channel(false);
dimmed: false, let (dbus_tx, dbus_rx) = watch::channel((false, false));
wayland_idle: false,
dbus_inhibited: false,
caffeine: false,
});
let tx_clone = state_tx.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
if let Err(err) = Self::start_idle_notifier(tx_clone) { if let Err(err) = Self::start_idle_notifier(wl_tx) {
error!(&LOGGER, "Idle Daemon unexpectedly quit, reason: {err}"); error!(&LOGGER, "Idle Daemon unexpectedly quit, reason: {err}");
} }
}); });
@@ -216,10 +187,10 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
.expect("Failed to create Tokio runtime"); .expect("Failed to create Tokio runtime");
runtime.block_on(async move { runtime.block_on(async move {
let res = IdleDaemon::start_dbus(state_tx).await; let res = IdleDaemon::start_dbus(dbus_tx).await;
match res { match res {
Ok((_, _)) => { Ok((_, _)) => {
Self::run_idle_timer(instance, state_rx, Duration::from_secs(10)).await; Self::run_idle_timer(instance, wl_rx, dbus_rx).await;
} }
Err(err) => error!(&LOGGER, "Error running D-Bus Screensaver: {err}"), Err(err) => error!(&LOGGER, "Error running D-Bus Screensaver: {err}"),
} }
@@ -228,41 +199,77 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
} }
} }
#[derive(Clone, Copy, Debug)]
enum IdleStage {
Dim,
TurnOff,
Sleep,
}
impl IdleStage {
fn next(self) -> Self {
match self {
IdleStage::Dim => IdleStage::TurnOff,
IdleStage::TurnOff => IdleStage::Sleep,
IdleStage::Sleep => IdleStage::Sleep,
}
}
fn timeout(self) -> Duration {
match self {
Self::Dim => Duration::from_secs(20),
Self::TurnOff => Duration::from_secs(20),
Self::Sleep => Duration::from_secs(20),
}
}
}
impl IdleDaemon { impl IdleDaemon {
async fn run_idle_timer( async fn run_idle_timer(
instance: Arc<Mutex<Self>>, _instance: Arc<Mutex<Self>>,
mut state_rx: watch::Receiver<IdleStateInner>, mut wl_rx: watch::Receiver<bool>,
timeout: Duration, mut dbus_rx: watch::Receiver<(bool, bool)>,
) { ) {
loop { loop {
let mut state = *state_rx.borrow_and_update(); if let Err(e) = wl_rx.changed().await {
instance.lock().unwrap().publish(state.into()); warn!(&LOGGER, "Error getting Wayland idle state {e}");
return;
if !state.should_dim() {
if state_rx.changed().await.is_err() {
return;
}
continue;
} }
let wl_idle = *wl_rx.borrow_and_update();
tokio::select! { let dbus_state = *dbus_rx.borrow_and_update();
_ = sleep(timeout) => { println!("Wayland idle state changed {wl_idle} (DBus state: {dbus_state:?})");
state.dimmed = true; if wl_idle && !dbus_state.0 {
instance.lock().unwrap().publish(state.into()); println!("Going into idle cycle");
} let mut stage = IdleStage::Dim;
result = state_rx.changed() => { loop {
if result.is_err() { tokio::select! {
return; _ = sleep(stage.timeout()) => {
stage = stage.next();
println!("Reached next stage {stage:?}");
}
res = wl_rx.changed() => {
if let Err(e) = res {
warn!(&LOGGER, "Error getting Wayland idle state {e}");
return;
}
println!("WL state change");
break;
}
res = dbus_rx.changed() => {
if let Err(e) = res {
warn!(&LOGGER, "Error getting D-Bus idle state {e}");
return;
}
println!("D-Bus state change");
break;
}
} }
state.dimmed = false;
instance.lock().unwrap().publish(state.into());
} }
} }
} }
} }
fn start_idle_notifier(tx: watch::Sender<IdleStateInner>) -> Result<(), Box<dyn Error>> { fn start_idle_notifier(tx: watch::Sender<bool>) -> Result<(), Box<dyn Error>> {
let conn = Connection::connect_to_env()?; let conn = Connection::connect_to_env()?;
let (globals, mut event_queue) = registry_queue_init::<WlState>(&conn)?; let (globals, mut event_queue) = registry_queue_init::<WlState>(&conn)?;
let queue_handle = event_queue.handle(); let queue_handle = event_queue.handle();
@@ -281,7 +288,7 @@ impl IdleDaemon {
} }
async fn start_dbus( async fn start_dbus(
tx: watch::Sender<IdleStateInner>, tx: watch::Sender<(bool, bool)>,
) -> zbus::Result<(ScreenSaver, zbus::connection::Connection)> { ) -> zbus::Result<(ScreenSaver, zbus::connection::Connection)> {
let service = ScreenSaver::new(tx); let service = ScreenSaver::new(tx);
@@ -345,11 +352,11 @@ struct ScreenSaver {
struct Inner { struct Inner {
next_cookie: u32, next_cookie: u32,
inhibitors: HashMap<u32, Inhibitor>, inhibitors: HashMap<u32, Inhibitor>,
tx: watch::Sender<IdleStateInner>, tx: watch::Sender<(bool, bool)>,
} }
impl ScreenSaver { impl ScreenSaver {
fn new(tx: watch::Sender<IdleStateInner>) -> Self { fn new(tx: watch::Sender<(bool, bool)>) -> Self {
Self { Self {
inner: Arc::new(Mutex::new(Inner { inner: Arc::new(Mutex::new(Inner {
next_cookie: 1, next_cookie: 1,
@@ -405,8 +412,8 @@ impl ScreenSaver {
); );
inner.tx.send_modify(|state| { inner.tx.send_modify(|state| {
state.dbus_inhibited = !inner.inhibitors.is_empty(); state.0 = !inner.inhibitors.is_empty();
state.caffeine = caffeine_flag; state.1 = caffeine_flag;
}); });
Ok(cookie) Ok(cookie)
} }
@@ -436,8 +443,8 @@ impl ScreenSaver {
} }
} }
inner.tx.send_modify(|state| { inner.tx.send_modify(|state| {
state.dbus_inhibited = !inner.inhibitors.is_empty(); state.0 = !inner.inhibitors.is_empty();
state.caffeine = caffeine_flag; state.1 = caffeine_flag;
}); });
Ok(()) Ok(())
} }

View File

@@ -16,8 +16,8 @@ const POLL_INTERVAL: u64 = 10_000;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Stats { pub struct Stats {
pub cpu: u8, pub cpu: Option<u8>,
pub ram: u8, pub ram: Option<u8>,
pub disks: Vec<(String, u8)>, pub disks: Vec<(String, u8)>,
pub gpus: Vec<(String, u8)>, pub gpus: Vec<(String, u8)>,
pub battery_charge: u8, pub battery_charge: u8,
@@ -28,8 +28,8 @@ pub struct Stats {
pub struct StatsBackend { pub struct StatsBackend {
subscribers: Vec<Sender<Stats>>, subscribers: Vec<Sender<Stats>>,
system: System, system: Option<System>,
disks: Disks, disks: Option<Disks>,
} }
impl BackendThread<Stats, ()> for StatsBackend { impl BackendThread<Stats, ()> for StatsBackend {
@@ -47,8 +47,8 @@ impl BackendThread<Stats, ()> for StatsBackend {
thread::spawn(move || { thread::spawn(move || {
let mut instance = Self { let mut instance = Self {
subscribers: Vec::new(), subscribers: Vec::new(),
system: System::new(), system: System::new().ok(),
disks: Disks::new(), disks: Disks::new().ok(),
}; };
let manager = battery::Manager::new(); let manager = battery::Manager::new();
let smi = AllSmi::new(); let smi = AllSmi::new();
@@ -58,24 +58,29 @@ impl BackendThread<Stats, ()> for StatsBackend {
instance.subscribers.push(tx); instance.subscribers.push(tx);
} }
instance.system.refresh_cpu_usage(); let mut cpu = None;
instance.system.refresh_memory(); let mut ram = None;
instance.disks.refresh(true); if let Some(system) = instance.system.as_mut() {
system.refresh_cpu_usage();
system.refresh_memory();
let cpu = instance.system.global_cpu_usage() as u8; cpu = Some(system.global_cpu_usage() as u8);
let ram = ram = Some((system.used_memory() * 100 / system.total_memory()) as u8);
(instance.system.used_memory() * 100 / instance.system.total_memory()) as u8; }
let disk_stats: Vec<(String, u8)> = instance let mut disk_stats = Vec::new();
.disks if let Some(disks) = instance.disks.as_mut() {
.iter() disks.refresh(true);
.map(|disk| { disk_stats = disks
let mount_point = disk.mount_point().display().to_string(); .iter()
let used_space = disk.total_space() - disk.available_space(); .map(|disk| {
let percentage = (used_space * 100 / disk.total_space()) as u8; let mount_point = disk.mount_point().display().to_string();
(mount_point, percentage) let used_space = disk.total_space() - disk.available_space();
}) let percentage = (used_space * 100 / disk.total_space()) as u8;
.collect(); (mount_point, percentage)
})
.collect();
}
let mut gpus: Vec<(String, u8)> = Vec::new(); let mut gpus: Vec<(String, u8)> = Vec::new();
if let Ok(smi) = &smi { if let Ok(smi) = &smi {

51
src/ctl.rs Normal file
View File

@@ -0,0 +1,51 @@
use jmb_logger_rs::*;
use std::env;
use std::io::Write;
use std::os::unix::net::UnixStream;
const CTL_LOGGER: Logger = Logger::create_with_custom_format(Level::DEBUG, String::new());
pub const SOCKET_PATH: &str = "/run/user/1000/jmb-bar.sock";
#[derive(Clone, Debug)]
pub enum ControlCommand {
OpenRunMenu,
}
impl ControlCommand {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"menu" => Some(ControlCommand::OpenRunMenu),
_ => None,
}
}
pub fn to_string(self) -> String {
match self {
ControlCommand::OpenRunMenu => String::from("menu"),
}
}
}
fn main() {
info!(&CTL_LOGGER, "Hello, this is mission control");
let socket = UnixStream::connect(SOCKET_PATH);
let Ok(mut stream) = socket else {
error!(
&CTL_LOGGER,
"Error Connecting to socket: {}",
socket.err().unwrap()
);
error!(&CTL_LOGGER, "Is the bar running?");
return;
};
let args: Vec<String> = env::args().collect();
match args.get(1) {
Some(str) if let Some(cmd) = ControlCommand::from_str(str) => stream
.write(cmd.to_string().as_bytes())
.expect("Error sending message"),
_ => 0usize,
};
}

View File

@@ -1,6 +1,7 @@
use crate::backend::Backend; use crate::backend::Backend;
mod backend; mod backend;
mod css; mod css;
mod ctl;
mod ui; mod ui;
use glib::clone; use glib::clone;
@@ -29,6 +30,7 @@ fn main() {
let backend = Backend::start(); let backend = Backend::start();
app.connect_startup(move |_| { app.connect_startup(move |_| {
debug!(&LOGGER, "Connect startup");
let provider = CssProvider::new(); let provider = CssProvider::new();
if let Some(css) = css::parse_css() { if let Some(css) = css::parse_css() {
provider.load_from_string(&css); provider.load_from_string(&css);
@@ -46,10 +48,12 @@ fn main() {
#[strong] #[strong]
backend, backend,
move |app| { move |app| {
debug!(&LOGGER, "Connect activate");
trace!(&LOGGER, "Starting bars"); trace!(&LOGGER, "Starting bars");
ui::create_bars(app, backend.clone()); ui::create_bars(app, backend.clone());
} }
)); ));
debug!(&LOGGER, "Running app");
app.run(); app.run();
} }

View File

@@ -1,7 +1,9 @@
use crate::LOGGER; use crate::LOGGER;
use crate::backend::window_manager_hypr::{HyprlandComms, WindowManagerEvent}; use crate::backend::window_manager_hypr::{HyprlandComms, WindowManagerEvent};
use crate::backend::{Backend, BackendChannels}; use crate::backend::{Backend, BackendChannels, ChannelType};
use crate::ctl::ControlCommand;
use crate::ui::action_centre::ActionCentre; use crate::ui::action_centre::ActionCentre;
use crate::ui::run_menu::RunMenu;
use crossbeam_channel::TryRecvError; use crossbeam_channel::TryRecvError;
use glib::ControlFlow; use glib::ControlFlow;
use glib::prelude::Cast; use glib::prelude::Cast;
@@ -17,6 +19,7 @@ use std::sync::Mutex;
use std::time::Duration; use std::time::Duration;
mod action_centre; mod action_centre;
mod run_menu;
mod systray; mod systray;
mod title_panel; mod title_panel;
mod utilities_panel; mod utilities_panel;
@@ -27,14 +30,14 @@ pub fn create_bars(app: &Application, backend: Backend) {
let display = Display::default().expect("Couldn't find GDK Display"); let display = Display::default().expect("Couldn't find GDK Display");
let app_clone = app.clone(); let app_clone = app.clone();
let bars: Rc<Mutex<HashMap<u32, ApplicationWindow>>> = Rc::new(Mutex::new(HashMap::new())); let bars: Rc<Mutex<HashMap<u32, ApplicationWindow>>> = Rc::new(Mutex::new(HashMap::new()));
let ac_channels = backend.subscribe(); let ac_channels = backend.subscribe_all();
let action_centre = ActionCentre::create_action_centre(app.clone(), ac_channels); let action_centre = ActionCentre::create_action_centre(app.clone(), ac_channels);
for monitor in 0..display.monitors().n_items() { for monitor in 0..display.monitors().n_items() {
debug!(&LOGGER, "Opening bar {monitor}"); debug!(&LOGGER, "Opening bar {monitor}");
let channels = backend.subscribe(); let channels = backend.subscribe_all();
bars.lock().expect("Couldn't get active bars").insert( bars.lock().expect("Couldn't get active bars").insert(
monitor, monitor,
create_bar( create_and_show_bar(
app_clone.clone(), app_clone.clone(),
&display, &display,
monitor, monitor,
@@ -43,6 +46,7 @@ pub fn create_bars(app: &Application, backend: Backend) {
), ),
); );
} }
let backend_clone = backend.clone();
display display
.monitors() .monitors()
.connect_items_changed(move |_list, pos, removed, added| { .connect_items_changed(move |_list, pos, removed, added| {
@@ -55,20 +59,35 @@ pub fn create_bars(app: &Application, backend: Backend) {
debug!(&LOGGER, "Detected new monitor: {pos}"); debug!(&LOGGER, "Detected new monitor: {pos}");
let app_clone = app_clone.clone(); let app_clone = app_clone.clone();
let action_centre = action_centre.clone(); let action_centre = action_centre.clone();
let channels = backend.subscribe(); let channels = backend_clone.subscribe_all();
let bars = bars.clone(); let bars = bars.clone();
let display = display.clone(); let display = display.clone();
glib::idle_add_local_once(move || { glib::idle_add_local_once(move || {
bars.lock().expect("Couldn't get active bars").insert( bars.lock().expect("Couldn't get active bars").insert(
pos, pos,
create_bar(app_clone, &display, pos, action_centre, channels), create_and_show_bar(app_clone, &display, pos, action_centre, channels),
); );
}); });
} }
}); });
let mut ctl_channel = backend.subscribe(&[ChannelType::Control]);
let ctl_rx = ctl_channel.take_ctl_rx().unwrap();
let app_clone = app.clone();
let menu = RunMenu::create(app_clone);
glib::timeout_add_local(Duration::from_millis(100), move || {
while let Ok(cmd) = ctl_rx.try_recv() {
match cmd {
ControlCommand::OpenRunMenu => {
menu.show();
}
}
}
ControlFlow::Continue
});
} }
fn create_bar( fn create_and_show_bar(
app: Application, app: Application,
display: &Display, display: &Display,
monitor_idx: u32, monitor_idx: u32,

View File

@@ -51,7 +51,7 @@ impl ActionCentre {
.build(); .build();
window.init_layer_shell(); window.init_layer_shell();
window.set_layer(Layer::Overlay); window.set_layer(Layer::Top);
window.set_anchor(Edge::Top, true); window.set_anchor(Edge::Top, true);
window.set_anchor(Edge::Bottom, true); window.set_anchor(Edge::Bottom, true);
window.set_anchor(Edge::Right, true); window.set_anchor(Edge::Right, true);
@@ -230,7 +230,7 @@ impl ActionCentre {
let idx = ((network.signal as f32 / 100.0) * (WIFI_ICONS.len() - 1) as f32 + 0.5) as usize; let idx = ((network.signal as f32 / 100.0) * (WIFI_ICONS.len() - 1) as f32 + 0.5) as usize;
widget.set_wifi_icon_from_name(WIFI_ICONS[idx]); widget.set_wifi_icon_from_name(WIFI_ICONS[idx]);
borrowed_wifi_networks_clone.insert(ssid.clone(), widget.clone()); borrowed_wifi_networks_clone.insert(ssid.clone(), widget.clone());
let net_tx = channels.net_tx(); let net_tx = channels.net_tx().unwrap();
widget.connect_clicked(clone!( widget.connect_clicked(clone!(
#[strong] #[strong]
net_tx, net_tx,

77
src/ui/run_menu.rs Normal file
View File

@@ -0,0 +1,77 @@
use gtk::prelude::{BoxExt, GtkWindowExt, WidgetExt};
use gtk::{Align, Application, ApplicationWindow, Orientation, PolicyType, gdk};
use gtk4_layer_shell::{KeyboardMode, Layer, LayerShell};
#[derive(Clone)]
pub struct RunMenu {
window: ApplicationWindow,
}
impl RunMenu {
pub fn create(application: Application) -> Self {
let window = gtk::ApplicationWindow::builder()
.application(&application)
.title("JmbRunMenu")
.default_width(500)
.default_height(300)
.css_classes(["run-menu"])
.build();
window.init_layer_shell();
window.set_layer(Layer::Overlay);
window.set_halign(Align::Center);
window.set_valign(Align::Center);
window.set_size_request(500, 300);
window.set_keyboard_mode(KeyboardMode::Exclusive);
let v_box = gtk::Box::new(Orientation::Vertical, 0);
let entry = gtk::Entry::new();
entry.add_css_class("entry");
let scrolled = gtk::ScrolledWindow::new();
scrolled.set_policy(PolicyType::Never, PolicyType::Automatic);
scrolled.set_vexpand(true);
scrolled.set_valign(Align::Fill);
let content_box = gtk::Box::new(Orientation::Vertical, 0);
content_box.add_css_class("content-box");
for _ in 0..10 {
let label = gtk::Label::new(Some("Test"));
content_box.append(&label);
}
scrolled.set_child(Some(&content_box));
v_box.append(&entry);
v_box.append(&scrolled);
window.set_child(Some(&v_box));
let window_clone = window.clone();
let instance = Self {
window: window_clone,
};
let instance_clone = instance.clone();
let key_controller = gtk::EventControllerKey::new();
key_controller.connect_key_pressed(move |_, key, _, _| {
if key == gdk::Key::Escape {
instance_clone.hide();
glib::Propagation::Stop
} else {
glib::Propagation::Proceed
}
});
window.add_controller(key_controller);
instance
}
pub fn hide(&self) {
self.window.set_visible(false);
}
pub fn show(&self) {
self.window.present();
}
}

View File

@@ -62,20 +62,11 @@ pub fn build(
String::new() String::new()
}; };
let monitor_valid = !monitor.is_empty(); let monitor_valid = !monitor.is_empty();
println!(
"{:?}",
monitors
.item(monitor_idx)
.unwrap()
.downcast::<gdk::Monitor>()
.unwrap()
.connector()
);
if monitor_valid { if monitor_valid {
let _ = channels let _ = channels
.br_tx() .br_tx()
.unwrap()
.send(BrightnessMessage::Request(monitor.clone())); .send(BrightnessMessage::Request(monitor.clone()));
println!("req");
} }
let volume_adjustment = Adjustment::new(60.0, 1.0, 100.0, 1.0, 5.0, 0.0); let volume_adjustment = Adjustment::new(60.0, 1.0, 100.0, 1.0, 5.0, 0.0);
@@ -85,7 +76,7 @@ pub fn build(
volume.imp().second_icon.set_icon_name("bluetooth-symbolic"); volume.imp().second_icon.set_icon_name("bluetooth-symbolic");
volume.imp().second_icon.add_child_css_class("bt-icon"); volume.imp().second_icon.add_child_css_class("bt-icon");
let media_tx = channels.media_tx(); let media_tx = channels.media_tx().unwrap();
let changed_progr = Rc::new(Cell::new(false)); let changed_progr = Rc::new(Cell::new(false));
let changed_progr_clone = changed_progr.clone(); let changed_progr_clone = changed_progr.clone();
volume.connect_value_changed(move |slider, button, _| { volume.connect_value_changed(move |slider, button, _| {
@@ -97,7 +88,7 @@ pub fn build(
)); ));
} }
}); });
let media_tx = channels.media_tx(); let media_tx = channels.media_tx().unwrap();
volume.connect_clicked(move |slider, button, _| { volume.connect_clicked(move |slider, button, _| {
if button.is_active() { if button.is_active() {
let _ = media_tx.send(MediaCommand::Cmd(slider.value() as u8, true, false)); let _ = media_tx.send(MediaCommand::Cmd(slider.value() as u8, true, false));
@@ -110,7 +101,7 @@ pub fn build(
let brightness = IconSlider::new(Some(brightness_adjustment)); let brightness = IconSlider::new(Some(brightness_adjustment));
brightness.imp().main_icon.set_font_icon(""); brightness.imp().main_icon.set_font_icon("");
let br_tx = channels.br_tx(); let br_tx = channels.br_tx().unwrap();
brightness.connect_value_changed(clone!( brightness.connect_value_changed(clone!(
#[strong] #[strong]
monitor, monitor,
@@ -128,7 +119,7 @@ pub fn build(
} }
} }
)); ));
let br_tx = channels.br_tx(); let br_tx = channels.br_tx().unwrap();
brightness.connect_clicked(clone!( brightness.connect_clicked(clone!(
#[strong] #[strong]
monitor, monitor,
@@ -144,7 +135,7 @@ pub fn build(
let caffeine_button = Button::with_label(""); let caffeine_button = Button::with_label("");
caffeine_button.add_css_class("caffeine"); caffeine_button.add_css_class("caffeine");
let idle_tx = channels.idle_tx(); let idle_tx = channels.idle_tx().unwrap();
caffeine_button.connect_clicked(move |button| { caffeine_button.connect_clicked(move |button| {
if button.has_css_class("on") { if button.has_css_class("on") {
let _ = idle_tx.send(IdleMessage::Release); let _ = idle_tx.send(IdleMessage::Release);
@@ -221,7 +212,7 @@ pub fn build(
let wg_button = Button::with_label("wg"); let wg_button = Button::with_label("wg");
wg_button.add_css_class("vpn"); wg_button.add_css_class("vpn");
let net_tx = channels.net_tx(); let net_tx = channels.net_tx().unwrap();
wg_button.connect_clicked(move |_| { wg_button.connect_clicked(move |_| {
let _ = net_tx.send(NetworkCommand::ToggleVpn(String::from("wg"))); let _ = net_tx.send(NetworkCommand::ToggleVpn(String::from("wg")));
}); });
@@ -328,7 +319,7 @@ pub fn build(
let netbird_rx = channels.take_netbird_rx().unwrap(); let netbird_rx = channels.take_netbird_rx().unwrap();
let idle_rx = channels.take_idle_rx().unwrap(); let idle_rx = channels.take_idle_rx().unwrap();
let monitor_clone = monitor.clone(); let monitor_clone = monitor.clone();
let br_tx = channels.br_tx(); let br_tx = channels.br_tx().unwrap();
glib::timeout_add_local(Duration::from_millis(100), move || { glib::timeout_add_local(Duration::from_millis(100), move || {
while let Ok(command) = media_rx.try_recv() { while let Ok(command) = media_rx.try_recv() {
if let MediaCommand::Cmd(vol, mute, bt) = command { if let MediaCommand::Cmd(vol, mute, bt) = command {
@@ -374,7 +365,6 @@ pub fn build(
let _ = let _ =
br_tx.send(BrightnessMessage::Value(monitor_clone.clone(), 20, true)); br_tx.send(BrightnessMessage::Value(monitor_clone.clone(), 20, true));
brightness.set_value(20.0); brightness.set_value(20.0);
println!("Dimmed: {monitor_clone}");
} }
caffeine_button.add_css_class("idle"); caffeine_button.add_css_class("idle");
caffeine_button.remove_css_class("inhibit"); caffeine_button.remove_css_class("inhibit");
@@ -474,8 +464,12 @@ pub fn build(
vpn_button.remove_css_class("login"); vpn_button.remove_css_class("login");
} }
while let Ok(stats) = stats_rx.try_recv() { while let Ok(stats) = stats_rx.try_recv() {
cpu_label.set_label(&format!("{}%", stats.cpu)); if let Some(cpu) = stats.cpu {
ram_label.set_label(&format!("{}%", stats.ram)); cpu_label.set_label(&format!("{}%", cpu));
}
if let Some(ram) = stats.ram {
ram_label.set_label(&format!("{}%", ram));
}
for disk in stats.disks { for disk in stats.disks {
if disk.0.eq("/") { if disk.0.eq("/") {
disk_label.set_label(&format!("{}%", disk.1)); disk_label.set_label(&format!("{}%", disk.1));

5
style/run_menu.scss Normal file
View File

@@ -0,0 +1,5 @@
@use "colors" as *;
.run-menu {
background: $background;
}

View File

@@ -1,3 +1,4 @@
@use "widgets"; @use "widgets";
@use "bar"; @use "bar";
@use "action_centre"; @use "action_centre";
@use "run_menu";