cleanup, but file watching breaks
This commit is contained in:
parent
1a12940829
commit
1f6fe840fc
8 changed files with 275 additions and 195 deletions
42
Cargo.lock
generated
42
Cargo.lock
generated
|
@ -335,9 +335,11 @@ dependencies = [
|
|||
"hotwatch",
|
||||
"ipc-channel",
|
||||
"maplit",
|
||||
"num",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"serde",
|
||||
"stoppable_thread",
|
||||
"structopt",
|
||||
"try_match",
|
||||
]
|
||||
|
@ -1064,6 +1066,20 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab3e176191bc4faad357e3122c4747aa098ac880e88b168f106386128736cf4a"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.3.0"
|
||||
|
@ -1075,6 +1091,15 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05ad05bd8977050b171b3f6b48175fea6e0565b7981059b486075e1026a9fb5"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.43"
|
||||
|
@ -1085,6 +1110,17 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.3.0"
|
||||
|
@ -1457,6 +1493,12 @@ version = "1.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
|
||||
|
||||
[[package]]
|
||||
name = "stoppable_thread"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6700c1ed393aa9c1fff950032a64a4856436dadee820641ce1b914cab65019f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
|
|
|
@ -26,6 +26,8 @@ grass = "0.10"
|
|||
hotwatch = "0.4"
|
||||
calloop = "0.6"
|
||||
crossbeam-channel = "0.4"
|
||||
num = "0.3"
|
||||
stoppable_thread = "0.2"
|
||||
|
||||
#thiserror = "1.0"
|
||||
|
||||
|
|
112
src/app.rs
Normal file
112
src/app.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
use crate::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EwwEvent {
|
||||
UserCommand(Opt),
|
||||
ReloadConfig(config::EwwConfig),
|
||||
ReloadCss(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct App {
|
||||
pub eww_state: EwwState,
|
||||
pub eww_config: config::EwwConfig,
|
||||
pub windows: HashMap<String, gtk::Window>,
|
||||
pub css_provider: gtk::CssProvider,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn handle_user_command(&mut self, opts: Opt) -> Result<()> {
|
||||
match opts.action {
|
||||
OptAction::Update { fieldname, value } => self.update_state(fieldname, value),
|
||||
OptAction::OpenWindow { window_name } => self.open_window(&window_name)?,
|
||||
OptAction::CloseWindow { window_name } => self.close_window(&window_name)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: EwwEvent) {
|
||||
let result: Result<_> = try {
|
||||
match event {
|
||||
EwwEvent::UserCommand(command) => self.handle_user_command(command)?,
|
||||
EwwEvent::ReloadConfig(config) => self.reload_all_windows(config)?,
|
||||
EwwEvent::ReloadCss(css) => self.load_css(&css)?,
|
||||
}
|
||||
};
|
||||
if let Err(err) = result {
|
||||
eprintln!("Error while handling event: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_state(&mut self, fieldname: String, value: PrimitiveValue) {
|
||||
self.eww_state.update_value(fieldname, value);
|
||||
}
|
||||
|
||||
fn close_window(&mut self, window_name: &str) -> Result<()> {
|
||||
let window = self
|
||||
.windows
|
||||
.get(window_name)
|
||||
.context(format!("No window with name '{}' is running.", window_name))?;
|
||||
window.close();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open_window(&mut self, window_name: &str) -> Result<()> {
|
||||
let window_def = self
|
||||
.eww_config
|
||||
.get_windows()
|
||||
.get(window_name)
|
||||
.context(format!("No window named '{}' defined", window_name))?
|
||||
.clone();
|
||||
|
||||
let window = gtk::Window::new(gtk::WindowType::Popup);
|
||||
window.set_title("Eww");
|
||||
window.set_wmclass("noswallow", "noswallow");
|
||||
window.set_type_hint(gdk::WindowTypeHint::Dock);
|
||||
window.set_position(gtk::WindowPosition::Center);
|
||||
window.set_default_size(window_def.size.0, window_def.size.1);
|
||||
window.set_size_request(window_def.size.0, window_def.size.1);
|
||||
window.set_decorated(false);
|
||||
window.set_resizable(false);
|
||||
|
||||
let empty_local_state = HashMap::new();
|
||||
let root_widget = &widgets::element_to_gtk_thing(
|
||||
&self.eww_config.get_widgets(),
|
||||
&mut self.eww_state,
|
||||
&empty_local_state,
|
||||
&window_def.widget,
|
||||
)?;
|
||||
root_widget.get_style_context().add_class(window_name);
|
||||
window.add(root_widget);
|
||||
|
||||
window.show_all();
|
||||
|
||||
let gdk_window = window.get_window().context("couldn't get gdk window from gtk window")?;
|
||||
gdk_window.set_override_redirect(true);
|
||||
gdk_window.move_(window_def.position.0, window_def.position.1);
|
||||
gdk_window.show();
|
||||
gdk_window.raise();
|
||||
window.set_keep_above(true);
|
||||
|
||||
self.windows.insert(window_name.to_string(), window);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> {
|
||||
self.eww_config = config;
|
||||
self.eww_state.clear_callbacks();
|
||||
let windows = self.windows.clone();
|
||||
for (window_name, window) in windows {
|
||||
window.close();
|
||||
self.open_window(&window_name)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_css(&mut self, css: &str) -> Result<()> {
|
||||
self.css_provider.load_from_data(css.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -9,13 +9,6 @@ use std::convert::TryFrom;
|
|||
pub mod element;
|
||||
pub mod hocon_ext;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EwwConfig {
|
||||
widgets: HashMap<String, WidgetDefinition>,
|
||||
windows: HashMap<String, EwwWindowDefinition>,
|
||||
default_vars: HashMap<String, PrimitiveValue>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! try_type {
|
||||
($typ:ty; $code:expr) => {{
|
||||
|
@ -28,8 +21,20 @@ macro_rules! try_type {
|
|||
}};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EwwConfig {
|
||||
widgets: HashMap<String, WidgetDefinition>,
|
||||
windows: HashMap<String, EwwWindowDefinition>,
|
||||
default_vars: HashMap<String, PrimitiveValue>,
|
||||
}
|
||||
|
||||
impl EwwConfig {
|
||||
pub fn from_hocon(hocon: &Hocon) -> Result<EwwConfig> {
|
||||
pub fn read_from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
EwwConfig::from_hocon(&parse_hocon(&content)?)
|
||||
}
|
||||
|
||||
pub fn from_hocon(hocon: &Hocon) -> Result<Self> {
|
||||
let data = hocon.as_hash()?;
|
||||
|
||||
let widgets = data
|
||||
|
|
|
@ -26,6 +26,15 @@ impl EwwState {
|
|||
..EwwState::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_callbacks(&mut self) {
|
||||
self.on_change_handlers.clear();
|
||||
}
|
||||
|
||||
pub fn get_command_polling_uses(&self) -> Vec<&CommandPollingUse> {
|
||||
self.polling_commands.iter().map(|(x, _)| x).collect()
|
||||
}
|
||||
|
||||
pub fn update_value(&mut self, key: String, value: PrimitiveValue) {
|
||||
if let Some(handlers) = self.on_change_handlers.get(&key) {
|
||||
for on_change in handlers {
|
||||
|
|
271
src/main.rs
271
src/main.rs
|
@ -4,24 +4,24 @@ extern crate gio;
|
|||
extern crate gtk;
|
||||
|
||||
use anyhow::*;
|
||||
use eww_state::*;
|
||||
use gdk::*;
|
||||
use grass;
|
||||
use gtk::prelude::*;
|
||||
use hotwatch;
|
||||
use ipc_channel::ipc;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use structopt::StructOpt;
|
||||
use value::PrimitiveValue;
|
||||
|
||||
pub mod app;
|
||||
pub mod config;
|
||||
pub mod eww_state;
|
||||
pub mod util;
|
||||
pub mod value;
|
||||
pub mod widgets;
|
||||
|
||||
use eww_state::*;
|
||||
use hotwatch;
|
||||
use value::PrimitiveValue;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! build {
|
||||
($var_name:ident = $value:expr ; $code:block) => {{
|
||||
|
@ -31,6 +31,15 @@ macro_rules! build {
|
|||
}};
|
||||
}
|
||||
|
||||
macro_rules! try_logging_errors {
|
||||
($context:literal => $code:block) => {{
|
||||
let result: Result<_> = try { $code };
|
||||
if let Err(err) = result {
|
||||
eprintln!("Error while {}: {:?}", $context, err);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = try_main() {
|
||||
eprintln!("{:?}", e);
|
||||
|
@ -38,15 +47,15 @@ fn main() {
|
|||
}
|
||||
|
||||
#[derive(StructOpt, Debug, Serialize, Deserialize)]
|
||||
struct Opt {
|
||||
pub struct Opt {
|
||||
#[structopt(short = "-c", parse(from_os_str))]
|
||||
config_file: Option<path::PathBuf>,
|
||||
config_file: Option<PathBuf>,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
action: OptAction,
|
||||
}
|
||||
#[derive(StructOpt, Debug, Serialize, Deserialize)]
|
||||
enum OptAction {
|
||||
pub enum OptAction {
|
||||
#[structopt(name = "update")]
|
||||
Update { fieldname: String, value: PrimitiveValue },
|
||||
|
||||
|
@ -57,13 +66,6 @@ enum OptAction {
|
|||
CloseWindow { window_name: String },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum EwwEvent {
|
||||
UserCommand(Opt),
|
||||
ReloadConfig(config::EwwConfig),
|
||||
ReloadCss(String),
|
||||
}
|
||||
|
||||
fn try_main() -> Result<()> {
|
||||
let opts: Opt = StructOpt::from_args();
|
||||
if let Ok(sender) = find_server_process() {
|
||||
|
@ -79,10 +81,10 @@ fn find_server_process() -> Result<ipc::IpcSender<Opt>> {
|
|||
Ok(ipc::IpcSender::connect(instance_path)?)
|
||||
}
|
||||
|
||||
fn get_config_file_path() -> path::PathBuf {
|
||||
fn get_config_file_path() -> PathBuf {
|
||||
std::env::var("XDG_CONFIG_HOME")
|
||||
.map(|v| path::PathBuf::from(v))
|
||||
.unwrap_or_else(|_| path::PathBuf::from(std::env::var("HOME").unwrap()).join(".config"))
|
||||
.map(|v| PathBuf::from(v))
|
||||
.unwrap_or_else(|_| PathBuf::from(std::env::var("HOME").unwrap()).join(".config"))
|
||||
.join("eww")
|
||||
.join("eww.conf")
|
||||
}
|
||||
|
@ -97,90 +99,33 @@ fn initialize_server(opts: Opt) -> Result<()> {
|
|||
.to_path_buf();
|
||||
let scss_file_path = config_dir.join("eww.scss");
|
||||
|
||||
let (watcher_tx, watcher_rx) = crossbeam_channel::unbounded();
|
||||
|
||||
let mut hotwatch = hotwatch::Hotwatch::new()?;
|
||||
hotwatch.watch(
|
||||
config_file_path.clone(),
|
||||
glib::clone!(@strong watcher_tx => move |evt| watcher_tx.send(evt).unwrap()),
|
||||
)?;
|
||||
|
||||
if let Err(e) = hotwatch.watch(
|
||||
scss_file_path.clone(),
|
||||
glib::clone!(@strong watcher_tx => move |evt| watcher_tx.send(evt).unwrap()),
|
||||
) {
|
||||
eprintln!("WARN: error while loading CSS file for hot-reloading: \n{}", e)
|
||||
}
|
||||
|
||||
let config_content = std::fs::read_to_string(config_file_path.clone())?;
|
||||
let scss_content = std::fs::read_to_string(scss_file_path.clone()).unwrap_or_default();
|
||||
|
||||
let eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(&config_content)?)?;
|
||||
let eww_css =
|
||||
grass::from_string(scss_content, &grass::Options::default()).map_err(|err| anyhow!("SCSS parsing error: {:?}", err))?;
|
||||
let eww_config = config::EwwConfig::read_from_file(&config_file_path)?;
|
||||
|
||||
gtk::init()?;
|
||||
|
||||
let mut app = App {
|
||||
let mut app = app::App {
|
||||
eww_state: EwwState::from_default_vars(eww_config.get_default_vars().clone()),
|
||||
eww_config,
|
||||
eww_css: eww_css.clone(),
|
||||
windows: HashMap::new(),
|
||||
css_provider: gtk::CssProvider::new(),
|
||||
};
|
||||
|
||||
gdk::Screen::get_default().map(|screen| {
|
||||
if let Some(screen) = gdk::Screen::get_default() {
|
||||
gtk::StyleContext::add_provider_for_screen(&screen, &app.css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
});
|
||||
}
|
||||
|
||||
app.load_css(&eww_css)?;
|
||||
if let Ok(eww_css) = util::parse_scss_from_file(&scss_file_path) {
|
||||
app.load_css(&eww_css)?;
|
||||
}
|
||||
|
||||
// run the command that eww was started with
|
||||
app.handle_user_command(opts)?;
|
||||
|
||||
let (send, recv) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
std::thread::spawn({
|
||||
let send = send.clone();
|
||||
{
|
||||
move || {
|
||||
let result: Result<_> = try {
|
||||
loop {
|
||||
let (ipc_server, instance_path): (ipc::IpcOneShotServer<Opt>, _) = ipc::IpcOneShotServer::new()?;
|
||||
std::fs::write("/tmp/eww-instance-path", instance_path)?;
|
||||
let (_, initial) = ipc_server.accept()?;
|
||||
send.send(EwwEvent::UserCommand(initial))?;
|
||||
}
|
||||
};
|
||||
if let Err(err) = result {
|
||||
eprintln!("error in server thread: {}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
std::thread::spawn(move || {
|
||||
while let Ok(event) = watcher_rx.recv() {
|
||||
let result: Result<_> = try {
|
||||
match event {
|
||||
hotwatch::Event::Write(path) | hotwatch::Event::NoticeWrite(path) if path == config_file_path => {
|
||||
let config_content = std::fs::read_to_string(path).unwrap_or_default();
|
||||
let new_eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(&config_content)?)?;
|
||||
send.send(EwwEvent::ReloadConfig(new_eww_config))?;
|
||||
}
|
||||
hotwatch::Event::Write(path) if path == scss_file_path => {
|
||||
let scss_content = std::fs::read_to_string(scss_file_path.clone()).unwrap_or_default();
|
||||
let eww_css = grass::from_string(scss_content, &grass::Options::default())
|
||||
.map_err(|err| anyhow!("SCSS parsing error: {:?}", err))?;
|
||||
send.send(EwwEvent::ReloadCss(eww_css))?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
if let Err(err) = result {
|
||||
eprintln!("error in file watcher thread: {}", err);
|
||||
}
|
||||
}
|
||||
});
|
||||
let (evt_send, evt_recv) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
run_server_thread(evt_send.clone());
|
||||
run_filewatch_thread(&config_file_path, &scss_file_path, evt_send.clone())?;
|
||||
|
||||
recv.attach(None, move |msg| {
|
||||
evt_recv.attach(None, move |msg| {
|
||||
app.handle_event(msg);
|
||||
glib::Continue(true)
|
||||
});
|
||||
|
@ -190,106 +135,62 @@ fn initialize_server(opts: Opt) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct App {
|
||||
eww_state: EwwState,
|
||||
eww_config: config::EwwConfig,
|
||||
eww_css: String,
|
||||
windows: HashMap<String, gtk::Window>,
|
||||
css_provider: gtk::CssProvider,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn handle_user_command(&mut self, opts: Opt) -> Result<()> {
|
||||
match opts.action {
|
||||
OptAction::Update { fieldname, value } => self.update_state(fieldname, value),
|
||||
OptAction::OpenWindow { window_name } => self.open_window(&window_name)?,
|
||||
OptAction::CloseWindow { window_name } => self.close_window(&window_name)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_state(&mut self, fieldname: String, value: PrimitiveValue) {
|
||||
self.eww_state.update_value(fieldname, value);
|
||||
}
|
||||
|
||||
fn close_window(&mut self, window_name: &str) -> Result<()> {
|
||||
let window = self
|
||||
.windows
|
||||
.get(window_name)
|
||||
.context(format!("No window with name '{}' is running.", window_name))?;
|
||||
window.close();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open_window(&mut self, window_name: &str) -> Result<()> {
|
||||
let window_def = self
|
||||
.eww_config
|
||||
.get_windows()
|
||||
.get(window_name)
|
||||
.context(format!("No window named '{}' defined", window_name))?
|
||||
.clone();
|
||||
|
||||
let window = gtk::Window::new(gtk::WindowType::Popup);
|
||||
window.set_title("Eww");
|
||||
window.set_wmclass("noswallow", "noswallow");
|
||||
window.set_type_hint(gdk::WindowTypeHint::Dock);
|
||||
window.set_position(gtk::WindowPosition::Center);
|
||||
window.set_default_size(window_def.size.0, window_def.size.1);
|
||||
window.set_size_request(window_def.size.0, window_def.size.1);
|
||||
window.set_decorated(false);
|
||||
window.set_resizable(false);
|
||||
|
||||
let empty_local_state = HashMap::new();
|
||||
let root_widget = &widgets::element_to_gtk_thing(
|
||||
&self.eww_config.get_widgets(),
|
||||
&mut self.eww_state,
|
||||
&empty_local_state,
|
||||
&window_def.widget,
|
||||
)?;
|
||||
root_widget.get_style_context().add_class(window_name);
|
||||
window.add(root_widget);
|
||||
|
||||
window.show_all();
|
||||
|
||||
let gdk_window = window.get_window().context("couldn't get gdk window from gtk window")?;
|
||||
gdk_window.set_override_redirect(true);
|
||||
gdk_window.move_(window_def.position.0, window_def.position.1);
|
||||
gdk_window.show();
|
||||
gdk_window.raise();
|
||||
window.set_keep_above(true);
|
||||
|
||||
self.windows.insert(window_name.to_string(), window);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> {
|
||||
self.eww_config = config;
|
||||
// TODO this needs to handle removing the callbacks to the old gtk windows, as otherwise this might by horribly fucked.
|
||||
let windows = self.windows.clone();
|
||||
for (window_name, window) in windows {
|
||||
window.close();
|
||||
self.open_window(&window_name)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_css(&mut self, css: &str) -> Result<()> {
|
||||
self.css_provider.load_from_data(css.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, event: EwwEvent) {
|
||||
fn run_server_thread(evt_send: glib::Sender<app::EwwEvent>) -> std::thread::JoinHandle<()> {
|
||||
std::thread::spawn(move || {
|
||||
let result: Result<_> = try {
|
||||
match event {
|
||||
EwwEvent::UserCommand(command) => self.handle_user_command(command)?,
|
||||
EwwEvent::ReloadConfig(config) => self.reload_all_windows(config)?,
|
||||
EwwEvent::ReloadCss(css) => self.load_css(&css)?,
|
||||
loop {
|
||||
let (ipc_server, instance_path): (ipc::IpcOneShotServer<Opt>, _) = ipc::IpcOneShotServer::new()?;
|
||||
std::fs::write("/tmp/eww-instance-path", instance_path)?;
|
||||
let (_, initial) = ipc_server.accept()?;
|
||||
evt_send.send(app::EwwEvent::UserCommand(initial))?;
|
||||
}
|
||||
};
|
||||
if let Err(err) = result {
|
||||
eprintln!("Error while handling event: {:?}", err);
|
||||
eprintln!("error in server thread: {}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn run_filewatch_thread<P: AsRef<Path>>(
|
||||
config_file_path: P,
|
||||
scss_file_path: P,
|
||||
evt_send: glib::Sender<app::EwwEvent>,
|
||||
) -> Result<()> {
|
||||
let mut hotwatch = hotwatch::Hotwatch::new()?;
|
||||
hotwatch.watch_file_changes(
|
||||
config_file_path,
|
||||
glib::clone!(@strong evt_send => move |path| {
|
||||
try_logging_errors!("handling change of config file" => {
|
||||
let new_eww_config = config::EwwConfig::read_from_file(path)?;
|
||||
evt_send.send(app::EwwEvent::ReloadConfig(new_eww_config))?;
|
||||
});
|
||||
}),
|
||||
)?;
|
||||
|
||||
let result = hotwatch.watch_file_changes(scss_file_path, move |path| {
|
||||
try_logging_errors!("handling change of scss file" => {
|
||||
let eww_css = util::parse_scss_from_file(path)?;
|
||||
evt_send.send(app::EwwEvent::ReloadCss(eww_css))?;
|
||||
})
|
||||
});
|
||||
if let Err(e) = result {
|
||||
eprintln!("WARN: error while loading CSS file for hot-reloading: \n{}", e)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[extend::ext(pub)]
|
||||
impl hotwatch::Hotwatch {
|
||||
fn watch_file_changes<P, F>(&mut self, path: P, callback: F) -> Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
F: 'static + Fn(PathBuf) + Send,
|
||||
{
|
||||
let result = self.watch(path, move |evt| match evt {
|
||||
hotwatch::Event::Write(path) | hotwatch::Event::NoticeWrite(path) => callback(path),
|
||||
_ => {}
|
||||
});
|
||||
Ok(result?)
|
||||
}
|
||||
}
|
||||
|
|
9
src/util.rs
Normal file
9
src/util.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use anyhow::*;
|
||||
use grass;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn parse_scss_from_file<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||
let scss_content = std::fs::read_to_string(path)?;
|
||||
grass::from_string(scss_content, &grass::Options::default())
|
||||
.map_err(|err| anyhow!("encountered SCSS parsing error: {:?}", err))
|
||||
}
|
|
@ -113,8 +113,8 @@ pub enum AttrValue {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CommandPollingUse {
|
||||
command: String,
|
||||
interval: std::time::Duration,
|
||||
pub command: String,
|
||||
pub interval: std::time::Duration,
|
||||
}
|
||||
|
||||
impl AttrValue {
|
||||
|
|
Loading…
Add table
Reference in a new issue