wayland_protocols_async/ext_session_lock_v1/
handler.rs

1use tokio::{sync::{mpsc::{Sender, Receiver}, oneshot}, io::unix::AsyncFd};
2use wayland_client::{EventQueue, protocol::{wl_seat::{WlSeat, self}, wl_registry::{WlRegistry, self}, wl_output::{WlOutput, self}, wl_compositor::{WlCompositor, self}, wl_surface::{WlSurface, self}}, globals::{self, GlobalListContents}, Connection, QueueHandle, Dispatch, WEnum};
3use wayland_protocols::ext::session_lock::v1::client::{
4    ext_session_lock_manager_v1::{self, ExtSessionLockManagerV1}, ext_session_lock_surface_v1::{self, ExtSessionLockSurfaceV1}, ext_session_lock_v1::{self, ExtSessionLockV1},
5};
6
7use super::errors::{SessionLockhandlerError, SessionLockhandlerErrorCodes};
8
9#[derive(Debug)]
10pub enum SessionLockMessage {
11    Lock { wl_surface: Option<WlSurface>, reply_to: oneshot::Sender<Result<bool, SessionLockhandlerError>> },
12    Unlock { reply_to: oneshot::Sender<Result<bool, SessionLockhandlerError>> },
13    AckConfigure { reply_to: oneshot::Sender<Result<bool, SessionLockhandlerError>> },
14}
15
16#[derive(Debug)]
17pub enum SessionLockEvent {
18    Locked,
19    Finished,
20    Configure { serial: u32, width: u32, height: u32 },
21}
22
23pub struct SessionLockState {
24    qh: QueueHandle<SessionLockState>,
25    event_tx: Sender<SessionLockEvent>,
26    seat: Option<WlSeat>,
27    output: Option<WlOutput>,
28    compositor: Option<WlCompositor>,
29    surface: Option<WlSurface>,
30    session_lock_manager: Option<ExtSessionLockManagerV1>,
31    session_lock: Option<ExtSessionLockV1>,
32    session_lock_surface: Option<ExtSessionLockSurfaceV1>,
33    session_lock_configure_serial: Option<u32>
34}
35
36pub struct SessionLockHandler {
37    event_queue: EventQueue<SessionLockState>,
38    state: SessionLockState,
39}
40
41impl SessionLockHandler {
42    pub fn new(event_tx: Sender<SessionLockEvent>) -> Self {
43        let conn = wayland_client::Connection::connect_to_env()
44            .map_err(|_| "could not connect to wayland socket, try setting WAYLAND_DISPLAY.")
45            .unwrap();
46        let (globals, mut event_queue) = globals::registry_queue_init::<SessionLockState>(&conn).unwrap();
47        let qh = event_queue.handle();
48
49        let session_lock_manager = globals
50            .bind::<ExtSessionLockManagerV1, _, _>(&qh, core::ops::RangeInclusive::new(1, 1), ())
51            .map_err(|_| "compositor does not implement ext session lock manager (v1).").unwrap();
52        let seat = globals
53            .bind::<WlSeat, _, _>(&qh, core::ops::RangeInclusive::new(1, 1), ())
54            .map_err(|_| "failed to retrieve the seat from global.")
55            .unwrap();
56        let output = globals
57            .bind::<WlOutput, _, _>(&qh, core::ops::RangeInclusive::new(1, 1), ())
58            .map_err(|_| "failed to retrieve the output from global.")
59            .unwrap();
60        let compositor = globals
61            .bind::<WlCompositor, _, _>(&qh, core::ops::RangeInclusive::new(1, 5), ())
62            .map_err(|_| "failed to retrieve the compositor from global").unwrap();
63        let surface = compositor.create_surface(&qh, ());
64
65        let mut state = SessionLockState {
66            qh,
67            event_tx,
68            seat: Some(seat),
69            output: Some(output),
70            compositor: Some(compositor),
71            surface: Some(surface),
72            session_lock_manager: Some(session_lock_manager),
73            session_lock: None,
74            session_lock_surface: None,
75            session_lock_configure_serial: None,
76        };
77
78        event_queue.roundtrip(&mut state).unwrap();
79    
80        SessionLockHandler {
81            event_queue,
82            state,
83        }
84    }
85
86    pub async fn run(&mut self, mut msg_rx: Receiver<SessionLockMessage>) {
87        let event_queue = &mut self.event_queue;
88        let mut session_lock_state = &mut self.state;
89    
90        loop {
91            // This would be required if other threads were reading from the socket.
92            event_queue.dispatch_pending(&mut session_lock_state).unwrap();
93            let read_guard = event_queue.prepare_read().unwrap();
94            let fd = read_guard.connection_fd();
95            let async_fd = AsyncFd::new(fd).unwrap();
96    
97            tokio::select! {
98                async_guard = async_fd.readable() => {
99                    async_guard.unwrap().clear_ready();
100                    // Drop the async_fd since it's holding a reference to the read_guard,
101                    // which is dropped on read. We don't need to read from it anyways.
102                    std::mem::drop(async_fd);
103                    // This should not block because we already ensured readiness
104                    let event = read_guard.read();
105                    match event {
106                        // There are events but another thread processed them, we don't need to dispatch
107                        Ok(0) => {},
108                        // We have some events
109                        Ok(_) => {
110                            event_queue.dispatch_pending(&mut session_lock_state).unwrap();
111                        },
112                        // No events to receive
113                        Err(_) => {}
114                        // Err(e) => eprintln!("error reading event {}", e),
115                    }
116                },
117                msg = msg_rx.recv() => {
118                    if msg.is_none() {
119                        continue;
120                    }
121                    match msg.unwrap() {
122                        SessionLockMessage::Lock { wl_surface, reply_to } => {
123                            let res = session_lock_state.lock(wl_surface);
124                            let _ = reply_to.send(res);
125                        },
126                        SessionLockMessage::Unlock { reply_to } => {
127                            let res = session_lock_state.unlock();
128                            let _ = reply_to.send(res);
129                        },
130                        SessionLockMessage::AckConfigure { reply_to } => {
131                            let res = session_lock_state.ack_configure();
132                            let _ = reply_to.send(res);
133                        },
134                    }
135                }
136            }
137    
138            // Send any new messages to the socket.
139            let _ = event_queue.flush().expect("wayland connection closed");
140        }
141    }
142}
143
144impl SessionLockState {
145    fn dispatch_event(&self, event: SessionLockEvent) {
146        let tx: Sender<SessionLockEvent> = self.event_tx.clone();
147        tokio::task::spawn(async move {
148            let _ = tx.send(event).await;
149        });
150    }
151
152    fn lock(&mut self, wl_surface: Option<WlSurface>) -> Result<bool, SessionLockhandlerError> {
153        let session_lock_manager = match &self.session_lock_manager {
154            Some(m) => m,
155            None => {
156                return Err(SessionLockhandlerError::new(
157                    SessionLockhandlerErrorCodes::ManagerIsNotSet,
158                    format!("session lock manager uninitialized or is not set")
159                ));
160            },
161        };
162        let output = match &self.output {
163            Some(o) => o,
164            None => {
165                return Err(SessionLockhandlerError::new(
166                    SessionLockhandlerErrorCodes::WLOutputIsNotSet,
167                    format!("wl_output uninitialized or is not set")
168                ));
169            },
170        };
171        // use surface from argument or use internal surface
172        let mut surface = match &self.surface {
173            Some(o) => o,
174            None => {
175                return Err(SessionLockhandlerError::new(
176                    SessionLockhandlerErrorCodes::WLSurfaceIsNotSet,
177                    format!("wl_surface uninitialized or is not set")
178                ));
179            },
180        };
181        if wl_surface.is_some() {
182            surface = wl_surface.as_ref().unwrap();
183        }
184
185        let session_lock = session_lock_manager.lock(&self.qh, ());
186
187        // set surface role as session lock surface
188        session_lock.get_lock_surface(&surface, &output, &self.qh, ());
189
190        // save to state
191        self.session_lock = Some(session_lock);
192        Ok(true)
193    }
194
195    fn unlock(&mut self) -> Result<bool, SessionLockhandlerError> {
196        let session_lock = match &self.session_lock {
197            Some(l) => l,
198            None => {
199                return Err(SessionLockhandlerError::new(
200                    SessionLockhandlerErrorCodes::SessionLockIsNotSet,
201                    format!("session_lock uninitialized or is not set, did you lock the session?")
202                ));
203            },
204        };
205
206        // unlock session and destroy the lock
207        session_lock.unlock_and_destroy();
208
209        // remove from state
210        self.session_lock = None;
211        self.session_lock_surface = None;
212        self.session_lock_configure_serial = None;
213
214        Ok(true)
215    }
216
217    fn ack_configure(&self) -> Result<bool, SessionLockhandlerError> {
218        let session_lock_surface = match &self.session_lock_surface {
219            Some(l) => l,
220            None => {
221                return Err(SessionLockhandlerError::new(
222                    SessionLockhandlerErrorCodes::SessionLockSurfaceIsNotSet,
223                    format!("session_lock_surface uninitialized or is not set, did you lock the session?")
224                ));
225            },
226        };
227        let session_lock_configure_serial = match self.session_lock_configure_serial {
228            Some(l) => l,
229            None => {
230                return Err(SessionLockhandlerError::new(
231                    SessionLockhandlerErrorCodes::SessionLockSurfaceSerialIsNotSet,
232                    format!("session_lock_configure_serial uninitialized or is not set")
233                ));
234            },
235        }; 
236
237        // send the configuration acknowledge
238        session_lock_surface.ack_configure(session_lock_configure_serial);
239
240        Ok(true)
241    }
242}
243
244impl Dispatch<WlRegistry, GlobalListContents> for SessionLockState {
245    fn event(
246        _: &mut Self,
247        registry: &wl_registry::WlRegistry,
248        event: wl_registry::Event,
249        _: &GlobalListContents,
250        _: &Connection,
251        qh: &QueueHandle<SessionLockState>,
252    ) {
253        if let wl_registry::Event::Global {
254            name,
255            interface,
256            version,
257        } = event
258        {
259            if interface == "wl_seat" {
260                registry.bind::<WlSeat, (), SessionLockState>(name, version, qh, ());
261            }
262        }
263    }
264}
265
266impl Dispatch<WlSeat, ()> for SessionLockState {
267    fn event(
268        state: &mut Self,
269        seat: &WlSeat,
270        event: wl_seat::Event,
271        _: &(),
272        _: &Connection,
273        _: &QueueHandle<SessionLockState>,
274    ) {
275        if let wl_seat::Event::Name { .. } = event {
276            state.seat = Some(seat.to_owned());
277        }
278    }
279}
280
281impl Dispatch<WlOutput, ()> for SessionLockState {
282    fn event(
283        state: &mut Self,
284        output: &WlOutput,
285        event: wl_output::Event,
286        _: &(),
287        _: &Connection,
288        _: &QueueHandle<SessionLockState>,
289    ) {
290        if let wl_output::Event::Name { .. } = event {
291            state.output = Some(output.to_owned());
292        }
293    }
294}
295
296impl Dispatch<WlCompositor, ()> for SessionLockState {
297    fn event(
298        state: &mut Self,
299        compositor: &WlCompositor,
300        event: wl_compositor::Event,
301        _: &(),
302        _: &Connection,
303        _: &QueueHandle<SessionLockState>,
304    ) {
305        state.compositor = Some(compositor.to_owned());
306    }
307}
308
309impl Dispatch<WlSurface, ()> for SessionLockState {
310    fn event(
311        state: &mut Self,
312        surface: &WlSurface,
313        event: wl_surface::Event,
314        _: &(),
315        _: &Connection,
316        _: &QueueHandle<SessionLockState>,
317    ) {
318        state.surface = Some(surface.to_owned());
319    }
320}
321
322impl Dispatch<ExtSessionLockManagerV1, ()> for SessionLockState {
323    fn event(
324        _: &mut Self,
325        _: &ExtSessionLockManagerV1,
326        event: <ExtSessionLockManagerV1 as wayland_client::Proxy>::Event,
327        _: &(),
328        _: &Connection,
329        _: &QueueHandle<Self>,
330    ) {}
331}
332
333impl Dispatch<ExtSessionLockV1, ()> for SessionLockState {
334    fn event(
335        state: &mut Self,
336        _: &ExtSessionLockV1,
337        event: <ExtSessionLockV1 as wayland_client::Proxy>::Event,
338        _: &(),
339        _: &Connection,
340        _: &QueueHandle<Self>,
341    ) {
342        match event {
343            ext_session_lock_v1::Event::Locked => {
344                state.dispatch_event(SessionLockEvent::Locked);
345            },
346            ext_session_lock_v1::Event::Finished => {
347                state.session_lock = None;
348                state.session_lock_surface = None;
349                state.session_lock_configure_serial = None;
350
351                state.dispatch_event(SessionLockEvent::Finished);
352            },
353            _ => {},
354        }
355    }
356}
357
358impl Dispatch<ExtSessionLockSurfaceV1, ()> for SessionLockState {
359    fn event(
360        state: &mut Self,
361        surface: &ExtSessionLockSurfaceV1,
362        event: <ExtSessionLockSurfaceV1 as wayland_client::Proxy>::Event,
363        _: &(),
364        _: &Connection,
365        _: &QueueHandle<Self>,
366    ) {
367        state.session_lock_surface = Some(surface.to_owned());
368        match event {
369            ext_session_lock_surface_v1::Event::Configure { serial, width, height } => {
370                state.session_lock_configure_serial = Some(serial);
371                state.dispatch_event(SessionLockEvent::Configure { serial, width, height })
372            },
373            _ => todo!(),
374        }
375    }
376}
377