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"}
[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"}
fuser = {version = "0.13"}
libc = {version = "0.2"}
dashmap = "5.5"
percent-encoding = "2.3"
utf16string = "0.2"
[target.'cfg(target_os = "linux")'.dependencies]
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() {
#[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();
}

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;
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub mod unix;
#[cfg(target_os = "linux")]
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub fn create_cliprdr_context(
enable_files: bool,
_enable_others: bool,
@ -48,11 +48,11 @@ pub fn create_cliprdr_context(
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");
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 {}

View File

@ -24,8 +24,14 @@ use self::url::{encode_path_to_uri, parse_plain_uri_list};
use super::fuse::FuseServer;
#[cfg(not(feature = "wayland"))]
#[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;
pub mod url;
@ -66,6 +72,7 @@ trait SysClipboard: Send + Sync {
fn get_file_list(&self) -> Vec<PathBuf>;
}
#[cfg(target_os = "linux")]
fn get_sys_clipboard(ignore_path: &PathBuf) -> Result<Box<dyn SysClipboard>, CliprdrError> {
#[cfg(feature = "wayland")]
{
@ -73,12 +80,19 @@ 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<_>)
}
}
#[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)]
enum FileContentsRequest {
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);
loop {
if self.is_stopped() {
std::thread::sleep(std::time::Duration::from_millis(100));
continue;
}
let sth = match self.wait_file_list() {
Ok(sth) => sth,
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 {
// just sleep
std::thread::sleep(std::time::Duration::from_millis(100));