diff --git a/Cargo.toml b/Cargo.toml index 8810b80..6a4099f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,13 @@ [package] name = "wsnotify" version = "0.1.0" -authors = ["Tomasz Cichocki "] +authors = ["Tomasz Cichocki "] [dependencies] notify = "4.0.0" -app_dirs = "^1.1.1" \ No newline at end of file +app_dirs = "^1.1.1" +systray = { git = "https://github.com/tocisz/systray-rs" } +# systray = { path = "../systray-rs" } + +[build-dependencies] +embed-resource = { git = "https://github.com/nabijaczleweli/rust-embed-resource" } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..bbc20de --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +extern crate embed_resource; + +fn main() { + embed_resource::compile("resource/icons.res"); +} \ No newline at end of file diff --git a/resource/icons.res b/resource/icons.res new file mode 100644 index 0000000..f852f72 --- /dev/null +++ b/resource/icons.res @@ -0,0 +1,3 @@ +Delete ICON "if_Delete_132746.ico" +Stop ICON "if_No-entry_132776.ico" +OK ICON "if_OK_132710.ico" \ No newline at end of file diff --git a/resource/if_Delete_132746.ico b/resource/if_Delete_132746.ico new file mode 100644 index 0000000..8b1ef11 Binary files /dev/null and b/resource/if_Delete_132746.ico differ diff --git a/resource/if_No-entry_132776.ico b/resource/if_No-entry_132776.ico new file mode 100644 index 0000000..fa48d6a Binary files /dev/null and b/resource/if_No-entry_132776.ico differ diff --git a/resource/if_OK_132710.ico b/resource/if_OK_132710.ico new file mode 100644 index 0000000..0ae0172 Binary files /dev/null and b/resource/if_OK_132710.ico differ diff --git a/src/main.rs b/src/main.rs index 1e83bd4..f6cf789 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ extern crate notify; use notify::{Watcher, RecursiveMode, RawEvent, raw_watcher}; use notify::op::Op; -use std::sync::mpsc::channel; +use std::sync::mpsc::{channel,Sender}; extern crate app_dirs; use app_dirs::*; @@ -12,7 +12,10 @@ use std::path::Path; use std::io::Seek; use std::io::SeekFrom; use std::io::Read; -//use std::io::Error; +use std::thread; + +mod ten_minutes; +use ten_minutes::MeterControlMessage; fn read_file_length(path: &std::path::Path) -> Result { match std::fs::metadata(path) { @@ -41,17 +44,23 @@ trait CameraEventWatcher { fn on_image_save(&self, line: &str); } -struct EchoCameraWatcher {} -impl CameraEventWatcher for EchoCameraWatcher { +struct SystrayCameraWatcher { + tx : Sender +} +impl CameraEventWatcher for SystrayCameraWatcher { fn on_camera_start(&self, line: &str) { println!("{}", line); } + fn on_camera_prepare(&self, line: &str) { println!("{}", line); } + fn on_camera_finish(&self, line: &str) { println!("{}", line); + self.tx.send(MeterControlMessage::MarkSafe).ok(); } + fn on_capture_start(&self, line: &str) { println!("{}", line); } @@ -90,12 +99,12 @@ impl FileScanner { fn handle_event(&mut self, op: Op) -> Result<(), String> { if op != notify::op::WRITE { - // TODO support handling logfile rolling + // TODO support handling of logfile rolling return Ok(()); } let len = read_file_length(&self.file_name).map_err( |err| err.to_string())?; - // println!("{:?} {}", op, len); + //println!("{:?} {}", op, len); let mut buffer = vec![0; len-self.last_byte_read].into_boxed_slice(); self.file.seek(SeekFrom::Start(self.last_byte_read as u64)) @@ -129,28 +138,37 @@ impl FileScanner { const APP_INFO: AppInfo = AppInfo{name: "Logs", author: "CrossoverWorkSmart"}; -fn main() { - let mut path = get_app_root(AppDataType::UserConfig, &APP_INFO).unwrap(); - path.push("deskapp.log"); - let path = path.as_path(); - - // Create a channel to receive the events. - let (tx, rx) = channel(); - - // Create a watcher object, delivering raw events. - // The notification back-end is selected based on the platform. - let mut watcher = raw_watcher(tx).unwrap(); - let camera = Box::new(EchoCameraWatcher{}); - let mut scanner = FileScanner::create(path, camera).unwrap(); - - // Add a path to be watched. All files and directories at that path and - // below will be monitored for changes. - watcher.watch(path, RecursiveMode::NonRecursive).unwrap(); - loop { - match rx.recv() { - Ok(RawEvent{path: Some(_path), op: Ok(op), cookie: _cookie}) => scanner.handle_event(op).unwrap(), - Ok(event) => println!("broken event: {:?}", event), - Err(e) => println!("watch error: {:?}", e), +fn create_log_watch_thread(ui_tx: Sender) { + thread::spawn(move || { + let mut path = get_app_root(AppDataType::UserConfig, &APP_INFO).unwrap(); + path.push("deskapp.log"); + let path = path.as_path(); + + // Create a channel to receive the events. + let (tx, rx) = channel(); + + // Create a watcher object, delivering raw events. + // The notification back-end is selected based on the platform. + let mut watcher = raw_watcher(tx).unwrap(); + let camera = Box::new(SystrayCameraWatcher { tx: ui_tx }); + let mut scanner = FileScanner::create(path, camera).unwrap(); + + // Add a path to be watched. All files and directories at that path and + // below will be monitored for changes. + watcher.watch(path, RecursiveMode::NonRecursive).unwrap(); + loop { + match rx.recv() { + Ok(RawEvent { path: Some(_path), op: Ok(op), cookie: _cookie }) => scanner.handle_event(op).unwrap(), + Ok(event) => println!("broken event: {:?}", event), + Err(e) => println!("watch error: {:?}", e), + } } - } + }); +} + +#[cfg(target_os = "windows")] +fn main() { + let (ui_tx, ui_rx) = channel(); + create_log_watch_thread(ui_tx); + ten_minutes::TenMinutesMeter::new(ui_rx).main(); } \ No newline at end of file diff --git a/src/ten_minutes.rs b/src/ten_minutes.rs new file mode 100644 index 0000000..60c3c84 --- /dev/null +++ b/src/ten_minutes.rs @@ -0,0 +1,82 @@ +extern crate systray; + +use std::sync::mpsc::Receiver; + +use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::process; + +pub enum MeterControlMessage { + MarkSafe +} + +pub struct TenMinutesMeter { + app : systray::Application, + rx : Receiver, + last_reminder : u16, +} + +impl TenMinutesMeter { + pub fn new(rx : Receiver) -> TenMinutesMeter { + match systray::Application::new() { + Ok(w) => { + let mut app = w; + TenMinutesMeter::init(&mut app); + TenMinutesMeter { + app, + rx, + last_reminder: 0, + } + }, + Err(_) => panic!("Can't create window!") + } + } + + fn init(app : &mut systray::Application) -> () { + app.set_icon_from_resource(&"Stop".to_string()).ok(); + // app.add_menu_item(&"Print a thing".to_string(), |_| { + // println!("Printing a thing!"); + // }).ok(); + // app.add_menu_separator().ok(); + app.add_menu_item(&"Quit".to_string(), |window| { + window.shutdown().ok(); + window.quit(); + process::exit(0); + }).ok(); + } + + pub fn main(&mut self) -> () { + loop { + match self.app.wait_for_message_timeout(Duration::from_secs(1)) { + Ok(()) => (), + Err(e) => { + println!("{:?}", e); + return; + }, + } + + for m in self.rx.try_iter() { + match m { + MeterControlMessage::MarkSafe => self.mark_safe() + } + } + + let since_epoch = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let reminder = (since_epoch.as_secs() % 600) as u16; + + if self.last_reminder > reminder { + self.mark_unsafe(); + } + + self.last_reminder = reminder; + } + } + + pub fn mark_unsafe(&self) { + self.app.set_icon_from_resource(&"Stop".to_string()).ok(); + } + + pub fn mark_safe(&self) { + self.app.set_icon_from_resource(&"OK".to_string()).ok(); + } +} +