Skip to content

Commit

Permalink
Add an ability to run external modules
Browse files Browse the repository at this point in the history
  • Loading branch information
2e3s committed Nov 25, 2023
1 parent 864d1dc commit ae1e320
Show file tree
Hide file tree
Showing 8 changed files with 431 additions and 85 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
src/bundle/logo.argb32
lcov.info
50 changes: 31 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ image = { version = "0.24.6" }
members = ["watchers"]

[workspace.package]
version = "0.2.2-beta2"
version = "0.2.3"

[workspace.dependencies]
anyhow = "1.0.75"
log = { version = "0.4.20", features = ["std"] }
tokio = { version = "1.32.0" }
serde = "1.0.193"

[dev-dependencies]
rstest = "0.18.2"
tempfile = "3.8.1"

[dependencies]
watchers = { path = "./watchers", default-features = false }
Expand All @@ -39,12 +44,13 @@ ksni = {version = "0.2.1", optional = true}
aw-server = { git = "https://github.com/ActivityWatch/aw-server-rust", optional = true, rev = "448312d" }
aw-datastore = { git = "https://github.com/ActivityWatch/aw-server-rust", optional = true, rev = "448312d" }
open = { version = "5.0.0", optional = true }
serde = { workspace = true, optional = true }

[features]
default = ["gnome", "kwin_window"]
gnome = ["watchers/gnome"]
kwin_window = ["watchers/kwin_window"]
bundle = ["ksni", "aw-server", "aw-datastore", "open"]
bundle = ["ksni", "aw-server", "aw-datastore", "open", "serde"]

[package.metadata.deb]
features = ["bundle"]
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ The binaries for the bundle, bundled DEB and ActivityWatch watchers replacement

### Bundle with built-in ActivityWatch

This is a single binary to run **awatcher** with the server without changing system and ActivityWatch configuration,
when only tracking activity windows and idle state is needed.
This is a single binary to run **awatcher** with the server without changing system and ActivityWatch configuration.
The bundle is **aw-server-rust** and **awatcher** as a single executable.
The data storage is compatible with ActivityWatch and **aw-server-rust** (**aw-server** has a different storage),
so this can later be run as a module for ActivityWatch.
The data storage is compatible with ActivityWatch and **aw-server-rust** (**aw-server** has a different storage), so this can later be run as a module for ActivityWatch.

External modules are run like in the original ActivityWatch distribution
by looking at `$PATH` and running all binaries which start with `aw-`.
They are controled from the tray, no additional configuration is necessary.

## Supported environments

Expand Down
46 changes: 8 additions & 38 deletions src/bundle.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,26 @@
mod menu;
mod modules;
mod server;

pub use menu::Tray;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::path::PathBuf;
use tokio::sync::mpsc::UnboundedSender;

fn get_config_watchers(config_path: &Path) -> Option<Vec<String>> {
let mut config_path = config_path.parent()?.to_path_buf();
config_path.push("bundle-config.toml");
debug!("Reading bundle config at {}", config_path.display());

let config_content = std::fs::read_to_string(&config_path).ok()?;
let toml_content: toml::Value = toml::from_str(&config_content).ok()?;

trace!("Bundle config: {toml_content:?}");

Some(
toml_content
.get("watchers")?
.get("autostart")?
.as_array()?
.iter()
.filter_map(|value| value.as_str())
.map(std::string::ToString::to_string)
.collect(),
)
}

pub async fn run(
host: String,
port: u32,
config_file: PathBuf,
no_tray: bool,
shutdown_sender: UnboundedSender<()>,
) {
let watchers: Vec<String> =
get_config_watchers(config_file.parent().unwrap()).unwrap_or_default();

for watcher in &watchers {
debug!("Starting an external watcher {}", watcher);
let _ = Command::new(watcher).spawn();
}
let manager = modules::Manager::new(
&std::env::var("PATH").unwrap_or_default(),
config_file.parent().unwrap(),
);

if !no_tray {
let service = ksni::TrayService::new(Tray::new(
host,
port,
config_file,
shutdown_sender,
watchers,
));
let tray = Tray::new(host, port, config_file, shutdown_sender, manager);
let service = ksni::TrayService::new(tray);
service.spawn();
}

Expand Down
62 changes: 41 additions & 21 deletions src/bundle/menu.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use std::collections::HashMap;
use std::path::PathBuf;

use tokio::sync::mpsc::UnboundedSender;

#[derive(Debug)]
use super::modules::Manager;

pub struct Tray {
server_host: String,
server_port: u32,
config_file: PathBuf,
shutdown_sender: UnboundedSender<()>,
watchers: Vec<String>,
watchers_manager: Manager,
checks: HashMap<PathBuf, bool>,
}

impl Tray {
Expand All @@ -17,14 +20,21 @@ impl Tray {
server_port: u32,
config_file: PathBuf,
shutdown_sender: UnboundedSender<()>,
watchers: Vec<String>,
watchers_manager: Manager,
) -> Self {
let checks = watchers_manager
.path_watchers
.iter()
.map(|watcher| (watcher.path().to_owned(), watcher.started()))
.collect();

Self {
server_host,
server_port,
config_file,
shutdown_sender,
watchers,
watchers_manager,
checks,
}
}
}
Expand All @@ -38,9 +48,14 @@ impl ksni::Tray for Tray {
}]
}

fn id(&self) -> String {
"awatcher-bundle".into()
}

fn title(&self) -> String {
"Awatcher".into()
}

fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
let mut watchers_submenu: Vec<ksni::MenuItem<Self>> = vec![
ksni::menu::CheckmarkItem {
Expand All @@ -59,12 +74,23 @@ impl ksni::Tray for Tray {
}
.into(),
];
for watcher in &self.watchers {
for watcher in &self.watchers_manager.path_watchers {
let path = watcher.path().to_owned();

watchers_submenu.push(
ksni::menu::CheckmarkItem {
label: watcher.clone(),
enabled: false,
checked: true,
label: watcher.name(),
enabled: true,
checked: watcher.started(),
activate: Box::new(move |this: &mut Self| {
let current_checked = *this.checks.get(&path).unwrap_or(&false);
this.checks.insert(path.clone(), !current_checked);
if current_checked {
this.watchers_manager.stop_watcher(&path);
} else {
this.watchers_manager.start_watcher(&path);
}
}),
..Default::default()
}
.into(),
Expand All @@ -76,13 +102,11 @@ impl ksni::Tray for Tray {
label: "ActivityWatch".into(),
// https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
icon_name: "document-properties".into(),
activate: {
let url = format!("http://{}:{}", self.server_host, self.server_port);
activate: Box::new(move |this: &mut Self| {
let url = format!("http://{}:{}", this.server_host, this.server_port);

Box::new(move |_| {
open::that(&url).unwrap();
})
},
open::that(url).unwrap();
}),
..Default::default()
}
.into(),
Expand All @@ -108,13 +132,9 @@ impl ksni::Tray for Tray {
ksni::menu::StandardItem {
label: "Exit".into(),
icon_name: "application-exit".into(),
activate: {
let shutdown_sender = self.shutdown_sender.clone();

Box::new(move |_| {
shutdown_sender.send(()).unwrap();
})
},
activate: Box::new(move |this: &mut Self| {
this.shutdown_sender.send(()).unwrap();
}),
..Default::default()
}
.into(),
Expand Down
Loading

0 comments on commit ae1e320

Please sign in to comment.