1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::io::{Error, ErrorKind, Read, Result, Write};
4use std::marker::PhantomData;
5use std::mem;
6use std::os::unix::io::{AsRawFd, RawFd};
7use std::rc::Rc;
8use std::sync::mpsc::Sender;
9
10use sctk::compositor::{CompositorHandler, CompositorState};
11use sctk::data_device_manager::data_device::{DataDevice, DataDeviceHandler};
12use sctk::data_device_manager::data_offer::{DataOfferError, DataOfferHandler, DragOffer};
13use sctk::data_device_manager::data_source::{CopyPasteSource, DataSourceHandler};
14use sctk::data_device_manager::{DataDeviceManagerState, WritePipe};
15use sctk::output::{OutputHandler, OutputState};
16use sctk::primary_selection::device::{PrimarySelectionDevice, PrimarySelectionDeviceHandler};
17use sctk::primary_selection::selection::{PrimarySelectionSource, PrimarySelectionSourceHandler};
18use sctk::primary_selection::PrimarySelectionManagerState;
19use sctk::reexports::client::protocol::wl_output::WlOutput;
20use sctk::reexports::client::protocol::wl_surface::WlSurface;
21use sctk::registry::{ProvidesRegistryState, RegistryState};
22use sctk::seat::pointer::{PointerData, PointerEvent, PointerEventKind, PointerHandler};
23use sctk::seat::{Capability, SeatHandler, SeatState};
24use sctk::shm::multi::MultiPool;
25use sctk::shm::{Shm, ShmHandler};
26use sctk::{
27 delegate_compositor, delegate_data_device, delegate_output, delegate_pointer,
28 delegate_primary_selection, delegate_registry, delegate_seat, delegate_shm, registry_handlers,
29};
30
31use sctk::reexports::calloop::{LoopHandle, PostAction};
32use sctk::reexports::client::globals::GlobalList;
33use sctk::reexports::client::protocol::wl_data_device::WlDataDevice;
34use sctk::reexports::client::protocol::wl_data_device_manager::DndAction;
35use sctk::reexports::client::protocol::wl_data_source::WlDataSource;
36use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
37use sctk::reexports::client::protocol::wl_pointer::WlPointer;
38use sctk::reexports::client::protocol::wl_seat::WlSeat;
39use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle};
40use sctk::reexports::protocols::wp::primary_selection::zv1::client::{
41 zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1,
42 zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
43};
44use wayland_backend::client::ObjectId;
45
46use crate::dnd::state::DndState;
47use crate::dnd::{DndEvent, DndSurface};
48use crate::mime::{AsMimeTypes, MimeType};
49use crate::text::Text;
50
51pub struct State<T> {
52 pub primary_selection_manager_state: Option<PrimarySelectionManagerState>,
53 pub data_device_manager_state: Option<DataDeviceManagerState>,
54 pub reply_tx: Sender<Result<(Vec<u8>, MimeType)>>,
55 pub exit: bool,
56
57 registry_state: RegistryState,
58 pub(crate) seat_state: SeatState,
59
60 pub(crate) seats: HashMap<ObjectId, ClipboardSeatState>,
61 pub(crate) latest_seat: Option<ObjectId>,
63
64 pub(crate) loop_handle: LoopHandle<'static, Self>,
65 pub(crate) queue_handle: QueueHandle<Self>,
66
67 primary_sources: Vec<PrimarySelectionSource>,
68 primary_selection_content: Box<dyn AsMimeTypes>,
69 primary_selection_mime_types: Rc<Cow<'static, [MimeType]>>,
70
71 data_sources: Vec<CopyPasteSource>,
72 data_selection_content: Box<dyn AsMimeTypes>,
73 data_selection_mime_types: Rc<Cow<'static, [MimeType]>>,
74 #[cfg(feature = "dnd")]
75 pub(crate) dnd_state: crate::dnd::state::DndState<T>,
76 pub(crate) compositor_state: CompositorState,
77 output_state: OutputState,
78 pub(crate) shm: Shm,
79 pub(crate) pool: MultiPool<u8>,
80 _phantom: PhantomData<T>,
81}
82
83impl<T: 'static + Clone> State<T> {
84 #[must_use]
85 pub fn new(
86 globals: &GlobalList,
87 queue_handle: &QueueHandle<Self>,
88 loop_handle: LoopHandle<'static, Self>,
89 reply_tx: Sender<Result<(Vec<u8>, MimeType)>>,
90 ) -> Option<Self> {
91 let mut seats = HashMap::new();
92
93 let data_device_manager_state = DataDeviceManagerState::bind(globals, queue_handle).ok();
94 let primary_selection_manager_state =
95 PrimarySelectionManagerState::bind(globals, queue_handle).ok();
96
97 if data_device_manager_state.is_none() && primary_selection_manager_state.is_none() {
99 return None;
100 }
101
102 let compositor_state =
103 CompositorState::bind(globals, queue_handle).expect("wl_compositor not available");
104 let output_state = OutputState::new(globals, queue_handle);
105 let shm = Shm::bind(globals, queue_handle).expect("wl_shm not available");
106
107 let seat_state = SeatState::new(globals, queue_handle);
108 for seat in seat_state.seats() {
109 seats.insert(seat.id(), Default::default());
110 }
111
112 Some(Self {
113 registry_state: RegistryState::new(globals),
114 primary_selection_content: Box::new(Text(String::new())),
115 data_selection_content: Box::new(Text(String::new())),
116 queue_handle: queue_handle.clone(),
117 primary_selection_manager_state,
118 primary_sources: Vec::new(),
119 data_device_manager_state,
120 data_sources: Vec::new(),
121 latest_seat: None,
122 loop_handle,
123 exit: false,
124 seat_state,
125 reply_tx,
126 seats,
127 primary_selection_mime_types: Rc::new(Default::default()),
128 data_selection_mime_types: Rc::new(Default::default()),
129 #[cfg(feature = "dnd")]
130 dnd_state: DndState::default(),
131 _phantom: PhantomData,
132 compositor_state,
133 output_state,
134 pool: MultiPool::new(&shm).expect("Failed to create memory pool."),
135 shm,
136 })
137 }
138
139 pub fn store_selection(&mut self, ty: Target, contents: Box<dyn AsMimeTypes>) -> Option<()> {
143 let latest = self.latest_seat.as_ref()?;
144 let seat = self.seats.get_mut(latest)?;
145
146 if !seat.has_focus {
147 return None;
148 }
149
150 match ty {
151 Target::Clipboard => {
152 let mgr = self.data_device_manager_state.as_ref()?;
153 let mime_types = contents.available();
154 self.data_selection_content = contents;
155 let source = mgr.create_copy_paste_source(&self.queue_handle, mime_types.iter());
156 self.data_selection_mime_types = Rc::new(mime_types);
157 source.set_selection(seat.data_device.as_ref().unwrap(), seat.latest_serial);
158 self.data_sources.push(source);
159 },
160 Target::Primary => {
161 let mgr = self.primary_selection_manager_state.as_ref()?;
162 let mime_types = contents.available();
163 self.primary_selection_content = contents;
164 let source = mgr.create_selection_source(&self.queue_handle, mime_types.iter());
165 self.primary_selection_mime_types = Rc::new(mime_types);
166 source.set_selection(seat.primary_device.as_ref().unwrap(), seat.latest_serial);
167 self.primary_sources.push(source);
168 },
169 }
170
171 Some(())
172 }
173
174 pub fn load(&mut self, ty: Target, allowed_mime_types: &[MimeType]) -> Result<()> {
176 let latest = self
177 .latest_seat
178 .as_ref()
179 .ok_or_else(|| Error::new(ErrorKind::Other, "no events received on any seat"))?;
180 let seat = self
181 .seats
182 .get_mut(latest)
183 .ok_or_else(|| Error::new(ErrorKind::Other, "active seat lost"))?;
184
185 if !seat.has_focus {
186 return Err(Error::new(ErrorKind::Other, "client doesn't have focus"));
187 }
188
189 let (read_pipe, mut mime_type) = match ty {
190 Target::Clipboard => {
191 let selection = seat
192 .data_device
193 .as_ref()
194 .and_then(|data| data.data().selection_offer())
195 .ok_or_else(|| Error::new(ErrorKind::Other, "selection is empty"))?;
196
197 let mime_type = selection
198 .with_mime_types(|offered| MimeType::find_allowed(offered, allowed_mime_types))
199 .ok_or_else(|| {
200 Error::new(ErrorKind::NotFound, "supported mime-type is not found")
201 })?;
202
203 (
204 selection.receive(mime_type.to_string()).map_err(|err| match err {
205 DataOfferError::InvalidReceive => {
206 Error::new(ErrorKind::Other, "offer is not ready yet")
207 },
208 DataOfferError::Io(err) => err,
209 })?,
210 mime_type,
211 )
212 },
213 Target::Primary => {
214 let selection = seat
215 .primary_device
216 .as_ref()
217 .and_then(|data| data.data().selection_offer())
218 .ok_or_else(|| Error::new(ErrorKind::Other, "selection is empty"))?;
219
220 let mime_type = selection
221 .with_mime_types(|offered| MimeType::find_allowed(offered, allowed_mime_types))
222 .ok_or_else(|| {
223 Error::new(ErrorKind::NotFound, "supported mime-type is not found")
224 })?;
225
226 (selection.receive(mime_type.to_string())?, mime_type)
227 },
228 };
229
230 unsafe {
232 set_non_blocking(read_pipe.as_raw_fd())?;
233 }
234
235 let mut reader_buffer = [0; 4096];
236 let mut content = Vec::new();
237 let _ = self.loop_handle.insert_source(read_pipe, move |_, file, state| {
238 let file = unsafe { file.get_mut() };
239 loop {
240 match file.read(&mut reader_buffer) {
241 Ok(0) => {
242 let _ = state
243 .reply_tx
244 .send(Ok((mem::take(&mut content), mem::take(&mut mime_type))));
245 break PostAction::Remove;
246 },
247 Ok(n) => content.extend_from_slice(&reader_buffer[..n]),
248 Err(err) if err.kind() == ErrorKind::WouldBlock => break PostAction::Continue,
249 Err(err) => {
250 let _ = state.reply_tx.send(Err(err));
251 break PostAction::Remove;
252 },
253 };
254 }
255 });
256
257 Ok(())
258 }
259
260 fn send_request(&mut self, ty: Target, write_pipe: WritePipe, mime: String) {
261 let Some(mime_type) = MimeType::find_allowed(&[mime], match ty {
262 Target::Clipboard => &self.data_selection_mime_types,
263 Target::Primary => &self.primary_selection_mime_types,
264 }) else {
265 return;
266 };
267
268 unsafe {
270 if set_non_blocking(write_pipe.as_raw_fd()).is_err() {
271 return;
272 }
273 }
274
275 let contents = match ty {
278 Target::Clipboard => self.data_selection_content.as_bytes(&mime_type),
279 Target::Primary => self.primary_selection_content.as_bytes(&mime_type),
280 };
281
282 let Some(contents) = contents else {
283 return;
284 };
285
286 let mut written = 0;
287 let _ = self.loop_handle.insert_source(write_pipe, move |_, file, _| {
288 let file = unsafe { file.get_mut() };
289 loop {
290 match file.write(&contents[written..]) {
291 Ok(n) if written + n == contents.len() => {
292 written += n;
293 break PostAction::Remove;
294 },
295 Ok(n) => written += n,
296 Err(err) if err.kind() == ErrorKind::WouldBlock => break PostAction::Continue,
297 Err(_) => break PostAction::Remove,
298 }
299 }
300 });
301 }
302}
303
304impl<T: 'static + Clone> SeatHandler for State<T> {
305 fn seat_state(&mut self) -> &mut SeatState {
306 &mut self.seat_state
307 }
308
309 fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, seat: WlSeat) {
310 self.seats.insert(seat.id(), Default::default());
311 }
312
313 fn new_capability(
314 &mut self,
315 _: &Connection,
316 qh: &QueueHandle<Self>,
317 seat: WlSeat,
318 capability: Capability,
319 ) {
320 let seat_state = self.seats.get_mut(&seat.id()).unwrap();
321
322 match capability {
323 Capability::Keyboard => {
324 seat_state.keyboard = Some(seat.get_keyboard(qh, seat.id()));
325
326 if seat_state.data_device.is_none() && self.data_device_manager_state.is_some() {
330 seat_state.data_device = self
331 .data_device_manager_state
332 .as_ref()
333 .map(|mgr| mgr.get_data_device(qh, &seat));
334 }
335
336 if seat_state.primary_device.is_none()
337 && self.primary_selection_manager_state.is_some()
338 {
339 seat_state.primary_device = self
340 .primary_selection_manager_state
341 .as_ref()
342 .map(|mgr| mgr.get_selection_device(qh, &seat));
343 }
344 },
345 Capability::Pointer => {
346 seat_state.pointer = self.seat_state.get_pointer(qh, &seat).ok();
347 },
348 _ => (),
349 }
350 }
351
352 fn remove_capability(
353 &mut self,
354 _: &Connection,
355 _: &QueueHandle<Self>,
356 seat: WlSeat,
357 capability: Capability,
358 ) {
359 let seat_state = self.seats.get_mut(&seat.id()).unwrap();
360 match capability {
361 Capability::Keyboard => {
362 seat_state.data_device = None;
363 seat_state.primary_device = None;
364
365 if let Some(keyboard) = seat_state.keyboard.take() {
366 if keyboard.version() >= 3 {
367 keyboard.release()
368 }
369 }
370 },
371 Capability::Pointer => {
372 if let Some(pointer) = seat_state.pointer.take() {
373 if pointer.version() >= 3 {
374 pointer.release()
375 }
376 }
377 },
378 _ => (),
379 }
380 }
381
382 fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, seat: WlSeat) {
383 self.seats.remove(&seat.id());
384 }
385}
386
387impl<T: 'static + Clone> PointerHandler for State<T> {
388 fn pointer_frame(
389 &mut self,
390 _: &Connection,
391 _: &QueueHandle<Self>,
392 pointer: &WlPointer,
393 events: &[PointerEvent],
394 ) {
395 let seat = pointer.data::<PointerData>().unwrap().seat();
396 let seat_id = seat.id();
397 let seat_state = match self.seats.get_mut(&seat_id) {
398 Some(seat_state) => seat_state,
399 None => return,
400 };
401
402 let mut updated_serial = false;
403 for event in events {
404 match event.kind {
405 PointerEventKind::Press { serial, .. }
406 | PointerEventKind::Release { serial, .. } => {
407 updated_serial = true;
408 seat_state.latest_serial = serial;
409 },
410 _ => (),
411 }
412 }
413
414 if updated_serial {
416 self.latest_seat = Some(seat_id);
417 }
418 }
419}
420
421impl<T: 'static + Clone> DataDeviceHandler for State<T>
422where
423 DndSurface<T>: Clone,
424{
425 fn enter(
426 &mut self,
427 _: &Connection,
428 _: &QueueHandle<Self>,
429 wl_data_device: &WlDataDevice,
430 x: f64,
431 y: f64,
432 surface: &WlSurface,
433 ) {
434 #[cfg(feature = "dnd")]
435 self.offer_enter(x, y, surface, wl_data_device);
436 }
437
438 fn leave(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataDevice) {
439 #[cfg(feature = "dnd")]
440 self.offer_leave();
441 }
442
443 fn motion(
444 &mut self,
445 _: &Connection,
446 _: &QueueHandle<Self>,
447 wl_data_device: &WlDataDevice,
448 x: f64,
449 y: f64,
450 ) {
451 #[cfg(feature = "dnd")]
452 self.offer_motion(x, y, wl_data_device);
453 }
454
455 fn drop_performed(&mut self, _: &Connection, _: &QueueHandle<Self>, d: &WlDataDevice) {
456 #[cfg(feature = "dnd")]
457 self.offer_drop(d)
458 }
459
460 fn selection(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataDevice) {}
462}
463
464impl<T: 'static + Clone> DataSourceHandler for State<T> {
465 fn send_request(
466 &mut self,
467 _: &Connection,
468 _: &QueueHandle<Self>,
469 _source: &WlDataSource,
470 mime: String,
471 write_pipe: WritePipe,
472 ) {
473 #[cfg(feature = "dnd")]
474 if self
475 .dnd_state
476 .dnd_source
477 .as_ref()
478 .map(|my_source| my_source.inner() == _source)
479 .unwrap_or_default()
480 {
481 self.send_dnd_request(write_pipe, mime);
482 return;
483 }
484 self.send_request(Target::Clipboard, write_pipe, mime)
485 }
486
487 fn cancelled(&mut self, _: &Connection, _: &QueueHandle<Self>, deleted: &WlDataSource) {
488 self.data_sources.retain(|source| source.inner() != deleted);
489 #[cfg(feature = "dnd")]
490 {
491 self.dnd_state.source_content = None;
492 self.dnd_state.dnd_source = None;
493 if let Some(s) = self.dnd_state.sender.as_ref() {
494 _ = s.send(DndEvent::Source(crate::dnd::SourceEvent::Cancelled));
495 }
496 _ = self.pool.remove(&0);
497 self.dnd_state.icon_surface = None;
498 }
499 }
500
501 fn accept_mime(
502 &mut self,
503 _: &Connection,
504 _: &QueueHandle<Self>,
505 _: &WlDataSource,
506 m: Option<String>,
507 ) {
508 #[cfg(feature = "dnd")]
509 {
510 if let Some(s) = self.dnd_state.sender.as_ref() {
511 _ = s.send(DndEvent::Source(crate::dnd::SourceEvent::Mime(
512 m.map(|s| MimeType::from(Cow::Owned(s))),
513 )));
514 }
515 }
516 }
517
518 fn dnd_dropped(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataSource) {
519 #[cfg(feature = "dnd")]
520 {
521 if let Some(s) = self.dnd_state.sender.as_ref() {
522 _ = s.send(DndEvent::Source(crate::dnd::SourceEvent::Dropped))
523 }
524 _ = self.pool.remove(&0);
525 self.dnd_state.icon_surface = None;
526 }
527 }
528
529 fn action(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataSource, a: DndAction) {
530 #[cfg(feature = "dnd")]
531 {
532 if let Some(s) = self.dnd_state.sender.as_ref() {
533 _ = s.send(DndEvent::Source(crate::dnd::SourceEvent::Action(a)))
534 }
535 }
536 }
537
538 fn dnd_finished(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlDataSource) {
539 #[cfg(feature = "dnd")]
540 {
541 self.dnd_state.source_content = None;
542 self.dnd_state.dnd_source = None;
543 if let Some(s) = self.dnd_state.sender.as_ref() {
544 _ = s.send(DndEvent::Source(crate::dnd::SourceEvent::Finished));
545 }
546 }
547 }
548}
549
550impl<T: 'static + Clone> DataOfferHandler for State<T> {
551 fn source_actions(
552 &mut self,
553 _: &Connection,
554 _: &QueueHandle<Self>,
555 _: &mut DragOffer,
556 _: DndAction,
557 ) {
558 }
559
560 fn selected_action(
561 &mut self,
562 _: &Connection,
563 _: &QueueHandle<Self>,
564 _: &mut DragOffer,
565 action: DndAction,
566 ) {
567 #[cfg(feature = "dnd")]
568 self.dnd_state.selected_action(action);
569 }
570}
571
572impl<T: 'static + Clone> ProvidesRegistryState for State<T> {
573 registry_handlers![SeatState];
574
575 fn registry(&mut self) -> &mut RegistryState {
576 &mut self.registry_state
577 }
578}
579
580impl<T: 'static + Clone> PrimarySelectionDeviceHandler for State<T> {
581 fn selection(
582 &mut self,
583 _: &Connection,
584 _: &QueueHandle<Self>,
585 _: &ZwpPrimarySelectionDeviceV1,
586 ) {
587 }
588}
589
590impl<T: 'static + Clone> PrimarySelectionSourceHandler for State<T> {
591 fn send_request(
592 &mut self,
593 _: &Connection,
594 _: &QueueHandle<Self>,
595 _: &ZwpPrimarySelectionSourceV1,
596 mime: String,
597 write_pipe: WritePipe,
598 ) {
599 self.send_request(Target::Primary, write_pipe, mime);
600 }
601
602 fn cancelled(
603 &mut self,
604 _: &Connection,
605 _: &QueueHandle<Self>,
606 deleted: &ZwpPrimarySelectionSourceV1,
607 ) {
608 self.primary_sources.retain(|source| source.inner() != deleted)
609 }
610}
611
612impl<T: 'static + Clone> Dispatch<WlKeyboard, ObjectId, State<T>> for State<T> {
613 fn event(
614 state: &mut State<T>,
615 _: &WlKeyboard,
616 event: <WlKeyboard as sctk::reexports::client::Proxy>::Event,
617 data: &ObjectId,
618 _: &Connection,
619 _: &QueueHandle<State<T>>,
620 ) {
621 use sctk::reexports::client::protocol::wl_keyboard::Event as WlKeyboardEvent;
622 let seat_state = match state.seats.get_mut(data) {
623 Some(seat_state) => seat_state,
624 None => return,
625 };
626 match event {
627 WlKeyboardEvent::Key { serial, .. } | WlKeyboardEvent::Modifiers { serial, .. } => {
628 seat_state.latest_serial = serial;
629 state.latest_seat = Some(data.clone());
630 },
631 WlKeyboardEvent::Enter { serial, .. } => {
633 seat_state.latest_serial = serial;
634 seat_state.has_focus = true;
635 },
636 WlKeyboardEvent::Leave { .. } => {
637 seat_state.latest_serial = 0;
638 seat_state.has_focus = false;
639 },
640 _ => (),
641 }
642 }
643}
644
645impl<T: 'static + Clone> CompositorHandler for State<T> {
646 fn scale_factor_changed(
647 &mut self,
648 _conn: &Connection,
649 _qh: &QueueHandle<Self>,
650 _surface: &sctk::reexports::client::protocol::wl_surface::WlSurface,
651 _new_factor: i32,
652 ) {
653 }
654
655 fn transform_changed(
656 &mut self,
657 _conn: &Connection,
658 _qh: &QueueHandle<Self>,
659 _surface: &sctk::reexports::client::protocol::wl_surface::WlSurface,
660 _new_transform: sctk::reexports::client::protocol::wl_output::Transform,
661 ) {
662 }
663
664 fn frame(
665 &mut self,
666 _conn: &Connection,
667 _qh: &QueueHandle<Self>,
668 _surface: &sctk::reexports::client::protocol::wl_surface::WlSurface,
669 _time: u32,
670 ) {
671 }
672
673 fn surface_enter(
674 &mut self,
675 _: &Connection,
676 _: &QueueHandle<Self>,
677 _: &WlSurface,
678 _: &WlOutput,
679 ) {
680 }
681
682 fn surface_leave(
683 &mut self,
684 _: &Connection,
685 _: &QueueHandle<Self>,
686 _: &WlSurface,
687 _: &WlOutput,
688 ) {
689 }
690}
691
692impl<T: 'static + Clone> OutputHandler for State<T> {
693 fn output_state(&mut self) -> &mut sctk::output::OutputState {
694 &mut self.output_state
695 }
696
697 fn new_output(
698 &mut self,
699 _conn: &Connection,
700 _qh: &QueueHandle<Self>,
701 _output: sctk::reexports::client::protocol::wl_output::WlOutput,
702 ) {
703 }
704
705 fn update_output(
706 &mut self,
707 _conn: &Connection,
708 _qh: &QueueHandle<Self>,
709 _output: sctk::reexports::client::protocol::wl_output::WlOutput,
710 ) {
711 }
712
713 fn output_destroyed(
714 &mut self,
715 _conn: &Connection,
716 _qh: &QueueHandle<Self>,
717 _output: sctk::reexports::client::protocol::wl_output::WlOutput,
718 ) {
719 }
720}
721
722impl<T: 'static + Clone> ShmHandler for State<T> {
723 fn shm_state(&mut self) -> &mut Shm {
724 &mut self.shm
725 }
726}
727
728delegate_compositor!(@<T: 'static + Clone> State<T>);
729delegate_output!(@<T: 'static + Clone> State<T>);
730delegate_shm!(@<T: 'static + Clone> State<T>);
731delegate_seat!(@<T: 'static + Clone> State<T>);
732delegate_pointer!(@<T: 'static + Clone> State<T>);
733delegate_data_device!(@<T: 'static + Clone> State<T>);
734delegate_primary_selection!(@<T: 'static + Clone> State<T>);
735delegate_registry!(@<T: 'static + Clone> State<T>);
736
737#[derive(Debug, Clone, Copy)]
738pub enum Target {
739 Clipboard,
741 Primary,
743}
744
745#[derive(Debug, Default)]
746pub(crate) struct ClipboardSeatState {
747 keyboard: Option<WlKeyboard>,
748 pointer: Option<WlPointer>,
749 pub(crate) data_device: Option<DataDevice>,
750 primary_device: Option<PrimarySelectionDevice>,
751 pub(crate) has_focus: bool,
752
753 pub(crate) latest_serial: u32,
755}
756
757impl Drop for ClipboardSeatState {
758 fn drop(&mut self) {
759 if let Some(keyboard) = self.keyboard.take() {
760 if keyboard.version() >= 3 {
761 keyboard.release();
762 }
763 }
764
765 if let Some(pointer) = self.pointer.take() {
766 if pointer.version() >= 3 {
767 pointer.release();
768 }
769 }
770 }
771}
772
773pub(crate) unsafe fn set_non_blocking(raw_fd: RawFd) -> std::io::Result<()> {
774 let flags = libc::fcntl(raw_fd, libc::F_GETFL);
775
776 if flags < 0 {
777 return Err(std::io::Error::last_os_error());
778 }
779
780 let result = libc::fcntl(raw_fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
781 if result < 0 {
782 return Err(std::io::Error::last_os_error());
783 }
784
785 Ok(())
786}