Skip to content

fix: clean lock-surface teardown + roundtrip on unlock (Hyprland multi-output)#6

Open
josephdunn wants to merge 1 commit into
JorySeverijnse:masterfrom
josephdunn:fix/lock-surface-cleanup-order
Open

fix: clean lock-surface teardown + roundtrip on unlock (Hyprland multi-output)#6
josephdunn wants to merge 1 commit into
JorySeverijnse:masterfrom
josephdunn:fix/lock-surface-cleanup-order

Conversation

@josephdunn

Copy link
Copy Markdown

Problem

On a multi-output Hyprland setup, unlocking rustlock consistently triggers Hyprland's "lockscreen died" failsafe screen instead of unlocking, even though rustlock exits cleanly with status 0. Single-output setups are unaffected (which is presumably why this hasn't shown up before).

Reproduced on Hyprland with three outputs (two rotated 2560x1440 + one 3840x2160).

Root cause

Two issues in the unlock path, both tolerated by Hyprland on single-output but not on multi-output:

  1. Wrong teardown order. ext-session-lock-v1 recommends destroying each ext_session_lock_surface_v1 before issuing unlock_and_destroy on ext_session_lock_v1. rustlock did the opposite: it called session_lock.unlock() first inside handle_auth_result, then cleared lock_surfaces from the auth-feedback callback afterwards.

  2. No event drain before disconnect. After unlock_and_destroy, the compositor sends back wl_keyboard.leave, wl_pointer.leave, and wl_display.delete_id events. Without dispatching those, rustlock disconnects mid-conversation; Hyprland treats that as an unclean exit.

A WAYLAND_DEBUG=client trace of the baseline showed unlock_and_destroy sent first, then lock_surface.destroy() / wl_surface.destroy() pairs, then disconnect — no wl_display.error from the compositor, just the failsafe afterwards. A trace of the fix shows the spec-recommended order followed by a brief drain of leave/delete_id events before exit, and Hyprland unlocks normally.

Fix

  • Move self.lock_surfaces.clear() into handle_auth_result so it runs before session_lock.unlock(). Remove the now-redundant clear from the auth-feedback callback.
  • Add state.conn.roundtrip() after the main event loop exits to drain the post-unlock events before disconnect.

Total change: +11/−3 in src/main.rs.

Test plan

  • Multi-output Hyprland (3 outputs): unlock works, no failsafe.
  • Single-output: tested on single-screen laptop — both changes should be no-ops there.
  • WAYLAND_DEBUG=client trace confirms teardown order matches the spec recommendation and leave/delete_id events are drained before disconnect.

Note

AI (Claude) helped me diagnose and write the fix.

Hyprland was treating multi-output rustlock unlocks as client
crashes and showing its "lockscreen died" failsafe instead of
unlocking. Two issues:

1. Order. ext-session-lock-v1 recommends destroying every
   ext_session_lock_surface_v1 before issuing unlock_and_destroy
   on ext_session_lock_v1. rustlock did the opposite. Move
   lock_surfaces.clear() into handle_auth_result so it runs
   before session_lock.unlock(), and drop the now-redundant
   clear from the auth-feedback callback.

2. Drain. After unlock_and_destroy the compositor sends
   keyboard/pointer leave events and delete_id acks; if the
   client disconnects before processing them, Hyprland treats
   the disconnect as unclean. Add a conn.roundtrip() after the
   main loop to drain those events before exit.

Single-output setups tolerate both of these (which is why the
bug does not show up on the upstream author's laptop). The
failsafe only reproduces with multiple outputs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant