1
0
Fork 0
forked from wry/wry

ipc: create separate offers/sources for X

This commit is contained in:
Julian Orth 2024-03-31 18:42:23 +02:00
parent 4e9dacce1a
commit 8bca8b0e86
19 changed files with 830 additions and 552 deletions

View file

@ -8,11 +8,11 @@ use {
ipc::{
add_data_source_mime_type, destroy_data_device, destroy_data_offer,
destroy_data_source, receive_data_offer,
wl_data_device::{ClipboardIpc, WlDataDevice},
zwp_primary_selection_device_v1::{
PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1,
},
DataOffer, DynDataOffer, DynDataSource, IpcVtable, XIpcVtable,
x_data_device::{XClipboardIpc, XIpc, XIpcDevice, XPrimarySelectionIpc},
x_data_offer::XDataOffer,
x_data_source::XDataSource,
DataOfferId, DataSourceId, DynDataOffer, DynDataSource, IpcLocation, IpcVtable,
SourceData,
},
wl_seat::{SeatId, WlSeatGlobal},
wl_surface::{
@ -30,7 +30,7 @@ use {
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, linkedlist::LinkedList, numcell::NumCell,
oserror::OsError, rc_eq::rc_eq,
},
wire::{WlDataDeviceId, WlSurfaceId, ZwpPrimarySelectionDeviceV1Id},
wire::WlSurfaceId,
wire_xcon::{
ChangeProperty, ChangeWindowAttributes, ClientMessage, CompositeRedirectSubwindows,
ConfigureNotify, ConfigureRequest, ConfigureWindow, ConfigureWindowValues,
@ -67,6 +67,7 @@ use {
std::{
borrow::Cow,
cell::{Cell, RefCell},
marker::PhantomData,
mem::{self},
ops::{Deref, DerefMut},
rc::Rc,
@ -151,47 +152,30 @@ atoms! {
XdndTypeList,
}
struct EnhancedOffer<T: IpcVtable> {
offer: Rc<T::Offer>,
struct EnhancedOffer {
offer: Rc<XDataOffer>,
mime_types: RefCell<Vec<u32>>,
active: Cell<bool>,
}
struct SelectionData<T: IpcVtable> {
devices: CopyHashMap<SeatId, Rc<T::Device>>,
sources: CopyHashMap<SeatId, Rc<T::Source>>,
offers: CopyHashMap<SeatId, Rc<EnhancedOffer<T>>>,
active_offer: CloneCell<Option<Rc<EnhancedOffer<T>>>>,
#[derive(Default)]
struct SelectionData<T: XIpc> {
sources: CopyHashMap<SeatId, Rc<XDataSource>>,
offers: CopyHashMap<SeatId, Rc<EnhancedOffer>>,
active_offer: CloneCell<Option<Rc<EnhancedOffer>>>,
win: Cell<u32>,
selection: Cell<u32>,
pending_transfers: RefCell<Vec<PendingTransfer>>,
_phantom: PhantomData<T>,
}
impl<T: IpcVtable> Default for SelectionData<T> {
fn default() -> Self {
Self {
devices: Default::default(),
sources: Default::default(),
offers: Default::default(),
active_offer: Default::default(),
win: Cell::new(0),
selection: Cell::new(0),
pending_transfers: RefCell::new(vec![]),
}
}
}
impl<T: XIpcVtable> SelectionData<T> {
impl<T: XIpc> SelectionData<T> {
fn destroy(&self) {
for (_, offer) in self.offers.lock().drain() {
destroy_data_offer::<T>(&offer.offer);
}
self.active_offer.take();
self.destroy_sources();
for (_, device) in self.devices.lock().drain() {
destroy_data_device::<T>(&device);
T::remove_from_seat(&device);
}
}
fn destroy_sources(&self) {
@ -208,14 +192,14 @@ impl<T: XIpcVtable> SelectionData<T> {
}
self.offers.remove(&id);
self.sources.remove(&id);
self.devices.remove(&id);
}
}
#[derive(Default)]
pub struct XwmShared {
data: SelectionData<ClipboardIpc>,
primary_selection: SelectionData<PrimarySelectionIpc>,
devices: CopyHashMap<SeatId, Rc<XIpcDevice>>,
data: SelectionData<XClipboardIpc>,
primary_selection: SelectionData<XPrimarySelectionIpc>,
transfers: CopyHashMap<u64, SpawnedFuture<()>>,
}
@ -223,6 +207,11 @@ impl Drop for XwmShared {
fn drop(&mut self) {
self.data.destroy();
self.primary_selection.destroy();
for (_, device) in self.devices.lock().drain() {
destroy_data_device::<XClipboardIpc>(&device);
destroy_data_device::<XPrimarySelectionIpc>(&device);
device.seat.unset_x_data_device(device.id);
}
self.transfers.clear();
}
}
@ -557,27 +546,19 @@ impl Wm {
for seat in removed_seats {
self.shared.data.seat_removed(seat);
self.shared.primary_selection.seat_removed(seat);
self.shared.devices.remove(&seat);
}
for seat in new_seats {
let dd = Rc::new(WlDataDevice::new(
WlDataDeviceId::NONE,
&self.client,
1,
&seat,
true,
));
seat.add_data_device(&dd);
self.shared.data.devices.set(seat.id(), dd);
let dd = Rc::new(ZwpPrimarySelectionDeviceV1::new(
ZwpPrimarySelectionDeviceV1Id::NONE,
&self.client,
1,
&seat,
true,
));
seat.add_primary_selection_device(&dd);
self.shared.primary_selection.devices.set(seat.id(), dd);
let dd = Rc::new(XIpcDevice {
id: self.state.xwayland.ipc_device_ids.next(),
clipboard: Default::default(),
primary_selection: Default::default(),
seat: seat.clone(),
state: self.state.clone(),
client: self.client.clone(),
});
seat.set_x_data_device(&dd);
self.shared.devices.set(seat.id(), dd.clone());
}
self.known_seats = current_seats;
}
@ -611,55 +592,121 @@ impl Wm {
XWaylandEvent::ActivateRoot => self.activate_window(None, Initiator::Wayland).await,
XWaylandEvent::Close(window) => self.close_window(&window).await,
XWaylandEvent::SeatChanged => self.seats_changed(),
XWaylandEvent::PrimarySelectionCancelSource(src) => {
self.dd_cancel_source(&self.shared.clone().primary_selection, &src)
}
XWaylandEvent::PrimarySelectionSendSource(src, mime_type, fd) => {
self.dd_send_source(&self.shared.clone().primary_selection, &src, mime_type, fd)
.await;
}
XWaylandEvent::PrimarySelectionSetOffer(offer) => {
self.dd_set_offer(&self.shared.clone().primary_selection, offer)
.await;
}
XWaylandEvent::PrimarySelectionSetSelection(seat, offer) => {
self.dd_set_selection(&self.shared.clone().primary_selection, seat, offer)
.await;
}
XWaylandEvent::PrimarySelectionAddOfferMimeType(offer, mt) => {
self.dd_add_offer_mime_type(&self.shared.clone().primary_selection, offer, mt)
.await;
}
XWaylandEvent::ClipboardCancelSource(src) => {
self.dd_cancel_source(&self.shared.clone().data, &src)
}
XWaylandEvent::ClipboardSendSource(src, mime_type, fd) => {
self.dd_send_source(&self.shared.clone().data, &src, mime_type, fd)
.await;
}
XWaylandEvent::ClipboardSetOffer(offer) => {
self.dd_set_offer(&self.shared.clone().data, offer).await;
}
XWaylandEvent::ClipboardSetSelection(seat, offer) => {
self.dd_set_selection(&self.shared.clone().data, seat, offer)
.await;
}
XWaylandEvent::ClipboardAddOfferMimeType(offer, mt) => {
self.dd_add_offer_mime_type(&self.shared.clone().data, offer, mt)
.await;
}
XWaylandEvent::IpcCancelSource {
location,
seat,
source,
} => match location {
IpcLocation::Clipboard => {
self.dd_cancel_source::<XClipboardIpc>(&self.shared.clone().data, seat, source)
}
IpcLocation::PrimarySelection => self.dd_cancel_source::<XPrimarySelectionIpc>(
&self.shared.clone().primary_selection,
seat,
source,
),
},
XWaylandEvent::IpcSendSource {
location,
seat,
source,
mime_type,
fd,
} => match location {
IpcLocation::Clipboard => {
self.dd_send_source::<XClipboardIpc>(
&self.shared.clone().data,
seat,
source,
mime_type,
fd,
)
.await
}
IpcLocation::PrimarySelection => {
self.dd_send_source::<XPrimarySelectionIpc>(
&self.shared.clone().primary_selection,
seat,
source,
mime_type,
fd,
)
.await
}
},
XWaylandEvent::IpcSetOffer {
location,
seat,
offer,
} => match location {
IpcLocation::Clipboard => {
self.dd_set_offer::<XClipboardIpc>(&self.shared.clone().data, seat, offer)
.await
}
IpcLocation::PrimarySelection => {
self.dd_set_offer::<XPrimarySelectionIpc>(
&self.shared.clone().primary_selection,
seat,
offer,
)
.await
}
},
XWaylandEvent::IpcSetSelection {
seat,
location,
offer,
} => match location {
IpcLocation::Clipboard => {
self.dd_set_selection::<XClipboardIpc>(&self.shared.clone().data, seat, offer)
.await
}
IpcLocation::PrimarySelection => {
self.dd_set_selection::<XPrimarySelectionIpc>(
&self.shared.clone().primary_selection,
seat,
offer,
)
.await
}
},
XWaylandEvent::IpcAddOfferMimeType {
location,
seat,
offer,
mime_type,
} => match location {
IpcLocation::Clipboard => {
self.dd_add_offer_mime_type::<XClipboardIpc>(
&self.shared.clone().data,
seat,
offer,
mime_type,
)
.await
}
IpcLocation::PrimarySelection => {
self.dd_add_offer_mime_type::<XPrimarySelectionIpc>(
&self.shared.clone().primary_selection,
seat,
offer,
mime_type,
)
.await
}
},
}
}
async fn dd_add_offer_mime_type<T: IpcVtable>(
async fn dd_add_offer_mime_type<T: XIpc>(
&mut self,
sd: &SelectionData<T>,
offer: Rc<T::Offer>,
seat: SeatId,
offer: DataOfferId,
mt: String,
) {
let seat = offer.get_seat();
let enhanced = match sd.offers.get(&seat.id()) {
Some(r) if !rc_eq(&r.offer, &offer) => {
let enhanced = match sd.offers.get(&seat) {
Some(r) if r.offer.offer_id != offer => {
return;
}
None => {
@ -677,20 +724,19 @@ impl Wm {
enhanced.mime_types.borrow_mut().push(mt);
}
async fn dd_set_offer<T: IpcVtable>(&mut self, sd: &SelectionData<T>, offer: Rc<T::Offer>) {
let seat = offer.get_seat();
async fn dd_set_offer<T: XIpc>(
&mut self,
sd: &SelectionData<T>,
seat: SeatId,
offer: Rc<XDataOffer>,
) {
let mut mime_types = vec![];
if let Some(offer) = sd.offers.remove(&seat.id()) {
if let Some(offer) = sd.offers.remove(&seat) {
destroy_data_offer::<T>(&offer.offer);
mime_types = mem::take(offer.mime_types.borrow_mut().deref_mut());
}
match offer.offer_data().source() {
None => return,
Some(s) if s.source_data().is_xwm => return,
_ => {}
}
sd.offers.set(
seat.id(),
seat,
Rc::new(EnhancedOffer {
offer,
mime_types: RefCell::new(mime_types),
@ -699,11 +745,11 @@ impl Wm {
);
}
async fn dd_set_selection<T: IpcVtable>(
async fn dd_set_selection<T: XIpc>(
&mut self,
sd: &SelectionData<T>,
seat: SeatId,
offer: Option<Rc<T::Offer>>,
offer: Option<Rc<XDataOffer>>,
) {
let offer = match offer {
None => {
@ -794,22 +840,19 @@ impl Wm {
}
}
async fn dd_send_source<T: IpcVtable>(
async fn dd_send_source<T: XIpc>(
&mut self,
sd: &SelectionData<T>,
src: &Rc<T::Source>,
seat: SeatId,
src: DataSourceId,
mime_type: String,
fd: Rc<OwnedFd>,
) {
let seat = match src.source_data().seat.get() {
Some(s) => s,
_ => return,
};
let actual_src = match sd.sources.get(&seat.id()) {
let actual_src = match sd.sources.get(&seat) {
None => return,
Some(src) => src,
};
if !rc_eq(src, &actual_src) {
if actual_src.source_data().id != src {
return;
}
let mime_type = match self.mime_type_to_atom(mime_type).await {
@ -838,13 +881,16 @@ impl Wm {
.push(PendingTransfer { mime_type, fd });
}
fn dd_cancel_source<T: IpcVtable>(&mut self, sd: &SelectionData<T>, src: &Rc<T::Source>) {
if let Some(seat) = src.source_data().seat.get() {
if let Some(cur) = sd.sources.get(&seat.id()) {
if rc_eq(src, &cur) {
sd.sources.remove(&seat.id());
destroy_data_source::<T>(&cur);
}
fn dd_cancel_source<T: XIpc>(
&mut self,
sd: &SelectionData<T>,
seat: SeatId,
source: DataSourceId,
) {
if let Some(cur) = sd.sources.get(&seat) {
if cur.source_data().id == source {
sd.sources.remove(&seat);
destroy_data_source::<T>(&cur);
}
}
}
@ -1508,7 +1554,7 @@ impl Wm {
}
}
async fn handle_xfixes_selection_notify_<T: XIpcVtable>(
async fn handle_xfixes_selection_notify_<T: XIpc>(
&mut self,
sd: &SelectionData<T>,
event: &XfixesSelectionNotify,
@ -1562,7 +1608,7 @@ impl Wm {
}
}
async fn handle_selection_request_<T: IpcVtable>(
async fn handle_selection_request_<T: XIpc>(
&mut self,
sd: &SelectionData<T>,
event: &SelectionRequest,
@ -1663,7 +1709,7 @@ impl Wm {
}
}
async fn handle_selection_notify_<T: XIpcVtable>(
async fn handle_selection_notify_<T: XIpc>(
&mut self,
sd: &SelectionData<T>,
event: &SelectionNotify,
@ -1676,12 +1722,17 @@ impl Wm {
}
if event.target == self.atoms.TARGETS {
let targets = self.get_selection_mime_types(sd.win.get()).await?;
for dev in sd.devices.lock().values() {
for dev in self.shared.devices.lock().values() {
let seat = T::get_device_seat(dev);
if !seat.may_modify_primary_selection(&self.client, None) {
continue;
}
let source = Rc::new(T::create_xwm_source(&self.client));
let source = Rc::new(XDataSource {
state: self.state.clone(),
device: dev.clone(),
data: SourceData::new(&self.client),
location: T::LOCATION,
});
if let Err(e) = T::set_seat_selection(&seat, &source, None) {
log::error!("Could not set selection: {}", ErrorFmt(e));
return Ok(());