Model lock ownership explicitly and allow protected crash recovery
This commit is contained in:
1 parent
570984a39b
commit
8ad5293178
22 files changed
+264
-80
No files matched your search
@@ -702,7 +702,7 @@ fn open_primary_device(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn sleep_locked_outputs(state: &mut Ferese) {
|
pub(crate) fn sleep_locked_outputs(state: &mut Ferese) {
|
||||||
if !state.session_lock.active || !state.session_lock.sleeping {
|
if !state.session_lock.active() || !state.session_lock.sleeping {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,7 +972,7 @@ fn render_output(
|
|||||||
crtc: crtc::Handle,
|
crtc: crtc::Handle,
|
||||||
plan: crate::frame_scheduler::FramePlan,
|
plan: crate::frame_scheduler::FramePlan,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if state.session_lock.active && state.session_lock.sleeping {
|
if state.session_lock.active() && state.session_lock.sleeping {
|
||||||
sleep_locked_outputs(state);
|
sleep_locked_outputs(state);
|
||||||
let asleep = state
|
let asleep = state
|
||||||
.direct_backend
|
.direct_backend
|
||||||
@@ -1025,7 +1025,7 @@ fn render_output(
|
|||||||
let flags = planes::frame_flags(
|
let flags = planes::frame_flags(
|
||||||
state.output_has_fullscreen_for_frame(&output.output, frame.overview.is_presenting()),
|
state.output_has_fullscreen_for_frame(&output.output, frame.overview.is_presenting()),
|
||||||
frame.animating,
|
frame.animating,
|
||||||
state.session_lock.active,
|
state.session_lock.active(),
|
||||||
);
|
);
|
||||||
let result =
|
let result =
|
||||||
output
|
output
|
||||||
@@ -1096,7 +1096,7 @@ fn render_output(
|
|||||||
.render_metrics
|
.render_metrics
|
||||||
.record_frame(render_started.elapsed(), &damage, missed_deadlines, effects);
|
.record_frame(render_started.elapsed(), &damage, missed_deadlines, effects);
|
||||||
output.frame_pending = true;
|
output.frame_pending = true;
|
||||||
output.lock_frame_pending = state.session_lock.active;
|
output.lock_frame_pending = state.session_lock.active();
|
||||||
Ok(true)
|
Ok(true)
|
||||||
})()
|
})()
|
||||||
};
|
};
|
||||||
@@ -1633,7 +1633,7 @@ fn deliver_frame_callbacks(state: &mut Ferese, node: DrmNode, crtc: crtc::Handle
|
|||||||
|
|
||||||
fn send_frame_callbacks(state: &mut Ferese, output: &Output) {
|
fn send_frame_callbacks(state: &mut Ferese, output: &Output) {
|
||||||
let eligible = state.callback_outputs();
|
let eligible = state.callback_outputs();
|
||||||
if state.session_lock.active {
|
if state.session_lock.active() {
|
||||||
state.lock_frame_callbacks(output);
|
state.lock_frame_callbacks(output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ impl XdgActivationHandler for Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn request_activation(&mut self, token: XdgActivationToken, data: XdgActivationTokenData, surface: WlSurface) {
|
fn request_activation(&mut self, token: XdgActivationToken, data: XdgActivationTokenData, surface: WlSurface) {
|
||||||
if self.session_lock.active || self.input_capture.captures(1) {
|
if self.session_lock.active() || self.input_capture.captures(1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let valid = data.serial.is_some() && data.timestamp.elapsed() <= TOKEN_MAX_AGE;
|
let valid = data.serial.is_some() && data.timestamp.elapsed() <= TOKEN_MAX_AGE;
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ impl CompositorHandler for Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn destroyed(&mut self, surface: &WlSurface) {
|
fn destroyed(&mut self, surface: &WlSurface) {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
self.session_lock
|
self.session_lock
|
||||||
.surfaces
|
.surfaces
|
||||||
.retain(|_, lock| lock.wl_surface() != surface);
|
.retain(|_, lock| lock.wl_surface() != surface);
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ pub fn handle_commit(state: &mut Ferese, surface: &WlSurface) {
|
|||||||
layer.layer_surface().send_pending_configure();
|
layer.layer_surface().send_pending_configure();
|
||||||
|
|
||||||
let policy = layer.cached_state();
|
let policy = layer.cached_state();
|
||||||
if !state.session_lock.active
|
if !state.session_lock.active()
|
||||||
&& !state.input_capture.captures(1)
|
&& !state.input_capture.captures(1)
|
||||||
&& policy.keyboard_interactivity == KeyboardInteractivity::Exclusive
|
&& policy.keyboard_interactivity == KeyboardInteractivity::Exclusive
|
||||||
&& matches!(policy.layer, Layer::Top | Layer::Overlay)
|
&& matches!(policy.layer, Layer::Top | Layer::Overlay)
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ impl Ferese {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut used = data.used.lock().unwrap();
|
let mut used = data.used.lock().unwrap();
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
frame.failed();
|
frame.failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ fn finish_conversion(state: &Ferese, result: Conversion) {
|
|||||||
if !frame.is_alive() || *data.used.lock().unwrap() {
|
if !frame.is_alive() || *data.used.lock().unwrap() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if state.session_lock.active
|
if state.session_lock.active()
|
||||||
|| !super::screencopy::capture_allowed()
|
|| !super::screencopy::capture_allowed()
|
||||||
|| !state.windows.ids().values().any(|id| *id == snapshot.id)
|
|| !state.windows.ids().values().any(|id| *id == snapshot.id)
|
||||||
{
|
{
|
||||||
@@ -263,7 +263,7 @@ impl Ferese {
|
|||||||
budget: &Arc<Mutex<Budget>>,
|
budget: &Arc<Mutex<Budget>>,
|
||||||
busy: &mut bool,
|
busy: &mut bool,
|
||||||
) -> Option<Snapshot> {
|
) -> Option<Snapshot> {
|
||||||
if self.session_lock.active || !super::screencopy::capture_allowed() {
|
if self.session_lock.active() || !super::screencopy::capture_allowed() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let window = self
|
let window = self
|
||||||
@@ -363,7 +363,7 @@ pub(super) fn copy_snapshot(state: &Ferese, resource: &ZwlrScreencopyFrameV1, da
|
|||||||
resource.failed();
|
resource.failed();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if state.session_lock.active
|
if state.session_lock.active()
|
||||||
|| !super::screencopy::capture_allowed()
|
|| !super::screencopy::capture_allowed()
|
||||||
|| !state.windows.ids().values().any(|id| *id == snapshot.id)
|
|| !state.windows.ids().values().any(|id| *id == snapshot.id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ impl XdgShellHandler for Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) {
|
fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Some(seat) = Seat::from_resource(&seat) else {
|
let Some(seat) = Seat::from_resource(&seat) else {
|
||||||
@@ -165,7 +165,7 @@ impl XdgShellHandler for Ferese {
|
|||||||
serial: Serial,
|
serial: Serial,
|
||||||
edges: smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
edges: smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
||||||
) {
|
) {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Some(seat) = Seat::from_resource(&seat) else {
|
let Some(seat) = Seat::from_resource(&seat) else {
|
||||||
@@ -240,7 +240,7 @@ impl XdgShellHandler for Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn grab(&mut self, surface: PopupSurface, seat: wl_seat::WlSeat, serial: Serial) {
|
fn grab(&mut self, surface: PopupSurface, seat: wl_seat::WlSeat, serial: Serial) {
|
||||||
if self.session_lock.active || self.input_capture.active() {
|
if self.session_lock.active() || self.input_capture.active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Some(seat) = Seat::from_resource(&seat) else {
|
let Some(seat) = Seat::from_resource(&seat) else {
|
||||||
|
|||||||
+10
-10
@@ -70,7 +70,7 @@ impl Ferese {
|
|||||||
let Some(hint) = self.locked_pointer_hint.take() else {
|
let Some(hint) = self.locked_pointer_hint.take() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !hint.surface.is_alive() || self.session_lock.active || self.input_capture.active() {
|
if !hint.surface.is_alive() || self.session_lock.active() || self.input_capture.active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Some(pointer) = self.seat.get_pointer() else {
|
let Some(pointer) = self.seat.get_pointer() else {
|
||||||
@@ -120,7 +120,7 @@ impl Ferese {
|
|||||||
self.lock_input_activity();
|
self.lock_input_activity();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
// An already-bound IME or drag client may install a grab after the
|
// An already-bound IME or drag client may install a grab after the
|
||||||
// lock request. Never let that grab receive subsequent lock input.
|
// lock request. Never let that grab receive subsequent lock input.
|
||||||
if let Some(keyboard) = seat.get_keyboard() {
|
if let Some(keyboard) = seat.get_keyboard() {
|
||||||
@@ -276,7 +276,7 @@ impl Ferese {
|
|||||||
event.time_msec(),
|
event.time_msec(),
|
||||||
|data, modifiers, keysym| {
|
|data, modifiers, keysym| {
|
||||||
if data.focus_cycle.is_some() {
|
if data.focus_cycle.is_some() {
|
||||||
if data.session_lock.active || data.input_capture.captures(1) {
|
if data.session_lock.active() || data.input_capture.captures(1) {
|
||||||
data.cancel_focus_cycle();
|
data.cancel_focus_cycle();
|
||||||
} else if !modifiers.alt {
|
} else if !modifiers.alt {
|
||||||
data.finish_focus_cycle();
|
data.finish_focus_cycle();
|
||||||
@@ -288,7 +288,7 @@ impl Ferese {
|
|||||||
return FilterResult::Intercept(());
|
return FilterResult::Intercept(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if data.session_lock.active {
|
if data.session_lock.active() {
|
||||||
return FilterResult::Forward;
|
return FilterResult::Forward;
|
||||||
}
|
}
|
||||||
let symbol = keysym.modified_sym().raw();
|
let symbol = keysym.modified_sym().raw();
|
||||||
@@ -695,7 +695,7 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start_floating_pointer_grab(&mut self, pointer: &PointerHandle<Self>, button: u32, serial: Serial) -> bool {
|
fn start_floating_pointer_grab(&mut self, pointer: &PointerHandle<Self>, button: u32, serial: Serial) -> bool {
|
||||||
if self.session_lock.active
|
if self.session_lock.active()
|
||||||
|| pointer.is_grabbed()
|
|| pointer.is_grabbed()
|
||||||
|| !matches!(button, BTN_LEFT | BTN_RIGHT)
|
|| !matches!(button, BTN_LEFT | BTN_RIGHT)
|
||||||
|| !self
|
|| !self
|
||||||
@@ -767,7 +767,7 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn swipe_navigation_blocked(&self) -> bool {
|
fn swipe_navigation_blocked(&self) -> bool {
|
||||||
self.session_lock.active
|
self.session_lock.active()
|
||||||
|| self.active_shortcuts_inhibitor.is_some()
|
|| self.active_shortcuts_inhibitor.is_some()
|
||||||
|| self.seat.get_pointer().is_some_and(|pointer| pointer.is_grabbed())
|
|| self.seat.get_pointer().is_some_and(|pointer| pointer.is_grabbed())
|
||||||
|| self.seat.get_keyboard().is_some_and(|keyboard| keyboard.is_grabbed())
|
|| self.seat.get_keyboard().is_some_and(|keyboard| keyboard.is_grabbed())
|
||||||
@@ -801,7 +801,7 @@ impl Ferese {
|
|||||||
if self.input_capture.captures(1) {
|
if self.input_capture.captures(1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
self.focus_output_at(position);
|
self.focus_output_at(position);
|
||||||
self.focus_lock_surface();
|
self.focus_lock_surface();
|
||||||
return;
|
return;
|
||||||
@@ -908,7 +908,7 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn focus_window_under_pointer(&mut self, pointer: &PointerHandle<Self>, position: Point<f64, Logical>) {
|
fn focus_window_under_pointer(&mut self, pointer: &PointerHandle<Self>, position: Point<f64, Logical>) {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.overview.is_active() {
|
if self.overview.is_active() {
|
||||||
@@ -963,7 +963,7 @@ impl Ferese {
|
|||||||
pointer: &PointerHandle<Self>,
|
pointer: &PointerHandle<Self>,
|
||||||
requested: Point<f64, Logical>,
|
requested: Point<f64, Logical>,
|
||||||
) -> Point<f64, Logical> {
|
) -> Point<f64, Logical> {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return requested;
|
return requested;
|
||||||
}
|
}
|
||||||
let current = pointer.current_location();
|
let current = pointer.current_location();
|
||||||
@@ -1003,7 +1003,7 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_focused_pointer_constraint(&self, pointer: &PointerHandle<Self>) {
|
pub fn activate_focused_pointer_constraint(&self, pointer: &PointerHandle<Self>) {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let position = pointer.current_location();
|
let position = pointer.current_location();
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ impl crate::Ferese {
|
|||||||
pub(crate) fn restore_input_capture_focus(&mut self) {
|
pub(crate) fn restore_input_capture_focus(&mut self) {
|
||||||
self.input_capture.restore_focus = false;
|
self.input_capture.restore_focus = false;
|
||||||
self.cursor_redraw_pending = true;
|
self.cursor_redraw_pending = true;
|
||||||
if !self.session_lock.active {
|
if !self.session_lock.active() {
|
||||||
self.restore_keyboard_focus();
|
self.restore_keyboard_focus();
|
||||||
if let Some(pointer) = self.seat.get_pointer() {
|
if let Some(pointer) = self.seat.get_pointer() {
|
||||||
let location = pointer.current_location();
|
let location = pointer.current_location();
|
||||||
@@ -475,7 +475,7 @@ impl crate::Ferese {
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
let pointer = self.seat.get_pointer().expect("seat has pointer");
|
let pointer = self.seat.get_pointer().expect("seat has pointer");
|
||||||
let active = self.input_capture.active();
|
let active = self.input_capture.active();
|
||||||
let allowed = !self.session_lock.active
|
let allowed = !self.session_lock.active()
|
||||||
&& !self.overview.is_presenting()
|
&& !self.overview.is_presenting()
|
||||||
&& !pointer.is_grabbed()
|
&& !pointer.is_grabbed()
|
||||||
&& !self.swipe.active()
|
&& !self.swipe.active()
|
||||||
|
|||||||
@@ -431,7 +431,7 @@ fn discard_undelivered_screenshot(response: &Response) {
|
|||||||
|
|
||||||
impl Ferese {
|
impl Ferese {
|
||||||
fn handle_ipc_request(&mut self, owner: u64, request: Request) -> Response {
|
fn handle_ipc_request(&mut self, owner: u64, request: Request) -> Response {
|
||||||
if self.session_lock.active
|
if self.session_lock.active()
|
||||||
&& !matches!(
|
&& !matches!(
|
||||||
request.command.as_str(),
|
request.command.as_str(),
|
||||||
"theme-get"
|
"theme-get"
|
||||||
@@ -544,10 +544,10 @@ impl Ferese {
|
|||||||
return Ok(json!({}));
|
return Ok(json!({}));
|
||||||
}
|
}
|
||||||
"portal-shortcuts-poll" => {
|
"portal-shortcuts-poll" => {
|
||||||
return Ok(self.portal_shortcuts.poll(owner, self.session_lock.active));
|
return Ok(self.portal_shortcuts.poll(owner, self.session_lock.active()));
|
||||||
}
|
}
|
||||||
"input-capture-register" => {
|
"input-capture-register" => {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return Err(CommandError::new("session_locked", "Session is locked"));
|
return Err(CommandError::new("session_locked", "Session is locked"));
|
||||||
}
|
}
|
||||||
let id = self
|
let id = self
|
||||||
@@ -716,7 +716,7 @@ impl Ferese {
|
|||||||
let _ = response.try_send(Response::error(request.id, $code, $message));
|
let _ = response.try_send(Response::error(request.id, $code, $message));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
reject!("session_locked", "IPC unavailable while session is locked");
|
reject!("session_locked", "IPC unavailable while session is locked");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -813,7 +813,7 @@ impl Ferese {
|
|||||||
let reject = |message: String| {
|
let reject = |message: String| {
|
||||||
let _ = response.try_send(Response::error(request.id, "window_capture_failed", message));
|
let _ = response.try_send(Response::error(request.id, "window_capture_failed", message));
|
||||||
};
|
};
|
||||||
if self.session_lock.active || !screencopy::capture_allowed() {
|
if self.session_lock.active() || !screencopy::capture_allowed() {
|
||||||
reject("Screen capture is unavailable".into());
|
reject("Screen capture is unavailable".into());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
// when no output has a pending frame.
|
// when no output has a pending frame.
|
||||||
let wallpaper_changed = state.wallpaper.poll();
|
let wallpaper_changed = state.wallpaper.poll();
|
||||||
// All input/Wayland callbacks have returned, releasing seat locks.
|
// All input/Wayland callbacks have returned, releasing seat locks.
|
||||||
if state.focus_cycle.is_some() && (state.session_lock.active || state.input_capture.captures(1)) {
|
if state.focus_cycle.is_some() && (state.session_lock.active() || state.input_capture.captures(1)) {
|
||||||
state.cancel_focus_cycle();
|
state.cancel_focus_cycle();
|
||||||
}
|
}
|
||||||
// Coalesce cursor changes and redraw here, never inside cursor_image.
|
// Coalesce cursor changes and redraw here, never inside cursor_image.
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ fn mapped_tree(surface: &WlSurface) -> bool {
|
|||||||
impl Ferese {
|
impl Ferese {
|
||||||
pub(crate) fn callback_outputs(&self) -> HashMap<Id, Output> {
|
pub(crate) fn callback_outputs(&self) -> HashMap<Id, Output> {
|
||||||
let mut surfaces = Vec::new();
|
let mut surfaces = Vec::new();
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
for lock in self.session_lock.surfaces.values() {
|
for lock in self.session_lock.surfaces.values() {
|
||||||
smithay::desktop::utils::with_surfaces_surface_tree(lock.wl_surface(), |surface, _| {
|
smithay::desktop::utils::with_surfaces_surface_tree(lock.wl_surface(), |surface, _| {
|
||||||
surfaces.push(surface.clone());
|
surfaces.push(surface.clone());
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ pub(crate) fn sampled_output_elements(
|
|||||||
include_cursor: bool,
|
include_cursor: bool,
|
||||||
frame: &crate::state::FrameScene,
|
frame: &crate::state::FrameScene,
|
||||||
) -> Vec<AnimatedWindowRenderElement> {
|
) -> Vec<AnimatedWindowRenderElement> {
|
||||||
if state.session_lock.active {
|
if state.session_lock.active() {
|
||||||
let Some(geometry) = state.space.output_geometry(output) else {
|
let Some(geometry) = state.space.output_geometry(output) else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
|
|||||||
+208
-31
@@ -78,15 +78,35 @@ impl IdleSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
enum Lifecycle {
|
||||||
|
#[default]
|
||||||
|
Unlocked,
|
||||||
|
Acquiring(SessionLocker),
|
||||||
|
Locked(ExtSessionLockV1),
|
||||||
|
Orphaned,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lifecycle {
|
||||||
|
fn owner(&self) -> Option<&ExtSessionLockV1> {
|
||||||
|
match self {
|
||||||
|
Self::Acquiring(confirmation) => Some(confirmation.ext_session_lock()),
|
||||||
|
Self::Locked(owner) => Some(owner),
|
||||||
|
Self::Unlocked | Self::Orphaned => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_acquire(&self) -> bool {
|
||||||
|
self.owner().is_none_or(|owner| !owner.is_alive())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct Lock {
|
pub(crate) struct Lock {
|
||||||
pub active: bool,
|
lifecycle: Lifecycle,
|
||||||
owner: Option<ExtSessionLockV1>,
|
|
||||||
confirmation: Option<SessionLocker>,
|
|
||||||
pub surfaces: HashMap<Output, LockSurface>,
|
pub surfaces: HashMap<Output, LockSurface>,
|
||||||
pub backgrounds: HashMap<Output, smithay::backend::renderer::element::solid::SolidColorBuffer>,
|
pub backgrounds: HashMap<Output, smithay::backend::renderer::element::solid::SolidColorBuffer>,
|
||||||
presented: HashSet<Output>,
|
presented: HashSet<Output>,
|
||||||
confirmed: bool,
|
|
||||||
idle_since: Option<Instant>,
|
idle_since: Option<Instant>,
|
||||||
pub(crate) idle_opacity: f32,
|
pub(crate) idle_opacity: f32,
|
||||||
pub(crate) sleeping: bool,
|
pub(crate) sleeping: bool,
|
||||||
@@ -95,8 +115,12 @@ pub(crate) struct Lock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Lock {
|
impl Lock {
|
||||||
|
pub(crate) fn active(&self) -> bool {
|
||||||
|
!matches!(self.lifecycle, Lifecycle::Unlocked)
|
||||||
|
}
|
||||||
|
|
||||||
fn ready_for_idle<'a>(&self, outputs: impl Iterator<Item = &'a Output>) -> bool {
|
fn ready_for_idle<'a>(&self, outputs: impl Iterator<Item = &'a Output>) -> bool {
|
||||||
self.active && outputs.into_iter().all(|output| self.presented.contains(output))
|
self.active() && outputs.into_iter().all(|output| self.presented.contains(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn output_added(&mut self, output: &Output) {
|
pub(crate) fn output_added(&mut self, output: &Output) {
|
||||||
@@ -109,7 +133,7 @@ impl Lock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn activity(&mut self, now: Instant) -> bool {
|
fn activity(&mut self, now: Instant) -> bool {
|
||||||
if !self.active {
|
if !self.active() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let changed = self.idle_opacity != 0.0 || self.sleeping;
|
let changed = self.idle_opacity != 0.0 || self.sleeping;
|
||||||
@@ -220,26 +244,39 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lock_frame_presented(&mut self, output: &Output) {
|
pub(crate) fn lock_frame_presented(&mut self, output: &Output) {
|
||||||
if !self.session_lock.active {
|
if !self.session_lock.active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let newly_presented = self.session_lock.presented.insert(output.clone());
|
let newly_presented = self.session_lock.presented.insert(output.clone());
|
||||||
if self
|
self.confirm_lock_if_ready();
|
||||||
.space
|
|
||||||
.outputs()
|
|
||||||
.all(|output| self.session_lock.presented.contains(output))
|
|
||||||
&& let Some(confirmation) = self.session_lock.confirmation.take()
|
|
||||||
&& confirmation.ext_session_lock().is_alive()
|
|
||||||
{
|
|
||||||
confirmation.lock();
|
|
||||||
self.session_lock.confirmed = true;
|
|
||||||
tracing::info!("session lock confirmed after safe output frames");
|
|
||||||
}
|
|
||||||
if newly_presented {
|
if newly_presented {
|
||||||
self.refresh_lock_idle_policy();
|
self.refresh_lock_idle_policy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn confirm_lock_if_ready(&mut self) {
|
||||||
|
// With no outputs there is no visible content to protect. Re-evaluate
|
||||||
|
// here after acquisition and output removal as well as presentation.
|
||||||
|
if !self.session_lock.ready_for_idle(self.space.outputs()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(self.session_lock.lifecycle, Lifecycle::Acquiring(_)) {
|
||||||
|
let Lifecycle::Acquiring(confirmation) =
|
||||||
|
std::mem::replace(&mut self.session_lock.lifecycle, Lifecycle::Orphaned)
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let owner = confirmation.ext_session_lock().clone();
|
||||||
|
if owner.is_alive() {
|
||||||
|
confirmation.lock();
|
||||||
|
self.session_lock.lifecycle = Lifecycle::Locked(owner);
|
||||||
|
tracing::info!("session lock confirmed after safe output frames");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn configure_lock_surfaces(&mut self) {
|
pub(crate) fn configure_lock_surfaces(&mut self) {
|
||||||
for (output, surface) in &self.session_lock.surfaces {
|
for (output, surface) in &self.session_lock.surfaces {
|
||||||
crate::handlers::set_surface_tree_output(surface.wl_surface(), output);
|
crate::handlers::set_surface_tree_output(surface.wl_surface(), output);
|
||||||
@@ -272,17 +309,19 @@ impl SessionLockHandler for Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn lock(&mut self, confirmation: SessionLocker) {
|
fn lock(&mut self, confirmation: SessionLocker) {
|
||||||
if self.session_lock.active {
|
if !self.session_lock.lifecycle.can_acquire() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replacing an orphan never passes through Unlocked. Existing protected
|
||||||
|
// presentation facts remain valid, but old surfaces must not own input.
|
||||||
|
self.session_lock.lifecycle = Lifecycle::Acquiring(confirmation);
|
||||||
|
self.session_lock.surfaces.clear();
|
||||||
self.cancel_logout_confirmation();
|
self.cancel_logout_confirmation();
|
||||||
self.session_lock.active = true;
|
|
||||||
self.refresh_idle_inhibition();
|
self.refresh_idle_inhibition();
|
||||||
self.session_lock.idle_since = Some(Instant::now());
|
self.session_lock.idle_since = Some(Instant::now());
|
||||||
self.input_capture.disable_all();
|
self.input_capture.disable_all();
|
||||||
self.portal_session.set_locked(true);
|
self.portal_session.set_locked(true);
|
||||||
self.session_lock.owner = Some(confirmation.ext_session_lock().clone());
|
|
||||||
self.session_lock.confirmation = Some(confirmation);
|
|
||||||
for capture in self.pending_screencopies.drain(..) {
|
for capture in self.pending_screencopies.drain(..) {
|
||||||
capture.fail();
|
capture.fail();
|
||||||
}
|
}
|
||||||
@@ -315,6 +354,8 @@ impl SessionLockHandler for Ferese {
|
|||||||
keyboard.unset_grab(self);
|
keyboard.unset_grab(self);
|
||||||
keyboard.set_focus(self, None, serial);
|
keyboard.set_focus(self, None, serial);
|
||||||
self.intercepted_keys.clear();
|
self.intercepted_keys.clear();
|
||||||
|
self.lock_input_activity();
|
||||||
|
self.confirm_lock_if_ready();
|
||||||
crate::backends::direct::render_all(self);
|
crate::backends::direct::render_all(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,9 +400,9 @@ impl Dispatch<ExtSessionLockV1, SessionLockState> for Ferese {
|
|||||||
display: &DisplayHandle,
|
display: &DisplayHandle,
|
||||||
data_init: &mut DataInit<'_, Self>,
|
data_init: &mut DataInit<'_, Self>,
|
||||||
) {
|
) {
|
||||||
let owner = state.session_lock.owner.as_ref() == Some(lock);
|
let owner = state.session_lock.lifecycle.owner() == Some(lock);
|
||||||
if matches!(request, ext_session_lock_v1::Request::UnlockAndDestroy)
|
if matches!(request, ext_session_lock_v1::Request::UnlockAndDestroy)
|
||||||
&& (!owner || !state.session_lock.confirmed)
|
&& (!owner || !matches!(state.session_lock.lifecycle, Lifecycle::Locked(_)))
|
||||||
{
|
{
|
||||||
lock.post_error(
|
lock.post_error(
|
||||||
ext_session_lock_v1::Error::InvalidUnlock,
|
ext_session_lock_v1::Error::InvalidUnlock,
|
||||||
@@ -377,6 +418,20 @@ impl Dispatch<ExtSessionLockV1, SessionLockState> for Ferese {
|
|||||||
state, client, lock, request, data, display, data_init,
|
state, client, lock, request, data, display, data_init,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn destroyed(
|
||||||
|
state: &mut Self,
|
||||||
|
_: smithay::reexports::wayland_server::backend::ClientId,
|
||||||
|
lock: &ExtSessionLockV1,
|
||||||
|
_: &SessionLockState,
|
||||||
|
) {
|
||||||
|
if state.session_lock.lifecycle.owner() == Some(lock) {
|
||||||
|
state.session_lock.lifecycle = Lifecycle::Orphaned;
|
||||||
|
state.session_lock.surfaces.clear();
|
||||||
|
state.focus_lock_surface();
|
||||||
|
crate::backends::direct::render_all(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
smithay::reexports::wayland_server::delegate_global_dispatch!(Ferese: [ExtSessionLockManagerV1: smithay::wayland::session_lock::SessionLockManagerGlobalData] => SessionLockManagerState);
|
smithay::reexports::wayland_server::delegate_global_dispatch!(Ferese: [ExtSessionLockManagerV1: smithay::wayland::session_lock::SessionLockManagerGlobalData] => SessionLockManagerState);
|
||||||
smithay::reexports::wayland_server::delegate_dispatch!(Ferese: [ExtSessionLockManagerV1: ()] => SessionLockManagerState);
|
smithay::reexports::wayland_server::delegate_dispatch!(Ferese: [ExtSessionLockManagerV1: ()] => SessionLockManagerState);
|
||||||
@@ -386,6 +441,129 @@ smithay::reexports::wayland_server::delegate_dispatch!(Ferese: [ExtSessionLockSu
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "requires a private XDG_RUNTIME_DIR and permission to bind test sockets"]
|
||||||
|
fn zero_outputs_confirm_and_dead_owner_can_be_replaced_without_unlocking() {
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::os::unix::net::UnixStream;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use smithay::reexports::calloop::EventLoop;
|
||||||
|
use smithay::reexports::wayland_server::Display;
|
||||||
|
|
||||||
|
let runtime = std::path::PathBuf::from(std::env::var_os("XDG_RUNTIME_DIR").unwrap());
|
||||||
|
assert!(runtime.starts_with(std::env::temp_dir()));
|
||||||
|
let mut event_loop = EventLoop::try_new().unwrap();
|
||||||
|
let config = crate::config::Config::default().runtime_config().unwrap();
|
||||||
|
let mut state = Ferese::new(&mut event_loop, Display::new().unwrap(), config).unwrap();
|
||||||
|
|
||||||
|
let acquire = |state: &mut Ferese| {
|
||||||
|
let (server, mut wire) = UnixStream::pair().unwrap();
|
||||||
|
let client = state
|
||||||
|
.display_handle
|
||||||
|
.insert_client(server, Arc::new(crate::state::ClientState::default()))
|
||||||
|
.unwrap();
|
||||||
|
let manager = client
|
||||||
|
.create_resource::<ExtSessionLockManagerV1, (), Ferese>(&state.display_handle, 1, ())
|
||||||
|
.unwrap();
|
||||||
|
// ext_session_lock_manager_v1.lock(new_id=2).
|
||||||
|
for word in [manager.id().protocol_id(), (12u32 << 16) | 1, 2] {
|
||||||
|
wire.write_all(&word.to_ne_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
wire
|
||||||
|
};
|
||||||
|
let dispatch = |event_loop: &mut EventLoop<'static, Ferese>, state: &mut Ferese| {
|
||||||
|
event_loop.dispatch(Duration::from_millis(20), state).unwrap();
|
||||||
|
state.display_handle.flush_clients().unwrap();
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut first = acquire(&mut state);
|
||||||
|
dispatch(&mut event_loop, &mut state);
|
||||||
|
assert!(
|
||||||
|
matches!(state.session_lock.lifecycle, Lifecycle::Locked(_)),
|
||||||
|
"no outputs must confirm immediately"
|
||||||
|
);
|
||||||
|
first.set_read_timeout(Some(Duration::from_secs(1))).unwrap();
|
||||||
|
let mut event = [0u8; 8];
|
||||||
|
first.read_exact(&mut event).unwrap();
|
||||||
|
assert_eq!(u32::from_ne_bytes(event[..4].try_into().unwrap()), 2);
|
||||||
|
assert_eq!(
|
||||||
|
u32::from_ne_bytes(event[4..].try_into().unwrap()),
|
||||||
|
8u32 << 16,
|
||||||
|
"protocol locked event"
|
||||||
|
);
|
||||||
|
|
||||||
|
let owner = state.session_lock.lifecycle.owner().cloned();
|
||||||
|
|
||||||
|
let mut competitor = acquire(&mut state);
|
||||||
|
dispatch(&mut event_loop, &mut state);
|
||||||
|
assert_eq!(
|
||||||
|
state.session_lock.lifecycle.owner(),
|
||||||
|
owner.as_ref(),
|
||||||
|
"a live owner cannot be replaced"
|
||||||
|
);
|
||||||
|
// A rejected client must not unlock the confirmed owner.
|
||||||
|
for word in [2u32, (8u32 << 16) | 2] {
|
||||||
|
competitor.write_all(&word.to_ne_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
dispatch(&mut event_loop, &mut state);
|
||||||
|
assert_eq!(state.session_lock.lifecycle.owner(), owner.as_ref());
|
||||||
|
|
||||||
|
drop(competitor);
|
||||||
|
drop(first);
|
||||||
|
dispatch(&mut event_loop, &mut state);
|
||||||
|
assert!(matches!(state.session_lock.lifecycle, Lifecycle::Orphaned));
|
||||||
|
assert!(state.session_lock.active());
|
||||||
|
|
||||||
|
let replacement = acquire(&mut state);
|
||||||
|
dispatch(&mut event_loop, &mut state);
|
||||||
|
assert!(matches!(state.session_lock.lifecycle, Lifecycle::Locked(_)));
|
||||||
|
assert_ne!(state.session_lock.lifecycle.owner(), owner.as_ref());
|
||||||
|
drop(replacement);
|
||||||
|
dispatch(&mut event_loop, &mut state);
|
||||||
|
assert!(state.session_lock.active());
|
||||||
|
|
||||||
|
let output = Output::new(
|
||||||
|
"last-output".into(),
|
||||||
|
smithay::output::PhysicalProperties {
|
||||||
|
size: (0, 0).into(),
|
||||||
|
subpixel: smithay::output::Subpixel::Unknown,
|
||||||
|
make: "test".into(),
|
||||||
|
model: "test".into(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
output.change_current_state(
|
||||||
|
Some(smithay::output::Mode {
|
||||||
|
size: (800, 600).into(),
|
||||||
|
refresh: 60_000,
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
state.space.map_output(&output, (0, 0));
|
||||||
|
state.register_output(&output, "last-output".into());
|
||||||
|
|
||||||
|
let pending = acquire(&mut state);
|
||||||
|
dispatch(&mut event_loop, &mut state);
|
||||||
|
assert!(
|
||||||
|
matches!(state.session_lock.lifecycle, Lifecycle::Acquiring(_)),
|
||||||
|
"a newly added output requires a protected frame"
|
||||||
|
);
|
||||||
|
drop(pending);
|
||||||
|
dispatch(&mut event_loop, &mut state);
|
||||||
|
assert!(matches!(state.session_lock.lifecycle, Lifecycle::Orphaned));
|
||||||
|
|
||||||
|
let _replacement = acquire(&mut state);
|
||||||
|
dispatch(&mut event_loop, &mut state);
|
||||||
|
assert!(matches!(state.session_lock.lifecycle, Lifecycle::Acquiring(_)));
|
||||||
|
state.unregister_output(&output);
|
||||||
|
assert!(
|
||||||
|
matches!(state.session_lock.lifecycle, Lifecycle::Locked(_)),
|
||||||
|
"removing the last pending output must confirm"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dim_fade_respects_speed_without_changing_idle_deadlines() {
|
fn dim_fade_respects_speed_without_changing_idle_deadlines() {
|
||||||
let policy = IdleSettings::default();
|
let policy = IdleSettings::default();
|
||||||
@@ -463,30 +641,29 @@ mod tests {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
let mut lock = Lock {
|
let mut lock = Lock {
|
||||||
active: true,
|
lifecycle: Lifecycle::Orphaned,
|
||||||
..Lock::default()
|
..Lock::default()
|
||||||
};
|
};
|
||||||
assert!(!lock.ready_for_idle(std::iter::once(&output)));
|
assert!(!lock.ready_for_idle(std::iter::once(&output)));
|
||||||
lock.presented.insert(output.clone());
|
lock.presented.insert(output.clone());
|
||||||
assert!(lock.ready_for_idle(std::iter::once(&output)));
|
assert!(lock.ready_for_idle(std::iter::once(&output)));
|
||||||
assert!(!lock.confirmed);
|
assert!(matches!(lock.lifecycle, Lifecycle::Orphaned));
|
||||||
lock.output_added(&output);
|
lock.output_added(&output);
|
||||||
assert!(!lock.ready_for_idle(std::iter::once(&output)));
|
assert!(!lock.ready_for_idle(std::iter::once(&output)));
|
||||||
assert!(!lock.confirmed);
|
assert!(matches!(lock.lifecycle, Lifecycle::Orphaned));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn input_wakes_a_sleeping_lock_without_unlocking_or_losing_confirmation() {
|
fn input_wakes_a_sleeping_orphan_without_unlocking() {
|
||||||
let mut lock = Lock {
|
let mut lock = Lock {
|
||||||
active: true,
|
lifecycle: Lifecycle::Orphaned,
|
||||||
confirmed: true,
|
|
||||||
sleeping: true,
|
sleeping: true,
|
||||||
idle_opacity: 1.0,
|
idle_opacity: 1.0,
|
||||||
..Lock::default()
|
..Lock::default()
|
||||||
};
|
};
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
assert!(lock.activity(now));
|
assert!(lock.activity(now));
|
||||||
assert!(lock.active && lock.confirmed);
|
assert!(lock.active() && matches!(lock.lifecycle, Lifecycle::Orphaned));
|
||||||
assert!(!lock.sleeping);
|
assert!(!lock.sleeping);
|
||||||
assert_eq!(lock.idle_opacity, 0.0);
|
assert_eq!(lock.idle_opacity, 0.0);
|
||||||
assert_eq!(lock.idle_since, Some(now));
|
assert_eq!(lock.idle_since, Some(now));
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ impl Dispatch<FereseShellV1, ()> for Ferese {
|
|||||||
_display: &DisplayHandle,
|
_display: &DisplayHandle,
|
||||||
_data_init: &mut DataInit<'_, Self>,
|
_data_init: &mut DataInit<'_, Self>,
|
||||||
) {
|
) {
|
||||||
if state.session_lock.active {
|
if state.session_lock.active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match request {
|
match request {
|
||||||
@@ -207,7 +207,7 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn toggle_keybinding_guide(&mut self) -> bool {
|
pub(crate) fn toggle_keybinding_guide(&mut self) -> bool {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let Some(shell) = self
|
let Some(shell) = self
|
||||||
@@ -225,7 +225,7 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn request_logout_confirmation(&mut self) {
|
pub(crate) fn request_logout_confirmation(&mut self) {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let shells = self
|
let shells = self
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn focus_last_window(&mut self) {
|
pub(crate) fn focus_last_window(&mut self) {
|
||||||
if self.session_lock.active || self.input_capture.captures(1) {
|
if self.session_lock.active() || self.input_capture.captures(1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.cancel_focus_cycle();
|
self.cancel_focus_cycle();
|
||||||
@@ -43,7 +43,7 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cycle_focus(&mut self, reverse: bool, preview: bool) {
|
pub(crate) fn cycle_focus(&mut self, reverse: bool, preview: bool) {
|
||||||
if self.session_lock.active || self.input_capture.captures(1) {
|
if self.session_lock.active() || self.input_capture.captures(1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let available = self.focus_candidates();
|
let available = self.focus_candidates();
|
||||||
@@ -85,7 +85,7 @@ impl Ferese {
|
|||||||
let Some(mut cycle) = self.focus_cycle.take() else {
|
let Some(mut cycle) = self.focus_cycle.take() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !self.session_lock.active
|
if !self.session_lock.active()
|
||||||
&& !self.input_capture.captures(1)
|
&& !self.input_capture.captures(1)
|
||||||
&& let Some(id) = cycle.reconcile(&self.focus_candidates())
|
&& let Some(id) = cycle.reconcile(&self.focus_candidates())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::*;
|
|||||||
|
|
||||||
impl Ferese {
|
impl Ferese {
|
||||||
pub fn surface_under(&self, position: Point<f64, Logical>) -> Option<(WlSurface, Point<f64, Logical>)> {
|
pub fn surface_under(&self, position: Point<f64, Logical>) -> Option<(WlSurface, Point<f64, Logical>)> {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return self.lock_surface_under(position);
|
return self.lock_surface_under(position);
|
||||||
}
|
}
|
||||||
if self.input_capture.active() {
|
if self.input_capture.active() {
|
||||||
@@ -18,7 +18,7 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn layer_under(&self, position: Point<f64, Logical>) -> Option<(LayerSurface, WlSurface, Point<f64, Logical>)> {
|
pub fn layer_under(&self, position: Point<f64, Logical>) -> Option<(LayerSurface, WlSurface, Point<f64, Logical>)> {
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if let Some(layer) = self.layer_surface_under(position, &[Layer::Overlay, Layer::Top]) {
|
if let Some(layer) = self.layer_surface_under(position, &[Layer::Overlay, Layer::Top]) {
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ impl Ferese {
|
|||||||
self.refresh_input_capture_zones();
|
self.refresh_input_capture_zones();
|
||||||
let visible_workspaces = self.visible_workspace_ids();
|
let visible_workspaces = self.visible_workspace_ids();
|
||||||
self.reconcile_workspaces(&visible_workspaces);
|
self.reconcile_workspaces(&visible_workspaces);
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
self.configure_lock_surfaces();
|
self.configure_lock_surfaces();
|
||||||
}
|
}
|
||||||
// Consume idle time before setting new targets; it must not become a
|
// Consume idle time before setting new targets; it must not become a
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ impl Ferese {
|
|||||||
|
|
||||||
pub(super) fn focus_swipe_is_current(&self, swipe: &FocusSwipe) -> bool {
|
pub(super) fn focus_swipe_is_current(&self, swipe: &FocusSwipe) -> bool {
|
||||||
self.animations_enabled
|
self.animations_enabled
|
||||||
&& !self.session_lock.active
|
&& !self.session_lock.active()
|
||||||
&& !self.overview.is_presenting()
|
&& !self.overview.is_presenting()
|
||||||
&& self.workspaces.active_id() == swipe.workspace
|
&& self.workspaces.active_id() == swipe.workspace
|
||||||
&& self.focused_window == Some(swipe.from)
|
&& self.focused_window == Some(swipe.from)
|
||||||
@@ -552,7 +552,7 @@ impl Ferese {
|
|||||||
if self.input_capture.captures(1) {
|
if self.input_capture.captures(1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
self.focus_lock_surface();
|
self.focus_lock_surface();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ impl Ferese {
|
|||||||
self.reset_animation_clock();
|
self.reset_animation_clock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.session_lock.active {
|
if self.session_lock.active() {
|
||||||
self.session_lock.output_added(output);
|
self.session_lock.output_added(output);
|
||||||
self.lock_input_activity();
|
self.lock_input_activity();
|
||||||
}
|
}
|
||||||
@@ -77,6 +77,8 @@ impl Ferese {
|
|||||||
self.session_lock.surfaces.remove(output);
|
self.session_lock.surfaces.remove(output);
|
||||||
self.session_lock.backgrounds.remove(output);
|
self.session_lock.backgrounds.remove(output);
|
||||||
self.session_lock.output_removed(output);
|
self.session_lock.output_removed(output);
|
||||||
|
self.confirm_lock_if_ready();
|
||||||
|
self.refresh_lock_idle_policy();
|
||||||
|
|
||||||
match self.output_workspaces.disconnect(output_id) {
|
match self.output_workspaces.disconnect(output_id) {
|
||||||
Ok(Some(target)) => {
|
Ok(Some(target)) => {
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
|||||||
state.display_presentation.presented(&output);
|
state.display_presentation.presented(&output);
|
||||||
state.refresh_idle_inhibition();
|
state.refresh_idle_inhibition();
|
||||||
|
|
||||||
if state.session_lock.active {
|
if state.session_lock.active() {
|
||||||
state.lock_frame_presented(&output);
|
state.lock_frame_presented(&output);
|
||||||
}
|
}
|
||||||
let elapsed = render_started.elapsed();
|
let elapsed = render_started.elapsed();
|
||||||
@@ -226,7 +226,7 @@ pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<()
|
|||||||
|
|
||||||
fn send_nested_frame_callbacks(state: &mut Ferese, output: &Output) {
|
fn send_nested_frame_callbacks(state: &mut Ferese, output: &Output) {
|
||||||
let eligible = state.callback_outputs();
|
let eligible = state.callback_outputs();
|
||||||
if state.session_lock.active {
|
if state.session_lock.active() {
|
||||||
state.lock_frame_callbacks(output);
|
state.lock_frame_callbacks(output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ class NativeLockTest(unittest.TestCase):
|
|||||||
result=capture(root/'after-crash.png')
|
result=capture(root/'after-crash.png')
|
||||||
assert result.returncode != 0, 'Capture unexpectedly succeeded after locker crash'
|
assert result.returncode != 0, 'Capture unexpectedly succeeded after locker crash'
|
||||||
print('Capture remains denied after locker crash',flush=True)
|
print('Capture remains denied after locker crash',flush=True)
|
||||||
|
locker=subprocess.Popen(['bwrap','--bind','/','/','--dev-bind','/dev','/dev','--ro-bind',str(pam),'/etc/pam.d','--unshare-user','--',str(binary_dir / 'ferese-lock')],env=childenv,stdout=subprocess.DEVNULL,stderr=locker_log,start_new_session=True)
|
||||||
|
assert locker.wait(timeout=20) == 0, (root/'locker.log').read_text()
|
||||||
|
result=capture(root/'replacement.png')
|
||||||
|
assert result.returncode != 0, 'Capture unexpectedly succeeded after locker takeover'
|
||||||
|
print('Replacement locker confirmed; capture remains denied',flush=True)
|
||||||
finally:
|
finally:
|
||||||
if locker is not None:
|
if locker is not None:
|
||||||
try: os.killpg(locker.pid, signal.SIGKILL)
|
try: os.killpg(locker.pid, signal.SIGKILL)
|
||||||
|
|||||||
Reference in new issue
Block a user