Skip to content

Commit 8b93492

Browse files
committed
wayland/session_lock: Move last_acked tracking to cached state
Same idea as with xdg-shell in the previous commit.
1 parent 41e5852 commit 8b93492

File tree

3 files changed

+163
-109
lines changed

3 files changed

+163
-109
lines changed

src/wayland/session_lock/lock.rs

Lines changed: 2 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33
use std::sync::atomic::{AtomicBool, Ordering};
44
use std::sync::{Arc, Mutex};
55

6-
use crate::backend::renderer::buffer_dimensions;
7-
use crate::utils::Size;
86
use crate::wayland::compositor::SurfaceAttributes;
97
use crate::wayland::compositor::{self, BufferAssignment};
10-
use crate::wayland::viewporter::{ViewportCachedState, ViewporterSurfaceState};
118
use _session_lock::ext_session_lock_surface_v1::ExtSessionLockSurfaceV1;
129
use _session_lock::ext_session_lock_v1::{Error, ExtSessionLockV1, Request};
13-
use wayland_protocols::ext::session_lock::v1::server::{self as _session_lock, ext_session_lock_surface_v1};
10+
use wayland_protocols::ext::session_lock::v1::server::{self as _session_lock};
1411
use wayland_server::{Client, DataInit, Dispatch, DisplayHandle, Resource};
1512

1613
use crate::wayland::session_lock::surface::{ExtLockSurfaceUserData, LockSurface, LockSurfaceAttributes};
@@ -101,75 +98,7 @@ where
10198
});
10299

103100
// Add pre-commit hook for updating surface state.
104-
compositor::add_pre_commit_hook::<D, _>(&surface, |_state, _dh, surface| {
105-
compositor::with_states(surface, |states| {
106-
let attributes = states.data_map.get::<Mutex<LockSurfaceAttributes>>();
107-
let attributes = attributes.unwrap().lock().unwrap();
108-
109-
let Some(state) = attributes.last_acked else {
110-
attributes.surface.post_error(
111-
ext_session_lock_surface_v1::Error::CommitBeforeFirstAck,
112-
"Committed before the first ack_configure.",
113-
);
114-
return;
115-
};
116-
117-
// Verify the attached buffer: ext-session-lock requires no NULL buffers
118-
// and an exact dimentions match.
119-
let mut guard = states.cached_state.get::<SurfaceAttributes>();
120-
let surface_attrs = guard.pending();
121-
if let Some(assignment) = surface_attrs.buffer.as_ref() {
122-
match assignment {
123-
BufferAssignment::Removed => {
124-
attributes.surface.post_error(
125-
ext_session_lock_surface_v1::Error::NullBuffer,
126-
"Surface attached a NULL buffer.",
127-
);
128-
}
129-
BufferAssignment::NewBuffer(buffer) => {
130-
if let Some(buf_size) = buffer_dimensions(buffer) {
131-
let viewport = states
132-
.data_map
133-
.get::<ViewporterSurfaceState>()
134-
.map(|v| v.lock().unwrap());
135-
let surface_size = if let Some(dest) =
136-
viewport.as_ref().and_then(|_| {
137-
let mut guard =
138-
states.cached_state.get::<ViewportCachedState>();
139-
let viewport_state = guard.pending();
140-
viewport_state.dst
141-
}) {
142-
Size::from((dest.w as u32, dest.h as u32))
143-
} else {
144-
let scale = surface_attrs.buffer_scale;
145-
let transform = surface_attrs.buffer_transform.into();
146-
let surface_size = buf_size.to_logical(scale, transform);
147-
148-
Size::from((surface_size.w as u32, surface_size.h as u32))
149-
};
150-
151-
if Some(surface_size) != state.size {
152-
attributes.surface.post_error(
153-
ext_session_lock_surface_v1::Error::DimensionsMismatch,
154-
"Surface dimensions do not match acked configure.",
155-
);
156-
}
157-
}
158-
}
159-
}
160-
}
161-
});
162-
});
163-
compositor::add_post_commit_hook::<D, _>(&surface, |_state, _dh, surface| {
164-
compositor::with_states(surface, |states| {
165-
let attributes = states.data_map.get::<Mutex<LockSurfaceAttributes>>();
166-
let mut attributes = attributes.unwrap().lock().unwrap();
167-
168-
if let Some(state) = attributes.last_acked {
169-
attributes.current = state;
170-
}
171-
});
172-
});
101+
compositor::add_pre_commit_hook::<D, _>(&surface, LockSurface::pre_commit_hook);
173102

174103
// Call compositor handler.
175104
let lock_surface = LockSurface::new(surface, lock_surface);

src/wayland/session_lock/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,14 @@ use wayland_server::protocol::wl_output::WlOutput;
5858
use wayland_server::protocol::wl_surface::WlSurface;
5959
use wayland_server::{Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New};
6060

61-
use crate::wayland::session_lock::surface::LockSurfaceConfigure;
62-
6361
mod lock;
6462
mod surface;
6563

6664
pub use lock::SessionLockState;
67-
pub use surface::{ExtLockSurfaceUserData, LockSurface, LockSurfaceState};
65+
pub use surface::{
66+
ExtLockSurfaceUserData, LockSurface, LockSurfaceAttributes, LockSurfaceCachedState, LockSurfaceConfigure,
67+
LockSurfaceData, LockSurfaceState,
68+
};
6869

6970
const MANAGER_VERSION: u32 = 1;
7071

src/wayland/session_lock/surface.rs

Lines changed: 157 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
33
use std::sync::Mutex;
44

5+
use crate::backend::renderer::buffer_dimensions;
56
use crate::utils::{IsAlive, Logical, Serial, Size, SERIAL_COUNTER};
6-
use crate::wayland::compositor;
7+
use crate::wayland::compositor::{self, BufferAssignment, Cacheable, SurfaceAttributes};
8+
use crate::wayland::viewporter::{ViewportCachedState, ViewporterSurfaceState};
79
use _session_lock::ext_session_lock_surface_v1::{Error, ExtSessionLockSurfaceV1, Request};
10+
use tracing::trace_span;
811
use wayland_protocols::ext::session_lock::v1::server::{self as _session_lock, ext_session_lock_surface_v1};
912
use wayland_server::protocol::wl_surface::WlSurface;
1013
use wayland_server::{Client, DataInit, Dispatch, DisplayHandle, Resource, Weak};
@@ -76,19 +79,33 @@ where
7679
.lock()
7780
.unwrap();
7881
attributes.reset();
82+
83+
let mut guard = states.cached_state.get::<LockSurfaceCachedState>();
84+
*guard.pending() = Default::default();
85+
*guard.current() = Default::default();
7986
});
8087
}
8188
}
8289
}
8390

91+
/// Data associated with session lock surface
92+
///
93+
/// ```no_run
94+
/// use smithay::wayland::compositor;
95+
/// use smithay::wayland::session_lock::LockSurfaceData;
96+
///
97+
/// # let wl_surface = todo!();
98+
/// compositor::with_states(&wl_surface, |states| {
99+
/// states.data_map.get::<LockSurfaceData>();
100+
/// });
101+
/// ```
102+
pub type LockSurfaceData = Mutex<LockSurfaceAttributes>;
103+
84104
/// Attributes for ext-session-lock surfaces.
85105
#[derive(Debug)]
86106
pub struct LockSurfaceAttributes {
87107
pub(crate) surface: ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
88108

89-
/// The serial of the last acked configure
90-
pub configure_serial: Option<Serial>,
91-
92109
/// Holds the pending state as set by the server.
93110
pub server_pending: Option<LockSurfaceState>,
94111

@@ -98,23 +115,19 @@ pub struct LockSurfaceAttributes {
98115
/// layer_surface.ack_configure.
99116
pub pending_configures: Vec<LockSurfaceConfigure>,
100117

101-
/// Holds the last server_pending state that has been acknowledged by the
102-
/// client. This state should be cloned to the current during a commit.
103-
pub last_acked: Option<LockSurfaceState>,
104-
105-
/// Holds the current state of the layer after a successful commit.
106-
pub current: LockSurfaceState,
118+
/// Holds the last configure that has been acknowledged by the client. This state should be
119+
/// cloned to the current during a commit. Note that this state can be newer than the last
120+
/// acked state at the time of the last commit.
121+
pub last_acked: Option<LockSurfaceConfigure>,
107122
}
108123

109124
impl LockSurfaceAttributes {
110125
pub(crate) fn new(surface: ext_session_lock_surface_v1::ExtSessionLockSurfaceV1) -> Self {
111126
Self {
112127
surface,
113-
configure_serial: None,
114128
server_pending: None,
115129
pending_configures: vec![],
116130
last_acked: None,
117-
current: Default::default(),
118131
}
119132
}
120133

@@ -127,18 +140,22 @@ impl LockSurfaceAttributes {
127140

128141
self.pending_configures
129142
.retain(|configure| configure.serial > serial);
130-
self.last_acked = Some(configure.state);
131-
self.configure_serial = Some(serial);
143+
self.last_acked = Some(configure);
132144

133145
Some(configure)
134146
}
135147

136148
fn reset(&mut self) {
137-
self.configure_serial = None;
138149
self.server_pending = None;
139150
self.pending_configures = Vec::new();
140151
self.last_acked = None;
141-
self.current = Default::default();
152+
}
153+
154+
fn current_server_state(&self) -> Option<&LockSurfaceState> {
155+
self.pending_configures
156+
.last()
157+
.map(|c| &c.state)
158+
.or(self.last_acked.as_ref().map(|c| &c.state))
142159
}
143160
}
144161

@@ -174,12 +191,8 @@ impl LockSurface {
174191
pub fn get_pending_state(&self, attributes: &mut LockSurfaceAttributes) -> Option<LockSurfaceState> {
175192
let server_pending = attributes.server_pending.take()?;
176193

177-
// Get the last pending state.
178-
let pending = attributes.pending_configures.last();
179-
let last_state = pending.map(|c| &c.state).or(attributes.last_acked.as_ref());
180-
181194
// Check if last state matches pending state.
182-
match last_state {
195+
match attributes.current_server_state() {
183196
Some(state) if state == &server_pending => None,
184197
_ => Some(server_pending),
185198
}
@@ -230,26 +243,119 @@ impl LockSurface {
230243
let mut attributes = attributes.unwrap().lock().unwrap();
231244

232245
// Ensure pending state is initialized.
233-
let current = attributes.current;
234-
let server_pending = attributes.server_pending.get_or_insert(current);
246+
if attributes.server_pending.is_none() {
247+
attributes.server_pending =
248+
Some(attributes.current_server_state().cloned().unwrap_or_default());
249+
}
235250

251+
let server_pending = attributes.server_pending.as_mut().unwrap();
236252
f(server_pending)
237253
})
238254
}
239255

240-
/// Get the current pending state.
241-
#[allow(unused)]
242-
pub fn current_state(&self) -> LockSurfaceState {
256+
/// Provides access to the current committed cached state.
257+
pub fn with_cached_state<F, T>(&self, f: F) -> T
258+
where
259+
F: FnOnce(&LockSurfaceCachedState) -> T,
260+
{
243261
compositor::with_states(&self.surface, |states| {
244-
states
245-
.data_map
246-
.get::<Mutex<LockSurfaceAttributes>>()
247-
.unwrap()
248-
.lock()
249-
.unwrap()
250-
.current
262+
let mut guard = states.cached_state.get::<LockSurfaceCachedState>();
263+
f(guard.current())
251264
})
252265
}
266+
267+
/// Provides access to the current committed state.
268+
///
269+
/// This is the state that the client last acked before making the current commit.
270+
pub fn with_committed_state<F, T>(&self, f: F) -> T
271+
where
272+
F: FnOnce(Option<&LockSurfaceState>) -> T,
273+
{
274+
self.with_cached_state(move |state| f(state.last_acked.as_ref().map(|c| &c.state)))
275+
}
276+
277+
/// Handles the role specific commit error checking
278+
///
279+
/// This should be called when the underlying WlSurface
280+
/// handles a wl_surface.commit request.
281+
pub(crate) fn pre_commit_hook<D: 'static>(_state: &mut D, _dh: &DisplayHandle, surface: &WlSurface) {
282+
let _span = trace_span!("session-lock-surface pre-commit", surface = %surface.id()).entered();
283+
284+
compositor::with_states(surface, |states| {
285+
let role = states.data_map.get::<LockSurfaceData>().unwrap().lock().unwrap();
286+
287+
let Some(last_acked) = role.last_acked else {
288+
role.surface.post_error(
289+
ext_session_lock_surface_v1::Error::CommitBeforeFirstAck,
290+
"Committed before the first ack_configure.",
291+
);
292+
return;
293+
};
294+
let LockSurfaceConfigure { state, serial: _ } = &last_acked;
295+
296+
let mut guard_layer = states.cached_state.get::<LockSurfaceCachedState>();
297+
let pending = guard_layer.pending();
298+
299+
// The presence of last_acked always follows the buffer assignment because the
300+
// surface is not allowed to attach a buffer without acking the initial configure.
301+
let had_buffer_before = pending.last_acked.is_some();
302+
303+
let mut guard_surface = states.cached_state.get::<SurfaceAttributes>();
304+
let surface_attrs = guard_surface.pending();
305+
let has_buffer = match &surface_attrs.buffer {
306+
Some(BufferAssignment::NewBuffer(buffer)) => {
307+
// Verify buffer size.
308+
if let Some(buf_size) = buffer_dimensions(buffer) {
309+
let viewport = states
310+
.data_map
311+
.get::<ViewporterSurfaceState>()
312+
.map(|v| v.lock().unwrap());
313+
let surface_size = if let Some(dest) = viewport.as_ref().and_then(|_| {
314+
let mut guard = states.cached_state.get::<ViewportCachedState>();
315+
let viewport_state = guard.pending();
316+
viewport_state.dst
317+
}) {
318+
Size::from((dest.w as u32, dest.h as u32))
319+
} else {
320+
let scale = surface_attrs.buffer_scale;
321+
let transform = surface_attrs.buffer_transform.into();
322+
let surface_size = buf_size.to_logical(scale, transform);
323+
324+
Size::from((surface_size.w as u32, surface_size.h as u32))
325+
};
326+
327+
if Some(surface_size) != state.size {
328+
role.surface.post_error(
329+
ext_session_lock_surface_v1::Error::DimensionsMismatch,
330+
"Surface dimensions do not match acked configure.",
331+
);
332+
return;
333+
}
334+
}
335+
336+
true
337+
}
338+
Some(BufferAssignment::Removed) => {
339+
role.surface.post_error(
340+
ext_session_lock_surface_v1::Error::NullBuffer,
341+
"Surface attached a NULL buffer.",
342+
);
343+
return;
344+
}
345+
None => had_buffer_before,
346+
};
347+
348+
if has_buffer {
349+
// The surface remains, or became mapped, track the last acked state.
350+
pending.last_acked = Some(last_acked);
351+
} else {
352+
// The surface remains unmapped, meaning that it's in the initial configure stage.
353+
pending.last_acked = None;
354+
}
355+
356+
// Lock surfaces aren't allowed to attach a null buffer, and therefore to unmap.
357+
});
358+
}
253359
}
254360

255361
/// State of an ext-session-lock surface.
@@ -277,3 +383,21 @@ impl LockSurfaceConfigure {
277383
}
278384
}
279385
}
386+
387+
/// Represents the client pending state
388+
#[derive(Debug, Default, Copy, Clone)]
389+
pub struct LockSurfaceCachedState {
390+
/// Configure last acknowledged by the client at the time of the commit.
391+
///
392+
/// Reset to `None` when the surface unmaps.
393+
pub last_acked: Option<LockSurfaceConfigure>,
394+
}
395+
396+
impl Cacheable for LockSurfaceCachedState {
397+
fn commit(&mut self, _dh: &DisplayHandle) -> Self {
398+
*self
399+
}
400+
fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
401+
*into = self;
402+
}
403+
}

0 commit comments

Comments
 (0)