wayland: implement xdg-toplevel-drag
This commit is contained in:
parent
e665a18242
commit
364872258a
21 changed files with 535 additions and 70 deletions
|
|
@ -254,6 +254,17 @@ impl dyn GfxFramebuffer {
|
|||
let seats = state.globals.lock_seats();
|
||||
for seat in seats.values() {
|
||||
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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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>) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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>,
|
||||
|
|
@ -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>(
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
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,
|
||||
|
|
@ -14,6 +19,7 @@ use {
|
|||
bitflags::BitflagsExt,
|
||||
buffd::{MsgParser, MsgParserError},
|
||||
cell_ext::CellExt,
|
||||
clonecell::CloneCell,
|
||||
},
|
||||
wire::{wl_data_source::*, WlDataSourceId},
|
||||
xwayland::XWaylandEvent,
|
||||
|
|
@ -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 {
|
||||
|
|
@ -42,6 +49,7 @@ impl WlDataSource {
|
|||
tracker: Default::default(),
|
||||
data: SourceData::new(client, is_xwm),
|
||||
version,
|
||||
toplevel_drag: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
|
|
@ -209,6 +225,7 @@ object_base! {
|
|||
impl Object for WlDataSource {
|
||||
fn break_loops(&self) {
|
||||
break_source_loops::<ClipboardIpc>(self);
|
||||
self.toplevel_drag.take();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ use {
|
|||
zwp_relative_pointer_v1::ZwpRelativePointerV1,
|
||||
},
|
||||
wl_surface::WlSurface,
|
||||
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use {
|
|||
CHANGE_CURSOR_MOVED,
|
||||
},
|
||||
wl_surface::WlSurface,
|
||||
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||
},
|
||||
state::DeviceHandlerData,
|
||||
tree::{FoundNode, Node},
|
||||
|
|
@ -128,6 +129,10 @@ impl PointerOwnerHolder {
|
|||
self.owner.get().dnd_icon()
|
||||
}
|
||||
|
||||
pub fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>> {
|
||||
self.owner.get().toplevel_drag()
|
||||
}
|
||||
|
||||
pub fn remove_dnd_icon(&self) {
|
||||
self.owner.get().remove_dnd_icon()
|
||||
}
|
||||
|
|
@ -158,6 +163,7 @@ trait PointerOwner {
|
|||
fn revert_to_default(&self, seat: &Rc<WlSeatGlobal>);
|
||||
fn dnd_target_removed(&self, seat: &Rc<WlSeatGlobal>);
|
||||
fn dnd_icon(&self) -> Option<Rc<WlSurface>>;
|
||||
fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>>;
|
||||
fn remove_dnd_icon(&self);
|
||||
}
|
||||
|
||||
|
|
@ -267,14 +273,14 @@ impl PointerOwner for DefaultPointerOwner {
|
|||
|
||||
fn start_drag(
|
||||
&self,
|
||||
_seat: &Rc<WlSeatGlobal>,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
_origin: &Rc<WlSurface>,
|
||||
source: Option<Rc<WlDataSource>>,
|
||||
_icon: Option<Rc<WlSurface>>,
|
||||
_serial: u32,
|
||||
) -> Result<(), WlSeatError> {
|
||||
if let Some(src) = source {
|
||||
src.send_cancelled();
|
||||
src.send_cancelled(seat);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -295,6 +301,10 @@ impl PointerOwner for DefaultPointerOwner {
|
|||
None
|
||||
}
|
||||
|
||||
fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn remove_dnd_icon(&self) {
|
||||
// nothing
|
||||
}
|
||||
|
|
@ -362,6 +372,9 @@ impl PointerOwner for GrabPointerOwner {
|
|||
}
|
||||
if let Some(new) = &src {
|
||||
ipc::attach_seat::<ClipboardIpc>(new, seat, ipc::Role::Dnd)?;
|
||||
if let Some(drag) = new.toplevel_drag.get() {
|
||||
drag.start_drag();
|
||||
}
|
||||
}
|
||||
*seat.dropped_dnd.borrow_mut() = None;
|
||||
let pointer_owner = Rc::new(DndPointerOwner {
|
||||
|
|
@ -411,6 +424,10 @@ impl PointerOwner for GrabPointerOwner {
|
|||
None
|
||||
}
|
||||
|
||||
fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn remove_dnd_icon(&self) {
|
||||
// nothing
|
||||
}
|
||||
|
|
@ -422,7 +439,7 @@ impl PointerOwner for DndPointerOwner {
|
|||
return;
|
||||
}
|
||||
if let Some(src) = &self.dnd.src {
|
||||
src.on_drop();
|
||||
src.on_drop(seat);
|
||||
}
|
||||
let should_drop = match &self.dnd.src {
|
||||
None => true,
|
||||
|
|
@ -439,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);
|
||||
ipc::detach_seat::<ClipboardIpc>(src, seat);
|
||||
}
|
||||
}
|
||||
if let Some(icon) = self.icon.get() {
|
||||
|
|
@ -493,14 +510,14 @@ impl PointerOwner for DndPointerOwner {
|
|||
|
||||
fn start_drag(
|
||||
&self,
|
||||
_seat: &Rc<WlSeatGlobal>,
|
||||
seat: &Rc<WlSeatGlobal>,
|
||||
_origin: &Rc<WlSurface>,
|
||||
source: Option<Rc<WlDataSource>>,
|
||||
_icon: Option<Rc<WlSurface>>,
|
||||
_serial: u32,
|
||||
) -> Result<(), WlSeatError> {
|
||||
if let Some(src) = source {
|
||||
src.send_cancelled();
|
||||
src.send_cancelled(seat);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -510,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);
|
||||
ipc::detach_seat::<ClipboardIpc>(src, seat);
|
||||
}
|
||||
if let Some(icon) = self.icon.get() {
|
||||
icon.dnd_icons.remove(&seat.id());
|
||||
|
|
@ -533,6 +550,14 @@ impl PointerOwner for DndPointerOwner {
|
|||
self.icon.get()
|
||||
}
|
||||
|
||||
fn toplevel_drag(&self) -> Option<Rc<XdgToplevelDragV1>> {
|
||||
if let Some(src) = &self.dnd.src {
|
||||
src.toplevel_drag.get()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_dnd_icon(&self) {
|
||||
self.icon.set(None);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -972,6 +972,10 @@ impl WlSurface {
|
|||
self.seat_state.set_visible(self, visible);
|
||||
}
|
||||
|
||||
pub fn detach_node(&self) {
|
||||
self.destroy_node();
|
||||
}
|
||||
|
||||
pub fn destroy_node(&self) {
|
||||
for (_, constraint) in &self.constraints {
|
||||
constraint.deactivate();
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ impl Xwindow {
|
|||
let ext = self.data.info.pending_extents.get();
|
||||
self.data
|
||||
.state
|
||||
.map_floating(self.clone(), ext.width(), ext.height(), &ws);
|
||||
.map_floating(self.clone(), ext.width(), ext.height(), &ws, None);
|
||||
self.data.title_changed();
|
||||
}
|
||||
Change::Map => {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use {
|
|||
leaks::Tracker,
|
||||
object::Object,
|
||||
rect::Rect,
|
||||
tree::{FindTreeResult, FoundNode, WorkspaceNode},
|
||||
tree::{FindTreeResult, FoundNode, OutputNode, WorkspaceNode},
|
||||
utils::{
|
||||
buffd::{MsgParser, MsgParserError},
|
||||
clonecell::CloneCell,
|
||||
|
|
@ -131,6 +131,14 @@ impl XdgSurface {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
||||
self.surface.set_output(output);
|
||||
let pu = self.popups.lock();
|
||||
for pu in pu.values() {
|
||||
pu.xdg.set_output(output);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_role(&self, role: XdgSurfaceRole) -> Result<(), XdgSurfaceError> {
|
||||
use XdgSurfaceRole::*;
|
||||
match (self.role.get(), role) {
|
||||
|
|
@ -157,6 +165,15 @@ impl XdgSurface {
|
|||
}
|
||||
}
|
||||
|
||||
fn detach_node(&self) {
|
||||
self.workspace.set(None);
|
||||
self.surface.detach_node();
|
||||
let popups = self.popups.lock();
|
||||
for popup in popups.values() {
|
||||
popup.detach_node();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn geometry(&self) -> Option<Rect> {
|
||||
self.geometry.get()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,6 +253,12 @@ impl XdgPopup {
|
|||
self.xdg.destroy_node();
|
||||
self.seat_state.destroy_node(self);
|
||||
}
|
||||
|
||||
pub fn detach_node(&self) {
|
||||
let _v = self.workspace_link.borrow_mut().take();
|
||||
self.xdg.detach_node();
|
||||
self.seat_state.destroy_node(self);
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use {
|
|||
xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt},
|
||||
WlSurface,
|
||||
},
|
||||
xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
|
|
@ -19,8 +20,8 @@ use {
|
|||
renderer::Renderer,
|
||||
state::State,
|
||||
tree::{
|
||||
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, ToplevelData,
|
||||
ToplevelNode, ToplevelNodeBase, ToplevelNodeId, WorkspaceNode,
|
||||
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, OutputNode,
|
||||
ToplevelData, ToplevelNode, ToplevelNodeBase, ToplevelNodeId, WorkspaceNode,
|
||||
},
|
||||
utils::{
|
||||
buffd::{MsgParser, MsgParserError},
|
||||
|
|
@ -99,6 +100,8 @@ pub struct XdgToplevel {
|
|||
max_height: Cell<Option<i32>>,
|
||||
pub tracker: Tracker<Self>,
|
||||
toplevel_data: ToplevelData,
|
||||
pub drag: CloneCell<Option<Rc<XdgToplevelDragV1>>>,
|
||||
is_mapped: Cell<bool>,
|
||||
}
|
||||
|
||||
impl Debug for XdgToplevel {
|
||||
|
|
@ -135,6 +138,8 @@ impl XdgToplevel {
|
|||
String::new(),
|
||||
Some(surface.surface.client.clone()),
|
||||
),
|
||||
drag: Default::default(),
|
||||
is_mapped: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -351,15 +356,20 @@ impl XdgToplevel {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn map_floating(self: &Rc<Self>, workspace: &Rc<WorkspaceNode>) {
|
||||
fn map_floating(self: &Rc<Self>, workspace: &Rc<WorkspaceNode>, abs_pos: Option<(i32, i32)>) {
|
||||
let (width, height) = self.toplevel_data.float_size(workspace);
|
||||
self.state
|
||||
.map_floating(self.clone(), width, height, workspace);
|
||||
.map_floating(self.clone(), width, height, workspace, abs_pos);
|
||||
}
|
||||
|
||||
fn map_child(self: &Rc<Self>, parent: &XdgToplevel) {
|
||||
fn map_child(self: &Rc<Self>, parent: &XdgToplevel, pos: Option<(&Rc<OutputNode>, i32, i32)>) {
|
||||
if let Some((output, x, y)) = pos {
|
||||
let w = output.ensure_workspace();
|
||||
self.map_floating(&w, Some((x, y)));
|
||||
return;
|
||||
}
|
||||
match parent.xdg.workspace.get() {
|
||||
Some(w) => self.map_floating(&w),
|
||||
Some(w) => self.map_floating(&w, None),
|
||||
_ => self.map_tiled(),
|
||||
}
|
||||
}
|
||||
|
|
@ -367,6 +377,80 @@ impl XdgToplevel {
|
|||
fn map_tiled(self: &Rc<Self>) {
|
||||
self.state.map_tiled(self.clone());
|
||||
}
|
||||
|
||||
pub fn prepare_toplevel_drag(&self) {
|
||||
if self.toplevel_data.parent.get().is_none() {
|
||||
return;
|
||||
}
|
||||
self.toplevel_data.detach_node(self);
|
||||
self.xdg.detach_node();
|
||||
}
|
||||
|
||||
pub fn after_toplevel_drag(self: &Rc<Self>, output: &Rc<OutputNode>, x: i32, y: i32) {
|
||||
assert!(self.toplevel_data.parent.is_none());
|
||||
let extents = match self.xdg.geometry.get() {
|
||||
None => self.xdg.extents.get(),
|
||||
Some(g) => g,
|
||||
};
|
||||
self.toplevel_data.float_width.set(extents.width());
|
||||
self.toplevel_data.float_height.set(extents.height());
|
||||
self.clone().after_commit(Some((output, x, y)));
|
||||
}
|
||||
|
||||
fn after_commit(self: &Rc<Self>, pos: Option<(&Rc<OutputNode>, i32, i32)>) {
|
||||
if pos.is_some() {
|
||||
self.is_mapped.set(false);
|
||||
}
|
||||
let surface = &self.xdg.surface;
|
||||
let should_be_mapped = surface.buffer.is_some();
|
||||
if let Some(drag) = self.drag.get() {
|
||||
if drag.is_ongoing() {
|
||||
if should_be_mapped {
|
||||
if !self.is_mapped.replace(true) {
|
||||
if let Some(seat) = drag.source.data.seat.get() {
|
||||
self.xdg.set_output(&seat.get_output());
|
||||
}
|
||||
self.toplevel_data.broadcast(self.clone());
|
||||
}
|
||||
self.extents_changed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if self.is_mapped.replace(should_be_mapped) == should_be_mapped {
|
||||
return;
|
||||
}
|
||||
if !should_be_mapped {
|
||||
self.tl_destroy();
|
||||
{
|
||||
let new_parent = self.parent.get();
|
||||
let mut children = self.children.borrow_mut();
|
||||
for (_, child) in children.drain() {
|
||||
child.parent.set(new_parent.clone());
|
||||
}
|
||||
}
|
||||
self.state.tree_changed();
|
||||
} else {
|
||||
if let Some(parent) = self.parent.get() {
|
||||
self.map_child(&parent, pos);
|
||||
} else {
|
||||
self.map_tiled();
|
||||
}
|
||||
self.extents_changed();
|
||||
if let Some(workspace) = self.xdg.workspace.get() {
|
||||
let output = workspace.output.get();
|
||||
surface.set_output(&output);
|
||||
}
|
||||
// {
|
||||
// let seats = surface.client.state.globals.lock_seats();
|
||||
// for seat in seats.values() {
|
||||
// seat.focus_toplevel(self.clone());
|
||||
// }
|
||||
// }
|
||||
self.state.tree_changed();
|
||||
self.toplevel_data.broadcast(self.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
@ -514,6 +598,9 @@ impl ToplevelNodeBase for XdgToplevel {
|
|||
}
|
||||
|
||||
fn tl_destroy_impl(&self) {
|
||||
if let Some(drag) = self.drag.take() {
|
||||
drag.toplevel.take();
|
||||
}
|
||||
self.xdg.destroy_node();
|
||||
}
|
||||
|
||||
|
|
@ -556,39 +643,7 @@ impl XdgSurfaceExt for XdgToplevel {
|
|||
}
|
||||
|
||||
fn post_commit(self: Rc<Self>) {
|
||||
let surface = &self.xdg.surface;
|
||||
if self.toplevel_data.parent.is_some() {
|
||||
if surface.buffer.is_none() {
|
||||
self.tl_destroy();
|
||||
{
|
||||
let new_parent = self.parent.get();
|
||||
let mut children = self.children.borrow_mut();
|
||||
for (_, child) in children.drain() {
|
||||
child.parent.set(new_parent.clone());
|
||||
}
|
||||
}
|
||||
self.state.tree_changed();
|
||||
}
|
||||
} else if surface.buffer.is_some() {
|
||||
if let Some(parent) = self.parent.get() {
|
||||
self.map_child(&parent);
|
||||
} else {
|
||||
self.map_tiled();
|
||||
}
|
||||
self.extents_changed();
|
||||
if let Some(workspace) = self.xdg.workspace.get() {
|
||||
let output = workspace.output.get();
|
||||
surface.set_output(&output);
|
||||
}
|
||||
// {
|
||||
// let seats = surface.client.state.globals.lock_seats();
|
||||
// for seat in seats.values() {
|
||||
// seat.focus_toplevel(self.clone());
|
||||
// }
|
||||
// }
|
||||
self.state.tree_changed();
|
||||
self.toplevel_data.broadcast(self.clone());
|
||||
}
|
||||
self.after_commit(None);
|
||||
}
|
||||
|
||||
fn extents_changed(&self) {
|
||||
|
|
|
|||
117
src/ifs/xdg_toplevel_drag_manager_v1.rs
Normal file
117
src/ifs/xdg_toplevel_drag_manager_v1.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::xdg_toplevel_drag_v1::XdgToplevelDragV1,
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
wire::{xdg_toplevel_drag_manager_v1::*, XdgToplevelDragManagerV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct XdgToplevelDragManagerV1Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
impl XdgToplevelDragManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: XdgToplevelDragManagerV1Id,
|
||||
client: &Rc<Client>,
|
||||
version: u32,
|
||||
) -> Result<(), XdgToplevelDragManagerV1Error> {
|
||||
let mgr = Rc::new(XdgToplevelDragManagerV1 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
});
|
||||
track!(client, mgr);
|
||||
client.add_client_obj(&mgr)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
XdgToplevelDragManagerV1Global,
|
||||
XdgToplevelDragManagerV1,
|
||||
XdgToplevelDragManagerV1Error
|
||||
);
|
||||
|
||||
simple_add_global!(XdgToplevelDragManagerV1Global);
|
||||
|
||||
impl Global for XdgToplevelDragManagerV1Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XdgToplevelDragManagerV1 {
|
||||
pub id: XdgToplevelDragManagerV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: u32,
|
||||
}
|
||||
|
||||
impl XdgToplevelDragManagerV1 {
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgToplevelDragManagerV1Error> {
|
||||
let _req: Destroy = self.client.parse(self, parser)?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_xdg_toplevel_drag(
|
||||
&self,
|
||||
parser: MsgParser<'_, '_>,
|
||||
) -> Result<(), XdgToplevelDragManagerV1Error> {
|
||||
let req: GetXdgToplevelDrag = self.client.parse(self, parser)?;
|
||||
let source = self.client.lookup(req.data_source)?;
|
||||
if source.data.was_used() {
|
||||
return Err(XdgToplevelDragManagerV1Error::AlreadyUsed);
|
||||
}
|
||||
if source.toplevel_drag.get().is_some() {
|
||||
return Err(XdgToplevelDragManagerV1Error::HasDrag);
|
||||
}
|
||||
let drag = Rc::new(XdgToplevelDragV1::new(req.id, &source));
|
||||
track!(&self.client, drag);
|
||||
self.client.add_client_obj(&drag)?;
|
||||
source.toplevel_drag.set(Some(drag));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = XdgToplevelDragManagerV1;
|
||||
|
||||
DESTROY => destroy,
|
||||
GET_XDG_TOPLEVEL_DRAG => get_xdg_toplevel_drag,
|
||||
}
|
||||
|
||||
impl Object for XdgToplevelDragManagerV1 {}
|
||||
|
||||
simple_add_obj!(XdgToplevelDragManagerV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum XdgToplevelDragManagerV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error("The data source has already been used")]
|
||||
AlreadyUsed,
|
||||
#[error("The source already has a drag object")]
|
||||
HasDrag,
|
||||
}
|
||||
efrom!(XdgToplevelDragManagerV1Error, ClientError);
|
||||
efrom!(XdgToplevelDragManagerV1Error, MsgParserError);
|
||||
144
src/ifs/xdg_toplevel_drag_v1.rs
Normal file
144
src/ifs/xdg_toplevel_drag_v1.rs
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::{
|
||||
ipc::wl_data_source::WlDataSource, wl_seat::WlSeatGlobal,
|
||||
wl_surface::xdg_surface::xdg_toplevel::XdgToplevel,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
utils::{
|
||||
buffd::{MsgParser, MsgParserError},
|
||||
clonecell::CloneCell,
|
||||
},
|
||||
wire::{xdg_toplevel_drag_v1::*, XdgToplevelDragV1Id},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct XdgToplevelDragV1 {
|
||||
pub id: XdgToplevelDragV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub source: Rc<WlDataSource>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub toplevel: CloneCell<Option<Rc<XdgToplevel>>>,
|
||||
pub x_off: Cell<i32>,
|
||||
pub y_off: Cell<i32>,
|
||||
}
|
||||
|
||||
impl XdgToplevelDragV1 {
|
||||
pub fn new(id: XdgToplevelDragV1Id, source: &Rc<WlDataSource>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
client: source.data.client.clone(),
|
||||
source: source.clone(),
|
||||
tracker: Default::default(),
|
||||
toplevel: Default::default(),
|
||||
x_off: Cell::new(0),
|
||||
y_off: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ongoing(&self) -> bool {
|
||||
self.source.data.was_used() && !self.source.data.was_dropped_or_cancelled()
|
||||
}
|
||||
|
||||
fn detach(&self) {
|
||||
self.source.toplevel_drag.take();
|
||||
if let Some(tl) = self.toplevel.take() {
|
||||
tl.drag.take();
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgToplevelDragV1Error> {
|
||||
let _req: Destroy = self.client.parse(self, parser)?;
|
||||
if self.is_ongoing() {
|
||||
return Err(XdgToplevelDragV1Error::ActiveDrag);
|
||||
}
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn attach(self: &Rc<Self>, parser: MsgParser<'_, '_>) -> Result<(), XdgToplevelDragV1Error> {
|
||||
let req: Attach = self.client.parse(&**self, parser)?;
|
||||
if self.source.data.was_dropped_or_cancelled() {
|
||||
return Ok(());
|
||||
}
|
||||
let toplevel = self.client.lookup(req.toplevel)?;
|
||||
if toplevel.drag.set(Some(self.clone())).is_some() {
|
||||
return Err(XdgToplevelDragV1Error::AlreadyDragged);
|
||||
}
|
||||
if let Some(prev) = self.toplevel.set(Some(toplevel)) {
|
||||
if prev.xdg.surface.buffer.get().is_some() {
|
||||
return Err(XdgToplevelDragV1Error::ToplevelAttached);
|
||||
}
|
||||
if prev.id != req.toplevel {
|
||||
prev.drag.set(None);
|
||||
}
|
||||
}
|
||||
self.x_off.set(req.x_offset);
|
||||
self.y_off.set(req.y_offset);
|
||||
self.start_drag();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start_drag(self: &Rc<Self>) {
|
||||
if !self.is_ongoing() {
|
||||
return;
|
||||
}
|
||||
let Some(tl) = self.toplevel.get() else {
|
||||
return;
|
||||
};
|
||||
tl.prepare_toplevel_drag();
|
||||
self.client.state.tree_changed();
|
||||
}
|
||||
|
||||
pub fn finish_drag(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
if self.source.data.was_used() {
|
||||
if let Some(tl) = self.toplevel.get() {
|
||||
let output = seat.get_output();
|
||||
let (x, y) = seat.position();
|
||||
tl.drag.take();
|
||||
tl.after_toplevel_drag(
|
||||
&output,
|
||||
x.round_down() - self.x_off.get(),
|
||||
y.round_down() - self.y_off.get(),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = XdgToplevelDragV1;
|
||||
|
||||
DESTROY => destroy,
|
||||
ATTACH => attach,
|
||||
}
|
||||
|
||||
impl Object for XdgToplevelDragV1 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(XdgToplevelDragV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum XdgToplevelDragV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error("The toplevel already has a drag attached")]
|
||||
AlreadyDragged,
|
||||
#[error("There already is a mapped toplevel attached")]
|
||||
ToplevelAttached,
|
||||
#[error("The drag is ongoing")]
|
||||
ActiveDrag,
|
||||
}
|
||||
efrom!(XdgToplevelDragV1Error, ClientError);
|
||||
efrom!(XdgToplevelDragV1Error, MsgParserError);
|
||||
|
|
@ -267,9 +267,7 @@ impl Renderer<'_> {
|
|||
) {
|
||||
let surface = &xdg.surface;
|
||||
if let Some(geo) = xdg.geometry() {
|
||||
let (xt, yt) = geo.translate(x, y);
|
||||
x = xt;
|
||||
y = yt;
|
||||
(x, y) = geo.translate(x, y);
|
||||
}
|
||||
self.render_surface(surface, x, y, bounds);
|
||||
}
|
||||
|
|
|
|||
13
src/state.rs
13
src/state.rs
|
|
@ -527,12 +527,23 @@ impl State {
|
|||
mut width: i32,
|
||||
mut height: i32,
|
||||
workspace: &Rc<WorkspaceNode>,
|
||||
abs_pos: Option<(i32, i32)>,
|
||||
) {
|
||||
width += 2 * self.theme.sizes.border_width.get();
|
||||
height += 2 * self.theme.sizes.border_width.get() + self.theme.sizes.title_height.get() + 1;
|
||||
let output = workspace.output.get();
|
||||
let output_rect = output.global.pos.get();
|
||||
let position = {
|
||||
let position = if let Some((mut x1, mut y1)) = abs_pos {
|
||||
if y1 <= output_rect.y1() {
|
||||
y1 = output_rect.y1() + 1;
|
||||
}
|
||||
if y1 > output_rect.y2() {
|
||||
y1 = output_rect.y2();
|
||||
}
|
||||
y1 -= self.theme.sizes.border_width.get() + self.theme.sizes.title_height.get() + 1;
|
||||
x1 -= self.theme.sizes.border_width.get();
|
||||
Rect::new_sized(x1, y1, width, height).unwrap()
|
||||
} else {
|
||||
let mut x1 = output_rect.x1();
|
||||
let mut y1 = output_rect.y1();
|
||||
if width < output_rect.width() {
|
||||
|
|
|
|||
|
|
@ -266,6 +266,10 @@ impl ToplevelData {
|
|||
handle.send_closed();
|
||||
}
|
||||
}
|
||||
self.detach_node(node);
|
||||
}
|
||||
|
||||
pub fn detach_node(&self, node: &dyn Node) {
|
||||
if let Some(fd) = self.fullscrceen_data.borrow_mut().take() {
|
||||
fd.placeholder.tl_destroy();
|
||||
}
|
||||
|
|
|
|||
10
wire/xdg_toplevel_drag_manager_v1.txt
Normal file
10
wire/xdg_toplevel_drag_manager_v1.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
|
||||
}
|
||||
|
||||
msg get_xdg_toplevel_drag = 1 {
|
||||
id: id(xdg_toplevel_drag_v1),
|
||||
data_source: id(wl_data_source),
|
||||
}
|
||||
11
wire/xdg_toplevel_drag_v1.txt
Normal file
11
wire/xdg_toplevel_drag_v1.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
|
||||
}
|
||||
|
||||
msg attach = 1 {
|
||||
toplevel: id(xdg_toplevel),
|
||||
x_offset: i32,
|
||||
y_offset: i32,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue