Skip to content

Commit

Permalink
layer-shell: implement popups
Browse files Browse the repository at this point in the history
  • Loading branch information
mahkoh committed May 8, 2024
1 parent c6ef63a commit 52afd94
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 56 deletions.
2 changes: 2 additions & 0 deletions deploy-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

- Needs jay-compositor release.

# 1.2.0

- Needs jay-config release.
Expand Down
2 changes: 2 additions & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

- Add remaining layer-shell features.

# 1.2.0 (2024-05-05)

- Add support for wp-security-manager-v1.
Expand Down
9 changes: 6 additions & 3 deletions src/client/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use {
wl_registry::WlRegistry,
wl_seat::{tablet::zwp_tablet_tool_v2::ZwpTabletToolV2, wl_pointer::WlPointer, WlSeat},
wl_surface::{
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
xdg_surface::{xdg_popup::XdgPopup, xdg_toplevel::XdgToplevel, XdgSurface},
WlSurface,
},
wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1,
Expand All @@ -33,8 +33,8 @@ use {
wire::{
JayOutputId, JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId,
WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId,
WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId,
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id,
WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId,
XdgPositionerId, XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id,
ZwpPrimarySelectionSourceV1Id, ZwpTabletToolV2Id,
},
},
Expand Down Expand Up @@ -66,6 +66,7 @@ pub struct Objects {
pub jay_toplevels: CopyHashMap<JayToplevelId, Rc<JayToplevel>>,
pub drm_lease_outputs: CopyHashMap<WpDrmLeaseConnectorV1Id, Rc<WpDrmLeaseConnectorV1>>,
pub tablet_tools: CopyHashMap<ZwpTabletToolV2Id, Rc<ZwpTabletToolV2>>,
pub xdg_popups: CopyHashMap<XdgPopupId, Rc<XdgPopup>>,
ids: RefCell<Vec<usize>>,
}

Expand Down Expand Up @@ -98,6 +99,7 @@ impl Objects {
jay_toplevels: Default::default(),
drm_lease_outputs: Default::default(),
tablet_tools: Default::default(),
xdg_popups: Default::default(),
ids: RefCell::new(vec![]),
}
}
Expand Down Expand Up @@ -134,6 +136,7 @@ impl Objects {
self.jay_toplevels.clear();
self.drm_lease_outputs.clear();
self.tablet_tools.clear();
self.xdg_popups.clear();
}

pub fn id<T>(&self, client_data: &Client) -> Result<T, ClientError>
Expand Down
40 changes: 34 additions & 6 deletions src/ifs/wl_surface/xdg_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ use {
rect::Rect,
tree::{FindTreeResult, FoundNode, OutputNode, StackedNode, WorkspaceNode},
utils::{
clonecell::CloneCell, copyhashmap::CopyHashMap, hash_map_ext::HashMapExt,
linkedlist::LinkedNode, 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},
},
Expand Down Expand Up @@ -66,6 +71,7 @@ pub struct XdgSurface {
extents: Cell<Rect>,
pub absolute_desired_extents: Cell<Rect>,
ext: CloneCell<Option<Rc<dyn XdgSurfaceExt>>>,
popup_display_stack: CloneCell<Rc<LinkedList<Rc<dyn StackedNode>>>>,
popups: CopyHashMap<XdgPopupId, Rc<Popup>>,
pub workspace: CloneCell<Option<Rc<WorkspaceNode>>>,
pub tracker: Tracker<Self>,
Expand Down Expand Up @@ -111,7 +117,12 @@ impl XdgPopupParent for Popup {
if wl.is_none() {
self.popup.xdg.set_workspace(&ws);
*wl = Some(ws.stacked.add_last(self.popup.clone()));
*dl = Some(state.root.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());
}
Expand Down Expand Up @@ -171,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(),
Expand Down Expand Up @@ -272,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 {
Expand Down Expand Up @@ -416,14 +441,17 @@ impl XdgSurface {
}

fn restack_popups(&self) {
let state = &self.surface.client.state;
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() {
state.root.stacked.add_last_existing(dl);
stack.add_last_existing(dl);
}
popup.popup.xdg.restack_popups();
}
state.tree_changed();
self.surface.client.state.tree_changed();
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/ifs/wl_surface/xdg_surface/xdg_popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub struct XdgPopup {
pub id: XdgPopupId,
node_id: PopupId,
pub xdg: Rc<XdgSurface>,
pub(super) parent: CloneCell<Option<Rc<dyn XdgPopupParent>>>,
pub(in super::super) parent: CloneCell<Option<Rc<dyn XdgPopupParent>>>,
relative_position: Cell<Rect>,
pos: RefCell<XdgPositioned>,
pub tracker: Tracker<Self>,
Expand Down Expand Up @@ -273,7 +273,7 @@ impl Object for XdgPopup {
}
}

simple_add_obj!(XdgPopup);
dedicated_add_obj!(XdgPopup, XdgPopupId, xdg_popups);

impl Node for XdgPopup {
fn node_id(&self) -> NodeId {
Expand Down
116 changes: 109 additions & 7 deletions src/ifs/wl_surface/zwlr_layer_surface_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +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, linkedlist::LinkedNode, numcell::NumCell, option_ext::OptionExt,
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},
cell::{Cell, RefCell, RefMut},
ops::Deref,
rc::Rc,
},
Expand Down Expand Up @@ -60,6 +71,7 @@ pub struct ZwlrLayerSurfaceV1 {
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)]
Expand Down Expand Up @@ -96,6 +108,13 @@ pub enum ExclusiveZone {
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)>,
Expand Down Expand Up @@ -158,6 +177,7 @@ impl ZwlrLayerSurfaceV1 {
last_configure: Default::default(),
exclusive_edge: Default::default(),
exclusive_size: Default::default(),
popups: Default::default(),
}
}

Expand Down Expand Up @@ -259,7 +279,21 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
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(())
}

Expand All @@ -268,6 +302,9 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
}

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();
Expand Down Expand Up @@ -466,10 +503,15 @@ impl ZwlrLayerSurfaceV1 {
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);
self.pos.set(a_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();
}
}
self.client.state.tree_changed();
}

Expand Down Expand Up @@ -497,6 +539,19 @@ impl ZwlrLayerSurfaceV1 {
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();
}
}
}
}

Expand Down Expand Up @@ -605,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;
Expand Down Expand Up @@ -647,6 +745,10 @@ pub enum ZwlrLayerSurfaceV1Error {
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);
22 changes: 14 additions & 8 deletions src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,18 +189,24 @@ impl Renderer<'_> {
self.render_workspace(&ws, x, y + th + 1);
}
}
for stacked in self.state.root.stacked.iter() {
if stacked.node_visible() {
self.base.ops.push(GfxApiOpt::Sync);
let pos = stacked.node_absolute_position();
if pos.intersects(&opos) {
let (x, y) = opos.translate(pos.x1(), pos.y1());
stacked.node_render(self, x, y, None);
macro_rules! render_stacked {
($stack:expr) => {
for stacked in $stack.iter() {
if stacked.node_visible() {
self.base.ops.push(GfxApiOpt::Sync);
let pos = stacked.node_absolute_position();
if pos.intersects(&opos) {
let (x, y) = opos.translate(pos.x1(), pos.y1());
stacked.node_render(self, x, y, None);
}
}
}
}
};
}
render_stacked!(self.state.root.stacked);
render_layer!(output.layers[2]);
render_layer!(output.layers[3]);
render_stacked!(self.state.root.stacked_above_layers);
if let Some(ws) = output.workspace.get() {
if ws.render_highlight.get() > 0 {
let color = self.state.theme.colors.highlight.get();
Expand Down
Loading

0 comments on commit 52afd94

Please sign in to comment.