diff --git a/build/wire.rs b/build/wire.rs index 5c5fa329..4700d9c1 100644 --- a/build/wire.rs +++ b/build/wire.rs @@ -213,6 +213,7 @@ enum Type { U32, I32, Str, + OptStr, BStr, Fixed, Fd, @@ -430,6 +431,7 @@ impl<'a> Parser<'a> { b"u32" => Type::U32, b"i32" => Type::I32, b"str" => Type::Str, + b"optstr" => Type::OptStr, b"bstr" => Type::BStr, b"fixed" => Type::Fixed, b"fd" => Type::Fd, @@ -513,6 +515,7 @@ fn write_type(f: &mut W, ty: &Type) -> Result<()> { Type::U32 => write!(f, "u32")?, Type::I32 => write!(f, "i32")?, Type::Str => write!(f, "&'a str")?, + Type::OptStr => write!(f, "Option<&'a str>")?, Type::BStr => write!(f, "&'a BStr")?, Type::Fixed => write!(f, "Fixed")?, Type::Fd => write!(f, "Rc")?, @@ -561,7 +564,7 @@ fn write_message_type( write!(f, ", ")?; } let formatter = match &field.val.ty.val { - Type::Str | Type::Fd | Type::Array(..) => "{:?}", + Type::OptStr | Type::Str | Type::Fd | Type::Array(..) => "{:?}", _ => "{}", }; write!(f, "{}: {}", field.val.name, formatter)?; @@ -578,7 +581,7 @@ fn write_message_type( fn write_message(f: &mut W, obj: &BStr, message: &Message) -> Result<()> { let has_reference_type = message.fields.iter().any(|f| match &f.val.ty.val { - Type::Str | Type::BStr | Type::Array(..) => true, + Type::OptStr | Type::Str | Type::BStr | Type::Array(..) => true, _ => false, }); let uppercase = message.name.to_ascii_uppercase(); @@ -609,6 +612,7 @@ fn write_message(f: &mut W, obj: &BStr, message: &Message) -> Result<( Type::Id(_) => "object", Type::U32 => "uint", Type::I32 => "int", + Type::OptStr => "optstr", Type::Str => "str", Type::Fixed => "fixed", Type::Fd => "fd", @@ -633,6 +637,7 @@ fn write_message(f: &mut W, obj: &BStr, message: &Message) -> Result<( Type::Id(_) => "object", Type::U32 => "uint", Type::I32 => "int", + Type::OptStr => "optstr", Type::Str | Type::BStr => "string", Type::Fixed => "fixed", Type::Fd => "fd", diff --git a/src/client/objects.rs b/src/client/objects.rs index d4050cb9..566ef167 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -1,4 +1,6 @@ use crate::client::{Client, ClientError}; +use crate::ifs::ipc::wl_data_source::WlDataSource; +use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1; use crate::ifs::wl_buffer::WlBuffer; use crate::ifs::wl_display::WlDisplay; use crate::ifs::wl_region::WlRegion; @@ -21,8 +23,6 @@ use ahash::AHashMap; use std::cell::{RefCell, RefMut}; use std::mem; use std::rc::Rc; -use crate::ifs::ipc::wl_data_source::WlDataSource; -use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1; pub struct Objects { pub display: CloneCell>>, diff --git a/src/globals.rs b/src/globals.rs index f1fcaa69..c08e656d 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -1,4 +1,6 @@ use crate::client::Client; +use crate::ifs::ipc::wl_data_device_manager::WlDataDeviceManagerGlobal; +use crate::ifs::ipc::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1Global; use crate::ifs::org_kde_kwin_server_decoration_manager::OrgKdeKwinServerDecorationManagerGlobal; use crate::ifs::wl_drm::WlDrmGlobal; use crate::ifs::wl_output::WlOutputGlobal; @@ -7,8 +9,8 @@ use crate::ifs::wl_seat::WlSeatGlobal; use crate::object::{Interface, ObjectId}; use crate::utils::copyhashmap::CopyHashMap; use crate::{ - NumCell, State, WlCompositorGlobal, WlShmGlobal, - WlSubcompositorGlobal, XdgWmBaseGlobal, ZwpLinuxDmabufV1Global, ZxdgDecorationManagerV1Global, + NumCell, State, WlCompositorGlobal, WlShmGlobal, WlSubcompositorGlobal, XdgWmBaseGlobal, + ZwpLinuxDmabufV1Global, ZxdgDecorationManagerV1Global, }; use ahash::AHashMap; use std::cell::RefMut; @@ -16,8 +18,6 @@ use std::error::Error; use std::fmt::{Display, Formatter}; use std::rc::Rc; use thiserror::Error; -use crate::ifs::ipc::wl_data_device_manager::WlDataDeviceManagerGlobal; -use crate::ifs::ipc::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1Global; #[derive(Debug, Error)] pub enum GlobalsError { diff --git a/src/ifs/ipc/mod.rs b/src/ifs/ipc/mod.rs index a6131d6e..84934a73 100644 --- a/src/ifs/ipc/mod.rs +++ b/src/ifs/ipc/mod.rs @@ -1,14 +1,16 @@ -use std::cell::{Cell, RefCell}; -use std::ops::{Deref, DerefMut}; -use std::rc::Rc; -use ahash::AHashSet; -use thiserror::Error; -use uapi::OwnedFd; use crate::client::{Client, ClientId, WaylandObject}; use crate::ifs::wl_seat::WlSeatGlobal; use crate::object::ObjectId; use crate::utils::clonecell::CloneCell; use crate::utils::smallmap::SmallMap; +use ahash::AHashSet; +use std::cell::{Cell, RefCell}; +use std::ops::{Deref}; +use std::rc::Rc; +use thiserror::Error; +use uapi::OwnedFd; +use crate::NumCell; +use crate::utils::bitflags::BitflagsExt; pub mod wl_data_device; pub mod wl_data_device_manager; @@ -27,112 +29,179 @@ pub enum Role { pub trait Vtable: Sized { type DeviceId: Eq + Copy; - type OfferId: Copy + From; + type OfferId: Eq + Copy + From; type Device; type Source; type Offer: WaylandObject; fn device_id(dd: &Self::Device) -> Self::DeviceId; + fn get_device_data(dd: &Self::Device) -> &DeviceData; fn get_offer_data(offer: &Self::Offer) -> &OfferData; fn get_source_data(src: &Self::Source) -> &SourceData; fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) - where C: FnMut(&Rc); - fn create_offer(client: &Rc, data: OfferData, id: ObjectId) -> Self::Offer; + where + C: FnMut(&Rc); + fn create_offer( + client: &Rc, + dd: &Rc, + data: OfferData, + id: ObjectId, + ) -> Self::Offer; fn send_selection(dd: &Self::Device, offer: Self::OfferId); fn send_cancelled(source: &Self::Source); fn get_offer_id(offer: &Self::Offer) -> Self::OfferId; fn send_offer(dd: &Self::Device, offer: &Self::Offer); fn send_mime_type(offer: &Self::Offer, mime_type: &str); - fn unset(seat: &Rc); + fn unset(seat: &Rc, role: Role); fn send_send(src: &Self::Source, mime_type: &str, fd: Rc); } -pub struct OfferData { - device_id: T::DeviceId, - source: CloneCell>>, - client: Rc, +pub struct DeviceData { + selection: CloneCell>>, + dnd: CloneCell>>, } -struct Attachment { - seat: Rc, - role: Role, - offers: SmallMap, 1>, -} - -impl Attachment { - fn detach_offers(&self) { - while let Some((_, offer)) = self.offers.pop() { - T::get_offer_data(&offer).source.set(None); +impl Default for DeviceData { + fn default() -> Self { + Self { + selection: Default::default(), + dnd: Default::default(), } } } +pub struct OfferData { + device: CloneCell>>, + source: CloneCell>>, + client: Rc, + shared: Rc, +} + #[derive(Debug, Error)] pub enum IpcError { #[error("The data source is already attached")] AlreadyAttached, + #[error("The data source does not have drag-and-drop actions set")] + ActionsNotSet, + #[error("The data source has drag-and-drop actions set")] + ActionsSet, } +const OFFER_STATE_ACCEPTED: u32 = 1 << 0; +const OFFER_STATE_FINISHED: u32 = 1 << 1; +const OFFER_STATE_DROPPED: u32 = 1 << 2; + +const SOURCE_STATE_USED: u32 = 1 << 1; +const SOURCE_STATE_FINISHED: u32 = 1 << 2; + pub struct SourceData { - attachment: RefCell>>, - disconnecting: Cell, + seat: CloneCell>>, + offers: SmallMap, 1>, + offer_client: Cell, mime_types: RefCell>, - client: Rc + client: Rc, + state: NumCell, + actions: Cell>, + role: Cell, + shared: CloneCell>, +} + +struct SharedState { + state: NumCell, + role: Cell, + receiver_actions: Cell, + receiver_preferred_action: Cell, + selected_action: Cell, +} + +impl Default for SharedState { + fn default() -> Self { + Self { + state: NumCell::new(0), + role: Cell::new(Role::Selection), + receiver_actions: Cell::new(0), + receiver_preferred_action: Cell::new(0), + selected_action: Cell::new(0) + } + } } impl SourceData { fn new(client: &Rc) -> Self { Self { - attachment: Default::default(), - disconnecting: Cell::new(false), + seat: Default::default(), + offers: Default::default(), + offer_client: Cell::new(client.id), mime_types: Default::default(), client: client.clone(), + state: NumCell::new(0), + actions: Cell::new(None), + role: Cell::new(Role::Selection), + shared: Default::default(), } } } -pub fn attach_source( +pub fn attach_seat( src: &T::Source, seat: &Rc, role: Role, ) -> Result<(), IpcError> { - let src = T::get_source_data(src); - let mut attachment = src.attachment.borrow_mut(); - if attachment.is_some() { + let data = T::get_source_data(src); + let mut state = data.state.get(); + if state.contains(SOURCE_STATE_USED) { return Err(IpcError::AlreadyAttached); } - *attachment = Some(Attachment { - seat: seat.clone(), - role, - offers: Default::default(), - }); + state |= SOURCE_STATE_USED; + if role == Role::Dnd { + if data.actions.get().is_none() { + return Err(IpcError::ActionsNotSet); + } + } else { + if data.actions.get().is_some() { + return Err(IpcError::ActionsSet); + } + } + data.state.set(state); + data.role.set(role); + data.seat.set(Some(seat.clone())); Ok(()) } -pub fn detach_source(src: &T::Source) { +pub fn cancel_offers(src: &T::Source) { let data = T::get_source_data(src); - if data.disconnecting.get() { - return; + while let Some((_, offer)) = data.offers.pop() { + let data = T::get_offer_data(&offer); + log::error!("cancel_offers"); + data.source.take(); + destroy_offer::(&offer); } - if let Some(attachment) = data.attachment.borrow_mut().take() { - attachment.detach_offers(); +} + +pub fn detach_seat(src: &T::Source) { + let data = T::get_source_data(src); + data.seat.set(None); + cancel_offers::(src); + if !data.state.get().contains(SOURCE_STATE_FINISHED) { + T::send_cancelled(src); } - T::send_cancelled(src); } pub fn offer_source_to(src: &Rc, client: &Rc) { let data = T::get_source_data(src); - let mut attachment = data.attachment.borrow_mut(); - let attachment = match attachment.deref_mut() { + let seat = match data.seat.get() { Some(a) => a, _ => { log::error!("Trying to create an offer from a unattached data source"); return; } }; - attachment.detach_offers(); - T::for_each_device(&attachment.seat, client.id, |dd| { + cancel_offers::(src); + data.offer_client.set(client.id); + let shared = data.shared.get(); + shared.role.set(data.role.get()); + T::for_each_device(&seat, client.id, |dd| { let id = match client.new_id() { Ok(id) => id, Err(e) => { @@ -140,60 +209,105 @@ pub fn offer_source_to(src: &Rc, client: &Rc) { return; } }; + let device_data = T::get_device_data(dd); let offer_data = OfferData { - device_id: T::device_id(dd), + device: CloneCell::new(Some(dd.clone())), source: CloneCell::new(Some(src.clone())), client: client.clone(), + shared: shared.clone(), }; - let offer = Rc::new(T::create_offer(client, offer_data, id)); - attachment.offers.insert(T::device_id(dd), offer.clone()); + let offer = Rc::new(T::create_offer(client, dd, offer_data, id)); + data.offers.insert(id.into(), offer.clone()); let mt = data.mime_types.borrow_mut(); T::send_offer(dd, &offer); for mt in mt.deref() { T::send_mime_type(&offer, mt); } - if attachment.role == Role::Selection { - T::send_selection(dd, T::get_offer_id(&offer)); + match data.role.get() { + Role::Selection => { + T::send_selection(dd, T::get_offer_id(&offer)); + device_data.selection.set(Some(offer.clone())); + } + Role::Dnd => { + device_data.dnd.set(Some(offer.clone())); + } } client.add_server_obj(&offer); }); } - fn add_mime_type(src: &T::Source, mime_type: &str) { let data = T::get_source_data(src); - if data - .mime_types - .borrow_mut() - .insert(mime_type.to_string()) - { - if let Some(attachment) = data.attachment.borrow_mut().deref_mut() { - for (_, offer) in &attachment.offers { - T::send_mime_type(&offer, mime_type); - let data = T::get_offer_data(&offer); - data.client.flush(); + if data.mime_types.borrow_mut().insert(mime_type.to_string()) { + for (_, offer) in &data.offers { + T::send_mime_type(&offer, mime_type); + let data = T::get_offer_data(&offer); + data.client.flush(); + } + } +} + +fn destroy_source(src: &T::Source) { + let data = T::get_source_data(src); + if let Some(seat) = data.seat.take() { + T::unset(&seat, data.role.get()); + } +} + +fn destroy_offer(offer: &T::Offer) { + let data = T::get_offer_data(offer); + if let Some(device) = data.device.take() { + let device_data = T::get_device_data(&device); + match data.shared.role.get() { + Role::Selection => { + T::send_selection(&device, ObjectId::NONE.into()); + device_data.selection.take(); + } + Role::Dnd => { + device_data.dnd.take(); + } + } + } + log::error!("destroy_offer"); + if let Some(src) = data.source.take() { + let src_data = T::get_source_data(&src); + src_data.offers.remove(&T::get_offer_id(offer)); + if src_data.offers.is_empty() && src_data.role.get() == Role::Dnd && data.shared.state.get().contains(OFFER_STATE_DROPPED) { + if let Some(seat) = src_data.seat.take() { + T::unset(&seat, data.shared.role.get()); } } } } -fn disconnect_source(src: &T::Source) { - let data = T::get_source_data(src); - data.disconnecting.set(true); - if let Some(attachment) = data.attachment.borrow_mut().take() { - attachment.detach_offers(); - T::unset(&attachment.seat); +fn destroy_device(dd: &T::Device) { + let data = T::get_device_data(dd); + let offers = [data.selection.take(), data.dnd.take()]; + for offer in offers.into_iter().flat_map(|o| o.into_iter()) { + T::get_offer_data(&offer).device.take(); + destroy_offer::(&offer); } } -fn disconnect_offer(offer: &T::Offer) { - let data = T::get_offer_data(offer); - if let Some(src) = data.source.set(None) { - let src_data = T::get_source_data(&src); - if let Some(attachment) = src_data.attachment.borrow_mut().deref_mut() { - attachment.offers.remove(&data.device_id); - } +fn break_source_loops(src: &T::Source) { + let data = T::get_source_data(src); + if data.offer_client.get() == data.client.id { + data.offers.take(); } + destroy_source::(src); +} + +fn break_offer_loops(offer: &T::Offer) { + let data = T::get_offer_data(offer); + data.device.set(None); + destroy_offer::(offer); +} + +fn break_device_loops(dd: &T::Device) { + let data = T::get_device_data(dd); + data.selection.take(); + data.dnd.take(); + destroy_device::(dd); } fn receive(offer: &T::Offer, mime_type: &str, fd: Rc) { diff --git a/src/ifs/ipc/wl_data_device.rs b/src/ifs/ipc/wl_data_device.rs index 03a43d5e..18595ff0 100644 --- a/src/ifs/ipc/wl_data_device.rs +++ b/src/ifs/ipc/wl_data_device.rs @@ -1,17 +1,20 @@ use crate::client::{Client, ClientError, ClientId}; +use crate::fixed::Fixed; use crate::ifs::ipc::wl_data_device_manager::WlDataDeviceManager; -use crate::ifs::ipc::wl_data_source::{WlDataSource}; +use crate::ifs::ipc::wl_data_offer::WlDataOffer; +use crate::ifs::ipc::wl_data_source::WlDataSource; +use crate::ifs::ipc::{ + break_device_loops, destroy_device, DeviceData, OfferData, Role, SourceData, Vtable, +}; use crate::ifs::wl_seat::{WlSeat, WlSeatError, WlSeatGlobal}; use crate::object::{Object, ObjectId}; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; use crate::wire::wl_data_device::*; -use crate::wire::{WlDataDeviceId, WlDataOfferId}; +use crate::wire::{WlDataDeviceId, WlDataOfferId, WlSurfaceId}; use std::rc::Rc; use thiserror::Error; use uapi::OwnedFd; -use crate::ifs::ipc::{OfferData, SourceData, Vtable}; -use crate::ifs::ipc::wl_data_offer::WlDataOffer; #[allow(dead_code)] const ROLE: u32 = 0; @@ -20,6 +23,7 @@ pub struct WlDataDevice { pub id: WlDataDeviceId, pub manager: Rc, pub seat: Rc, + pub data: DeviceData, } impl WlDataDevice { @@ -28,6 +32,7 @@ impl WlDataDevice { id, manager: manager.clone(), seat: seat.clone(), + data: Default::default(), } } @@ -45,8 +50,43 @@ impl WlDataDevice { }) } + pub fn send_leave(&self) { + self.manager.client.event(Leave { self_id: self.id }) + } + + pub fn send_enter(&self, surface: WlSurfaceId, x: Fixed, y: Fixed, offer: WlDataOfferId) { + self.manager.client.event(Enter { + self_id: self.id, + serial: 0, + surface, + x, + y, + id: offer, + }) + } + + pub fn send_motion(&self, x: Fixed, y: Fixed) { + self.manager.client.event(Motion { + self_id: self.id, + time: 0, + x, + y, + }) + } + + pub fn send_drop(&self) { + self.manager.client.event(Drop { self_id: self.id }) + } + fn start_drag(&self, parser: MsgParser<'_, '_>) -> Result<(), StartDragError> { - let _req: StartDrag = self.manager.client.parse(self, parser)?; + let req: StartDrag = self.manager.client.parse(self, parser)?; + let origin = self.manager.client.lookup(req.origin)?; + let source = if req.source.is_some() { + Some(self.manager.client.lookup(req.source)?) + } else { + None + }; + self.seat.global.start_drag(&origin, source)?; Ok(()) } @@ -63,6 +103,7 @@ impl WlDataDevice { fn release(&self, parser: MsgParser<'_, '_>) -> Result<(), ReleaseError> { let _req: Release = self.manager.client.parse(self, parser)?; + destroy_device::(self); self.seat.remove_data_device(self); self.manager.client.remove_obj(self)?; Ok(()) @@ -80,23 +121,36 @@ impl Vtable for WlDataDevice { dd.id } + fn get_device_data(dd: &Self::Device) -> &DeviceData { + &dd.data + } + fn get_offer_data(offer: &Self::Offer) -> &OfferData { - &offer.offer_data + &offer.data } fn get_source_data(src: &Self::Source) -> &SourceData { &src.data } - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) where C: FnMut(&Rc) { + fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) + where + C: FnMut(&Rc), + { seat.for_each_data_device(0, client, f); } - fn create_offer(client: &Rc, offer_data: OfferData, id: ObjectId) -> Self::Offer { + fn create_offer( + client: &Rc, + device: &Rc, + offer_data: OfferData, + id: ObjectId, + ) -> Self::Offer { WlDataOffer { id: id.into(), client: client.clone(), - offer_data, + device: device.clone(), + data: offer_data, } } @@ -120,8 +174,11 @@ impl Vtable for WlDataDevice { offer.send_offer(mime_type); } - fn unset(seat: &Rc) { - seat.unset_selection(); + fn unset(seat: &Rc, role: Role) { + match role { + Role::Selection => seat.unset_selection(), + Role::Dnd => seat.cancel_dnd(), + } } fn send_send(src: &Self::Source, mime_type: &str, fd: Rc) { @@ -143,6 +200,7 @@ impl Object for WlDataDevice { } fn break_loops(&self) { + break_device_loops::(self); self.seat.remove_data_device(self); } } @@ -168,9 +226,12 @@ pub enum StartDragError { ParseFailed(#[source] Box), #[error(transparent)] ClientError(Box), + #[error(transparent)] + WlSeatError(Box), } efrom!(StartDragError, ParseFailed, MsgParserError); efrom!(StartDragError, ClientError); +efrom!(StartDragError, WlSeatError); #[derive(Debug, Error)] pub enum SetSelectionError { diff --git a/src/ifs/ipc/wl_data_device_manager.rs b/src/ifs/ipc/wl_data_device_manager.rs index a7464597..8d5a1664 100644 --- a/src/ifs/ipc/wl_data_device_manager.rs +++ b/src/ifs/ipc/wl_data_device_manager.rs @@ -10,14 +10,14 @@ use crate::wire::WlDataDeviceManagerId; use std::rc::Rc; use thiserror::Error; +pub(super) const DND_NONE: u32 = 0; #[allow(dead_code)] -const DND_NONE: u32 = 0; +pub(super) const DND_COPY: u32 = 1; #[allow(dead_code)] -const DND_COPY: u32 = 1; +pub(super) const DND_MOVE: u32 = 2; #[allow(dead_code)] -const DND_MOVE: u32 = 2; -#[allow(dead_code)] -const DND_ASK: u32 = 4; +pub(super) const DND_ASK: u32 = 4; +pub(super) const DND_ALL: u32 = 7; pub struct WlDataDeviceManagerGlobal { name: GlobalName, diff --git a/src/ifs/ipc/wl_data_offer.rs b/src/ifs/ipc/wl_data_offer.rs index a8e54661..ad1dc4fe 100644 --- a/src/ifs/ipc/wl_data_offer.rs +++ b/src/ifs/ipc/wl_data_offer.rs @@ -1,13 +1,15 @@ use crate::client::{Client, ClientError}; +use crate::ifs::ipc::wl_data_device::WlDataDevice; +use crate::ifs::ipc::{break_offer_loops, destroy_offer, receive, OfferData, Role, OFFER_STATE_FINISHED, OFFER_STATE_DROPPED, OFFER_STATE_ACCEPTED, SOURCE_STATE_FINISHED}; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; use crate::wire::wl_data_offer::*; -use crate::wire::{WlDataOfferId}; +use crate::wire::WlDataOfferId; use std::rc::Rc; use thiserror::Error; -use crate::ifs::ipc::{disconnect_offer, OfferData, receive}; -use crate::ifs::ipc::wl_data_device::WlDataDevice; +use crate::ifs::ipc::wl_data_device_manager::{DND_ALL}; +use crate::utils::bitflags::BitflagsExt; #[allow(dead_code)] const INVALID_FINISH: u32 = 0; @@ -18,11 +20,11 @@ const INVALID_ACTION: u32 = 2; #[allow(dead_code)] const INVALID_OFFER: u32 = 3; - pub struct WlDataOffer { pub id: WlDataOfferId, pub client: Rc, - pub offer_data: OfferData, + pub device: Rc, + pub data: OfferData, } impl WlDataOffer { @@ -33,31 +35,101 @@ impl WlDataOffer { }) } + pub fn send_source_actions(&self) { + if let Some(src) = self.data.source.get() { + if let Some(source_actions) = src.data.actions.get() { + self.client.event(SourceActions { + self_id: self.id, + source_actions, + }) + } + } + } + + pub fn send_action(&self, dnd_action: u32) { + self.client.event(Action { + self_id: self.id, + dnd_action, + }) + } + fn accept(&self, parser: MsgParser<'_, '_>) -> Result<(), AcceptError> { - let _req: Accept = self.client.parse(self, parser)?; + let req: Accept = self.client.parse(self, parser)?; + let mut state = self.data.shared.state.get(); + if state.contains(OFFER_STATE_FINISHED) { + return Err(AcceptError::AlreadyFinished); + } + if req.mime_type.is_some() { + state |= OFFER_STATE_ACCEPTED; + } else { + state &= !OFFER_STATE_ACCEPTED; + } + self.data.shared.state.set(state); + if let Some(src) = self.data.source.get() { + src.send_target(req.mime_type); + } Ok(()) } fn receive(&self, parser: MsgParser<'_, '_>) -> Result<(), ReceiveError> { let req: Receive = self.client.parse(self, parser)?; + if self.data.shared.state.get().contains(OFFER_STATE_FINISHED) { + return Err(ReceiveError::AlreadyFinished); + } receive::(self, req.mime_type, req.fd); Ok(()) } fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { let _req: Destroy = self.client.parse(self, parser)?; - disconnect_offer::(self); + destroy_offer::(self); self.client.remove_obj(self)?; Ok(()) } fn finish(&self, parser: MsgParser<'_, '_>) -> Result<(), FinishError> { let _req: Finish = self.client.parse(self, parser)?; + if self.data.shared.role.get() != Role::Dnd { + return Err(FinishError::NotDnd); + } + let mut state = self.data.shared.state.get(); + if state.contains(OFFER_STATE_FINISHED) { + return Err(FinishError::AlreadyFinished); + } + if !state.contains(OFFER_STATE_DROPPED) { + return Err(FinishError::StillDragging); + } + if !state.contains(OFFER_STATE_ACCEPTED) { + return Err(FinishError::NoMimeTypeAccepted); + } + state |= OFFER_STATE_FINISHED; + if let Some(src) = self.data.source.get() { + src.data.state.or_assign(SOURCE_STATE_FINISHED); + src.send_dnd_finished(); + } else { + log::error!("no source"); + } + self.data.shared.state.set(state); Ok(()) } fn set_actions(&self, parser: MsgParser<'_, '_>) -> Result<(), SetActionsError> { - let _req: SetActions = self.client.parse(self, parser)?; + let req: SetActions = self.client.parse(self, parser)?; + let state = self.data.shared.state.get(); + if state.contains(OFFER_STATE_FINISHED) { + return Err(SetActionsError::AlreadyFinished); + } + if (req.dnd_actions & !DND_ALL, req.preferred_action & !DND_ALL) != (0, 0) { + return Err(SetActionsError::InvalidActions); + } + if req.preferred_action.count_ones() > 1 { + return Err(SetActionsError::MultiplePreferred); + } + self.data.shared.receiver_actions.set(req.dnd_actions); + self.data.shared.receiver_preferred_action.set(req.preferred_action); + if let Some(src) = self.data.source.get() { + src.update_selected_action(); + } Ok(()) } } @@ -78,7 +150,7 @@ impl Object for WlDataOffer { } fn break_loops(&self) { - disconnect_offer::(self); + break_offer_loops::(self); } } @@ -107,6 +179,8 @@ pub enum AcceptError { ParseFailed(#[source] Box), #[error(transparent)] ClientError(Box), + #[error("`finish` was already called")] + AlreadyFinished, } efrom!(AcceptError, ParseFailed, MsgParserError); efrom!(AcceptError, ClientError); @@ -117,6 +191,8 @@ pub enum ReceiveError { ParseFailed(#[source] Box), #[error(transparent)] ClientError(Box), + #[error("`finish` was already called")] + AlreadyFinished, } efrom!(ReceiveError, ParseFailed, MsgParserError); efrom!(ReceiveError, ClientError); @@ -137,6 +213,14 @@ pub enum FinishError { ParseFailed(#[source] Box), #[error(transparent)] ClientError(Box), + #[error("`finish` was already called")] + AlreadyFinished, + #[error("The drag operation is still ongoing")] + StillDragging, + #[error("Client did not accept a mime type")] + NoMimeTypeAccepted, + #[error("This is not a drag-and-drop offer")] + NotDnd, } efrom!(FinishError, ParseFailed, MsgParserError); efrom!(FinishError, ClientError); @@ -147,6 +231,12 @@ pub enum SetActionsError { ParseFailed(#[source] Box), #[error(transparent)] ClientError(Box), + #[error("`finish` was already called")] + AlreadyFinished, + #[error("The set of actions is invalid")] + InvalidActions, + #[error("Multiple preferred actions were specified")] + MultiplePreferred, } efrom!(SetActionsError, ParseFailed, MsgParserError); efrom!(SetActionsError, ClientError); diff --git a/src/ifs/ipc/wl_data_source.rs b/src/ifs/ipc/wl_data_source.rs index 2d840a82..d6fd3e37 100644 --- a/src/ifs/ipc/wl_data_source.rs +++ b/src/ifs/ipc/wl_data_source.rs @@ -1,14 +1,17 @@ use crate::client::{Client, ClientError}; +use crate::ifs::ipc::wl_data_device::WlDataDevice; +use crate::ifs::ipc::wl_data_offer::WlDataOffer; +use crate::ifs::ipc::{add_mime_type, break_source_loops, cancel_offers, destroy_source, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, SharedState, SourceData}; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; use crate::wire::wl_data_source::*; -use crate::wire::{WlDataSourceId}; +use crate::wire::WlDataSourceId; use std::rc::Rc; use thiserror::Error; use uapi::OwnedFd; -use crate::ifs::ipc::{add_mime_type, disconnect_source, SourceData}; -use crate::ifs::ipc::wl_data_device::WlDataDevice; +use crate::ifs::ipc::wl_data_device_manager::{DND_ALL, DND_NONE}; +use crate::utils::bitflags::BitflagsExt; #[allow(dead_code)] const INVALID_ACTION_MASK: u32 = 0; @@ -28,6 +31,61 @@ impl WlDataSource { } } + pub fn on_leave(&self) { + if self.data.shared.get().state.get().contains(OFFER_STATE_DROPPED) { + return; + } + self.data.shared.set(Rc::new(SharedState::default())); + self.send_target(None); + self.send_action(DND_NONE); + cancel_offers::(self); + } + + pub fn update_selected_action(&self) { + let shared = self.data.shared.get(); + let server_actions = match self.data.actions.get() { + Some(n) => n, + _ => { + log::error!("Server actions not set"); + return; + } + }; + let actions = server_actions & shared.receiver_actions.get(); + let action = if actions.contains(shared.receiver_preferred_action.get()) { + shared.receiver_preferred_action.get() + } else if actions != 0 { + 1 << actions.trailing_zeros() + } else { + 0 + }; + log::info!("sa = {}, ra = {}, action = {}", server_actions, shared.receiver_actions.get(), action); + if shared.selected_action.replace(action) != action { + for (_, offer) in &self.data.offers { + offer.send_action(action); + offer.client.flush(); + } + self.send_action(action); + self.data.client.flush(); + } + } + + pub fn for_each_data_offer(&self, mut f: C) { + for (_, offer) in &self.data.offers { + f(&offer); + } + } + + pub fn can_drop(&self) -> bool { + let shared = self.data.shared.get(); + shared.selected_action.get() != 0 && shared.state.get().contains(OFFER_STATE_ACCEPTED) + } + + pub fn on_drop(&self) { + self.send_dnd_drop_performed(); + let shared = self.data.shared.get(); + shared.state.or_assign(OFFER_STATE_DROPPED); + } + pub fn send_cancelled(&self) { self.data.client.event(Cancelled { self_id: self.id }) } @@ -40,6 +98,25 @@ impl WlDataSource { }) } + pub fn send_target(&self, mime_type: Option<&str>) { + self.data.client.event(Target { + self_id: self.id, + mime_type, + }) + } + + pub fn send_dnd_finished(&self) { + self.data.client.event(DndFinished { self_id: self.id }) + } + + pub fn send_action(&self, dnd_action: u32) { + self.data.client.event(Action { self_id: self.id, dnd_action, }) + } + + pub fn send_dnd_drop_performed(&self) { + self.data.client.event(DndDropPerformed { self_id: self.id }) + } + fn offer(&self, parser: MsgParser<'_, '_>) -> Result<(), OfferError> { let req: Offer = self.data.client.parse(self, parser)?; add_mime_type::(self, req.mime_type); @@ -48,13 +125,20 @@ impl WlDataSource { fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { let _req: Destroy = self.data.client.parse(self, parser)?; - disconnect_source::(self); + destroy_source::(self); self.data.client.remove_obj(self)?; Ok(()) } fn set_actions(&self, parser: MsgParser<'_, '_>) -> Result<(), SetActionsError> { - let _req: SetActions = self.data.client.parse(self, parser)?; + let req: SetActions = self.data.client.parse(self, parser)?; + if self.data.actions.get().is_some() { + return Err(SetActionsError::AlreadySet); + } + if req.dnd_actions & !DND_ALL != 0 { + return Err(SetActionsError::InvalidActions); + } + self.data.actions.set(Some(req.dnd_actions)); Ok(()) } } @@ -73,7 +157,7 @@ impl Object for WlDataSource { } fn break_loops(&self) { - disconnect_source::(self); + break_source_loops::(self); } } @@ -118,6 +202,10 @@ pub enum SetActionsError { ParseFailed(#[source] Box), #[error(transparent)] ClientError(Box), + #[error("The set of actions is invalid")] + InvalidActions, + #[error("The actions have already been set")] + AlreadySet, } efrom!(SetActionsError, ParseFailed, MsgParserError); efrom!(SetActionsError, ClientError); diff --git a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs index da51b89c..073fb623 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs @@ -1,5 +1,7 @@ use crate::client::{Client, ClientError}; use crate::globals::{Global, GlobalName}; +use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; +use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -7,8 +9,6 @@ use crate::wire::zwp_primary_selection_device_manager_v1::*; use crate::wire::ZwpPrimarySelectionDeviceManagerV1Id; use std::rc::Rc; use thiserror::Error; -use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; -use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1; pub struct ZwpPrimarySelectionDeviceManagerV1Global { name: GlobalName, diff --git a/src/ifs/ipc/zwp_primary_selection_device_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_v1.rs index c7fe1b99..f3ad4061 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_v1.rs @@ -1,4 +1,10 @@ use crate::client::{Client, ClientError, ClientId}; +use crate::ifs::ipc::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1; +use crate::ifs::ipc::zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1; +use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1; +use crate::ifs::ipc::{ + break_device_loops, destroy_device, DeviceData, OfferData, Role, SourceData, Vtable, +}; use crate::ifs::wl_seat::{WlSeat, WlSeatError, WlSeatGlobal}; use crate::object::{Object, ObjectId}; use crate::utils::buffd::{MsgParser, MsgParserError}; @@ -7,15 +13,12 @@ use crate::wire::{ZwpPrimarySelectionDeviceV1Id, ZwpPrimarySelectionOfferV1Id}; use std::rc::Rc; use thiserror::Error; use uapi::OwnedFd; -use crate::ifs::ipc::{OfferData, SourceData, Vtable}; -use crate::ifs::ipc::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1; -use crate::ifs::ipc::zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1; -use crate::ifs::ipc::zwp_primary_selection_source_v1::{ZwpPrimarySelectionSourceV1}; pub struct ZwpPrimarySelectionDeviceV1 { pub id: ZwpPrimarySelectionDeviceV1Id, pub manager: Rc, seat: Rc, + data: DeviceData, } impl ZwpPrimarySelectionDeviceV1 { @@ -28,6 +31,7 @@ impl ZwpPrimarySelectionDeviceV1 { id, manager: manager.clone(), seat: seat.clone(), + data: DeviceData::default(), } } @@ -58,6 +62,7 @@ impl ZwpPrimarySelectionDeviceV1 { fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { let _req: Destroy = self.manager.client.parse(self, parser)?; + destroy_device::(self); self.seat.remove_primary_selection_device(self); self.manager.client.remove_obj(self)?; Ok(()) @@ -75,6 +80,10 @@ impl Vtable for ZwpPrimarySelectionDeviceV1 { dd.id } + fn get_device_data(dd: &Self::Device) -> &DeviceData { + &dd.data + } + fn get_offer_data(offer: &Self::Offer) -> &OfferData { &offer.offer_data } @@ -83,11 +92,19 @@ impl Vtable for ZwpPrimarySelectionDeviceV1 { &src.data } - fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) where C: FnMut(&Rc) { + fn for_each_device(seat: &WlSeatGlobal, client: ClientId, f: C) + where + C: FnMut(&Rc), + { seat.for_each_primary_selection_device(0, client, f) } - fn create_offer(client: &Rc, offer_data: OfferData, id: ObjectId) -> Self::Offer { + fn create_offer( + client: &Rc, + _device: &Rc, + offer_data: OfferData, + id: ObjectId, + ) -> Self::Offer { ZwpPrimarySelectionOfferV1 { id: id.into(), client: client.clone(), @@ -115,7 +132,7 @@ impl Vtable for ZwpPrimarySelectionDeviceV1 { offer.send_offer(mime_type); } - fn unset(seat: &Rc) { + fn unset(seat: &Rc, _role: Role) { seat.unset_primary_selection(); } @@ -137,6 +154,7 @@ impl Object for ZwpPrimarySelectionDeviceV1 { } fn break_loops(&self) { + break_device_loops::(self); self.seat.remove_primary_selection_device(self); } } diff --git a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs index 5fa98f7a..27aac843 100644 --- a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs @@ -1,12 +1,12 @@ use crate::client::{Client, ClientError}; +use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; +use crate::ifs::ipc::{break_offer_loops, destroy_offer, receive, OfferData}; use crate::object::Object; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::wire::zwp_primary_selection_offer_v1::*; use crate::wire::ZwpPrimarySelectionOfferV1Id; use std::rc::Rc; use thiserror::Error; -use crate::ifs::ipc::{disconnect_offer, OfferData, receive}; -use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; pub struct ZwpPrimarySelectionOfferV1 { pub id: ZwpPrimarySelectionOfferV1Id, @@ -30,7 +30,7 @@ impl ZwpPrimarySelectionOfferV1 { fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { let _req: Destroy = self.client.parse(self, parser)?; - disconnect_offer::(self); + destroy_offer::(self); self.client.remove_obj(self)?; Ok(()) } @@ -49,7 +49,7 @@ impl Object for ZwpPrimarySelectionOfferV1 { } fn break_loops(&self) { - disconnect_offer::(self); + break_offer_loops::(self); } } diff --git a/src/ifs/ipc/zwp_primary_selection_source_v1.rs b/src/ifs/ipc/zwp_primary_selection_source_v1.rs index 0795e223..def96cd5 100644 --- a/src/ifs/ipc/zwp_primary_selection_source_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_source_v1.rs @@ -1,4 +1,6 @@ use crate::client::{Client, ClientError}; +use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; +use crate::ifs::ipc::{add_mime_type, break_source_loops, destroy_source, SourceData}; use crate::object::Object; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::wire::zwp_primary_selection_source_v1::*; @@ -6,8 +8,6 @@ use crate::wire::ZwpPrimarySelectionSourceV1Id; use std::rc::Rc; use thiserror::Error; use uapi::OwnedFd; -use crate::ifs::ipc::{add_mime_type, disconnect_source, SourceData}; -use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; pub struct ZwpPrimarySelectionSourceV1 { pub id: ZwpPrimarySelectionSourceV1Id, @@ -42,7 +42,7 @@ impl ZwpPrimarySelectionSourceV1 { fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { let _req: Destroy = self.data.client.parse(self, parser)?; - disconnect_source::(self); + destroy_source::(self); self.data.client.remove_obj(self)?; Ok(()) } @@ -61,7 +61,7 @@ impl Object for ZwpPrimarySelectionSourceV1 { } fn break_loops(&self) { - disconnect_source::(self); + break_source_loops::(self); } } diff --git a/src/ifs/mod.rs b/src/ifs/mod.rs index 448892eb..fd20959b 100644 --- a/src/ifs/mod.rs +++ b/src/ifs/mod.rs @@ -1,3 +1,4 @@ +pub mod ipc; pub mod org_kde_kwin_server_decoration; pub mod org_kde_kwin_server_decoration_manager; pub mod wl_buffer; @@ -19,4 +20,3 @@ pub mod zwp_linux_buffer_params_v1; pub mod zwp_linux_dmabuf_v1; pub mod zxdg_decoration_manager_v1; pub mod zxdg_toplevel_decoration_v1; -pub mod ipc; diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 4a511971..946ccd19 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -1,17 +1,25 @@ -mod handling; +mod event_handling; pub mod wl_keyboard; pub mod wl_pointer; pub mod wl_touch; +mod pointer_owner; use crate::backend::{Seat, SeatId}; use crate::client::{Client, ClientError, ClientId}; use crate::cursor::{Cursor, KnownCursor}; use crate::fixed::Fixed; use crate::globals::{Global, GlobalName}; +use crate::ifs::ipc; +use crate::ifs::ipc::wl_data_device::WlDataDevice; +use crate::ifs::ipc::wl_data_source::WlDataSource; +use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; +use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1; +use crate::ifs::ipc::IpcError; use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardError, REPEAT_INFO_SINCE}; use crate::ifs::wl_seat::wl_pointer::WlPointer; use crate::ifs::wl_seat::wl_touch::WlTouch; use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; +use crate::ifs::wl_surface::WlSurface; use crate::object::{Object, ObjectId}; use crate::tree::{FloatNode, FoundNode, Node}; use crate::utils::asyncevent::AsyncEvent; @@ -20,29 +28,22 @@ use crate::utils::buffd::MsgParserError; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::linkedlist::LinkedList; -use crate::utils::smallmap::SmallMap; use crate::wire::wl_seat::*; use crate::wire::{ - WlDataDeviceId, WlKeyboardId, WlPointerId, WlSeatId, - ZwpPrimarySelectionDeviceV1Id, + WlDataDeviceId, WlKeyboardId, WlPointerId, WlSeatId, ZwpPrimarySelectionDeviceV1Id, }; use crate::xkbcommon::{XkbContext, XkbState}; use crate::{NumCell, State}; use ahash::{AHashMap, AHashSet}; use bstr::ByteSlice; -pub use handling::NodeSeatState; +pub use event_handling::NodeSeatState; use std::cell::{Cell, RefCell}; use std::collections::hash_map::Entry; use std::io::Write; use std::rc::Rc; use thiserror::Error; use uapi::{c, OwnedFd}; -use crate::ifs::ipc; -use crate::ifs::ipc::IpcError; -use crate::ifs::ipc::wl_data_device::WlDataDevice; -use crate::ifs::ipc::wl_data_source::WlDataSource; -use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; -use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1; +use crate::ifs::wl_seat::pointer_owner::{PointerOwnerHolder}; const POINTER: u32 = 1; const KEYBOARD: u32 = 2; @@ -56,19 +57,22 @@ pub const BTN_LEFT: u32 = 0x110; pub const SEAT_NAME_SINCE: u32 = 2; -struct PointerGrab { - seat: Rc, +#[derive(Clone)] +pub struct Dnd { + pub seat: Rc, + client: Rc, + src: Option>, } -struct PointerGrabber { - node: Rc, - buttons: SmallMap, +pub struct DroppedDnd { + dnd: Dnd, } -impl Drop for PointerGrab { +impl Drop for DroppedDnd { fn drop(&mut self) { - *self.seat.grabber.borrow_mut() = None; - self.seat.tree_changed.trigger(); + if let Some(src) = self.dnd.src.take() { + ipc::detach_seat::(&src); + } } } @@ -99,10 +103,11 @@ pub struct WlSeatGlobal { layout_size: u32, cursor: CloneCell>>, serial: NumCell, - grabber: RefCell>, tree_changed: Rc, selection: CloneCell>>, primary_selection: CloneCell>>, + pointer_owner: PointerOwnerHolder, + dropped_dnd: RefCell>, } impl WlSeatGlobal { @@ -151,10 +156,11 @@ impl WlSeatGlobal { layout_size, cursor: Default::default(), serial: Default::default(), - grabber: RefCell::new(None), tree_changed: tree_changed.clone(), selection: Default::default(), primary_selection: Default::default(), + pointer_owner: Default::default(), + dropped_dnd: RefCell::new(None) } } @@ -164,14 +170,16 @@ impl WlSeatGlobal { src: Option>, ) -> Result<(), WlSeatError> { if let Some(new) = &src { - ipc::attach_source::(new, self, ipc::Role::Selection)?; + ipc::attach_seat::(new, self, ipc::Role::Selection)?; } if let Some(old) = field.set(src.clone()) { - ipc::detach_source::(&old); + ipc::detach_seat::(&old); } if let Some(client) = self.keyboard_node.get().client() { match src { - Some(src) => ipc::offer_source_to::(&src, &client), + Some(src) => { + ipc::offer_source_to::(&src, &client) + }, _ => T::for_each_device(self, client.id, |device| { T::send_selection(device, ObjectId::NONE.into()); }), @@ -181,6 +189,18 @@ impl WlSeatGlobal { Ok(()) } + pub fn start_drag( + self: &Rc, + origin: &Rc, + source: Option>, + ) -> Result<(), WlSeatError> { + self.pointer_owner.start_drag(self, origin, source) + } + + pub fn cancel_dnd(self: &Rc) { + self.pointer_owner.cancel_dnd(self); + } + pub fn unset_selection(self: &Rc) { let _ = self.set_selection(None); } diff --git a/src/ifs/wl_seat/handling.rs b/src/ifs/wl_seat/event_handling.rs similarity index 68% rename from src/ifs/wl_seat/handling.rs rename to src/ifs/wl_seat/event_handling.rs index 6997fb01..3cfe8d41 100644 --- a/src/ifs/wl_seat/handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -1,61 +1,68 @@ use crate::backend::{KeyState, OutputId, ScrollAxis, SeatEvent, SeatId}; use crate::client::{Client, ClientId}; use crate::fixed::Fixed; +use crate::ifs::ipc; +use crate::ifs::ipc::wl_data_device::WlDataDevice; +use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; use crate::ifs::wl_seat::wl_keyboard::WlKeyboard; use crate::ifs::wl_seat::wl_pointer::{WlPointer, POINTER_FRAME_SINCE_VERSION}; -use crate::ifs::wl_seat::{ - wl_keyboard, wl_pointer, PointerGrab, PointerGrabber, WlSeat, WlSeatGlobal, -}; +use crate::ifs::wl_seat::{wl_keyboard, wl_pointer, WlSeat, WlSeatGlobal, Dnd}; use crate::ifs::wl_surface::xdg_surface::xdg_popup::XdgPopup; use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; use crate::ifs::wl_surface::xdg_surface::XdgSurface; use crate::ifs::wl_surface::WlSurface; -use crate::tree::{FloatNode, FoundNode, Node}; -use crate::utils::smallmap::SmallMap; -use crate::xkbcommon::{ModifierState, XKB_KEY_DOWN, XKB_KEY_UP}; -use std::ops::{Deref, DerefMut}; -use std::rc::Rc; -use crate::ifs::ipc; -use crate::ifs::ipc::wl_data_device::WlDataDevice; -use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; use crate::object::ObjectId; +use crate::tree::{FloatNode, Node}; use crate::utils::clonecell::CloneCell; +use crate::utils::smallmap::SmallMap; +use crate::wire::WlDataOfferId; +use crate::xkbcommon::{ModifierState, XKB_KEY_DOWN, XKB_KEY_UP}; +use std::ops::{Deref}; +use std::rc::Rc; #[derive(Default)] pub struct NodeSeatState { pointer_foci: SmallMap, 1>, kb_foci: SmallMap, 1>, - grabs: SmallMap, + grabs: SmallMap, 1>, + dnd_targets: SmallMap, 1>, } impl NodeSeatState { - fn enter(&self, seat: &Rc) { + pub(super) fn enter(&self, seat: &Rc) { self.pointer_foci.insert(seat.seat.id(), seat.clone()); } - fn leave(&self, seat: &WlSeatGlobal) { + pub(super) fn leave(&self, seat: &WlSeatGlobal) { self.pointer_foci.remove(&seat.seat.id()); } - fn focus(&self, seat: &Rc) -> bool { + pub(super) fn focus(&self, seat: &Rc) -> bool { self.kb_foci.insert(seat.seat.id(), seat.clone()); self.kb_foci.len() == 1 } - fn unfocus(&self, seat: &WlSeatGlobal) -> bool { + pub(super) fn unfocus(&self, seat: &WlSeatGlobal) -> bool { self.kb_foci.remove(&seat.seat.id()); self.kb_foci.len() == 0 } - fn add_pointer_grab(&self, seat: &Rc) { - self.grabs - .insert(seat.id(), PointerGrab { seat: seat.clone() }); + pub(super) fn add_pointer_grab(&self, seat: &Rc) { + self.grabs.insert(seat.id(), seat.clone()); } - fn remove_pointer_grab(&self, seat: &WlSeatGlobal) { + pub(super) fn remove_pointer_grab(&self, seat: &WlSeatGlobal) { self.grabs.remove(&seat.id()); } + pub(super) fn add_dnd_target(&self, seat: &Rc) { + self.dnd_targets.insert(seat.id(), seat.clone()); + } + + pub(super) fn remove_dnd_target(&self, seat: &WlSeatGlobal) { + self.dnd_targets.remove(&seat.id()); + } + // pub fn remove_pointer_grabs(&self) { // self.grabs.clear(); // } @@ -65,8 +72,13 @@ impl NodeSeatState { } pub fn destroy_node(&self, node: &dyn Node) { - self.grabs.clear(); + while let Some((_, seat)) = self.grabs.pop() { + seat.pointer_owner.revert_to_default(&seat); + } let node_id = node.id(); + while let Some((_, seat)) = self.dnd_targets.pop() { + seat.pointer_owner.dnd_target_removed(&seat); + } while let Some((_, seat)) = self.pointer_foci.pop() { let mut ps = seat.pointer_stack.borrow_mut(); while let Some(last) = ps.pop() { @@ -90,8 +102,8 @@ impl WlSeatGlobal { match event { SeatEvent::OutputPosition(o, x, y) => self.output_position_event(o, x, y), SeatEvent::Motion(dx, dy) => self.motion_event(dx, dy), - SeatEvent::Button(b, s) => self.button_event(b, s), - SeatEvent::Scroll(d, a) => self.scroll_event(d, a), + SeatEvent::Button(b, s) => self.pointer_owner.button(self, b, s), + SeatEvent::Scroll(d, a) => self.pointer_owner.scroll(self, d, a), SeatEvent::Key(k, s) => self.key_event(k, s), } } @@ -111,52 +123,6 @@ impl WlSeatGlobal { self.set_new_position(x + dx, y + dy); } - fn button_event(self: &Rc, button: u32, state: KeyState) { - let mut release_grab = false; - let mut grabber = self.grabber.borrow_mut(); - let node = if let Some(pg) = grabber.deref_mut() { - if state == KeyState::Released { - pg.buttons.remove(&button); - if pg.buttons.is_empty() { - release_grab = true; - } - } else { - pg.buttons.insert(button, ()); - } - pg.node.clone() - } else if state == KeyState::Pressed { - match self.pointer_node() { - Some(n) => { - *grabber = Some(PointerGrabber { - node: n.clone(), - buttons: SmallMap::new_with(button, ()), - }); - n.seat_state().add_pointer_grab(self); - n - } - _ => return, - } - } else { - return; - }; - drop(grabber); - if release_grab { - node.seat_state().remove_pointer_grab(self); - } - node.button(self, button, state); - } - - fn scroll_event(&self, delta: i32, axis: ScrollAxis) { - let node = match self.grabber.borrow_mut().as_ref().map(|g| g.node.clone()) { - Some(n) => n, - _ => match self.pointer_node() { - Some(n) => n, - _ => return, - }, - }; - node.scroll(self, delta, axis); - } - fn key_event(&self, key: u32, state: KeyState) { let (state, xkb_dir) = { let mut pk = self.pressed_keys.borrow_mut(); @@ -182,7 +148,7 @@ impl WlSeatGlobal { } impl WlSeatGlobal { - fn pointer_node(&self) -> Option> { + pub(super) fn pointer_node(&self) -> Option> { self.pointer_stack.borrow().last().cloned() } @@ -246,13 +212,22 @@ impl WlSeatGlobal { if old.client_id() != Some(surface.client.id) { self.offer_selection::(&self.selection, &surface.client); - self.offer_selection::(&self.primary_selection, &surface.client); + self.offer_selection::( + &self.primary_selection, + &surface.client, + ); } } - fn offer_selection(&self, field: &CloneCell>>, client: &Rc) { + fn offer_selection( + &self, + field: &CloneCell>>, + client: &Rc, + ) { match field.get() { - Some(sel) => ipc::offer_source_to::(&sel, &client), + Some(sel) => { + ipc::offer_source_to::(&sel, &client) + }, None => T::for_each_device(self, client.id, |dd| { T::send_selection(dd, ObjectId::NONE.into()); }), @@ -367,86 +342,7 @@ impl WlSeatGlobal { cursor.set_position(x.round_down(), y.round_down()); } } - 'handle_grab: { - let grab_node = { - let grabber = self.grabber.borrow_mut(); - match grabber.as_ref() { - Some(n) => n.node.clone(), - None => break 'handle_grab, - } - }; - if pos_changed { - let pos = grab_node.absolute_position(); - let (x_int, y_int) = pos.translate(x.round_down(), y.round_down()); - grab_node.motion(self, x.apply_fract(x_int), y.apply_fract(y_int)); - } - return; - } - let mut found_tree = self.found_tree.borrow_mut(); - let mut stack = self.pointer_stack.borrow_mut(); - // if self.move_.get() { - // for node in stack.iter().rev() { - // if let NodeKind::Toplevel(tn) = node.clone().into_kind() { - // let (move_start_x, move_start_y) = self.move_start_pos.get(); - // let (move_start_ex, move_start_ey) = self.extents_start_pos.get(); - // let mut ex = tn.common.extents.get(); - // ex.x = (x - move_start_x).round_down() + move_start_ex; - // ex.y = (y - move_start_y).round_down() + move_start_ey; - // tn.common.extents.set(ex); - // } - // } - // return; - // } - let x_int = x.round_down(); - let y_int = y.round_down(); - found_tree.push(FoundNode { - node: self.state.root.clone(), - x: x_int, - y: y_int, - }); - self.state.root.find_tree_at(x_int, y_int, &mut found_tree); - let mut divergence = found_tree.len().min(stack.len()); - for (i, (found, stack)) in found_tree.iter().zip(stack.iter()).enumerate() { - if found.node.id() != stack.id() { - divergence = i; - break; - } - } - if (stack.len(), found_tree.len()) == (divergence, divergence) { - if pos_changed { - if let Some(node) = found_tree.last() { - node.node - .motion(self, x.apply_fract(node.x), y.apply_fract(node.y)); - } - } - } else { - if let Some(last) = stack.last() { - last.pointer_untarget(self); - } - for old in stack.drain(divergence..).rev() { - old.leave(self); - old.seat_state().leave(self); - } - if found_tree.len() == divergence { - if let Some(node) = found_tree.last() { - node.node - .clone() - .motion(self, x.apply_fract(node.x), y.apply_fract(node.y)); - } - } else { - for new in found_tree.drain(divergence..) { - new.node.seat_state().enter(self); - new.node - .clone() - .enter(self, x.apply_fract(new.x), y.apply_fract(new.y)); - stack.push(new.node); - } - } - if let Some(node) = stack.last() { - node.pointer_target(self); - } - } - found_tree.clear(); + self.pointer_owner.handle_pointer_position(self); } } @@ -544,3 +440,54 @@ impl WlSeatGlobal { } } } + +// Dnd callbacks +impl WlSeatGlobal { + pub fn dnd_surface_leave(&self, surface: &WlSurface, dnd: &Dnd) { + if dnd.src.is_some() || surface.client.id == dnd.client.id { + self.for_each_data_device(0, surface.client.id, |dd| { + dd.send_leave(); + }) + } + if let Some(src) = &dnd.src { + src.on_leave(); + } + surface.client.flush(); + } + + pub fn dnd_surface_drop(&self, surface: &WlSurface, dnd: &Dnd) { + if dnd.src.is_some() || surface.client.id == dnd.client.id { + self.for_each_data_device(0, surface.client.id, |dd| { + dd.send_drop(); + }) + } + if let Some(src) = &dnd.src { + src.on_drop(); + } + surface.client.flush(); + } + + pub fn dnd_surface_enter(&self, surface: &WlSurface, dnd: &Dnd, x: Fixed, y: Fixed) { + if let Some(src) = &dnd.src { + ipc::offer_source_to::(src, &surface.client); + src.for_each_data_offer(|offer| { + offer.device.send_enter(surface.id, x, y, offer.id); + offer.send_source_actions(); + }) + } else if surface.client.id == dnd.client.id { + self.for_each_data_device(0, dnd.client.id, |dd| { + dd.send_enter(surface.id, x, y, WlDataOfferId::NONE); + }) + } + surface.client.flush(); + } + + pub fn dnd_surface_motion(&self, surface: &WlSurface, dnd: &Dnd, x: Fixed, y: Fixed) { + if dnd.src.is_some() || surface.client.id == dnd.client.id { + self.for_each_data_device(0, surface.client.id, |dd| { + dd.send_motion(x, y); + }) + } + surface.client.flush(); + } +} diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs new file mode 100644 index 00000000..31539995 --- /dev/null +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -0,0 +1,353 @@ +use std::cell::Cell; +use std::rc::Rc; +use crate::backend::{KeyState, ScrollAxis}; +use crate::fixed::Fixed; +use crate::ifs::ipc; +use crate::ifs::ipc::wl_data_device::WlDataDevice; +use crate::ifs::ipc::wl_data_source::WlDataSource; +use crate::ifs::wl_seat::{Dnd, DroppedDnd, WlSeatError, WlSeatGlobal}; +use crate::ifs::wl_surface::WlSurface; +use crate::tree::{FoundNode, Node}; +use crate::utils::clonecell::CloneCell; +use crate::utils::smallmap::SmallMap; + +pub struct PointerOwnerHolder { + default: Rc, + owner: CloneCell>, +} + +impl Default for PointerOwnerHolder { + fn default() -> Self { + Self { + default: Rc::new(DefaultPointerOwner), + owner: CloneCell::new(Rc::new(DefaultPointerOwner)), + } + } +} + +impl PointerOwnerHolder { + pub fn button(&self, seat: &Rc, button: u32, state: KeyState) { + self.owner.get().button(seat, button, state) + } + + pub fn scroll(&self, seat: &Rc, delta: i32, axis: ScrollAxis) { + self.owner.get().scroll(seat, delta, axis) + } + + pub fn handle_pointer_position(&self, seat: &Rc) { + self.owner.get().handle_pointer_position(seat) + } + + pub fn start_drag(&self, seat: &Rc, origin: &Rc, source: Option>) -> Result<(), WlSeatError> { + self.owner.get().start_drag(seat, origin, source) + } + + pub fn cancel_dnd(&self, seat: &Rc) { + self.owner.get().cancel_dnd(seat) + } + + pub fn revert_to_default(&self, seat: &Rc) { + self.owner.get().revert_to_default(seat) + } + + pub fn dnd_target_removed(&self, seat: &Rc) { + self.owner.get().dnd_target_removed(seat); + } +} + +trait PointerOwner { + fn button(&self, seat: &Rc, button: u32, state: KeyState); + fn scroll(&self, seat: &Rc, delta: i32, axis: ScrollAxis); + fn handle_pointer_position(&self, seat: &Rc); + fn start_drag(&self, seat: &Rc, origin: &Rc, source: Option>) -> Result<(), WlSeatError>; + fn cancel_dnd(&self, seat: &Rc); + fn revert_to_default(&self, seat: &Rc); + fn dnd_target_removed(&self, seat: &Rc); +} + +struct DefaultPointerOwner; + +struct GrabPointerOwner { + buttons: SmallMap, + node: Rc, +} + +struct DndPointerOwner { + button: u32, + dnd: Dnd, + target: CloneCell>, + pos_x: Cell, + pos_y: Cell, +} + +impl PointerOwner for DefaultPointerOwner { + fn button(&self, seat: &Rc, button: u32, state: KeyState) { + if state != KeyState::Pressed { + return; + } + let pn = match seat.pointer_node() { + Some(n) => n, + _ => return, + }; + seat.pointer_owner.owner.set(Rc::new(GrabPointerOwner { + buttons: SmallMap::new_with(button, ()), + node: pn.clone(), + })); + pn.seat_state().add_pointer_grab(seat); + pn.button(seat, button, state); + } + + fn scroll(&self, seat: &Rc, delta: i32, axis: ScrollAxis) { + if let Some(pn) = seat.pointer_node() { + pn.scroll(seat, delta, axis); + } + } + + fn handle_pointer_position(&self, seat: &Rc) { + let (x, y) = seat.pos.get(); + let mut found_tree = seat.found_tree.borrow_mut(); + let mut stack = seat.pointer_stack.borrow_mut(); + let x_int = x.round_down(); + let y_int = y.round_down(); + found_tree.push(FoundNode { + node: seat.state.root.clone(), + x: x_int, + y: y_int, + }); + seat.state.root.find_tree_at(x_int, y_int, &mut found_tree); + let mut divergence = found_tree.len().min(stack.len()); + for (i, (found, stack)) in found_tree.iter().zip(stack.iter()).enumerate() { + if found.node.id() != stack.id() { + divergence = i; + break; + } + } + if (stack.len(), found_tree.len()) == (divergence, divergence) { + if let Some(node) = found_tree.last() { + node.node.motion(seat, x.apply_fract(node.x), y.apply_fract(node.y)); + } + } else { + if let Some(last) = stack.last() { + last.pointer_untarget(seat); + } + for old in stack.drain(divergence..).rev() { + old.leave(seat); + old.seat_state().leave(seat); + } + if found_tree.len() == divergence { + if let Some(node) = found_tree.last() { + node.node + .clone() + .motion(seat, x.apply_fract(node.x), y.apply_fract(node.y)); + } + } else { + for new in found_tree.drain(divergence..) { + new.node.seat_state().enter(seat); + new.node + .clone() + .enter(seat, x.apply_fract(new.x), y.apply_fract(new.y)); + stack.push(new.node); + } + } + if let Some(node) = stack.last() { + node.pointer_target(seat); + } + } + found_tree.clear(); + } + + fn start_drag(&self, _seat: &Rc, _origin: &Rc, _source: Option>) -> Result<(), WlSeatError> { + Ok(()) + } + + fn cancel_dnd(&self, seat: &Rc) { + seat.dropped_dnd.borrow_mut().take(); + } + + fn revert_to_default(&self, _seat: &Rc) { + // nothing + } + + fn dnd_target_removed(&self, seat: &Rc) { + self.cancel_dnd(seat); + } +} + +impl PointerOwner for GrabPointerOwner { + fn button(&self, seat: &Rc, button: u32, state: KeyState) { + match state { + KeyState::Released => { + self.buttons.remove(&button); + if self.buttons.is_empty() { + self.node.seat_state().remove_pointer_grab(seat); + seat.tree_changed.trigger(); + seat.pointer_owner.owner.set(seat.pointer_owner.default.clone()); + } + }, + KeyState::Pressed => { + self.buttons.insert(button, ()); + }, + } + self.node.clone().button(seat, button, state); + } + + fn scroll(&self, seat: &Rc, delta: i32, axis: ScrollAxis) { + self.node.scroll(seat, delta, axis); + } + + fn handle_pointer_position(&self, seat: &Rc) { + let (x, y) = seat.pos.get(); + let pos = self.node.absolute_position(); + let (x_int, y_int) = pos.translate(x.round_down(), y.round_down()); + self.node.motion(seat, x.apply_fract(x_int), y.apply_fract(y_int)); + } + + fn start_drag(&self, seat: &Rc, origin: &Rc, src: Option>) -> Result<(), WlSeatError> { + let button = match self.buttons.iter().next() { + Some((b, _)) => b, + None => return Ok(()), + }; + if self.buttons.len() != 1 { + return Ok(()); + } + if self.node.id() != origin.node_id { + return Ok(()); + } + if let Some(new) = &src { + ipc::attach_seat::(&new, seat, ipc::Role::Dnd)?; + } + *seat.dropped_dnd.borrow_mut() = None; + let pointer_owner = Rc::new(DndPointerOwner { + button, + dnd: Dnd { + seat: seat.clone(), + client: origin.client.clone(), + src, + }, + target: CloneCell::new(seat.state.root.clone()), + pos_x: Cell::new(Fixed::from_int(0)), + pos_y: Cell::new(Fixed::from_int(0)), + }); + { + let mut stack = seat.pointer_stack.borrow_mut(); + for node in stack.drain(1..).rev() { + node.leave(seat); + node.seat_state().leave(seat); + } + } + self.node.seat_state().remove_pointer_grab(seat); + // { + // let old = seat.keyboard_node.set(seat.state.root.clone()); + // old.seat_state().unfocus(seat); + // old.unfocus(seat); + // } + seat.pointer_owner.owner.set(pointer_owner.clone()); + pointer_owner.handle_pointer_position(seat); + Ok(()) + } + + fn cancel_dnd(&self, seat: &Rc) { + seat.dropped_dnd.borrow_mut().take(); + } + + fn revert_to_default(&self, seat: &Rc) { + self.node.seat_state().remove_pointer_grab(seat); + seat.pointer_owner.owner.set(seat.pointer_owner.default.clone()); + } + + fn dnd_target_removed(&self, seat: &Rc) { + self.cancel_dnd(seat) + } +} + +impl PointerOwner for DndPointerOwner { + fn button(&self, seat: &Rc, button: u32, state: KeyState) { + if button != self.button || state != KeyState::Released { + return; + } + let should_drop = match &self.dnd.src { + None => true, + Some(s) => s.can_drop(), + }; + let target = self.target.get(); + if should_drop { + self.target.get().dnd_drop(&self.dnd); + *seat.dropped_dnd.borrow_mut() = Some(DroppedDnd { + dnd: self.dnd.clone(), + }); + } + target.dnd_leave(&self.dnd); + target.seat_state().remove_dnd_target(seat); + if !should_drop { + if let Some(src) = &self.dnd.src { + ipc::detach_seat::(src); + } + } + seat.pointer_owner.owner.set(seat.pointer_owner.default.clone()); + seat.tree_changed.trigger(); + } + + fn scroll(&self, _seat: &Rc, _delta: i32, _axis: ScrollAxis) { + // nothing + } + + fn handle_pointer_position(&self, seat: &Rc) { + let (x, y) = seat.pos.get(); + let (x_int, y_int) = (x.round_down(), y.round_down()); + let (node, x_int, y_int) = { + let mut found_tree = seat.found_tree.borrow_mut(); + found_tree.push(FoundNode { + node: seat.state.root.clone(), + x: x_int, + y: y_int, + }); + seat.state.root.find_tree_at(x_int, y_int, &mut found_tree); + let FoundNode { + node, + x, + y, + } = found_tree.pop().unwrap(); + found_tree.clear(); + (node, x, y) + }; + let (x, y) = (x.apply_fract(x_int), y.apply_fract(y_int)); + let mut target = self.target.get(); + if node.id() != target.id() { + target.dnd_leave(&self.dnd); + target.seat_state().remove_dnd_target(seat); + target = node; + target.dnd_enter(&self.dnd, x, y); + target.seat_state().add_dnd_target(seat); + self.target.set(target); + } else if (self.pos_x.get(), self.pos_y.get()) != (x, y) { + node.dnd_motion(&self.dnd, x, y); + } + self.pos_x.set(x); + self.pos_y.set(y); + } + + fn start_drag(&self, _seat: &Rc, _origin: &Rc, _source: Option>) -> Result<(), WlSeatError> { + Ok(()) + } + + fn cancel_dnd(&self, seat: &Rc) { + let target = self.target.get(); + target.dnd_leave(&self.dnd); + target.seat_state().remove_dnd_target(seat); + if let Some(src) = &self.dnd.src { + ipc::detach_seat::(src); + } + seat.pointer_owner.owner.set(seat.pointer_owner.default.clone()); + seat.tree_changed.trigger(); + } + + fn revert_to_default(&self, seat: &Rc) { + self.cancel_dnd(seat) + } + + fn dnd_target_removed(&self, seat: &Rc) { + self.target.get().dnd_leave(&self.dnd); + self.target.set(seat.state.root.clone()); + seat.state.tree_changed(); + } +} diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 2caf6b37..b9449603 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -7,7 +7,7 @@ use crate::client::{Client, ClientError, RequestParser}; use crate::fixed::Fixed; use crate::ifs::wl_buffer::WlBuffer; use crate::ifs::wl_callback::WlCallback; -use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; +use crate::ifs::wl_seat::{Dnd, NodeSeatState, WlSeatGlobal}; use crate::ifs::wl_surface::cursor::CursorSurface; use crate::ifs::wl_surface::wl_subsurface::WlSubsurface; use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceRole}; @@ -633,6 +633,26 @@ impl Node for WlSurface { fn client(&self) -> Option> { Some(self.client.clone()) } + + fn into_surface(self: Rc) -> Option> { + Some(self) + } + + fn dnd_enter(&self, dnd: &Dnd, x: Fixed, y: Fixed) { + dnd.seat.dnd_surface_enter(self, dnd, x, y); + } + + fn dnd_drop(&self, dnd: &Dnd) { + dnd.seat.dnd_surface_drop(self, dnd); + } + + fn dnd_leave(&self, dnd: &Dnd) { + dnd.seat.dnd_surface_leave(self, dnd); + } + + fn dnd_motion(&self, dnd: &Dnd, x: Fixed, y: Fixed) { + dnd.seat.dnd_surface_motion(self, dnd, x, y); + } } #[derive(Debug, Error)] diff --git a/src/main.rs b/src/main.rs index 6b396e26..9cf3ab66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#![feature(c_variadic, thread_local, label_break_value)] +#![feature(c_variadic, thread_local, label_break_value, ptr_metadata)] #![allow( clippy::len_zero, clippy::needless_lifetimes, diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 02325c86..e7ce8296 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -3,7 +3,8 @@ use crate::client::{Client, ClientId}; use crate::cursor::KnownCursor; use crate::fixed::Fixed; use crate::ifs::wl_output::WlOutputGlobal; -use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; +use crate::ifs::wl_seat::{Dnd, NodeSeatState, WlSeatGlobal}; +use crate::ifs::wl_surface::WlSurface; use crate::rect::Rect; use crate::render::Renderer; use crate::utils::clonecell::CloneCell; @@ -176,6 +177,30 @@ pub trait Node { fn client_id(&self) -> Option { self.client().map(|c| c.id) } + + fn into_surface(self: Rc) -> Option> { + None + } + + fn dnd_drop(&self, dnd: &Dnd) { + let _ = dnd; + } + + fn dnd_leave(&self, dnd: &Dnd) { + let _ = dnd; + } + + fn dnd_enter(&self, dnd: &Dnd, x: Fixed, y: Fixed) { + let _ = dnd; + let _ = x; + let _ = y; + } + + fn dnd_motion(&self, dnd: &Dnd, x: Fixed, y: Fixed) { + let _ = dnd; + let _ = x; + let _ = y; + } } pub struct FoundNode { diff --git a/src/utils/buffd/formatter.rs b/src/utils/buffd/formatter.rs index 539d61e8..d98f2003 100644 --- a/src/utils/buffd/formatter.rs +++ b/src/utils/buffd/formatter.rs @@ -36,6 +36,13 @@ impl<'a> MsgFormatter<'a> { self } + pub fn optstr + ?Sized>(&mut self, s: Option<&S>) -> &mut Self { + match s { + Some(s) => self.string(s), + _ => self.uint(0), + } + } + pub fn string + ?Sized>(&mut self, s: &S) -> &mut Self { let s = s.as_ref(); let len = s.len() + 1; diff --git a/src/utils/buffd/parser.rs b/src/utils/buffd/parser.rs index 1d39c275..34927e5a 100644 --- a/src/utils/buffd/parser.rs +++ b/src/utils/buffd/parser.rs @@ -79,6 +79,17 @@ impl<'a, 'b> MsgParser<'a, 'b> { Ok(s[..s.len() - 1].as_bstr()) } + pub fn optstr(&mut self) -> Result, MsgParserError> { + let s = self.array()?; + if s.len() == 0 { + return Ok(None); + } + match s[..s.len() - 1].as_bstr().to_str() { + Ok(s) => Ok(Some(s)), + _ => Err(MsgParserError::NonUtf8), + } + } + pub fn str(&mut self) -> Result<&'b str, MsgParserError> { match self.bstr()?.to_str() { Ok(s) => Ok(s), diff --git a/src/utils/clonecell.rs b/src/utils/clonecell.rs index ad635d66..8071fb7e 100644 --- a/src/utils/clonecell.rs +++ b/src/utils/clonecell.rs @@ -47,3 +47,5 @@ unsafe impl UnsafeCellCloneSafe for Option {} unsafe impl UnsafeCellCloneSafe for Rc {} unsafe impl UnsafeCellCloneSafe for NodeRef {} + +unsafe impl UnsafeCellCloneSafe for () {} diff --git a/src/utils/smallmap.rs b/src/utils/smallmap.rs index 69bc286f..9468e8a9 100644 --- a/src/utils/smallmap.rs +++ b/src/utils/smallmap.rs @@ -77,6 +77,10 @@ impl SmallMap { pub fn pop(&self) -> Option<(K, V)> { unsafe { self.m.get().deref_mut().pop() } } + + pub fn iter<'a>(&'a self) -> SmallMapIter<'a, K, V, N> { + SmallMapIter { pos: 0, map: self } + } } impl SmallMap { diff --git a/wire/wl_data_offer.txt b/wire/wl_data_offer.txt index d67465a3..a9667105 100644 --- a/wire/wl_data_offer.txt +++ b/wire/wl_data_offer.txt @@ -2,7 +2,7 @@ msg accept = 0 { serial: u32, - mime_type: str, + mime_type: optstr, } msg receive = 1 { diff --git a/wire/wl_data_source.txt b/wire/wl_data_source.txt index 8c6465a1..bce10863 100644 --- a/wire/wl_data_source.txt +++ b/wire/wl_data_source.txt @@ -15,7 +15,7 @@ msg set_actions = 2 { # events msg target = 0 { - mime_type: str, + mime_type: optstr, } msg send = 1 {