use crate::{
errors::{ExtDataControlConnectError, ExtDataControlReadError},
wl_event::{WlEvent, WlRegistryEvent},
wl_events_stream::WlEventsStream,
};
use crossbeam_queue::SegQueue;
use std::os::fd::{AsFd, AsRawFd};
use wayland_client::{Connection, EventQueue, backend::WaylandError, protocol::wl_seat::WlSeat};
use wayland_protocols::ext::data_control::v1::client::{
ext_data_control_device_v1::ExtDataControlDeviceV1,
ext_data_control_manager_v1::ExtDataControlManagerV1,
ext_data_control_source_v1::ExtDataControlSourceV1,
};
static WL_REGISTRY_EVENTS_QUEUE: SegQueue<WlRegistryEvent> = SegQueue::new();
static WL_EVENTS_QUEUE: SegQueue<WlEvent> = SegQueue::new();
pub struct AppConnection {
conn: Connection,
pub(crate) queue: EventQueue<WlEventsStream>,
wl_seat: WlSeat,
ext_data_control_manager: ExtDataControlManagerV1,
ext_data_control_device: ExtDataControlDeviceV1,
}
impl AppConnection {
pub(crate) fn connect() -> Result<Self, ExtDataControlConnectError> {
let conn = Connection::connect_to_env()?;
let mut queue = conn.new_event_queue::<WlEventsStream>();
let (wl_seat, ext_data_control_manager, ext_data_control_device) =
get_startup_objects(&conn, &mut queue)?;
queue.flush()?;
Ok(Self {
conn,
queue,
wl_seat,
ext_data_control_manager,
ext_data_control_device,
})
}
pub(crate) fn read_until_blocked(&mut self) -> Result<Vec<WlEvent>, ExtDataControlReadError> {
let wl_read_guard = self
.queue
.prepare_read()
.ok_or(ExtDataControlReadError::FailedToCreateReadGuard)?;
wl_read_guard.read()?;
self.queue.dispatch_pending(&mut WlEventsStream)?;
let mut events = vec![];
while let Some(event) = WL_EVENTS_QUEUE.pop() {
events.push(event);
}
Ok(events)
}
pub(crate) fn cleanup_and_flush(&self) {
self.ext_data_control_device.destroy();
self.wl_seat.release();
self.ext_data_control_manager.destroy();
if let Err(err) = self.queue.flush() {
log::error!("failed to finish cleanup: {err:?}");
}
}
pub(crate) fn offer_text(
&self,
custom_mime_type: impl Into<String>,
) -> Result<ExtDataControlSourceV1, WaylandError> {
let source = self
.ext_data_control_manager
.create_data_source(&self.queue.handle(), &WL_EVENTS_QUEUE);
source.offer("text/plain;charset=utf-8".to_string());
source.offer("text/plain".to_string());
source.offer(custom_mime_type.into());
self.ext_data_control_device.set_selection(Some(&source));
self.queue.flush()?;
Ok(source)
}
pub(crate) fn wl_events_queue() -> &'static SegQueue<WlEvent> {
&WL_EVENTS_QUEUE
}
}
impl AsFd for AppConnection {
fn as_fd(&self) -> std::os::unix::prelude::BorrowedFd<'_> {
self.conn.as_fd()
}
}
impl AsRawFd for AppConnection {
fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
self.conn.as_fd().as_raw_fd()
}
}
fn get_startup_objects(
conn: &Connection,
queue: &mut EventQueue<WlEventsStream>,
) -> Result<(WlSeat, ExtDataControlManagerV1, ExtDataControlDeviceV1), ExtDataControlConnectError> {
let registry = conn
.display()
.get_registry(&queue.handle(), &WL_REGISTRY_EVENTS_QUEUE);
queue.roundtrip(&mut WlEventsStream)?;
let mut wl_seat: Option<WlSeat> = None;
let mut ext_data_control_manager: Option<ExtDataControlManagerV1> = None;
while let Some(event) = WL_REGISTRY_EVENTS_QUEUE.pop() {
match event {
WlRegistryEvent::WlSeat { name, version } => {
wl_seat = Some(registry.bind(name, version, &queue.handle(), ()));
}
WlRegistryEvent::ExtDataControlManager { name, version } => {
ext_data_control_manager = Some(registry.bind(name, version, &queue.handle(), ()));
}
WlRegistryEvent::Other => {}
}
}
let wl_seat = wl_seat.ok_or(ExtDataControlConnectError::NoSeat)?;
let ext_data_control_manager =
ext_data_control_manager.ok_or(ExtDataControlConnectError::Unsupported)?;
let ext_data_control_device =
ext_data_control_manager.get_data_device(&wl_seat, &queue.handle(), &WL_EVENTS_QUEUE);
Ok((wl_seat, ext_data_control_manager, ext_data_control_device))
}