2
2
3
3
use std:: sync:: Mutex ;
4
4
5
+ use crate :: backend:: renderer:: buffer_dimensions;
5
6
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 } ;
7
9
use _session_lock:: ext_session_lock_surface_v1:: { Error , ExtSessionLockSurfaceV1 , Request } ;
10
+ use tracing:: trace_span;
8
11
use wayland_protocols:: ext:: session_lock:: v1:: server:: { self as _session_lock, ext_session_lock_surface_v1} ;
9
12
use wayland_server:: protocol:: wl_surface:: WlSurface ;
10
13
use wayland_server:: { Client , DataInit , Dispatch , DisplayHandle , Resource , Weak } ;
@@ -76,19 +79,33 @@ where
76
79
. lock ( )
77
80
. unwrap ( ) ;
78
81
attributes. reset ( ) ;
82
+
83
+ let mut guard = states. cached_state . get :: < LockSurfaceCachedState > ( ) ;
84
+ * guard. pending ( ) = Default :: default ( ) ;
85
+ * guard. current ( ) = Default :: default ( ) ;
79
86
} ) ;
80
87
}
81
88
}
82
89
}
83
90
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
+
84
104
/// Attributes for ext-session-lock surfaces.
85
105
#[ derive( Debug ) ]
86
106
pub struct LockSurfaceAttributes {
87
107
pub ( crate ) surface : ext_session_lock_surface_v1:: ExtSessionLockSurfaceV1 ,
88
108
89
- /// The serial of the last acked configure
90
- pub configure_serial : Option < Serial > ,
91
-
92
109
/// Holds the pending state as set by the server.
93
110
pub server_pending : Option < LockSurfaceState > ,
94
111
@@ -98,23 +115,19 @@ pub struct LockSurfaceAttributes {
98
115
/// layer_surface.ack_configure.
99
116
pub pending_configures : Vec < LockSurfaceConfigure > ,
100
117
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 > ,
107
122
}
108
123
109
124
impl LockSurfaceAttributes {
110
125
pub ( crate ) fn new ( surface : ext_session_lock_surface_v1:: ExtSessionLockSurfaceV1 ) -> Self {
111
126
Self {
112
127
surface,
113
- configure_serial : None ,
114
128
server_pending : None ,
115
129
pending_configures : vec ! [ ] ,
116
130
last_acked : None ,
117
- current : Default :: default ( ) ,
118
131
}
119
132
}
120
133
@@ -127,18 +140,22 @@ impl LockSurfaceAttributes {
127
140
128
141
self . pending_configures
129
142
. retain ( |configure| configure. serial > serial) ;
130
- self . last_acked = Some ( configure. state ) ;
131
- self . configure_serial = Some ( serial) ;
143
+ self . last_acked = Some ( configure) ;
132
144
133
145
Some ( configure)
134
146
}
135
147
136
148
fn reset ( & mut self ) {
137
- self . configure_serial = None ;
138
149
self . server_pending = None ;
139
150
self . pending_configures = Vec :: new ( ) ;
140
151
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 ) )
142
159
}
143
160
}
144
161
@@ -174,12 +191,8 @@ impl LockSurface {
174
191
pub fn get_pending_state ( & self , attributes : & mut LockSurfaceAttributes ) -> Option < LockSurfaceState > {
175
192
let server_pending = attributes. server_pending . take ( ) ?;
176
193
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
-
181
194
// Check if last state matches pending state.
182
- match last_state {
195
+ match attributes . current_server_state ( ) {
183
196
Some ( state) if state == & server_pending => None ,
184
197
_ => Some ( server_pending) ,
185
198
}
@@ -230,26 +243,119 @@ impl LockSurface {
230
243
let mut attributes = attributes. unwrap ( ) . lock ( ) . unwrap ( ) ;
231
244
232
245
// 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
+ }
235
250
251
+ let server_pending = attributes. server_pending . as_mut ( ) . unwrap ( ) ;
236
252
f ( server_pending)
237
253
} )
238
254
}
239
255
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
+ {
243
261
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 ( ) )
251
264
} )
252
265
}
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
+ }
253
359
}
254
360
255
361
/// State of an ext-session-lock surface.
@@ -277,3 +383,21 @@ impl LockSurfaceConfigure {
277
383
}
278
384
}
279
385
}
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