fix: win, file clipboard, try empty (#10609)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2025-01-27 16:16:44 +08:00
committed by GitHub
parent f08cb0412d
commit 55005f8129
5 changed files with 72 additions and 7 deletions

View File

@@ -107,6 +107,7 @@ pub enum ClipboardFile {
stream_id: i32,
requested_data: Vec<u8>,
},
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<()> {

View File

@@ -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
}

View File

@@ -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())
{

View File

@@ -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<ClipboardFile> {
requested_data: data.requested_data.into(),
})
}
Some(cliprdr::Union::TryEmpty(_)) => Some(ClipboardFile::TryEmpty),
_ => None,
}
}