From d42add4d189573bbb7d3614b7b3d075cd6fbb8d0 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 19 May 2022 23:39:11 +0200 Subject: [PATCH] all: implement screen locking --- src/cli.rs | 4 + src/cli/unlock.rs | 21 +++ src/compositor.rs | 7 +- src/globals.rs | 2 + src/ifs.rs | 2 + src/ifs/ext_session_lock_manager_v1.rs | 129 ++++++++++++++ src/ifs/ext_session_lock_v1.rs | 136 +++++++++++++++ src/ifs/jay_compositor.rs | 22 ++- src/ifs/wl_output.rs | 2 + src/ifs/wl_seat.rs | 7 + src/ifs/wl_seat/event_handling.rs | 3 +- src/ifs/wl_seat/pointer_owner.rs | 3 +- src/ifs/wl_surface.rs | 3 + .../wl_surface/ext_session_lock_surface_v1.rs | 163 ++++++++++++++++++ src/ifs/wl_surface/wl_subsurface.rs | 2 +- src/render/renderer/renderer.rs | 8 + src/state.rs | 8 + src/tasks/connector.rs | 3 + src/tree/output.rs | 30 +++- src/tree/walker.rs | 15 ++ wire/ext_session_lock_manager_v1.txt | 8 + wire/ext_session_lock_surface_v1.txt | 17 ++ wire/ext_session_lock_v1.txt | 25 +++ wire/jay_compositor.txt | 4 + 24 files changed, 618 insertions(+), 6 deletions(-) create mode 100644 src/cli/unlock.rs create mode 100644 src/ifs/ext_session_lock_manager_v1.rs create mode 100644 src/ifs/ext_session_lock_v1.rs create mode 100644 src/ifs/wl_surface/ext_session_lock_surface_v1.rs create mode 100644 wire/ext_session_lock_manager_v1.txt create mode 100644 wire/ext_session_lock_surface_v1.txt create mode 100644 wire/ext_session_lock_v1.txt diff --git a/src/cli.rs b/src/cli.rs index 277adb3d..ec21dc66 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -4,6 +4,7 @@ mod log; mod quit; pub mod screenshot; mod set_log_level; +mod unlock; use { crate::compositor::start_compositor, @@ -40,6 +41,8 @@ pub enum Cmd { SetLogLevel(SetLogArgs), /// Stop the compositor. Quit, + /// Unlocks the compositor. + Unlock, /// Take a screenshot. Screenshot(ScreenshotArgs), /// Inspect/modify the idle (screensaver) settings. @@ -186,6 +189,7 @@ pub fn main() { Cmd::SetLogLevel(a) => set_log_level::main(cli.global, a), Cmd::Screenshot(a) => screenshot::main(cli.global, a), Cmd::Idle(a) => idle::main(cli.global, a), + Cmd::Unlock => unlock::main(cli.global), #[cfg(feature = "it")] Cmd::RunTests => crate::it::run_tests(), } diff --git a/src/cli/unlock.rs b/src/cli/unlock.rs new file mode 100644 index 00000000..9e5d2f59 --- /dev/null +++ b/src/cli/unlock.rs @@ -0,0 +1,21 @@ +use { + crate::{cli::GlobalArgs, tools::tool_client::ToolClient, wire::jay_compositor::Unlock}, + std::rc::Rc, +}; + +pub fn main(global: GlobalArgs) { + let tc = ToolClient::new(global.log_level.into()); + let logger = Rc::new(Unlocker { tc: tc.clone() }); + tc.run(run(logger)); +} + +struct Unlocker { + tc: Rc, +} + +async fn run(log: Rc) { + let tc = &log.tc; + let comp = tc.jay_compositor().await; + tc.send(Unlock { self_id: comp }); + tc.round_trip().await; +} diff --git a/src/compositor.rs b/src/compositor.rs index c69977b2..959cf32e 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -22,7 +22,7 @@ use { logger::Logger, render::{self, RenderError}, sighand::{self, SighandError}, - state::{ConnectorData, IdleState, State, XWaylandState}, + state::{ConnectorData, IdleState, ScreenlockState, State, XWaylandState}, tasks::{self, idle}, tree::{ container_layout, container_render_data, float_layout, float_titles, DisplayNode, @@ -183,6 +183,10 @@ fn start_compositor2( data_offer_ids: Default::default(), drm_dev_ids: Default::default(), ring: ring.clone(), + lock: ScreenlockState { + locked: Cell::new(false), + lock: Default::default(), + }, }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); @@ -370,6 +374,7 @@ fn create_dummy_output(state: &Rc) { status: Default::default(), scroll: Default::default(), pointer_positions: Default::default(), + lock_surface: Default::default(), }); let dummy_workspace = Rc::new(WorkspaceNode { id: state.node_ids.next(), diff --git a/src/globals.rs b/src/globals.rs index 8109096b..3e53cee7 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -3,6 +3,7 @@ use { backend::Backend, client::Client, ifs::{ + ext_session_lock_manager_v1::ExtSessionLockManagerV1Global, ipc::{ wl_data_device_manager::WlDataDeviceManagerGlobal, zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1Global, @@ -139,6 +140,7 @@ impl Globals { add_singleton!(JayCompositorGlobal); add_singleton!(ZwlrScreencopyManagerV1Global); add_singleton!(ZwpRelativePointerManagerV1Global); + add_singleton!(ExtSessionLockManagerV1Global); if backend.supports_idle() { add_singleton!(ZwpIdleInhibitManagerV1Global); diff --git a/src/ifs.rs b/src/ifs.rs index 11bc5afa..63fe5e89 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -1,3 +1,5 @@ +pub mod ext_session_lock_manager_v1; +pub mod ext_session_lock_v1; pub mod ipc; pub mod jay_compositor; pub mod jay_idle; diff --git a/src/ifs/ext_session_lock_manager_v1.rs b/src/ifs/ext_session_lock_manager_v1.rs new file mode 100644 index 00000000..a7451f87 --- /dev/null +++ b/src/ifs/ext_session_lock_manager_v1.rs @@ -0,0 +1,129 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::ext_session_lock_v1::ExtSessionLockV1, + leaks::Tracker, + object::Object, + utils::buffd::{MsgParser, MsgParserError}, + wire::{ext_session_lock_manager_v1::*, ExtSessionLockManagerV1Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +pub struct ExtSessionLockManagerV1Global { + pub name: GlobalName, +} + +impl ExtSessionLockManagerV1Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: ExtSessionLockManagerV1Id, + client: &Rc, + _version: u32, + ) -> Result<(), ExtSessionLockManagerV1Error> { + let obj = Rc::new(ExtSessionLockManagerV1 { + id, + client: client.clone(), + tracker: Default::default(), + }); + track!(client, obj); + client.add_client_obj(&obj)?; + Ok(()) + } +} + +pub struct ExtSessionLockManagerV1 { + pub id: ExtSessionLockManagerV1Id, + pub client: Rc, + pub tracker: Tracker, +} + +impl ExtSessionLockManagerV1 { + fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtSessionLockManagerV1Error> { + let _req: Destroy = self.client.parse(self, msg)?; + self.client.remove_obj(self)?; + Ok(()) + } + + fn lock(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtSessionLockManagerV1Error> { + let req: Lock = self.client.parse(self, msg)?; + let did_lock = self.client.state.lock.locked.get() == false; + let new = Rc::new(ExtSessionLockV1 { + id: req.id, + client: self.client.clone(), + tracker: Default::default(), + did_lock, + finished: Cell::new(false), + }); + track!(new.client, new); + self.client.add_client_obj(&new)?; + if did_lock { + log::info!("Client {} locks the screen", self.client.id); + let state = &self.client.state; + for seat in state.globals.seats.lock().values() { + seat.prepare_for_lock(); + } + state.lock.locked.set(true); + state.lock.lock.set(Some(new.clone())); + state.tree_changed(); + state.damage(); + new.send_locked(); + } else { + new.finish(); + } + Ok(()) + } +} + +global_base!( + ExtSessionLockManagerV1Global, + ExtSessionLockManagerV1, + ExtSessionLockManagerV1Error +); + +impl Global for ExtSessionLockManagerV1Global { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 1 + } + + fn secure(&self) -> bool { + true + } +} + +simple_add_global!(ExtSessionLockManagerV1Global); + +object_base! { + ExtSessionLockManagerV1; + + DESTROY => destroy, + LOCK => lock, +} + +impl Object for ExtSessionLockManagerV1 { + fn num_requests(&self) -> u32 { + LOCK + 1 + } +} + +simple_add_obj!(ExtSessionLockManagerV1); + +#[derive(Debug, Error)] +pub enum ExtSessionLockManagerV1Error { + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(ExtSessionLockManagerV1Error, MsgParserError); +efrom!(ExtSessionLockManagerV1Error, ClientError); diff --git a/src/ifs/ext_session_lock_v1.rs b/src/ifs/ext_session_lock_v1.rs new file mode 100644 index 00000000..0d404bb0 --- /dev/null +++ b/src/ifs/ext_session_lock_v1.rs @@ -0,0 +1,136 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::wl_surface::ext_session_lock_surface_v1::{ + ExtSessionLockSurfaceV1, ExtSessionLockSurfaceV1Error, + }, + leaks::Tracker, + object::Object, + utils::buffd::{MsgParser, MsgParserError}, + wire::{ext_session_lock_v1::*, ExtSessionLockV1Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +pub struct ExtSessionLockV1 { + pub id: ExtSessionLockV1Id, + pub client: Rc, + pub tracker: Tracker, + pub did_lock: bool, + pub finished: Cell, +} + +impl ExtSessionLockV1 { + pub fn send_locked(&self) { + self.client.event(Locked { self_id: self.id }) + } + + fn send_finished(&self) { + self.client.event(Finished { self_id: self.id }) + } + + pub fn finish(&self) { + self.send_finished(); + self.finished.set(true); + } + + fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtSessionLockV1Error> { + let _req: Destroy = self.client.parse(self, msg)?; + if !self.finished.get() { + self.client.state.lock.lock.take(); + } + self.client.remove_obj(self)?; + Ok(()) + } + + fn get_lock_surface(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtSessionLockV1Error> { + let req: GetLockSurface = self.client.parse(self, msg)?; + let output = self.client.lookup(req.output)?; + let surface = self.client.lookup(req.surface)?; + let new = Rc::new(ExtSessionLockSurfaceV1 { + id: req.id, + node_id: self.client.state.node_ids.next(), + client: self.client.clone(), + surface, + tracker: Default::default(), + serial: Default::default(), + output: output.global.node.get(), + seat_state: Default::default(), + }); + track!(new.client, new); + new.install()?; + self.client.add_client_obj(&new)?; + if !output.global.destroyed.get() && !self.finished.get() { + if let Some(node) = output.global.node.get() { + if node.lock_surface.get().is_some() { + return Err(ExtSessionLockV1Error::OutputAlreadyLocked); + } + node.lock_surface.set(Some(new.clone())); + let pos = output.global.pos.get(); + new.change_extents(pos); + self.client.state.tree_changed(); + } + } + Ok(()) + } + + fn unlock_and_destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtSessionLockV1Error> { + let _req: UnlockAndDestroy = self.client.parse(self, msg)?; + if !self.did_lock { + return Err(ExtSessionLockV1Error::NeverLocked); + } + if !self.finished.get() { + let state = &self.client.state; + state.lock.locked.set(false); + state.lock.lock.take(); + for output in state.outputs.lock().values() { + if let Some(surface) = output.node.lock_surface.take() { + surface.destroy_node(); + } + } + state.tree_changed(); + state.damage(); + } + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + ExtSessionLockV1; + + DESTROY => destroy, + GET_LOCK_SURFACE => get_lock_surface, + UNLOCK_AND_DESTROY => unlock_and_destroy, +} + +impl Object for ExtSessionLockV1 { + fn num_requests(&self) -> u32 { + UNLOCK_AND_DESTROY + 1 + } + + fn break_loops(&self) { + if !self.finished.get() { + self.client.state.lock.lock.take(); + } + } +} + +simple_add_obj!(ExtSessionLockV1); + +#[derive(Debug, Error)] +pub enum ExtSessionLockV1Error { + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error("The lock was not accepted")] + NeverLocked, + #[error("The output already has a lock surface attached")] + OutputAlreadyLocked, + #[error(transparent)] + ExtSessionLockSurfaceV1Error(#[from] ExtSessionLockSurfaceV1Error), +} +efrom!(ExtSessionLockV1Error, MsgParserError); +efrom!(ExtSessionLockV1Error, ClientError); diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index f74838b9..bb3c3a1a 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -174,6 +174,25 @@ impl JayCompositor { self.client.symmetric_delete.set(true); Ok(()) } + + fn unlock(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> { + let _req: Unlock = self.client.parse(self, parser)?; + let state = &self.client.state; + if state.lock.locked.replace(false) { + if let Some(lock) = state.lock.lock.take() { + lock.finish(); + } + for output in state.outputs.lock().values() { + if let Some(surface) = output.node.lock_surface.take() { + surface.destroy_node(); + } + } + state.tree_changed(); + state.damage(); + } + self.client.symmetric_delete.set(true); + Ok(()) + } } object_base! { @@ -187,11 +206,12 @@ object_base! { GET_IDLE => get_idle, GET_CLIENT_ID => get_client_id, ENABLE_SYMMETRIC_DELETE => enable_symmetric_delete, + UNLOCK => unlock, } impl Object for JayCompositor { fn num_requests(&self) -> u32 { - GET_CLIENT_ID + 1 + UNLOCK + 1 } } diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index a44f56c4..1579faea 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -79,6 +79,7 @@ pub struct WlOutputGlobal { pub bindings: RefCell>>>, pub unused_captures: LinkedList>, pub pending_captures: LinkedList>, + pub destroyed: Cell, } impl WlOutputGlobal { @@ -112,6 +113,7 @@ impl WlOutputGlobal { bindings: Default::default(), unused_captures: Default::default(), pending_captures: Default::default(), + destroyed: Cell::new(false), } } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 6a1537cc..45c38ccb 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -118,6 +118,7 @@ pub struct WlSeatGlobal { extents_start_pos: Cell<(i32, i32)>, pos: Cell<(Fixed, Fixed)>, pointer_stack: RefCell>>, + pointer_stack_modified: Cell, found_tree: RefCell>, keyboard_node: CloneCell>, pressed_keys: RefCell>, @@ -164,6 +165,7 @@ impl WlSeatGlobal { extents_start_pos: Cell::new((0, 0)), pos: Cell::new((Fixed(0), Fixed(0))), pointer_stack: RefCell::new(vec![]), + pointer_stack_modified: Cell::new(false), found_tree: RefCell::new(vec![]), keyboard_node: CloneCell::new(state.root.clone()), pressed_keys: RefCell::new(Default::default()), @@ -330,6 +332,11 @@ impl WlSeatGlobal { } } + pub fn prepare_for_lock(self: &Rc) { + self.pointer_owner.revert_to_default(self); + self.kb_owner.ungrab(self); + } + pub fn set_position(&self, x: i32, y: i32) { self.pos.set((Fixed::from_int(x), Fixed::from_int(y))); self.trigger_tree_changed(); diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 14153c6a..b71f6131 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -134,6 +134,7 @@ impl NodeSeatState { last.node_seat_state().leave(&seat); last.node_on_leave(&seat); } + seat.pointer_stack_modified.set(true); seat.state.tree_changed(); } self.release_kb_focus2(focus_last); @@ -273,7 +274,7 @@ impl WlSeatGlobal { let new_mods; { let mut kb_state = self.kb_state.borrow_mut(); - if state == wl_keyboard::PRESSED { + if !self.state.lock.locked.get() && state == wl_keyboard::PRESSED { let old_mods = kb_state.mods(); let keysyms = kb_state.unmodified_keysyms(key); for &sym in keysyms { diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index a9b46534..3a4ea8e7 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -201,7 +201,8 @@ impl PointerOwner for DefaultPointerOwner { break; } } - if (stack.len(), found_tree.len()) == (divergence, divergence) { + let psm = seat.pointer_stack_modified.replace(false); + if !psm && (stack.len(), found_tree.len()) == (divergence, divergence) { if let Some(node) = found_tree.last() { node.node.clone().node_on_pointer_motion( seat, diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 88cd5311..be646852 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -1,4 +1,5 @@ pub mod cursor; +pub mod ext_session_lock_surface_v1; pub mod wl_subsurface; pub mod xdg_surface; pub mod xwindow; @@ -64,6 +65,7 @@ pub enum SurfaceRole { DndIcon, ZwlrLayerSurface, XSurface, + ExtSessionLockSurface, } impl SurfaceRole { @@ -76,6 +78,7 @@ impl SurfaceRole { SurfaceRole::DndIcon => "dnd_icon", SurfaceRole::ZwlrLayerSurface => "zwlr_layer_surface", SurfaceRole::XSurface => "xwayland surface", + SurfaceRole::ExtSessionLockSurface => "ext_session_lock_surface", } } } diff --git a/src/ifs/wl_surface/ext_session_lock_surface_v1.rs b/src/ifs/wl_surface/ext_session_lock_surface_v1.rs new file mode 100644 index 00000000..f16f6c18 --- /dev/null +++ b/src/ifs/wl_surface/ext_session_lock_surface_v1.rs @@ -0,0 +1,163 @@ +use { + crate::{ + client::{Client, ClientError}, + fixed::Fixed, + ifs::{ + wl_seat::{NodeSeatState, WlSeatGlobal}, + wl_surface::{SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError}, + }, + leaks::Tracker, + object::Object, + rect::Rect, + tree::{FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, OutputNode}, + utils::{ + buffd::{MsgParser, MsgParserError}, + numcell::NumCell, + }, + wire::{ext_session_lock_surface_v1::*, ExtSessionLockSurfaceV1Id, WlSurfaceId}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct ExtSessionLockSurfaceV1 { + pub id: ExtSessionLockSurfaceV1Id, + pub node_id: ExtSessionLockSurfaceV1NodeId, + pub client: Rc, + pub surface: Rc, + pub tracker: Tracker, + pub serial: NumCell, + pub output: Option>, + pub seat_state: NodeSeatState, +} + +impl ExtSessionLockSurfaceV1 { + pub fn install(self: &Rc) -> Result<(), ExtSessionLockSurfaceV1Error> { + self.surface.set_role(SurfaceRole::ExtSessionLockSurface)?; + if self.surface.ext.get().is_some() { + return Err(ExtSessionLockSurfaceV1Error::AlreadyAttached( + self.surface.id, + )); + } + self.surface.ext.set(self.clone()); + Ok(()) + } + + pub fn change_extents(&self, rect: Rect) { + self.send_configure(rect.width(), rect.height()); + self.surface.set_absolute_position(rect.x1(), rect.x2()); + } + + fn send_configure(&self, width: i32, height: i32) { + self.client.event(Configure { + self_id: self.id, + serial: self.serial.fetch_add(1), + width: width as _, + height: height as _, + }); + } + + fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtSessionLockSurfaceV1Error> { + let _req: Destroy = self.client.parse(self, msg)?; + self.destroy_node(); + self.surface.unset_ext(); + self.client.remove_obj(self)?; + Ok(()) + } + + fn ack_configure(&self, msg: MsgParser<'_, '_>) -> Result<(), ExtSessionLockSurfaceV1Error> { + let _req: AckConfigure = self.client.parse(self, msg)?; + Ok(()) + } + + pub fn destroy_node(&self) { + if let Some(output) = &self.output { + if let Some(ls) = output.lock_surface.get() { + if ls.node_id == self.node_id { + output.lock_surface.take(); + self.client.state.tree_changed(); + } + } + } + self.surface.destroy_node(); + self.seat_state.destroy_node(self); + } +} + +impl SurfaceExt for ExtSessionLockSurfaceV1 { + fn extents_changed(&self) { + self.client.state.tree_changed(); + } + + fn focus_node(&self) -> Option> { + Some(self.surface.clone()) + } +} + +tree_id!(ExtSessionLockSurfaceV1NodeId); +impl Node for ExtSessionLockSurfaceV1 { + fn node_id(&self) -> NodeId { + self.node_id.into() + } + + fn node_seat_state(&self) -> &NodeSeatState { + &self.seat_state + } + + fn node_visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_lock_surface(&self); + } + + fn node_visit_children(&self, visitor: &mut dyn NodeVisitor) { + visitor.visit_surface(&self.surface); + } + + fn node_visible(&self) -> bool { + true + } + + fn node_absolute_position(&self) -> Rect { + self.surface.node_absolute_position() + } + + fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec) -> FindTreeResult { + self.surface.find_tree_at_(x, y, tree) + } + + fn node_on_pointer_enter(self: Rc, seat: &Rc, _x: Fixed, _y: Fixed) { + seat.focus_node(self.surface.clone()); + } +} + +object_base! { + ExtSessionLockSurfaceV1; + + DESTROY => destroy, + ACK_CONFIGURE => ack_configure, +} + +impl Object for ExtSessionLockSurfaceV1 { + fn num_requests(&self) -> u32 { + ACK_CONFIGURE + 1 + } + + fn break_loops(&self) { + self.destroy_node(); + } +} + +simple_add_obj!(ExtSessionLockSurfaceV1); + +#[derive(Debug, Error)] +pub enum ExtSessionLockSurfaceV1Error { + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error(transparent)] + WlSurfaceError(#[from] WlSurfaceError), + #[error("Surface {0} cannot be turned into an ext_session_lock_surface because it already has an attached ext_session_lock_surface")] + AlreadyAttached(WlSurfaceId), +} +efrom!(ExtSessionLockSurfaceV1Error, MsgParserError); +efrom!(ExtSessionLockSurfaceV1Error, ClientError); diff --git a/src/ifs/wl_surface/wl_subsurface.rs b/src/ifs/wl_surface/wl_subsurface.rs index d0ffd907..ed9c5b15 100644 --- a/src/ifs/wl_surface/wl_subsurface.rs +++ b/src/ifs/wl_surface/wl_subsurface.rs @@ -270,7 +270,7 @@ simple_add_obj!(WlSubsurface); impl SurfaceExt for WlSubsurface { fn pre_commit(self: Rc, ctx: CommitContext) -> Result { if ctx == CommitContext::RootCommit && self.sync() { - log::info!("Aborting commit due to sync"); + log::debug!("Aborting commit due to sync"); return Ok(CommitAction::AbortCommit); } Ok(CommitAction::ContinueCommit) diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index aaf603ec..096bdea3 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -73,6 +73,14 @@ impl Renderer<'_> { } pub fn render_output(&mut self, output: &OutputNode, x: i32, y: i32) { + if self.state.lock.locked.get() { + if let Some(surface) = output.lock_surface.get() { + if surface.surface.buffer.get().is_some() { + self.render_surface(&surface.surface, x, y); + } + } + return; + } if let Some(ws) = output.workspace.get() { if let Some(fs) = ws.fullscreen.get() { fs.tl_as_node().node_render(self, x, y); diff --git a/src/state.rs b/src/state.rs index a0e75fab..6d37da4e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -15,6 +15,7 @@ use { forker::ForkerProxy, globals::{Globals, GlobalsError, WaylandGlobal}, ifs::{ + ext_session_lock_v1::ExtSessionLockV1, wl_drm::WlDrmGlobal, wl_seat::{SeatIds, WlSeatGlobal}, wl_surface::{ @@ -108,6 +109,7 @@ pub struct State { pub tracker: Tracker, pub data_offer_ids: NumCell, pub ring: Rc, + pub lock: ScreenlockState, } // impl Drop for State { @@ -122,6 +124,11 @@ impl Debug for State { } } +pub struct ScreenlockState { + pub locked: Cell, + pub lock: CloneCell>>, +} + pub struct XWaylandState { pub enabled: Cell, pub handler: RefCell>>, @@ -485,6 +492,7 @@ impl State { } pub fn clear(&self) { + self.lock.lock.take(); self.xwayland.handler.borrow_mut().take(); self.clients.clear(); if let Some(config) = self.config.set(None) { diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index c8552a99..beaf3b01 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -119,6 +119,7 @@ impl ConnectorHandler { status: self.state.status.clone(), scroll: Default::default(), pointer_positions: Default::default(), + lock_surface: Default::default(), }); let mode = info.initial_mode; let output_data = Rc::new(OutputData { @@ -158,10 +159,12 @@ impl ConnectorHandler { config.connector_disconnected(self.id); } global.node.set(None); + global.destroyed.set(true); let _ = self.state.remove_global(&*global); self.state.root.outputs.remove(&self.id); self.data.connected.set(false); self.state.outputs.remove(&self.id); + on.lock_surface.take(); let mut target_is_dummy = false; let target = match self.state.outputs.lock().values().next() { Some(o) => o.node.clone(), diff --git a/src/tree/output.rs b/src/tree/output.rs index 36c3cdf8..e3a996c1 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -9,7 +9,10 @@ use { collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT, }, - wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, + wl_surface::{ + ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, + zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, + }, zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP}, }, rect::Rect, @@ -47,6 +50,7 @@ pub struct OutputNode { pub status: CloneCell>, pub scroll: Scroller, pub pointer_positions: CopyHashMap, + pub lock_surface: CloneCell>>, } impl OutputNode { @@ -58,6 +62,7 @@ impl OutputNode { workspace.clear(); } self.render_data.borrow_mut().titles.clear(); + self.lock_surface.take(); } pub fn on_spaces_changed(self: &Rc) { @@ -264,6 +269,9 @@ impl OutputNode { self.global.pos.set(*rect); self.state.root.update_extents(); self.update_render_data(); + if let Some(ls) = self.lock_surface.get() { + ls.change_extents(*rect); + } if let Some(c) = self.workspace.get() { if let Some(fs) = c.fullscreen.get() { fs.tl_change_extents(rect); @@ -355,6 +363,9 @@ impl Node for OutputNode { } fn node_visit_children(&self, visitor: &mut dyn NodeVisitor) { + if let Some(ls) = self.lock_surface.get() { + visitor.visit_lock_surface(&ls); + } for ws in self.workspaces.iter() { visitor.visit_workspace(ws.deref()); } @@ -374,12 +385,29 @@ impl Node for OutputNode { } fn node_do_focus(self: Rc, seat: &Rc, direction: Direction) { + if self.state.lock.locked.get() { + if let Some(lock) = self.lock_surface.get() { + seat.focus_node(lock.surface.clone()); + } + return; + } if let Some(ws) = self.workspace.get() { ws.node_do_focus(seat, direction); } } fn node_find_tree_at(&self, x: i32, mut y: i32, tree: &mut Vec) -> FindTreeResult { + if self.state.lock.locked.get() { + if let Some(ls) = self.lock_surface.get() { + tree.push(FoundNode { + node: ls.clone(), + x, + y, + }); + return ls.node_find_tree_at(x, y, tree); + } + return FindTreeResult::AcceptsInput; + } if let Some(ws) = self.workspace.get() { if let Some(fs) = ws.fullscreen.get() { tree.push(FoundNode { diff --git a/src/tree/walker.rs b/src/tree/walker.rs index 40be895b..a14aedce 100644 --- a/src/tree/walker.rs +++ b/src/tree/walker.rs @@ -1,6 +1,7 @@ use { crate::{ ifs::wl_surface::{ + ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, xdg_surface::{xdg_popup::XdgPopup, xdg_toplevel::XdgToplevel}, xwindow::Xwindow, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, @@ -57,6 +58,10 @@ pub trait NodeVisitorBase: Sized { fn visit_placeholder(&mut self, node: &Rc) { node.node_visit_children(self); } + + fn visit_lock_surface(&mut self, node: &Rc) { + node.node_visit_children(self); + } } pub trait NodeVisitor { @@ -71,6 +76,7 @@ pub trait NodeVisitor { fn visit_layer_surface(&mut self, node: &Rc); fn visit_xwindow(&mut self, node: &Rc); fn visit_placeholder(&mut self, node: &Rc); + fn visit_lock_surface(&mut self, node: &Rc); } impl NodeVisitor for T { @@ -117,6 +123,10 @@ impl NodeVisitor for T { fn visit_placeholder(&mut self, node: &Rc) { ::visit_placeholder(self, node) } + + fn visit_lock_surface(&mut self, node: &Rc) { + ::visit_lock_surface(self, node) + } } pub struct GenericNodeVisitor { @@ -182,6 +192,11 @@ impl)> NodeVisitor for GenericNodeVisitor { (self.f)(node.clone()); node.node_visit_children(self); } + + fn visit_lock_surface(&mut self, node: &Rc) { + (self.f)(node.clone()); + node.node_visit_children(self); + } } // pub fn visit_containers)>(f: F) -> impl NodeVisitor { diff --git a/wire/ext_session_lock_manager_v1.txt b/wire/ext_session_lock_manager_v1.txt new file mode 100644 index 00000000..0236a45a --- /dev/null +++ b/wire/ext_session_lock_manager_v1.txt @@ -0,0 +1,8 @@ +# requests + +msg destroy = 0 { +} + +msg lock = 1 { + id: id(ext_session_lock_v1), +} diff --git a/wire/ext_session_lock_surface_v1.txt b/wire/ext_session_lock_surface_v1.txt new file mode 100644 index 00000000..df20adb1 --- /dev/null +++ b/wire/ext_session_lock_surface_v1.txt @@ -0,0 +1,17 @@ +# request + +msg destroy = 0 { + +} + +msg ack_configure = 1 { + serial: u32, +} + +# events + +msg configure = 0 { + serial: u32, + width: u32, + height: u32, +} diff --git a/wire/ext_session_lock_v1.txt b/wire/ext_session_lock_v1.txt new file mode 100644 index 00000000..633483b7 --- /dev/null +++ b/wire/ext_session_lock_v1.txt @@ -0,0 +1,25 @@ +# requests + +msg destroy = 0 { + +} + +msg get_lock_surface = 1 { + id: id(ext_session_lock_surface_v1), + surface: id(wl_surface), + output: id(wl_output), +} + +msg unlock_and_destroy = 2 { + +} + +# events + +msg locked = 0 { + +} + +msg finished = 1 { + +} diff --git a/wire/jay_compositor.txt b/wire/jay_compositor.txt index 80ba918d..a6d1ef3b 100644 --- a/wire/jay_compositor.txt +++ b/wire/jay_compositor.txt @@ -31,6 +31,10 @@ msg enable_symmetric_delete = 7 { } +msg unlock = 8 { + +} + # events msg client_id = 0 {