diff --git a/libs/clipboard/src/lib.rs b/libs/clipboard/src/lib.rs index f4d3c9de1..33547a6f2 100644 --- a/libs/clipboard/src/lib.rs +++ b/libs/clipboard/src/lib.rs @@ -20,6 +20,7 @@ pub mod cliprdr; #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(tag = "t", content = "c")] pub enum ClipbaordFile { + MonitorReady, FormatList { format_list: Vec<(i32, String)>, }, @@ -152,6 +153,12 @@ pub fn server_clip_file( msg: ClipbaordFile, ) -> u32 { match msg { + ClipbaordFile::MonitorReady => { + log::debug!("server_monitor_ready called"); + let ret = server_monitor_ready(context, conn_id); + log::debug!("server_monitor_ready called, return {}", ret); + ret + } ClipbaordFile::FormatList { format_list } => { log::debug!("server_format_list called"); let ret = server_format_list(context, conn_id, format_list); @@ -226,6 +233,19 @@ pub fn server_clip_file( } } +pub fn server_monitor_ready(context: &mut Box, conn_id: i32) -> u32 { + unsafe { + let monitor_ready = CLIPRDR_MONITOR_READY { + connID: conn_id as UINT32, + msgType: 0 as UINT16, + msgFlags: 0 as UINT16, + dataLen: 0 as UINT32, + }; + let ret = ((**context).MonitorReady.unwrap())(&mut (**context), &monitor_ready); + ret as u32 + } +} + pub fn server_format_list( context: &mut Box, conn_id: i32, @@ -441,7 +461,7 @@ extern "C" fn client_format_list( ) -> UINT { log::debug!("client_format_list called"); - // let conn_id; + let conn_id; let mut format_list: Vec<(i32, String)> = Vec::new(); unsafe { let mut i = 0u32; @@ -463,15 +483,19 @@ extern "C" fn client_format_list( // log::debug!("format list item {}: format id: {}, format name: {}", i, format_data.formatId, &format_name); i += 1; } - // conn_id = (*clip_format_list).connID as i32; + conn_id = (*clip_format_list).connID as i32; } let data = ClipbaordFile::FormatList { format_list }; // no need to handle result here - VEC_MSG_CHANNEL - .read() - .unwrap() - .iter() - .for_each(|msg_channel| allow_err!(msg_channel.sender.send(data.clone()))); + if conn_id == 0 { + VEC_MSG_CHANNEL + .read() + .unwrap() + .iter() + .for_each(|msg_channel| allow_err!(msg_channel.sender.send(data.clone()))); + } else { + send_data(conn_id, data); + } 0 } diff --git a/libs/clipboard/src/windows/wf_cliprdr.c b/libs/clipboard/src/windows/wf_cliprdr.c index 9dcec737d..7374b27c1 100644 --- a/libs/clipboard/src/windows/wf_cliprdr.c +++ b/libs/clipboard/src/windows/wf_cliprdr.c @@ -1348,7 +1348,7 @@ static BOOL cliprdr_GetUpdatedClipboardFormats(wfClipboard *clipboard, PUINT lpu return TRUE; } -static UINT cliprdr_send_format_list(wfClipboard *clipboard) +static UINT cliprdr_send_format_list(wfClipboard *clipboard, UINT32 connID) { UINT rc; int count = 0; @@ -1423,7 +1423,7 @@ static UINT cliprdr_send_format_list(wfClipboard *clipboard) } } - formatList.connID = 0; + formatList.connID = connID; formatList.numFormats = numFormats; formatList.formats = formats; formatList.msgType = CB_FORMAT_LIST; @@ -1653,7 +1653,7 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM clipboard->hmem = NULL; } - cliprdr_send_format_list(clipboard); + cliprdr_send_format_list(clipboard, 0); } } @@ -1704,7 +1704,7 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM if ((GetClipboardOwner() != clipboard->hwnd) && (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj))) { - cliprdr_send_format_list(clipboard); + cliprdr_send_format_list(clipboard, 0); } SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam); @@ -2137,9 +2137,14 @@ static UINT wf_cliprdr_send_client_capabilities(wfClipboard *clipboard) CLIPRDR_CAPABILITIES capabilities; CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet; - if (!clipboard || !clipboard->context || !clipboard->context->ClientCapabilities) + if (!clipboard || !clipboard->context) return ERROR_INTERNAL_ERROR; + // Ignore ClientCapabilities for now + if (!clipboard->context->ClientCapabilities) { + return CHANNEL_RC_OK; + } + capabilities.connID = 0; capabilities.cCapabilitiesSets = 1; capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET *)&(generalCapabilitySet); @@ -2171,7 +2176,7 @@ static UINT wf_cliprdr_monitor_ready(CliprdrClientContext *context, if (rc != CHANNEL_RC_OK) return rc; - return cliprdr_send_format_list(clipboard); + return cliprdr_send_format_list(clipboard, monitorReady->connID); } /** diff --git a/src/clipboard_file.rs b/src/clipboard_file.rs index 6ae211eeb..e6f40e215 100644 --- a/src/clipboard_file.rs +++ b/src/clipboard_file.rs @@ -3,6 +3,15 @@ use hbb_common::message_proto::*; pub fn clip_2_msg(clip: ClipbaordFile) -> Message { match clip { + ClipbaordFile::MonitorReady => Message { + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::Ready(CliprdrMonitorReady { + ..Default::default() + })), + ..Default::default() + })), + ..Default::default() + }, ClipbaordFile::FormatList { format_list } => { let mut formats: Vec = Vec::new(); for v in format_list.iter() { @@ -116,6 +125,7 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { pub fn msg_2_clip(msg: Cliprdr) -> Option { match msg.union { + Some(cliprdr::Union::Ready(_)) => Some(ClipbaordFile::MonitorReady), Some(cliprdr::Union::FormatList(data)) => { let mut format_list: Vec<(i32, String)> = Vec::new(); for v in data.formats.iter() { diff --git a/src/flutter.rs b/src/flutter.rs index c651c9fbe..d477b62d5 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -22,6 +22,7 @@ pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; pub(super) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward"; lazy_static::lazy_static! { + static ref CUR_SESSION_ID: RwLock = Default::default(); pub static ref SESSIONS: RwLock>> = Default::default(); pub static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } @@ -564,3 +565,13 @@ pub fn make_fd_flutter(id: i32, entries: &Vec, only_count: bool) -> S m.insert("total_size".into(), json!(n as f64)); serde_json::to_string(&m).unwrap_or("".into()) } + +pub fn get_cur_session_id() -> String { + *CUR_SESSION_ID.read().unwrap() +} + +pub fn set_cur_session_id(id: String) { + if get_cur_session_id() != id { + *CUR_SESSION_ID.write().unwrap() = id; + } +} diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 26f3d636c..0356a46a9 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -4,7 +4,7 @@ use std::{ iter::FromIterator, sync::{ atomic::{AtomicI64, Ordering}, - RwLock, + Arc, RwLock, }, }; @@ -24,7 +24,10 @@ use hbb_common::{ protobuf::Message as _, tokio::{ self, - sync::mpsc::{self, UnboundedSender}, + sync::{ + mpsc::{self, unbounded_channel, UnboundedSender}, + Mutex as TokioMutex, + }, task::spawn_blocking, }, }; @@ -48,6 +51,17 @@ pub struct Client { tx: UnboundedSender, } +struct IpcTaskRunner { + stream: Connection, + cm: ConnectionManager, + tx_file: mpsc::UnboundedSender, + tx: mpsc::UnboundedSender, + rx: mpsc::UnboundedReceiver, + conn_id: i32, + #[cfg(windows)] + file_transfer_enabled: bool, +} + lazy_static::lazy_static! { static ref CLIENTS: RwLock> = Default::default(); static ref CLICK_TIME: AtomicI64 = AtomicI64::new(0); @@ -224,159 +238,147 @@ pub enum ClipboardFileData { Enable((i32, bool)), } -async fn cm_ipc_task_wait_login( - mut stream: Connection, - cm: ConnectionManager, - tx: mpsc::UnboundedSender, -) -> (Connection, ConnectionManager, Option<(i32, bool)>) { - let mut ret = None; - loop { - tokio::select! { - res = stream.next() => { - match res { - Err(err) => { - log::info!("cm ipc connection closed: {}", err); - break; - } - Ok(Some(data)) => { - match data { - Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled, restart, recording} => { - log::debug!("conn_id: {}", id); - cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, tx); - ret = Some((id, file_transfer_enabled)); - break; - } - Data::Close => { - log::info!("cm ipc connection closed from connection request"); - break; - } - Data::Disconnected => { - log::info!("cm ipc connection disconnect"); - break; - } - _ => { +impl IpcTaskRunner { + async fn run(&mut self) { + use hbb_common::config::LocalConfig; + // for tmp use, without real conn id + let conn_id_tmp = -1; + let mut write_jobs: Vec = Vec::new(); + let mut close = true; + + #[cfg(windows)] + if self.conn_id > 0 { + allow_err!(self.tx_file.send(ClipboardFileData::Enable(( + self.conn_id, + self.file_transfer_enabled + )))); + } + + #[cfg(windows)] + let rx_clip1; + let mut rx_clip; + let _tx_clip; + #[cfg(windows)] + if self.conn_id > 0 { + rx_clip1 = clipboard::get_rx_cliprdr_server(self.conn_id); + rx_clip = rx_clip1.lock().await; + } else { + let rx_clip2; + (_tx_clip, rx_clip2) = unbounded_channel::(); + rx_clip1 = Arc::new(TokioMutex::new(rx_clip2)); + rx_clip = rx_clip1.lock().await; + } + + loop { + tokio::select! { + res = self.stream.next() => { + match res { + Err(err) => { + log::info!("cm ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled, restart, recording} => { + log::debug!("conn_id: {}", id); + self.cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, self.tx.clone()); + self.conn_id = id; + #[cfg(windows)] + { + self.file_transfer_enabled = file_transfer_enabled; + } + break; + } + Data::Close => { + allow_err!(self.tx_file.send(ClipboardFileData::Enable((self.conn_id, false)))); + 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)))); + log::info!("cm ipc connection disconnect"); + break; + } + Data::PrivacyModeState((id, _)) => { + self.conn_id = conn_id_tmp; + allow_err!(self.tx.send(data)); + } + Data::ClickTime(ms) => { + CLICK_TIME.store(ms, Ordering::SeqCst); + } + Data::ChatMessage { text } => { + self.cm.new_message(self.conn_id, text); + } + 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)] + Data::ClipboardFileEnabled(enabled) => { + allow_err!(self.tx_file.send(ClipboardFileData::Enable((self.conn_id, enabled)))); + } + Data::Theme(dark) => { + self.cm.change_theme(dark); + } + Data::Language(lang) => { + LocalConfig::set_option("lang".to_owned(), lang); + self.cm.change_language(); + } + _ => { + + } } } + _ => {} } - _ => {} } - } - } - } - (stream, cm, ret) -} - -async fn cm_ipc_task_loop( - mut stream: Connection, - cm: ConnectionManager, - tx_file: mpsc::UnboundedSender, - tx: mpsc::UnboundedSender, - mut rx: mpsc::UnboundedReceiver, - mut conn_id: i32, - #[cfg(windows)] file_transfer_enabled: bool, -) { - use hbb_common::config::LocalConfig; - - // for tmp use, without real conn id - let conn_id_tmp = -1; - let mut write_jobs: Vec = Vec::new(); - let mut close = true; - - #[cfg(windows)] - allow_err!(tx_file.send(ClipboardFileData::Enable((conn_id, file_transfer_enabled)))); - - #[cfg(windows)] - let rx_clip_client1 = clipboard::get_rx_cliprdr_server(conn_id); - #[cfg(windows)] - let mut rx_clip_client = rx_clip_client1.lock().await; - - loop { - tokio::select! { - res = stream.next() => { - match res { - Err(err) => { - log::info!("cm ipc connection closed: {}", err); + Some(data) = self.rx.recv() => { + if self.stream.send(&data).await.is_err() { break; } - Ok(Some(data)) => { - match data { - Data::Close => { - allow_err!(tx_file.send(ClipboardFileData::Enable((conn_id, false)))); - log::info!("cm ipc connection closed from connection request"); - break; - } - Data::Disconnected => { - close = false; - allow_err!(tx_file.send(ClipboardFileData::Enable((conn_id, false)))); - log::info!("cm ipc connection disconnect"); - break; - } - Data::PrivacyModeState((id, _)) => { - conn_id = conn_id_tmp; - allow_err!(tx.send(data)); - } - Data::ClickTime(ms) => { - CLICK_TIME.store(ms, Ordering::SeqCst); - } - Data::ChatMessage { text } => { - cm.new_message(conn_id, text); - } - Data::FS(fs) => { - handle_fs(fs, &mut write_jobs, &tx).await; - } - #[cfg(windows)] - Data::ClipbaordFile(_clip) => { - allow_err!(tx_file.send(ClipboardFileData::Clip((conn_id, _clip)))); - } - #[cfg(windows)] - Data::ClipboardFileEnabled(enabled) => { - allow_err!(tx_file.send(ClipboardFileData::Enable((conn_id, enabled)))); - } - Data::Theme(dark) => { - cm.change_theme(dark); - } - Data::Language(lang) => { - LocalConfig::set_option("lang".to_owned(), lang); - cm.change_language(); - } - _ => { - - } - } + } + clip_file = rx_clip.recv() => match clip_file { + Some(clip) => { + #[cfg(windows)] + allow_err!(self.tx.send(Data::ClipbaordFile(clip))); } - _ => {} - } + None => { + // + } + }, } - Some(data) = rx.recv() => { - if stream.send(&data).await.is_err() { - break; - } - } - clip_file = rx_clip_client.recv() => match clip_file { - Some(clip) => { - allow_err!(tx.send(Data::ClipbaordFile(clip))); - } - None => { - // - } - }, + } + if self.conn_id != 0 && self.conn_id != conn_id_tmp { + self.cm.remove_connection(self.conn_id, close); } } - if conn_id != conn_id_tmp { - cm.remove_connection(conn_id, close); - } -} -async fn cm_ipc_task( - stream: Connection, - cm: ConnectionManager, - tx_file: mpsc::UnboundedSender, -) { - let (tx, rx) = mpsc::unbounded_channel::(); - let (stream, cm, wait_res) = cm_ipc_task_wait_login(stream, cm, tx.clone()).await; - if let Some((conn_id, file_transfer_enabled)) = wait_res { - cm_ipc_task_loop(stream, cm, tx_file, tx, rx, conn_id, file_transfer_enabled).await; + async fn ipc_task( + stream: Connection, + cm: ConnectionManager, + tx_file: mpsc::UnboundedSender, + ) { + let (tx, rx) = mpsc::unbounded_channel::(); + let mut task_runner = Self { + stream, + cm, + tx_file, + tx, + rx, + conn_id: 0, + #[cfg(windows)] + file_transfer_enabled: false, + }; + + task_runner.run().await; + if task_runner.conn_id > 0 { + task_runner.run().await; + } } } @@ -410,7 +412,7 @@ pub async fn start_ipc(cm: ConnectionManager) { match result { Ok(stream) => { log::debug!("Got new connection"); - tokio::spawn(cm_ipc_task( + tokio::spawn(IpcTaskRunner::::ipc_task( Connection::new(stream), cm.clone(), tx_file.clone(),