Skip to content

Commit

Permalink
Add session-lock protocol
Browse files Browse the repository at this point in the history
I modelled the API here on how layer shell is handled.

`SessionLock` and `SessionLockSurface` destroy their corresponding
Wayland objects on drop. In the case of `SessionLock`, it is incorrect
to do this before `locked`/`finished` are received, but also incorrect
to not destroy it. For security, the compositor remains locked if any
of these things are done incorrectly.

Ideally `SessionLock` then might be a linear type that you can't drop
but have to pass to an explicit destroy function, but in lieu of such
functionality, it's marked `#[must_use]`.

The example simply creates a lock, creates a lock surface for each
output with the same colorful graphic as `simple_window`, and terminates
after 5 seconds.
  • Loading branch information
ids1024 committed Oct 13, 2023
1 parent ba656fb commit 8db37ee
Show file tree
Hide file tree
Showing 4 changed files with 529 additions and 0 deletions.
229 changes: 229 additions & 0 deletions examples/session_lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
use smithay_client_toolkit::{
compositor::{CompositorHandler, CompositorState},
output::{OutputHandler, OutputState},
reexports::{
calloop::{
timer::{TimeoutAction, Timer},
EventLoop, LoopHandle,
},
calloop_wayland_source::WaylandSource,
},
registry::{ProvidesRegistryState, RegistryState},
registry_handlers,
session_lock::{
SessionLock, SessionLockHandler, SessionLockState, SessionLockSurface,
SessionLockSurfaceConfigure,
},
shm::{raw::RawPool, Shm, ShmHandler},
};
use std::time::Duration;
use wayland_client::{
globals::registry_queue_init,
protocol::{wl_buffer, wl_output, wl_shm, wl_surface},
Connection, QueueHandle,
};

struct AppData {
loop_handle: LoopHandle<'static, Self>,
conn: Connection,
compositor_state: CompositorState,
output_state: OutputState,
registry_state: RegistryState,
shm: Shm,
session_lock_state: SessionLockState,
session_lock: Option<SessionLock>,
lock_surfaces: Vec<SessionLockSurface>,
exit: bool,
}

fn main() {
env_logger::init();

let conn = Connection::connect_to_env().unwrap();

let (globals, event_queue) = registry_queue_init(&conn).unwrap();
let qh: QueueHandle<AppData> = event_queue.handle();
let mut event_loop: EventLoop<AppData> =
EventLoop::try_new().expect("Failed to initialize the event loop!");

let mut app_data = AppData {
loop_handle: event_loop.handle(),
conn: conn.clone(),
compositor_state: CompositorState::bind(&globals, &qh).unwrap(),
output_state: OutputState::new(&globals, &qh),
registry_state: RegistryState::new(&globals),
shm: Shm::bind(&globals, &qh).unwrap(),
session_lock_state: SessionLockState::new(&globals, &qh),
session_lock: None,
lock_surfaces: Vec::new(),
exit: false,
};

app_data.session_lock =
Some(app_data.session_lock_state.lock(&qh).expect("ext-session-lock not supported"));

WaylandSource::new(conn.clone(), event_queue).insert(event_loop.handle()).unwrap();

loop {
event_loop.dispatch(Duration::from_millis(16), &mut app_data).unwrap();

if app_data.exit {
break;
}
}
}

impl SessionLockHandler for AppData {
fn locked(&mut self, _conn: &Connection, qh: &QueueHandle<Self>, session_lock: SessionLock) {
println!("Locked");

for output in self.output_state.outputs() {
let surface = self.compositor_state.create_surface(&qh);
let lock_surface = session_lock.create_lock_surface(surface, &output, qh);
self.lock_surfaces.push(lock_surface);
}

// After 5 seconds, destroy lock
self.loop_handle
.insert_source(Timer::from_duration(Duration::from_secs(5)), |_, _, app_data| {
// Destroy the session lock
app_data.session_lock.take();
// Sync connection to make sure compostor receives destroy
app_data.conn.roundtrip().unwrap();
// Then we can exit
app_data.exit = true;
TimeoutAction::Drop
})
.unwrap();
}

fn finished(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_session_lock: SessionLock,
) {
println!("Finished");
self.exit = true;
}

fn configure(
&mut self,
_conn: &Connection,
qh: &QueueHandle<Self>,
session_lock_surface: SessionLockSurface,
configure: SessionLockSurfaceConfigure,
_serial: u32,
) {
let (width, height) = configure.new_size;

let mut pool = RawPool::new(width as usize * height as usize * 4, &self.shm).unwrap();
let canvas = pool.mmap();
canvas.chunks_exact_mut(4).enumerate().for_each(|(index, chunk)| {
let x = (index % width as usize) as u32;
let y = (index / width as usize) as u32;

let a = 0xFF;
let r = u32::min(((width - x) * 0xFF) / width, ((height - y) * 0xFF) / height);
let g = u32::min((x * 0xFF) / width, ((height - y) * 0xFF) / height);
let b = u32::min(((width - x) * 0xFF) / width, (y * 0xFF) / height);
let color = (a << 24) + (r << 16) + (g << 8) + b;

let array: &mut [u8; 4] = chunk.try_into().unwrap();
*array = color.to_le_bytes();
});
let buffer = pool.create_buffer(
0,
width as i32,
height as i32,
width as i32 * 4,
wl_shm::Format::Argb8888,
(),
qh,
);

session_lock_surface.wl_surface().attach(Some(&buffer), 0, 0);
session_lock_surface.wl_surface().commit();

buffer.destroy();
}
}

impl CompositorHandler for AppData {
fn scale_factor_changed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_surface: &wl_surface::WlSurface,
_new_factor: i32,
) {
}

fn transform_changed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_surface: &wl_surface::WlSurface,
_new_transform: wl_output::Transform,
) {
}

fn frame(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_surface: &wl_surface::WlSurface,
_time: u32,
) {
}
}

impl OutputHandler for AppData {
fn output_state(&mut self) -> &mut OutputState {
&mut self.output_state
}

fn new_output(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_output: wl_output::WlOutput,
) {
}

fn update_output(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_output: wl_output::WlOutput,
) {
}

fn output_destroyed(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_output: wl_output::WlOutput,
) {
}
}

impl ProvidesRegistryState for AppData {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
registry_handlers![OutputState,];
}

impl ShmHandler for AppData {
fn shm_state(&mut self) -> &mut Shm {
&mut self.shm
}
}

smithay_client_toolkit::delegate_compositor!(AppData);
smithay_client_toolkit::delegate_output!(AppData);
smithay_client_toolkit::delegate_session_lock!(AppData);
smithay_client_toolkit::delegate_shm!(AppData);
smithay_client_toolkit::delegate_registry!(AppData);
wayland_client::delegate_noop!(AppData: ignore wl_buffer::WlBuffer);
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub mod output;
pub mod primary_selection;
pub mod registry;
pub mod seat;
pub mod session_lock;
pub mod shell;
pub mod shm;
pub mod subcompositor;
88 changes: 88 additions & 0 deletions src/session_lock/dispatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use crate::globals::GlobalData;
use std::sync::atomic::Ordering;
use wayland_client::{Connection, Dispatch, QueueHandle};
use wayland_protocols::ext::session_lock::v1::client::{
ext_session_lock_manager_v1, ext_session_lock_surface_v1, ext_session_lock_v1,
};

use super::{
SessionLock, SessionLockData, SessionLockHandler, SessionLockState, SessionLockSurface,
SessionLockSurfaceConfigure, SessionLockSurfaceData,
};

impl<D> Dispatch<ext_session_lock_manager_v1::ExtSessionLockManagerV1, GlobalData, D>
for SessionLockState
where
D: Dispatch<ext_session_lock_manager_v1::ExtSessionLockManagerV1, GlobalData>,
{
fn event(
_state: &mut D,
_proxy: &ext_session_lock_manager_v1::ExtSessionLockManagerV1,
_event: ext_session_lock_manager_v1::Event,
_: &GlobalData,
_: &Connection,
_: &QueueHandle<D>,
) {
unreachable!()
}
}

impl<D> Dispatch<ext_session_lock_v1::ExtSessionLockV1, SessionLockData, D> for SessionLockState
where
D: Dispatch<ext_session_lock_v1::ExtSessionLockV1, SessionLockData> + SessionLockHandler,
{
fn event(
state: &mut D,
proxy: &ext_session_lock_v1::ExtSessionLockV1,
event: ext_session_lock_v1::Event,
_: &SessionLockData,
conn: &Connection,
qh: &QueueHandle<D>,
) {
if let Some(session_lock) = SessionLock::from_ext_session_lock(proxy) {
match event {
ext_session_lock_v1::Event::Locked => {
session_lock.0.locked.store(true, Ordering::SeqCst);
state.locked(conn, qh, session_lock);
}
ext_session_lock_v1::Event::Finished => {
state.finished(conn, qh, session_lock);
}
_ => unreachable!(),
}
}
}
}

impl<D> Dispatch<ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, SessionLockSurfaceData, D>
for SessionLockState
where
D: Dispatch<ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, SessionLockSurfaceData>
+ SessionLockHandler,
{
fn event(
state: &mut D,
proxy: &ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
event: ext_session_lock_surface_v1::Event,
_: &SessionLockSurfaceData,
conn: &Connection,
qh: &QueueHandle<D>,
) {
if let Some(session_lock_surface) = SessionLockSurface::from_ext_session_lock_surface(proxy)
{
match event {
ext_session_lock_surface_v1::Event::Configure { serial, width, height } => {
proxy.ack_configure(serial);
state.configure(
conn,
qh,
session_lock_surface,
SessionLockSurfaceConfigure { new_size: (width, height) },
serial,
);
}
_ => unreachable!(),
}
}
}
}
Loading

0 comments on commit 8db37ee

Please sign in to comment.