feat(ipc): connect GUI to daemon via D-Bus reload and live status detection
This commit is contained in:
@@ -123,7 +123,14 @@ fn main() -> ExitCode {
|
|||||||
if let Ok(backend) = blocker_platform_linux::kwin::KwinBackend::connect() {
|
if let Ok(backend) = blocker_platform_linux::kwin::KwinBackend::connect() {
|
||||||
println!("backend: kwin — native Wayland windows are visible");
|
println!("backend: kwin — native Wayland windows are visible");
|
||||||
let _keep_connection = watcher_conn;
|
let _keep_connection = watcher_conn;
|
||||||
run_loop(&rx, &schedules, &usage, &context, blocker_platform_linux::Enforcement::Freeze);
|
run_loop(
|
||||||
|
&rx,
|
||||||
|
&schedules,
|
||||||
|
&usage,
|
||||||
|
&context,
|
||||||
|
blocker_platform_linux::Enforcement::Freeze,
|
||||||
|
&path,
|
||||||
|
);
|
||||||
backend.shutdown();
|
backend.shutdown();
|
||||||
return ExitCode::SUCCESS;
|
return ExitCode::SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -188,25 +195,32 @@ fn main() -> ExitCode {
|
|||||||
/// charge, and this consumes it. Two blocked threads and no CPU between events.
|
/// charge, and this consumes it. Two blocked threads and no CPU between events.
|
||||||
fn run_loop(
|
fn run_loop(
|
||||||
rx: &std::sync::mpsc::Receiver<blocker_platform_linux::FocusEvent>,
|
rx: &std::sync::mpsc::Receiver<blocker_platform_linux::FocusEvent>,
|
||||||
schedules: &[blocker_core::model::Schedule],
|
schedules_initial: &[blocker_core::model::Schedule],
|
||||||
usage: &blocker_core::engine::UsageSnapshot,
|
usage: &blocker_core::engine::UsageSnapshot,
|
||||||
context: &blocker_core::engine::PlatformContext,
|
context: &blocker_core::engine::PlatformContext,
|
||||||
enforcement: blocker_platform_linux::Enforcement,
|
enforcement: blocker_platform_linux::Enforcement,
|
||||||
|
db_path: &std::path::Path,
|
||||||
) {
|
) {
|
||||||
let ctx = runner::LoopContext {
|
let mut current_schedules = schedules_initial.to_vec();
|
||||||
schedules,
|
|
||||||
usage,
|
|
||||||
platform_context: context,
|
|
||||||
boot_id: boot::current_boot_id(),
|
|
||||||
enforcement,
|
|
||||||
};
|
|
||||||
let clock = blocker_platform_linux::SystemMonotonic;
|
let clock = blocker_platform_linux::SystemMonotonic;
|
||||||
println!("enforcement: {enforcement:?}; entering the event loop");
|
println!("enforcement: {enforcement:?}; entering the event loop");
|
||||||
'events: loop {
|
'events: loop {
|
||||||
|
let ctx = runner::LoopContext {
|
||||||
|
schedules: ¤t_schedules,
|
||||||
|
usage,
|
||||||
|
platform_context: context,
|
||||||
|
boot_id: boot::current_boot_id(),
|
||||||
|
enforcement,
|
||||||
|
};
|
||||||
let (action, blocked, _) = runner::step(rx, &ctx, &clock, time::now());
|
let (action, blocked, _) = runner::step(rx, &ctx, &clock, time::now());
|
||||||
if blocked > 0 {
|
if blocked > 0 {
|
||||||
println!("blocked an app ({action:?})");
|
println!("blocked an app ({action:?})");
|
||||||
}
|
}
|
||||||
|
if let Ok(store) = blocker_storage::Store::open(db_path) {
|
||||||
|
if let Ok(reloaded) = store.load_schedules() {
|
||||||
|
current_schedules = reloaded;
|
||||||
|
}
|
||||||
|
}
|
||||||
if action == runner::Action::Idle {
|
if action == runner::Action::Idle {
|
||||||
break 'events;
|
break 'events;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,11 +108,13 @@ pub fn step(
|
|||||||
let action = match &event {
|
let action = match &event {
|
||||||
FocusEvent::Activated(_) => Action::Focused,
|
FocusEvent::Activated(_) => Action::Focused,
|
||||||
FocusEvent::Cleared => Action::Unfocused,
|
FocusEvent::Cleared => Action::Unfocused,
|
||||||
|
FocusEvent::ReloadSchedules => Action::Idle,
|
||||||
};
|
};
|
||||||
|
|
||||||
let blocked = match &event {
|
let blocked = match &event {
|
||||||
FocusEvent::Activated(window) => enforce_if_blocked(window, ctx, clock, wall),
|
FocusEvent::Activated(window) => enforce_if_blocked(window, ctx, clock, wall),
|
||||||
FocusEvent::Cleared => 0,
|
FocusEvent::Cleared => 0,
|
||||||
|
FocusEvent::ReloadSchedules => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
(action, blocked, wait)
|
(action, blocked, wait)
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ pub enum FocusEvent {
|
|||||||
Activated(ForegroundWindow),
|
Activated(ForegroundWindow),
|
||||||
/// The focused window closed, so there is no longer a foreground window.
|
/// The focused window closed, so there is no longer a foreground window.
|
||||||
Cleared,
|
Cleared,
|
||||||
|
/// Reload active schedules from storage triggered by UI IPC.
|
||||||
|
ReloadSchedules,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Source of focus events.
|
/// Source of focus events.
|
||||||
|
|||||||
@@ -74,6 +74,16 @@ impl Watcher {
|
|||||||
// thread exits when the connection drops.
|
// thread exits when the connection drops.
|
||||||
let _ = self.sink.send(event);
|
let _ = self.sink.send(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Instruct the daemon to reload schedules from the database immediately.
|
||||||
|
fn reload_schedules(&self) {
|
||||||
|
let _ = self.sink.send(FocusEvent::ReloadSchedules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ping method to check if the daemon is alive.
|
||||||
|
fn ping(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acquire the bus name and export [`Watcher`], returning the live connection.
|
/// Acquire the bus name and export [`Watcher`], returning the live connection.
|
||||||
@@ -115,3 +125,25 @@ pub fn serve(sink: Sender<FocusEvent>) -> Result<Connection, WatcherError> {
|
|||||||
pub fn release(connection: &Connection) {
|
pub fn release(connection: &Connection) {
|
||||||
let _ = connection.release_name(BUS_NAME);
|
let _ = connection.release_name(BUS_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tell the running daemon via D-Bus to reload schedules from storage.
|
||||||
|
pub fn notify_daemon_reload() -> Result<(), zbus::Error> {
|
||||||
|
let conn = Connection::session()?;
|
||||||
|
let proxy =
|
||||||
|
zbus::blocking::Proxy::new(&conn, BUS_NAME, OBJECT_PATH, "vu.valiq.blocker.Watcher")?;
|
||||||
|
let _: () = proxy.call("ReloadSchedules", &())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query if the daemon is running and responding on D-Bus.
|
||||||
|
pub fn is_daemon_running() -> bool {
|
||||||
|
if let Ok(conn) = Connection::session() {
|
||||||
|
if let Ok(proxy) =
|
||||||
|
zbus::blocking::Proxy::new(&conn, BUS_NAME, OBJECT_PATH, "vu.valiq.blocker.Watcher")
|
||||||
|
{
|
||||||
|
let res: zbus::Result<bool> = proxy.call("Ping", &());
|
||||||
|
return res.unwrap_or(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|||||||
+13
-1
@@ -88,6 +88,7 @@ impl BlockerApp {
|
|||||||
}
|
}
|
||||||
let mut store = blocker_storage::Store::open(&path).map_err(|e| e.to_string())?;
|
let mut store = blocker_storage::Store::open(&path).map_err(|e| e.to_string())?;
|
||||||
store.save_schedules(&self.schedules).map_err(|e| e.to_string())?;
|
store.save_schedules(&self.schedules).map_err(|e| e.to_string())?;
|
||||||
|
let _ = blocker_platform_linux::watcher::notify_daemon_reload();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,7 +175,18 @@ impl BlockerApp {
|
|||||||
Tab::Status => {
|
Tab::Status => {
|
||||||
ui.heading("Current Status");
|
ui.heading("Current Status");
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
ui.label(format!("Daemon Status: Connected ({})", self.status_message));
|
let daemon_live = blocker_platform_linux::watcher::is_daemon_running();
|
||||||
|
if daemon_live {
|
||||||
|
ui.colored_label(
|
||||||
|
egui::Color32::from_rgb(0, 200, 100),
|
||||||
|
"Daemon Status: Running (D-Bus connected)",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ui.colored_label(
|
||||||
|
egui::Color32::from_rgb(220, 80, 80),
|
||||||
|
"Daemon Status: Inactive (not detected on D-Bus)",
|
||||||
|
);
|
||||||
|
}
|
||||||
ui.label(format!("Active Schedules: {}", self.schedules.len()));
|
ui.label(format!("Active Schedules: {}", self.schedules.len()));
|
||||||
ui.add_space(16.0);
|
ui.add_space(16.0);
|
||||||
if ui.button("Test Block Notification").clicked() {
|
if ui.button("Test Block Notification").clicked() {
|
||||||
|
|||||||
Reference in New Issue
Block a user