Add live config reload, session locking and managed daemons

Refine focus gradients, shell workspace controls and popup motion. Correct floating stacking and preserve client-chosen dialog sizes. Add lock protocol probes and session integration.
This commit is contained in:
Samuel Onoja committed 2026-09-26 19:03:37 +01:00
1 parent b422448d88
commit 672b4e2b60
41 files changed
+2694 -298

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
[package]
name = "ferese-lock-probe"
version.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
[dependencies]
wayland-client.workspace = true
wayland-protocols = { version = "0.32.13", features = ["client", "staging"] }
+109
View File
@@ -0,0 +1,109 @@
//! Deliberately has no password authentication. Nested test sessions only.
use std::{error::Error, time::Duration};
use wayland_client::{Connection, Dispatch, QueueHandle, protocol::wl_registry};
use wayland_protocols::ext::session_lock::v1::client::{
ext_session_lock_manager_v1::ExtSessionLockManagerV1,
ext_session_lock_v1::{self, ExtSessionLockV1},
};
#[derive(Default)]
struct Probe {
manager: Option<ExtSessionLockManagerV1>,
locked: bool,
rejected: bool,
}
impl Dispatch<wl_registry::WlRegistry, ()> for Probe {
fn event(
state: &mut Self,
registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &(),
_: &Connection,
qh: &QueueHandle<Self>,
) {
if let wl_registry::Event::Global {
name, interface, ..
} = event
{
if interface == "ext_session_lock_manager_v1" {
state.manager = Some(registry.bind(name, 1, qh, ()));
}
}
}
}
wayland_client::delegate_noop!(Probe: ignore ExtSessionLockManagerV1);
impl Dispatch<ExtSessionLockV1, bool> for Probe {
fn event(
state: &mut Self,
_: &ExtSessionLockV1,
event: ext_session_lock_v1::Event,
competing: &bool,
_: &Connection,
_: &QueueHandle<Self>,
) {
match event {
ext_session_lock_v1::Event::Locked if !competing => state.locked = true,
ext_session_lock_v1::Event::Finished if *competing => state.rejected = true,
ext_session_lock_v1::Event::Finished => panic!("lock rejected"),
_ => {}
}
}
}
fn main() -> Result<(), Box<dyn Error>> {
if std::env::var_os("FERESE_LOCK_TEST").is_none() {
return Err(
"set FERESE_LOCK_TEST=1; this probe is only for disposable nested sessions".into(),
);
}
let mode = std::env::args().nth(1).unwrap_or_else(|| "unlock".into());
let connection = Connection::connect_to_env()?;
let mut queue = connection.new_event_queue();
let qh = queue.handle();
connection.display().get_registry(&qh, ());
let mut state = Probe::default();
queue.roundtrip(&mut state)?;
let manager = state
.manager
.clone()
.ok_or("session lock protocol unavailable")?;
let lock = manager.lock(&qh, false);
if mode == "invalid-unlock" {
lock.unlock_and_destroy();
assert!(
queue.roundtrip(&mut state).is_err(),
"premature unlock accepted"
);
println!("PASS premature unlock disconnected");
return Ok(());
}
while !state.locked {
queue.blocking_dispatch(&mut state)?;
}
println!("PASS locked event received after safe frame");
match mode.as_str() {
"crash" => return Ok(()),
"hold" => std::thread::sleep(Duration::from_secs(10)),
"competing" | "attack" => {
let other = manager.lock(&qh, true);
queue.roundtrip(&mut state)?;
assert!(state.rejected, "competing lock not rejected");
if mode == "attack" {
other.unlock_and_destroy();
assert!(
queue.roundtrip(&mut state).is_err(),
"rejected lock unlocked owner"
);
println!("PASS rejected owner cannot unlock");
return Ok(());
}
other.destroy();
}
"unlock" => {}
_ => return Err("use unlock, hold, crash, competing, attack, or invalid-unlock".into()),
}
lock.unlock_and_destroy();
queue.roundtrip(&mut state)?;
println!("PASS owner unlocked");
Ok(())
}