1
0
Fork 0
forked from wry/wry

wl-seat: split selection handling

This commit is contained in:
kossLAN 2026-05-29 21:50:09 -04:00
parent 4074cee365
commit 8f7997e270
No known key found for this signature in database
2 changed files with 165 additions and 150 deletions

View file

@ -7,6 +7,7 @@ mod kb_owner;
mod pointer_owner; mod pointer_owner;
mod position_hint; mod position_hint;
mod seat_object; mod seat_object;
mod selection;
pub mod tablet; pub mod tablet;
pub mod text_input; pub mod text_input;
mod touch_owner; mod touch_owner;
@ -38,16 +39,12 @@ use {
ifs::{ ifs::{
ext_idle_notification_v1::ExtIdleNotificationV1, ext_idle_notification_v1::ExtIdleNotificationV1,
data_transfer::{ data_transfer::{
self, DynDataSource, TransferError, TransferLocation, self, DynDataSource, TransferError,
data_control::{DataControlDeviceId, DynDataControlDevice}, data_control::{DataControlDeviceId, DynDataControlDevice},
offer_source_to_regular_client, wl_data_device::WlDataDevice,
wl_data_device::{ClipboardTransfer, WlDataDevice},
wl_data_source::WlDataSource, wl_data_source::WlDataSource,
x_data_device::{XClipboardTransfer, XTransferDevice, XTransferDeviceId, XPrimarySelectionTransfer}, x_data_device::{XTransferDevice, XTransferDeviceId},
zwp_primary_selection_device_v1::{ zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1,
PrimarySelectionTransfer, ZwpPrimarySelectionDeviceV1,
},
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
}, },
wl_output::WlOutputGlobal, wl_output::WlOutputGlobal,
wl_seat::{ wl_seat::{
@ -1287,66 +1284,6 @@ impl WlSeatGlobal {
} }
} }
fn set_selection_<T, X, S>(
self: &Rc<Self>,
field: &CloneCell<Option<Rc<dyn DynDataSource>>>,
src: Option<Rc<S>>,
location: TransferLocation,
) -> Result<(), WlSeatError>
where
T: data_transfer::IterableTransferVtable,
X: data_transfer::TransferVtable<Device = XTransferDevice>,
S: DynDataSource,
{
if let (Some(new), Some(old)) = (&src, &field.get())
&& new.source_data().id == old.source_data().id
{
return Ok(());
}
if let Some(new) = &src {
data_transfer::attach_seat(&**new, self, data_transfer::Role::Selection)?;
}
let src_dyn = src.clone().map(|s| s as Rc<dyn DynDataSource>);
if let Some(old) = field.set(src_dyn) {
old.detach_seat(self);
}
if let Some(client) = self.keyboard_node.get().node_client() {
self.offer_selection_to_client::<T, X>(src.clone().map(|v| v as Rc<_>), &client);
// client.flush();
}
let dyn_source = src.map(|s| s as Rc<dyn DynDataSource>);
for dd in self.data_control_devices.lock().values() {
dd.clone().handle_new_source(location, dyn_source.clone());
}
Ok(())
}
fn offer_selection_to_client<T, X>(
&self,
selection: Option<Rc<dyn DynDataSource>>,
client: &Rc<Client>,
) where
T: data_transfer::IterableTransferVtable,
X: data_transfer::TransferVtable<Device = XTransferDevice>,
{
if let Some(src) = &selection {
src.cancel_unprivileged_offers();
}
if client.is_xwayland {
self.for_each_x_data_device(|dd| match &selection {
Some(src) => src.clone().offer_to_x(&dd),
_ => X::send_selection(&dd, None),
});
} else {
match selection {
Some(src) => offer_source_to_regular_client::<T>(src, client),
_ => T::for_each_device(self, client.id, |device| {
T::send_selection(device, None);
}),
}
}
}
pub fn start_drag( pub fn start_drag(
self: &Rc<Self>, self: &Rc<Self>,
origin: &Rc<WlSurface>, origin: &Rc<WlSurface>,
@ -1398,88 +1335,6 @@ impl WlSeatGlobal {
self.pointer_owner.cancel_dnd(self); self.pointer_owner.cancel_dnd(self);
} }
pub fn unset_selection(self: &Rc<Self>) {
let _ = self.set_wl_data_source_selection(None, None);
}
pub fn set_wl_data_source_selection(
self: &Rc<Self>,
selection: Option<Rc<WlDataSource>>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
if let Some(serial) = serial {
self.selection_serial.set(serial);
}
if let Some(selection) = &selection
&& selection.toplevel_drag.is_some()
{
return Err(WlSeatError::OfferHasDrag);
}
self.set_selection(selection)
}
pub fn set_selection<S: DynDataSource>(
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<ClipboardTransfer, XClipboardTransfer, _>(
&self.selection,
selection,
TransferLocation::Clipboard,
)
}
pub fn get_selection(&self) -> Option<Rc<dyn DynDataSource>> {
self.selection.get()
}
pub fn may_modify_selection(&self, client: &Rc<Client>, serial: u64) -> bool {
if serial < self.selection_serial.get() {
return false;
}
self.keyboard_node.get().node_client_id() == Some(client.id)
}
pub fn may_modify_primary_selection(&self, client: &Rc<Client>, serial: Option<u64>) -> bool {
if let Some(serial) = serial
&& serial < self.primary_selection_serial.get()
{
return false;
}
self.keyboard_node.get().node_client_id() == Some(client.id)
|| self.pointer_node().and_then(|n| n.node_client_id()) == Some(client.id)
}
pub fn unset_primary_selection(self: &Rc<Self>) {
let _ = self.set_zwp_primary_selection(None, None);
}
pub fn set_zwp_primary_selection(
self: &Rc<Self>,
selection: Option<Rc<ZwpPrimarySelectionSourceV1>>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
if let Some(serial) = serial {
self.primary_selection_serial.set(serial);
}
self.set_primary_selection(selection)
}
pub fn set_primary_selection<S: DynDataSource>(
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<PrimarySelectionTransfer, XPrimarySelectionTransfer, _>(
&self.primary_selection,
selection,
TransferLocation::PrimarySelection,
)
}
pub fn get_primary_selection(&self) -> Option<Rc<dyn DynDataSource>> {
self.primary_selection.get()
}
pub fn dnd_icon(&self) -> Option<Rc<DndIcon>> { pub fn dnd_icon(&self) -> Option<Rc<DndIcon>> {
self.pointer_owner.dnd_icon() self.pointer_owner.dnd_icon()
} }

View file

@ -0,0 +1,160 @@
use {
super::{WlSeatError, WlSeatGlobal},
crate::{
client::Client,
ifs::data_transfer::{
self, DynDataSource, TransferLocation, offer_source_to_regular_client,
wl_data_device::ClipboardTransfer,
wl_data_source::WlDataSource,
x_data_device::{XClipboardTransfer, XPrimarySelectionTransfer, XTransferDevice},
zwp_primary_selection_device_v1::PrimarySelectionTransfer,
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
},
utils::clonecell::CloneCell,
},
std::rc::Rc,
};
impl WlSeatGlobal {
fn set_selection_<T, X, S>(
self: &Rc<Self>,
field: &CloneCell<Option<Rc<dyn DynDataSource>>>,
src: Option<Rc<S>>,
location: TransferLocation,
) -> Result<(), WlSeatError>
where
T: data_transfer::IterableTransferVtable,
X: data_transfer::TransferVtable<Device = XTransferDevice>,
S: DynDataSource,
{
if let (Some(new), Some(old)) = (&src, &field.get())
&& new.source_data().id == old.source_data().id
{
return Ok(());
}
if let Some(new) = &src {
data_transfer::attach_seat(&**new, self, data_transfer::Role::Selection)?;
}
let src_dyn = src.clone().map(|s| s as Rc<dyn DynDataSource>);
if let Some(old) = field.set(src_dyn) {
old.detach_seat(self);
}
if let Some(client) = self.keyboard_node.get().node_client() {
self.offer_selection_to_client::<T, X>(src.clone().map(|v| v as Rc<_>), &client);
// client.flush();
}
let dyn_source = src.map(|s| s as Rc<dyn DynDataSource>);
for dd in self.data_control_devices.lock().values() {
dd.clone().handle_new_source(location, dyn_source.clone());
}
Ok(())
}
pub(super) fn offer_selection_to_client<T, X>(
&self,
selection: Option<Rc<dyn DynDataSource>>,
client: &Rc<Client>,
) where
T: data_transfer::IterableTransferVtable,
X: data_transfer::TransferVtable<Device = XTransferDevice>,
{
if let Some(src) = &selection {
src.cancel_unprivileged_offers();
}
if client.is_xwayland {
self.for_each_x_data_device(|dd| match &selection {
Some(src) => src.clone().offer_to_x(&dd),
_ => X::send_selection(&dd, None),
});
} else {
match selection {
Some(src) => offer_source_to_regular_client::<T>(src, client),
_ => T::for_each_device(self, client.id, |device| {
T::send_selection(device, None);
}),
}
}
}
pub fn unset_selection(self: &Rc<Self>) {
let _ = self.set_wl_data_source_selection(None, None);
}
pub fn set_wl_data_source_selection(
self: &Rc<Self>,
selection: Option<Rc<WlDataSource>>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
if let Some(serial) = serial {
self.selection_serial.set(serial);
}
if let Some(selection) = &selection
&& selection.toplevel_drag.is_some()
{
return Err(WlSeatError::OfferHasDrag);
}
self.set_selection(selection)
}
pub fn set_selection<S: DynDataSource>(
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<ClipboardTransfer, XClipboardTransfer, _>(
&self.selection,
selection,
TransferLocation::Clipboard,
)
}
pub fn get_selection(&self) -> Option<Rc<dyn DynDataSource>> {
self.selection.get()
}
pub fn may_modify_selection(&self, client: &Rc<Client>, serial: u64) -> bool {
if serial < self.selection_serial.get() {
return false;
}
self.keyboard_node.get().node_client_id() == Some(client.id)
}
pub fn may_modify_primary_selection(&self, client: &Rc<Client>, serial: Option<u64>) -> bool {
if let Some(serial) = serial
&& serial < self.primary_selection_serial.get()
{
return false;
}
self.keyboard_node.get().node_client_id() == Some(client.id)
|| self.pointer_node().and_then(|n| n.node_client_id()) == Some(client.id)
}
pub fn unset_primary_selection(self: &Rc<Self>) {
let _ = self.set_zwp_primary_selection(None, None);
}
pub fn set_zwp_primary_selection(
self: &Rc<Self>,
selection: Option<Rc<ZwpPrimarySelectionSourceV1>>,
serial: Option<u64>,
) -> Result<(), WlSeatError> {
if let Some(serial) = serial {
self.primary_selection_serial.set(serial);
}
self.set_primary_selection(selection)
}
pub fn set_primary_selection<S: DynDataSource>(
self: &Rc<Self>,
selection: Option<Rc<S>>,
) -> Result<(), WlSeatError> {
self.set_selection_::<PrimarySelectionTransfer, XPrimarySelectionTransfer, _>(
&self.primary_selection,
selection,
TransferLocation::PrimarySelection,
)
}
pub fn get_primary_selection(&self) -> Option<Rc<dyn DynDataSource>> {
self.primary_selection.get()
}
}