smithay_clipboard/
worker.rs

1use std::borrow::Cow;
2use std::io::{Error, ErrorKind, Result};
3use std::marker::PhantomData;
4use std::sync::mpsc::Sender;
5
6use sctk::reexports::calloop::channel::Channel;
7use sctk::reexports::calloop::{channel, EventLoop};
8use sctk::reexports::calloop_wayland_source::WaylandSource;
9use sctk::reexports::client::globals::registry_queue_init;
10use sctk::reexports::client::Connection;
11
12use crate::dnd::DndRequest;
13use crate::mime::{AsMimeTypes, MimeType};
14use crate::state::{State, Target};
15
16/// Spawn a clipboard worker, which dispatches its own `EventQueue` and handles
17/// clipboard requests.
18pub fn spawn<T: 'static + Send + Clone>(
19    name: String,
20    display: Connection,
21    rx_chan: Channel<Command<T>>,
22    worker_replier: Sender<Result<(Vec<u8>, MimeType)>>,
23) -> Option<std::thread::JoinHandle<()>> {
24    std::thread::Builder::new()
25        .name(name)
26        .spawn(move || {
27            worker_impl(display, rx_chan, worker_replier);
28        })
29        .ok()
30}
31
32/// Clipboard worker thread command.
33pub enum Command<T> {
34    /// Loads data for the first available mime type in the provided list.
35    Load(Cow<'static, [MimeType]>, Target),
36    /// Store Data with the given mime types.
37    Store(Box<dyn AsMimeTypes + Send>, Target),
38    #[cfg(feature = "dnd")]
39    /// Init DnD
40    DndRequest(DndRequest<T>),
41    /// Shutdown the worker.
42    Exit,
43    /// Phantom data
44    Phantom(PhantomData<T>),
45}
46
47/// Handle clipboard requests.
48fn worker_impl<T: 'static + Clone>(
49    connection: Connection,
50    rx_chan: Channel<Command<T>>,
51    reply_tx: Sender<Result<(Vec<u8>, MimeType)>>,
52) {
53    let (globals, event_queue) = match registry_queue_init(&connection) {
54        Ok(data) => data,
55        Err(_) => return,
56    };
57
58    let mut event_loop = EventLoop::<State<T>>::try_new().unwrap();
59    let loop_handle = event_loop.handle();
60
61    let mut state = match State::new(&globals, &event_queue.handle(), loop_handle.clone(), reply_tx)
62    {
63        Some(state) => state,
64        None => return,
65    };
66
67    loop_handle
68        .insert_source(rx_chan, |event, _, state| {
69            if let channel::Event::Msg(event) = event {
70                match event {
71                    Command::Exit => state.exit = true,
72                    Command::Store(data, target) => {
73                        state.store_selection(target, data);
74                    },
75                    Command::Load(mime_types, Target::Clipboard)
76                        if state.data_device_manager_state.is_some() =>
77                    {
78                        if let Err(err) = state.load(Target::Clipboard, &mime_types) {
79                            let _ = state.reply_tx.send(Err(err));
80                        }
81                    },
82                    Command::Load(mime_types, Target::Primary)
83                        if state.primary_selection_manager_state.is_some() =>
84                    {
85                        if let Err(err) = state.load(Target::Primary, &mime_types) {
86                            let _ = state.reply_tx.send(Err(err));
87                        }
88                    },
89                    Command::Load(..) => {
90                        let _ = state.reply_tx.send(Err(Error::new(
91                            ErrorKind::Other,
92                            "requested selection is not supported",
93                        )));
94                    },
95                    #[cfg(feature = "dnd")]
96                    Command::DndRequest(r) => {
97                        state.handle_dnd_request(r);
98                    },
99                    Command::Phantom(_) => unreachable!(),
100                }
101            }
102        })
103        .unwrap();
104
105    WaylandSource::new(connection, event_queue).insert(loop_handle).unwrap();
106
107    loop {
108        if event_loop.dispatch(None, &mut state).is_err() || state.exit {
109            break;
110        }
111    }
112}