perf: async fprintd initialization for instant window display

Move all fprintd D-Bus calls (init, availability check, claim, verify)
from synchronous to async using gio futures. Windows now appear
immediately without waiting for D-Bus — fingerprint label fades in
once fprintd is ready. Single shared FingerprintListener across all
monitors instead of one per monitor.
This commit is contained in:
2026-03-28 09:57:56 +01:00
parent 58c076198f
commit 13b329cd98
3 changed files with 194 additions and 130 deletions
+37 -52
View File
@@ -32,32 +32,31 @@ pub struct FingerprintListener {
}
impl FingerprintListener {
/// Create a new FingerprintListener.
/// Connects to fprintd synchronously — call before creating GTK windows.
/// Create a lightweight FingerprintListener without any D-Bus calls.
/// Call `init_async().await` afterwards to connect to fprintd.
pub fn new() -> Self {
let mut listener = FingerprintListener {
FingerprintListener {
device_proxy: None,
signal_id: None,
running: false,
failed_attempts: 0,
on_success: None,
on_failure: None,
};
listener.init_device();
listener
}
}
/// Connect to fprintd and get the default device.
fn init_device(&mut self) {
let manager = match gio::DBusProxy::for_bus_sync(
/// Connect to fprintd and get the default device asynchronously.
pub async fn init_async(&mut self) {
let manager = match gio::DBusProxy::for_bus_future(
gio::BusType::System,
gio::DBusProxyFlags::NONE,
None,
FPRINTD_BUS_NAME,
FPRINTD_MANAGER_PATH,
FPRINTD_MANAGER_IFACE,
gio::Cancellable::NONE,
) {
)
.await
{
Ok(m) => m,
Err(e) => {
log::debug!("fprintd manager not available: {e}");
@@ -66,13 +65,10 @@ impl FingerprintListener {
};
// Call GetDefaultDevice
let result = match manager.call_sync(
"GetDefaultDevice",
None,
gio::DBusCallFlags::NONE,
-1,
gio::Cancellable::NONE,
) {
let result = match manager
.call_future("GetDefaultDevice", None, gio::DBusCallFlags::NONE, -1)
.await
{
Ok(r) => r,
Err(e) => {
log::debug!("fprintd GetDefaultDevice failed: {e}");
@@ -86,15 +82,16 @@ impl FingerprintListener {
return;
}
match gio::DBusProxy::for_bus_sync(
match gio::DBusProxy::for_bus_future(
gio::BusType::System,
gio::DBusProxyFlags::NONE,
None,
FPRINTD_BUS_NAME,
&device_path,
FPRINTD_DEVICE_IFACE,
gio::Cancellable::NONE,
) {
)
.await
{
Ok(proxy) => {
self.device_proxy = Some(proxy);
}
@@ -104,21 +101,18 @@ impl FingerprintListener {
}
}
/// Check if fprintd is available and the user has enrolled fingerprints.
pub fn is_available(&self, username: &str) -> bool {
/// Check if fprintd is available and the user has enrolled fingerprints (async).
pub async fn is_available_async(&self, username: &str) -> bool {
let proxy = match &self.device_proxy {
Some(p) => p,
None => return false,
};
let args = glib::Variant::from((&username,));
match proxy.call_sync(
"ListEnrolledFingers",
Some(&args),
gio::DBusCallFlags::NONE,
-1,
gio::Cancellable::NONE,
) {
match proxy
.call_future("ListEnrolledFingers", Some(&args), gio::DBusCallFlags::NONE, -1)
.await
{
Ok(result) => {
// Result is a tuple of (array of strings)
let fingers: Vec<String> = result.child_get::<Vec<String>>(0);
@@ -129,9 +123,10 @@ impl FingerprintListener {
}
/// Start listening for fingerprint verification.
/// Claims the device and starts verification using async D-Bus calls.
/// Connects the D-Bus g-signal handler internally. The `listener` parameter
/// must be the same `Rc<RefCell<FingerprintListener>>` that owns `self`.
pub fn start<F, G>(
pub async fn start_async<F, G>(
listener: &Rc<RefCell<FingerprintListener>>,
username: &str,
on_success: F,
@@ -156,34 +151,24 @@ impl FingerprintListener {
// Claim the device
let args = glib::Variant::from((&username,));
if let Err(e) = proxy.call_sync(
"Claim",
Some(&args),
gio::DBusCallFlags::NONE,
-1,
gio::Cancellable::NONE,
) {
if let Err(e) = proxy
.call_future("Claim", Some(&args), gio::DBusCallFlags::NONE, -1)
.await
{
log::error!("Failed to claim fingerprint device: {e}");
return;
}
// Start verification
let start_args = glib::Variant::from((&"any",));
if let Err(e) = proxy.call_sync(
"VerifyStart",
Some(&start_args),
gio::DBusCallFlags::NONE,
-1,
gio::Cancellable::NONE,
) {
if let Err(e) = proxy
.call_future("VerifyStart", Some(&start_args), gio::DBusCallFlags::NONE, -1)
.await
{
log::error!("Failed to start fingerprint verification: {e}");
let _ = proxy.call_sync(
"Release",
None,
gio::DBusCallFlags::NONE,
-1,
gio::Cancellable::NONE,
);
let _ = proxy
.call_future("Release", None, gio::DBusCallFlags::NONE, -1)
.await;
return;
}