| 1 | use crate::{compositor::Surface, error::GlobalError, globals::GlobalData, registry::GlobalProxy}; |
| 2 | use std::sync::{ |
| 3 | atomic::{AtomicBool, Ordering}, |
| 4 | Arc, Weak, |
| 5 | }; |
| 6 | use wayland_client::{ |
| 7 | globals::GlobalList, |
| 8 | protocol::{wl_output, wl_surface}, |
| 9 | Connection, Dispatch, Proxy, QueueHandle, |
| 10 | }; |
| 11 | use wayland_protocols::ext::session_lock::v1::client::{ |
| 12 | ext_session_lock_manager_v1, ext_session_lock_surface_v1, ext_session_lock_v1, |
| 13 | }; |
| 14 | |
| 15 | mod dispatch; |
| 16 | |
| 17 | /// Handler trait for session lock protocol. |
| 18 | pub 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)] |
| 41 | pub struct SessionLockSurfaceConfigure { |
| 42 | pub new_size: (u32, u32), |
| 43 | } |
| 44 | |
| 45 | #[derive (Debug)] |
| 46 | struct SessionLockSurfaceInner { |
| 47 | surface: Surface, |
| 48 | session_lock_surface: ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, |
| 49 | } |
| 50 | |
| 51 | impl Drop for SessionLockSurfaceInner { |
| 52 | fn drop(&mut self) { |
| 53 | self.session_lock_surface.destroy(); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | #[must_use ] |
| 58 | #[derive (Debug, Clone)] |
| 59 | pub struct SessionLockSurface(Arc<SessionLockSurfaceInner>); |
| 60 | |
| 61 | impl 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| 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)] |
| 74 | pub struct SessionLockSurfaceData { |
| 75 | inner: Weak<SessionLockSurfaceInner>, |
| 76 | } |
| 77 | |
| 78 | impl SessionLockSurfaceData { |
| 79 | pub fn session_lock_surface(&self) -> Option<SessionLockSurface> { |
| 80 | self.inner.upgrade().map(SessionLockSurface) |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | #[derive (Debug)] |
| 85 | pub struct SessionLockInner { |
| 86 | session_lock: ext_session_lock_v1::ExtSessionLockV1, |
| 87 | locked: AtomicBool, |
| 88 | } |
| 89 | |
| 90 | impl Drop for SessionLockInner { |
| 91 | fn drop(&mut self) { |
| 92 | // This does nothing if unlock() was called. It may trigger a protocol error if unlock was |
| 93 | // not called; this is an application bug, and choosing not to unlock here results in us |
| 94 | // failing secure. |
| 95 | self.session_lock.destroy(); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | /// A session lock |
| 100 | /// |
| 101 | /// Once a lock is created, you must wait for either a `locked` or `finished` event before |
| 102 | /// destroying this object. If you get a `locked` event, you must explicitly call `unlock` prior |
| 103 | /// to dropping this object. |
| 104 | #[derive (Debug, Clone)] |
| 105 | pub struct SessionLock(Arc<SessionLockInner>); |
| 106 | |
| 107 | impl SessionLock { |
| 108 | pub fn from_ext_session_lock(surface: &ext_session_lock_v1::ExtSessionLockV1) -> Option<Self> { |
| 109 | surface.data::<SessionLockData>().and_then(|data| data.inner.upgrade()).map(Self) |
| 110 | } |
| 111 | |
| 112 | pub fn is_locked(&self) -> bool { |
| 113 | self.0.locked.load(order:Ordering::SeqCst) |
| 114 | } |
| 115 | |
| 116 | pub fn unlock(&self) { |
| 117 | if self.0.locked.load(order:Ordering::SeqCst) { |
| 118 | self.0.session_lock.unlock_and_destroy(); |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | #[derive (Debug)] |
| 124 | pub struct SessionLockData { |
| 125 | inner: Weak<SessionLockInner>, |
| 126 | } |
| 127 | |
| 128 | impl SessionLock { |
| 129 | pub fn create_lock_surface<D>( |
| 130 | &self, |
| 131 | surface: impl Into<Surface>, |
| 132 | output: &wl_output::WlOutput, |
| 133 | qh: &QueueHandle<D>, |
| 134 | ) -> SessionLockSurface |
| 135 | where |
| 136 | D: Dispatch<ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, SessionLockSurfaceData> |
| 137 | + 'static, |
| 138 | { |
| 139 | // Freeze the queue during the creation of the Arc to avoid a race between events on the |
| 140 | // new objects being processed and the Weak in the SessionLockSurfaceData becoming usable. |
| 141 | let freeze = qh.freeze(); |
| 142 | let surface = surface.into(); |
| 143 | |
| 144 | let inner = Arc::new_cyclic(|weak| { |
| 145 | let session_lock_surface = self.0.session_lock.get_lock_surface( |
| 146 | surface.wl_surface(), |
| 147 | output, |
| 148 | qh, |
| 149 | SessionLockSurfaceData { inner: weak.clone() }, |
| 150 | ); |
| 151 | |
| 152 | SessionLockSurfaceInner { surface, session_lock_surface } |
| 153 | }); |
| 154 | drop(freeze); |
| 155 | |
| 156 | SessionLockSurface(inner) |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | /// A handler for [`ext_session_lock_manager_v1::ExtSessionLockManagerV1`] |
| 161 | #[derive (Debug)] |
| 162 | pub struct SessionLockState { |
| 163 | session_lock_manager: GlobalProxy<ext_session_lock_manager_v1::ExtSessionLockManagerV1>, |
| 164 | } |
| 165 | |
| 166 | impl SessionLockState { |
| 167 | pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self |
| 168 | where |
| 169 | D: Dispatch<ext_session_lock_manager_v1::ExtSessionLockManagerV1, GlobalData> + 'static, |
| 170 | { |
| 171 | let session_lock_manager = GlobalProxy::from(globals.bind(qh, 1..=1, GlobalData)); |
| 172 | Self { session_lock_manager } |
| 173 | } |
| 174 | |
| 175 | pub fn lock<D>(&self, qh: &QueueHandle<D>) -> Result<SessionLock, GlobalError> |
| 176 | where |
| 177 | D: Dispatch<ext_session_lock_v1::ExtSessionLockV1, SessionLockData> + 'static, |
| 178 | { |
| 179 | let session_lock_manager = self.session_lock_manager.get()?; |
| 180 | |
| 181 | // Freeze the queue during the creation of the Arc to avoid a race between events on the |
| 182 | // new objects being processed and the Weak in the SessionLockData becoming usable. |
| 183 | let freeze = qh.freeze(); |
| 184 | |
| 185 | let inner = Arc::new_cyclic(|weak| { |
| 186 | let session_lock = |
| 187 | session_lock_manager.lock(qh, SessionLockData { inner: weak.clone() }); |
| 188 | |
| 189 | SessionLockInner { session_lock, locked: AtomicBool::new(false) } |
| 190 | }); |
| 191 | drop(freeze); |
| 192 | |
| 193 | Ok(SessionLock(inner)) |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | #[macro_export ] |
| 198 | macro_rules! delegate_session_lock { |
| 199 | ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { |
| 200 | $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: |
| 201 | [ |
| 202 | $crate::reexports::protocols::ext::session_lock::v1::client::ext_session_lock_manager_v1::ExtSessionLockManagerV1: $crate::globals::GlobalData |
| 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_v1::ExtSessionLockV1: $crate::session_lock::SessionLockData |
| 208 | ] => $crate::session_lock::SessionLockState |
| 209 | ); |
| 210 | $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: |
| 211 | [ |
| 212 | $crate::reexports::protocols::ext::session_lock::v1::client::ext_session_lock_surface_v1::ExtSessionLockSurfaceV1: $crate::session_lock::SessionLockSurfaceData |
| 213 | ] => $crate::session_lock::SessionLockState |
| 214 | ); |
| 215 | }; |
| 216 | } |
| 217 | |