floem/platform/
clipboard.rs1use parking_lot::Mutex;
2use raw_window_handle::RawDisplayHandle;
3
4use copypasta::{ClipboardContext, ClipboardProvider};
5
6static CLIPBOARD: Mutex<Option<Clipboard>> = Mutex::new(None);
7
8pub struct Clipboard {
9 clipboard: Box<dyn ClipboardProvider>,
10 #[allow(dead_code)]
11 selection: Option<Box<dyn ClipboardProvider>>,
12}
13
14#[derive(Clone, Debug)]
15pub enum ClipboardError {
16 NotAvailable,
17 ProviderError(String),
18 PathError(String),
19}
20
21impl Clipboard {
22 pub fn get_contents() -> Result<String, ClipboardError> {
23 CLIPBOARD
24 .lock()
25 .as_mut()
26 .ok_or(ClipboardError::NotAvailable)?
27 .clipboard
28 .get_contents()
29 .map_err(|e| ClipboardError::ProviderError(e.to_string()))
30 }
31
32 pub fn set_contents(s: String) -> Result<(), ClipboardError> {
33 if s.is_empty() {
34 return Err(ClipboardError::ProviderError(
35 "content is empty".to_string(),
36 ));
37 }
38 CLIPBOARD
39 .lock()
40 .as_mut()
41 .ok_or(ClipboardError::NotAvailable)?
42 .clipboard
43 .set_contents(s)
44 .map_err(|e| ClipboardError::ProviderError(e.to_string()))
45 }
46
47 #[cfg(windows)]
48 pub fn get_file_list() -> Result<Vec<std::path::PathBuf>, ClipboardError> {
49 use std::{path::PathBuf, str::FromStr};
50
51 let mut out: Vec<String> = Vec::new();
52 clipboard_win::raw::get_file_list(&mut out)
53 .map_err(|e| ClipboardError::ProviderError(e.to_string()))?;
54 out.iter()
55 .map(|s| PathBuf::from_str(s).map_err(|e| ClipboardError::PathError(e.to_string())))
56 .collect()
57 }
58
59 pub(crate) unsafe fn init(display: RawDisplayHandle) {
60 *CLIPBOARD.lock() = Some(unsafe { Self::new(display) });
61 }
62
63 unsafe fn new(
66 #[allow(unused_variables)] display: RawDisplayHandle,
67 ) -> Self {
68 #[cfg(not(any(
69 target_os = "macos",
70 target_os = "windows",
71 target_os = "ios",
72 target_os = "android",
73 target_arch = "wasm32"
74 )))]
75 {
76 if let RawDisplayHandle::Wayland(display) = display {
77 use copypasta::wayland_clipboard;
78 let (selection, clipboard) = unsafe {
79 wayland_clipboard::create_clipboards_from_external(display.display.as_ptr())
80 };
81 return Self {
82 clipboard: Box::new(clipboard),
83 selection: Some(Box::new(selection)),
84 };
85 }
86
87 use copypasta::x11_clipboard::{Primary, X11ClipboardContext};
88 Self {
89 clipboard: Box::new(ClipboardContext::new().unwrap()),
90 selection: Some(Box::new(X11ClipboardContext::<Primary>::new().unwrap())),
91 }
92 }
93
94 #[cfg(any(
96 target_os = "macos",
97 target_os = "windows",
98 target_os = "ios",
99 target_os = "android",
100 target_arch = "wasm32"
101 ))]
102 return Self {
103 clipboard: Box::new(ClipboardContext::new().unwrap()),
104 selection: None,
105 };
106 }
107}