use crate::client::Client;
use crate::client::LookupError;
use crate::configurable::ConfigurableData;
use crate::ifs::wl_surface::ext_session_lock_surface_v1::ExtSessionLockSurfaceV1;
use crate::ifs::wl_surface::ext_session_lock_surface_v1::ExtSessionLockSurfaceV1Error;
use crate::leaks::Tracker;
use crate::object::BreakLoops;
use crate::object::Version;
use crate::tree::TreeTimeline::LiveTL;
use crate::wire::ExtSessionLockV1Id;
use crate::wire::ext_session_lock_v1::*;
use jay_proc::Object;
use std::cell::Cell;
use std::rc::Rc;
use thiserror::Error;
#[derive(Object)]
#[break_loops]
pub struct ExtSessionLockV1 {
pub id: ExtSessionLockV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub did_lock: bool,
pub awaiting_locked: Cell<bool>,
pub finished: Cell<bool>,
pub version: Version,
}
impl ExtSessionLockV1 {
pub fn check_locked(&self) {
if !self.awaiting_locked.get() {
return;
}
for output in self.client.state.outputs.lock().values() {
if !output.connector.connector.effectively_locked() {
return;
}
}
self.send_locked();
self.awaiting_locked.set(false);
}
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);
}
}
impl ExtSessionLockV1RequestHandler for ExtSessionLockV1 {
type Error = ExtSessionLockV1Error;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if !self.finished.get() {
self.client.state.lock.lock.take();
}
self.client.remove_obj(self);
Ok(())
}
fn get_lock_surface(&self, req: GetLockSurface, _slf: &Rc<Self>) -> Result<(), Self::Error> {
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(),
enabled_transactions: Cell::new(Some(surface.enable_transactions())),
surface,
tracker: Default::default(),
output: output.global.clone(),
seat_state: Default::default(),
version: self.version,
destroyed: Cell::new(false),
configurable_data: ConfigurableData::new(&self.client.state),
desired_size: Default::default(),
liveness: Default::default(),
});
track!(new.client, new);
new.install()?;
self.client.add_client_obj(&new);
if !self.finished.get()
&& let Some(node) = output.global.node()
{
let ons = &node.node_state[LiveTL];
if ons.lock_surface.is_some() {
return Err(ExtSessionLockV1Error::OutputAlreadyLocked);
}
node.set_lock_surface(Some(new.clone()));
let pos = ons.pos.get();
new.change_extents(pos);
new.surface.set_output_without_workspace(&node);
self.client.state.tree_changed();
}
Ok(())
}
fn unlock_and_destroy(
&self,
_req: UnlockAndDestroy,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
if !self.did_lock {
return Err(ExtSessionLockV1Error::NeverLocked);
}
if !self.finished.get() {
self.client.state.do_unlock();
}
self.client.remove_obj(self);
Ok(())
}
}
impl BreakLoops for ExtSessionLockV1 {
fn break_loops(self: Rc<Self>) {
if !self.finished.get() {
self.client.state.lock.lock.take();
}
}
}
#[derive(Debug, Error)]
pub enum ExtSessionLockV1Error {
#[error(transparent)]
Lookup(#[from] LookupError),
#[error("The lock was not accepted")]
NeverLocked,
#[error("The output already has a lock surface attached")]
OutputAlreadyLocked,
#[error(transparent)]
ExtSessionLockSurfaceV1Error(#[from] ExtSessionLockSurfaceV1Error),
}