wl_clipboard_rs/
common.rs1use std::collections::HashMap;
2use std::ffi::OsString;
3use std::os::unix::net::UnixStream;
4use std::path::PathBuf;
5use std::{env, io};
6
7use wayland_backend::client::WaylandError;
8use wayland_client::globals::{registry_queue_init, GlobalError, GlobalListContents};
9use wayland_client::protocol::wl_registry::WlRegistry;
10use wayland_client::protocol::wl_seat::{self, WlSeat};
11use wayland_client::{ConnectError, Connection, Dispatch, EventQueue, Proxy};
12use wayland_protocols::ext::data_control::v1::client::ext_data_control_manager_v1::ExtDataControlManagerV1;
13use wayland_protocols_wlr::data_control::v1::client::zwlr_data_control_manager_v1::ZwlrDataControlManagerV1;
14
15use crate::data_control::Manager;
16use crate::seat_data::SeatData;
17
18pub struct State {
19 pub seats: HashMap<WlSeat, SeatData>,
20 pub clipboard_manager: Manager,
21}
22
23#[derive(thiserror::Error, Debug)]
24pub enum Error {
25 #[allow(clippy::enum_variant_names)]
26 #[error("Couldn't open the provided Wayland socket")]
27 SocketOpenError(#[source] io::Error),
28
29 #[error("Couldn't connect to the Wayland compositor")]
30 WaylandConnection(#[source] ConnectError),
31
32 #[error("Wayland compositor communication error")]
33 WaylandCommunication(#[source] WaylandError),
34
35 #[error(
36 "A required Wayland protocol ({name} version {version}) is not supported by the compositor"
37 )]
38 MissingProtocol { name: &'static str, version: u32 },
39}
40
41impl<S> Dispatch<WlSeat, (), S> for State
42where
43 S: Dispatch<WlSeat, ()> + AsMut<State>,
44{
45 fn event(
46 parent: &mut S,
47 seat: &WlSeat,
48 event: <WlSeat as wayland_client::Proxy>::Event,
49 _data: &(),
50 _conn: &wayland_client::Connection,
51 _qh: &wayland_client::QueueHandle<S>,
52 ) {
53 let state = parent.as_mut();
54
55 if let wl_seat::Event::Name { name } = event {
56 state.seats.get_mut(seat).unwrap().set_name(name);
57 }
58 }
59}
60
61pub fn initialize<S>(
62 primary: bool,
63 socket_name: Option<OsString>,
64) -> Result<(EventQueue<S>, State), Error>
65where
66 S: Dispatch<WlRegistry, GlobalListContents> + 'static,
67 S: Dispatch<ZwlrDataControlManagerV1, ()>,
68 S: Dispatch<ExtDataControlManagerV1, ()>,
69 S: Dispatch<WlSeat, ()>,
70 S: AsMut<State>,
71{
72 let conn = match socket_name {
74 Some(name) => {
75 let mut socket_path = env::var_os("XDG_RUNTIME_DIR")
76 .map(Into::<PathBuf>::into)
77 .ok_or(ConnectError::NoCompositor)
78 .map_err(Error::WaylandConnection)?;
79 if !socket_path.is_absolute() {
80 return Err(Error::WaylandConnection(ConnectError::NoCompositor));
81 }
82 socket_path.push(name);
83
84 let stream = UnixStream::connect(socket_path).map_err(Error::SocketOpenError)?;
85 Connection::from_socket(stream)
86 }
87 None => Connection::connect_to_env(),
88 }
89 .map_err(Error::WaylandConnection)?;
90
91 let (globals, queue) =
93 registry_queue_init::<S>(&conn).map_err(|err| match err {
94 GlobalError::Backend(err) => Error::WaylandCommunication(err),
95 GlobalError::InvalidId(err) => panic!("How's this possible? \
96 Is there no wl_registry? \
97 {:?}",
98 err),
99 })?;
100 let qh = &queue.handle();
101
102 let ext_manager = globals.bind(qh, 1..=1, ()).ok().map(Manager::Ext);
104
105 let wlr_v = if primary { 2 } else { 1 };
106 let wlr_manager = || globals.bind(qh, wlr_v..=wlr_v, ()).ok().map(Manager::Zwlr);
107
108 let clipboard_manager = match ext_manager.or_else(wlr_manager) {
109 Some(manager) => manager,
110 None => {
111 return Err(Error::MissingProtocol {
112 name: "ext-data-control, or wlr-data-control",
113 version: wlr_v,
114 })
115 }
116 };
117
118 let registry = globals.registry();
119 let seats = globals.contents().with_list(|globals| {
120 globals
121 .iter()
122 .filter(|global| global.interface == WlSeat::interface().name && global.version >= 2)
123 .map(|global| {
124 let seat = registry.bind(global.name, 2, qh, ());
125 (seat, SeatData::default())
126 })
127 .collect()
128 });
129
130 let state = State {
131 seats,
132 clipboard_manager,
133 };
134
135 Ok((queue, state))
136}