feat: add x11 clipboard support
Signed-off-by: 蔡略 <cailue@bupt.edu.cn>
This commit is contained in:
parent
4f7036a405
commit
25cf36a948
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -946,6 +946,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"utf16string",
|
||||
"x11-clipboard",
|
||||
"x11rb 0.12.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7269,8 +7270,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "x11-clipboard"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b41aca1115b1f195f21c541c5efb423470848d48143127d0f07f8b90c27440df"
|
||||
source = "git+https://github.com/clslaid/x11-clipboard?branch=feat/store-batch#5fc2e73bc01ada3681159b34cf3ea8f0d14cd904"
|
||||
dependencies = [
|
||||
"x11rb 0.12.0",
|
||||
]
|
||||
|
@ -18,6 +18,7 @@ hbb_common = { path = "../hbb_common" }
|
||||
parking_lot = {version = "0.12"}
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||
x11rb = {version = "0.12", features = ["all-extensions"]}
|
||||
rand = {version = "0.8"}
|
||||
fuser = {version = "0.13"}
|
||||
libc = {version = "0.2"}
|
||||
@ -25,4 +26,4 @@ rayon = {version = "1.7"}
|
||||
dashmap = "5.5"
|
||||
percent-encoding = "2.3"
|
||||
utf16string = "0.2"
|
||||
x11-clipboard = "0.8"
|
||||
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch"}
|
||||
|
@ -1,39 +1,37 @@
|
||||
use cc;
|
||||
|
||||
fn build_c_impl() {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let mut build = cc::Build::new();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
build.file("src/windows/wf_cliprdr.c");
|
||||
#[cfg(target_os = "linux")]
|
||||
build.file("src/X11/xf_cliprdr.c");
|
||||
#[cfg(target_os = "macos")]
|
||||
build.file("src/OSX/Clipboard.m");
|
||||
|
||||
build.flag_if_supported("-Wno-c++0x-extensions");
|
||||
build.flag_if_supported("-Wno-return-type-c-linkage");
|
||||
build.flag_if_supported("-Wno-invalid-offsetof");
|
||||
build.flag_if_supported("-Wno-unused-parameter");
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
build.flag_if_supported("-Wno-c++0x-extensions");
|
||||
build.flag_if_supported("-Wno-return-type-c-linkage");
|
||||
build.flag_if_supported("-Wno-invalid-offsetof");
|
||||
build.flag_if_supported("-Wno-unused-parameter");
|
||||
|
||||
if build.get_compiler().is_like_msvc() {
|
||||
build.define("WIN32", "");
|
||||
// build.define("_AMD64_", "");
|
||||
build.flag("-Z7");
|
||||
build.flag("-GR-");
|
||||
// build.flag("-std:c++11");
|
||||
} else {
|
||||
build.flag("-fPIC");
|
||||
// build.flag("-std=c++11");
|
||||
// build.flag("-include");
|
||||
// build.flag(&confdefs_path.to_string_lossy());
|
||||
if build.get_compiler().is_like_msvc() {
|
||||
build.define("WIN32", "");
|
||||
// build.define("_AMD64_", "");
|
||||
build.flag("-Z7");
|
||||
build.flag("-GR-");
|
||||
// build.flag("-std:c++11");
|
||||
} else {
|
||||
build.flag("-fPIC");
|
||||
// build.flag("-std=c++11");
|
||||
// build.flag("-include");
|
||||
// build.flag(&confdefs_path.to_string_lossy());
|
||||
}
|
||||
|
||||
build.compile("mycliprdr");
|
||||
}
|
||||
|
||||
build.compile("mycliprdr");
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
println!("cargo:rerun-if-changed=src/windows/wf_cliprdr.c");
|
||||
#[cfg(target_os = "linux")]
|
||||
println!("cargo:rerun-if-changed=src/X11/xf_cliprdr.c");
|
||||
#[cfg(target_os = "macos")]
|
||||
println!("cargo:rerun-if-changed=src/OSX/Clipboard.m");
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[allow(dead_code)]
|
||||
use std::{
|
||||
ffi::{CStr, CString},
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
};
|
||||
|
||||
@ -18,7 +19,9 @@ pub mod context_send;
|
||||
pub mod platform;
|
||||
pub use context_send::*;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const ERR_CODE_SERVER_FUNCTION_NONE: u32 = 0x00000001;
|
||||
#[cfg(target_os = "windows")]
|
||||
const ERR_CODE_INVALID_PARAMETER: u32 = 0x00000002;
|
||||
|
||||
pub(crate) use platform::create_cliprdr_context;
|
||||
@ -33,7 +36,7 @@ pub trait CliprdrServiceContext: Send + Sync {
|
||||
/// set to be stopped
|
||||
fn set_is_stopped(&mut self) -> Result<(), CliprdrError>;
|
||||
/// clear the content on clipboard
|
||||
fn empty_clipboard(&mut self, conn_id: i32) -> bool;
|
||||
fn empty_clipboard(&mut self, conn_id: i32) -> Result<bool, CliprdrError>;
|
||||
|
||||
/// run as a server for clipboard RPC
|
||||
fn server_clip_file(&mut self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError>;
|
||||
@ -51,12 +54,16 @@ pub enum CliprdrError {
|
||||
ClipboardInternalError,
|
||||
#[error("cliprdr occupied")]
|
||||
ClipboardOccupied,
|
||||
#[error("content not available")]
|
||||
ContentNotAvailable,
|
||||
#[error("conversion failure")]
|
||||
ConversionFailure,
|
||||
#[error("failure to read clipboard")]
|
||||
OpenClipboard,
|
||||
#[error("failure to read file metadata or content")]
|
||||
FileError { path: PathBuf, err: std::io::Error },
|
||||
#[error("invalid request")]
|
||||
InvalidRequest { description: String },
|
||||
#[error("unknown cliprdr error")]
|
||||
Unknown { description: String },
|
||||
Unknown(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,21 +1,54 @@
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fs::File,
|
||||
os::unix::prelude::FileExt,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use crate::CliprdrError;
|
||||
use dashmap::DashMap;
|
||||
use fuser::MountOption;
|
||||
use hbb_common::{
|
||||
bytes::{BufMut, BytesMut},
|
||||
log,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
use utf16string::WString;
|
||||
|
||||
use super::fuse::{self, FuseServer};
|
||||
use crate::{send_data, ClipboardFile, CliprdrError, CliprdrServiceContext};
|
||||
|
||||
use super::{fuse::FuseServer, LDAP_EPOCH_DELTA};
|
||||
|
||||
#[cfg(not(feature = "wayland"))]
|
||||
pub mod x11;
|
||||
|
||||
trait SysClipboard {
|
||||
// not actual format id, just a placeholder
|
||||
const FILEDESCRIPTOR_FORMAT_ID: i32 = 49334;
|
||||
const FILEDESCRIPTORW_FORMAT_NAME: &str = "FileGroupDescriptorW";
|
||||
// not actual format id, just a placeholder
|
||||
const FILECONTENTS_FORMAT_ID: i32 = 49267;
|
||||
const FILECONTENTS_FORMAT_NAME: &str = "FileContents";
|
||||
|
||||
lazy_static! {
|
||||
static ref REMOTE_FORMAT_MAP: DashMap<i32, String> = DashMap::new();
|
||||
}
|
||||
|
||||
fn get_local_format(remote_id: i32) -> Option<String> {
|
||||
REMOTE_FORMAT_MAP.get(&remote_id).map(|s| s.clone())
|
||||
}
|
||||
|
||||
fn add_remote_format(local_name: &str, remote_id: i32) {
|
||||
REMOTE_FORMAT_MAP.insert(remote_id, local_name.to_string());
|
||||
}
|
||||
|
||||
trait SysClipboard: Send + Sync {
|
||||
fn wait_file_list(&self) -> Result<Vec<PathBuf>, CliprdrError>;
|
||||
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError>;
|
||||
}
|
||||
|
||||
fn get_sys_clipboard() -> Box<dyn SysClipboard> {
|
||||
fn get_sys_clipboard() -> Result<Box<dyn SysClipboard>, CliprdrError> {
|
||||
#[cfg(feature = "wayland")]
|
||||
{
|
||||
unimplemented!()
|
||||
@ -23,7 +56,8 @@ fn get_sys_clipboard() -> Box<dyn SysClipboard> {
|
||||
#[cfg(not(feature = "wayland"))]
|
||||
{
|
||||
pub use x11::*;
|
||||
X11Clipboard::new()
|
||||
let x11_clip = X11Clipboard::new()?;
|
||||
Ok(Box::new(x11_clip) as Box<_>)
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,18 +106,6 @@ fn parse_plain_uri_list(v: Vec<u8>) -> Result<Vec<PathBuf>, CliprdrError> {
|
||||
parse_uri_list(&text)
|
||||
}
|
||||
|
||||
// helper parse function
|
||||
// convert "x-special/gnome-copied-files", "x-special/x-kde-cutselection" and "x-special/nautilus-clipboard" data to a list of valid Paths
|
||||
// # Note
|
||||
// - none utf8 data will lead to error
|
||||
fn parse_de_uri_list(v: Vec<u8>) -> Result<Vec<PathBuf>, CliprdrError> {
|
||||
let text = String::from_utf8(v).map_err(|_| CliprdrError::ConversionFailure)?;
|
||||
let plain_list = text
|
||||
.trim_start_matches("copy\n")
|
||||
.trim_start_matches("cut\n");
|
||||
parse_uri_list(plain_list)
|
||||
}
|
||||
|
||||
// helper parse function
|
||||
// convert 'text/uri-list' data to a list of valid Paths
|
||||
// # Note
|
||||
@ -92,6 +114,9 @@ fn parse_uri_list(text: &str) -> Result<Vec<PathBuf>, CliprdrError> {
|
||||
let mut list = Vec::new();
|
||||
|
||||
for line in text.lines() {
|
||||
if !line.starts_with("file://") {
|
||||
continue;
|
||||
}
|
||||
let decoded = parse_uri_to_path(line)?;
|
||||
list.push(decoded)
|
||||
}
|
||||
@ -99,37 +124,590 @@ fn parse_uri_list(text: &str) -> Result<Vec<PathBuf>, CliprdrError> {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClipboardContext {
|
||||
pub stop: bool,
|
||||
pub fuse_mount_point: PathBuf,
|
||||
pub fuse_server: FuseServer,
|
||||
pub file_list: HashSet<PathBuf>,
|
||||
pub clipboard: Clipboard,
|
||||
struct LocalFile {
|
||||
pub path: PathBuf,
|
||||
pub handle: Option<File>,
|
||||
|
||||
pub bkg_session: fuser::BackgroundSession,
|
||||
pub name: String,
|
||||
pub size: u64,
|
||||
pub last_write_time: SystemTime,
|
||||
pub is_dir: bool,
|
||||
pub read_only: bool,
|
||||
pub hidden: bool,
|
||||
pub system: bool,
|
||||
pub archive: bool,
|
||||
pub normal: bool,
|
||||
}
|
||||
|
||||
impl LocalFile {
|
||||
pub fn try_open(path: &PathBuf) -> Result<Self, CliprdrError> {
|
||||
let mt = std::fs::metadata(path).map_err(|e| CliprdrError::FileError {
|
||||
path: path.clone(),
|
||||
err: e,
|
||||
})?;
|
||||
let size = mt.len() as u64;
|
||||
let is_dir = mt.is_dir();
|
||||
let read_only = mt.permissions().readonly();
|
||||
let system = false;
|
||||
let hidden = false;
|
||||
let archive = false;
|
||||
let normal = !is_dir;
|
||||
let last_write_time = mt.modified().unwrap_or(SystemTime::UNIX_EPOCH);
|
||||
|
||||
let name = path
|
||||
.display()
|
||||
.to_string()
|
||||
.trim_start_matches('/')
|
||||
.replace('/', "\\");
|
||||
|
||||
let handle = if is_dir {
|
||||
None
|
||||
} else {
|
||||
let file = std::fs::File::open(path).map_err(|e| CliprdrError::FileError {
|
||||
path: path.clone(),
|
||||
err: e,
|
||||
})?;
|
||||
let reader = file;
|
||||
Some(reader)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
path: path.clone(),
|
||||
handle,
|
||||
size,
|
||||
last_write_time,
|
||||
is_dir,
|
||||
read_only,
|
||||
system,
|
||||
hidden,
|
||||
archive,
|
||||
normal,
|
||||
})
|
||||
}
|
||||
pub fn as_bin(&self) -> Vec<u8> {
|
||||
let mut buf = BytesMut::with_capacity(592);
|
||||
|
||||
let read_only_flag = if self.read_only { 0x1 } else { 0 };
|
||||
let hidden_flag = if self.hidden { 0x2 } else { 0 };
|
||||
let system_flag = if self.system { 0x4 } else { 0 };
|
||||
let directory_flag = if self.is_dir { 0x10 } else { 0 };
|
||||
let archive_flag = if self.archive { 0x20 } else { 0 };
|
||||
let normal_flag = if self.normal { 0x80 } else { 0 };
|
||||
|
||||
let file_attributes: u32 = read_only_flag
|
||||
| hidden_flag
|
||||
| system_flag
|
||||
| directory_flag
|
||||
| archive_flag
|
||||
| normal_flag;
|
||||
|
||||
let win32_time = self
|
||||
.last_write_time
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos() as u64
|
||||
/ 100
|
||||
+ LDAP_EPOCH_DELTA;
|
||||
|
||||
let size_high = (self.size >> 32) as u32;
|
||||
let size_low = (self.size & (u32::MAX as u64)) as u32;
|
||||
|
||||
let wstr: WString<utf16string::LE> = WString::from(&self.name);
|
||||
let name = wstr.as_bytes();
|
||||
|
||||
let flags = 0x4064;
|
||||
|
||||
// flags, 4 bytes
|
||||
buf.put_u32_le(flags);
|
||||
// 32 bytes reserved
|
||||
buf.put(&[0u8; 32][..]);
|
||||
// file attributes, 4 bytes
|
||||
buf.put_u32_le(file_attributes);
|
||||
// 16 bytes reserved
|
||||
buf.put(&[0u8; 16][..]);
|
||||
// last write time, 8 bytes
|
||||
buf.put_u64_le(win32_time);
|
||||
// file size (high)
|
||||
buf.put_u32_le(size_high);
|
||||
// file size (low)
|
||||
buf.put_u32_le(size_low);
|
||||
// put name and padding to 520 bytes
|
||||
let name_len = name.len();
|
||||
buf.put(name);
|
||||
buf.put(&vec![0u8; 520 - name_len][..]);
|
||||
|
||||
buf.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_file_list(paths: &[PathBuf]) -> Result<Vec<LocalFile>, CliprdrError> {
|
||||
fn constr_file_lst(
|
||||
path: &PathBuf,
|
||||
file_list: &mut Vec<LocalFile>,
|
||||
visited: &mut HashSet<PathBuf>,
|
||||
) -> Result<(), CliprdrError> {
|
||||
// prevent fs loop
|
||||
if visited.contains(path) {
|
||||
return Ok(());
|
||||
}
|
||||
visited.insert(path.clone());
|
||||
|
||||
let local_file = LocalFile::try_open(path)?;
|
||||
file_list.push(local_file);
|
||||
|
||||
let mt = std::fs::metadata(path).map_err(|e| CliprdrError::FileError {
|
||||
path: path.clone(),
|
||||
err: e,
|
||||
})?;
|
||||
if mt.is_dir() {
|
||||
let dir = std::fs::read_dir(path).unwrap();
|
||||
for entry in dir {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
constr_file_lst(&path, file_list, visited)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let mut file_list = Vec::new();
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
for path in paths {
|
||||
constr_file_lst(path, &mut file_list, &mut visited)?;
|
||||
}
|
||||
Ok(file_list)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FileContentsRequest {
|
||||
Size {
|
||||
stream_id: i32,
|
||||
file_idx: usize,
|
||||
},
|
||||
|
||||
Range {
|
||||
stream_id: i32,
|
||||
file_idx: usize,
|
||||
offset: u64,
|
||||
length: u64,
|
||||
},
|
||||
}
|
||||
|
||||
/// this is a proxy type for the clipboard context
|
||||
pub struct CliprdrClient {
|
||||
pub context: Arc<ClipboardContext>,
|
||||
}
|
||||
|
||||
impl CliprdrServiceContext for CliprdrClient {
|
||||
fn set_is_stopped(&mut self) -> Result<(), CliprdrError> {
|
||||
self.context.set_is_stopped()
|
||||
}
|
||||
|
||||
fn empty_clipboard(&mut self, conn_id: i32) -> Result<bool, CliprdrError> {
|
||||
self.context.empty_clipboard(conn_id)
|
||||
}
|
||||
|
||||
fn server_clip_file(&mut self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError> {
|
||||
self.context.serve(conn_id, msg)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClipboardContext {
|
||||
pub stop: AtomicBool,
|
||||
pub fuse_mount_point: PathBuf,
|
||||
fuse_server: Arc<FuseServer>,
|
||||
file_list: RwLock<Vec<LocalFile>>,
|
||||
clipboard: Arc<dyn SysClipboard>,
|
||||
}
|
||||
|
||||
impl ClipboardContext {
|
||||
fn new(timeout: Duration, mount_path: PathBuf) -> Result<Self, CliprdrError> {
|
||||
pub fn new(timeout: Duration, mount_path: PathBuf) -> Result<Self, CliprdrError> {
|
||||
// assert mount path exists
|
||||
let mountpoint = mount_path
|
||||
.canonicalize()
|
||||
.map_err(|e| CliprdrError::Unknown {
|
||||
description: format!("invalid mount point: {:?}", e),
|
||||
})?;
|
||||
let fuse_server = FuseServer::new(timeout);
|
||||
let mnt_opts = [
|
||||
fuser::MountOption::FSName("clipboard".to_string()),
|
||||
fuser::MountOption::NoAtime,
|
||||
fuser::MountOption::RO,
|
||||
fuser::MountOption::NoExec,
|
||||
];
|
||||
let bkg_session = fuser::spawn_mount2(fuse_server, mountpoint, &mnt_opts).map_err(|e| {
|
||||
CliprdrError::Unknown {
|
||||
description: format!("failed to mount fuse: {:?}", e),
|
||||
}
|
||||
let fuse_mount_point = mount_path.canonicalize().map_err(|e| {
|
||||
log::error!("failed to canonicalize mount path: {:?}", e);
|
||||
CliprdrError::CliprdrInit
|
||||
})?;
|
||||
|
||||
log::debug!("mounting clipboard fuse to {}", mount_path.display());
|
||||
let fuse_server = Arc::new(FuseServer::new(timeout));
|
||||
let clipboard = get_sys_clipboard()?;
|
||||
let clipboard = Arc::from(clipboard);
|
||||
let file_list = RwLock::new(vec![]);
|
||||
|
||||
Ok(Self {
|
||||
stop: AtomicBool::new(false),
|
||||
fuse_mount_point,
|
||||
fuse_server,
|
||||
file_list,
|
||||
clipboard,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn client(self: Arc<Self>) -> CliprdrClient {
|
||||
CliprdrClient { context: self }
|
||||
}
|
||||
|
||||
// mount and run fuse server, blocking
|
||||
pub fn mount(&self) -> Result<(), CliprdrError> {
|
||||
let mount_opts = [
|
||||
MountOption::FSName("rustdesk-cliprdr-fs".to_string()),
|
||||
MountOption::RO,
|
||||
MountOption::NoAtime,
|
||||
];
|
||||
let fuse_client = self.fuse_server.client();
|
||||
fuser::mount2(fuse_client, self.fuse_mount_point.clone(), &mount_opts).map_err(|e| {
|
||||
log::error!("failed to mount fuse: {:?}", e);
|
||||
CliprdrError::CliprdrInit
|
||||
})
|
||||
}
|
||||
|
||||
pub fn listen_clipboard(&self) -> Result<(), CliprdrError> {
|
||||
while let Ok(v) = self.clipboard.wait_file_list() {
|
||||
let filtered: Vec<_> = v
|
||||
.into_iter()
|
||||
.filter(|pb| !pb.starts_with(&self.fuse_mount_point))
|
||||
.collect();
|
||||
if filtered.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// construct format list update and send
|
||||
let data = ClipboardFile::FormatList {
|
||||
format_list: vec![
|
||||
(
|
||||
FILEDESCRIPTOR_FORMAT_ID,
|
||||
FILEDESCRIPTORW_FORMAT_NAME.to_string(),
|
||||
),
|
||||
(FILECONTENTS_FORMAT_ID, FILECONTENTS_FORMAT_NAME.to_string()),
|
||||
],
|
||||
};
|
||||
|
||||
send_data(0, data)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_format_list(&self, conn_id: i32) -> Result<(), CliprdrError> {
|
||||
let data = self.clipboard.wait_file_list()?;
|
||||
let filtered: Vec<_> = data
|
||||
.into_iter()
|
||||
.filter(|pb| !pb.starts_with(&self.fuse_mount_point))
|
||||
.collect();
|
||||
if filtered.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let format_list = ClipboardFile::FormatList {
|
||||
format_list: vec![
|
||||
(
|
||||
FILEDESCRIPTOR_FORMAT_ID,
|
||||
FILEDESCRIPTORW_FORMAT_NAME.to_string(),
|
||||
),
|
||||
(FILECONTENTS_FORMAT_ID, FILECONTENTS_FORMAT_NAME.to_string()),
|
||||
],
|
||||
};
|
||||
|
||||
send_data(conn_id, format_list);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_file_list(&self, conn_id: i32) -> Result<(), CliprdrError> {
|
||||
let data = self.clipboard.wait_file_list()?;
|
||||
let filtered: Vec<_> = data
|
||||
.into_iter()
|
||||
.filter(|pb| !pb.starts_with(&self.fuse_mount_point))
|
||||
.collect();
|
||||
|
||||
let files = construct_file_list(filtered.as_slice())?;
|
||||
|
||||
let mut data = BytesMut::with_capacity(4 + 592 * files.len());
|
||||
data.put_u32_le(filtered.len() as u32);
|
||||
for file in files.iter() {
|
||||
data.put(file.as_bin().as_slice());
|
||||
}
|
||||
|
||||
{
|
||||
let mut w_list = self.file_list.write();
|
||||
*w_list = files;
|
||||
}
|
||||
|
||||
let format_data = data.to_vec();
|
||||
|
||||
send_data(
|
||||
conn_id,
|
||||
ClipboardFile::FormatDataResponse {
|
||||
msg_flags: 1,
|
||||
format_data,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serve_file_contents(
|
||||
&self,
|
||||
conn_id: i32,
|
||||
request: FileContentsRequest,
|
||||
) -> Result<(), CliprdrError> {
|
||||
log::debug!("file contents (range) requested from conn: {}", conn_id);
|
||||
let file_contents_req = match request {
|
||||
FileContentsRequest::Size {
|
||||
stream_id,
|
||||
file_idx,
|
||||
} => {
|
||||
let file_list = self.file_list.read();
|
||||
let Some(file) = file_list.get(file_idx) else {
|
||||
log::error!(
|
||||
"invalid file index {} requested from conn: {}",
|
||||
file_idx,
|
||||
conn_id
|
||||
);
|
||||
resp_file_contents_fail(conn_id, stream_id);
|
||||
|
||||
return Err(CliprdrError::InvalidRequest {
|
||||
description: format!(
|
||||
"invalid file index {} requested from conn: {}",
|
||||
file_idx, conn_id
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
log::debug!("conn {} requested file {}", conn_id, file.name);
|
||||
|
||||
let size = file.size;
|
||||
ClipboardFile::FileContentsResponse {
|
||||
msg_flags: 0x1,
|
||||
stream_id,
|
||||
requested_data: size.to_le_bytes().to_vec(),
|
||||
}
|
||||
}
|
||||
FileContentsRequest::Range {
|
||||
stream_id,
|
||||
file_idx,
|
||||
offset,
|
||||
length,
|
||||
} => {
|
||||
let file_list = self.file_list.read();
|
||||
let Some(file) = file_list.get(file_idx) else {
|
||||
log::error!(
|
||||
"invalid file index {} requested from conn: {}",
|
||||
file_idx,
|
||||
conn_id
|
||||
);
|
||||
resp_file_contents_fail(conn_id, stream_id);
|
||||
return Err(CliprdrError::InvalidRequest {
|
||||
description: format!(
|
||||
"invalid file index {} requested from conn: {}",
|
||||
file_idx, conn_id
|
||||
),
|
||||
});
|
||||
};
|
||||
log::debug!("conn {} requested file {}", conn_id, file.name);
|
||||
|
||||
let Some(handle) = &file.handle else {
|
||||
log::error!(
|
||||
"invalid file index {} requested from conn: {}",
|
||||
file_idx,
|
||||
conn_id
|
||||
);
|
||||
resp_file_contents_fail(conn_id, stream_id);
|
||||
|
||||
return Err(CliprdrError::InvalidRequest {
|
||||
description: format!(
|
||||
"request to read directory on index {} as file from conn: {}",
|
||||
file_idx, conn_id
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
if offset > file.size {
|
||||
log::error!("invalid reading offset requested from conn: {}", conn_id);
|
||||
resp_file_contents_fail(conn_id, stream_id);
|
||||
|
||||
return Err(CliprdrError::InvalidRequest {
|
||||
description: format!(
|
||||
"invalid reading offset requested from conn: {}",
|
||||
conn_id
|
||||
),
|
||||
});
|
||||
}
|
||||
let read_size = if offset + length > file.size {
|
||||
file.size - offset
|
||||
} else {
|
||||
length
|
||||
};
|
||||
|
||||
let mut buf = vec![0u8; read_size as usize];
|
||||
|
||||
handle
|
||||
.read_exact_at(&mut buf, offset)
|
||||
.map_err(|e| CliprdrError::FileError {
|
||||
path: file.path.clone(),
|
||||
err: e,
|
||||
})?;
|
||||
|
||||
ClipboardFile::FileContentsResponse {
|
||||
msg_flags: 0x1,
|
||||
stream_id,
|
||||
requested_data: buf,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
send_data(conn_id, file_contents_req);
|
||||
log::debug!("file contents sent to conn: {}", conn_id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn resp_file_contents_fail(conn_id: i32, stream_id: i32) {
|
||||
let resp = ClipboardFile::FileContentsResponse {
|
||||
msg_flags: 0x2,
|
||||
stream_id,
|
||||
requested_data: vec![],
|
||||
};
|
||||
send_data(conn_id, resp)
|
||||
}
|
||||
|
||||
impl ClipboardContext {
|
||||
pub fn set_is_stopped(&self) -> Result<(), CliprdrError> {
|
||||
// do nothing
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn empty_clipboard(&self, conn_id: i32) -> Result<bool, CliprdrError> {
|
||||
// gc all files, the clipboard is going to shutdown
|
||||
self.fuse_server
|
||||
.update_files(conn_id, FILEDESCRIPTOR_FORMAT_ID, FILECONTENTS_FORMAT_ID)
|
||||
}
|
||||
|
||||
pub fn serve(&self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError> {
|
||||
match msg {
|
||||
ClipboardFile::NotifyCallback { .. } => {
|
||||
unreachable!()
|
||||
}
|
||||
ClipboardFile::MonitorReady => {
|
||||
log::debug!("server_monitor_ready called");
|
||||
|
||||
// ignore capabilities for now
|
||||
|
||||
self.send_file_list(0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ClipboardFile::FormatList { format_list } => {
|
||||
// filter out "FileGroupDescriptorW" and "FileContents"
|
||||
let fmt_lst: Vec<(i32, String)> = format_list
|
||||
.into_iter()
|
||||
.filter(|(_, name)| {
|
||||
name == FILEDESCRIPTORW_FORMAT_NAME || name == FILECONTENTS_FORMAT_NAME
|
||||
})
|
||||
.collect();
|
||||
if fmt_lst.len() != 2 {
|
||||
log::debug!("no supported formats");
|
||||
return Ok(());
|
||||
}
|
||||
log::debug!("supported formats: {:?}", fmt_lst);
|
||||
let file_contents_id = fmt_lst
|
||||
.iter()
|
||||
.find(|(_, name)| name == FILECONTENTS_FORMAT_NAME)
|
||||
.map(|(id, _)| *id)
|
||||
.unwrap();
|
||||
let file_descriptor_id = fmt_lst
|
||||
.iter()
|
||||
.find(|(_, name)| name == FILEDESCRIPTORW_FORMAT_NAME)
|
||||
.map(|(id, _)| *id)
|
||||
.unwrap();
|
||||
|
||||
add_remote_format(FILECONTENTS_FORMAT_NAME, file_contents_id);
|
||||
add_remote_format(FILEDESCRIPTORW_FORMAT_NAME, file_descriptor_id);
|
||||
self.fuse_server
|
||||
.update_files(conn_id, file_descriptor_id, file_contents_id)?;
|
||||
Ok(())
|
||||
}
|
||||
ClipboardFile::FormatListResponse { msg_flags } => {
|
||||
if msg_flags != 0x1 {
|
||||
self.send_format_list(conn_id)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
ClipboardFile::FormatDataRequest {
|
||||
requested_format_id,
|
||||
} => {
|
||||
let Some(format) = get_local_format(requested_format_id) else {
|
||||
log::error!(
|
||||
"got unsupported format data request: id={} from conn={}",
|
||||
requested_format_id,
|
||||
conn_id
|
||||
);
|
||||
resp_format_data_failure(conn_id);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if format == FILEDESCRIPTORW_FORMAT_NAME {
|
||||
self.send_file_list(requested_format_id)?;
|
||||
} else if format == FILECONTENTS_FORMAT_NAME {
|
||||
log::error!(
|
||||
"try to read file contents with FormatDataRequest from conn={}",
|
||||
conn_id
|
||||
);
|
||||
resp_format_data_failure(conn_id);
|
||||
} else {
|
||||
log::error!(
|
||||
"got unsupported format data request: id={} from conn={}",
|
||||
requested_format_id,
|
||||
conn_id
|
||||
);
|
||||
resp_format_data_failure(conn_id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ClipboardFile::FormatDataResponse { .. }
|
||||
| ClipboardFile::FileContentsResponse { .. } => {
|
||||
self.fuse_server.recv(conn_id, msg);
|
||||
Ok(())
|
||||
}
|
||||
ClipboardFile::FileContentsRequest {
|
||||
stream_id,
|
||||
list_index,
|
||||
dw_flags,
|
||||
n_position_low,
|
||||
n_position_high,
|
||||
cb_requested,
|
||||
..
|
||||
} => {
|
||||
let fcr = if dw_flags == 0x1 {
|
||||
FileContentsRequest::Size {
|
||||
stream_id,
|
||||
file_idx: list_index as usize,
|
||||
}
|
||||
} else if dw_flags == 0x2 {
|
||||
let offset = (n_position_high as u64) << 32 | n_position_low as u64;
|
||||
let length = cb_requested as u64;
|
||||
|
||||
FileContentsRequest::Range {
|
||||
stream_id,
|
||||
file_idx: list_index as usize,
|
||||
offset,
|
||||
length,
|
||||
}
|
||||
} else {
|
||||
log::error!("got invalid FileContentsRequest from conn={}", conn_id);
|
||||
resp_file_contents_fail(conn_id, stream_id);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
self.serve_file_contents(conn_id, fcr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resp_format_data_failure(conn_id: i32) {
|
||||
let data = ClipboardFile::FormatDataResponse {
|
||||
msg_flags: 0x2,
|
||||
format_data: vec![],
|
||||
};
|
||||
send_data(conn_id, data)
|
||||
}
|
||||
|
@ -1,7 +1,69 @@
|
||||
use super::SysClipboard;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct X11Clipboard {}
|
||||
use x11_clipboard::Clipboard;
|
||||
use x11rb::protocol::xproto::Atom;
|
||||
|
||||
use crate::CliprdrError;
|
||||
|
||||
use super::{encode_path_to_uri, parse_plain_uri_list, SysClipboard};
|
||||
|
||||
pub struct X11Clipboard {
|
||||
text_uri_list: Atom,
|
||||
gnome_copied_files: Atom,
|
||||
clipboard: Clipboard,
|
||||
}
|
||||
|
||||
impl X11Clipboard {
|
||||
pub fn new() -> Result<Self, CliprdrError> {
|
||||
let clipboard = Clipboard::new().map_err(|_| CliprdrError::CliprdrInit)?;
|
||||
let text_uri_list = clipboard
|
||||
.setter
|
||||
.get_atom("text/uri-list")
|
||||
.map_err(|_| CliprdrError::CliprdrInit)?;
|
||||
let gnome_copied_files = clipboard
|
||||
.setter
|
||||
.get_atom("x-special/gnome-copied-files")
|
||||
.map_err(|_| CliprdrError::CliprdrInit)?;
|
||||
Ok(Self {
|
||||
text_uri_list,
|
||||
gnome_copied_files,
|
||||
clipboard,
|
||||
})
|
||||
}
|
||||
|
||||
fn load(&self, target: Atom) -> Result<Vec<u8>, CliprdrError> {
|
||||
let clip = self.clipboard.setter.atoms.clipboard;
|
||||
let prop = self.clipboard.setter.atoms.property;
|
||||
self.clipboard
|
||||
.load_wait(clip, target, prop)
|
||||
.map_err(|_| CliprdrError::ConversionFailure)
|
||||
}
|
||||
|
||||
fn store_batch(&self, batch: Vec<(Atom, Vec<u8>)>) -> Result<(), CliprdrError> {
|
||||
let clip = self.clipboard.setter.atoms.clipboard;
|
||||
self.clipboard
|
||||
.store_batch(clip, batch)
|
||||
.map_err(|_| CliprdrError::ClipboardInternalError)
|
||||
}
|
||||
}
|
||||
|
||||
impl SysClipboard for X11Clipboard {
|
||||
todo!()
|
||||
fn wait_file_list(&self) -> Result<Vec<PathBuf>, CliprdrError> {
|
||||
let v = self.load(self.text_uri_list)?;
|
||||
// loading 'text/uri-list' should be enough?
|
||||
parse_plain_uri_list(v)
|
||||
}
|
||||
|
||||
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
|
||||
let uri_list: Vec<String> = paths.iter().map(|pb| encode_path_to_uri(pb)).collect();
|
||||
let uri_list = uri_list.join("\n");
|
||||
let text_uri_list_data = uri_list.as_bytes().to_vec();
|
||||
let gnome_copied_files_data = vec!["copy\n".as_bytes(), uri_list.as_bytes()].concat();
|
||||
let batch = vec![
|
||||
(self.text_uri_list, text_uri_list_data),
|
||||
(self.gnome_copied_files, gnome_copied_files_data),
|
||||
];
|
||||
self.store_batch(batch)
|
||||
.map_err(|_| CliprdrError::ClipboardInternalError)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use parking_lot::{Condvar, Mutex};
|
||||
use crate::{CliprdrError, CliprdrServiceContext};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod windows;
|
||||
@ -19,8 +19,48 @@ pub mod linux;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn create_cliprdr_context(
|
||||
enable_files: bool,
|
||||
enable_others: bool,
|
||||
_enable_others: bool,
|
||||
response_wait_timeout_secs: u32,
|
||||
) -> crate::ResultType<Box<dyn crate::CliprdrServiceContext>> {
|
||||
unimplemented!()
|
||||
use std::sync::Arc;
|
||||
|
||||
if !enable_files {
|
||||
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
|
||||
}
|
||||
|
||||
let timeout = std::time::Duration::from_secs(response_wait_timeout_secs as u64);
|
||||
let mut tmp_path = std::env::temp_dir();
|
||||
tmp_path.push("rustdesk-cliprdr");
|
||||
let rd_mnt = tmp_path;
|
||||
std::fs::create_dir(rd_mnt.clone())?;
|
||||
let linux_ctx = Arc::new(linux::ClipboardContext::new(timeout, rd_mnt)?);
|
||||
|
||||
let fuse_ctx = linux_ctx.clone();
|
||||
std::thread::spawn(move || fuse_ctx.mount());
|
||||
let clipboard_listen_ctx = linux_ctx.clone();
|
||||
std::thread::spawn(move || clipboard_listen_ctx.listen_clipboard());
|
||||
|
||||
Ok(Box::new(linux_ctx.client()) as Box<_>)
|
||||
}
|
||||
|
||||
struct DummyCliprdrContext {}
|
||||
|
||||
impl CliprdrServiceContext for DummyCliprdrContext {
|
||||
fn set_is_stopped(&mut self) -> Result<(), CliprdrError> {
|
||||
Ok(())
|
||||
}
|
||||
fn empty_clipboard(&mut self, _conn_id: i32) -> Result<bool, CliprdrError> {
|
||||
Ok(true)
|
||||
}
|
||||
fn server_clip_file(
|
||||
&mut self,
|
||||
_conn_id: i32,
|
||||
_msg: crate::ClipboardFile,
|
||||
) -> Result<(), crate::CliprdrError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// begin of epoch used by microsoft
|
||||
// 1601-01-01 00:00:00 + LDAP_EPOCH_DELTA*(100 ns) = 1970-01-01 00:00:00
|
||||
const LDAP_EPOCH_DELTA: u64 = 116444772610000000;
|
||||
|
@ -5,7 +5,7 @@ use std::sync::{
|
||||
Arc,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
use clipboard::ContextSend;
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
use hbb_common::config::{PeerConfig, TransferSerde};
|
||||
@ -20,7 +20,7 @@ use hbb_common::rendezvous_proto::ConnType;
|
||||
use hbb_common::sleep;
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
use hbb_common::tokio::sync::mpsc::error::TryRecvError;
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
use hbb_common::tokio::sync::Mutex as TokioMutex;
|
||||
use hbb_common::tokio::{
|
||||
self,
|
||||
@ -57,7 +57,7 @@ pub struct Remote<T: InvokeUiSession> {
|
||||
timer: Interval,
|
||||
last_update_jobs_status: (Instant, HashMap<i32, u64>),
|
||||
first_frame: bool,
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
client_conn_id: i32, // used for file clipboard
|
||||
data_count: Arc<AtomicUsize>,
|
||||
frame_count: Arc<AtomicUsize>,
|
||||
@ -91,7 +91,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
timer: time::interval(SEC30),
|
||||
last_update_jobs_status: (Instant::now(), Default::default()),
|
||||
first_frame: false,
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
client_conn_id: 0,
|
||||
data_count: Arc::new(AtomicUsize::new(0)),
|
||||
frame_count,
|
||||
@ -131,14 +131,14 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
|
||||
// just build for now
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
||||
let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::<i32>();
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
let (_tx_holder, rx) = mpsc::unbounded_channel();
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
let mut rx_clip_client_lock = Arc::new(TokioMutex::new(rx));
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
{
|
||||
let is_conn_not_default = self.handler.is_file_transfer()
|
||||
|| self.handler.is_port_forward()
|
||||
@ -148,7 +148,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
clipboard::get_rx_cliprdr_client(&self.handler.session_id);
|
||||
};
|
||||
}
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
let mut rx_clip_client = rx_clip_client_lock.lock().await;
|
||||
|
||||
let mut status_timer = time::interval(Duration::new(1, 0));
|
||||
@ -204,7 +204,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
}
|
||||
_msg = rx_clip_client.recv() => {
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os="windows", target_os="linux"))]
|
||||
match _msg {
|
||||
Some(clip) => match clip {
|
||||
clipboard::ClipboardFile::NotifyCallback{r#type, title, text} => {
|
||||
@ -278,11 +278,11 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
Client::try_stop_clipboard(&self.handler.session_id);
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
{
|
||||
let conn_id = self.client_conn_id;
|
||||
let _ = ContextSend::proc(|context| -> ResultType<()> {
|
||||
context.empty_clipboard(conn_id);
|
||||
context.empty_clipboard(conn_id)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
@ -1031,7 +1031,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
Some(message::Union::Cliprdr(clip)) => {
|
||||
self.handle_cliprdr_msg(clip);
|
||||
}
|
||||
@ -1551,7 +1551,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
|
||||
#[cfg(not(feature = "flutter"))]
|
||||
fn check_clipboard_file_context(&self) {
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
{
|
||||
let enabled = *self.handler.server_file_transfer_enabled.read().unwrap()
|
||||
&& self.handler.lc.read().unwrap().enable_file_transfer.v;
|
||||
@ -1559,7 +1559,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
fn handle_cliprdr_msg(&self, clip: hbb_common::message_proto::Cliprdr) {
|
||||
#[cfg(feature = "flutter")]
|
||||
if let Some(hbb_common::message_proto::cliprdr::Union::FormatList(_)) = &clip.union {
|
||||
|
@ -59,7 +59,7 @@ mod ui_session_interface;
|
||||
|
||||
mod hbbs_http;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
pub mod clipboard_file;
|
||||
|
||||
#[cfg(windows)]
|
||||
|
@ -63,8 +63,8 @@ pub fn make_tray() -> hbb_common::ResultType<()> {
|
||||
let open_func = move || {
|
||||
#[cfg(not(feature = "flutter"))]
|
||||
{
|
||||
crate::run_me::<&str>(vec![]).ok();
|
||||
return;
|
||||
crate::run_me::<&str>(vec![]).ok();
|
||||
return;
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
crate::platform::macos::handle_application_should_open_untitled_file();
|
||||
|
@ -1,6 +1,6 @@
|
||||
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||
use std::iter::FromIterator;
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@ -15,11 +15,11 @@ use std::{
|
||||
use crate::ipc::Connection;
|
||||
#[cfg(not(any(target_os = "ios")))]
|
||||
use crate::ipc::{self, Data};
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
use clipboard::ContextSend;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
use hbb_common::tokio::sync::mpsc::unbounded_channel;
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
use hbb_common::tokio::sync::Mutex as TokioMutex;
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
@ -71,9 +71,9 @@ struct IpcTaskRunner<T: InvokeUiCM> {
|
||||
running: bool,
|
||||
authorized: bool,
|
||||
conn_id: i32,
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
file_transfer_enabled: bool,
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
file_transfer_enabled_peer: bool,
|
||||
}
|
||||
|
||||
@ -174,10 +174,10 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
||||
.map(|c| c.disconnected = true);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
{
|
||||
let _ = ContextSend::proc(|context| -> ResultType<()> {
|
||||
context.empty_clipboard(id);
|
||||
context.empty_clipboard(id)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
@ -318,11 +318,11 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
// for tmp use, without real conn id
|
||||
let mut write_jobs: Vec<fs::TransferJob> = Vec::new();
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
let rx_clip1;
|
||||
let mut rx_clip;
|
||||
let _tx_clip;
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
if self.conn_id > 0 && self.authorized {
|
||||
rx_clip1 = clipboard::get_rx_cliprdr_server(self.conn_id);
|
||||
rx_clip = rx_clip1.lock().await;
|
||||
@ -332,12 +332,12 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
rx_clip1 = Arc::new(TokioMutex::new(rx_clip2));
|
||||
rx_clip = rx_clip1.lock().await;
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
||||
{
|
||||
(_tx_clip, rx_clip) = unbounded_channel::<i32>();
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
{
|
||||
if ContextSend::is_enabled() {
|
||||
allow_err!(
|
||||
@ -402,7 +402,7 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
Data::ClipboardFile(_clip) => {
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(windows, linux))]
|
||||
{
|
||||
let is_stopping_allowed = _clip.is_stopping_allowed_from_peer();
|
||||
let is_clipboard_enabled = ContextSend::is_enabled();
|
||||
@ -423,7 +423,7 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
}
|
||||
}
|
||||
Data::ClipboardFileEnabled(_enabled) => {
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os= "windows",target_os ="linux"))]
|
||||
{
|
||||
self.file_transfer_enabled_peer = _enabled;
|
||||
}
|
||||
@ -468,7 +468,7 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
}
|
||||
clip_file = rx_clip.recv() => match clip_file {
|
||||
Some(_clip) => {
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os ="linux"))]
|
||||
{
|
||||
let is_stopping_allowed = _clip.is_stopping_allowed();
|
||||
let is_clipboard_enabled = ContextSend::is_enabled();
|
||||
@ -505,9 +505,9 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
running: true,
|
||||
authorized: false,
|
||||
conn_id: 0,
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
file_transfer_enabled: false,
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
file_transfer_enabled_peer: false,
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user