1 | use std::io::{Error, ErrorKind, Result}; |
2 | use std::sync::mpsc::Sender; |
3 | |
4 | use sctk::reexports::calloop::channel::Channel; |
5 | use sctk::reexports::calloop::{channel, EventLoop}; |
6 | use sctk::reexports::calloop_wayland_source::WaylandSource; |
7 | use sctk::reexports::client::globals::registry_queue_init; |
8 | use sctk::reexports::client::Connection; |
9 | |
10 | use crate::state::{SelectionTarget, State}; |
11 | |
12 | /// Spawn a clipboard worker, which dispatches its own `EventQueue` and handles |
13 | /// clipboard requests. |
14 | pub fn spawn( |
15 | name: String, |
16 | display: Connection, |
17 | rx_chan: Channel<Command>, |
18 | worker_replier: Sender<Result<String>>, |
19 | ) -> Option<std::thread::JoinHandle<()>> { |
20 | stdResult, Error>::thread::Builder::new() |
21 | .name(name) |
22 | .spawn(move || { |
23 | worker_impl(connection:display, rx_chan, reply_tx:worker_replier); |
24 | }) |
25 | .ok() |
26 | } |
27 | |
28 | /// Clipboard worker thread command. |
29 | #[derive (Eq, PartialEq)] |
30 | pub enum Command { |
31 | /// Store data to a clipboard. |
32 | Store(String), |
33 | /// Store data to a primary selection. |
34 | StorePrimary(String), |
35 | /// Load data from a clipboard. |
36 | Load, |
37 | /// Load primary selection. |
38 | LoadPrimary, |
39 | /// Shutdown the worker. |
40 | Exit, |
41 | } |
42 | |
43 | /// Handle clipboard requests. |
44 | fn worker_impl( |
45 | connection: Connection, |
46 | rx_chan: Channel<Command>, |
47 | reply_tx: Sender<Result<String>>, |
48 | ) { |
49 | let (globals, event_queue) = match registry_queue_init(&connection) { |
50 | Ok(data) => data, |
51 | Err(_) => return, |
52 | }; |
53 | |
54 | let mut event_loop = EventLoop::<State>::try_new().unwrap(); |
55 | let loop_handle = event_loop.handle(); |
56 | |
57 | let mut state = match State::new(&globals, &event_queue.handle(), loop_handle.clone(), reply_tx) |
58 | { |
59 | Some(state) => state, |
60 | None => return, |
61 | }; |
62 | |
63 | loop_handle |
64 | .insert_source(rx_chan, |event, _, state| { |
65 | if let channel::Event::Msg(event) = event { |
66 | match event { |
67 | Command::StorePrimary(contents) => { |
68 | state.store_selection(SelectionTarget::Primary, contents); |
69 | }, |
70 | Command::Store(contents) => { |
71 | state.store_selection(SelectionTarget::Clipboard, contents); |
72 | }, |
73 | Command::Load if state.data_device_manager_state.is_some() => { |
74 | if let Err(err) = state.load_selection(SelectionTarget::Clipboard) { |
75 | let _ = state.reply_tx.send(Err(err)); |
76 | } |
77 | }, |
78 | Command::LoadPrimary if state.data_device_manager_state.is_some() => { |
79 | if let Err(err) = state.load_selection(SelectionTarget::Primary) { |
80 | let _ = state.reply_tx.send(Err(err)); |
81 | } |
82 | }, |
83 | Command::Load | Command::LoadPrimary => { |
84 | let _ = state.reply_tx.send(Err(Error::new( |
85 | ErrorKind::Other, |
86 | "requested selection is not supported" , |
87 | ))); |
88 | }, |
89 | Command::Exit => state.exit = true, |
90 | } |
91 | } |
92 | }) |
93 | .unwrap(); |
94 | |
95 | WaylandSource::new(connection, event_queue).insert(loop_handle).unwrap(); |
96 | |
97 | loop { |
98 | if event_loop.dispatch(None, &mut state).is_err() || state.exit { |
99 | break; |
100 | } |
101 | } |
102 | } |
103 | |