diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs index 3e94d2824..811aebb7b 100644 --- a/src/server/clipboard_service.rs +++ b/src/server/clipboard_service.rs @@ -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, -} - -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, -} - -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::(notify, run); + sp.run::<_>(listen::run); sp } -fn notify(tx: Sender) -> 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(()) } diff --git a/src/server/service.rs b/src/server/service.rs index 3e8c840a2..b60f5a8d4 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -1,6 +1,5 @@ use super::*; use std::{ - sync::{self, mpsc::Sender}, thread::{self, JoinHandle}, time, }; @@ -154,39 +153,6 @@ impl> ServiceTmpl { } } - pub fn listen(&self, notify: N, callback: F) - where - N: 'static + FnMut(Sender) -> 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(&self, interval_ms: u64, callback: F) where F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send,