1use crate::{compositor::Surface, error::GlobalError, globals::GlobalData, registry::GlobalProxy};
2use std::sync::{
3 atomic::{AtomicBool, Ordering},
4 Arc, Weak,
5};
6use wayland_client::{
7 globals::GlobalList,
8 protocol::{wl_output, wl_surface},
9 Connection, Dispatch, Proxy, QueueHandle,
10};
11use wayland_protocols::ext::session_lock::v1::client::{
12 ext_session_lock_manager_v1, ext_session_lock_surface_v1, ext_session_lock_v1,
13};
14
15mod dispatch;
16
17/// Handler trait for session lock protocol.
18pub trait SessionLockHandler: Sized {
19 /// The session lock is active, and the client may create lock surfaces.
20 fn locked(&mut self, conn: &Connection, qh: &QueueHandle<Self>, session_lock: SessionLock);
21
22 /// Session lock is not active and should be destroyed.
23 ///
24 /// This may be sent immediately if the compositor denys the requires to create a lock,
25 /// or may be sent some time after `lock`.
26 fn finished(&mut self, conn: &Connection, qh: &QueueHandle<Self>, session_lock: SessionLock);
27
28 /// Compositor has requested size for surface.
29 fn configure(
30 &mut self,
31 conn: &Connection,
32 qh: &QueueHandle<Self>,
33 surface: SessionLockSurface,
34 configure: SessionLockSurfaceConfigure,
35 serial: u32,
36 );
37}
38
39#[non_exhaustive]
40#[derive(Debug, Clone)]
41pub struct SessionLockSurfaceConfigure {
42 pub new_size: (u32, u32),
43}
44
45#[derive(Debug)]
46struct SessionLockSurfaceInner {
47 surface: Surface,
48 session_lock_surface: ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
49}
50
51impl Drop for SessionLockSurfaceInner {
52 fn drop(&mut self) {
53 self.session_lock_surface.destroy();
54 }
55}
56
57#[must_use]
58#[derive(Debug, Clone)]
59pub struct SessionLockSurface(Arc<SessionLockSurfaceInner>);
60
61impl SessionLockSurface {
62 pub fn from_ext_session_lock_surface(
63 surface: &ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
64 ) -> Option<Self> {
65 surface.data::<SessionLockSurfaceData>().and_then(|data: &SessionLockSurfaceData| data.inner.upgrade()).map(Self)
66 }
67
68 pub fn wl_surface(&self) -> &wl_surface::WlSurface {
69 self.0.surface.wl_surface()
70 }
71}
72
73#[derive(Debug)]
74pub struct SessionLockSurfaceData {
75 inner: Weak<SessionLockSurfaceInner>,
76}
77
78impl SessionLockSurfaceData {
79 pub fn session_lock_surface(&self) -> Option<SessionLockSurface> {
80 self.inner.upgrade().map(SessionLockSurface)
81 }
82}
83
84#[derive(Debug)]
85pub struct SessionLockInner {
86 session_lock: ext_session_lock_v1::ExtSessionLockV1,
87 locked: AtomicBool,
88}
89
90impl Drop for SessionLockInner {
91 fn drop(&mut self) {
92 if self.locked.load(order:Ordering::SeqCst) {
93 self.session_lock.unlock_and_destroy();
94 } else {
95 self.session_lock.destroy();
96 }
97 }
98}
99
100/// A session lock
101///
102/// The lock is destroyed on drop, which must be done after `locked` or `finished`
103/// is received.
104#[must_use]
105#[derive(Debug, Clone)]
106pub struct SessionLock(Arc<SessionLockInner>);
107
108impl SessionLock {
109 pub fn from_ext_session_lock(surface: &ext_session_lock_v1::ExtSessionLockV1) -> Option<Self> {
110 surface.data::<SessionLockData>().and_then(|data: &SessionLockData| data.inner.upgrade()).map(Self)
111 }
112
113 pub fn is_locked(&self) -> bool {
114 self.0.locked.load(order:Ordering::SeqCst)
115 }
116}
117
118#[derive(Debug)]
119pub struct SessionLockData {
120 inner: Weak<SessionLockInner>,
121}
122
123impl SessionLock {
124 pub fn create_lock_surface<D>(
125 &self,
126 surface: impl Into<Surface>,
127 output: &wl_output::WlOutput,
128 qh: &QueueHandle<D>,
129 ) -> SessionLockSurface
130 where
131 D: Dispatch<ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, SessionLockSurfaceData>
132 + 'static,
133 {
134 // Freeze the queue during the creation of the Arc to avoid a race between events on the
135 // new objects being processed and the Weak in the SessionLockSurfaceData becoming usable.
136 let freeze = qh.freeze();
137 let surface = surface.into();
138
139 let inner = Arc::new_cyclic(|weak| {
140 let session_lock_surface = self.0.session_lock.get_lock_surface(
141 surface.wl_surface(),
142 output,
143 qh,
144 SessionLockSurfaceData { inner: weak.clone() },
145 );
146
147 SessionLockSurfaceInner { surface, session_lock_surface }
148 });
149 drop(freeze);
150
151 SessionLockSurface(inner)
152 }
153}
154
155/// A handler for [`ext_session_lock_manager_v1::ExtSessionLockManagerV1`]
156#[derive(Debug)]
157pub struct SessionLockState {
158 session_lock_manager: GlobalProxy<ext_session_lock_manager_v1::ExtSessionLockManagerV1>,
159}
160
161impl SessionLockState {
162 pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
163 where
164 D: Dispatch<ext_session_lock_manager_v1::ExtSessionLockManagerV1, GlobalData> + 'static,
165 {
166 let session_lock_manager = GlobalProxy::from(globals.bind(qh, 1..=1, GlobalData));
167 Self { session_lock_manager }
168 }
169
170 pub fn lock<D>(&self, qh: &QueueHandle<D>) -> Result<SessionLock, GlobalError>
171 where
172 D: Dispatch<ext_session_lock_v1::ExtSessionLockV1, SessionLockData> + 'static,
173 {
174 let session_lock_manager = self.session_lock_manager.get()?;
175
176 // Freeze the queue during the creation of the Arc to avoid a race between events on the
177 // new objects being processed and the Weak in the SessionLockData becoming usable.
178 let freeze = qh.freeze();
179
180 let inner = Arc::new_cyclic(|weak| {
181 let session_lock =
182 session_lock_manager.lock(qh, SessionLockData { inner: weak.clone() });
183
184 SessionLockInner { session_lock, locked: AtomicBool::new(false) }
185 });
186 drop(freeze);
187
188 Ok(SessionLock(inner))
189 }
190}
191
192#[macro_export]
193macro_rules! delegate_session_lock {
194 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
195 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
196 [
197 $crate::reexports::protocols::ext::session_lock::v1::client::ext_session_lock_manager_v1::ExtSessionLockManagerV1: $crate::globals::GlobalData
198 ] => $crate::session_lock::SessionLockState
199 );
200 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
201 [
202 $crate::reexports::protocols::ext::session_lock::v1::client::ext_session_lock_v1::ExtSessionLockV1: $crate::session_lock::SessionLockData
203 ] => $crate::session_lock::SessionLockState
204 );
205 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
206 [
207 $crate::reexports::protocols::ext::session_lock::v1::client::ext_session_lock_surface_v1::ExtSessionLockSurfaceV1: $crate::session_lock::SessionLockSurfaceData
208 ] => $crate::session_lock::SessionLockState
209 );
210 };
211}
212