diff --git a/libs/clipboard/src/lib.rs b/libs/clipboard/src/lib.rs index 30055740e..6bdd2293a 100644 --- a/libs/clipboard/src/lib.rs +++ b/libs/clipboard/src/lib.rs @@ -107,6 +107,7 @@ pub enum ClipboardFile { stream_id: i32, requested_data: Vec, }, + TryEmpty, } struct MsgChannel { @@ -226,6 +227,16 @@ fn send_data_to_channel(conn_id: i32, data: ClipboardFile) -> ResultType<()> { } } +#[cfg(target_os = "windows")] +pub fn send_data_exclude(conn_id: i32, data: ClipboardFile) { + use hbb_common::log; + for msg_channel in VEC_MSG_CHANNEL.read().unwrap().iter() { + if msg_channel.conn_id != conn_id { + allow_err!(msg_channel.sender.send(data.clone())); + } + } +} + #[cfg(feature = "unix-file-copy-paste")] #[inline] fn send_data_to_all(data: ClipboardFile) -> ResultType<()> { diff --git a/libs/clipboard/src/platform/windows.rs b/libs/clipboard/src/platform/windows.rs index 5d1aa086d..3b931a4a6 100644 --- a/libs/clipboard/src/platform/windows.rs +++ b/libs/clipboard/src/platform/windows.rs @@ -6,10 +6,10 @@ #![allow(deref_nullptr)] use crate::{ - allow_err, send_data, ClipboardFile, CliprdrError, CliprdrServiceContext, ResultType, + send_data, send_data_exclude, ClipboardFile, CliprdrError, CliprdrServiceContext, ResultType, ERR_CODE_INVALID_PARAMETER, ERR_CODE_SEND_MSG, ERR_CODE_SERVER_FUNCTION_NONE, VEC_MSG_CHANNEL, }; -use hbb_common::log; +use hbb_common::{allow_err, log}; use std::{ boxed::Box, ffi::{CStr, CString}, @@ -643,6 +643,7 @@ pub fn server_clip_file( conn_id, &format_list ); + send_data_exclude(conn_id as _, ClipboardFile::TryEmpty); ret = server_format_list(context, conn_id, format_list); log::debug!( "server_format_list called, conn_id {}, return {}", @@ -740,6 +741,11 @@ pub fn server_clip_file( ret ); } + ClipboardFile::TryEmpty => { + log::debug!("empty_clipboard called"); + let ret = empty_clipboard(context, conn_id); + log::debug!("empty_clipboard called, conn_id {}, return {}", conn_id, ret); + } } ret } diff --git a/libs/clipboard/src/windows/wf_cliprdr.c b/libs/clipboard/src/windows/wf_cliprdr.c index 6f8381a6a..e065be215 100644 --- a/libs/clipboard/src/windows/wf_cliprdr.c +++ b/libs/clipboard/src/windows/wf_cliprdr.c @@ -269,6 +269,7 @@ static UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 con DWORD positionlow, ULONG request); static BOOL is_file_descriptor_from_remote(); +static BOOL is_set_by_instance(wfClipboard *clipboard); static void CliprdrDataObject_Delete(CliprdrDataObject *instance); @@ -600,8 +601,11 @@ static CliprdrStream *CliprdrStream_New(UINT32 connID, ULONG index, void *pData, clipboard->req_fdata = NULL; } } - else + else { + instance->m_lSize.QuadPart = + ((UINT64)instance->m_Dsc.nFileSizeHigh << 32) | instance->m_Dsc.nFileSizeLow; success = TRUE; + } } } @@ -1745,8 +1749,7 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM DEBUG_CLIPRDR("info: WM_CLIPBOARDUPDATE"); // if (clipboard->sync) { - if ((GetClipboardOwner() != clipboard->hwnd) && - (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj))) + if (!is_set_by_instance(clipboard)) { if (clipboard->hmem) { @@ -2086,6 +2089,8 @@ static FILEDESCRIPTORW *wf_cliprdr_get_file_descriptor(WCHAR *file_name, size_t return NULL; } + // to-do: use `fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI`. + // We keep `fd->dwFlags = FD_ATTRIBUTES | FD_WRITESTIME | FD_PROGRESSUI` for compatibility. // fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI; fd->dwFlags = FD_ATTRIBUTES | FD_WRITESTIME | FD_PROGRESSUI; fd->dwFileAttributes = GetFileAttributesW(file_name); @@ -2849,6 +2854,31 @@ wf_cliprdr_server_file_contents_request(CliprdrClientContext *context, goto exit; } + // If the clipboard is set by the instance, or the file descriptor is from remote, + // we should not process the request. + // Because this may be the following cases: + // 1. `A` -> `B`, `C` + // 2. Copy in `A` + // 3. Copy in `B` + // 4. Paste in `C` + // In this case, `C` should not get the file content from `A`. The clipboard is set by `B`. + // + // Or + // 1. `B` -> `A` -> `C` + // 2. Copy in `A` + // 2. Copy in `B` + // 3. Paste in `C` + // In this case, `C` should not get the file content from `A`. The clipboard is set by `B`. + // + // We can simply notify `C` to clear the clipboard when `A` received copy message from `B`, + // if connections are in the same process. + // But if connections are in different processes, it is not easy to notify the other process. + // So we just ignore the request from `C` in this case. + if (is_set_by_instance(clipboard) || is_file_descriptor_from_remote()) { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } + cbRequested = fileContentsRequest->cbRequested; if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE) cbRequested = sizeof(UINT64); @@ -3089,6 +3119,14 @@ wf_cliprdr_server_file_contents_response(CliprdrClientContext *context, return rc; } +BOOL is_set_by_instance(wfClipboard *clipboard) +{ + if (GetClipboardOwner() == clipboard->hwnd || S_OK == OleIsCurrentClipboard(clipboard->data_obj)) { + return TRUE; + } + return FALSE; +} + BOOL is_file_descriptor_from_remote() { UINT fsid = 0; @@ -3175,7 +3213,7 @@ BOOL wf_cliprdr_uninit(wfClipboard *clipboard, CliprdrClientContext *cliprdr) /* discard all contexts in clipboard */ if (try_open_clipboard(clipboard->hwnd)) { - if (is_file_descriptor_from_remote()) + if (is_set_by_instance(clipboard) || is_file_descriptor_from_remote()) { if (!EmptyClipboard()) { diff --git a/libs/hbb_common b/libs/hbb_common index 49c6b24a7..79f8ac2d6 160000 --- a/libs/hbb_common +++ b/libs/hbb_common @@ -1 +1 @@ -Subproject commit 49c6b24a7a8c39d4448e07b743007ef1a3febd43 +Subproject commit 79f8ac2d68e7b3304773c553f91f1de825bacdf5 diff --git a/src/clipboard_file.rs b/src/clipboard_file.rs index a4bfc1aef..4548cdbea 100644 --- a/src/clipboard_file.rs +++ b/src/clipboard_file.rs @@ -134,6 +134,15 @@ pub fn clip_2_msg(clip: ClipboardFile) -> Message { })), ..Default::default() }, + ClipboardFile::TryEmpty => Message { + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::TryEmpty(CliprdrTryEmpty { + ..Default::default() + })), + ..Default::default() + })), + ..Default::default() + }, } } @@ -176,6 +185,7 @@ pub fn msg_2_clip(msg: Cliprdr) -> Option { requested_data: data.requested_data.into(), }) } + Some(cliprdr::Union::TryEmpty(_)) => Some(ClipboardFile::TryEmpty), _ => None, } }