1#![doc = include_str!("../README.md")]
2
3use futures::{
4 Stream,
5 channel::mpsc::{self, Receiver, Sender},
6};
7use log::{debug, error, info, trace, warn};
8use std::{
9 collections::HashMap,
10 fmt::Display,
11 path::PathBuf,
12 pin::Pin,
13 sync::{
14 Arc, Mutex,
15 atomic::{AtomicBool, AtomicUsize, Ordering},
16 mpsc::sync_channel,
17 },
18 task::{Context, Poll},
19 thread::JoinHandle,
20 time::Duration,
21};
22
23mod body;
24pub use body::*;
25
26mod body_senders;
27use body_senders::*;
28
29mod error;
30pub use error::*;
31
32mod event_listener;
33pub use event_listener::*;
34
35mod logging;
36use logging::*;
37
38mod stream;
39pub use stream::*;
40
41mod formats;
42pub use formats::*;
43
44#[cfg(target_os = "linux")]
45mod linux {
46 pub(crate) mod driver;
47 pub(crate) mod observer;
48}
49#[cfg(target_os = "macos")]
50mod macos {
51 pub(crate) mod driver;
52 pub(crate) mod observer;
53}
54#[cfg(windows)]
55mod win {
56 mod driver;
57 mod observer;
58}
59
60pub(crate) trait Observer {
61 fn observe(&mut self, body_senders: Arc<BodySenders>);
62}
63
64#[derive(Debug)]
66pub(crate) struct Driver {
67 pub(crate) stop: Arc<AtomicBool>,
69
70 pub(crate) handle: Option<JoinHandle<()>>,
72}
73
74#[derive(Clone, Copy)]
76pub struct ClipboardContext<'a> {
77 formats: &'a Formats,
78 #[cfg(target_os = "linux")]
79 x11: &'a linux::observer::X11Context,
80 #[cfg(target_os = "macos")]
81 pasteboard: &'a objc2::rc::Retained<objc2_app_kit::NSPasteboard>,
82}
83
84impl ClipboardContext<'_> {
85 #[must_use]
87 #[inline]
88 pub const fn formats(&self) -> &Formats {
89 self.formats
90 }
91
92 #[must_use]
94 #[inline]
95 pub fn has_format(&self, name: &str) -> bool {
96 self.formats.iter().any(|d| d.name.as_ref() == name)
97 }
98
99 #[must_use]
101 #[inline]
102 pub fn get_format(&self, name: &str) -> Option<&Format> {
103 self.formats.iter().find(|d| d.name.as_ref() == name)
104 }
105
106 #[must_use]
108 #[inline]
109 pub fn get_format_as_u32(&self, name: &str) -> Option<u32> {
110 self
111 .get_format_data(name)
112 .and_then(|bytes| Some(u32::from_ne_bytes(bytes.try_into().ok()?)))
113 }
114
115 #[must_use]
117 #[inline]
118 pub fn get_format_data(&self, name: &str) -> Option<Vec<u8>> {
119 self
120 .formats
121 .iter()
122 .find(|d| d.name.as_ref() == name)
123 .and_then(|f| self.get_data(f))
124 }
125}
126
127pub trait Gatekeeper: Send + Sync + 'static {
133 fn check(&self, ctx: ClipboardContext) -> bool;
134}
135
136impl<F> Gatekeeper for F
137where
138 F: Fn(ClipboardContext) -> bool + Send + Sync + 'static,
139{
140 #[inline]
141 fn check(&self, ctx: ClipboardContext) -> bool {
142 (self)(ctx)
143 }
144}
145
146#[derive(Default)]
147pub struct DefaultGatekeeper;
148
149impl Gatekeeper for DefaultGatekeeper {
150 #[inline]
151 fn check(&self, _: ClipboardContext) -> bool {
152 true
153 }
154}