ipc: create separate offers/sources for X
This commit is contained in:
parent
4e9dacce1a
commit
8bca8b0e86
19 changed files with 830 additions and 552 deletions
|
|
@ -191,6 +191,7 @@ fn start_compositor2(
|
|||
enabled: Cell::new(true),
|
||||
handler: Default::default(),
|
||||
queue: Default::default(),
|
||||
ipc_device_ids: Default::default(),
|
||||
},
|
||||
acceptor: Default::default(),
|
||||
serial: Default::default(),
|
||||
|
|
|
|||
156
src/ifs/ipc.rs
156
src/ifs/ipc.rs
|
|
@ -1,8 +1,11 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError, ClientId, WaylandObject},
|
||||
client::{Client, ClientError, ClientId},
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::{WlSeatError, WlSeatGlobal},
|
||||
ifs::{
|
||||
ipc::x_data_device::XIpcDevice,
|
||||
wl_seat::{WlSeatError, WlSeatGlobal},
|
||||
},
|
||||
utils::{
|
||||
bitflags::BitflagsExt, cell_ext::CellExt, clonecell::CloneCell, numcell::NumCell,
|
||||
smallmap::SmallMap,
|
||||
|
|
@ -25,6 +28,9 @@ pub mod wl_data_device;
|
|||
pub mod wl_data_device_manager;
|
||||
pub mod wl_data_offer;
|
||||
pub mod wl_data_source;
|
||||
pub mod x_data_device;
|
||||
pub mod x_data_offer;
|
||||
pub mod x_data_source;
|
||||
pub mod zwp_primary_selection_device_manager_v1;
|
||||
pub mod zwp_primary_selection_device_v1;
|
||||
pub mod zwp_primary_selection_offer_v1;
|
||||
|
|
@ -33,6 +39,12 @@ pub mod zwp_primary_selection_source_v1;
|
|||
linear_ids!(DataSourceIds, DataSourceId, u64);
|
||||
linear_ids!(DataOfferIds, DataOfferId, u64);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IpcLocation {
|
||||
Clipboard,
|
||||
PrimarySelection,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Role {
|
||||
Selection,
|
||||
|
|
@ -40,14 +52,15 @@ pub enum Role {
|
|||
}
|
||||
|
||||
pub trait DataSource: DynDataSource {
|
||||
fn send_cancelled(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>);
|
||||
fn send_cancelled(&self, seat: &Rc<WlSeatGlobal>);
|
||||
}
|
||||
|
||||
pub trait DynDataSource: 'static {
|
||||
fn source_data(&self) -> &SourceData;
|
||||
fn send_send(self: Rc<Self>, mime_type: &str, fd: Rc<OwnedFd>);
|
||||
fn offer_to(self: Rc<Self>, client: &Rc<Client>);
|
||||
fn detach_seat(self: Rc<Self>, seat: &Rc<WlSeatGlobal>);
|
||||
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>);
|
||||
fn offer_to_regular_client(self: Rc<Self>, client: &Rc<Client>);
|
||||
fn offer_to_x(self: Rc<Self>, dd: &Rc<XIpcDevice>);
|
||||
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>);
|
||||
fn cancel_offers(&self);
|
||||
|
||||
fn send_target(&self, mime_type: Option<&str>) {
|
||||
|
|
@ -80,7 +93,7 @@ pub trait DataOffer: DynDataOffer {
|
|||
pub trait DynDataOffer: 'static {
|
||||
fn offer_id(&self) -> DataOfferId;
|
||||
fn client_id(&self) -> ClientId;
|
||||
fn send_offer(self: Rc<Self>, mime_type: &str);
|
||||
fn send_offer(&self, mime_type: &str);
|
||||
fn destroy(&self);
|
||||
fn cancel(&self);
|
||||
fn get_seat(&self) -> Rc<WlSeatGlobal>;
|
||||
|
|
@ -110,15 +123,18 @@ pub trait DynDataOffer: 'static {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait XIpcVtable: IpcVtable {
|
||||
fn create_xwm_source(client: &Rc<Client>) -> Self::Source;
|
||||
fn remove_from_seat(device: &Self::Device);
|
||||
pub trait IterableIpcVtable: IpcVtable {
|
||||
fn for_each_device<C>(seat: &WlSeatGlobal, client: ClientId, f: C)
|
||||
where
|
||||
C: FnMut(&Rc<Self::Device>);
|
||||
}
|
||||
|
||||
pub trait IpcVtable: Sized {
|
||||
const LOCATION: IpcLocation;
|
||||
|
||||
type Device;
|
||||
type Source: DataSource;
|
||||
type Offer: DataOffer<Device = Self::Device> + WaylandObject;
|
||||
type Offer: DataOffer<Device = Self::Device>;
|
||||
|
||||
fn get_device_data(dd: &Self::Device) -> &DeviceData<Self::Offer>;
|
||||
fn get_device_seat(dd: &Self::Device) -> Rc<WlSeatGlobal>;
|
||||
|
|
@ -127,36 +143,34 @@ pub trait IpcVtable: Sized {
|
|||
source: &Rc<Self::Source>,
|
||||
serial: Option<u32>,
|
||||
) -> Result<(), WlSeatError>;
|
||||
fn for_each_device<C>(seat: &WlSeatGlobal, client: ClientId, f: C)
|
||||
where
|
||||
C: FnMut(&Rc<Self::Device>);
|
||||
fn create_offer(
|
||||
client: &Rc<Client>,
|
||||
dd: &Rc<Self::Device>,
|
||||
data: OfferData<Self::Device>,
|
||||
) -> Result<Rc<Self::Offer>, ClientError>;
|
||||
fn send_selection(dd: &Self::Device, offer: Option<&Rc<Self::Offer>>);
|
||||
fn send_offer(dd: &Self::Device, offer: &Rc<Self::Offer>);
|
||||
fn unset(seat: &Rc<WlSeatGlobal>, role: Role);
|
||||
fn device_client(dd: &Rc<Self::Device>) -> &Rc<Client>;
|
||||
}
|
||||
|
||||
pub struct DeviceData<O> {
|
||||
selection: CloneCell<Option<Rc<O>>>,
|
||||
dnd: CloneCell<Option<Rc<O>>>,
|
||||
pub is_xwm: bool,
|
||||
}
|
||||
|
||||
impl<O> Default for DeviceData<O> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
selection: Default::default(),
|
||||
dnd: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OfferData<D> {
|
||||
device: CloneCell<Option<Rc<D>>>,
|
||||
source: CloneCell<Option<Rc<dyn DynDataSource>>>,
|
||||
shared: Rc<SharedState>,
|
||||
pub is_xwm: bool,
|
||||
}
|
||||
|
||||
impl<T> OfferData<T> {
|
||||
pub fn source(&self) -> Option<Rc<dyn DynDataSource>> {
|
||||
self.source.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -189,7 +203,6 @@ pub struct SourceData {
|
|||
actions: Cell<Option<u32>>,
|
||||
role: Cell<Role>,
|
||||
shared: CloneCell<Rc<SharedState>>,
|
||||
pub is_xwm: bool,
|
||||
}
|
||||
|
||||
struct SharedState {
|
||||
|
|
@ -213,7 +226,7 @@ impl Default for SharedState {
|
|||
}
|
||||
|
||||
impl SourceData {
|
||||
fn new(client: &Rc<Client>, is_xwm: bool) -> Self {
|
||||
pub fn new(client: &Rc<Client>) -> Self {
|
||||
Self {
|
||||
seat: Default::default(),
|
||||
id: client.state.data_source_ids.next(),
|
||||
|
|
@ -224,7 +237,6 @@ impl SourceData {
|
|||
actions: Cell::new(None),
|
||||
role: Cell::new(Role::Selection),
|
||||
shared: Default::default(),
|
||||
is_xwm,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -265,7 +277,7 @@ pub fn attach_seat<S: DynDataSource>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cancel_offers<T: IpcVtable>(src: &T::Source) {
|
||||
pub fn cancel_offers<S: DynDataSource>(src: &S) {
|
||||
let data = src.source_data();
|
||||
while let Some((_, offer)) = data.offers.pop() {
|
||||
offer.cancel();
|
||||
|
|
@ -278,17 +290,68 @@ pub fn cancel_offer<T: IpcVtable>(offer: &T::Offer) {
|
|||
destroy_data_offer::<T>(&offer);
|
||||
}
|
||||
|
||||
pub fn detach_seat<T: IpcVtable>(src: &Rc<T::Source>, seat: &Rc<WlSeatGlobal>) {
|
||||
pub fn detach_seat<S: DataSource>(src: &S, seat: &Rc<WlSeatGlobal>) {
|
||||
let data = src.source_data();
|
||||
data.seat.set(None);
|
||||
cancel_offers::<T>(src);
|
||||
cancel_offers(src);
|
||||
if !data.state.get().contains(SOURCE_STATE_FINISHED) {
|
||||
src.send_cancelled(seat);
|
||||
}
|
||||
// data.client.flush();
|
||||
}
|
||||
|
||||
pub fn offer_source_to<T: IpcVtable, S: DynDataSource>(src: &Rc<S>, client: &Rc<Client>) {
|
||||
fn offer_source_to_device<T: IpcVtable, S: DynDataSource>(
|
||||
src: &Rc<S>,
|
||||
dd: &Rc<T::Device>,
|
||||
data: &SourceData,
|
||||
shared: Rc<SharedState>,
|
||||
) {
|
||||
let device_data = T::get_device_data(dd);
|
||||
let offer_data = OfferData {
|
||||
device: CloneCell::new(Some(dd.clone())),
|
||||
source: CloneCell::new(Some(src.clone())),
|
||||
shared: shared.clone(),
|
||||
};
|
||||
let offer = match T::create_offer(dd, offer_data) {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
T::device_client(dd).error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
data.offers.insert(offer.offer_id(), offer.clone());
|
||||
let mt = data.mime_types.borrow_mut();
|
||||
T::send_offer(dd, &offer);
|
||||
for mt in mt.deref() {
|
||||
offer.clone().send_offer(mt);
|
||||
}
|
||||
match data.role.get() {
|
||||
Role::Selection => {
|
||||
T::send_selection(dd, Some(&offer));
|
||||
device_data.selection.set(Some(offer.clone()));
|
||||
}
|
||||
Role::Dnd => {
|
||||
device_data.dnd.set(Some(offer.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn offer_source_to_x<T, S>(src: &Rc<S>, dd: &Rc<XIpcDevice>)
|
||||
where
|
||||
T: IpcVtable<Device = XIpcDevice>,
|
||||
S: DynDataSource,
|
||||
{
|
||||
let data = src.source_data();
|
||||
src.cancel_offers();
|
||||
let shared = data.shared.get();
|
||||
shared.role.set(data.role.get());
|
||||
offer_source_to_device::<T, S>(src, dd, data, shared);
|
||||
}
|
||||
|
||||
fn offer_source_to_regular_client<T: IterableIpcVtable, S: DynDataSource>(
|
||||
src: &Rc<S>,
|
||||
client: &Rc<Client>,
|
||||
) {
|
||||
let data = src.source_data();
|
||||
let seat = match data.seat.get() {
|
||||
Some(a) => a,
|
||||
|
|
@ -301,38 +364,7 @@ pub fn offer_source_to<T: IpcVtable, S: DynDataSource>(src: &Rc<S>, client: &Rc<
|
|||
let shared = data.shared.get();
|
||||
shared.role.set(data.role.get());
|
||||
T::for_each_device(&seat, client.id, |dd| {
|
||||
let device_data = T::get_device_data(dd);
|
||||
let offer_data = OfferData {
|
||||
device: CloneCell::new(Some(dd.clone())),
|
||||
source: CloneCell::new(Some(src.clone())),
|
||||
shared: shared.clone(),
|
||||
is_xwm: device_data.is_xwm,
|
||||
};
|
||||
let offer = match T::create_offer(client, dd, offer_data) {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
client.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
data.offers.insert(offer.offer_id(), offer.clone());
|
||||
let mt = data.mime_types.borrow_mut();
|
||||
T::send_offer(dd, &offer);
|
||||
for mt in mt.deref() {
|
||||
offer.clone().send_offer(mt);
|
||||
}
|
||||
match data.role.get() {
|
||||
Role::Selection => {
|
||||
T::send_selection(dd, Some(&offer));
|
||||
device_data.selection.set(Some(offer.clone()));
|
||||
}
|
||||
Role::Dnd => {
|
||||
device_data.dnd.set(Some(offer.clone()));
|
||||
}
|
||||
}
|
||||
if !device_data.is_xwm {
|
||||
client.add_server_obj(&offer);
|
||||
}
|
||||
offer_source_to_device::<T, S>(src, dd, data, shared.clone());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use {
|
|||
ifs::{
|
||||
ipc::{
|
||||
break_device_loops, destroy_data_device, wl_data_offer::WlDataOffer,
|
||||
wl_data_source::WlDataSource, DeviceData, IpcVtable, OfferData, Role, XIpcVtable,
|
||||
wl_data_source::WlDataSource, DeviceData, IpcLocation, IpcVtable,
|
||||
IterableIpcVtable, OfferData, Role,
|
||||
},
|
||||
wl_seat::{WlSeatError, WlSeatGlobal},
|
||||
wl_surface::{SurfaceRole, WlSurfaceError},
|
||||
|
|
@ -13,8 +14,7 @@ use {
|
|||
leaks::Tracker,
|
||||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
wire::{wl_data_device::*, WlDataDeviceId, WlDataOfferId, WlDataSourceId, WlSurfaceId},
|
||||
xwayland::XWaylandEvent,
|
||||
wire::{wl_data_device::*, WlDataDeviceId, WlDataOfferId, WlSurfaceId},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
|
|
@ -38,60 +38,34 @@ impl WlDataDevice {
|
|||
client: &Rc<Client>,
|
||||
version: u32,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
is_xwm: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
client: client.clone(),
|
||||
version,
|
||||
seat: seat.clone(),
|
||||
data: DeviceData {
|
||||
selection: Default::default(),
|
||||
dnd: Default::default(),
|
||||
is_xwm,
|
||||
},
|
||||
data: Default::default(),
|
||||
tracker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_data_offer(&self, offer: &Rc<WlDataOffer>) {
|
||||
if self.data.is_xwm {
|
||||
self.client
|
||||
.state
|
||||
.xwayland
|
||||
.queue
|
||||
.push(XWaylandEvent::ClipboardSetOffer(offer.clone()));
|
||||
} else {
|
||||
self.client.event(DataOffer {
|
||||
self_id: self.id,
|
||||
id: offer.id,
|
||||
})
|
||||
}
|
||||
self.client.event(DataOffer {
|
||||
self_id: self.id,
|
||||
id: offer.id,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_selection(&self, offer: Option<&Rc<WlDataOffer>>) {
|
||||
if self.data.is_xwm {
|
||||
self.client
|
||||
.state
|
||||
.xwayland
|
||||
.queue
|
||||
.push(XWaylandEvent::ClipboardSetSelection(
|
||||
self.seat.id(),
|
||||
offer.cloned(),
|
||||
));
|
||||
} else {
|
||||
let id = offer.map(|o| o.id).unwrap_or(WlDataOfferId::NONE);
|
||||
self.client.event(Selection {
|
||||
self_id: self.id,
|
||||
id,
|
||||
})
|
||||
}
|
||||
let id = offer.map(|o| o.id).unwrap_or(WlDataOfferId::NONE);
|
||||
self.client.event(Selection {
|
||||
self_id: self.id,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_leave(&self) {
|
||||
if !self.data.is_xwm {
|
||||
self.client.event(Leave { self_id: self.id })
|
||||
}
|
||||
self.client.event(Leave { self_id: self.id })
|
||||
}
|
||||
|
||||
pub fn send_enter(
|
||||
|
|
@ -102,33 +76,27 @@ impl WlDataDevice {
|
|||
offer: WlDataOfferId,
|
||||
serial: u32,
|
||||
) {
|
||||
if !self.data.is_xwm {
|
||||
self.client.event(Enter {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
surface,
|
||||
x,
|
||||
y,
|
||||
id: offer,
|
||||
})
|
||||
}
|
||||
self.client.event(Enter {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
surface,
|
||||
x,
|
||||
y,
|
||||
id: offer,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_motion(&self, time_usec: u64, x: Fixed, y: Fixed) {
|
||||
if !self.data.is_xwm {
|
||||
self.client.event(Motion {
|
||||
self_id: self.id,
|
||||
time: (time_usec / 1000) as _,
|
||||
x,
|
||||
y,
|
||||
})
|
||||
}
|
||||
self.client.event(Motion {
|
||||
self_id: self.id,
|
||||
time: (time_usec / 1000) as _,
|
||||
x,
|
||||
y,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_drop(&self) {
|
||||
if !self.data.is_xwm {
|
||||
self.client.event(Drop { self_id: self.id })
|
||||
}
|
||||
self.client.event(Drop { self_id: self.id })
|
||||
}
|
||||
|
||||
fn start_drag(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataDeviceError> {
|
||||
|
|
@ -185,17 +153,18 @@ impl WlDataDevice {
|
|||
|
||||
pub struct ClipboardIpc;
|
||||
|
||||
impl XIpcVtable for ClipboardIpc {
|
||||
fn create_xwm_source(client: &Rc<Client>) -> Self::Source {
|
||||
WlDataSource::new(WlDataSourceId::NONE, client, true, 3)
|
||||
}
|
||||
|
||||
fn remove_from_seat(device: &Self::Device) {
|
||||
device.seat.remove_data_device(device);
|
||||
impl IterableIpcVtable for ClipboardIpc {
|
||||
fn for_each_device<C>(seat: &WlSeatGlobal, client: ClientId, f: C)
|
||||
where
|
||||
C: FnMut(&Rc<Self::Device>),
|
||||
{
|
||||
seat.for_each_data_device(0, client, f);
|
||||
}
|
||||
}
|
||||
|
||||
impl IpcVtable for ClipboardIpc {
|
||||
const LOCATION: IpcLocation = IpcLocation::Clipboard;
|
||||
|
||||
type Device = WlDataDevice;
|
||||
type Source = WlDataSource;
|
||||
type Offer = WlDataOffer;
|
||||
|
|
@ -216,27 +185,20 @@ impl IpcVtable for ClipboardIpc {
|
|||
seat.set_wl_data_source_selection(Some(source.clone()), serial)
|
||||
}
|
||||
|
||||
fn for_each_device<C>(seat: &WlSeatGlobal, client: ClientId, f: C)
|
||||
where
|
||||
C: FnMut(&Rc<Self::Device>),
|
||||
{
|
||||
seat.for_each_data_device(0, client, f);
|
||||
}
|
||||
|
||||
fn create_offer(
|
||||
client: &Rc<Client>,
|
||||
device: &Rc<WlDataDevice>,
|
||||
offer_data: OfferData<Self::Device>,
|
||||
) -> Result<Rc<Self::Offer>, ClientError> {
|
||||
let rc = Rc::new(WlDataOffer {
|
||||
id: client.new_id()?,
|
||||
offer_id: client.state.data_offer_ids.next(),
|
||||
client: client.clone(),
|
||||
id: device.client.new_id()?,
|
||||
offer_id: device.client.state.data_offer_ids.next(),
|
||||
client: device.client.clone(),
|
||||
device: device.clone(),
|
||||
data: offer_data,
|
||||
tracker: Default::default(),
|
||||
});
|
||||
track!(client, rc);
|
||||
track!(device.client, rc);
|
||||
device.client.add_server_obj(&rc);
|
||||
Ok(rc)
|
||||
}
|
||||
|
||||
|
|
@ -254,6 +216,10 @@ impl IpcVtable for ClipboardIpc {
|
|||
Role::Dnd => seat.cancel_dnd(),
|
||||
}
|
||||
}
|
||||
|
||||
fn device_client(dd: &Rc<Self::Device>) -> &Rc<Client> {
|
||||
&dd.client
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ impl WlDataDeviceManager {
|
|||
parser: MsgParser<'_, '_>,
|
||||
) -> Result<(), WlDataDeviceManagerError> {
|
||||
let req: CreateDataSource = self.client.parse(self, parser)?;
|
||||
let res = Rc::new(WlDataSource::new(req.id, &self.client, false, self.version));
|
||||
let res = Rc::new(WlDataSource::new(req.id, &self.client, self.version));
|
||||
track!(self.client, res);
|
||||
self.client.add_client_obj(&res)?;
|
||||
Ok(())
|
||||
|
|
@ -78,7 +78,6 @@ impl WlDataDeviceManager {
|
|||
&self.client,
|
||||
self.version,
|
||||
&seat.global,
|
||||
false,
|
||||
));
|
||||
track!(self.client, dev);
|
||||
seat.global.add_data_device(&dev);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ use {
|
|||
buffd::{MsgParser, MsgParserError},
|
||||
},
|
||||
wire::{wl_data_offer::*, WlDataOfferId, WlSurfaceId},
|
||||
xwayland::XWaylandEvent,
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
|
|
@ -64,8 +63,8 @@ impl DynDataOffer for WlDataOffer {
|
|||
WlDataOffer::send_action(self, action);
|
||||
}
|
||||
|
||||
fn send_offer(self: Rc<Self>, mime_type: &str) {
|
||||
WlDataOffer::send_offer(&self, mime_type);
|
||||
fn send_offer(&self, mime_type: &str) {
|
||||
WlDataOffer::send_offer(self, mime_type);
|
||||
}
|
||||
|
||||
fn destroy(&self) {
|
||||
|
|
@ -90,46 +89,29 @@ impl DynDataOffer for WlDataOffer {
|
|||
}
|
||||
|
||||
impl WlDataOffer {
|
||||
pub fn send_offer(self: &Rc<Self>, mime_type: &str) {
|
||||
if self.data.is_xwm {
|
||||
if let Some(src) = self.data.source.get() {
|
||||
if !src.source_data().is_xwm {
|
||||
self.client.state.xwayland.queue.push(
|
||||
XWaylandEvent::ClipboardAddOfferMimeType(
|
||||
self.clone(),
|
||||
mime_type.to_string(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.client.event(Offer {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
})
|
||||
}
|
||||
pub fn send_offer(&self, mime_type: &str) {
|
||||
self.client.event(Offer {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_source_actions(&self) {
|
||||
if !self.data.is_xwm {
|
||||
if let Some(src) = self.data.source.get() {
|
||||
if let Some(source_actions) = src.source_data().actions.get() {
|
||||
self.client.event(SourceActions {
|
||||
self_id: self.id,
|
||||
source_actions,
|
||||
})
|
||||
}
|
||||
if let Some(src) = self.data.source.get() {
|
||||
if let Some(source_actions) = src.source_data().actions.get() {
|
||||
self.client.event(SourceActions {
|
||||
self_id: self.id,
|
||||
source_actions,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_action(&self, dnd_action: u32) {
|
||||
if !self.data.is_xwm {
|
||||
self.client.event(Action {
|
||||
self_id: self.id,
|
||||
dnd_action,
|
||||
})
|
||||
}
|
||||
self.client.event(Action {
|
||||
self_id: self.id,
|
||||
dnd_action,
|
||||
})
|
||||
}
|
||||
|
||||
fn accept(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataOfferError> {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ use {
|
|||
ifs::{
|
||||
ipc::{
|
||||
add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source,
|
||||
detach_seat, offer_source_to,
|
||||
detach_seat, offer_source_to_regular_client, offer_source_to_x,
|
||||
wl_data_device::ClipboardIpc,
|
||||
wl_data_device_manager::{DND_ALL, DND_NONE},
|
||||
x_data_device::{XClipboardIpc, XIpcDevice},
|
||||
DataSource, DynDataOffer, DynDataSource, SharedState, SourceData,
|
||||
OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, SOURCE_STATE_CANCELLED,
|
||||
SOURCE_STATE_DROPPED,
|
||||
|
|
@ -23,7 +24,6 @@ use {
|
|||
clonecell::CloneCell,
|
||||
},
|
||||
wire::{wl_data_source::*, WlDataSourceId},
|
||||
xwayland::XWaylandEvent,
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
|
|
@ -44,7 +44,7 @@ pub struct WlDataSource {
|
|||
}
|
||||
|
||||
impl DataSource for WlDataSource {
|
||||
fn send_cancelled(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>) {
|
||||
fn send_cancelled(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
WlDataSource::send_cancelled(self, seat);
|
||||
}
|
||||
}
|
||||
|
|
@ -54,20 +54,24 @@ impl DynDataSource for WlDataSource {
|
|||
&self.data
|
||||
}
|
||||
|
||||
fn send_send(self: Rc<Self>, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
WlDataSource::send_send(&self, mime_type, fd);
|
||||
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
WlDataSource::send_send(self, mime_type, fd);
|
||||
}
|
||||
|
||||
fn offer_to(self: Rc<Self>, client: &Rc<Client>) {
|
||||
offer_source_to::<ClipboardIpc, Self>(&self, client);
|
||||
fn offer_to_regular_client(self: Rc<Self>, client: &Rc<Client>) {
|
||||
offer_source_to_regular_client::<ClipboardIpc, Self>(&self, client);
|
||||
}
|
||||
|
||||
fn detach_seat(self: Rc<Self>, seat: &Rc<WlSeatGlobal>) {
|
||||
detach_seat::<ClipboardIpc>(&self, seat);
|
||||
fn offer_to_x(self: Rc<Self>, dd: &Rc<XIpcDevice>) {
|
||||
offer_source_to_x::<XClipboardIpc, Self>(&self, dd);
|
||||
}
|
||||
|
||||
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
detach_seat(self, seat);
|
||||
}
|
||||
|
||||
fn cancel_offers(&self) {
|
||||
cancel_offers::<ClipboardIpc>(self);
|
||||
cancel_offers(self);
|
||||
}
|
||||
|
||||
fn send_target(&self, mime_type: Option<&str>) {
|
||||
|
|
@ -84,11 +88,11 @@ impl DynDataSource for WlDataSource {
|
|||
}
|
||||
|
||||
impl WlDataSource {
|
||||
pub fn new(id: WlDataSourceId, client: &Rc<Client>, is_xwm: bool, version: u32) -> Self {
|
||||
pub fn new(id: WlDataSourceId, client: &Rc<Client>, version: u32) -> Self {
|
||||
Self {
|
||||
id,
|
||||
tracker: Default::default(),
|
||||
data: SourceData::new(client, is_xwm),
|
||||
data: SourceData::new(client),
|
||||
version,
|
||||
toplevel_drag: Default::default(),
|
||||
}
|
||||
|
|
@ -108,7 +112,7 @@ impl WlDataSource {
|
|||
self.data.shared.set(Rc::new(SharedState::default()));
|
||||
self.send_target(None);
|
||||
self.send_action(DND_NONE);
|
||||
cancel_offers::<ClipboardIpc>(self);
|
||||
cancel_offers(self);
|
||||
}
|
||||
|
||||
pub fn update_selected_action(&self) {
|
||||
|
|
@ -159,74 +163,44 @@ impl WlDataSource {
|
|||
shared.state.or_assign(OFFER_STATE_DROPPED);
|
||||
}
|
||||
|
||||
pub fn send_cancelled(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>) {
|
||||
if self.data.is_xwm {
|
||||
self.data
|
||||
.client
|
||||
.state
|
||||
.xwayland
|
||||
.queue
|
||||
.push(XWaylandEvent::ClipboardCancelSource(self.clone()));
|
||||
} else {
|
||||
self.data.state.or_assign(SOURCE_STATE_CANCELLED);
|
||||
if let Some(drag) = self.toplevel_drag.take() {
|
||||
drag.finish_drag(seat);
|
||||
}
|
||||
self.data.client.event(Cancelled { self_id: self.id })
|
||||
pub fn send_cancelled(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.data.state.or_assign(SOURCE_STATE_CANCELLED);
|
||||
if let Some(drag) = self.toplevel_drag.take() {
|
||||
drag.finish_drag(seat);
|
||||
}
|
||||
self.data.client.event(Cancelled { self_id: self.id })
|
||||
}
|
||||
|
||||
pub fn send_send(self: &Rc<Self>, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
if self.data.is_xwm {
|
||||
self.data
|
||||
.client
|
||||
.state
|
||||
.xwayland
|
||||
.queue
|
||||
.push(XWaylandEvent::ClipboardSendSource(
|
||||
self.clone(),
|
||||
mime_type.to_string(),
|
||||
fd,
|
||||
));
|
||||
} else {
|
||||
self.data.client.event(Send {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
fd,
|
||||
})
|
||||
}
|
||||
pub fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
self.data.client.event(Send {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
fd,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_target(&self, mime_type: Option<&str>) {
|
||||
if !self.data.is_xwm {
|
||||
self.data.client.event(Target {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
})
|
||||
}
|
||||
self.data.client.event(Target {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_dnd_finished(&self) {
|
||||
if !self.data.is_xwm {
|
||||
self.data.client.event(DndFinished { self_id: self.id })
|
||||
}
|
||||
self.data.client.event(DndFinished { self_id: self.id })
|
||||
}
|
||||
|
||||
pub fn send_action(&self, dnd_action: u32) {
|
||||
if !self.data.is_xwm {
|
||||
self.data.client.event(Action {
|
||||
self_id: self.id,
|
||||
dnd_action,
|
||||
})
|
||||
}
|
||||
self.data.client.event(Action {
|
||||
self_id: self.id,
|
||||
dnd_action,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_dnd_drop_performed(&self) {
|
||||
if !self.data.is_xwm {
|
||||
self.data
|
||||
.client
|
||||
.event(DndDropPerformed { self_id: self.id })
|
||||
}
|
||||
self.data
|
||||
.client
|
||||
.event(DndDropPerformed { self_id: self.id })
|
||||
}
|
||||
|
||||
fn offer(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataSourceError> {
|
||||
|
|
|
|||
134
src/ifs/ipc/x_data_device.rs
Normal file
134
src/ifs/ipc/x_data_device.rs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::{
|
||||
ipc::{
|
||||
x_data_offer::XDataOffer, x_data_source::XDataSource, DeviceData, IpcLocation,
|
||||
IpcVtable, OfferData, Role,
|
||||
},
|
||||
wl_seat::{WlSeatError, WlSeatGlobal},
|
||||
},
|
||||
state::State,
|
||||
xwayland::XWaylandEvent,
|
||||
},
|
||||
std::rc::Rc,
|
||||
XWaylandEvent::IpcSetOffer,
|
||||
};
|
||||
|
||||
linear_ids!(XIpcDeviceIds, XIpcDeviceId, u64);
|
||||
|
||||
pub struct XIpcDevice {
|
||||
pub id: XIpcDeviceId,
|
||||
pub clipboard: DeviceData<XDataOffer>,
|
||||
pub primary_selection: DeviceData<XDataOffer>,
|
||||
pub seat: Rc<WlSeatGlobal>,
|
||||
pub state: Rc<State>,
|
||||
pub client: Rc<Client>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct XClipboardIpc;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct XPrimarySelectionIpc;
|
||||
|
||||
pub trait XIpc {
|
||||
const LOCATION: IpcLocation;
|
||||
|
||||
fn x_unset(seat: &Rc<WlSeatGlobal>);
|
||||
|
||||
fn x_device_data(dd: &XIpcDevice) -> &DeviceData<XDataOffer>;
|
||||
}
|
||||
|
||||
impl XIpc for XClipboardIpc {
|
||||
const LOCATION: IpcLocation = IpcLocation::Clipboard;
|
||||
|
||||
fn x_unset(seat: &Rc<WlSeatGlobal>) {
|
||||
seat.unset_selection();
|
||||
}
|
||||
|
||||
fn x_device_data(dd: &XIpcDevice) -> &DeviceData<XDataOffer> {
|
||||
&dd.clipboard
|
||||
}
|
||||
}
|
||||
|
||||
impl XIpc for XPrimarySelectionIpc {
|
||||
const LOCATION: IpcLocation = IpcLocation::PrimarySelection;
|
||||
|
||||
fn x_unset(seat: &Rc<WlSeatGlobal>) {
|
||||
seat.unset_primary_selection();
|
||||
}
|
||||
|
||||
fn x_device_data(dd: &XIpcDevice) -> &DeviceData<XDataOffer> {
|
||||
&dd.primary_selection
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: XIpc> IpcVtable for T {
|
||||
const LOCATION: IpcLocation = T::LOCATION;
|
||||
type Device = XIpcDevice;
|
||||
type Source = XDataSource;
|
||||
type Offer = XDataOffer;
|
||||
|
||||
fn get_device_data(dd: &Self::Device) -> &DeviceData<Self::Offer> {
|
||||
T::x_device_data(dd)
|
||||
}
|
||||
|
||||
fn get_device_seat(dd: &Self::Device) -> Rc<WlSeatGlobal> {
|
||||
dd.seat.clone()
|
||||
}
|
||||
|
||||
fn set_seat_selection(
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
source: &Rc<Self::Source>,
|
||||
_serial: Option<u32>,
|
||||
) -> Result<(), WlSeatError> {
|
||||
match source.location {
|
||||
IpcLocation::Clipboard => seat.set_selection(Some(source.clone())),
|
||||
IpcLocation::PrimarySelection => seat.set_primary_selection(Some(source.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_offer(
|
||||
dd: &Rc<Self::Device>,
|
||||
data: OfferData<Self::Device>,
|
||||
) -> Result<Rc<Self::Offer>, ClientError> {
|
||||
debug_assert!(dd.client.is_xwayland);
|
||||
let rc = Rc::new(XDataOffer {
|
||||
offer_id: dd.state.data_offer_ids.next(),
|
||||
device: dd.clone(),
|
||||
data,
|
||||
tracker: Default::default(),
|
||||
location: T::LOCATION,
|
||||
});
|
||||
track!(dd.client, rc);
|
||||
Ok(rc)
|
||||
}
|
||||
|
||||
fn send_selection(dd: &Self::Device, offer: Option<&Rc<Self::Offer>>) {
|
||||
dd.state
|
||||
.xwayland
|
||||
.queue
|
||||
.push(XWaylandEvent::IpcSetSelection {
|
||||
seat: dd.seat.id(),
|
||||
location: T::LOCATION,
|
||||
offer: offer.cloned(),
|
||||
});
|
||||
}
|
||||
|
||||
fn send_offer(dd: &Self::Device, offer: &Rc<Self::Offer>) {
|
||||
dd.state.xwayland.queue.push(IpcSetOffer {
|
||||
location: T::LOCATION,
|
||||
seat: dd.seat.id(),
|
||||
offer: offer.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
fn unset(seat: &Rc<WlSeatGlobal>, _role: Role) {
|
||||
T::x_unset(seat)
|
||||
}
|
||||
|
||||
fn device_client(dd: &Rc<Self::Device>) -> &Rc<Client> {
|
||||
&dd.client
|
||||
}
|
||||
}
|
||||
70
src/ifs/ipc/x_data_offer.rs
Normal file
70
src/ifs/ipc/x_data_offer.rs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
use {
|
||||
crate::{
|
||||
client::ClientId,
|
||||
ifs::{
|
||||
ipc::{
|
||||
cancel_offer, destroy_data_offer,
|
||||
x_data_device::{XClipboardIpc, XIpcDevice, XPrimarySelectionIpc},
|
||||
DataOffer, DataOfferId, DynDataOffer, IpcLocation, OfferData,
|
||||
},
|
||||
wl_seat::WlSeatGlobal,
|
||||
},
|
||||
leaks::Tracker,
|
||||
xwayland::XWaylandEvent,
|
||||
},
|
||||
std::rc::Rc,
|
||||
XWaylandEvent::IpcAddOfferMimeType,
|
||||
};
|
||||
|
||||
pub struct XDataOffer {
|
||||
pub offer_id: DataOfferId,
|
||||
pub device: Rc<XIpcDevice>,
|
||||
pub data: OfferData<XIpcDevice>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub location: IpcLocation,
|
||||
}
|
||||
|
||||
impl DataOffer for XDataOffer {
|
||||
type Device = XIpcDevice;
|
||||
|
||||
fn offer_data(&self) -> &OfferData<Self::Device> {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl DynDataOffer for XDataOffer {
|
||||
fn offer_id(&self) -> DataOfferId {
|
||||
self.offer_id
|
||||
}
|
||||
|
||||
fn client_id(&self) -> ClientId {
|
||||
self.device.client.id
|
||||
}
|
||||
|
||||
fn send_offer(&self, mime_type: &str) {
|
||||
self.device.state.xwayland.queue.push(IpcAddOfferMimeType {
|
||||
location: self.location,
|
||||
seat: self.device.seat.id(),
|
||||
offer: self.offer_id,
|
||||
mime_type: mime_type.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
fn destroy(&self) {
|
||||
match self.location {
|
||||
IpcLocation::Clipboard => destroy_data_offer::<XClipboardIpc>(self),
|
||||
IpcLocation::PrimarySelection => destroy_data_offer::<XPrimarySelectionIpc>(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel(&self) {
|
||||
match self.location {
|
||||
IpcLocation::Clipboard => cancel_offer::<XClipboardIpc>(self),
|
||||
IpcLocation::PrimarySelection => cancel_offer::<XPrimarySelectionIpc>(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_seat(&self) -> Rc<WlSeatGlobal> {
|
||||
self.device.seat.clone()
|
||||
}
|
||||
}
|
||||
79
src/ifs/ipc/x_data_source.rs
Normal file
79
src/ifs/ipc/x_data_source.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use {
|
||||
crate::{
|
||||
client::Client,
|
||||
ifs::{
|
||||
ipc::{
|
||||
cancel_offers, detach_seat, offer_source_to_regular_client,
|
||||
wl_data_device::ClipboardIpc, x_data_device::XIpcDevice,
|
||||
zwp_primary_selection_device_v1::PrimarySelectionIpc, DataSource, DynDataSource,
|
||||
IpcLocation, SourceData,
|
||||
},
|
||||
wl_seat::WlSeatGlobal,
|
||||
},
|
||||
state::State,
|
||||
xwayland::XWaylandEvent::{IpcCancelSource, IpcSendSource, IpcSetSelection},
|
||||
},
|
||||
std::rc::Rc,
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct XDataSource {
|
||||
pub state: Rc<State>,
|
||||
pub device: Rc<XIpcDevice>,
|
||||
pub data: SourceData,
|
||||
pub location: IpcLocation,
|
||||
}
|
||||
|
||||
impl DataSource for XDataSource {
|
||||
fn send_cancelled(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.state.xwayland.queue.push(IpcCancelSource {
|
||||
location: self.location,
|
||||
seat: seat.id(),
|
||||
source: self.data.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl DynDataSource for XDataSource {
|
||||
fn source_data(&self) -> &SourceData {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
self.state.xwayland.queue.push(IpcSendSource {
|
||||
location: self.location,
|
||||
seat: self.device.seat.id(),
|
||||
source: self.data.id,
|
||||
mime_type: mime_type.to_string(),
|
||||
fd,
|
||||
});
|
||||
}
|
||||
|
||||
fn offer_to_regular_client(self: Rc<Self>, client: &Rc<Client>) {
|
||||
match self.location {
|
||||
IpcLocation::Clipboard => {
|
||||
offer_source_to_regular_client::<ClipboardIpc, Self>(&self, client)
|
||||
}
|
||||
IpcLocation::PrimarySelection => {
|
||||
offer_source_to_regular_client::<PrimarySelectionIpc, Self>(&self, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn offer_to_x(self: Rc<Self>, _dd: &Rc<XIpcDevice>) {
|
||||
self.cancel_offers();
|
||||
self.state.xwayland.queue.push(IpcSetSelection {
|
||||
location: self.location,
|
||||
seat: self.device.seat.id(),
|
||||
offer: None,
|
||||
});
|
||||
}
|
||||
|
||||
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
detach_seat(self, seat);
|
||||
}
|
||||
|
||||
fn cancel_offers(&self) {
|
||||
cancel_offers(self)
|
||||
}
|
||||
}
|
||||
|
|
@ -55,11 +55,7 @@ impl ZwpPrimarySelectionDeviceManagerV1 {
|
|||
parser: MsgParser<'_, '_>,
|
||||
) -> Result<(), ZwpPrimarySelectionDeviceManagerV1Error> {
|
||||
let req: CreateSource = self.client.parse(self, parser)?;
|
||||
let res = Rc::new(ZwpPrimarySelectionSourceV1::new(
|
||||
req.id,
|
||||
&self.client,
|
||||
false,
|
||||
));
|
||||
let res = Rc::new(ZwpPrimarySelectionSourceV1::new(req.id, &self.client));
|
||||
track!(self.client, res);
|
||||
self.client.add_client_obj(&res)?;
|
||||
Ok(())
|
||||
|
|
@ -76,7 +72,6 @@ impl ZwpPrimarySelectionDeviceManagerV1 {
|
|||
&self.client,
|
||||
self.version,
|
||||
&seat.global,
|
||||
false,
|
||||
));
|
||||
track!(self.client, dev);
|
||||
seat.global.add_primary_selection_device(&dev);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use {
|
|||
break_device_loops, destroy_data_device,
|
||||
zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1,
|
||||
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, DeviceData,
|
||||
IpcVtable, OfferData, Role, XIpcVtable,
|
||||
IpcLocation, IpcVtable, IterableIpcVtable, OfferData, Role,
|
||||
},
|
||||
wl_seat::{WlSeatError, WlSeatGlobal},
|
||||
},
|
||||
|
|
@ -15,9 +15,8 @@ use {
|
|||
utils::buffd::{MsgParser, MsgParserError},
|
||||
wire::{
|
||||
zwp_primary_selection_device_v1::*, ZwpPrimarySelectionDeviceV1Id,
|
||||
ZwpPrimarySelectionOfferV1Id, ZwpPrimarySelectionSourceV1Id,
|
||||
ZwpPrimarySelectionOfferV1Id,
|
||||
},
|
||||
xwayland::XWaylandEvent,
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
|
|
@ -38,56 +37,32 @@ impl ZwpPrimarySelectionDeviceV1 {
|
|||
client: &Rc<Client>,
|
||||
version: u32,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
is_xwm: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
client: client.clone(),
|
||||
version,
|
||||
seat: seat.clone(),
|
||||
data: DeviceData {
|
||||
selection: Default::default(),
|
||||
dnd: Default::default(),
|
||||
is_xwm,
|
||||
},
|
||||
data: Default::default(),
|
||||
tracker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_data_offer(&self, offer: &Rc<ZwpPrimarySelectionOfferV1>) {
|
||||
if self.data.is_xwm {
|
||||
self.client
|
||||
.state
|
||||
.xwayland
|
||||
.queue
|
||||
.push(XWaylandEvent::PrimarySelectionSetOffer(offer.clone()));
|
||||
} else {
|
||||
self.client.event(DataOffer {
|
||||
self_id: self.id,
|
||||
offer: offer.id,
|
||||
})
|
||||
}
|
||||
self.client.event(DataOffer {
|
||||
self_id: self.id,
|
||||
offer: offer.id,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_selection(&self, offer: Option<&Rc<ZwpPrimarySelectionOfferV1>>) {
|
||||
if self.data.is_xwm {
|
||||
self.client
|
||||
.state
|
||||
.xwayland
|
||||
.queue
|
||||
.push(XWaylandEvent::PrimarySelectionSetSelection(
|
||||
self.seat.id(),
|
||||
offer.cloned(),
|
||||
));
|
||||
} else {
|
||||
let id = offer
|
||||
.map(|o| o.id)
|
||||
.unwrap_or(ZwpPrimarySelectionOfferV1Id::NONE);
|
||||
self.client.event(Selection {
|
||||
self_id: self.id,
|
||||
id,
|
||||
})
|
||||
}
|
||||
let id = offer
|
||||
.map(|o| o.id)
|
||||
.unwrap_or(ZwpPrimarySelectionOfferV1Id::NONE);
|
||||
self.client.event(Selection {
|
||||
self_id: self.id,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_selection(
|
||||
|
|
@ -126,17 +101,18 @@ impl ZwpPrimarySelectionDeviceV1 {
|
|||
|
||||
pub struct PrimarySelectionIpc;
|
||||
|
||||
impl XIpcVtable for PrimarySelectionIpc {
|
||||
fn create_xwm_source(client: &Rc<Client>) -> Self::Source {
|
||||
ZwpPrimarySelectionSourceV1::new(ZwpPrimarySelectionSourceV1Id::NONE, client, true)
|
||||
}
|
||||
|
||||
fn remove_from_seat(device: &Self::Device) {
|
||||
device.seat.remove_primary_selection_device(device);
|
||||
impl IterableIpcVtable for PrimarySelectionIpc {
|
||||
fn for_each_device<C>(seat: &WlSeatGlobal, client: ClientId, f: C)
|
||||
where
|
||||
C: FnMut(&Rc<Self::Device>),
|
||||
{
|
||||
seat.for_each_primary_selection_device(0, client, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl IpcVtable for PrimarySelectionIpc {
|
||||
const LOCATION: IpcLocation = IpcLocation::PrimarySelection;
|
||||
|
||||
type Device = ZwpPrimarySelectionDeviceV1;
|
||||
type Source = ZwpPrimarySelectionSourceV1;
|
||||
type Offer = ZwpPrimarySelectionOfferV1;
|
||||
|
|
@ -157,32 +133,20 @@ impl IpcVtable for PrimarySelectionIpc {
|
|||
seat.set_zwp_primary_selection(Some(source.clone()), serial)
|
||||
}
|
||||
|
||||
fn for_each_device<C>(seat: &WlSeatGlobal, client: ClientId, f: C)
|
||||
where
|
||||
C: FnMut(&Rc<Self::Device>),
|
||||
{
|
||||
seat.for_each_primary_selection_device(0, client, f)
|
||||
}
|
||||
|
||||
fn create_offer(
|
||||
client: &Rc<Client>,
|
||||
device: &Rc<ZwpPrimarySelectionDeviceV1>,
|
||||
offer_data: OfferData<Self::Device>,
|
||||
) -> Result<Rc<Self::Offer>, ClientError> {
|
||||
let id = if device.data.is_xwm {
|
||||
ZwpPrimarySelectionOfferV1Id::NONE
|
||||
} else {
|
||||
client.new_id()?
|
||||
};
|
||||
let rc = Rc::new(ZwpPrimarySelectionOfferV1 {
|
||||
id,
|
||||
offer_id: client.state.data_offer_ids.next(),
|
||||
id: device.client.new_id()?,
|
||||
offer_id: device.client.state.data_offer_ids.next(),
|
||||
seat: device.seat.clone(),
|
||||
client: client.clone(),
|
||||
client: device.client.clone(),
|
||||
data: offer_data,
|
||||
tracker: Default::default(),
|
||||
});
|
||||
track!(client, rc);
|
||||
track!(device.client, rc);
|
||||
device.client.add_server_obj(&rc);
|
||||
Ok(rc)
|
||||
}
|
||||
|
||||
|
|
@ -197,6 +161,10 @@ impl IpcVtable for PrimarySelectionIpc {
|
|||
fn unset(seat: &Rc<WlSeatGlobal>, _role: Role) {
|
||||
seat.unset_primary_selection();
|
||||
}
|
||||
|
||||
fn device_client(dd: &Rc<Self::Device>) -> &Rc<Client> {
|
||||
&dd.client
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ use {
|
|||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
wire::{zwp_primary_selection_offer_v1::*, ZwpPrimarySelectionOfferV1Id},
|
||||
xwayland::XWaylandEvent,
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
|
|
@ -47,8 +46,8 @@ impl DynDataOffer for ZwpPrimarySelectionOfferV1 {
|
|||
self.client.id
|
||||
}
|
||||
|
||||
fn send_offer(self: Rc<Self>, mime_type: &str) {
|
||||
ZwpPrimarySelectionOfferV1::send_offer(&self, mime_type);
|
||||
fn send_offer(&self, mime_type: &str) {
|
||||
ZwpPrimarySelectionOfferV1::send_offer(self, mime_type);
|
||||
}
|
||||
|
||||
fn destroy(&self) {
|
||||
|
|
@ -65,24 +64,11 @@ impl DynDataOffer for ZwpPrimarySelectionOfferV1 {
|
|||
}
|
||||
|
||||
impl ZwpPrimarySelectionOfferV1 {
|
||||
pub fn send_offer(self: &Rc<Self>, mime_type: &str) {
|
||||
if self.data.is_xwm {
|
||||
if let Some(src) = self.data.source.get() {
|
||||
if !src.source_data().is_xwm {
|
||||
self.client.state.xwayland.queue.push(
|
||||
XWaylandEvent::PrimarySelectionAddOfferMimeType(
|
||||
self.clone(),
|
||||
mime_type.to_string(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.client.event(Offer {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
})
|
||||
}
|
||||
pub fn send_offer(&self, mime_type: &str) {
|
||||
self.client.event(Offer {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
})
|
||||
}
|
||||
|
||||
fn receive(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionOfferV1Error> {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ use {
|
|||
ifs::{
|
||||
ipc::{
|
||||
add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source,
|
||||
detach_seat, offer_source_to, zwp_primary_selection_device_v1::PrimarySelectionIpc,
|
||||
detach_seat, offer_source_to_regular_client, offer_source_to_x,
|
||||
x_data_device::{XIpcDevice, XPrimarySelectionIpc},
|
||||
zwp_primary_selection_device_v1::PrimarySelectionIpc,
|
||||
DataSource, DynDataSource, SourceData,
|
||||
},
|
||||
wl_seat::WlSeatGlobal,
|
||||
|
|
@ -13,7 +15,6 @@ use {
|
|||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
wire::{zwp_primary_selection_source_v1::*, ZwpPrimarySelectionSourceV1Id},
|
||||
xwayland::XWaylandEvent,
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
|
|
@ -27,7 +28,7 @@ pub struct ZwpPrimarySelectionSourceV1 {
|
|||
}
|
||||
|
||||
impl DataSource for ZwpPrimarySelectionSourceV1 {
|
||||
fn send_cancelled(self: &Rc<Self>, _seat: &Rc<WlSeatGlobal>) {
|
||||
fn send_cancelled(&self, _seat: &Rc<WlSeatGlobal>) {
|
||||
ZwpPrimarySelectionSourceV1::send_cancelled(self);
|
||||
}
|
||||
}
|
||||
|
|
@ -37,64 +38,46 @@ impl DynDataSource for ZwpPrimarySelectionSourceV1 {
|
|||
&self.data
|
||||
}
|
||||
|
||||
fn send_send(self: Rc<Self>, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
ZwpPrimarySelectionSourceV1::send_send(&self, mime_type, fd)
|
||||
fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
ZwpPrimarySelectionSourceV1::send_send(self, mime_type, fd)
|
||||
}
|
||||
|
||||
fn offer_to(self: Rc<Self>, client: &Rc<Client>) {
|
||||
offer_source_to::<PrimarySelectionIpc, Self>(&self, client);
|
||||
fn offer_to_regular_client(self: Rc<Self>, client: &Rc<Client>) {
|
||||
offer_source_to_regular_client::<PrimarySelectionIpc, Self>(&self, client);
|
||||
}
|
||||
|
||||
fn detach_seat(self: Rc<Self>, seat: &Rc<WlSeatGlobal>) {
|
||||
detach_seat::<PrimarySelectionIpc>(&self, seat);
|
||||
fn offer_to_x(self: Rc<Self>, dd: &Rc<XIpcDevice>) {
|
||||
offer_source_to_x::<XPrimarySelectionIpc, Self>(&self, dd);
|
||||
}
|
||||
|
||||
fn detach_seat(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
detach_seat(self, seat);
|
||||
}
|
||||
|
||||
fn cancel_offers(&self) {
|
||||
cancel_offers::<PrimarySelectionIpc>(self);
|
||||
cancel_offers(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl ZwpPrimarySelectionSourceV1 {
|
||||
pub fn new(id: ZwpPrimarySelectionSourceV1Id, client: &Rc<Client>, is_xwm: bool) -> Self {
|
||||
pub fn new(id: ZwpPrimarySelectionSourceV1Id, client: &Rc<Client>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
data: SourceData::new(client, is_xwm),
|
||||
data: SourceData::new(client),
|
||||
tracker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_cancelled(self: &Rc<Self>) {
|
||||
if self.data.is_xwm {
|
||||
self.data
|
||||
.client
|
||||
.state
|
||||
.xwayland
|
||||
.queue
|
||||
.push(XWaylandEvent::PrimarySelectionCancelSource(self.clone()));
|
||||
} else {
|
||||
self.data.client.event(Cancelled { self_id: self.id });
|
||||
}
|
||||
pub fn send_cancelled(&self) {
|
||||
self.data.client.event(Cancelled { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn send_send(self: &Rc<Self>, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
if self.data.is_xwm {
|
||||
self.data
|
||||
.client
|
||||
.state
|
||||
.xwayland
|
||||
.queue
|
||||
.push(XWaylandEvent::PrimarySelectionSendSource(
|
||||
self.clone(),
|
||||
mime_type.to_string(),
|
||||
fd,
|
||||
));
|
||||
} else {
|
||||
self.data.client.event(Send {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
fd,
|
||||
})
|
||||
}
|
||||
pub fn send_send(&self, mime_type: &str, fd: Rc<OwnedFd>) {
|
||||
self.data.client.event(Send {
|
||||
self_id: self.id,
|
||||
mime_type,
|
||||
fd,
|
||||
})
|
||||
}
|
||||
|
||||
fn offer(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionSourceV1Error> {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use {
|
|||
self,
|
||||
wl_data_device::{ClipboardIpc, WlDataDevice},
|
||||
wl_data_source::WlDataSource,
|
||||
x_data_device::{XClipboardIpc, XIpcDevice, XIpcDeviceId, XPrimarySelectionIpc},
|
||||
zwp_primary_selection_device_v1::{
|
||||
PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1,
|
||||
},
|
||||
|
|
@ -58,6 +59,7 @@ use {
|
|||
linkedlist::LinkedNode,
|
||||
numcell::NumCell,
|
||||
rc_eq::rc_eq,
|
||||
smallmap::SmallMap,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
wire::{
|
||||
|
|
@ -108,7 +110,7 @@ pub struct DroppedDnd {
|
|||
impl Drop for DroppedDnd {
|
||||
fn drop(&mut self) {
|
||||
if let Some(src) = self.dnd.src.take() {
|
||||
ipc::detach_seat::<ClipboardIpc>(&src, &self.dnd.seat);
|
||||
ipc::detach_seat(&*src, &self.dnd.seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,6 +133,7 @@ pub struct WlSeatGlobal {
|
|||
keyboard_node: CloneCell<Rc<dyn Node>>,
|
||||
pressed_keys: RefCell<AHashSet<u32>>,
|
||||
bindings: RefCell<AHashMap<ClientId, AHashMap<WlSeatId, Rc<WlSeat>>>>,
|
||||
x_data_devices: SmallMap<XIpcDeviceId, Rc<XIpcDevice>, 1>,
|
||||
data_devices: RefCell<AHashMap<ClientId, AHashMap<WlDataDeviceId, Rc<WlDataDevice>>>>,
|
||||
primary_selection_devices: RefCell<
|
||||
AHashMap<
|
||||
|
|
@ -190,6 +193,7 @@ impl WlSeatGlobal {
|
|||
keyboard_node: CloneCell::new(state.root.clone()),
|
||||
pressed_keys: RefCell::new(Default::default()),
|
||||
bindings: Default::default(),
|
||||
x_data_devices: Default::default(),
|
||||
data_devices: RefCell::new(Default::default()),
|
||||
primary_selection_devices: RefCell::new(Default::default()),
|
||||
repeat_rate: Cell::new((25, 250)),
|
||||
|
|
@ -349,6 +353,20 @@ impl WlSeatGlobal {
|
|||
.insert(device.id, device.clone());
|
||||
}
|
||||
|
||||
pub fn set_x_data_device(&self, device: &Rc<XIpcDevice>) {
|
||||
self.x_data_devices.insert(device.id, device.clone());
|
||||
}
|
||||
|
||||
pub fn unset_x_data_device(&self, id: XIpcDeviceId) {
|
||||
self.x_data_devices.remove(&id);
|
||||
}
|
||||
|
||||
pub fn for_each_x_data_device(&self, mut f: impl FnMut(&Rc<XIpcDevice>)) {
|
||||
for (_, dev) in &self.x_data_devices {
|
||||
f(&dev);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_data_device(&self, device: &WlDataDevice) {
|
||||
let mut dd = self.data_devices.borrow_mut();
|
||||
if let Entry::Occupied(mut e) = dd.entry(device.client.id) {
|
||||
|
|
@ -709,11 +727,16 @@ impl WlSeatGlobal {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_selection_<T: ipc::IpcVtable, S: DynDataSource>(
|
||||
fn set_selection_<T, X, S>(
|
||||
self: &Rc<Self>,
|
||||
field: &CloneCell<Option<Rc<dyn DynDataSource>>>,
|
||||
src: Option<Rc<S>>,
|
||||
) -> Result<(), WlSeatError> {
|
||||
) -> Result<(), WlSeatError>
|
||||
where
|
||||
T: ipc::IterableIpcVtable,
|
||||
X: ipc::IpcVtable<Device = XIpcDevice>,
|
||||
S: DynDataSource,
|
||||
{
|
||||
if let (Some(new), Some(old)) = (&src, &field.get()) {
|
||||
if new.source_data().id == old.source_data().id {
|
||||
return Ok(());
|
||||
|
|
@ -727,15 +750,36 @@ impl WlSeatGlobal {
|
|||
old.detach_seat(self);
|
||||
}
|
||||
if let Some(client) = self.keyboard_node.get().node_client() {
|
||||
match src {
|
||||
Some(src) => ipc::offer_source_to::<T, _>(&src, &client),
|
||||
self.offer_selection_to_client::<T, X>(src.map(|v| v as Rc<_>), &client);
|
||||
// client.flush();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn offer_selection_to_client<T, X>(
|
||||
&self,
|
||||
selection: Option<Rc<dyn DynDataSource>>,
|
||||
client: &Rc<Client>,
|
||||
) where
|
||||
T: ipc::IterableIpcVtable,
|
||||
X: ipc::IpcVtable<Device = XIpcDevice>,
|
||||
{
|
||||
if let Some(src) = &selection {
|
||||
src.cancel_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) => src.offer_to_regular_client(client),
|
||||
_ => T::for_each_device(self, client.id, |device| {
|
||||
T::send_selection(device, None);
|
||||
}),
|
||||
}
|
||||
// client.flush();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start_drag(
|
||||
|
|
@ -780,7 +824,7 @@ impl WlSeatGlobal {
|
|||
self: &Rc<Self>,
|
||||
selection: Option<Rc<S>>,
|
||||
) -> Result<(), WlSeatError> {
|
||||
self.set_selection_::<ClipboardIpc, _>(&self.selection, selection)
|
||||
self.set_selection_::<ClipboardIpc, XClipboardIpc, _>(&self.selection, selection)
|
||||
}
|
||||
|
||||
pub fn may_modify_selection(&self, client: &Rc<Client>, serial: u32) -> bool {
|
||||
|
|
@ -821,7 +865,10 @@ impl WlSeatGlobal {
|
|||
self: &Rc<Self>,
|
||||
selection: Option<Rc<S>>,
|
||||
) -> Result<(), WlSeatError> {
|
||||
self.set_selection_::<PrimarySelectionIpc, _>(&self.primary_selection, selection)
|
||||
self.set_selection_::<PrimarySelectionIpc, XPrimarySelectionIpc, _>(
|
||||
&self.primary_selection,
|
||||
selection,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn reload_known_cursor(&self) {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@ use {
|
|||
client::ClientId,
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
ipc,
|
||||
ipc::{
|
||||
wl_data_device::{ClipboardIpc, WlDataDevice},
|
||||
zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1,
|
||||
x_data_device::{XClipboardIpc, XPrimarySelectionIpc},
|
||||
zwp_primary_selection_device_v1::{
|
||||
PrimarySelectionIpc, ZwpPrimarySelectionDeviceV1,
|
||||
},
|
||||
DynDataSource,
|
||||
},
|
||||
wl_seat::{
|
||||
wl_keyboard::{self, WlKeyboard},
|
||||
|
|
@ -742,22 +745,14 @@ impl WlSeatGlobal {
|
|||
});
|
||||
|
||||
if self.keyboard_node.get().node_client_id() != Some(surface.client.id) {
|
||||
match self.selection.get() {
|
||||
Some(sel) => sel.offer_to(&surface.client),
|
||||
None => {
|
||||
self.for_each_data_device(0, surface.client.id, |dd| {
|
||||
dd.send_selection(None);
|
||||
});
|
||||
}
|
||||
}
|
||||
match self.primary_selection.get() {
|
||||
Some(sel) => sel.offer_to(&surface.client),
|
||||
None => {
|
||||
self.for_each_primary_selection_device(0, surface.client.id, |dd| {
|
||||
dd.send_selection(None);
|
||||
});
|
||||
}
|
||||
}
|
||||
self.offer_selection_to_client::<ClipboardIpc, XClipboardIpc>(
|
||||
self.selection.get(),
|
||||
&surface.client,
|
||||
);
|
||||
self.offer_selection_to_client::<PrimarySelectionIpc, XPrimarySelectionIpc>(
|
||||
self.primary_selection.get(),
|
||||
&surface.client,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -819,7 +814,9 @@ impl WlSeatGlobal {
|
|||
serial: u32,
|
||||
) {
|
||||
if let Some(src) = &dnd.src {
|
||||
ipc::offer_source_to::<ClipboardIpc, _>(src, &surface.client);
|
||||
if !surface.client.is_xwayland {
|
||||
src.clone().offer_to_regular_client(&surface.client);
|
||||
}
|
||||
src.for_each_data_offer(|offer| {
|
||||
offer.send_enter(surface.id, x, y, serial);
|
||||
offer.send_source_actions();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use {
|
|||
fixed::Fixed,
|
||||
ifs::{
|
||||
ipc,
|
||||
ipc::{wl_data_device::ClipboardIpc, wl_data_source::WlDataSource},
|
||||
ipc::wl_data_source::WlDataSource,
|
||||
wl_seat::{
|
||||
wl_pointer::PendingScroll, Dnd, DroppedDnd, WlSeatError, WlSeatGlobal,
|
||||
CHANGE_CURSOR_MOVED,
|
||||
|
|
@ -456,7 +456,7 @@ impl PointerOwner for DndPointerOwner {
|
|||
target.node_seat_state().remove_dnd_target(seat);
|
||||
if !should_drop {
|
||||
if let Some(src) = &self.dnd.src {
|
||||
ipc::detach_seat::<ClipboardIpc>(src, seat);
|
||||
ipc::detach_seat(&**src, seat);
|
||||
}
|
||||
}
|
||||
if let Some(icon) = self.icon.get() {
|
||||
|
|
@ -527,7 +527,7 @@ impl PointerOwner for DndPointerOwner {
|
|||
target.node_on_dnd_leave(&self.dnd);
|
||||
target.node_seat_state().remove_dnd_target(seat);
|
||||
if let Some(src) = &self.dnd.src {
|
||||
ipc::detach_seat::<ClipboardIpc>(src, seat);
|
||||
ipc::detach_seat(&**src, seat);
|
||||
}
|
||||
if let Some(icon) = self.icon.get() {
|
||||
icon.set_dnd_icon_seat(seat.id(), None);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use {
|
|||
ifs::{
|
||||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
||||
ext_session_lock_v1::ExtSessionLockV1,
|
||||
ipc::{DataOfferIds, DataSourceIds},
|
||||
ipc::{x_data_device::XIpcDeviceIds, DataOfferIds, DataSourceIds},
|
||||
jay_render_ctx::JayRenderCtx,
|
||||
jay_seat_events::JaySeatEvents,
|
||||
jay_workspace_watcher::JayWorkspaceWatcher,
|
||||
|
|
@ -198,6 +198,7 @@ pub struct XWaylandState {
|
|||
pub enabled: Cell<bool>,
|
||||
pub handler: RefCell<Option<SpawnedFuture<()>>>,
|
||||
pub queue: Rc<AsyncQueue<XWaylandEvent>>,
|
||||
pub ipc_device_ids: XIpcDeviceIds,
|
||||
}
|
||||
|
||||
pub struct IdleState {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ use {
|
|||
compositor::DISPLAY,
|
||||
forker::{ForkerError, ForkerProxy},
|
||||
ifs::{
|
||||
ipc::{
|
||||
wl_data_offer::WlDataOffer, wl_data_source::WlDataSource,
|
||||
zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1,
|
||||
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
|
||||
},
|
||||
ipc::{x_data_offer::XDataOffer, DataOfferId, DataSourceId, IpcLocation},
|
||||
wl_seat::SeatId,
|
||||
wl_surface::x_surface::xwindow::{Xwindow, XwindowData},
|
||||
},
|
||||
|
|
@ -237,15 +233,32 @@ pub enum XWaylandEvent {
|
|||
#[allow(dead_code)]
|
||||
SeatChanged,
|
||||
|
||||
PrimarySelectionCancelSource(Rc<ZwpPrimarySelectionSourceV1>),
|
||||
PrimarySelectionSendSource(Rc<ZwpPrimarySelectionSourceV1>, String, Rc<OwnedFd>),
|
||||
PrimarySelectionSetOffer(Rc<ZwpPrimarySelectionOfferV1>),
|
||||
PrimarySelectionSetSelection(SeatId, Option<Rc<ZwpPrimarySelectionOfferV1>>),
|
||||
PrimarySelectionAddOfferMimeType(Rc<ZwpPrimarySelectionOfferV1>, String),
|
||||
|
||||
ClipboardCancelSource(Rc<WlDataSource>),
|
||||
ClipboardSendSource(Rc<WlDataSource>, String, Rc<OwnedFd>),
|
||||
ClipboardSetOffer(Rc<WlDataOffer>),
|
||||
ClipboardSetSelection(SeatId, Option<Rc<WlDataOffer>>),
|
||||
ClipboardAddOfferMimeType(Rc<WlDataOffer>, String),
|
||||
IpcCancelSource {
|
||||
location: IpcLocation,
|
||||
seat: SeatId,
|
||||
source: DataSourceId,
|
||||
},
|
||||
IpcSendSource {
|
||||
location: IpcLocation,
|
||||
seat: SeatId,
|
||||
source: DataSourceId,
|
||||
mime_type: String,
|
||||
fd: Rc<OwnedFd>,
|
||||
},
|
||||
IpcSetOffer {
|
||||
location: IpcLocation,
|
||||
seat: SeatId,
|
||||
offer: Rc<XDataOffer>,
|
||||
},
|
||||
IpcSetSelection {
|
||||
location: IpcLocation,
|
||||
seat: SeatId,
|
||||
offer: Option<Rc<XDataOffer>>,
|
||||
},
|
||||
IpcAddOfferMimeType {
|
||||
location: IpcLocation,
|
||||
seat: SeatId,
|
||||
offer: DataOfferId,
|
||||
mime_type: String,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue