patch(0): implement cliprdr for macos

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
ClSlaid 2023-10-29 02:15:16 +08:00
parent f6a137cd43
commit 2bb1310094
No known key found for this signature in database
GPG Key ID: E0A5F564C51C056E
7 changed files with 146 additions and 31 deletions

View File

@ -18,12 +18,17 @@ hbb_common = { path = "../hbb_common" }
parking_lot = {version = "0.12"} parking_lot = {version = "0.12"}
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
once_cell = "1.18"
x11rb = {version = "0.12", features = ["all-extensions"]}
rand = {version = "0.8"} rand = {version = "0.8"}
fuser = {version = "0.13"} fuser = {version = "0.13"}
libc = {version = "0.2"} libc = {version = "0.2"}
dashmap = "5.5" dashmap = "5.5"
percent-encoding = "2.3" percent-encoding = "2.3"
utf16string = "0.2" utf16string = "0.2"
[target.'cfg(target_os = "linux")'.dependencies]
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch"} x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch"}
once_cell = "1.18"
x11rb = {version = "0.12", features = ["all-extensions"]}
[target.'cfg(target_os = "macos")'.dependencies]
cacao = {git="https://github.com/clslaid/cacao", branch = "feat/set-file-urls"}

View File

@ -1,13 +1,9 @@
#[cfg(target_os = "windows")]
fn build_c_impl() { fn build_c_impl() {
#[cfg(not(target_os = "linux"))]
let mut build = cc::Build::new(); let mut build = cc::Build::new();
#[cfg(target_os = "windows")]
build.file("src/windows/wf_cliprdr.c"); 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-c++0x-extensions");
build.flag_if_supported("-Wno-return-type-c-linkage"); build.flag_if_supported("-Wno-return-type-c-linkage");
@ -30,12 +26,10 @@ fn build_c_impl() {
build.compile("mycliprdr"); build.compile("mycliprdr");
} }
#[cfg(target_os = "windows")]
println!("cargo:rerun-if-changed=src/windows/wf_cliprdr.c"); 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() { fn main() {
#[cfg(target_os = "windows")]
build_c_impl(); build_c_impl();
} }

View File

@ -1,11 +0,0 @@
#include "../cliprdr.h"
void mac_cliprdr_init(CliprdrClientContext *cliprdr)
{
(void)cliprdr;
}
void mac_cliprdr_uninit(CliprdrClientContext *cliprdr)
{
(void)cliprdr;
}

View File

@ -19,7 +19,7 @@ pub fn create_cliprdr_context(
pub mod fuse; pub mod fuse;
#[cfg(any(target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "macos"))]
pub mod unix; pub mod unix;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "macos"))]
pub fn create_cliprdr_context( pub fn create_cliprdr_context(
enable_files: bool, enable_files: bool,
_enable_others: bool, _enable_others: bool,
@ -48,11 +48,11 @@ pub fn create_cliprdr_context(
log::warn!("umount {:?} may fail: {:?}", mnt_path, e); log::warn!("umount {:?} may fail: {:?}", mnt_path, e);
} }
let linux_ctx = unix::ClipboardContext::new(timeout, mnt_path.parse().unwrap())?; let unix_ctx = unix::ClipboardContext::new(timeout, mnt_path.parse().unwrap())?;
log::debug!("start cliprdr FUSE"); log::debug!("start cliprdr FUSE");
linux_ctx.run().expect("failed to start cliprdr FUSE"); unix_ctx.run().expect("failed to start cliprdr FUSE");
Ok(Box::new(linux_ctx) as Box<_>) Ok(Box::new(unix_ctx) as Box<_>)
} }
struct DummyCliprdrContext {} struct DummyCliprdrContext {}

View File

@ -24,8 +24,14 @@ use self::url::{encode_path_to_uri, parse_plain_uri_list};
use super::fuse::FuseServer; use super::fuse::FuseServer;
#[cfg(not(feature = "wayland"))] #[cfg(not(feature = "wayland"))]
#[cfg(target_os = "linux")]
/// clipboard implementation of x11
pub mod x11; pub mod x11;
#[cfg(target_os = "macos")]
/// clipboard implementation of macos
pub mod ns_clipboard;
pub mod local_file; pub mod local_file;
pub mod url; pub mod url;
@ -66,6 +72,7 @@ trait SysClipboard: Send + Sync {
fn get_file_list(&self) -> Vec<PathBuf>; fn get_file_list(&self) -> Vec<PathBuf>;
} }
#[cfg(target_os = "linux")]
fn get_sys_clipboard(ignore_path: &PathBuf) -> Result<Box<dyn SysClipboard>, CliprdrError> { fn get_sys_clipboard(ignore_path: &PathBuf) -> Result<Box<dyn SysClipboard>, CliprdrError> {
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
{ {
@ -73,12 +80,19 @@ fn get_sys_clipboard(ignore_path: &PathBuf) -> Result<Box<dyn SysClipboard>, Cli
} }
#[cfg(not(feature = "wayland"))] #[cfg(not(feature = "wayland"))]
{ {
pub use x11::*; use x11::*;
let x11_clip = X11Clipboard::new(ignore_path)?; let x11_clip = X11Clipboard::new(ignore_path)?;
Ok(Box::new(x11_clip) as Box<_>) Ok(Box::new(x11_clip) as Box<_>)
} }
} }
#[cfg(target_os = "macos")]
fn get_sys_clipboard(ignore_path: &PathBuf) -> Result<Box<dyn SysClipboard>, CliprdrError> {
use ns_clipboard::*;
let ns_pb = NSPasteboard::new(ignore_path)?;
Ok(Box::new(ns_pb) as Box<_>)
}
#[derive(Debug)] #[derive(Debug)]
enum FileContentsRequest { enum FileContentsRequest {
Size { Size {

View File

@ -0,0 +1,113 @@
use std::{
collections::BTreeSet,
path::PathBuf,
sync::atomic::{AtomicBool, Ordering},
};
use cacao::pasteboard::{Pasteboard, PasteboardName};
use parking_lot::Mutex;
use crate::{platform::unix::send_format_list, CliprdrError};
use super::SysClipboard;
pub struct NsPasteboard {
stopped: AtomicBool,
pasteboard: Pasteboard,
ignore_path: PathBuf,
former_file_list: Mutex<Vec<PathBuf>>,
}
impl NsPasteboard {
pub fn new(ignore_path: &PathBuf) -> Result<Self, CliprdrError> {
let pasteboard = Pasteboard::named(PasteboardName::General);
Ok(Self {
stopped: AtomicBool::new(false),
ignore_path: ignore_path.to_owned(),
pasteboard,
former_file_list: Mutex::new(vec![]),
})
}
fn wait_file_list(&self) -> Option<Vec<PathBuf>> {
self.pasteboard
.get_file_urls()
.ok()
.map(|v| v.into_iter().map(|nsurl| nsurl.to_path_buf()).collect())
}
#[inline]
fn is_stopped(&self) -> bool {
self.stopped.load(Ordering::Relaxed)
}
}
impl SysClipboard for NsPasteboard {
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
*self.former_file_list.lock() = paths.to_vec();
let uri_list: Vec<String> = paths.iter().map(encode_path_to_uri).collect();
let uri_list = uri_list.join("\n");
let uri_list = uri_list.as_bytes().to_vec();
self.pasteboard
.set_file_urls(uri_list)
.map_err(|_| CliprdrError::ClipboardInternalError)
}
fn start(&self) {
self.stopped.store(false, Ordering::Relaxed);
loop {
if self.is_stopped() {
std::thread::sleep(std::time::Duration::from_millis(100));
continue;
}
let file_list = match self.wait_file_list() {
Some(v) => v,
None => {
std::thread::sleep(std::time::Duration::from_millis(100));
continue;
}
};
let filtered = paths
.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 stop(&self) {
self.stopped.store(true, Ordering::Relaxed);
}
fn get_file_list(&self) -> Vec<PathBuf> {
self.former_file_list.lock().clone()
}
}

View File

@ -122,6 +122,11 @@ impl SysClipboard for X11Clipboard {
self.stop.store(false, Ordering::Relaxed); self.stop.store(false, Ordering::Relaxed);
loop { loop {
if self.is_stopped() {
std::thread::sleep(std::time::Duration::from_millis(100));
continue;
}
let sth = match self.wait_file_list() { let sth = match self.wait_file_list() {
Ok(sth) => sth, Ok(sth) => sth,
Err(e) => { Err(e) => {
@ -131,11 +136,6 @@ impl SysClipboard for X11Clipboard {
} }
}; };
if self.is_stopped() {
std::thread::sleep(std::time::Duration::from_millis(100));
continue;
}
let Some(paths) = sth else { let Some(paths) = sth else {
// just sleep // just sleep
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(100));