patch(0): implement cliprdr for macos
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
parent
f6a137cd43
commit
2bb1310094
@ -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"}
|
||||
|
@ -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;
|
||||
}
|
@ -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 {}
|
||||
|
@ -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 {
|
||||
|
113
libs/clipboard/src/platform/unix/ns_clipboard.rs
Normal file
113
libs/clipboard/src/platform/unix/ns_clipboard.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user