1
0
Fork 0
forked from wry/wry

layer-shell: implement exclusive zones

This commit is contained in:
Julian Orth 2024-05-07 20:19:41 +02:00
parent 8dc31110b9
commit 8a91c070be
9 changed files with 300 additions and 90 deletions

View file

@ -127,7 +127,7 @@ Jay's supports leasing VR headsets to applications.
Jay supports the following wayland protocols:
| Global | Version | Privileged |
|-----------------------------------------|:-----------------|---------------|
|-----------------------------------------|:----------------|---------------|
| ext_foreign_toplevel_list_v1 | 1 | Yes |
| ext_idle_notifier_v1 | 1 | Yes |
| ext_session_lock_manager_v1 | 1 | Yes |
@ -156,7 +156,7 @@ Jay supports the following wayland protocols:
| xdg_wm_base | 6 | |
| xdg_wm_dialog_v1 | 1 | |
| zwlr_data_control_manager_v1 | 2 | Yes |
| zwlr_layer_shell_v1 | 4[^no_exclusive] | No[^lsaccess] |
| zwlr_layer_shell_v1 | 5 | No[^lsaccess] |
| zwlr_screencopy_manager_v1 | 3 | Yes |
| zwp_idle_inhibit_manager_v1 | 1 | |
| zwp_input_method_manager_v2 | 1 | Yes |
@ -173,7 +173,6 @@ Jay supports the following wayland protocols:
[^no_touch]: Touch input is not supported.
[^no_tearing]: Tearing screen updates are not supported.
[^no_exclusive]: Exclusive zones are not supported.
[^lsaccess]: Sandboxes can restrict access to this protocol.
[^ts_rejected]: Seat creation is always rejected.

View file

@ -449,6 +449,10 @@ fn create_dummy_output(state: &Rc<State>) {
workspace: Default::default(),
seat_state: Default::default(),
layers: Default::default(),
exclusive_zones: Default::default(),
workspace_rect: Default::default(),
non_exclusive_rect_rel: Default::default(),
non_exclusive_rect: Default::default(),
render_data: Default::default(),
state: state.clone(),
is_dummy: true,

View file

@ -52,22 +52,59 @@ pub struct ZwlrLayerSurfaceV1 {
requested_serial: NumCell<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>,
}
#[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),
}
#[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>,
exclusive_edge: Option<u32>,
}
impl PendingLayerSurfaceData {
@ -113,12 +150,14 @@ impl ZwlrLayerSurfaceV1 {
requested_serial: Default::default(),
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(),
}
}
@ -181,7 +220,16 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let mut pending = self.pending();
pending.exclusive_zone = Some(req.zone);
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(())
}
@ -234,9 +282,73 @@ impl ZwlrLayerSurfaceV1RequestHandler for ZwlrLayerSurfaceV1 {
pending.layer = Some(req.layer);
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 {
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();
if let Some(size) = pending.size.take() {
@ -257,6 +369,9 @@ impl ZwlrLayerSurfaceV1 {
if let Some(layer) = pending.layer.take() {
self.layer.set(layer);
}
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) {
@ -265,17 +380,25 @@ impl ZwlrLayerSurfaceV1 {
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);
}
}
self.configure();
Ok(())
}
fn configure(&self) {
let Some(global) = self.output.get() else {
let Some(node) = self.output.node() else {
return;
};
let (mut width, mut height) = self.size.get();
let (mt, mr, mb, ml) = self.margin.get();
let (mut available_width, mut available_height) = global.position().size();
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;
@ -308,7 +431,7 @@ impl ZwlrLayerSurfaceV1 {
}
fn compute_position(&self) {
let Some(global) = self.output.get() else {
let Some(output) = self.output.node() else {
return;
};
let extents = self.surface.extents.get();
@ -318,8 +441,12 @@ impl ZwlrLayerSurfaceV1 {
anchor = LEFT | RIGHT | TOP | BOTTOM;
}
let (mt, mr, mb, ml) = self.margin.get();
let opos = global.pos.get();
let (owidth, oheight) = opos.size();
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 | RIGHT) {
@ -336,8 +463,8 @@ impl ZwlrLayerSurfaceV1 {
} else if anchor.contains(BOTTOM) {
y1 = oheight - height - mb;
}
let o_rect = Rect::new_sized(x1, y1, width, height).unwrap();
let a_rect = o_rect.move_(opos.x1(), opos.y1());
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 abs_x = a_rect.x1() - extents.x1();
@ -351,6 +478,13 @@ impl ZwlrLayerSurfaceV1 {
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);
@ -358,6 +492,11 @@ impl ZwlrLayerSurfaceV1 {
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();
}
}
}
}
@ -383,12 +522,14 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
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();
@ -500,6 +641,12 @@ pub enum ZwlrLayerSurfaceV1Error {
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,
}
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

@ -86,6 +86,11 @@ impl Rect {
Self::new(x1, y1, x1 + width, y1 + height)
}
#[track_caller]
pub fn new_sized_unchecked(x1: i32, y1: i32, width: i32, height: i32) -> Self {
Self::new_sized(x1, y1, width, height).unwrap()
}
pub fn union(&self, other: Self) -> Self {
Self {
raw: RectRaw {

View file

@ -120,10 +120,14 @@ impl Renderer<'_> {
} else {
render_layer!(output.layers[0]);
render_layer!(output.layers[1]);
let non_exclusive_rect = output.non_exclusive_rect_rel.get();
let (x, y) = non_exclusive_rect.translate_inv(x, y);
{
let c = theme.colors.bar_background.get();
self.base.fill_boxes2(
slice::from_ref(&Rect::new_sized(0, 0, opos.width(), th).unwrap()),
slice::from_ref(
&Rect::new_sized(0, 0, non_exclusive_rect.width(), th).unwrap(),
),
&c,
x,
y,

View file

@ -147,6 +147,10 @@ impl ConnectorHandler {
seat_state: Default::default(),
global: global.clone(),
layers: Default::default(),
exclusive_zones: Default::default(),
workspace_rect: Default::default(),
non_exclusive_rect: Default::default(),
non_exclusive_rect_rel: Default::default(),
render_data: RefCell::new(OutputRenderData {
active_workspace: None,
underline: Default::default(),
@ -169,6 +173,7 @@ impl ConnectorHandler {
hardware_cursor_needs_render: Cell::new(false),
screencopies: Default::default(),
});
on.update_rects();
self.state
.add_output_scale(on.global.persistent.scale.get());
let output_data = Rc::new(OutputData {

View file

@ -18,8 +18,8 @@ use {
},
wl_surface::{
ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, SurfaceSendPreferredScaleVisitor,
SurfaceSendPreferredTransformVisitor,
zwlr_layer_surface_v1::{ExclusiveSize, ZwlrLayerSurfaceV1},
SurfaceSendPreferredScaleVisitor, SurfaceSendPreferredTransformVisitor,
},
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
@ -47,7 +47,7 @@ use {
std::{
cell::{Cell, RefCell},
fmt::{Debug, Formatter},
ops::{Deref, Sub},
ops::Deref,
rc::Rc,
},
};
@ -61,6 +61,10 @@ pub struct OutputNode {
pub workspace: CloneCell<Option<Rc<WorkspaceNode>>>,
pub seat_state: NodeSeatState,
pub layers: [LinkedList<Rc<ZwlrLayerSurfaceV1>>; 4],
pub exclusive_zones: Cell<ExclusiveSize>,
pub workspace_rect: Cell<Rect>,
pub non_exclusive_rect: Cell<Rect>,
pub non_exclusive_rect_rel: Cell<Rect>,
pub render_data: RefCell<OutputRenderData>,
pub state: Rc<State>,
pub is_dummy: bool,
@ -94,6 +98,26 @@ pub async fn output_render_data(state: Rc<State>) {
}
impl OutputNode {
pub fn update_exclusive_zones(self: &Rc<Self>) {
let mut exclusive = ExclusiveSize::default();
for layer in &self.layers {
for surface in layer.iter() {
exclusive = exclusive.max(&surface.exclusive_size());
}
}
if self.exclusive_zones.replace(exclusive) != exclusive {
self.update_rects();
for layer in &self.layers {
for surface in layer.iter() {
surface.exclusive_zones_changed();
}
}
if let Some(c) = self.workspace.get() {
c.change_extents(&self.workspace_rect.get());
}
}
}
pub fn add_screencast(&self, sc: &Rc<JayScreencast>) {
self.screencasts.set((sc.client.id, sc.id), sc.clone());
self.screencast_changed();
@ -222,9 +246,9 @@ impl OutputNode {
}
pub fn on_spaces_changed(self: &Rc<Self>) {
self.schedule_update_render_data();
self.update_rects();
if let Some(c) = self.workspace.get() {
c.change_extents(&self.workspace_rect());
c.change_extents(&self.workspace_rect.get());
}
}
@ -281,7 +305,7 @@ impl OutputNode {
texture_height = (th as f64 * scale).round() as _;
}
let active_id = self.workspace.get().map(|w| w.id);
let output_width = self.global.pos.get().width();
let output_width = self.non_exclusive_rect.get().width();
rd.underline = Rect::new_sized(0, th, output_width, 1).unwrap();
for ws in self.workspaces.iter() {
let old_tex = ws.title_texture.take();
@ -430,7 +454,7 @@ impl OutputNode {
if let Some(fs) = ws.fullscreen.get() {
fs.tl_change_extents(&self.global.pos.get());
}
ws.change_extents(&self.workspace_rect());
ws.change_extents(&self.workspace_rect.get());
for seat in seats {
ws.clone().node_do_focus(&seat, Direction::Unspecified);
}
@ -478,16 +502,29 @@ impl OutputNode {
ws
}
fn workspace_rect(&self) -> Rect {
pub fn update_rects(self: &Rc<Self>) {
let rect = self.global.pos.get();
let th = self.state.theme.sizes.title_height.get();
Rect::new_sized(
rect.x1(),
rect.y1() + th + 1,
rect.width(),
rect.height().sub(th + 1).max(0),
)
.unwrap()
let exclusive = self.exclusive_zones.get();
let y1 = rect.y1() + exclusive.top;
let x2 = rect.x2() - exclusive.right;
let y2 = rect.y2() - exclusive.bottom;
let x1 = rect.x1() + exclusive.left;
let width = (x2 - x1).max(0);
let height = (y2 - y1).max(0);
self.non_exclusive_rect
.set(Rect::new_sized_unchecked(x1, y1, width, height));
self.non_exclusive_rect_rel.set(Rect::new_sized_unchecked(
exclusive.left,
exclusive.top,
width,
height,
));
let y1 = y1 + th + 1;
let height = (y2 - y1).max(0);
self.workspace_rect
.set(Rect::new_sized_unchecked(x1, y1, width, height));
self.schedule_update_render_data();
}
pub fn set_position(self: &Rc<Self>, x: i32, y: i32) {
@ -546,7 +583,7 @@ impl OutputNode {
self.global.persistent.pos.set((rect.x1(), rect.y1()));
self.global.pos.set(*rect);
self.state.root.update_extents();
self.schedule_update_render_data();
self.update_rects();
if let Some(ls) = self.lock_surface.get() {
ls.change_extents(*rect);
}
@ -554,7 +591,7 @@ impl OutputNode {
if let Some(fs) = c.fullscreen.get() {
fs.tl_change_extents(rect);
}
c.change_extents(&self.workspace_rect());
c.change_extents(&self.workspace_rect.get());
}
for layer in &self.layers {
for surface in layer.iter() {
@ -657,6 +694,7 @@ impl OutputNode {
Some(p) => p,
_ => return,
};
let (x, y) = self.non_exclusive_rect_rel.get().translate(x, y);
if y >= self.state.theme.sizes.title_height.get() {
return;
}
@ -844,6 +882,9 @@ impl Node for OutputNode {
fs.tl_as_node().node_find_tree_at(x, y, tree, usecase)
} else {
let mut search_layers = true;
let non_exclusive_rect = self.non_exclusive_rect_rel.get();
if non_exclusive_rect.contains(x, y) {
let (x, y) = non_exclusive_rect.translate(x, y);
if y < bar_height {
search_layers = false;
} else {
@ -863,6 +904,7 @@ impl Node for OutputNode {
}
}
}
}
if search_layers {
self.find_layer_surface_at(x, y, &[BOTTOM, BACKGROUND], tree, usecase);
}

View file

@ -38,6 +38,10 @@ request set_layer (since = 2) {
layer: u32,
}
request set_exclusive_edge (since = 5) {
edge: u32,
}
# events
event configure {