1use std::collections::HashMap;
17use std::os::fd::AsFd;
18use std::os::unix::net::UnixStream;
19use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
20use std::sync::{Arc, Condvar, Mutex};
21use std::time::Duration;
22
23use crate::sink::SelectionWatch;
24use wayland_client::backend::ObjectId;
25use wayland_client::protocol::{wl_registry, wl_seat};
26use wayland_client::{Connection, Dispatch, Proxy, QueueHandle, delegate_noop};
27use wayland_protocols::ext::data_control::v1::client::{
28 ext_data_control_device_v1::{self, ExtDataControlDeviceV1},
29 ext_data_control_manager_v1::ExtDataControlManagerV1,
30 ext_data_control_offer_v1::{self, ExtDataControlOfferV1},
31 ext_data_control_source_v1::{self, ExtDataControlSourceV1},
32};
33use wayland_protocols_wlr::data_control::v1::client::{
34 zwlr_data_control_device_v1::{self, ZwlrDataControlDeviceV1},
35 zwlr_data_control_manager_v1::ZwlrDataControlManagerV1,
36 zwlr_data_control_offer_v1::{self, ZwlrDataControlOfferV1},
37 zwlr_data_control_source_v1::{self, ZwlrDataControlSourceV1},
38};
39
40use crate::wayland::wlclient::{
41 IO_TIMEOUT, SyncState, bounded_roundtrip, impl_sync_callback, pipe_cloexec, read_fd_to_end,
42 wait_readable, write_fd_all,
43};
44
45const STOP_POLL: Duration = Duration::from_millis(500);
48
49static SERVED: Mutex<(u64, Vec<String>)> = Mutex::new((0, Vec::new()));
53static SERVED_GREW: Condvar = Condvar::new();
54static OWN_READS: AtomicUsize = AtomicUsize::new(0);
57
58struct OwnRead;
59
60impl OwnRead {
61 fn begin() -> Self {
62 OWN_READS.fetch_add(1, Ordering::SeqCst);
63 OwnRead
64 }
65}
66
67impl Drop for OwnRead {
68 fn drop(&mut self) {
69 OWN_READS.fetch_sub(1, Ordering::SeqCst);
70 }
71}
72
73enum DcManager {
79 Ext(ExtDataControlManagerV1),
80 Wlr(ZwlrDataControlManagerV1),
81}
82
83enum DcDevice {
84 Ext(ExtDataControlDeviceV1),
85 Wlr(ZwlrDataControlDeviceV1),
86}
87
88enum DcSource {
89 Ext(ExtDataControlSourceV1),
90 Wlr(ZwlrDataControlSourceV1),
91}
92
93#[derive(Clone)]
94enum DcOffer {
95 Ext(ExtDataControlOfferV1),
96 Wlr(ZwlrDataControlOfferV1),
97}
98
99impl DcManager {
100 fn get_data_device(&self, seat: &wl_seat::WlSeat, qh: &QueueHandle<DcState>) -> DcDevice {
101 match self {
102 DcManager::Ext(m) => DcDevice::Ext(m.get_data_device(seat, qh, ())),
103 DcManager::Wlr(m) => DcDevice::Wlr(m.get_data_device(seat, qh, ())),
104 }
105 }
106
107 fn create_data_source(&self, qh: &QueueHandle<DcState>) -> DcSource {
108 match self {
109 DcManager::Ext(m) => DcSource::Ext(m.create_data_source(qh, ())),
110 DcManager::Wlr(m) => DcSource::Wlr(m.create_data_source(qh, ())),
111 }
112 }
113}
114
115impl DcDevice {
116 fn set_selection(&self, source: Option<&DcSource>) {
117 match self {
118 DcDevice::Ext(d) => d.set_selection(source.map(|s| match s {
119 DcSource::Ext(s) => s,
120 DcSource::Wlr(_) => unreachable!("mixed data-control families"),
121 })),
122 DcDevice::Wlr(d) => d.set_selection(source.map(|s| match s {
123 DcSource::Wlr(s) => s,
124 DcSource::Ext(_) => unreachable!("mixed data-control families"),
125 })),
126 }
127 }
128
129 fn destroy(&self) {
130 match self {
131 DcDevice::Ext(d) => d.destroy(),
132 DcDevice::Wlr(d) => d.destroy(),
133 }
134 }
135}
136
137impl DcSource {
138 fn offer(&self, mime: String) {
139 match self {
140 DcSource::Ext(s) => s.offer(mime),
141 DcSource::Wlr(s) => s.offer(mime),
142 }
143 }
144
145 fn destroy(&self) {
146 match self {
147 DcSource::Ext(s) => s.destroy(),
148 DcSource::Wlr(s) => s.destroy(),
149 }
150 }
151}
152
153impl DcOffer {
154 fn id(&self) -> ObjectId {
155 match self {
156 DcOffer::Ext(o) => o.id(),
157 DcOffer::Wlr(o) => o.id(),
158 }
159 }
160
161 fn receive(&self, mime: String, fd: std::os::fd::BorrowedFd<'_>) {
162 match self {
163 DcOffer::Ext(o) => o.receive(mime, fd),
164 DcOffer::Wlr(o) => o.receive(mime, fd),
165 }
166 }
167
168 fn destroy(&self) {
169 match self {
170 DcOffer::Ext(o) => o.destroy(),
171 DcOffer::Wlr(o) => o.destroy(),
172 }
173 }
174}
175
176#[derive(Default)]
177struct DcState {
178 seat: Option<wl_seat::WlSeat>,
179 manager_ext: Option<ExtDataControlManagerV1>,
180 manager_wlr: Option<ZwlrDataControlManagerV1>,
181 offer_mimes: HashMap<ObjectId, Vec<String>>,
183 selection: Option<DcOffer>,
184 selection_changed: bool,
186 finished: bool,
188 canceled: bool,
190 serve: Vec<(String, Vec<u8>)>,
192 write_no: u64,
194 sync_done: bool,
195}
196
197impl SyncState for DcState {
198 fn sync_done_mut(&mut self) -> &mut bool {
199 &mut self.sync_done
200 }
201}
202impl_sync_callback!(DcState);
203
204impl DcState {
205 fn take_manager(&mut self) -> Result<DcManager, String> {
207 if let Some(m) = self.manager_ext.take() {
208 if let Some(w) = self.manager_wlr.take() {
209 w.destroy();
210 }
211 return Ok(DcManager::Ext(m));
212 }
213 self.manager_wlr.take().map(DcManager::Wlr).ok_or_else(|| {
214 "app compositor advertises neither ext_data_control_manager_v1 \
215 nor zwlr_data_control_manager_v1"
216 .to_string()
217 })
218 }
219
220 fn on_data_offer(&mut self, id: ObjectId) {
221 self.offer_mimes.entry(id).or_default();
222 }
223
224 fn on_offer_mime(&mut self, id: ObjectId, mime_type: String) {
225 self.offer_mimes.entry(id).or_default().push(mime_type);
226 }
227
228 fn on_selection(&mut self, offer: Option<DcOffer>) {
229 if let Some(old) = self.selection.take()
232 && offer.as_ref().map(|o| o.id()) != Some(old.id())
233 {
234 self.offer_mimes.remove(&old.id());
235 old.destroy();
236 }
237 self.selection = offer;
238 self.selection_changed = true;
239 }
240
241 fn on_primary_selection(&mut self, offer: Option<DcOffer>) {
245 if let Some(o) = offer
246 && self.selection.as_ref().map(|s| s.id()) != Some(o.id())
247 {
248 self.offer_mimes.remove(&o.id());
249 o.destroy();
250 }
251 }
252
253 fn on_send(&mut self, mime_type: &str, fd: std::os::fd::OwnedFd) {
254 let sent = self
255 .serve
256 .iter()
257 .find(|(m, _)| m == mime_type)
258 .is_some_and(|(_, data)| write_fd_all(&fd, data, IO_TIMEOUT).is_ok());
259 let own = OWN_READS.load(Ordering::SeqCst) > 0;
261 drop(fd);
263 let mut served = SERVED.lock().unwrap_or_else(|e| e.into_inner());
264 if sent && !own && served.0 == self.write_no {
265 served.1.push(mime_type.to_string());
266 SERVED_GREW.notify_all();
267 }
268 }
269}
270
271impl Dispatch<wl_registry::WlRegistry, ()> for DcState {
272 fn event(
273 state: &mut Self,
274 registry: &wl_registry::WlRegistry,
275 event: wl_registry::Event,
276 _: &(),
277 _: &Connection,
278 qh: &QueueHandle<Self>,
279 ) {
280 if let wl_registry::Event::Global {
281 name, interface, ..
282 } = event
283 {
284 match interface.as_str() {
287 "wl_seat" if state.seat.is_none() => {
288 state.seat = Some(registry.bind(name, 1, qh, ()));
289 }
290 "ext_data_control_manager_v1" if state.manager_ext.is_none() => {
291 state.manager_ext = Some(registry.bind(name, 1, qh, ()));
292 }
293 "zwlr_data_control_manager_v1" if state.manager_wlr.is_none() => {
294 state.manager_wlr = Some(registry.bind(name, 1, qh, ()));
295 }
296 _ => {}
297 }
298 }
299 }
300}
301
302delegate_noop!(DcState: ignore wl_seat::WlSeat);
303delegate_noop!(DcState: ExtDataControlManagerV1);
304delegate_noop!(DcState: ZwlrDataControlManagerV1);
305
306impl Dispatch<ExtDataControlOfferV1, ()> for DcState {
307 fn event(
308 state: &mut Self,
309 offer: &ExtDataControlOfferV1,
310 event: ext_data_control_offer_v1::Event,
311 _: &(),
312 _: &Connection,
313 _: &QueueHandle<Self>,
314 ) {
315 if let ext_data_control_offer_v1::Event::Offer { mime_type } = event {
316 state.on_offer_mime(offer.id(), mime_type);
317 }
318 }
319}
320
321impl Dispatch<ZwlrDataControlOfferV1, ()> for DcState {
322 fn event(
323 state: &mut Self,
324 offer: &ZwlrDataControlOfferV1,
325 event: zwlr_data_control_offer_v1::Event,
326 _: &(),
327 _: &Connection,
328 _: &QueueHandle<Self>,
329 ) {
330 if let zwlr_data_control_offer_v1::Event::Offer { mime_type } = event {
331 state.on_offer_mime(offer.id(), mime_type);
332 }
333 }
334}
335
336impl Dispatch<ExtDataControlDeviceV1, ()> for DcState {
337 fn event(
338 state: &mut Self,
339 _: &ExtDataControlDeviceV1,
340 event: ext_data_control_device_v1::Event,
341 _: &(),
342 _: &Connection,
343 _: &QueueHandle<Self>,
344 ) {
345 match event {
346 ext_data_control_device_v1::Event::DataOffer { id } => {
347 state.on_data_offer(id.id());
348 }
349 ext_data_control_device_v1::Event::Selection { id } => {
350 state.on_selection(id.map(DcOffer::Ext));
351 }
352 ext_data_control_device_v1::Event::PrimarySelection { id } => {
353 state.on_primary_selection(id.map(DcOffer::Ext));
354 }
355 ext_data_control_device_v1::Event::Finished => {
356 state.finished = true;
357 }
358 _ => {}
359 }
360 }
361
362 wayland_client::event_created_child!(DcState, ExtDataControlDeviceV1, [
363 ext_data_control_device_v1::EVT_DATA_OFFER_OPCODE => (ExtDataControlOfferV1, ()),
364 ]);
365}
366
367impl Dispatch<ZwlrDataControlDeviceV1, ()> for DcState {
368 fn event(
369 state: &mut Self,
370 _: &ZwlrDataControlDeviceV1,
371 event: zwlr_data_control_device_v1::Event,
372 _: &(),
373 _: &Connection,
374 _: &QueueHandle<Self>,
375 ) {
376 match event {
377 zwlr_data_control_device_v1::Event::DataOffer { id } => {
378 state.on_data_offer(id.id());
379 }
380 zwlr_data_control_device_v1::Event::Selection { id } => {
381 state.on_selection(id.map(DcOffer::Wlr));
382 }
383 zwlr_data_control_device_v1::Event::Finished => {
384 state.finished = true;
385 }
386 _ => {}
387 }
388 }
389
390 wayland_client::event_created_child!(DcState, ZwlrDataControlDeviceV1, [
391 zwlr_data_control_device_v1::EVT_DATA_OFFER_OPCODE => (ZwlrDataControlOfferV1, ()),
392 ]);
393}
394
395impl Dispatch<ExtDataControlSourceV1, ()> for DcState {
396 fn event(
397 state: &mut Self,
398 _: &ExtDataControlSourceV1,
399 event: ext_data_control_source_v1::Event,
400 _: &(),
401 _: &Connection,
402 _: &QueueHandle<Self>,
403 ) {
404 match event {
405 ext_data_control_source_v1::Event::Send { mime_type, fd } => {
406 state.on_send(&mime_type, fd);
407 }
408 ext_data_control_source_v1::Event::Cancelled => {
409 state.canceled = true;
410 }
411 _ => {}
412 }
413 }
414}
415
416impl Dispatch<ZwlrDataControlSourceV1, ()> for DcState {
417 fn event(
418 state: &mut Self,
419 _: &ZwlrDataControlSourceV1,
420 event: zwlr_data_control_source_v1::Event,
421 _: &(),
422 _: &Connection,
423 _: &QueueHandle<Self>,
424 ) {
425 match event {
426 zwlr_data_control_source_v1::Event::Send { mime_type, fd } => {
427 state.on_send(&mime_type, fd);
428 }
429 zwlr_data_control_source_v1::Event::Cancelled => {
430 state.canceled = true;
431 }
432 _ => {}
433 }
434 }
435}
436
437fn open_device(
440 socket_path: &str,
441) -> Result<
442 (
443 Connection,
444 wayland_client::EventQueue<DcState>,
445 DcState,
446 DcDevice,
447 ),
448 String,
449> {
450 let stream =
451 UnixStream::connect(socket_path).map_err(|e| format!("connect {socket_path}: {e}"))?;
452 let conn = Connection::from_socket(stream).map_err(|e| format!("wayland setup: {e}"))?;
453 let mut queue = conn.new_event_queue();
454 let qh = queue.handle();
455 let _registry = conn.display().get_registry(&qh, ());
456 let mut state = DcState::default();
457 bounded_roundtrip(&conn, &mut queue, &mut state)?;
458 let seat = state
459 .seat
460 .clone()
461 .ok_or("app compositor advertises no wl_seat")?;
462 let manager = state.take_manager()?;
463 let device = manager.get_data_device(&seat, &qh);
464 bounded_roundtrip(&conn, &mut queue, &mut state)?;
465 Ok((conn, queue, state, device))
466}
467
468pub(crate) fn list_types(socket_path: &str) -> Result<Vec<String>, String> {
470 let (_conn, _queue, state, device) = open_device(socket_path)?;
471 let out = state
472 .selection
473 .as_ref()
474 .and_then(|o| state.offer_mimes.get(&o.id()).cloned())
475 .unwrap_or_default();
476 device.destroy();
477 Ok(out)
478}
479
480pub(crate) fn read(socket_path: &str, mime: &str) -> Result<Option<Vec<u8>>, String> {
483 let (conn, mut queue, mut state, device) = open_device(socket_path)?;
484 let Some(offer) = state.selection.clone() else {
485 device.destroy();
486 return Ok(None);
487 };
488 let offered = state
489 .offer_mimes
490 .get(&offer.id())
491 .is_some_and(|m| m.iter().any(|x| x == mime));
492 if !offered {
493 device.destroy();
494 return Ok(None);
495 }
496 let (rd, wr) = pipe_cloexec()?;
497 let _own = OwnRead::begin();
498 offer.receive(mime.to_string(), wr.as_fd());
499 queue.flush().map_err(|e| format!("flush: {e}"))?;
500 drop(wr);
501 let data = read_fd_to_end(&rd, IO_TIMEOUT)?;
504 let _ = bounded_roundtrip(&conn, &mut queue, &mut state);
505 device.destroy();
506 Ok(Some(data))
507}
508
509pub(crate) fn write(socket_path: &str, entries: Vec<(String, Vec<u8>)>) -> Result<(), String> {
516 let stream =
517 UnixStream::connect(socket_path).map_err(|e| format!("connect {socket_path}: {e}"))?;
518 let conn = Connection::from_socket(stream).map_err(|e| format!("wayland setup: {e}"))?;
519 let mut queue = conn.new_event_queue();
520 let qh = queue.handle();
521 let _registry = conn.display().get_registry(&qh, ());
522 let mut state = DcState::default();
523 bounded_roundtrip(&conn, &mut queue, &mut state)?;
524 let seat = state
525 .seat
526 .clone()
527 .ok_or("app compositor advertises no wl_seat")?;
528 let manager = state.take_manager()?;
529 let device = manager.get_data_device(&seat, &qh);
530 let source = manager.create_data_source(&qh);
531 for (mime, _) in &entries {
532 source.offer(mime.clone());
533 }
534 state.serve = entries;
535 state.write_no = {
536 let mut served = SERVED.lock().unwrap_or_else(|e| e.into_inner());
537 served.0 += 1;
538 served.1.clear();
539 served.0
540 };
541 device.set_selection(Some(&source));
542 bounded_roundtrip(&conn, &mut queue, &mut state)?;
543 std::thread::Builder::new()
544 .name("pf-dc-selection".into())
545 .spawn(move || {
546 if let Err(e) = serve_selection(conn, queue, state, device, source) {
547 eprintln!("[Clipboard] app-compositor selection serve ended: {e}");
548 }
549 })
550 .map_err(|e| format!("spawn: {e}"))?;
551 Ok(())
552}
553
554fn serve_selection(
555 conn: Connection,
556 mut queue: wayland_client::EventQueue<DcState>,
557 mut state: DcState,
558 device: DcDevice,
559 source: DcSource,
560) -> Result<(), String> {
561 while !state.canceled && !state.finished {
562 queue.flush().map_err(|e| format!("flush: {e}"))?;
565 if let Some(guard) = conn.prepare_read() {
566 use std::os::fd::AsRawFd;
567 if wait_readable(guard.connection_fd().as_raw_fd(), STOP_POLL)? {
568 guard.read().map_err(|e| format!("read: {e}"))?;
569 }
570 }
571 queue
572 .dispatch_pending(&mut state)
573 .map_err(|e| format!("dispatch: {e}"))?;
574 }
575 source.destroy();
576 device.destroy();
577 let _ = queue.flush();
578 Ok(())
579}
580
581pub(crate) fn served(after: usize, timeout: Duration) -> Vec<String> {
584 let served = SERVED.lock().unwrap_or_else(|e| e.into_inner());
585 let (served, _) = SERVED_GREW
586 .wait_timeout_while(served, timeout, |s| s.1.len() <= after)
587 .unwrap_or_else(|e| e.into_inner());
588 served.1.clone()
589}
590
591pub(crate) fn clear(socket_path: &str) -> Result<(), String> {
593 let (conn, mut queue, mut state, device) = open_device(socket_path)?;
594 device.set_selection(None);
595 bounded_roundtrip(&conn, &mut queue, &mut state)?;
596 device.destroy();
597 Ok(())
598}
599
600struct WatchHandle {
601 stop: Arc<AtomicBool>,
602}
603
604static WATCHERS: Mutex<Option<HashMap<String, WatchHandle>>> = Mutex::new(None);
605
606pub(crate) fn watch(socket_path: &str, callback: Arc<dyn SelectionWatch>) -> Result<(), String> {
610 let stop = Arc::new(AtomicBool::new(false));
611 {
612 let mut reg = WATCHERS.lock().unwrap();
613 let map = reg.get_or_insert_with(HashMap::new);
614 if let Some(old) = map.insert(socket_path.to_string(), WatchHandle { stop: stop.clone() }) {
615 old.stop.store(true, Ordering::Relaxed);
616 }
617 }
618 let path = socket_path.to_string();
619 std::thread::Builder::new()
620 .name("pf-dc-watch".into())
621 .spawn(move || {
622 if let Err(e) = watch_loop(&path, callback, &stop) {
623 eprintln!("[Clipboard] app-compositor watch ended: {e}");
624 }
625 })
626 .map_err(|e| format!("spawn: {e}"))?;
627 Ok(())
628}
629
630pub(crate) fn unwatch_all() {
633 let mut reg = WATCHERS.lock().unwrap();
634 if let Some(map) = reg.as_mut() {
635 for (_, handle) in map.drain() {
636 handle.stop.store(true, Ordering::Relaxed);
637 }
638 }
639}
640
641pub(crate) fn unwatch(socket_path: &str) {
643 let mut reg = WATCHERS.lock().unwrap();
644 if let Some(map) = reg.as_mut()
645 && let Some(handle) = map.remove(socket_path)
646 {
647 handle.stop.store(true, Ordering::Relaxed);
648 }
649}
650
651fn watch_loop(
652 socket_path: &str,
653 callback: Arc<dyn SelectionWatch>,
654 stop: &AtomicBool,
655) -> Result<(), String> {
656 let (conn, mut queue, mut state, device) = open_device(socket_path)?;
657 while !stop.load(Ordering::Relaxed) && !state.finished {
658 if state.selection_changed {
659 state.selection_changed = false;
660 let mimes = state
661 .selection
662 .as_ref()
663 .and_then(|o| state.offer_mimes.get(&o.id()).cloned())
664 .unwrap_or_default();
665 if crate::SHUTDOWN.load(Ordering::Relaxed) {
666 break;
667 }
668 callback.mimes(mimes);
669 }
670 queue.flush().map_err(|e| format!("flush: {e}"))?;
671 if let Some(guard) = conn.prepare_read() {
672 use std::os::fd::AsRawFd;
673 if wait_readable(guard.connection_fd().as_raw_fd(), STOP_POLL)? {
674 guard.read().map_err(|e| format!("read: {e}"))?;
675 }
676 }
677 queue
678 .dispatch_pending(&mut state)
679 .map_err(|e| format!("dispatch: {e}"))?;
680 }
681 device.destroy();
682 let _ = queue.flush();
683 Ok(())
684}