Merge pull request #279 from cc-morning/master
Clipboard service optimization and bug fixes
This commit is contained in:
		
						commit
						247ea5fed3
					
				| @ -1,71 +1,106 @@ | ||||
| use std::sync::mpsc::Sender; | ||||
| 
 | ||||
| use clipboard_master::{CallbackResult, ClipboardHandler, Master}; | ||||
| 
 | ||||
| use super::*; | ||||
| pub use crate::common::{ | ||||
|     check_clipboard, ClipboardContext, CLIPBOARD_INTERVAL as INTERVAL, CLIPBOARD_NAME as NAME, | ||||
|     CONTENT, | ||||
| }; | ||||
| 
 | ||||
| struct State { | ||||
|     ctx: Option<ClipboardContext>, | ||||
| } | ||||
| 
 | ||||
| impl Default for State { | ||||
|     fn default() -> Self { | ||||
|         let ctx = match ClipboardContext::new() { | ||||
|             Ok(ctx) => Some(ctx), | ||||
|             Err(err) => { | ||||
|                 log::error!("Failed to start {}: {}", NAME, err); | ||||
|                 None | ||||
|             } | ||||
|         }; | ||||
|         Self { ctx } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl super::service::Reset for State { | ||||
|     fn reset(&mut self) { | ||||
|         *CONTENT.lock().unwrap() = Default::default(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| struct ClipHandle { | ||||
|     tx: Sender<bool>, | ||||
| } | ||||
| 
 | ||||
| impl ClipboardHandler for ClipHandle { | ||||
|     fn on_clipboard_change(&mut self) -> CallbackResult { | ||||
|         let _ = self.tx.send(true); | ||||
|         CallbackResult::Next | ||||
|     } | ||||
| } | ||||
| use clipboard_master::{CallbackResult, ClipboardHandler, Master}; | ||||
| use hbb_common::{anyhow, ResultType}; | ||||
| use std::{ | ||||
|     io, sync, | ||||
|     sync::{ | ||||
|         atomic::{AtomicBool, Ordering}, | ||||
|         mpsc::SyncSender, | ||||
|     }, | ||||
|     time::Duration, | ||||
| }; | ||||
| 
 | ||||
| pub fn new() -> GenericService { | ||||
|     let sp = GenericService::new(NAME, true); | ||||
|     sp.listen::<State, _, _>(notify, run); | ||||
|     sp.run::<_>(listen::run); | ||||
|     sp | ||||
| } | ||||
| 
 | ||||
| fn notify(tx: Sender<bool>) -> ResultType<()> { | ||||
|     Master::new(ClipHandle { tx }).run()?; | ||||
|     Ok(()) | ||||
| } | ||||
| mod listen { | ||||
|     use super::*; | ||||
| 
 | ||||
| fn run(sp: GenericService, state: &mut State) -> ResultType<()> { | ||||
|     if let Some(ctx) = state.ctx.as_mut() { | ||||
|         if let Some(msg) = check_clipboard(ctx, None) { | ||||
|             sp.send(msg); | ||||
|         } | ||||
|         sp.snapshot(|sps| { | ||||
|             let txt = crate::CONTENT.lock().unwrap().clone(); | ||||
|             if !txt.is_empty() { | ||||
|                 let msg_out = crate::create_clipboard_msg(txt); | ||||
|                 sps.send_shared(Arc::new(msg_out)); | ||||
|             } | ||||
|             Ok(()) | ||||
|         })?; | ||||
|     static RUNNING: AtomicBool = AtomicBool::new(true); | ||||
|     static WAIT: Duration = Duration::from_millis(1500); | ||||
| 
 | ||||
|     struct ClipHandle { | ||||
|         tx: SyncSender<()>, | ||||
|     } | ||||
| 
 | ||||
|     impl ClipboardHandler for ClipHandle { | ||||
|         fn on_clipboard_change(&mut self) -> CallbackResult { | ||||
|             if !RUNNING.load(Ordering::SeqCst) { | ||||
|                 return CallbackResult::Stop; | ||||
|             } | ||||
| 
 | ||||
|             let _ = self.tx.send(()); | ||||
|             CallbackResult::Next | ||||
|         } | ||||
| 
 | ||||
|         fn on_clipboard_error(&mut self, error: io::Error) -> CallbackResult { | ||||
|             if !RUNNING.load(Ordering::SeqCst) { | ||||
|                 CallbackResult::Stop | ||||
|             } else { | ||||
|                 CallbackResult::StopWithError(error) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[tokio::main] | ||||
|     pub async fn run(sp: GenericService) -> ResultType<()> { | ||||
|         let mut ctx = match ClipboardContext::new() { | ||||
|             Ok(ctx) => ctx, | ||||
|             Err(err) => { | ||||
|                 log::error!("Failed to start {}: {}", NAME, err); | ||||
|                 return Err(anyhow::Error::from(err)); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         if !RUNNING.load(Ordering::SeqCst) { | ||||
|             RUNNING.store(true, Ordering::SeqCst); | ||||
|         } | ||||
| 
 | ||||
|         let (tx, rx) = sync::mpsc::sync_channel(12); | ||||
|         let listener = tokio::spawn(async { | ||||
|             log::info!("Clipboard listener running!"); | ||||
|             let _ = Master::new(ClipHandle { tx }).run(); | ||||
|         }); | ||||
| 
 | ||||
|         while sp.ok() { | ||||
|             if let Ok(_) = rx.recv_timeout(WAIT) { | ||||
|                 if let Some(msg) = check_clipboard(&mut ctx, None) { | ||||
|                     sp.send(msg); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             sp.snapshot(|sps| { | ||||
|                 let txt = crate::CONTENT.lock().unwrap().clone(); | ||||
|                 if !txt.is_empty() { | ||||
|                     let msg_out = crate::create_clipboard_msg(txt); | ||||
|                     sps.send_shared(Arc::new(msg_out)); | ||||
|                 } | ||||
|                 Ok(()) | ||||
|             })?; | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             RUNNING.store(false, Ordering::SeqCst); | ||||
|             trigger(&mut ctx); | ||||
|             let _ = listener.await; | ||||
|             log::info!("Clipboard listener stopped!"); | ||||
|         } | ||||
| 
 | ||||
|         *CONTENT.lock().unwrap() = Default::default(); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn trigger(ctx: &mut ClipboardContext) { | ||||
|         let _ = match ctx.get_text() { | ||||
|             Ok(text) => ctx.set_text(text), | ||||
|             Err(_) => ctx.set_text(Default::default()), | ||||
|         }; | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| use super::*; | ||||
| use std::{ | ||||
|     sync::{self, mpsc::Sender}, | ||||
|     thread::{self, JoinHandle}, | ||||
|     time, | ||||
| }; | ||||
| @ -154,39 +153,6 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn listen<S, N, F>(&self, notify: N, callback: F) | ||||
|     where | ||||
|         N: 'static + FnMut(Sender<bool>) -> ResultType<()> + Send, | ||||
|         F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send, | ||||
|         S: 'static + Default + Reset, | ||||
|     { | ||||
|         let mut notify = notify; | ||||
|         let mut callback = callback; | ||||
|         let sp = self.clone(); | ||||
|         let (tx, rx) = sync::mpsc::channel(); | ||||
|         thread::spawn(move || { | ||||
|             let _ = notify(tx); | ||||
|         }); | ||||
| 
 | ||||
|         let thread = thread::spawn(move || { | ||||
|             let mut state = S::default(); | ||||
|             while let Ok(changed) = rx.recv() { | ||||
|                 if changed && sp.active() { | ||||
|                     if sp.has_subscribes() { | ||||
|                         if let Err(err) = callback(sp.clone(), &mut state) { | ||||
|                             log::error!("Error of {} service: {}", sp.name(), err); | ||||
|                             #[cfg(windows)] | ||||
|                             crate::platform::windows::try_change_desktop(); | ||||
|                         } | ||||
|                     } else { | ||||
|                         state.reset(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         self.0.write().unwrap().handle = Some(thread); | ||||
|     } | ||||
| 
 | ||||
|     pub fn repeat<S, F>(&self, interval_ms: u64, callback: F) | ||||
|     where | ||||
|         F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user