Pass config from main to the relevant places

This commit is contained in:
jmb05
2026-11-02 17:06:42 +01:00
parent de1f731e63
commit cdfc5b8d36
13 changed files with 170 additions and 107 deletions

View File

@@ -1,4 +1,7 @@
[idle]
idle_timeout = 20
dim_timeout = 60
lock_timeout = 300
sleep_timeout = 320
sleep_timeout = 320
lock_command = 'hyprlock'
sleep_command = 'systemctl sleep'

View File

@@ -17,6 +17,7 @@ use crate::backend::json_socket::{JsonSocketBackend, State};
use crate::backend::network::{NetworkBackendHandle, NetworkCommand, NetworkState};
use crate::backend::perf_stats::{Stats, StatsBackend};
use crate::backend::window_manager_hypr::{HyprlandComms, WindowManagerEvent};
use crate::config::Config;
use crate::ctl::ControlCommand;
use crossbeam_channel::{Receiver, Sender};
use jmb_logger_rs::*;
@@ -28,7 +29,7 @@ pub(crate) type Registrator<M> = Sender<Sender<M>>;
pub trait BackendThread<M: Debug + Clone, D> {
fn subscribe(registrator: Registrator<M>) -> (Receiver<M>, D);
fn subscribers(&self) -> &Vec<Sender<M>>;
fn init(registration_rx: Receiver<Sender<M>>);
fn init(self, registration_rx: Receiver<Sender<M>>);
fn publish(&self, message: M) {
trace!(&LOGGER, "Sending: {message:?} to {:?}", self.subscribers());
for tx in self.subscribers() {
@@ -41,16 +42,19 @@ pub trait BackendThread<M: Debug + Clone, D> {
}
}
}
fn start() -> Registrator<M> {
fn start(self) -> Registrator<M>
where
Self: Sized,
{
let (registration_tx, registration_rx) = crossbeam_channel::unbounded::<Sender<M>>();
Self::init(registration_rx);
Self::init(self, registration_rx);
registration_tx
}
}
pub trait SimpleBackendThread<M: Debug + Clone>: BackendThread<M, ()> {
fn subscribers(&self) -> &Vec<Sender<M>>;
fn init(registration_rx: Receiver<Sender<M>>);
fn init(self, registration_rx: Receiver<Sender<M>>);
}
impl<M: Debug + Clone, S: SimpleBackendThread<M>> BackendThread<M, ()> for S {
@@ -64,8 +68,8 @@ impl<M: Debug + Clone, S: SimpleBackendThread<M>> BackendThread<M, ()> for S {
SimpleBackendThread::subscribers(self)
}
fn init(registration_rx: Receiver<Sender<M>>) {
<S as SimpleBackendThread<M>>::init(registration_rx)
fn init(self, registration_rx: Receiver<Sender<M>>) {
<S as SimpleBackendThread<M>>::init(self, registration_rx)
}
}
@@ -95,17 +99,17 @@ pub enum ChannelType {
}
impl Backend {
pub fn start() -> Self {
pub fn start(config: &Config) -> Self {
debug!(&LOGGER, "Starting Backend threads...");
let hypr_registrator = HyprlandComms::start();
let net_registrator = NetworkBackendHandle::start();
let br_registrator = BrightnessManager::start();
let stats_registrator = StatsBackend::start();
let audio_registrator = AudioBackend::start();
let media_registration = MediaBackend::start();
let netbird_registrator = JsonSocketBackend::start();
let idle_registrator = IdleDaemon::start();
let control_registrator = ControlSocket::start();
let hypr_registrator = HyprlandComms::new().start();
let net_registrator = NetworkBackendHandle::new().start();
let br_registrator = BrightnessManager::new().start();
let stats_registrator = StatsBackend::new().start();
let audio_registrator = AudioBackend::new().start();
let media_registration = MediaBackend::new().start();
let netbird_registrator = JsonSocketBackend::new().start();
let idle_registrator = IdleDaemon::new(config.idle.clone()).start();
let control_registrator = ControlSocket::new().start();
debug!(&LOGGER, "Started Backend threads");
Self {
hypr_registrator,

View File

@@ -266,7 +266,16 @@ pub enum AudioMessage {
pub struct AudioBackend {
subscribers: Vec<Sender<AudioMessage>>,
command_tx: Sender<AudioMessage>,
command_tx: Option<Sender<AudioMessage>>,
}
impl AudioBackend {
pub fn new() -> Self {
Self {
subscribers: vec![],
command_tx: None,
}
}
}
impl BackendThread<AudioMessage, Sender<AudioMessage>> for AudioBackend {
@@ -288,12 +297,10 @@ impl BackendThread<AudioMessage, Sender<AudioMessage>> for AudioBackend {
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<AudioMessage>>) {
fn init(mut self, registration_rx: Receiver<Sender<AudioMessage>>) {
let (command_tx, command_rx) = crossbeam_channel::unbounded::<AudioMessage>();
let instance = Arc::new(Mutex::new(Self {
subscribers: vec![],
command_tx,
}));
self.command_tx = Some(command_tx);
let instance = Arc::new(Mutex::new(self));
let (controller_tx, controller_rx) = crossbeam_channel::unbounded::<ControllerMsg>();
let notify = Arc::new(EventFD::new(0, EfdFlags::EFD_CLOEXEC).unwrap());
thread::spawn({
@@ -381,7 +388,8 @@ impl BackendThread<AudioMessage, Sender<AudioMessage>> for AudioBackend {
.lock()
.expect("Couldn't get lock")
.command_tx
.clone(),
.clone()
.unwrap(),
))
.is_ok()
{
@@ -460,13 +468,9 @@ impl BackendThread<MediaMessage, Sender<MediaMessage>> for MediaBackend {
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<MediaMessage>>) {
fn init(self, registration_rx: Receiver<Sender<MediaMessage>>) {
let (tx, rx) = crossbeam_channel::unbounded::<MediaMessage>();
let instance = Arc::new(Mutex::new(Self {
subscribers: vec![],
players: HashMap::new(),
active_player: None,
}));
let instance = Arc::new(Mutex::new(self));
thread::spawn({
let instance = instance.clone();
move || {
@@ -545,6 +549,13 @@ impl BackendThread<MediaMessage, Sender<MediaMessage>> for MediaBackend {
}
impl MediaBackend {
pub fn new() -> Self {
Self {
subscribers: vec![],
players: HashMap::new(),
active_player: None,
}
}
async fn find_existing_players(
connection: &zbus::Connection,
instance: Arc<Mutex<Self>>,

View File

@@ -143,12 +143,9 @@ impl BackendThread<BrightnessMessage, Sender<BrightnessMessage>> for BrightnessM
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<BrightnessMessage>>) {
fn init(self, registration_rx: Receiver<Sender<BrightnessMessage>>) {
let (f2b_tx, f2b_rx) = crossbeam_channel::unbounded::<BrightnessMessage>();
let instance = Arc::new(Mutex::new(Self {
display_cache: HashMap::new(),
subscribers: Vec::new(),
}));
let instance = Arc::new(Mutex::new(self));
let instance_clone = instance.clone();
thread::spawn(move || {
while let Ok(tx) = registration_rx.recv() {
@@ -246,6 +243,12 @@ enum Operation {
}
impl BrightnessManager {
pub fn new() -> Self {
Self {
subscribers: vec![],
display_cache: HashMap::new(),
}
}
pub fn set_brightness(&mut self, connector: String, percent: u8) {
if !self.display_cache.contains_key(&connector) {
self.register_display(connector.clone());

View File

@@ -12,15 +12,21 @@ pub struct ControlSocket {
subscribers: Vec<Sender<ControlCommand>>,
}
impl ControlSocket {
pub fn new() -> Self {
Self {
subscribers: vec![],
}
}
}
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(),
}));
fn init(self, registration_rx: Receiver<Sender<ControlCommand>>) {
let instance = Arc::new(Mutex::new(self));
let instance_clone = instance.clone();
std::thread::spawn(move || {
while let Ok(sender) = registration_rx.recv() {

View File

@@ -30,7 +30,6 @@ struct PerfEventAttr {
sig_data: u64,
}
const PERF_TYPE_RAW: u32 = 4;
const PERF_FORMAT_TOTAL_TIME_ENABLED: u64 = 1;
const PERF_FORMAT_TOTAL_TIME_RUNNING: u64 = 2;
@@ -95,8 +94,8 @@ impl I915Engine {
fn open(name: &str) -> io::Result<Self> {
let base = "/sys/bus/event_source/devices/i915";
let pmu_type = read_number(&format!("{base}/type")).unwrap() as u32;
let config = read_config(&format!("{base}/events/{name}")).unwrap();
let pmu_type = read_number(&format!("{base}/type"))? as u32;
let config = read_config(&format!("{base}/events/{name}"))?;
let mut attr = PerfEventAttr::default();
attr.type_ = pmu_type;

View File

@@ -1,5 +1,5 @@
use crate::backend::{BackendThread, Registrator};
use crate::config::Config;
use crate::config::IdleConfig;
use crate::{APP_NAME, LOGGER};
use crossbeam_channel::{Receiver, Sender};
use jmb_logger_rs::*;
@@ -100,6 +100,7 @@ pub enum IdleMessage {
#[derive(Debug)]
pub struct IdleDaemon {
subscribers: Vec<Sender<IdleState>>,
config: IdleConfig,
}
impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
@@ -121,12 +122,9 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<IdleState>>) {
let config = Config::load();
fn init(self, registration_rx: Receiver<Sender<IdleState>>) {
let (tx, rx) = crossbeam_channel::unbounded::<IdleMessage>();
let instance = Arc::new(Mutex::new(Self {
subscribers: Vec::new(),
}));
let instance = Arc::new(Mutex::new(self));
std::thread::spawn({
let instance = instance.clone();
let tx = tx.clone();
@@ -146,9 +144,9 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
let (wl_tx, wl_rx) = watch::channel(false);
let (dbus_tx, dbus_rx) = watch::channel(false);
std::thread::spawn({
let config = config.clone();
let instance = instance.clone();
move || {
if let Err(err) = Self::start_idle_notifier(wl_tx, &config) {
if let Err(err) = Self::start_idle_notifier(wl_tx, instance) {
error!(&LOGGER, "Idle Daemon unexpectedly quit, reason: {err}");
}
}
@@ -202,7 +200,7 @@ impl BackendThread<IdleState, Sender<IdleMessage>> for IdleDaemon {
let res = IdleDaemon::start_dbus(dbus_tx, instance_clone).await;
match res {
Ok((_, _)) => {
Self::run_idle_timer(instance, wl_rx, dbus_rx, &config).await;
Self::run_idle_timer(instance, wl_rx, dbus_rx).await;
}
Err(err) => error!(&LOGGER, "Error running D-Bus Screensaver: {err}"),
}
@@ -228,7 +226,7 @@ impl IdleStage {
}
}
fn timeout(self, cfg: &Config) -> Duration {
fn timeout(self, cfg: &IdleConfig) -> Duration {
match self {
Self::Dim => Duration::from_secs(cfg.dim_timeout_delta()),
Self::TurnOff => Duration::from_secs(cfg.lock_timeout_delta()),
@@ -238,11 +236,16 @@ impl IdleStage {
}
impl IdleDaemon {
pub fn new(config: IdleConfig) -> Self {
Self {
subscribers: vec![],
config,
}
}
async fn run_idle_timer(
instance: Arc<Mutex<Self>>,
mut wl_rx: watch::Receiver<bool>,
mut dbus_rx: watch::Receiver<bool>,
config: &Config,
) {
loop {
if let Err(e) = wl_rx.changed().await {
@@ -254,12 +257,13 @@ impl IdleDaemon {
if wl_idle && !dbus_state {
let mut stage = IdleStage::Dim;
loop {
let timeout = stage.timeout(&instance.lock().unwrap().config);
tokio::select! {
_ = sleep(stage.timeout(config)) => {
_ = sleep(timeout) => {
match stage {
IdleStage::Dim => instance.lock().unwrap().publish(IdleState::Dimmed),
IdleStage::TurnOff => _ = Command::new("hyprlock").spawn(),
IdleStage::Sleep => _ = Command::new("systemctl").arg("sleep").spawn(),
IdleStage::TurnOff => _ = Command::new("bash").arg("-c").arg(format!("'{}'", instance.lock().unwrap().config.lock_command())).spawn(),
IdleStage::Sleep => _ = Command::new("bash").arg("-c").arg(format!("'{}'", instance.lock().unwrap().config.sleep_command())).spawn(),
}
stage = stage.next();
}
@@ -286,7 +290,10 @@ impl IdleDaemon {
}
}
fn start_idle_notifier(tx: watch::Sender<bool>, config: &Config) -> Result<(), Box<dyn Error>> {
fn start_idle_notifier(
tx: watch::Sender<bool>,
instance: Arc<Mutex<IdleDaemon>>,
) -> Result<(), Box<dyn Error>> {
let conn = Connection::connect_to_env()?;
let (globals, mut event_queue) = registry_queue_init::<WlState>(&conn)?;
let queue_handle = event_queue.handle();
@@ -298,7 +305,7 @@ impl IdleDaemon {
let mut state = WlState { idle: None, tx };
let _notification = notifier.get_idle_notification(
(config.idle_timeout() * 1000) as u32,
(instance.lock().unwrap().config.idle_timeout() * 1000) as u32,
&seat,
&queue_handle,
(),

View File

@@ -37,6 +37,13 @@ impl State {
}
impl JsonSocketBackend {
pub fn new() -> Self {
Self {
subscribers: vec![],
state: initial_state_tailscale(),
socket_address: String::from("/var/run/netbird-http.sock"),
}
}
async fn _watch(instance: Arc<Mutex<Self>>) -> Result<(), Box<dyn Error + Send + Sync>> {
let socket_address = instance
.lock()
@@ -197,14 +204,8 @@ impl BackendThread<State, ()> for JsonSocketBackend {
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<State>>) {
let state = initial_state_tailscale();
let instance = Arc::new(Mutex::new(Self {
subscribers: vec![],
state,
socket_address: String::from("/var/run/netbird-http.sock"),
}));
fn init(self, registration_rx: Receiver<Sender<State>>) {
let instance = Arc::new(Mutex::new(self));
let instance_clone = instance.clone();
std::thread::spawn(move || {

View File

@@ -112,7 +112,7 @@ pub enum NetworkState {
#[derive(Debug, Clone)]
pub struct NetworkBackendHandle {
subscribers: Vec<Sender<NetworkState>>,
command_tx: UnboundedSender<NetworkCommand>,
command_tx: Option<UnboundedSender<NetworkCommand>>,
}
struct EventDebouncer {
@@ -140,12 +140,9 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<NetworkState>>) {
fn init(mut self, registration_rx: Receiver<Sender<NetworkState>>) {
let (command_tx, mut command_rx) = tokio::sync::mpsc::unbounded_channel::<NetworkCommand>();
let instance = Self {
subscribers: vec![],
command_tx,
};
self.command_tx = Some(command_tx);
std::thread::spawn(move || {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
@@ -163,7 +160,7 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
std::thread::spawn({
let nm = nm.clone();
let mut instance = instance.clone();
let mut instance = self.clone();
move || {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
@@ -172,7 +169,7 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
runtime.block_on(async move {
while let Ok(tx) = registration_rx.recv() {
if tx.send(NetworkState::Init(instance.command_tx.clone())).is_ok() {
if tx.send(NetworkState::Init(instance.command_tx.clone().unwrap())).is_ok() {
instance.subscribers.push(tx);
let _ = instance.send_all_networks(&nm).await;
let _ = instance.send_active_connection(&nm).await;
@@ -205,7 +202,7 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
// Run the command without stopping event processing
// while a potentially slow connection operation runs.
let nm = nm.clone();
let instance_clone = instance.clone();
let instance_clone = self.clone();
tokio::spawn(async move {
match command {
@@ -241,7 +238,7 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
match event {
Some(event_res) => {
match event_res {
Ok(event) => instance.handle_event(&nm, event, &mut debouncer).await,
Ok(event) => self.handle_event(&nm, event, &mut debouncer).await,
Err(err) => error!(&LOGGER, "Error: {err:?}"),
}
}
@@ -250,7 +247,7 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
}
_ = &mut debouncer.sleep, if debouncer.deadline.is_some() => {
debouncer.deadline = None;
let _ = instance.send_all_networks(&nm).await;
let _ = self.send_all_networks(&nm).await;
}
}
}
@@ -260,6 +257,12 @@ impl BackendThread<NetworkState, UnboundedSender<NetworkCommand>> for NetworkBac
}
impl NetworkBackendHandle {
pub fn new() -> NetworkBackendHandle {
Self {
subscribers: vec![],
command_tx: None,
}
}
async fn connect(&self, nm: &NetworkManager, ssid: String, creds: WifiSecurity) {
let previous_ssid = nm.current_ssid().await;
let res = nm.connect(&ssid, None, creds).await;

View File

@@ -31,6 +31,14 @@ pub struct StatsBackend {
subscribers: Vec<Sender<Stats>>,
}
impl StatsBackend {
pub fn new() -> Self {
Self {
subscribers: vec![],
}
}
}
impl BackendThread<Stats, ()> for StatsBackend {
fn subscribe(registrator: Registrator<Stats>) -> (Receiver<Stats>, ()) {
let (tx, rx) = crossbeam_channel::unbounded::<Stats>();
@@ -42,10 +50,8 @@ impl BackendThread<Stats, ()> for StatsBackend {
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<Stats>>) {
let instance = Arc::new(Mutex::new(Self {
subscribers: Vec::new(),
}));
fn init(self, registration_rx: Receiver<Sender<Stats>>) {
let instance = Arc::new(Mutex::new(self));
thread::spawn({
let instance = instance.clone();
move || {

View File

@@ -28,11 +28,9 @@ impl SimpleBackendThread<WindowManagerEvent> for HyprlandComms {
&self.subscribers
}
fn init(registration_rx: Receiver<Sender<WindowManagerEvent>>) {
fn init(self, registration_rx: Receiver<Sender<WindowManagerEvent>>) {
std::thread::spawn(move || {
let instance = Arc::new(Mutex::new(Self {
subscribers: vec![],
}));
let instance = Arc::new(Mutex::new(self));
let mut listener = EventListener::new();
let inst_clone = instance.clone();
@@ -153,6 +151,12 @@ impl SimpleBackendThread<WindowManagerEvent> for HyprlandComms {
}
impl HyprlandComms {
pub fn new() -> Self {
Self {
subscribers: vec![],
}
}
fn monitor_id_from_workspace_id(id: WorkspaceId) -> Option<MonitorId> {
if let Ok(wss) = Workspaces::get()
&& let Some(ws) = wss.to_vec().iter().find(|ws| ws.id().unwrap() == id)

View File

@@ -1,23 +1,9 @@
use serde::Deserialize;
use std::fs;
#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Debug, Clone, Default)]
pub(crate) struct Config {
idle_timeout: u64,
dim_timeout: u64,
lock_timeout: u64,
sleep_timeout: u64,
}
impl Default for Config {
fn default() -> Self {
Self {
idle_timeout: 20,
dim_timeout: 60,
lock_timeout: 300,
sleep_timeout: 320,
}
}
pub idle: IdleConfig,
}
impl Config {
@@ -28,7 +14,31 @@ impl Config {
.flatten()
.unwrap_or_default()
}
}
#[derive(Deserialize, Debug, Clone)]
pub(crate) struct IdleConfig {
idle_timeout: u64,
dim_timeout: u64,
lock_timeout: u64,
sleep_timeout: u64,
lock_command: String,
sleep_command: String,
}
impl Default for IdleConfig {
fn default() -> Self {
Self {
idle_timeout: 20,
dim_timeout: 60,
lock_timeout: 300,
sleep_timeout: 320,
..Default::default()
}
}
}
impl IdleConfig {
pub fn idle_timeout(&self) -> u64 {
self.idle_timeout
}
@@ -56,4 +66,12 @@ impl Config {
pub fn sleep_timeout_delta(&self) -> u64 {
self.sleep_timeout - self.lock_timeout_delta()
}
pub fn lock_command(&self) -> &str {
&self.lock_command
}
pub fn sleep_command(&self) -> &str {
&self.sleep_command
}
}

View File

@@ -5,13 +5,13 @@ mod css;
mod ctl;
mod ui;
use crate::config::Config;
use glib::clone;
use gtk::prelude::*;
use gtk::{Application, CssProvider, gdk, gio};
use jmb_logger_rs::*;
use notify::RecommendedWatcher;
use std::cell::RefCell;
use std::process::Command;
use std::rc::Rc;
pub const LOGGER: Logger = Logger::create(Level::DEBUG);
@@ -20,16 +20,14 @@ pub const APP_NAME: &str = "net.jmb19905.jmb-bar";
fn main() {
info!(&LOGGER, "Starting jmb-bar");
gio::resources_register_include!("templates.gresource").expect("Failed to register resources");
let config = Config::load();
let backend = Backend::start(&config);
gio::resources_register_include!("templates.gresource").expect("Failed to register resources");
let app = Application::builder().application_id(APP_NAME).build();
let watcher_slot: Rc<RefCell<Option<RecommendedWatcher>>> = Rc::new(RefCell::new(None));
let _ = Command::new("pkill hypridle").output(); // Kill existing hypridle processes
let backend = Backend::start();
app.connect_startup(move |_| {
debug!(&LOGGER, "Connect startup");
let provider = CssProvider::new();