use std::{cell::RefCell, rc::Rc};
use wayland_protocols::misc::gtk_primary_selection::client::gtk_primary_selection_device_manager::GtkPrimarySelectionDeviceManager;
use wayland_protocols::unstable::primary_selection::v1::client::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1;
use wayland_client::{
protocol::{wl_registry::WlRegistry, wl_seat::WlSeat},
Attached, DispatchData,
};
use crate::lazy_global::LazyGlobal;
use crate::seat::{SeatHandling, SeatListener};
use crate::{environment::GlobalHandler, MissingGlobal};
mod device;
mod offer;
mod source;
pub use self::device::PrimarySelectionDevice;
pub use self::offer::PrimarySelectionOffer;
pub use self::source::{PrimarySelectionSource, PrimarySelectionSourceEvent};
#[derive(Debug)]
pub struct PrimarySelectionHandler {
inner: Rc<RefCell<PrimarySelectionDeviceManagerInner>>,
_listener: SeatListener,
}
#[derive(Debug)]
pub enum PrimarySelectionDeviceManager {
Zwp(Attached<ZwpPrimarySelectionDeviceManagerV1>),
Gtk(Attached<GtkPrimarySelectionDeviceManager>),
}
impl PrimarySelectionHandler {
pub fn init<S: SeatHandling>(seat_handler: &mut S) -> Self {
let inner = Rc::new(RefCell::new(PrimarySelectionDeviceManagerInner {
registry: None,
zwp_mgr: LazyGlobal::Unknown,
gtk_mgr: LazyGlobal::Unknown,
state: PrimarySelectionDeviceManagerInitState::Pending { seats: Vec::new() },
}));
let seat_inner = inner.clone();
let listener = seat_handler.listen(move |seat, seat_data, _| {
if seat_data.defunct {
seat_inner.borrow_mut().remove_seat(&seat);
} else {
seat_inner.borrow_mut().new_seat(&seat);
}
});
Self { inner, _listener: listener }
}
}
pub trait PrimarySelectionHandling {
fn with_primary_selection<F: FnOnce(&PrimarySelectionDevice)>(
&self,
seat: &WlSeat,
f: F,
) -> Result<(), MissingGlobal>;
fn get_primary_selection_manager(&self) -> Option<PrimarySelectionDeviceManager>;
}
impl<E: PrimarySelectionHandling> crate::environment::Environment<E> {
pub fn get_primary_selection_manager(&self) -> Option<PrimarySelectionDeviceManager> {
self.with_inner(|manager| manager.get_primary_selection_manager())
}
pub fn with_primary_selection<F: FnOnce(&PrimarySelectionDevice)>(
&self,
seat: &WlSeat,
f: F,
) -> Result<(), MissingGlobal> {
self.with_inner(|inner| inner.with_primary_selection(seat, f))
}
pub fn new_primary_selection_source<F>(
&self,
mime_types: Vec<String>,
callback: F,
) -> PrimarySelectionSource
where
F: FnMut(PrimarySelectionSourceEvent, DispatchData) + 'static,
{
let manager = match self.get_primary_selection_manager() {
Some(manager) => manager,
None => panic!("[SCTK] primary selection was required"),
};
PrimarySelectionSource::new(&manager, mime_types, callback)
}
}
impl PrimarySelectionHandling for PrimarySelectionHandler {
fn get_primary_selection_manager(&self) -> Option<PrimarySelectionDeviceManager> {
GlobalHandler::<ZwpPrimarySelectionDeviceManagerV1>::get(self)
.map(PrimarySelectionDeviceManager::Zwp)
.or_else(|| {
GlobalHandler::<GtkPrimarySelectionDeviceManager>::get(self)
.map(PrimarySelectionDeviceManager::Gtk)
})
}
fn with_primary_selection<F: FnOnce(&PrimarySelectionDevice)>(
&self,
seat: &WlSeat,
f: F,
) -> Result<(), MissingGlobal> {
self.inner.borrow().with_primary_selection(seat, f)
}
}
#[derive(Debug)]
enum PrimarySelectionDeviceManagerInitState {
Ready { manager: PrimarySelectionDeviceManager, devices: Vec<(WlSeat, PrimarySelectionDevice)> },
Pending { seats: Vec<WlSeat> },
}
#[derive(Debug)]
struct PrimarySelectionDeviceManagerInner {
registry: Option<Attached<WlRegistry>>,
zwp_mgr: LazyGlobal<ZwpPrimarySelectionDeviceManagerV1>,
gtk_mgr: LazyGlobal<GtkPrimarySelectionDeviceManager>,
pub state: PrimarySelectionDeviceManagerInitState,
}
impl PrimarySelectionDeviceManagerInner {
fn init_selection_manager(&mut self, manager: PrimarySelectionDeviceManager) {
let seats =
if let PrimarySelectionDeviceManagerInitState::Pending { seats } = &mut self.state {
std::mem::take(seats)
} else {
log::warn!("Ignoring second primary selection manager.");
return;
};
let mut devices = Vec::new();
for seat in seats {
let device = PrimarySelectionDevice::init_for_seat(&manager, &seat);
devices.push((seat.clone(), device));
}
self.state = PrimarySelectionDeviceManagerInitState::Ready { devices, manager }
}
fn new_seat(&mut self, seat: &WlSeat) {
match &mut self.state {
PrimarySelectionDeviceManagerInitState::Ready { devices, manager } => {
if devices.iter().any(|(s, _)| s == seat) {
return;
}
let device = PrimarySelectionDevice::init_for_seat(manager, seat);
devices.push((seat.clone(), device));
}
PrimarySelectionDeviceManagerInitState::Pending { seats } => {
seats.push(seat.clone());
}
}
}
fn remove_seat(&mut self, seat: &WlSeat) {
match &mut self.state {
PrimarySelectionDeviceManagerInitState::Ready { devices, .. } => {
devices.retain(|(s, _)| s != seat)
}
PrimarySelectionDeviceManagerInitState::Pending { seats } => {
seats.retain(|s| s != seat)
}
}
}
fn with_primary_selection<F: FnOnce(&PrimarySelectionDevice)>(
&self,
seat: &WlSeat,
f: F,
) -> Result<(), MissingGlobal> {
match &self.state {
PrimarySelectionDeviceManagerInitState::Pending { .. } => Err(MissingGlobal),
PrimarySelectionDeviceManagerInitState::Ready { devices, .. } => {
for (s, device) in devices {
if s == seat {
f(device);
return Ok(());
}
}
Err(MissingGlobal)
}
}
}
}
impl GlobalHandler<ZwpPrimarySelectionDeviceManagerV1> for PrimarySelectionHandler {
fn created(&mut self, registry: Attached<WlRegistry>, id: u32, version: u32, _: DispatchData) {
let mut inner = self.inner.borrow_mut();
if inner.registry.is_none() {
inner.registry = Some(registry);
}
if let LazyGlobal::Unknown = inner.zwp_mgr {
inner.zwp_mgr = LazyGlobal::Seen { id, version };
} else {
log::warn!(
"Compositor advertised zwp_primary_selection_device_manager_v1 multiple\
times, ignoring."
)
}
}
fn get(&self) -> Option<Attached<ZwpPrimarySelectionDeviceManagerV1>> {
let mut inner = self.inner.borrow_mut();
match inner.zwp_mgr {
LazyGlobal::Bound(ref mgr) => Some(mgr.clone()),
LazyGlobal::Unknown => None,
LazyGlobal::Seen { id, version } => {
let registry = inner.registry.as_ref().unwrap();
let version = std::cmp::min(1, version);
let mgr = registry.bind::<ZwpPrimarySelectionDeviceManagerV1>(version, id);
let manager = PrimarySelectionDeviceManager::Zwp((*mgr).clone());
inner.init_selection_manager(manager);
inner.zwp_mgr = LazyGlobal::Bound((*mgr).clone());
Some((*mgr).clone())
}
}
}
}
impl GlobalHandler<GtkPrimarySelectionDeviceManager> for PrimarySelectionHandler {
fn created(&mut self, registry: Attached<WlRegistry>, id: u32, version: u32, _: DispatchData) {
let mut inner = self.inner.borrow_mut();
if inner.registry.is_none() {
inner.registry = Some(registry);
}
if let LazyGlobal::Unknown = inner.gtk_mgr {
inner.gtk_mgr = LazyGlobal::Seen { id, version };
} else {
log::warn!(
"Compositor advertised gtk_primary_selection_device_manager multiple times,\
ignoring."
)
}
}
fn get(&self) -> Option<Attached<GtkPrimarySelectionDeviceManager>> {
let mut inner = self.inner.borrow_mut();
match inner.gtk_mgr {
LazyGlobal::Bound(ref mgr) => Some(mgr.clone()),
LazyGlobal::Unknown => None,
LazyGlobal::Seen { id, version } => {
let registry = inner.registry.as_ref().unwrap();
let version = std::cmp::min(1, version);
let mgr = registry.bind::<GtkPrimarySelectionDeviceManager>(version, id);
let manager = PrimarySelectionDeviceManager::Gtk((*mgr).clone());
inner.init_selection_manager(manager);
inner.gtk_mgr = LazyGlobal::Bound((*mgr).clone());
Some((*mgr).clone())
}
}
}
}