Skip to content

Commit 3eac833

Browse files
committed
wayland/shell/layer: Move last_acked tracking to cached state
Same idea as with xdg-shell in the previous commit.
1 parent 21a554b commit 3eac833

File tree

2 files changed

+116
-115
lines changed

2 files changed

+116
-115
lines changed

src/wayland/shell/wlr_layer/handlers.rs

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -122,49 +122,10 @@ where
122122
});
123123

124124
if initial {
125-
compositor::add_pre_commit_hook::<D, _>(&wl_surface, |_state, _dh, surface| {
126-
compositor::with_states(surface, |states| {
127-
let guard = states
128-
.data_map
129-
.get::<Mutex<LayerSurfaceAttributes>>()
130-
.unwrap()
131-
.lock()
132-
.unwrap();
133-
134-
let mut cached_guard = states.cached_state.get::<LayerSurfaceCachedState>();
135-
let pending = cached_guard.pending();
136-
137-
if pending.size.w == 0 && !pending.anchor.anchored_horizontally() {
138-
guard.surface.post_error(
139-
zwlr_layer_surface_v1::Error::InvalidSize,
140-
"width 0 requested without setting left and right anchors",
141-
);
142-
return;
143-
}
144-
145-
if pending.size.h == 0 && !pending.anchor.anchored_vertically() {
146-
guard.surface.post_error(
147-
zwlr_layer_surface_v1::Error::InvalidSize,
148-
"height 0 requested without setting top and bottom anchors",
149-
);
150-
}
151-
});
152-
});
153-
154-
compositor::add_post_commit_hook::<D, _>(&wl_surface, |_state, _dh, surface| {
155-
compositor::with_states(surface, |states| {
156-
let mut guard = states
157-
.data_map
158-
.get::<Mutex<LayerSurfaceAttributes>>()
159-
.unwrap()
160-
.lock()
161-
.unwrap();
162-
163-
if let Some(state) = guard.last_acked.clone() {
164-
guard.current = state;
165-
}
166-
});
167-
});
125+
compositor::add_pre_commit_hook::<D, _>(
126+
&wl_surface,
127+
super::LayerSurface::pre_commit_hook,
128+
);
168129
}
169130

170131
let handle = super::LayerSurface {

src/wayland/shell/wlr_layer/mod.rs

Lines changed: 112 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,20 @@
4747
4848
use std::sync::{Arc, Mutex};
4949

50+
use tracing::{trace, trace_span};
5051
use wayland_protocols_wlr::layer_shell::v1::server::{
51-
zwlr_layer_shell_v1::{self, ZwlrLayerShellV1},
52-
zwlr_layer_surface_v1,
52+
zwlr_layer_shell_v1::ZwlrLayerShellV1, zwlr_layer_surface_v1,
5353
};
5454
use wayland_server::{
5555
backend::GlobalId,
5656
protocol::{wl_output::WlOutput, wl_surface},
57-
Client, DisplayHandle, GlobalDispatch, Resource,
57+
Client, DisplayHandle, GlobalDispatch, Resource as _,
5858
};
5959

6060
use crate::{
6161
utils::{alive_tracker::IsAlive, Logical, Serial, Size, SERIAL_COUNTER},
6262
wayland::{
63-
compositor::{self, Cacheable},
63+
compositor::{self, BufferAssignment, Cacheable, SurfaceAttributes},
6464
shell::xdg,
6565
},
6666
};
@@ -91,11 +91,6 @@ pub type LayerSurfaceData = Mutex<LayerSurfaceAttributes>;
9191
#[derive(Debug)]
9292
pub struct LayerSurfaceAttributes {
9393
surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
94-
/// Defines if the surface has received at least one
95-
/// layer_surface.ack_configure from the client
96-
pub configured: bool,
97-
/// The serial of the last acked configure
98-
pub configure_serial: Option<Serial>,
9994
/// Holds the state if the surface has sent the initial
10095
/// configure event to the client. It is expected that
10196
/// during the first commit a initial
@@ -109,26 +104,20 @@ pub struct LayerSurfaceAttributes {
109104
pending_configures: Vec<LayerSurfaceConfigure>,
110105
/// Holds the pending state as set by the server.
111106
pub server_pending: Option<LayerSurfaceState>,
112-
/// Holds the last server_pending state that has been acknowledged
113-
/// by the client. This state should be cloned to the current
114-
/// during a commit.
115-
pub last_acked: Option<LayerSurfaceState>,
116-
/// Holds the current state of the layer after a successful
117-
/// commit.
118-
pub current: LayerSurfaceState,
107+
/// Holds the last configure that has been acknowledged by the client. This state should be
108+
/// cloned to the current during a commit. Note that this state can be newer than the last
109+
/// acked state at the time of the last commit.
110+
pub last_acked: Option<LayerSurfaceConfigure>,
119111
}
120112

121113
impl LayerSurfaceAttributes {
122114
fn new(surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1) -> Self {
123115
Self {
124116
surface,
125-
configured: false,
126-
configure_serial: None,
127117
initial_configure_sent: false,
128118
pending_configures: Vec::new(),
129119
server_pending: None,
130120
last_acked: None,
131-
current: Default::default(),
132121
}
133122
}
134123

@@ -139,36 +128,32 @@ impl LayerSurfaceAttributes {
139128
.find(|configure| configure.serial == serial)
140129
.cloned()?;
141130

142-
self.last_acked = Some(configure.state.clone());
131+
self.last_acked = Some(configure.clone());
143132

144-
self.configured = true;
145-
self.configure_serial = Some(serial);
146133
self.pending_configures.retain(|c| c.serial > serial);
147134
Some(configure)
148135
}
149136

150137
fn reset(&mut self) {
151-
self.configured = false;
152-
self.configure_serial = None;
153138
self.initial_configure_sent = false;
154139
self.pending_configures = Vec::new();
155140
self.server_pending = None;
156141
self.last_acked = None;
157-
self.current = Default::default();
158142
}
159143

160-
fn current_server_state(&self) -> &LayerSurfaceState {
144+
fn current_server_state(&self) -> LayerSurfaceState {
161145
self.pending_configures
162146
.last()
163147
.map(|c| &c.state)
164-
.or(self.last_acked.as_ref())
165-
.unwrap_or(&self.current)
148+
.or(self.last_acked.as_ref().map(|c| &c.state))
149+
.cloned()
150+
.unwrap_or_default()
166151
}
167152

168153
fn has_pending_changes(&self) -> bool {
169154
self.server_pending
170155
.as_ref()
171-
.map(|s| s != self.current_server_state())
156+
.map(|s| *s != self.current_server_state())
172157
.unwrap_or(false)
173158
}
174159
}
@@ -181,7 +166,7 @@ pub struct LayerSurfaceState {
181166
}
182167

183168
/// Represents the client pending state
184-
#[derive(Debug, Default, Clone, Copy)]
169+
#[derive(Debug, Default, Clone)]
185170
pub struct LayerSurfaceCachedState {
186171
/// The size requested by the client
187172
pub size: Size<i32, Logical>,
@@ -195,11 +180,15 @@ pub struct LayerSurfaceCachedState {
195180
pub keyboard_interactivity: KeyboardInteractivity,
196181
/// The layer that the surface is rendered on
197182
pub layer: Layer,
183+
/// Configure last acknowledged by the client at the time of the commit.
184+
///
185+
/// Reset to `None` when the surface unmaps.
186+
pub last_acked: Option<LayerSurfaceConfigure>,
198187
}
199188

200189
impl Cacheable for LayerSurfaceCachedState {
201190
fn commit(&mut self, _dh: &DisplayHandle) -> Self {
202-
*self
191+
self.clone()
203192
}
204193
fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
205194
*into = self;
@@ -398,30 +387,6 @@ impl LayerSurface {
398387
serial
399388
}
400389

401-
/// Make sure this surface was configured
402-
///
403-
/// Returns `true` if it was, if not, returns `false` and raise
404-
/// a protocol error to the associated layer surface. Also returns `false`
405-
/// if the surface is already destroyed.
406-
pub fn ensure_configured(&self) -> bool {
407-
let configured = compositor::with_states(&self.wl_surface, |states| {
408-
states
409-
.data_map
410-
.get::<Mutex<LayerSurfaceAttributes>>()
411-
.unwrap()
412-
.lock()
413-
.unwrap()
414-
.configured
415-
});
416-
if !configured {
417-
self.shell_surface.post_error(
418-
zwlr_layer_shell_v1::Error::AlreadyConstructed,
419-
"layer_surface has never been configured",
420-
);
421-
}
422-
configured
423-
}
424-
425390
/// Send a "close" event to the client
426391
pub fn send_close(&self) {
427392
self.shell_surface.closed()
@@ -479,23 +444,6 @@ impl LayerSurface {
479444
})
480445
}
481446

482-
/// Gets a copy of the current state of this layer
483-
///
484-
/// Returns `None` if the underlying surface has been
485-
/// destroyed
486-
pub fn current_state(&self) -> LayerSurfaceState {
487-
compositor::with_states(&self.wl_surface, |states| {
488-
let attributes = states
489-
.data_map
490-
.get::<Mutex<LayerSurfaceAttributes>>()
491-
.unwrap()
492-
.lock()
493-
.unwrap();
494-
495-
attributes.current.clone()
496-
})
497-
}
498-
499447
/// Provides access to the current committed cached state.
500448
pub fn with_cached_state<F, T>(&self, f: F) -> T
501449
where
@@ -507,6 +455,98 @@ impl LayerSurface {
507455
})
508456
}
509457

458+
/// Provides access to the current committed state.
459+
///
460+
/// This is the state that the client last acked before making the current commit.
461+
pub fn with_committed_state<F, T>(&self, f: F) -> T
462+
where
463+
F: FnOnce(Option<&LayerSurfaceState>) -> T,
464+
{
465+
self.with_cached_state(move |state| f(state.last_acked.as_ref().map(|c| &c.state)))
466+
}
467+
468+
/// Handles the role specific commit error checking
469+
///
470+
/// This should be called when the underlying WlSurface
471+
/// handles a wl_surface.commit request.
472+
pub(crate) fn pre_commit_hook<D: 'static>(
473+
_state: &mut D,
474+
_dh: &DisplayHandle,
475+
surface: &wl_surface::WlSurface,
476+
) {
477+
let _span = trace_span!("layer-surface pre-commit", surface = %surface.id()).entered();
478+
479+
compositor::with_states(surface, |states| {
480+
let mut role = states.data_map.get::<LayerSurfaceData>().unwrap().lock().unwrap();
481+
482+
let mut guard_layer = states.cached_state.get::<LayerSurfaceCachedState>();
483+
let pending = guard_layer.pending();
484+
485+
if pending.size.w == 0 && !pending.anchor.anchored_horizontally() {
486+
role.surface.post_error(
487+
zwlr_layer_surface_v1::Error::InvalidSize,
488+
"width 0 requested without setting left and right anchors",
489+
);
490+
return;
491+
}
492+
493+
if pending.size.h == 0 && !pending.anchor.anchored_vertically() {
494+
role.surface.post_error(
495+
zwlr_layer_surface_v1::Error::InvalidSize,
496+
"height 0 requested without setting top and bottom anchors",
497+
);
498+
return;
499+
}
500+
501+
// The presence of last_acked always follows the buffer assignment because the
502+
// surface is not allowed to attach a buffer without acking the initial configure.
503+
let had_buffer_before = pending.last_acked.is_some();
504+
505+
let mut guard_surface = states.cached_state.get::<SurfaceAttributes>();
506+
let has_buffer = match &guard_surface.pending().buffer {
507+
Some(BufferAssignment::NewBuffer(_)) => true,
508+
Some(BufferAssignment::Removed) => false,
509+
None => had_buffer_before,
510+
};
511+
// Need to check had_buffer_before in case the client attaches a null buffer for the
512+
// initial commit---we don't want to consider that as "got unmapped" and reset role.
513+
// Reproducer: waybar.
514+
let got_unmapped = had_buffer_before && !has_buffer;
515+
516+
if has_buffer {
517+
let Some(last_acked) = role.last_acked.clone() else {
518+
role.surface.post_error(
519+
zwlr_layer_surface_v1::Error::InvalidSurfaceState,
520+
"must ack the initial configure before attaching buffer",
521+
);
522+
return;
523+
};
524+
525+
// The surface remains, or became mapped, track the last acked state.
526+
pending.last_acked = Some(last_acked);
527+
} else {
528+
// The surface remains, or became, unmapped, meaning that it's in the initial
529+
// configure stage.
530+
pending.last_acked = None;
531+
}
532+
533+
if got_unmapped {
534+
trace!(
535+
"got unmapped; resetting role and cached state; have {} pending configures",
536+
role.pending_configures.len()
537+
);
538+
539+
// All attributes are discarded when an xdg_surface is unmapped. Though, we keep
540+
// the list of pending configures because there's no way for a surface to tell
541+
// an in-flight configure apart from our next initial configure after unmapping.
542+
let pending_configures = std::mem::take(&mut role.pending_configures);
543+
role.reset();
544+
role.pending_configures = pending_configures;
545+
*guard_layer.pending() = Default::default();
546+
}
547+
});
548+
}
549+
510550
/// Access the underlying `zwlr_layer_surface_v1` of this layer surface
511551
///
512552
pub fn shell_surface(&self) -> &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1 {

0 commit comments

Comments
 (0)