diff --git a/src/compositor.rs b/src/compositor.rs index 61e02dce2..ef6b9888c 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -269,6 +269,7 @@ fn start_compositor2( cpu_worker, ui_drag_enabled: Cell::new(true), ui_drag_threshold_squared: Cell::new(10), + toplevel_drag_ids: Default::default(), }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); diff --git a/src/gfx_api.rs b/src/gfx_api.rs index a82141010..b1c3fe0d6 100644 --- a/src/gfx_api.rs +++ b/src/gfx_api.rs @@ -726,7 +726,9 @@ pub fn create_render_pass( renderer.render_highlight(&highlight.move_(-rect.x1(), -rect.y1())); } if let Some(drag) = seat.toplevel_drag() { - drag.render(&mut renderer, &rect, x, y); + if drag.enabled.get() { + drag.render(&mut renderer, &rect, x, y); + } } if let Some(dnd_icon) = seat.dnd_icon() { dnd_icon.render(&mut renderer, &rect, x, y); diff --git a/src/ifs.rs b/src/ifs.rs index 3d81f7702..15c13bc24 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -62,7 +62,6 @@ 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 xdg_wm_dialog_v1; pub mod zwlr_layer_shell_v1; diff --git a/src/ifs/ipc/wl_data_source.rs b/src/ifs/ipc/wl_data_source.rs index a21feed0c..8b3aebfa4 100644 --- a/src/ifs/ipc/wl_data_source.rs +++ b/src/ifs/ipc/wl_data_source.rs @@ -15,7 +15,7 @@ use { SOURCE_STATE_DROPPED, }, wl_seat::WlSeatGlobal, - xdg_toplevel_drag_v1::XdgToplevelDragV1, + wl_surface::xdg_surface::xdg_toplevel::xdg_toplevel_drag_v1::XdgToplevelDragV1, }, leaks::Tracker, object::{Object, Version}, diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index b68c911b9..85885717f 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -64,8 +64,10 @@ use { zwp_pointer_gesture_swipe_v1::ZwpPointerGestureSwipeV1, zwp_relative_pointer_v1::ZwpRelativePointerV1, }, - wl_surface::{dnd_icon::DndIcon, WlSurface}, - xdg_toplevel_drag_v1::XdgToplevelDragV1, + wl_surface::{ + dnd_icon::DndIcon, + xdg_surface::xdg_toplevel::xdg_toplevel_drag_v1::XdgToplevelDragV1, WlSurface, + }, }, leaks::Tracker, object::{Object, Version}, diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index 5d88ae242..dbdbda0b9 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -10,8 +10,10 @@ use { wl_pointer::PendingScroll, Dnd, DroppedDnd, NodeSeatState, WlSeatError, WlSeatGlobal, BTN_LEFT, BTN_RIGHT, CHANGE_CURSOR_MOVED, CHANGE_TREE, }, - wl_surface::{dnd_icon::DndIcon, WlSurface}, - xdg_toplevel_drag_v1::XdgToplevelDragV1, + wl_surface::{ + dnd_icon::DndIcon, + xdg_surface::xdg_toplevel::xdg_toplevel_drag_v1::XdgToplevelDragV1, WlSurface, + }, }, rect::Rect, tree::{ diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index 2821bfe42..a3e19bb51 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -8,7 +8,9 @@ use { wl_surface::{ xdg_surface::{ xdg_popup::{XdgPopup, XdgPopupError, XdgPopupParent}, - xdg_toplevel::{XdgToplevel, WM_CAPABILITIES_SINCE}, + xdg_toplevel::{ + xdg_toplevel_drag_v1::ToplevelDragId, XdgToplevel, WM_CAPABILITIES_SINCE, + }, }, PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError, }, @@ -141,6 +143,8 @@ impl XdgPopupParent for Popup { #[derive(Default, Debug)] pub struct PendingXdgSurfaceData { geometry: Option, + toplevel_drag_offset: Option<(i32, i32)>, + toplevel_drag_id: Option, } impl PendingXdgSurfaceData { @@ -153,12 +157,18 @@ impl PendingXdgSurfaceData { }; } opt!(geometry); + opt!(toplevel_drag_offset); + opt!(toplevel_drag_id); } } pub trait XdgSurfaceExt: Debug { - fn initial_configure(self: Rc) -> Result<(), XdgSurfaceError> { - Ok(()) + fn initial_configure(self: Rc) { + // nothing + } + + fn before_apply_commit(self: Rc, pending: &mut PendingXdgSurfaceData) { + let _ = pending; } fn post_commit(self: Rc) { @@ -493,7 +503,7 @@ impl SurfaceExt for XdgSurface { ) -> Result<(), WlSurfaceError> { if !self.have_initial_commit.get() { if let Some(ext) = self.ext.get() { - ext.initial_configure()?; + ext.initial_configure(); self.do_send_configure(); self.have_initial_commit.set(true); } @@ -509,6 +519,9 @@ impl SurfaceExt for XdgSurface { } } } + if let Some(ext) = self.ext.get() { + ext.before_apply_commit(pending); + } } Ok(()) } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index 7351dde93..f73930a9a 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -5,7 +5,7 @@ use { fixed::Fixed, ifs::{ wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal}, - wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt}, + wl_surface::xdg_surface::{XdgSurface, XdgSurfaceExt}, xdg_positioner::{ XdgPositioned, XdgPositioner, CA_FLIP_X, CA_FLIP_Y, CA_RESIZE_X, CA_RESIZE_Y, CA_SLIDE_X, CA_SLIDE_Y, @@ -374,13 +374,12 @@ impl StackedNode for XdgPopup { } impl XdgSurfaceExt for XdgPopup { - fn initial_configure(self: Rc) -> Result<(), XdgSurfaceError> { + fn initial_configure(self: Rc) { if let Some(parent) = self.parent.get() { self.update_position(&*parent); let rel = self.relative_position.get(); self.send_configure(rel.x1(), rel.y1(), rel.width(), rel.height()); } - Ok(()) } fn post_commit(self: Rc) { diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index 96cdcfec5..ea90b218d 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -1,4 +1,5 @@ pub mod xdg_dialog_v1; +pub mod xdg_toplevel_drag_v1; use { crate::{ @@ -12,12 +13,11 @@ use { wl_seat::{tablet::TabletTool, NodeSeatState, SeatId, WlSeatGlobal}, wl_surface::{ xdg_surface::{ - xdg_toplevel::xdg_dialog_v1::XdgDialogV1, XdgSurface, XdgSurfaceError, + xdg_toplevel::xdg_dialog_v1::XdgDialogV1, PendingXdgSurfaceData, XdgSurface, XdgSurfaceExt, }, WlSurface, }, - xdg_toplevel_drag_v1::XdgToplevelDragV1, }, leaks::Tracker, object::{Object, Version}, @@ -41,6 +41,7 @@ use { rc::Rc, }, thiserror::Error, + xdg_toplevel_drag_v1::XdgToplevelDragV1, }; #[derive(Copy, Clone, Debug, FromPrimitive)] @@ -681,14 +682,41 @@ impl ToplevelNodeBase for XdgToplevel { } impl XdgSurfaceExt for XdgToplevel { - fn initial_configure(self: Rc) -> Result<(), XdgSurfaceError> { + fn initial_configure(self: Rc) { let rect = self.xdg.absolute_desired_extents.get(); if rect.is_empty() { self.send_configure(0, 0); } else { self.send_configure_checked(rect.width(), rect.height()); } - Ok(()) + } + + fn before_apply_commit(self: Rc, pending: &mut PendingXdgSurfaceData) { + if let Some((x_off, y_off)) = pending.toplevel_drag_offset.take() { + if let Some(drag) = self.drag.get() { + if drag.is_ongoing() { + if self.node_visible() { + self.xdg.damage(); + } + let extents = self.xdg.absolute_desired_extents.get(); + let extents = extents.move_(drag.x_off.get() - x_off, drag.y_off.get() - y_off); + self.clone().tl_change_extents(&extents); + if self.node_visible() { + self.xdg.damage(); + } + } + drag.x_off.set(x_off); + drag.y_off.set(y_off); + } + } + if let Some(id) = pending.toplevel_drag_id.take() { + if let Some(drag) = self.drag.get() { + if id == drag.toplevel_id.get() { + drag.enabled.set(true); + drag.start_drag(); + } + } + } } fn post_commit(self: Rc) { diff --git a/src/ifs/xdg_toplevel_drag_v1.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel/xdg_toplevel_drag_v1.rs similarity index 75% rename from src/ifs/xdg_toplevel_drag_v1.rs rename to src/ifs/wl_surface/xdg_surface/xdg_toplevel/xdg_toplevel_drag_v1.rs index e41f5f258..9e4267d2d 100644 --- a/src/ifs/xdg_toplevel_drag_v1.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel/xdg_toplevel_drag_v1.rs @@ -17,8 +17,18 @@ use { thiserror::Error, }; +fn clamp_offset(offset: i32) -> i32 { + const OFFSET_CLAMP: i32 = 100_000; + offset.clamp(-OFFSET_CLAMP, OFFSET_CLAMP) +} + +const SET_OFFSET_SINCE: Version = Version(2); + +linear_ids!(ToplevelDragIds, ToplevelDragId, u64); + pub struct XdgToplevelDragV1 { pub id: XdgToplevelDragV1Id, + pub toplevel_id: Cell, pub client: Rc, pub source: Rc, pub tracker: Tracker, @@ -26,12 +36,14 @@ pub struct XdgToplevelDragV1 { pub x_off: Cell, pub y_off: Cell, pub version: Version, + pub enabled: Cell, } impl XdgToplevelDragV1 { pub fn new(id: XdgToplevelDragV1Id, source: &Rc, version: Version) -> Self { Self { id, + toplevel_id: Cell::new(source.data.client.state.toplevel_drag_ids.next()), client: source.data.client.clone(), source: source.clone(), tracker: Default::default(), @@ -39,11 +51,14 @@ impl XdgToplevelDragV1 { x_off: Cell::new(0), y_off: Cell::new(0), version, + enabled: Cell::new(true), } } pub fn is_ongoing(&self) -> bool { - self.source.data.was_used() && !self.source.data.was_dropped_or_cancelled() + self.enabled.get() + && self.source.data.was_used() + && !self.source.data.was_dropped_or_cancelled() } fn detach(&self) { @@ -101,7 +116,7 @@ impl XdgToplevelDragV1RequestHandler for XdgToplevelDragV1 { if toplevel.drag.set(Some(slf.clone())).is_some() { return Err(XdgToplevelDragV1Error::AlreadyDragged); } - if let Some(prev) = self.toplevel.set(Some(toplevel)) { + if let Some(prev) = self.toplevel.set(Some(toplevel.clone())) { if prev.xdg.surface.buffer.is_some() { return Err(XdgToplevelDragV1Error::ToplevelAttached); } @@ -109,9 +124,29 @@ impl XdgToplevelDragV1RequestHandler for XdgToplevelDragV1 { prev.drag.set(None); } } - self.x_off.set(req.x_offset); - self.y_off.set(req.y_offset); - self.start_drag(); + let x_off = clamp_offset(req.x_offset); + let y_off = clamp_offset(req.y_offset); + if self.version >= SET_OFFSET_SINCE { + let id = self.client.state.toplevel_drag_ids.next(); + self.toplevel_id.set(id); + self.enabled.set(false); + let pending = &mut *toplevel.xdg.pending(); + pending.toplevel_drag_id = Some(id); + pending.toplevel_drag_offset = Some((x_off, y_off)); + } else { + self.x_off.set(x_off); + self.y_off.set(y_off); + self.start_drag(); + } + Ok(()) + } + + fn set_offset(&self, req: SetOffset, _slf: &Rc) -> Result<(), Self::Error> { + if let Some(tl) = self.toplevel.get() { + let x_off = clamp_offset(req.x_offset); + let y_off = clamp_offset(req.y_offset); + tl.xdg.pending().toplevel_drag_offset = Some((x_off, y_off)); + } Ok(()) } } @@ -133,7 +168,7 @@ impl XdgToplevelDragV1 { } pub fn finish_drag(&self, seat: &Rc) { - if self.source.data.was_used() { + if self.enabled.get() && self.source.data.was_used() { if let Some(tl) = self.toplevel.get() { let output = seat.get_output(); let (x, y) = seat.pointer_cursor().position(); diff --git a/src/ifs/xdg_toplevel_drag_manager_v1.rs b/src/ifs/xdg_toplevel_drag_manager_v1.rs index 94dda82eb..97208a141 100644 --- a/src/ifs/xdg_toplevel_drag_manager_v1.rs +++ b/src/ifs/xdg_toplevel_drag_manager_v1.rs @@ -2,7 +2,7 @@ use { crate::{ client::{Client, ClientError}, globals::{Global, GlobalName}, - ifs::xdg_toplevel_drag_v1::XdgToplevelDragV1, + ifs::wl_surface::xdg_surface::xdg_toplevel::xdg_toplevel_drag_v1::XdgToplevelDragV1, leaks::Tracker, object::{Object, Version}, wire::{xdg_toplevel_drag_manager_v1::*, XdgToplevelDragManagerV1Id}, @@ -52,7 +52,7 @@ impl Global for XdgToplevelDragManagerV1Global { } fn version(&self) -> u32 { - 1 + 2 } } diff --git a/src/state.rs b/src/state.rs index 1871084b3..144090513 100644 --- a/src/state.rs +++ b/src/state.rs @@ -48,6 +48,7 @@ use { }, wl_surface::{ wl_subsurface::SubsurfaceIds, + xdg_surface::xdg_toplevel::xdg_toplevel_drag_v1::ToplevelDragIds, zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1}, zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2, NoneSurfaceExt, @@ -220,6 +221,7 @@ pub struct State { pub cpu_worker: Rc, pub ui_drag_enabled: Cell, pub ui_drag_threshold_squared: Cell, + pub toplevel_drag_ids: ToplevelDragIds, } // impl Drop for State { diff --git a/wire/xdg_toplevel_drag_v1.txt b/wire/xdg_toplevel_drag_v1.txt index 568999870..c99f8d37f 100644 --- a/wire/xdg_toplevel_drag_v1.txt +++ b/wire/xdg_toplevel_drag_v1.txt @@ -9,3 +9,8 @@ request attach { x_offset: i32, y_offset: i32, } + +request set_offset (since = 2) { + x_offset: i32, + y_offset: i32, +}