Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wayland: implement xdg-toplevel-drag #112

Merged
merged 3 commits into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions src/gfx_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,18 +253,29 @@ impl dyn GfxFramebuffer {
if let Some(rect) = cursor_rect {
let seats = state.globals.lock_seats();
for seat in seats.values() {
if let Some(cursor) = seat.get_cursor() {
let (mut x, mut y) = seat.get_position();
if let Some(dnd_icon) = seat.dnd_icon() {
let extents = dnd_icon.extents.get().move_(
x.round_down() + dnd_icon.buf_x.get(),
y.round_down() + dnd_icon.buf_y.get(),
);
if extents.intersects(&rect) {
let (x, y) = rect.translate(extents.x1(), extents.y1());
renderer.render_surface(&dnd_icon, x, y, None);
let (mut x, mut y) = seat.get_position();
if let Some(drag) = seat.toplevel_drag() {
if let Some(tl) = drag.toplevel.get() {
if tl.xdg.surface.buffer.get().is_some() {
let (x, y) = rect.translate(
x.round_down() - drag.x_off.get(),
y.round_down() - drag.y_off.get(),
);
renderer.render_xdg_surface(&tl.xdg, x, y, None)
}
}
}
if let Some(dnd_icon) = seat.dnd_icon() {
let extents = dnd_icon.extents.get().move_(
x.round_down() + dnd_icon.buf_x.get(),
y.round_down() + dnd_icon.buf_y.get(),
);
if extents.intersects(&rect) {
let (x, y) = rect.translate(extents.x1(), extents.y1());
renderer.render_surface(&dnd_icon, x, y, None);
}
}
if let Some(cursor) = seat.get_cursor() {
if render_hardware_cursor || !seat.hardware_cursor() {
cursor.tick();
x -= Fixed::from_int(rect.x1());
Expand Down
2 changes: 2 additions & 0 deletions src/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use {
wp_tearing_control_manager_v1::WpTearingControlManagerV1Global,
wp_viewporter::WpViewporterGlobal,
xdg_activation_v1::XdgActivationV1Global,
xdg_toplevel_drag_manager_v1::XdgToplevelDragManagerV1Global,
xdg_wm_base::XdgWmBaseGlobal,
zwlr_layer_shell_v1::ZwlrLayerShellV1Global,
zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1Global,
Expand Down Expand Up @@ -169,6 +170,7 @@ impl Globals {
add_singleton!(ExtForeignToplevelListV1Global);
add_singleton!(ZwpIdleInhibitManagerV1Global);
add_singleton!(ExtIdleNotifierV1Global);
add_singleton!(XdgToplevelDragManagerV1Global);
}

pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
Expand Down
2 changes: 2 additions & 0 deletions src/ifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub mod wp_viewporter;
pub mod xdg_activation_token_v1;
pub mod xdg_activation_v1;
pub mod xdg_positioner;
pub mod xdg_toplevel_drag_manager_v1;
pub mod xdg_toplevel_drag_v1;
pub mod xdg_wm_base;
pub mod zwlr_layer_shell_v1;
pub mod zwlr_screencopy_frame_v1;
Expand Down
21 changes: 17 additions & 4 deletions src/ifs/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub trait IpcVtable: Sized {
data: OfferData<Self>,
) -> Result<Rc<Self::Offer>, ClientError>;
fn send_selection(dd: &Self::Device, offer: Option<&Rc<Self::Offer>>);
fn send_cancelled(source: &Rc<Self::Source>);
fn send_cancelled(source: &Rc<Self::Source>, seat: &Rc<WlSeatGlobal>);
fn get_offer_id(offer: &Self::Offer) -> u64;
fn send_offer(dd: &Self::Device, offer: &Rc<Self::Offer>);
fn send_mime_type(offer: &Rc<Self::Offer>, mime_type: &str);
Expand Down Expand Up @@ -102,13 +102,16 @@ const OFFER_STATE_DROPPED: u32 = 1 << 2;

const SOURCE_STATE_USED: u32 = 1 << 1;
const SOURCE_STATE_FINISHED: u32 = 1 << 2;
const SOURCE_STATE_DROPPED: u32 = 1 << 3;
const SOURCE_STATE_CANCELLED: u32 = 1 << 4;
const SOURCE_STATE_DROPPED_OR_CANCELLED: u32 = SOURCE_STATE_DROPPED | SOURCE_STATE_CANCELLED;

pub struct SourceData<T: IpcVtable> {
pub seat: CloneCell<Option<Rc<WlSeatGlobal>>>,
offers: SmallMap<u64, Rc<T::Offer>, 1>,
offer_client: Cell<ClientId>,
mime_types: RefCell<AHashSet<String>>,
client: Rc<Client>,
pub client: Rc<Client>,
state: NumCell<u32>,
actions: Cell<Option<u32>>,
role: Cell<Role>,
Expand Down Expand Up @@ -151,6 +154,16 @@ impl<T: IpcVtable> SourceData<T> {
is_xwm,
}
}

pub fn was_used(&self) -> bool {
self.state.get().contains(SOURCE_STATE_USED)
}

pub fn was_dropped_or_cancelled(&self) -> bool {
self.state
.get()
.intersects(SOURCE_STATE_DROPPED_OR_CANCELLED)
}
}

pub fn attach_seat<T: IpcVtable>(
Expand Down Expand Up @@ -188,12 +201,12 @@ pub fn cancel_offers<T: IpcVtable>(src: &T::Source) {
}
}

pub fn detach_seat<T: IpcVtable>(src: &Rc<T::Source>) {
pub fn detach_seat<T: IpcVtable>(src: &Rc<T::Source>, seat: &Rc<WlSeatGlobal>) {
let data = T::get_source_data(src);
data.seat.set(None);
cancel_offers::<T>(src);
if !data.state.get().contains(SOURCE_STATE_FINISHED) {
T::send_cancelled(src);
T::send_cancelled(src, seat);
}
// data.client.flush();
}
Expand Down
4 changes: 2 additions & 2 deletions src/ifs/ipc/wl_data_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ impl IpcVtable for ClipboardIpc {
dd.send_selection(offer);
}

fn send_cancelled(source: &Rc<Self::Source>) {
source.send_cancelled();
fn send_cancelled(source: &Rc<Self::Source>, seat: &Rc<WlSeatGlobal>) {
source.send_cancelled(seat);
}

fn get_offer_id(offer: &Self::Offer) -> u64 {
Expand Down
33 changes: 25 additions & 8 deletions src/ifs/ipc/wl_data_source.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
use {
crate::{
client::{Client, ClientError},
ifs::ipc::{
add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source,
wl_data_device::ClipboardIpc,
wl_data_device_manager::{DND_ALL, DND_NONE},
wl_data_offer::WlDataOffer,
SharedState, SourceData, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED,
ifs::{
ipc::{
add_data_source_mime_type, break_source_loops, cancel_offers, destroy_data_source,
wl_data_device::ClipboardIpc,
wl_data_device_manager::{DND_ALL, DND_NONE},
wl_data_offer::WlDataOffer,
SharedState, SourceData, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED,
SOURCE_STATE_CANCELLED, SOURCE_STATE_DROPPED,
},
wl_seat::WlSeatGlobal,
xdg_toplevel_drag_v1::XdgToplevelDragV1,
},
leaks::Tracker,
object::Object,
utils::{
bitflags::BitflagsExt,
buffd::{MsgParser, MsgParserError},
cell_ext::CellExt,
clonecell::CloneCell,
},
wire::{wl_data_source::*, WlDataSourceId},
xwayland::XWaylandEvent,
Expand All @@ -33,6 +39,7 @@ pub struct WlDataSource {
pub data: SourceData<ClipboardIpc>,
pub version: u32,
pub tracker: Tracker<Self>,
pub toplevel_drag: CloneCell<Option<Rc<XdgToplevelDragV1>>>,
}

impl WlDataSource {
Expand All @@ -42,6 +49,7 @@ impl WlDataSource {
tracker: Default::default(),
data: SourceData::new(client, is_xwm),
version,
toplevel_drag: Default::default(),
}
}

Expand Down Expand Up @@ -100,13 +108,17 @@ impl WlDataSource {
shared.selected_action.get() != 0 && shared.state.get().contains(OFFER_STATE_ACCEPTED)
}

pub fn on_drop(&self) {
pub fn on_drop(&self, seat: &Rc<WlSeatGlobal>) {
self.data.state.or_assign(SOURCE_STATE_DROPPED);
if let Some(drag) = self.toplevel_drag.take() {
drag.finish_drag(seat);
}
self.send_dnd_drop_performed();
let shared = self.data.shared.get();
shared.state.or_assign(OFFER_STATE_DROPPED);
}

pub fn send_cancelled(self: &Rc<Self>) {
pub fn send_cancelled(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>) {
if self.data.is_xwm {
self.data
.client
Expand All @@ -115,6 +127,10 @@ impl WlDataSource {
.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 })
}
}
Expand Down Expand Up @@ -209,6 +225,7 @@ object_base! {
impl Object for WlDataSource {
fn break_loops(&self) {
break_source_loops::<ClipboardIpc>(self);
self.toplevel_drag.take();
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/ifs/ipc/zwp_primary_selection_device_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ impl IpcVtable for PrimarySelectionIpc {
dd.send_selection(offer);
}

fn send_cancelled(source: &Rc<Self::Source>) {
fn send_cancelled(source: &Rc<Self::Source>, _seat: &Rc<WlSeatGlobal>) {
source.send_cancelled();
}

Expand Down
24 changes: 21 additions & 3 deletions src/ifs/wl_seat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use {
zwp_relative_pointer_v1::ZwpRelativePointerV1,
},
wl_surface::WlSurface,
xdg_toplevel_drag_v1::XdgToplevelDragV1,
},
leaks::Tracker,
object::Object,
Expand Down Expand Up @@ -107,7 +108,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);
ipc::detach_seat::<ClipboardIpc>(&src, &self.dnd.seat);
}
}
}
Expand Down Expand Up @@ -232,6 +233,10 @@ impl WlSeatGlobal {
slf
}

pub fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>> {
self.pointer_owner.toplevel_drag()
}

pub fn set_hardware_cursor(&self, hardware_cursor: bool) {
self.hardware_cursor.set(hardware_cursor);
}
Expand Down Expand Up @@ -397,6 +402,7 @@ impl WlSeatGlobal {
tl.tl_data().float_width.get(),
tl.tl_data().float_height.get(),
ws,
None,
);
} else {
self.state.map_tiled_on(tl, ws);
Expand Down Expand Up @@ -519,6 +525,11 @@ impl WlSeatGlobal {
if let Some(dnd) = self.pointer_owner.dnd_icon() {
dnd.set_output(output);
}
if let Some(drag) = self.pointer_owner.toplevel_drag() {
if let Some(tl) = drag.toplevel.get() {
tl.xdg.set_output(output);
}
}
}

pub fn position(&self) -> (Fixed, Fixed) {
Expand Down Expand Up @@ -630,7 +641,7 @@ impl WlSeatGlobal {
} else if let Some(ws) = data.workspace.get() {
cn.cnode_remove_child2(tl.tl_as_node(), true);
let (width, height) = data.float_size(&ws);
self.state.map_floating(tl, width, height, &ws);
self.state.map_floating(tl, width, height, &ws, None);
}
}
}
Expand Down Expand Up @@ -700,7 +711,7 @@ impl WlSeatGlobal {
ipc::attach_seat::<T>(new, self, ipc::Role::Selection)?;
}
if let Some(old) = field.set(src.clone()) {
ipc::detach_seat::<T>(&old);
ipc::detach_seat::<T>(&old, self);
}
if let Some(client) = self.keyboard_node.get().node_client() {
match src {
Expand Down Expand Up @@ -744,6 +755,11 @@ impl WlSeatGlobal {
if let Some(serial) = serial {
self.selection_serial.set(serial);
}
if let Some(selection) = &selection {
if selection.toplevel_drag.is_some() {
return Err(WlSeatError::OfferHasDrag);
}
}
self.set_selection_::<ClipboardIpc>(&self.selection, selection)
}

Expand Down Expand Up @@ -1117,6 +1133,8 @@ pub enum WlSeatError {
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
WlKeyboardError(Box<WlKeyboardError>),
#[error("Data source has a toplevel attached")]
OfferHasDrag,
}
efrom!(WlSeatError, ClientError);
efrom!(WlSeatError, MsgParserError);
Expand Down
3 changes: 0 additions & 3 deletions src/ifs/wl_seat/event_handling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,9 +808,6 @@ impl WlSeatGlobal {
dd.send_drop();
})
}
if let Some(src) = &dnd.src {
src.on_drop();
}
// surface.client.flush();
}

Expand Down
Loading
Loading