diff --git a/libs/clipboard/src/context_send.rs b/libs/clipboard/src/context_send.rs new file mode 100644 index 000000000..869e8a11b --- /dev/null +++ b/libs/clipboard/src/context_send.rs @@ -0,0 +1,60 @@ +use crate::cliprdr::*; +use hbb_common::log; +use std::sync::Mutex; + +#[cfg(windows)] +lazy_static::lazy_static! { + static ref CONTEXT_SEND: ContextSend = ContextSend{addr: Mutex::new(0)}; +} + +pub struct ContextSend { + addr: Mutex<u64>, +} + +impl ContextSend { + pub fn is_enabled() -> bool { + *CONTEXT_SEND.addr.lock().unwrap() != 0 + } + + pub fn enable(enabled: bool) { + let mut lock = CONTEXT_SEND.addr.lock().unwrap(); + if enabled { + if *lock == 0 { + match crate::create_cliprdr_context(true, false, crate::ProcessSide::ClientSide) { + Ok(context) => { + log::info!("clipboard context for file transfer created."); + *lock = Box::into_raw(context) as _; + } + Err(err) => { + log::error!( + "Create clipboard context for file transfer: {}", + err.to_string() + ); + } + } + } + } else { + if *lock != 0 { + unsafe { + let _ = Box::from_raw(*lock as *mut CliprdrClientContext); + } + log::info!("clipboard context for file transfer destroyed."); + *lock = 0; + } + } + } + + pub fn proc<F: FnOnce(&mut Box<CliprdrClientContext>) -> u32>(f: F) -> u32 { + let mut lock = CONTEXT_SEND.addr.lock().unwrap(); + if *lock != 0 { + unsafe { + let mut context = Box::from_raw(*lock as *mut CliprdrClientContext); + let res = f(&mut context); + *lock = Box::into_raw(context) as _; + res + } + } else { + 0 + } + } +} diff --git a/libs/clipboard/src/lib.rs b/libs/clipboard/src/lib.rs index 33547a6f2..fa65adc37 100644 --- a/libs/clipboard/src/lib.rs +++ b/libs/clipboard/src/lib.rs @@ -16,6 +16,8 @@ use std::{ }; pub mod cliprdr; +pub mod context_send; +pub use context_send::*; #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(tag = "t", content = "c")] diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 5ab6e3d85..662addea5 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -6,6 +6,9 @@ use crate::common; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}; +#[cfg(windows)] +use clipboard::{cliprdr::CliprdrClientContext, ContextSend}; + use crate::ui_session_interface::{InvokeUiSession, Session}; use crate::{client::Data, client::Interface}; @@ -33,11 +36,6 @@ use std::collections::HashMap; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -#[cfg(windows)] -lazy_static::lazy_static! { - static ref CLIPBOARD_FILE_CONTEXT: Mutex<u64> = Mutex::new(0); -} - pub struct Remote<T: InvokeUiSession> { handler: Session<T>, video_sender: MediaSender, @@ -58,37 +56,6 @@ pub struct Remote<T: InvokeUiSession> { video_format: CodecFormat, } -#[cfg(windows)] -fn check_clipboard_file_context(enable_file_transfer: bool) { - let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst) && enable_file_transfer; - let mut lock = CLIPBOARD_FILE_CONTEXT.lock().unwrap(); - if enabled { - if *lock == 0 { - match clipboard::create_cliprdr_context(true, false, clipboard::ProcessSide::ClientSide) - { - Ok(context) => { - log::info!("clipboard context for file transfer created."); - *lock = Box::into_raw(context) as _; - } - Err(err) => { - log::error!( - "Create clipboard context for file transfer: {}", - err.to_string() - ); - } - } - } - } else { - if *lock != 0 { - unsafe { - let _ = Box::from_raw(*lock as *mut clipboard::cliprdr::CliprdrClientContext); - } - log::info!("clipboard context for file transfer destroyed."); - *lock = 0; - } - } -} - impl<T: InvokeUiSession> Remote<T> { pub fn new( handler: Session<T>, @@ -1202,23 +1169,19 @@ impl<T: InvokeUiSession> Remote<T> { fn check_clipboard_file_context(&self) { #[cfg(windows)] { - check_clipboard_file_context(self.handler.lc.read().unwrap().enable_file_transfer); + let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst) + && self.handler.lc.read().unwrap().enable_file_transfer; + ContextSend::enable(enabled); } } #[cfg(windows)] fn handle_cliprdr_msg(&self, clip: message_proto::Cliprdr) { if !self.handler.lc.read().unwrap().disable_clipboard { - let mut lock = CLIPBOARD_FILE_CONTEXT.lock().unwrap(); - if *lock != 0 { - unsafe { - let mut context = - Box::from_raw(*lock as *mut clipboard::cliprdr::CliprdrClientContext); - if let Some(clip) = crate::clipboard_file::msg_2_clip(clip) { - clipboard::server_clip_file(&mut context, self.client_conn_id, clip); - } - *lock = Box::into_raw(context) as _; - } + if let Some(clip) = crate::clipboard_file::msg_2_clip(clip) { + ContextSend::proc(|context: &mut Box<CliprdrClientContext>| -> u32 { + clipboard::server_clip_file(context, self.client_conn_id, clip) + }); } } } diff --git a/src/ui.rs b/src/ui.rs index 6a9837a59..548bfad43 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -53,11 +53,8 @@ fn check_connect_status( ) { let status = Arc::new(Mutex::new((0, false, 0, "".to_owned()))); let options = Arc::new(Mutex::new(Config::get_options())); - let cloned = status.clone(); - let cloned_options = options.clone(); let (tx, rx) = mpsc::unbounded_channel::<ipc::Data>(); let password = Arc::new(Mutex::new(String::default())); - let cloned_password = password.clone(); std::thread::spawn(move || crate::ui_interface::check_connect_status_(reconnect, rx)); (status, options, tx, password) } diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 79152abe6..9c45ad269 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -11,9 +11,7 @@ use std::{ }; #[cfg(windows)] -use clipboard::empty_clipboard; -#[cfg(windows)] -use hbb_common::chrono::Duration; +use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, ContextSend}; use serde_derive::Serialize; use crate::ipc::{self, new_listener, Connection, Data}; @@ -56,7 +54,6 @@ pub struct Client { struct IpcTaskRunner<T: InvokeUiCM> { stream: Connection, cm: ConnectionManager<T>, - tx_file: mpsc::UnboundedSender<ClipboardFileData>, tx: mpsc::UnboundedSender<Data>, rx: mpsc::UnboundedReceiver<Data>, conn_id: i32, @@ -233,14 +230,31 @@ pub fn get_clients_length() -> usize { clients.len() } -#[derive(Debug)] -pub enum ClipboardFileData { - #[cfg(windows)] - Clip((i32, ipc::ClipbaordFile)), - Enable((i32, bool)), -} - impl<T: InvokeUiCM> IpcTaskRunner<T> { + #[cfg(windows)] + async fn enable_cliprdr_file_context(&mut self, conn_id: i32, enabled: bool) { + if conn_id == 0 { + return; + } + + let pre_enabled = ContextSend::is_enabled(); + ContextSend::enable(enabled); + if !pre_enabled && ContextSend::is_enabled() { + allow_err!( + self.stream + .send(&Data::ClipbaordFile(clipboard::ClipbaordFile::MonitorReady)) + .await + ); + } + clipboard::set_conn_enabled(conn_id, enabled); + if !enabled { + ContextSend::proc(|context: &mut Box<CliprdrClientContext>| -> u32 { + clipboard::empty_clipboard(context, conn_id); + 0 + }); + } + } + async fn run(&mut self) { use hbb_common::config::LocalConfig; @@ -251,10 +265,8 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> { #[cfg(windows)] if self.conn_id > 0 { - allow_err!(self.tx_file.send(ClipboardFileData::Enable(( - self.conn_id, - self.file_transfer_enabled - )))); + self.enable_cliprdr_file_context(self.conn_id, self.file_transfer_enabled) + .await; } #[cfg(windows)] @@ -297,13 +309,15 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> { break; } Data::Close => { - allow_err!(self.tx_file.send(ClipboardFileData::Enable((self.conn_id, false)))); + #[cfg(windows)] + self.enable_cliprdr_file_context(self.conn_id, false).await; log::info!("cm ipc connection closed from connection request"); break; } Data::Disconnected => { close = false; - allow_err!(self.tx_file.send(ClipboardFileData::Enable((self.conn_id, false)))); + #[cfg(windows)] + self.enable_cliprdr_file_context(self.conn_id, false).await; log::info!("cm ipc connection disconnect"); break; } @@ -320,13 +334,20 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> { Data::FS(fs) => { handle_fs(fs, &mut write_jobs, &self.tx).await; } - #[cfg(windows)] Data::ClipbaordFile(_clip) => { - allow_err!(self.tx_file.send(ClipboardFileData::Clip((self.conn_id, _clip)))); + #[cfg(windows)] + { + let conn_id = self.conn_id; + + ContextSend::proc(|context: &mut Box<CliprdrClientContext>| -> u32 { + clipboard::server_clip_file(context, conn_id, _clip) + }); + } } #[cfg(windows)] - Data::ClipboardFileEnabled(enabled) => { - allow_err!(self.tx_file.send(ClipboardFileData::Enable((self.conn_id, enabled)))); + Data::ClipboardFileEnabled(_enabled) => { + #[cfg(windows)] + self.enable_cliprdr_file_context(self.conn_id, _enabled).await; } Data::Theme(dark) => { self.cm.change_theme(dark); @@ -364,16 +385,11 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> { } } - async fn ipc_task( - stream: Connection, - cm: ConnectionManager<T>, - tx_file: mpsc::UnboundedSender<ClipboardFileData>, - ) { + async fn ipc_task(stream: Connection, cm: ConnectionManager<T>) { let (tx, rx) = mpsc::unbounded_channel::<Data>(); let mut task_runner = Self { stream, cm, - tx_file, tx, rx, conn_id: 0, @@ -391,13 +407,6 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> { #[cfg(not(any(target_os = "android", target_os = "ios")))] #[tokio::main(flavor = "current_thread")] pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) { - #[cfg(windows)] - let cm_clip = cm.clone(); - - let (tx_file, _rx_file) = mpsc::unbounded_channel::<ClipboardFileData>(); - #[cfg(windows)] - std::thread::spawn(move || start_clipboard_file(_rx_file)); - #[cfg(windows)] std::thread::spawn(move || { log::info!("try create privacy mode window"); @@ -422,7 +431,6 @@ pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) { tokio::spawn(IpcTaskRunner::<T>::ipc_task( Connection::new(stream), cm.clone(), - tx_file.clone(), )); } Err(err) => { @@ -723,47 +731,3 @@ fn send_raw(msg: Message, tx: &UnboundedSender<Data>) { err => allow_err!(err), } } - -#[cfg(windows)] -#[tokio::main(flavor = "current_thread")] -pub async fn start_clipboard_file(mut rx: mpsc::UnboundedReceiver<ClipboardFileData>) { - let mut cliprdr_context = None; - - loop { - tokio::select! { - server_msg = rx.recv() => match server_msg { - Some(ClipboardFileData::Clip((conn_id, clip))) => { - if let Some(ctx) = cliprdr_context.as_mut() { - clipboard::server_clip_file(ctx, conn_id, clip); - } - } - Some(ClipboardFileData::Enable((id, enabled))) => { - if enabled && cliprdr_context.is_none() { - cliprdr_context = Some(match clipboard::create_cliprdr_context(true, false, clipboard::ProcessSide::ServerSide) { - Ok(context) => { - log::info!("clipboard context for file transfer created."); - context - } - Err(err) => { - log::error!( - "Create clipboard context for file transfer: {}", - err.to_string() - ); - return; - } - }); - } - clipboard::set_conn_enabled(id, enabled); - if !enabled { - if let Some(ctx) = cliprdr_context.as_mut() { - clipboard::empty_clipboard(ctx, id); - } - } - } - None => { - break - } - } - } - } -} diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f95a743c2..a085e9822 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -6,6 +6,7 @@ use crate::client::{ load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP, SERVER_KEYBOARD_ENABLED, }; +#[cfg(target_os = "linux")] use crate::common::IS_X11; use crate::{client::Data, client::Interface}; use async_trait::async_trait; @@ -812,6 +813,7 @@ impl<T: InvokeUiSession> Session<T> { let keycode: u32 = keycode as u32; let scancode: u32 = scancode as u32; + #[cfg(not(target_os = "windows"))] let key = rdev::key_from_scancode(scancode) as RdevKey; // Windows requires special handling #[cfg(target_os = "windows")]