1use std::{
4 collections::HashMap,
5 io::{Read, Write},
6 os::{
7 fd::OwnedFd,
8 unix::{io::AsFd, net::UnixStream},
9 },
10 sync::{
11 Arc, Mutex, MutexGuard,
12 atomic::{AtomicBool, Ordering},
13 mpsc,
14 },
15 thread::JoinHandle,
16 time::Duration,
17};
18
19use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
20
21use crate::{
22 error::{Error, Result},
23 mime::find_mime_match,
24 options::{Options, Protocol},
25 state::{Command, Shared},
26 worker,
27};
28
29const READ_IDLE_TIMEOUT: Duration = Duration::from_secs(5);
31
32#[derive(Debug, Clone, Default)]
40pub struct Content {
41 mime_types: Vec<String>,
42 data: HashMap<String, Vec<u8>>,
43}
44
45impl Content {
46 #[must_use]
48 pub fn new() -> Self {
49 Self::default()
50 }
51
52 #[must_use]
54 pub fn data(mut self, mime_type: impl Into<String>, data: impl Into<Vec<u8>>) -> Self {
55 let mime_type = mime_type.into();
56 self.push_type(&mime_type);
57 self.data.insert(mime_type, data.into());
58 self
59 }
60
61 #[must_use]
63 pub fn advertise(mut self, mime_type: impl Into<String>) -> Self {
64 self.push_type(&mime_type.into());
65 self
66 }
67
68 fn push_type(&mut self, mime_type: &str) {
69 if !self.mime_types.iter().any(|m| m == mime_type) {
70 self.mime_types.push(mime_type.to_owned());
71 }
72 }
73}
74
75struct Link {
78 commands: mpsc::Sender<Command>,
79 wake: UnixStream,
80}
81
82impl Link {
83 fn send(&self, command: Command) -> Result<()> {
84 self.commands.send(command).map_err(|_| Error::Stopped)?;
85 self.notify();
86 Ok(())
87 }
88
89 fn notify(&self) {
90 let _ = (&self.wake).write(&[1]);
92 }
93}
94
95#[derive(Debug)]
101#[must_use = "a paste stays open until the request is answered"]
102pub struct TransferRequest {
103 serial: u32,
104 mime_type: String,
105 link: Arc<Link>,
106}
107
108impl std::fmt::Debug for Link {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 f.debug_struct("Link").finish_non_exhaustive()
111 }
112}
113
114impl TransferRequest {
115 #[must_use]
117 pub fn mime_type(&self) -> &str {
118 &self.mime_type
119 }
120
121 pub fn complete(self, data: impl Into<Vec<u8>>) -> Result<()> {
128 self.link.send(Command::CompleteTransfer {
129 serial: self.serial,
130 data: Some(data.into()),
131 })
132 }
133
134 pub fn fail(self) -> Result<()> {
140 self.link.send(Command::CompleteTransfer {
141 serial: self.serial,
142 data: None,
143 })
144 }
145}
146
147pub struct DataControl {
162 link: Arc<Link>,
163 shared: Arc<Mutex<Shared>>,
164 protocol: Protocol,
165 max_read_bytes: usize,
166 stop: Arc<AtomicBool>,
167 thread: Option<JoinHandle<()>>,
168}
169
170impl std::fmt::Debug for DataControl {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 f.debug_struct("DataControl")
173 .field("protocol", &self.protocol)
174 .finish_non_exhaustive()
175 }
176}
177
178impl DataControl {
179 pub fn connect() -> Result<Self> {
185 Self::connect_with(&Options::new())
186 }
187
188 pub fn connect_with(options: &Options) -> Result<Self> {
194 let (command_tx, command_rx) = mpsc::channel();
195 let (wake_tx, wake_rx) = UnixStream::pair()?;
196 wake_tx.set_nonblocking(true)?;
197 wake_rx.set_nonblocking(true)?;
198 let stop = Arc::new(AtomicBool::new(false));
199 let (ready_tx, ready_rx) = mpsc::sync_channel(1);
200
201 let thread_options = options.clone();
202 let thread_stop = Arc::clone(&stop);
203 let thread = std::thread::Builder::new()
204 .name("lamco-data-control".into())
205 .spawn(move || {
206 match worker::connect(&thread_options, command_rx, wake_rx, thread_stop) {
207 Ok((worker, connected)) => {
208 if ready_tx.send(Ok(connected)).is_ok() {
209 worker.run();
210 }
211 }
212 Err(error) => {
213 let _ = ready_tx.send(Err(error));
214 }
215 }
216 })?;
217
218 let connected = match ready_rx.recv() {
219 Ok(Ok(connected)) => connected,
220 Ok(Err(error)) => {
221 let _ = thread.join();
222 return Err(error);
223 }
224 Err(_) => {
225 let _ = thread.join();
226 return Err(Error::Stopped);
227 }
228 };
229
230 Ok(Self {
231 link: Arc::new(Link {
232 commands: command_tx,
233 wake: wake_tx,
234 }),
235 shared: connected.shared,
236 protocol: connected.protocol,
237 max_read_bytes: options.max_read_bytes,
238 stop,
239 thread: Some(thread),
240 })
241 }
242
243 #[must_use]
245 pub fn protocol(&self) -> Protocol {
246 self.protocol
247 }
248
249 fn shared(&self) -> MutexGuard<'_, Shared> {
250 self.shared
251 .lock()
252 .unwrap_or_else(std::sync::PoisonError::into_inner)
253 }
254
255 #[must_use]
257 pub fn selection_mime_types(&self) -> Vec<String> {
258 self.shared().mime_types.clone()
259 }
260
261 #[must_use]
263 pub fn serial(&self) -> u32 {
264 self.shared().serial
265 }
266
267 pub fn on_change(&self, callback: impl Fn(Vec<String>) + Send + Sync + 'static) {
276 self.shared().on_change = Some(Arc::new(callback));
277 }
278
279 pub fn on_transfer(&self, callback: impl Fn(TransferRequest) + Send + Sync + 'static) {
285 let link = Arc::clone(&self.link);
286 self.shared().on_transfer = Some(Arc::new(move |serial, mime_type| {
287 callback(TransferRequest {
288 serial,
289 mime_type,
290 link: Arc::clone(&link),
291 });
292 }));
293 }
294
295 pub fn set_selection(&self, content: Content) -> Result<()> {
301 self.link.send(Command::SetSelection {
302 mime_types: content.mime_types,
303 data: content.data,
304 })
305 }
306
307 pub fn update_data(
314 &self,
315 mime_type: impl Into<String>,
316 data: impl Into<Vec<u8>>,
317 ) -> Result<()> {
318 self.link.send(Command::UpdateSourceData {
319 mime_type: mime_type.into(),
320 data: data.into(),
321 })
322 }
323
324 pub fn clear_selection(&self) -> Result<()> {
330 self.link.send(Command::ClearSelection)
331 }
332
333 pub fn read(&self, mime_type: &str) -> Result<Option<Vec<u8>>> {
344 let available = self.selection_mime_types();
345 let Some(matched) = find_mime_match(mime_type, &available) else {
346 return Ok(None);
347 };
348
349 let (mut reader, writer) = std::io::pipe()?;
350 self.link.send(Command::ReceiveFromOffer {
351 mime_type: matched.to_owned(),
352 fd: OwnedFd::from(writer),
353 })?;
354
355 let mut data = Vec::new();
356 let mut chunk = vec![0u8; 64 * 1024];
357 loop {
358 let mut fds = [PollFd::new(reader.as_fd(), PollFlags::POLLIN)];
359 let timeout = PollTimeout::try_from(READ_IDLE_TIMEOUT).unwrap_or(PollTimeout::NONE);
360 match poll(&mut fds, timeout) {
361 Ok(0) => return Err(Error::Timeout),
362 Ok(_) => {}
363 Err(nix::errno::Errno::EINTR) => continue,
364 Err(errno) => return Err(std::io::Error::from(errno).into()),
365 }
366 match reader.read(&mut chunk) {
367 Ok(0) => return Ok(Some(data)),
369 Ok(n) => {
370 if data.len() + n > self.max_read_bytes {
371 return Err(Error::TooLarge {
372 size: data.len() + n,
373 limit: self.max_read_bytes,
374 });
375 }
376 data.extend_from_slice(&chunk[..n]);
377 }
378 Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
379 Err(error) => return Err(error.into()),
380 }
381 }
382 }
383}
384
385impl Drop for DataControl {
386 fn drop(&mut self) {
387 self.stop.store(true, Ordering::Relaxed);
388 self.link.notify();
389 if let Some(thread) = self.thread.take() {
390 let _ = thread.join();
391 }
392 }
393}