1
0
Fork 0
forked from wry/wry

Merge pull request #197 from mahkoh/jorth/layer-shell

Implement remaining layer shell features
This commit is contained in:
mahkoh 2024-05-09 12:14:22 +02:00 committed by GitHub
commit 3c61dd5fd4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 857 additions and 402 deletions

View file

@ -33,7 +33,7 @@ use {
object::Version,
state::DeviceHandlerData,
tree::{Direction, Node, ToplevelNode},
utils::{bitflags::BitflagsExt, smallmap::SmallMap},
utils::{bitflags::BitflagsExt, hash_map_ext::HashMapExt, smallmap::SmallMap},
wire::WlDataOfferId,
xkbcommon::{KeyboardState, XkbState, XKB_KEY_DOWN, XKB_KEY_UP},
},
@ -237,7 +237,7 @@ impl WlSeatGlobal {
| InputEvent::TabletPadStrip { time_usec, .. } => {
self.last_input_usec.set(time_usec);
if self.idle_notifications.is_not_empty() {
for (_, notification) in self.idle_notifications.lock().drain() {
for notification in self.idle_notifications.lock().drain_values() {
notification.resume.trigger();
}
}

View file

@ -19,7 +19,10 @@ use {
object::Version,
time::now_usec,
tree::{FoundNode, Node},
utils::{bindings::PerClientBindings, clonecell::CloneCell, copyhashmap::CopyHashMap},
utils::{
bindings::PerClientBindings, clonecell::CloneCell, copyhashmap::CopyHashMap,
hash_map_ext::HashMapExt,
},
},
std::{
cell::{Cell, RefCell},
@ -293,18 +296,18 @@ impl WlSeatGlobal {
pub fn tablet_clear(&self) {
self.tablet.seats.clear();
for (_, tablet) in self.tablet.tablets.lock().drain() {
for tablet in self.tablet.tablets.lock().drain_values() {
tablet.pads.clear();
tablet.bindings.clear();
tablet.tools.clear();
}
for (_, tool) in self.tablet.tools.lock().drain() {
for tool in self.tablet.tools.lock().drain_values() {
tool.cursor.detach();
tool.opt.tool.take();
tool.tool_owner.destroy(&tool);
tool.bindings.clear();
}
for (_, pad) in self.tablet.pads.lock().drain() {
for pad in self.tablet.pads.lock().drain_values() {
pad.pad_owner.destroy(&pad);
pad.tablet.take();
pad.bindings.clear();
@ -324,14 +327,14 @@ impl WlSeatGlobal {
let Some(tablet) = self.tablet.tablets.remove(&id) else {
return;
};
for (_, tool) in tablet.tools.lock().drain() {
for tool in tablet.tools.lock().drain_values() {
self.tablet_handle_remove_tool(now_usec(), tool.id);
}
for (_, pad) in tablet.pads.lock().drain() {
for pad in tablet.pads.lock().drain_values() {
pad.pad_owner.destroy(&pad);
pad.tablet.take();
}
for (_, binding) in tablet.bindings.lock().drain() {
for binding in tablet.bindings.lock().drain_values() {
binding.send_removed();
}
}

View file

@ -14,7 +14,7 @@ use {
wl_surface::WlSurface,
},
time::{now_usec, usec_to_msec},
utils::clonecell::CloneCell,
utils::{clonecell::CloneCell, hash_map_ext::HashMapExt},
},
std::{cell::Cell, rc::Rc},
};
@ -76,7 +76,7 @@ impl WlSeatGlobal {
if let Some(tablet) = pad.tablet.take() {
tablet.pads.remove(&pad.id);
}
for (_, binding) in pad.bindings.lock().drain() {
for binding in pad.bindings.lock().drain_values() {
binding.send_removed();
}
}

View file

@ -15,7 +15,7 @@ use {
},
rect::Rect,
time::usec_to_msec,
utils::clonecell::CloneCell,
utils::{clonecell::CloneCell, hash_map_ext::HashMapExt},
},
std::{cell::Cell, rc::Rc},
};
@ -31,7 +31,7 @@ impl WlSeatGlobal {
tool.opt.tool.take();
tool.cursor.detach();
tool.tool_owner.destroy(&tool);
for (_, binding) in tool.bindings.lock().drain() {
for binding in tool.bindings.lock().drain_values() {
binding.send_removed();
}
tool.tablet.tools.remove(&id);

View file

@ -74,6 +74,7 @@ pub struct WlPointer {
id: WlPointerId,
pub seat: Rc<WlSeat>,
pub tracker: Tracker<Self>,
last_motion: Cell<(Fixed, Fixed)>,
}
impl WlPointer {
@ -82,10 +83,12 @@ impl WlPointer {
id,
seat: seat.clone(),
tracker: Default::default(),
last_motion: Default::default(),
}
}
pub fn send_enter(&self, serial: u32, surface: WlSurfaceId, x: Fixed, y: Fixed) {
self.last_motion.set((x, y));
self.seat.client.event(Enter {
self_id: self.id,
serial,
@ -104,6 +107,9 @@ impl WlPointer {
}
pub fn send_motion(&self, time: u32, x: Fixed, y: Fixed) {
if self.last_motion.replace((x, y)) == (x, y) {
return;
}
self.seat.client.event(Motion {
self_id: self.id,
time,

View file

@ -4,6 +4,7 @@ use {
utils::{
clonecell::CloneCell,
copyhashmap::CopyHashMap,
hash_map_ext::HashMapExt,
linkedlist::{LinkedList, LinkedNode, NodeRef},
numcell::NumCell,
},
@ -102,7 +103,7 @@ impl CommitTimelines {
}
pub fn clear(&self) {
for (_, list) in self.gc.lock().drain() {
for list in self.gc.lock().drain_values() {
break_loops(&list);
}
}

View file

@ -7,7 +7,7 @@ use {
ifs::{
wl_surface::{
xdg_surface::{
xdg_popup::{XdgPopup, XdgPopupError},
xdg_popup::{XdgPopup, XdgPopupError, XdgPopupParent},
xdg_toplevel::{XdgToplevel, WM_CAPABILITIES_SINCE},
},
PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
@ -17,14 +17,20 @@ use {
leaks::Tracker,
object::Object,
rect::Rect,
tree::{FindTreeResult, FoundNode, OutputNode, WorkspaceNode},
tree::{FindTreeResult, FoundNode, OutputNode, StackedNode, WorkspaceNode},
utils::{
clonecell::CloneCell, copyhashmap::CopyHashMap, numcell::NumCell, option_ext::OptionExt,
clonecell::CloneCell,
copyhashmap::CopyHashMap,
hash_map_ext::HashMapExt,
linkedlist::{LinkedList, LinkedNode},
numcell::NumCell,
option_ext::OptionExt,
rc_eq::rc_eq,
},
wire::{xdg_surface::*, WlSurfaceId, XdgPopupId, XdgSurfaceId},
},
std::{
cell::{Cell, RefMut},
cell::{Cell, RefCell, RefMut},
fmt::Debug,
rc::Rc,
},
@ -65,11 +71,72 @@ pub struct XdgSurface {
extents: Cell<Rect>,
pub absolute_desired_extents: Cell<Rect>,
ext: CloneCell<Option<Rc<dyn XdgSurfaceExt>>>,
popups: CopyHashMap<XdgPopupId, Rc<XdgPopup>>,
popup_display_stack: CloneCell<Rc<LinkedList<Rc<dyn StackedNode>>>>,
popups: CopyHashMap<XdgPopupId, Rc<Popup>>,
pub workspace: CloneCell<Option<Rc<WorkspaceNode>>>,
pub tracker: Tracker<Self>,
}
struct Popup {
parent: Rc<XdgSurface>,
popup: Rc<XdgPopup>,
display_link: RefCell<Option<LinkedNode<Rc<dyn StackedNode>>>>,
workspace_link: RefCell<Option<LinkedNode<Rc<dyn StackedNode>>>>,
}
impl XdgPopupParent for Popup {
fn position(&self) -> Rect {
self.parent.absolute_desired_extents.get()
}
fn remove_popup(&self) {
self.parent.popups.remove(&self.popup.id);
}
fn output(&self) -> Rc<OutputNode> {
self.parent.surface.output.get()
}
fn has_workspace_link(&self) -> bool {
self.workspace_link.borrow().is_some()
}
fn post_commit(&self) {
let mut wl = self.workspace_link.borrow_mut();
let mut dl = self.display_link.borrow_mut();
let ws = match self.parent.workspace.get() {
Some(ws) => ws,
_ => {
log::info!("no ws");
return;
}
};
let surface = &self.popup.xdg.surface;
let state = &surface.client.state;
if surface.buffer.is_some() {
if wl.is_none() {
self.popup.xdg.set_workspace(&ws);
*wl = Some(ws.stacked.add_last(self.popup.clone()));
*dl = Some(
self.parent
.popup_display_stack
.get()
.add_last(self.popup.clone()),
);
state.tree_changed();
self.popup.set_visible(self.parent.surface.visible.get());
}
} else {
if wl.take().is_some() {
drop(wl);
drop(dl);
self.popup.set_visible(false);
self.popup.destroy_node();
}
}
}
}
#[derive(Default, Debug)]
pub struct PendingXdgSurfaceData {
geometry: Option<Rect>,
@ -115,6 +182,7 @@ impl XdgSurface {
extents: Cell::new(Default::default()),
absolute_desired_extents: Cell::new(Default::default()),
ext: Default::default(),
popup_display_stack: CloneCell::new(surface.client.state.root.stacked.clone()),
popups: Default::default(),
workspace: Default::default(),
tracker: Default::default(),
@ -139,7 +207,7 @@ impl XdgSurface {
self.surface.set_output(&ws.output.get());
let pu = self.popups.lock();
for pu in pu.values() {
pu.xdg.set_workspace(ws);
pu.popup.xdg.set_workspace(ws);
}
}
@ -147,7 +215,7 @@ impl XdgSurface {
self.surface.set_output(output);
let pu = self.popups.lock();
for pu in pu.values() {
pu.xdg.set_output(output);
pu.popup.xdg.set_output(output);
}
}
@ -171,9 +239,8 @@ impl XdgSurface {
fn destroy_node(&self) {
self.workspace.set(None);
self.surface.destroy_node();
let popups = self.popups.lock();
for popup in popups.values() {
popup.destroy_node();
for popup in self.popups.lock().drain_values() {
popup.popup.destroy_node();
}
}
@ -182,7 +249,8 @@ impl XdgSurface {
self.surface.detach_node(false);
let popups = self.popups.lock();
for popup in popups.values() {
popup.detach_node();
let _v = popup.workspace_link.borrow_mut().take();
popup.popup.detach_node();
}
}
@ -216,6 +284,19 @@ impl XdgSurface {
p.xdg_surface.get_or_insert_default_ext()
})
}
pub fn set_popup_stack(&self, stack: &Rc<LinkedList<Rc<dyn StackedNode>>>) {
let prev = self.popup_display_stack.set(stack.clone());
if rc_eq(&prev, stack) {
return;
}
for popup in self.popups.lock().values() {
if let Some(dl) = &*popup.display_link.borrow() {
stack.add_last_existing(dl);
}
popup.popup.xdg.set_popup_stack(stack);
}
}
}
impl XdgSurfaceRequestHandler for XdgSurface {
@ -279,11 +360,18 @@ impl XdgSurfaceRequestHandler for XdgSurface {
);
return Err(XdgSurfaceError::AlreadyConstructed);
}
let popup = Rc::new(XdgPopup::new(req.id, slf, parent.as_ref(), &positioner)?);
let popup = Rc::new(XdgPopup::new(req.id, slf, &positioner)?);
track!(self.surface.client, popup);
self.surface.client.add_client_obj(&popup)?;
if let Some(parent) = &parent {
parent.popups.set(req.id, popup.clone());
let user = Rc::new(Popup {
parent: parent.clone(),
popup: popup.clone(),
display_link: Default::default(),
workspace_link: Default::default(),
});
popup.parent.set(Some(user.clone()));
parent.popups.set(req.id, user);
}
self.ext.set(Some(popup));
Ok(())
@ -341,21 +429,29 @@ impl XdgSurface {
fn update_popup_positions(&self) {
let popups = self.popups.lock();
for popup in popups.values() {
popup.update_absolute_position();
popup.popup.update_absolute_position();
}
}
fn set_visible(&self, visible: bool) {
self.surface.set_visible(visible);
for popup in self.popups.lock().values() {
popup.set_visible(visible);
popup.popup.set_visible(visible);
}
}
fn restack_popups(&self) {
for popup in self.popups.lock().values() {
popup.restack();
if self.popups.is_empty() {
return;
}
let stack = self.popup_display_stack.get();
for popup in self.popups.lock().values() {
if let Some(dl) = &*popup.display_link.borrow() {
stack.add_last_existing(dl);
}
popup.popup.xdg.restack_popups();
}
self.surface.client.state.tree_changed();
}
}

View file

@ -16,10 +16,10 @@ use {
rect::Rect,
renderer::Renderer,
tree::{
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, StackedNode,
WorkspaceNode,
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode,
StackedNode,
},
utils::{clonecell::CloneCell, linkedlist::LinkedNode},
utils::clonecell::CloneCell,
wire::{xdg_popup::*, XdgPopupId},
},
std::{
@ -35,14 +35,20 @@ const INVALID_GRAB: u32 = 1;
tree_id!(PopupId);
pub trait XdgPopupParent {
fn position(&self) -> Rect;
fn remove_popup(&self);
fn output(&self) -> Rc<OutputNode>;
fn has_workspace_link(&self) -> bool;
fn post_commit(&self);
}
pub struct XdgPopup {
id: XdgPopupId,
pub id: XdgPopupId,
node_id: PopupId,
pub xdg: Rc<XdgSurface>,
pub(super) parent: CloneCell<Option<Rc<XdgSurface>>>,
pub(in super::super) parent: CloneCell<Option<Rc<dyn XdgPopupParent>>>,
relative_position: Cell<Rect>,
display_link: RefCell<Option<LinkedNode<Rc<dyn StackedNode>>>>,
workspace_link: RefCell<Option<LinkedNode<Rc<dyn StackedNode>>>>,
pos: RefCell<XdgPositioned>,
pub tracker: Tracker<Self>,
seat_state: NodeSeatState,
@ -59,7 +65,6 @@ impl XdgPopup {
pub fn new(
id: XdgPopupId,
xdg: &Rc<XdgSurface>,
parent: Option<&Rc<XdgSurface>>,
pos: &Rc<XdgPositioner>,
) -> Result<Self, XdgPopupError> {
let pos = pos.value();
@ -70,10 +75,8 @@ impl XdgPopup {
id,
node_id: xdg.surface.client.state.node_ids.next(),
xdg: xdg.clone(),
parent: CloneCell::new(parent.cloned()),
parent: Default::default(),
relative_position: Cell::new(Default::default()),
display_link: RefCell::new(None),
workspace_link: RefCell::new(None),
pos: RefCell::new(pos),
tracker: Default::default(),
seat_state: Default::default(),
@ -105,17 +108,13 @@ impl XdgPopup {
.event(PopupDone { self_id: self.id })
}
fn update_position(&self, parent: &XdgSurface) -> Result<(), XdgPopupError> {
// let parent = parent.extents.get();
fn update_position(&self, parent: &dyn XdgPopupParent) {
let positioner = self.pos.borrow_mut();
// if !parent.contains_rect(&positioner.ar) {
// return Err(XdgPopupError::AnchorRectOutside);
// }
let parent_abs = parent.absolute_desired_extents.get();
let parent_abs = parent.position();
let mut rel_pos = positioner.get_position(false, false);
let mut abs_pos = rel_pos.move_(parent_abs.x1(), parent_abs.y1());
if let Some(ws) = parent.workspace.get() {
let output_pos = ws.output.get().global.pos.get();
{
let output_pos = parent.output().global.pos.get();
let mut overflow = output_pos.get_overflow(&abs_pos);
if !overflow.is_contained() {
let mut flip_x = positioner.ca.contains(CA_FLIP_X) && overflow.x_overflow();
@ -146,17 +145,21 @@ impl XdgPopup {
}
let (mut dx, mut dy) = (0, 0);
if positioner.ca.contains(CA_SLIDE_X) && overflow.x_overflow() {
dx = if overflow.left > 0 || overflow.left + overflow.right > 0 {
dx = if overflow.left + overflow.right > 0 {
parent_abs.x1() - abs_pos.x1()
} else if overflow.left > 0 {
overflow.left
} else {
parent_abs.x2() - abs_pos.x2()
-overflow.right
};
}
if positioner.ca.contains(CA_SLIDE_Y) && overflow.y_overflow() {
dy = if overflow.top > 0 || overflow.top + overflow.bottom > 0 {
dy = if overflow.top + overflow.bottom > 0 {
parent_abs.y1() - abs_pos.y1()
} else if overflow.top > 0 {
overflow.top
} else {
parent_abs.y2() - abs_pos.y2()
-overflow.bottom
};
}
if dx != 0 || dy != 0 {
@ -174,32 +177,35 @@ impl XdgPopup {
dy2 = -overflow.bottom.max(0);
}
if dx1 > 0 || dx2 < 0 || dy1 > 0 || dy2 < 0 {
abs_pos = Rect::new(
let maybe_abs_pos = Rect::new(
abs_pos.x1() + dx1,
abs_pos.y1() + dy1,
abs_pos.x2() + dx2,
abs_pos.y2() + dy2,
)
.unwrap();
rel_pos = Rect::new_sized(
abs_pos.x1() - parent_abs.x1(),
abs_pos.y1() - parent_abs.y1(),
abs_pos.width(),
abs_pos.height(),
)
.unwrap();
);
// If the popup is completely outside the output, this will fail. Just
// use its position as is.
if let Some(maybe_abs_pos) = maybe_abs_pos {
abs_pos = maybe_abs_pos;
rel_pos = Rect::new_sized(
abs_pos.x1() - parent_abs.x1(),
abs_pos.y1() - parent_abs.y1(),
abs_pos.width(),
abs_pos.height(),
)
.unwrap();
}
}
}
}
self.relative_position.set(rel_pos);
self.xdg.set_absolute_desired_extents(&abs_pos);
Ok(())
}
pub fn update_absolute_position(&self) {
if let Some(parent) = self.parent.get() {
let rel = self.relative_position.get();
let parent = parent.absolute_desired_extents.get();
let parent = parent.position();
self.xdg
.set_absolute_desired_extents(&rel.move_(parent.x1(), parent.y1()));
}
@ -211,15 +217,8 @@ impl XdgPopupRequestHandler for XdgPopup {
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.destroy_node();
{
if let Some(parent) = self.parent.take() {
parent.popups.remove(&self.id);
}
}
self.xdg.ext.set(None);
self.xdg.surface.client.remove_obj(self)?;
*self.display_link.borrow_mut() = None;
*self.workspace_link.borrow_mut() = None;
Ok(())
}
@ -230,7 +229,7 @@ impl XdgPopupRequestHandler for XdgPopup {
fn reposition(&self, req: Reposition, _slf: &Rc<Self>) -> Result<(), Self::Error> {
*self.pos.borrow_mut() = self.xdg.surface.client.lookup(req.positioner)?.value();
if let Some(parent) = self.parent.get() {
self.update_position(&parent)?;
self.update_position(&*parent);
let rel = self.relative_position.get();
self.send_repositioned(req.token);
self.send_configure(rel.x1(), rel.y1(), rel.width(), rel.height());
@ -241,10 +240,6 @@ impl XdgPopupRequestHandler for XdgPopup {
}
impl XdgPopup {
fn get_parent_workspace(&self) -> Option<Rc<WorkspaceNode>> {
self.parent.get()?.workspace.get()
}
pub fn set_visible(&self, visible: bool) {
// log::info!("set visible = {}", visible);
self.set_visible_prepared.set(false);
@ -253,27 +248,18 @@ impl XdgPopup {
}
pub fn destroy_node(&self) {
let _v = self.display_link.borrow_mut().take();
let _v = self.workspace_link.borrow_mut().take();
self.xdg.destroy_node();
self.seat_state.destroy_node(self);
if let Some(parent) = self.parent.take() {
parent.remove_popup();
}
self.send_popup_done();
}
pub fn detach_node(&self) {
let _v = self.workspace_link.borrow_mut().take();
self.xdg.detach_node();
self.seat_state.destroy_node(self);
}
pub fn restack(&self) {
let state = &self.xdg.surface.client.state;
let dl = self.display_link.borrow();
if let Some(dl) = &*dl {
state.root.stacked.add_last_existing(dl);
}
self.xdg.restack_popups();
state.tree_changed();
}
}
object_base! {
@ -284,13 +270,10 @@ object_base! {
impl Object for XdgPopup {
fn break_loops(&self) {
self.destroy_node();
self.parent.set(None);
*self.display_link.borrow_mut() = None;
*self.workspace_link.borrow_mut() = None;
}
}
simple_add_obj!(XdgPopup);
dedicated_add_obj!(XdgPopup, XdgPopupId, xdg_popups);
impl Node for XdgPopup {
fn node_id(&self) -> NodeId {
@ -374,7 +357,10 @@ impl StackedNode for XdgPopup {
}
fn stacked_has_workspace_link(&self) -> bool {
self.workspace_link.borrow().is_some()
match self.parent.get() {
Some(p) => p.has_workspace_link(),
_ => false,
}
}
fn stacked_absolute_position_constrains_input(&self) -> bool {
@ -385,7 +371,7 @@ impl StackedNode for XdgPopup {
impl XdgSurfaceExt for XdgPopup {
fn initial_configure(self: Rc<Self>) -> Result<(), XdgSurfaceError> {
if let Some(parent) = self.parent.get() {
self.update_position(&parent)?;
self.update_position(&*parent);
let rel = self.relative_position.get();
self.send_configure(rel.x1(), rel.y1(), rel.width(), rel.height());
}
@ -393,38 +379,8 @@ impl XdgSurfaceExt for XdgPopup {
}
fn post_commit(self: Rc<Self>) {
let mut wl = self.workspace_link.borrow_mut();
let mut dl = self.display_link.borrow_mut();
let ws = match self.get_parent_workspace() {
Some(ws) => ws,
_ => {
log::info!("no ws");
return;
}
};
let surface = &self.xdg.surface;
let state = &surface.client.state;
if surface.buffer.is_some() {
if wl.is_none() {
self.xdg.set_workspace(&ws);
*wl = Some(ws.stacked.add_last(self.clone()));
*dl = Some(state.root.stacked.add_last(self.clone()));
state.tree_changed();
self.set_visible(
self.parent
.get()
.map(|p| p.surface.visible.get())
.unwrap_or(false),
);
}
} else {
if wl.take().is_some() {
drop(wl);
drop(dl);
self.set_visible(false);
self.destroy_node();
self.send_popup_done();
}
if let Some(parent) = self.parent.get() {
parent.post_commit();
}
}

View file

@ -29,7 +29,7 @@ use {
OutputNode, ToplevelData, ToplevelNode, ToplevelNodeBase, ToplevelNodeId,
WorkspaceNode,
},
utils::clonecell::CloneCell,
utils::{clonecell::CloneCell, hash_map_ext::HashMapExt},
wire::{xdg_toplevel::*, XdgToplevelId},
},
ahash::{AHashMap, AHashSet},
@ -218,7 +218,7 @@ impl XdgToplevelRequestHandler for XdgToplevel {
Some(p) => Some(p.children.borrow_mut()),
_ => None,
};
for (_, child) in children.drain() {
for child in children.drain_values() {
child.parent.set(parent.clone());
if let Some(parent_children) = &mut parent_children {
parent_children.insert(child.id, child);
@ -418,7 +418,7 @@ impl XdgToplevel {
{
let new_parent = self.parent.get();
let mut children = self.children.borrow_mut();
for (_, child) in children.drain() {
for child in children.drain_values() {
child.parent.set(new_parent.clone());
}
}

View file

@ -4,23 +4,32 @@ use {
ifs::{
wl_output::OutputGlobalOpt,
wl_seat::NodeSeatState,
wl_surface::{PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError},
wl_surface::{
xdg_surface::xdg_popup::{XdgPopup, XdgPopupParent},
PendingState, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError,
},
zwlr_layer_shell_v1::{ZwlrLayerShellV1, OVERLAY},
},
leaks::Tracker,
object::Object,
rect::Rect,
renderer::Renderer,
tree::{FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor},
tree::{
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitor, OutputNode,
StackedNode,
},
utils::{
bitflags::BitflagsExt, cell_ext::CellExt, linkedlist::LinkedNode, numcell::NumCell,
bitflags::BitflagsExt,
copyhashmap::CopyHashMap,
hash_map_ext::HashMapExt,
linkedlist::{LinkedList, LinkedNode},
numcell::NumCell,
option_ext::OptionExt,
},
wire::{zwlr_layer_surface_v1::*, WlSurfaceId, ZwlrLayerSurfaceV1Id},
wire::{zwlr_layer_surface_v1::*, WlSurfaceId, XdgPopupId, ZwlrLayerSurfaceV1Id},
},
std::{
cell::{Cell, RefMut},
mem,
cell::{Cell, RefCell, RefMut},
ops::Deref,
rc::Rc,
},
@ -47,30 +56,74 @@ pub struct ZwlrLayerSurfaceV1 {
pub output: Rc<OutputGlobalOpt>,
pub namespace: String,
pub tracker: Tracker<Self>,
output_pos: Cell<Rect>,
output_extents: Cell<Rect>,
pos: Cell<Rect>,
mapped: Cell<bool>,
layer: Cell<u32>,
requested_serial: NumCell<u32>,
acked_serial: Cell<Option<u32>>,
size: Cell<(i32, i32)>,
anchor: Cell<u32>,
exclusive_zone: Cell<i32>,
exclusive_zone: Cell<ExclusiveZone>,
margin: Cell<(i32, i32, i32, i32)>,
keyboard_interactivity: Cell<u32>,
link: Cell<Option<LinkedNode<Rc<Self>>>>,
seat_state: NodeSeatState,
last_configure: Cell<(i32, i32)>,
exclusive_edge: Cell<Option<u32>>,
exclusive_size: Cell<ExclusiveSize>,
popups: CopyHashMap<XdgPopupId, Rc<Popup>>,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct ExclusiveSize {
pub top: i32,
pub right: i32,
pub bottom: i32,
pub left: i32,
}
impl ExclusiveSize {
pub fn is_empty(&self) -> bool {
*self == ExclusiveSize::default()
}
pub fn is_not_empty(&self) -> bool {
!self.is_empty()
}
pub fn max(&self, other: &Self) -> Self {
Self {
top: self.top.max(other.top),
right: self.right.max(other.right),
bottom: self.bottom.max(other.bottom),
left: self.left.max(other.left),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ExclusiveZone {
MoveSelf,
FixedSelf,
Acquire(i32),
}
struct Popup {
parent: Rc<ZwlrLayerSurfaceV1>,
popup: Rc<XdgPopup>,
stack: Rc<LinkedList<Rc<dyn StackedNode>>>,
stack_link: RefCell<Option<LinkedNode<Rc<dyn StackedNode>>>>,
}
#[derive(Default)]
pub struct PendingLayerSurfaceData {
size: Option<(i32, i32)>,
anchor: Option<u32>,
exclusive_zone: Option<i32>,
exclusive_zone: Option<ExclusiveZone>,
margin: Option<(i32, i32, i32, i32)>,
keyboard_interactivity: Option<u32>,
layer: Option<u32>,
any: bool,
exclusive_edge: Option<u32>,
}
impl PendingLayerSurfaceData {
@ -88,7 +141,6 @@ impl PendingLayerSurfaceData {
opt!(margin);
opt!(keyboard_interactivity);
opt!(layer);
self.any |= mem::take(&mut next.any);
}
}
@ -110,19 +162,22 @@ impl ZwlrLayerSurfaceV1 {
output: output.clone(),
namespace: namespace.to_string(),
tracker: Default::default(),
output_pos: Default::default(),
output_extents: Default::default(),
pos: Default::default(),
mapped: Cell::new(false),
layer: Cell::new(layer),
requested_serial: Default::default(),
acked_serial: Cell::new(None),
size: Cell::new((0, 0)),
anchor: Cell::new(0),
exclusive_zone: Cell::new(0),
exclusive_zone: Cell::new(ExclusiveZone::MoveSelf),
margin: Cell::new((0, 0, 0, 0)),
keyboard_interactivity: Cell::new(0),
link: Cell::new(None),
seat_state: Default::default(),
last_configure: Default::default(),
exclusive_edge: Default::default(),
exclusive_size: Default::default(),
popups: Default::default(),
}
}
@ -167,7 +222,6 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
}
let mut pending = self.pending();
pending.size = Some((req.width as _, req.height as _));
pending.any = true;
Ok(())
}
@ -177,7 +231,6 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
}
let mut pending = self.pending();
pending.anchor = Some(req.anchor);
pending.any = true;
Ok(())
}
@ -187,15 +240,27 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let mut pending = self.pending();
pending.exclusive_zone = Some(req.zone);
pending.any = true;
let zone = if req.zone < 0 {
ExclusiveZone::FixedSelf
} else if req.zone == 0 {
ExclusiveZone::MoveSelf
} else if req.zone > u16::MAX as i32 {
return Err(ZwlrLayerSurfaceV1Error::ExcessiveExclusive);
} else {
ExclusiveZone::Acquire(req.zone)
};
pending.exclusive_zone = Some(zone);
Ok(())
}
fn set_margin(&self, req: SetMargin, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let mut pending = self.pending();
for s in [req.top, req.right, req.bottom, req.left] {
if (s as i64).abs() > u16::MAX as i64 {
return Err(ZwlrLayerSurfaceV1Error::ExcessiveMargin);
}
}
pending.margin = Some((req.top, req.right, req.bottom, req.left));
pending.any = true;
Ok(())
}
@ -211,20 +276,35 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
}
let mut pending = self.pending();
pending.keyboard_interactivity = Some(req.keyboard_interactivity);
pending.any = true;
Ok(())
}
fn get_popup(&self, _req: GetPopup, _slf: &Rc<Self>) -> Result<(), Self::Error> {
fn get_popup(&self, req: GetPopup, slf: &Rc<Self>) -> Result<(), Self::Error> {
let popup = self.client.lookup(req.popup)?;
if popup.parent.is_some() {
return Err(ZwlrLayerSurfaceV1Error::PopupHasParent);
}
let stack = self.client.state.root.stacked_above_layers.clone();
popup.xdg.set_popup_stack(&stack);
let user = Rc::new(Popup {
parent: slf.clone(),
popup: popup.clone(),
stack,
stack_link: Default::default(),
});
popup.parent.set(Some(user.clone()));
self.popups.set(popup.id, user);
Ok(())
}
fn ack_configure(&self, req: AckConfigure, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.acked_serial.set(Some(req.serial));
fn ack_configure(&self, _req: AckConfigure, _slf: &Rc<Self>) -> Result<(), Self::Error> {
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.popups.is_not_empty() {
return Err(ZwlrLayerSurfaceV1Error::HasPopups);
}
self.destroy_node();
self.client.remove_obj(self)?;
self.surface.unset_ext();
@ -237,18 +317,77 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
}
let mut pending = self.pending();
pending.layer = Some(req.layer);
pending.any = true;
Ok(())
}
fn set_exclusive_edge(
&self,
req: SetExclusiveEdge,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
if req.edge & !(LEFT | RIGHT | TOP | BOTTOM) != 0 {
return Err(ZwlrLayerSurfaceV1Error::UnknownAnchor(req.edge));
}
if req.edge.count_ones() > 1 {
return Err(ZwlrLayerSurfaceV1Error::TooManyExclusiveEdges);
}
let mut pending = self.pending();
if req.edge == 0 {
pending.exclusive_edge = None;
} else {
pending.exclusive_edge = Some(req.edge);
}
Ok(())
}
}
impl ZwlrLayerSurfaceV1 {
fn pre_commit(&self, pending: &mut PendingState) -> Result<(), ZwlrLayerSurfaceV1Error> {
let Some(global) = self.output.get() else {
return Ok(());
pub fn exclusive_size(&self) -> ExclusiveSize {
self.exclusive_size.get()
}
fn update_exclusive_size(&self) {
let exclusive_edge = {
if let Some(ee) = self.exclusive_edge.get() {
Some(ee)
} else {
let anchor = self.anchor.get();
let edges = anchor.count_ones();
if edges == 1 {
Some(anchor)
} else if edges == 3 {
match (!anchor) & (TOP | BOTTOM | LEFT | RIGHT) {
TOP => Some(BOTTOM),
BOTTOM => Some(TOP),
LEFT => Some(RIGHT),
RIGHT => Some(LEFT),
_ => None,
}
} else {
None
}
}
};
let mut exclusive_size = ExclusiveSize::default();
if let (ExclusiveZone::Acquire(s), Some(edge)) = (self.exclusive_zone.get(), exclusive_edge)
{
match edge {
TOP => exclusive_size.top = s,
RIGHT => exclusive_size.right = s,
BOTTOM => exclusive_size.bottom = s,
LEFT => exclusive_size.left = s,
_ => {}
}
}
if self.exclusive_size.replace(exclusive_size) != exclusive_size {
if let Some(output) = self.output.node.get() {
output.update_exclusive_zones();
}
}
}
fn pre_commit(&self, pending: &mut PendingState) -> Result<(), ZwlrLayerSurfaceV1Error> {
let pending = pending.layer_surface.get_or_insert_default_ext();
let mut send_configure = mem::replace(&mut pending.any, false);
if let Some(size) = pending.size.take() {
self.size.set(size);
}
@ -267,84 +406,152 @@ impl ZwlrLayerSurfaceV1 {
if let Some(layer) = pending.layer.take() {
self.layer.set(layer);
}
{
let (mut width, mut height) = self.size.get();
let anchor = self.anchor.get();
if width == 0 {
if !anchor.contains(LEFT | RIGHT) {
return Err(ZwlrLayerSurfaceV1Error::WidthZero);
}
send_configure = true;
width = global.position().width();
if let Some(edge) = pending.exclusive_edge.take() {
self.exclusive_edge.set(Some(edge));
}
let anchor = self.anchor.get();
let (width, height) = self.size.get();
if width == 0 && !anchor.contains(LEFT | RIGHT) {
return Err(ZwlrLayerSurfaceV1Error::WidthZero);
}
if height == 0 && !anchor.contains(TOP | BOTTOM) {
return Err(ZwlrLayerSurfaceV1Error::HeightZero);
}
if let Some(ee) = self.exclusive_edge.get() {
if !self.anchor.get().contains(ee) {
return Err(ZwlrLayerSurfaceV1Error::ExclusiveEdgeNotAnchored);
}
if height == 0 {
if !anchor.contains(TOP | BOTTOM) {
return Err(ZwlrLayerSurfaceV1Error::HeightZero);
}
send_configure = true;
height = global.position().height();
}
self.size.set((width, height));
}
if self.acked_serial.is_none() {
send_configure = true;
}
if send_configure {
let (width, height) = self.size.get();
let serial = self.requested_serial.fetch_add(1) + 1;
self.send_configure(serial, width as _, height as _);
}
self.configure();
Ok(())
}
pub fn output_position(&self) -> Rect {
self.output_pos.get()
}
pub fn position(&self) -> Rect {
self.pos.get()
}
pub fn compute_position(&self) {
let Some(global) = self.output.get() else {
fn configure(&self) {
let Some(node) = self.output.node() else {
return;
};
let (width, height) = self.size.get();
let (mut width, mut height) = self.size.get();
let (mt, mr, mb, ml) = self.margin.get();
let (mut available_width, mut available_height) = match self.exclusive_zone.get() {
ExclusiveZone::MoveSelf => node.non_exclusive_rect.get().size(),
_ => node.global.pos.get().size(),
};
let anchor = self.anchor.get();
if anchor.contains(LEFT) {
available_width -= ml;
}
if anchor.contains(RIGHT) {
available_width -= mr;
}
if anchor.contains(TOP) {
available_height -= mt;
}
if anchor.contains(BOTTOM) {
available_height -= mb;
}
if width == 0 {
width = available_width;
}
width = width.min(available_width).max(1);
if height == 0 {
height = available_height;
}
height = height.min(available_height).max(1);
let serial = self.requested_serial.fetch_add(1) + 1;
if self.last_configure.replace((width, height)) != (width, height) {
self.send_configure(serial, width as _, height as _);
}
}
pub fn output_extents(&self) -> Rect {
self.output_extents.get()
}
fn compute_position(&self) {
let Some(output) = self.output.node() else {
return;
};
let extents = self.surface.extents.get();
let (width, height) = extents.size();
let mut anchor = self.anchor.get();
if anchor == 0 {
anchor = LEFT | RIGHT | TOP | BOTTOM;
}
let opos = global.pos.get();
let (mt, mr, mb, ml) = self.margin.get();
let opos = output.global.pos.get();
let rect = match self.exclusive_zone.get() {
ExclusiveZone::MoveSelf => output.non_exclusive_rect.get(),
_ => opos,
};
let (owidth, oheight) = rect.size();
let mut x1 = 0;
let mut y1 = 0;
if anchor.contains(LEFT) {
if anchor.contains(RIGHT) {
x1 += (opos.width() - width) / 2;
}
if anchor.contains(LEFT | RIGHT) {
x1 = (owidth - width - ml - mr) / 2;
} else if anchor.contains(LEFT) {
x1 = ml;
} else if anchor.contains(RIGHT) {
x1 += opos.width() - width;
x1 = owidth - width - mr;
}
if anchor.contains(TOP) {
if anchor.contains(BOTTOM) {
y1 += (opos.height() - height) / 2;
}
if anchor.contains(TOP | BOTTOM) {
y1 = (oheight - height - mt - mb) / 2;
} else if anchor.contains(TOP) {
y1 = mt;
} else if anchor.contains(BOTTOM) {
y1 += opos.height() - height;
y1 = oheight - height - mb;
}
let a_rect = Rect::new_sized(x1 + rect.x1(), y1 + rect.y1(), width, height).unwrap();
let o_rect = a_rect.move_(-opos.x1(), -opos.y1());
self.output_extents.set(o_rect);
let a_rect_old = self.pos.replace(a_rect);
let abs_x = a_rect.x1() - extents.x1();
let abs_y = a_rect.y1() - extents.y1();
self.surface.set_absolute_position(abs_x, abs_y);
if a_rect_old != a_rect {
for popup in self.popups.lock().values() {
popup.popup.update_absolute_position();
}
}
let o_rect = Rect::new_sized(x1, y1, width, height).unwrap();
let a_rect = o_rect.move_(opos.x1(), opos.y1());
self.output_pos.set(o_rect);
self.pos.set(a_rect);
self.surface.set_absolute_position(a_rect.x1(), a_rect.y1());
self.client.state.tree_changed();
}
pub fn output_resized(&self) {
self.configure();
self.compute_position();
}
pub fn exclusive_zones_changed(&self) {
if self.exclusive_zone.get() != ExclusiveZone::MoveSelf {
return;
}
self.output_resized();
}
pub fn destroy_node(&self) {
self.link.set(None);
self.mapped.set(false);
self.surface.destroy_node();
self.seat_state.destroy_node(self);
self.client.state.tree_changed();
self.last_configure.take();
if self.exclusive_size.take().is_not_empty() {
if let Some(node) = self.output.node() {
node.update_exclusive_zones();
}
}
for popup in self.popups.lock().drain_values() {
popup.popup.destroy_node();
}
}
pub fn set_visible(&self, visible: bool) {
self.surface.set_visible(visible);
if !visible {
for popup in self.popups.lock().drain_values() {
popup.popup.set_visible(false);
popup.popup.destroy_node();
}
}
}
}
@ -367,17 +574,17 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
if !buffer_is_some {
self.destroy_node();
} else {
let pos = self.pos.get();
let (width, height) = self.size.get();
if width != pos.width() || height != pos.height() {
if self.surface.extents.get().size() != self.pos.get().size() {
self.compute_position();
}
self.update_exclusive_size();
}
} else if buffer_is_some {
let layer = &output.layers[self.layer.get() as usize];
self.link.set(Some(layer.add_last(self.clone())));
self.mapped.set(true);
self.compute_position();
self.update_exclusive_size();
}
if self.mapped.get() != was_mapped {
output.update_visible();
@ -444,7 +651,8 @@ impl Node for ZwlrLayerSurfaceV1 {
tree: &mut Vec<FoundNode>,
_usecase: FindTreeUsecase,
) -> FindTreeResult {
self.surface.find_tree_at_(x, y, tree)
let (dx, dy) = self.surface.extents.get().position();
self.surface.find_tree_at_(x + dx, y + dy, tree)
}
fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, _bounds: Option<&Rect>) {
@ -452,6 +660,49 @@ impl Node for ZwlrLayerSurfaceV1 {
}
}
impl XdgPopupParent for Popup {
fn position(&self) -> Rect {
self.parent.pos.get()
}
fn remove_popup(&self) {
self.parent.popups.remove(&self.popup.id);
}
fn output(&self) -> Rc<OutputNode> {
self.parent.surface.output.get()
}
fn has_workspace_link(&self) -> bool {
false
}
fn post_commit(&self) {
let mut dl = self.stack_link.borrow_mut();
let output = self.output();
let surface = &self.popup.xdg.surface;
let state = &surface.client.state;
if surface.buffer.is_some() {
if dl.is_none() {
if self.parent.surface.visible.get() {
self.popup.xdg.set_output(&output);
*dl = Some(self.stack.add_last(self.popup.clone()));
state.tree_changed();
self.popup.set_visible(self.parent.surface.visible.get());
} else {
self.popup.destroy_node();
}
}
} else {
if dl.take().is_some() {
drop(dl);
self.popup.set_visible(false);
self.popup.destroy_node();
}
}
}
}
object_base! {
self = ZwlrLayerSurfaceV1;
version = self.shell.version;
@ -482,10 +733,22 @@ pub enum ZwlrLayerSurfaceV1Error {
UnknownLayer(u32),
#[error("Surface size must not be larger than 65535x65535")]
ExcessiveSize,
#[error("Margin must not be larger than 65535")]
ExcessiveMargin,
#[error("Unknown anchor {0}")]
UnknownAnchor(u32),
#[error("Unknown keyboard interactivity {0}")]
UnknownKi(u32),
#[error("Surface is not anchored at exclusive edge")]
ExclusiveEdgeNotAnchored,
#[error("Request must contain exactly one edge")]
TooManyExclusiveEdges,
#[error("Exclusive zone not be larger than 65535")]
ExcessiveExclusive,
#[error("Popup already has a parent")]
PopupHasParent,
#[error("Surface still has popups")]
HasPopups,
}
efrom!(ZwlrLayerSurfaceV1Error, WlSurfaceError);
efrom!(ZwlrLayerSurfaceV1Error, ClientError);

View file

@ -107,7 +107,7 @@ impl Global for ZwlrLayerShellV1Global {
}
fn version(&self) -> u32 {
4
5
}
fn required_caps(&self) -> ClientCaps {

View file

@ -5,7 +5,7 @@ use {
ifs::{wl_buffer::WlBuffer, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1},
leaks::Tracker,
object::Object,
utils::errorfmt::ErrorFmt,
utils::{errorfmt::ErrorFmt, hash_map_ext::HashMapExt},
video::dmabuf::{DmaBuf, DmaBufPlane, PlaneVec, MAX_PLANES},
wire::{zwp_linux_buffer_params_v1::*, WlBufferId, ZwpLinuxBufferParamsV1Id},
},
@ -90,7 +90,7 @@ impl ZwpLinuxBufferParamsV1 {
modifier,
planes: PlaneVec::new(),
};
let mut planes: Vec<_> = self.planes.borrow_mut().drain().map(|v| v.1).collect();
let mut planes: Vec<_> = self.planes.borrow_mut().drain_values().collect();
planes.sort_by_key(|a| a.plane_idx);
for (i, p) in planes.into_iter().enumerate() {
if p.plane_idx as usize != i {