smithay_clipboard/
state.rs

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    /// The latest seat which got an event.
62    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        // When both globals are not available nothing could be done.
98        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    /// Store selection for the given target.
140    ///
141    /// Selection source is only created when `Some(())` is returned.
142    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    /// Load data for the given target.
175    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        // Mark FD as non-blocking so we won't block ourselves.
231        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        // Mark FD as non-blocking so we won't block ourselves.
269        unsafe {
270            if set_non_blocking(write_pipe.as_raw_fd()).is_err() {
271                return;
272            }
273        }
274
275        // Don't access the content on the state directly, since it could change during
276        // the send.
277        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                // Selection sources are tied to the keyboard, so add/remove decives
327                // when we gain/loss capability.
328
329                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        // Only update the seat we're using when the serial got updated.
415        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    // The selection is finished and ready to be used.
461    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            // NOTE both selections rely on keyboard focus.
632            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    /// The target is clipboard selection.
740    Clipboard,
741    /// The target is primary selection.
742    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    /// The latest serial used to set the selection content.
754    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}