Merge pull request #6227 from ClSlaid/feat/osx/pasteboard-file
[feat] osx pasteboard file copy and paste support
This commit is contained in:
commit
7480ead76a
2462
Cargo.lock
generated
2462
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -69,7 +69,7 @@ num_cpus = "1.15"
|
||||
bytes = { version = "1.4", features = ["serde"] }
|
||||
default-net = "0.14"
|
||||
wol-rs = "1.0"
|
||||
flutter_rust_bridge = { version = "1.75", features = ["uuid"], optional = true}
|
||||
flutter_rust_bridge = { version = "=1.75", features = ["uuid"], optional = true}
|
||||
errno = "0.3"
|
||||
rdev = { git = "https://github.com/fufesou/rdev" }
|
||||
url = { version = "2.3", features = ["serde"] }
|
||||
@ -164,7 +164,7 @@ winapi = { version = "0.3", features = [ "winnt" ] }
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
hbb_common = { path = "libs/hbb_common" }
|
||||
flutter_rust_bridge_codegen = "1.75"
|
||||
flutter_rust_bridge_codegen = "=1.75"
|
||||
os-version = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -20,7 +20,8 @@ unix-file-copy-paste = [
|
||||
"dep:dashmap",
|
||||
"dep:percent-encoding",
|
||||
"dep:utf16string",
|
||||
"dep:once_cell"
|
||||
"dep:once_cell",
|
||||
"dep:cacao"
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
@ -32,12 +33,17 @@ hbb_common = { path = "../hbb_common" }
|
||||
parking_lot = {version = "0.12"}
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||
once_cell = {version = "1.18", optional = true}
|
||||
x11rb = {version = "0.12", features = ["all-extensions"], optional = true}
|
||||
rand = {version = "0.8", optional = true}
|
||||
fuser = {version = "0.13", optional = true}
|
||||
libc = {version = "0.2", optional = true}
|
||||
dashmap = {version ="5.5", optional = true}
|
||||
percent-encoding = {version ="2.3", optional = true}
|
||||
utf16string = {version = "0.2", optional = true}
|
||||
once_cell = {version = "1.18", optional = true}
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
percent-encoding = {version ="2.3", optional = true}
|
||||
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true}
|
||||
x11rb = {version = "0.12", features = ["all-extensions"], optional = true}
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cacao = {git="https://github.com/clslaid/cacao", branch = "feat/set-file-urls", optional = true}
|
||||
|
@ -1,13 +1,9 @@
|
||||
#[cfg(target_os = "windows")]
|
||||
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 = "macos")]
|
||||
build.file("src/OSX/Clipboard.m");
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
build.flag_if_supported("-Wno-c++0x-extensions");
|
||||
build.flag_if_supported("-Wno-return-type-c-linkage");
|
||||
@ -30,12 +26,10 @@ fn build_c_impl() {
|
||||
build.compile("mycliprdr");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
println!("cargo:rerun-if-changed=src/windows/wf_cliprdr.c");
|
||||
#[cfg(target_os = "macos")]
|
||||
println!("cargo:rerun-if-changed=src/OSX/Clipboard.m");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(target_os = "windows")]
|
||||
build_c_impl();
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
#include "../cliprdr.h"
|
||||
|
||||
void mac_cliprdr_init(CliprdrClientContext *cliprdr)
|
||||
{
|
||||
(void)cliprdr;
|
||||
}
|
||||
|
||||
void mac_cliprdr_uninit(CliprdrClientContext *cliprdr)
|
||||
{
|
||||
(void)cliprdr;
|
||||
}
|
@ -154,6 +154,11 @@ impl fuser::Filesystem for FuseClient {
|
||||
let mut server = self.server.lock();
|
||||
server.getattr(req, ino, reply)
|
||||
}
|
||||
|
||||
fn statfs(&mut self, req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyStatfs) {
|
||||
let mut server = self.server.lock();
|
||||
server.statfs(req, ino, reply)
|
||||
}
|
||||
}
|
||||
|
||||
/// fuse server
|
||||
@ -486,6 +491,15 @@ impl fuser::Filesystem for FuseServer {
|
||||
let attr = (&entry.attributes).into();
|
||||
reply.attr(&std::time::Duration::default(), &attr)
|
||||
}
|
||||
|
||||
fn statfs(&mut self, _req: &fuser::Request<'_>, _ino: u64, reply: fuser::ReplyStatfs) {
|
||||
let mut blocks = 0;
|
||||
for file in self.files.iter() {
|
||||
blocks += file.attributes.size / (BLOCK_SIZE as u64)
|
||||
+ (file.attributes.size % (BLOCK_SIZE as u64) != 0) as u64;
|
||||
}
|
||||
reply.statfs(blocks, 0, 0, 0, 0, BLOCK_SIZE, 512, BLOCK_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
impl FuseServer {
|
||||
|
@ -58,6 +58,7 @@ pub fn create_cliprdr_context(
|
||||
|
||||
Ok(Box::new(unix_ctx) as Box<_>)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unix-file-copy-paste"))]
|
||||
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
|
||||
}
|
||||
@ -81,6 +82,7 @@ impl CliprdrServiceContext for DummyCliprdrContext {
|
||||
}
|
||||
|
||||
#[cfg(feature = "unix-file-copy-paste")]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
// 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;
|
||||
|
@ -25,8 +25,13 @@ use self::url::{encode_path_to_uri, parse_plain_uri_list};
|
||||
use super::fuse::FuseServer;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
/// clipboard implementation of x11
|
||||
pub mod x11;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
/// clipboard implementation of macos
|
||||
pub mod ns_clipboard;
|
||||
|
||||
pub mod local_file;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
@ -76,7 +81,7 @@ fn get_sys_clipboard(ignore_path: &PathBuf) -> Result<Box<dyn SysClipboard>, Cli
|
||||
}
|
||||
#[cfg(not(feature = "wayland"))]
|
||||
{
|
||||
pub use x11::*;
|
||||
use x11::*;
|
||||
let x11_clip = X11Clipboard::new(ignore_path)?;
|
||||
Ok(Box::new(x11_clip) as Box<_>)
|
||||
}
|
||||
@ -84,7 +89,9 @@ fn get_sys_clipboard(ignore_path: &PathBuf) -> Result<Box<dyn SysClipboard>, Cli
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn get_sys_clipboard(ignore_path: &PathBuf) -> Result<Box<dyn SysClipboard>, CliprdrError> {
|
||||
unimplemented!()
|
||||
use ns_clipboard::*;
|
||||
let ns_pb = NsPasteboard::new(ignore_path)?;
|
||||
Ok(Box::new(ns_pb) as Box<_>)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
97
libs/clipboard/src/platform/unix/ns_clipboard.rs
Normal file
97
libs/clipboard/src/platform/unix/ns_clipboard.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use std::{collections::BTreeSet, path::PathBuf};
|
||||
|
||||
use cacao::pasteboard::{Pasteboard, PasteboardName};
|
||||
use hbb_common::log;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::{platform::unix::send_format_list, CliprdrError};
|
||||
|
||||
use super::SysClipboard;
|
||||
|
||||
#[inline]
|
||||
fn wait_file_list() -> Option<Vec<PathBuf>> {
|
||||
let pb = Pasteboard::named(PasteboardName::General);
|
||||
pb.get_file_urls()
|
||||
.ok()
|
||||
.map(|v| v.into_iter().map(|nsurl| nsurl.pathbuf()).collect())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_file_list(file_list: &[PathBuf]) -> Result<(), CliprdrError> {
|
||||
let pb = Pasteboard::named(PasteboardName::General);
|
||||
pb.set_files(file_list.to_vec())
|
||||
.map_err(|_| CliprdrError::ClipboardInternalError)
|
||||
}
|
||||
|
||||
pub struct NsPasteboard {
|
||||
ignore_path: PathBuf,
|
||||
|
||||
former_file_list: Mutex<Vec<PathBuf>>,
|
||||
}
|
||||
|
||||
impl NsPasteboard {
|
||||
pub fn new(ignore_path: &PathBuf) -> Result<Self, CliprdrError> {
|
||||
Ok(Self {
|
||||
ignore_path: ignore_path.to_owned(),
|
||||
former_file_list: Mutex::new(vec![]),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SysClipboard for NsPasteboard {
|
||||
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
|
||||
*self.former_file_list.lock() = paths.to_vec();
|
||||
set_file_list(paths)
|
||||
}
|
||||
|
||||
fn start(&self) {
|
||||
{
|
||||
*self.former_file_list.lock() = vec![];
|
||||
}
|
||||
|
||||
loop {
|
||||
let file_list = match wait_file_list() {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let filtered = file_list
|
||||
.into_iter()
|
||||
.filter(|pb| !pb.starts_with(&self.ignore_path))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if filtered.is_empty() {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
let mut former = self.former_file_list.lock();
|
||||
|
||||
let filtered_st: BTreeSet<_> = filtered.iter().collect();
|
||||
let former_st = former.iter().collect::<BTreeSet<_>>();
|
||||
if filtered_st == former_st {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
*former = filtered;
|
||||
}
|
||||
|
||||
if let Err(e) = send_format_list(0) {
|
||||
log::warn!("failed to send format list: {}", e);
|
||||
break;
|
||||
}
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
log::debug!("stop listening file related atoms on clipboard");
|
||||
}
|
||||
|
||||
fn get_file_list(&self) -> Vec<PathBuf> {
|
||||
self.former_file_list.lock().clone()
|
||||
}
|
||||
}
|
@ -479,7 +479,7 @@ impl Connection {
|
||||
ipc::Data::RawMessage(bytes) => {
|
||||
allow_err!(conn.stream.send_raw(bytes).await);
|
||||
}
|
||||
#[cfg(any(target_os="windows", target_os="linux"))]
|
||||
#[cfg(any(target_os="windows", target_os="linux", target_os = "macos"))]
|
||||
ipc::Data::ClipboardFile(clip) => {
|
||||
allow_err!(conn.stream.send(&clip_2_msg(clip)).await);
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
}
|
||||
}
|
||||
Data::ClipboardFileEnabled(_enabled) => {
|
||||
#[cfg(any(target_os= "windows",target_os ="linux"))]
|
||||
#[cfg(any(target_os= "windows",target_os ="linux", target_os = "macos"))]
|
||||
{
|
||||
self.file_transfer_enabled_peer = _enabled;
|
||||
}
|
||||
@ -489,7 +489,7 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
}
|
||||
match &data {
|
||||
Data::SwitchPermission{name: _name, enabled: _enabled} => {
|
||||
#[cfg(any(target_os="linux", target_os="windows"))]
|
||||
#[cfg(any(target_os="linux", target_os="windows", target_os = "macos"))]
|
||||
if _name == "file" {
|
||||
self.file_transfer_enabled = *_enabled;
|
||||
}
|
||||
@ -512,7 +512,7 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
let file_transfer_enabled_peer = self.file_transfer_enabled_peer;
|
||||
let stop = is_stopping_allowed && !(is_clipboard_enabled && file_transfer_enabled && file_transfer_enabled_peer);
|
||||
log::debug!(
|
||||
"Process clipboard message from cm, stop: {}, is_stopping_allowed: {}, is_clipboard_enabled: {}, file_transfer_enabled: {}, file_transfer_enabled_peer: {}",
|
||||
"Process clipboard message from clip, stop: {}, is_stopping_allowed: {}, is_clipboard_enabled: {}, file_transfer_enabled: {}, file_transfer_enabled_peer: {}",
|
||||
stop, is_stopping_allowed, is_clipboard_enabled, file_transfer_enabled, file_transfer_enabled_peer);
|
||||
if stop {
|
||||
ContextSend::set_is_stopped();
|
||||
|
Loading…
x
Reference in New Issue
Block a user