From d9f1c88b1fccb7fa06f2b6fb3cb820947124d2ef Mon Sep 17 00:00:00 2001 From: Tomasz Cichocki Date: Sun, 4 Feb 2018 23:05:52 +0100 Subject: [PATCH] Add tray icon --- Cargo.toml | 9 +++- build.rs | 5 ++ resource/icons.res | 3 ++ resource/if_Delete_132746.ico | Bin 0 -> 1150 bytes resource/if_No-entry_132776.ico | Bin 0 -> 1150 bytes resource/if_OK_132710.ico | Bin 0 -> 1150 bytes src/main.rs | 76 ++++++++++++++++++----------- src/ten_minutes.rs | 82 ++++++++++++++++++++++++++++++++ 8 files changed, 144 insertions(+), 31 deletions(-) create mode 100644 build.rs create mode 100644 resource/icons.res create mode 100644 resource/if_Delete_132746.ico create mode 100644 resource/if_No-entry_132776.ico create mode 100644 resource/if_OK_132710.ico create mode 100644 src/ten_minutes.rs 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 0000000000000000000000000000000000000000..8b1ef11d524245c6adfb9e9a204dcf37ec903e3f GIT binary patch literal 1150 zcmaKsNl#N@7==HA2@4WH=E2&TqHVRvAQVezX=#HJ1q7`TDisi25VS^2A&S3%h5i9I z?sQ``1{V@zj0+_(k{A|BEt6$P?@+rve($YT2}5t*+qdVO=iJ=eri^9qBQKBfvxbcv zV62!iR>g-)TuJHtjWO=!XQxK_{O44d^9&^iUm-91G5*(g+bKg?V#rsQzkpE}gGnz! zUGW(OIZyWNAdfPXC5Bjs{R7&3Nf?YGj+$j?weu*_- z)61lNkKnacwDwA{xdqhvBxP*wI9xpv28LIlD}T2un`&UpqW=g_XAE^6BI?h?m8t6x z;Obn5S^sHYmO500b2!nmic{w$G@KLAa8^JQ-)lWFsEX$H<<%wgsBT_H^F1$Z@QQQoRxH6GIE6-52p=|@2vM8MHO?2N%z&$8o@IiuK zSAyyAYMTA~y;zNFxIM`;Qwex@wrh;vlXC-Z=N2ZWelVrKIHyznOWT|MUDmyv#7B5Yx$tiFG$C zv}MelF%}>Yl2Dd)u`woU8i!?;zBgDD>`sD`H8 zosJHDaB-C3?u>PiMm6$~cWobgo5w1Z-O~{_%?v!_BF%P$0V5E1k;j-yp*B5TG6I+^%%mAeO7q;nc(9${ZGh4UdpHy9Xhpb zk6~xvEIj>ZVW%;{_CJt^yp-9#t;ev-cTjQ(uwu{Nx42yK9_8g9P+Gb%R3nd{@Om9w zfQ$1xDJ|_g_X_8TIy#K44xMfojt9(e)(r6E#BrWcVMccL)K=q&j+uZua2}psy*x=b zE}gz&MoBrxVE-a|`>lBM+KQG|3tFCA(Yl5gZB}%5Eg&|YBQ9?mNxDx`Z1|X=tj2<@ zN{-Z$Iiv~F1<6ITh{~1`o+&sfI3*({Pe$rRjx*H@xKuZR=&+9lZRn^jH%Es4fdx6& zInL@i3T`cP{x-*jx<3@waOB?LxM>73GW(znm2`9mB7-N}D{JRb_t1j;Dvop2A|tXz zwH(xgD5#zjwK>#30?Mv^Mp)pNrTatqV~3|yk^4;}`now7x~!;bvZAw)5G+3Z- zTEPA8MO>}HohEH^QwR_eTA*&mT*RMa2UA4P-_QI-gn z*36limT5L3FNhW8-AcS}n(1c55X;Kq>AYtsW_{!N_I~gC{GM~p_AzF|KMNKxewVWp zTgH|!#(cSWi+8Mj{>2!dIY_J$&;Pkevb_}cMJqBLN>-a3%h!yOsHPsx{)33t+${H5 zcDH==%c{4)wIK|iU26Dr#|TkPJ(`h+yi;=|*JLy8a(&IRCKcS;q9AXIgl)M$q_tb% z(y#;4SKA?Pju12Qke8T=49RsDy{thA_qN?w{8|C$2SLbg$;F!D5G<wI&$ zDZqFXPPzaq``x;3!8QAf1Slcw-U3fH^H<0Akr+4;FrB&%;{?I*vZl72(3SbW0hwR zP|p9m)&mG^KZ<2<_d!vn7P+($@q6U2fLBvIPJDX-r>lWut{44HM@Gt!@KJ}b&J08w z^N`S!hoo*TlKKjv?6`sOigq37S5(f(q+owf*-#x?` zt~SJBs&*4N}`&2jAqj= Xi#=miGZ;H-W7V|rKaUeWVu<|%J6kAH literal 0 HcmV?d00001 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(); + } +} +