Skip to content

Commit 15a1d96

Browse files
committed
feat: add fprint support
1 parent 8981501 commit 15a1d96

File tree

3 files changed

+265
-13
lines changed

3 files changed

+265
-13
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2025 Titouan Real <[email protected]>
2+
// SPDX-License-Identifier: GPL-3.0-only
3+
4+
use zbus::{proxy, Connection};
5+
6+
#[proxy(
7+
interface = "net.reactivated.Fprint.Manager",
8+
default_service = "net.reactivated.Fprint",
9+
default_path = "/net/reactivated/Fprint/Manager"
10+
)]
11+
pub trait FprintManager {
12+
fn get_default_device(&self) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
13+
14+
fn get_devices(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
15+
}
16+
17+
#[proxy(
18+
interface = "net.reactivated.Fprint.Device",
19+
default_service = "net.reactivated.Fprint",
20+
assume_defaults = true
21+
)]
22+
pub trait FprintDevice {
23+
fn claim(&self, username: &str) -> zbus::Result<()>;
24+
25+
fn delete_enrolled_finger(&self, finger_name: &str) -> zbus::Result<()>;
26+
27+
fn delete_enrolled_fingers(&self, username: &str) -> zbus::Result<()>;
28+
29+
fn delete_enrolled_fingers2(&self) -> zbus::Result<()>;
30+
31+
fn enroll_start(&self, finger_name: &str) -> zbus::Result<()>;
32+
33+
fn enroll_stop(&self) -> zbus::Result<()>;
34+
35+
fn list_enrolled_fingers(&self, username: &str) -> zbus::Result<Vec<String>>;
36+
37+
fn release(&self) -> zbus::Result<()>;
38+
39+
fn verify_start(&self, finger_name: &str) -> zbus::Result<()>;
40+
41+
fn verify_stop(&self) -> zbus::Result<()>;
42+
43+
#[zbus(signal)]
44+
fn enroll_status(&self, result: &str, done: bool) -> zbus::Result<()>;
45+
46+
#[zbus(signal)]
47+
fn verify_finger_selected(&self, finger_name: &str) -> zbus::Result<()>;
48+
49+
#[zbus(signal)]
50+
fn verify_status(&self, result: &str, done: bool) -> zbus::Result<()>;
51+
52+
#[zbus(property, name = "finger-needed")]
53+
fn finger_needed(&self) -> zbus::Result<bool>;
54+
55+
#[zbus(property, name = "finger-present")]
56+
fn finger_present(&self) -> zbus::Result<bool>;
57+
58+
#[zbus(property, name = "name")]
59+
fn name(&self) -> zbus::Result<String>;
60+
61+
#[zbus(property, name = "num-enroll-stages")]
62+
fn num_enroll_stages(&self) -> zbus::Result<i32>;
63+
64+
#[zbus(property, name = "scan-type")]
65+
fn scan_type(&self) -> zbus::Result<String>;
66+
}
67+
68+
pub async fn get_fprint_manager_proxy<'a>() -> Result<FprintManagerProxy<'a>, ()> {
69+
let connection = match Connection::system().await {
70+
Ok(c) => c,
71+
Err(e) => {
72+
tracing::error!("zbus connection failed. {e}");
73+
return Err(());
74+
}
75+
};
76+
77+
match FprintManagerProxy::new(&connection).await {
78+
Ok(d) => Ok(d),
79+
Err(e) => {
80+
tracing::error!("Fprint daemon proxy can't be created. Is it installed? {e}");
81+
Err(())
82+
}
83+
}
84+
}
85+
86+
pub async fn get_fprint_device_proxy<'a>(
87+
device_object_path: zbus::zvariant::OwnedObjectPath,
88+
) -> Result<FprintDeviceProxy<'a>, ()> {
89+
let connection = match Connection::system().await {
90+
Ok(c) => c,
91+
Err(e) => {
92+
tracing::error!("zbus connection failed. {e}");
93+
return Err(());
94+
}
95+
};
96+
97+
let proxy_builder = match FprintDeviceProxy::builder(&connection).path(device_object_path) {
98+
Ok(d) => d,
99+
Err(e) => {
100+
tracing::error!("{e}");
101+
return Err(());
102+
}
103+
};
104+
105+
match proxy_builder.build().await {
106+
Ok(d) => Ok(d),
107+
Err(e) => {
108+
tracing::error!("Fprint daemon proxy can't be created. Is it installed? {e}");
109+
Err(())
110+
}
111+
}
112+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
mod fprintdaemon;
2+
3+
#[derive(Clone, Debug)]
4+
pub enum FingerName {
5+
Any,
6+
LeftThumb,
7+
LeftIndexFinger,
8+
LeftMiddleFinger,
9+
LeftRingFinger,
10+
LeftLittleFinger,
11+
RightThumb,
12+
RightIndexFinger,
13+
RightMiddleFinger,
14+
RightRingFinger,
15+
RightLittleFinger,
16+
}
17+
18+
#[derive(Clone, Debug, Default)]
19+
pub struct FprintDeviceInfo {
20+
pub name: String,
21+
pub enrolled_fingers: Vec<FingerName>,
22+
}
23+
24+
#[derive(Clone, Debug, Default)]
25+
pub struct FprintInfo {
26+
pub default_device: Option<FprintDeviceInfo>,
27+
pub other_devices: Vec<FprintDeviceInfo>,
28+
}
29+
30+
async fn get_fprint_device_info(
31+
object_path: zbus::zvariant::OwnedObjectPath,
32+
username: &str,
33+
) -> Result<FprintDeviceInfo, ()> {
34+
let daemon = fprintdaemon::get_fprint_device_proxy(object_path).await?;
35+
let name = match daemon.name().await {
36+
Ok(name) => name,
37+
Err(e) => {
38+
tracing::error!("{e}");
39+
return Err(());
40+
}
41+
};
42+
let fingers = match daemon.list_enrolled_fingers(username).await {
43+
Ok(fingers) => {
44+
let mut fingers_vec = Vec::new();
45+
for finger in fingers {
46+
fingers_vec.push(match finger.as_ref() {
47+
"left-thumb" => FingerName::LeftThumb,
48+
"left-index-finger" => FingerName::LeftIndexFinger,
49+
"left-middle-finger" => FingerName::LeftMiddleFinger,
50+
"left-ring-finger" => FingerName::LeftRingFinger,
51+
"left-little-finger" => FingerName::LeftLittleFinger,
52+
"right-thumb" => FingerName::RightThumb,
53+
"right-index-finger" => FingerName::RightIndexFinger,
54+
"right-middle-finger" => FingerName::RightMiddleFinger,
55+
"right-ring-finger" => FingerName::RightRingFinger,
56+
"right-little-finger" => FingerName::RightLittleFinger,
57+
other => {
58+
tracing::error!("Received unexpected finger name: {other}");
59+
return Err(());
60+
}
61+
});
62+
}
63+
fingers_vec
64+
}
65+
Err(e) => {
66+
tracing::error!("{e}");
67+
return Err(());
68+
}
69+
};
70+
71+
Ok(FprintDeviceInfo {
72+
name,
73+
enrolled_fingers: fingers,
74+
})
75+
}
76+
77+
pub async fn get_fprint_info(username: &str) -> Result<FprintInfo, ()> {
78+
let daemon = fprintdaemon::get_fprint_manager_proxy().await?;
79+
80+
let default_device_path = match daemon.get_default_device().await {
81+
Ok(device) => Some(device),
82+
Err(zbus::Error::MethodError(_, _, _)) => None,
83+
Err(e) => {
84+
tracing::error!("{e}");
85+
return Err(());
86+
}
87+
};
88+
89+
let default_device_info = match default_device_path {
90+
Some(ref path) => Some(get_fprint_device_info(path.clone(), username).await?),
91+
None => None,
92+
};
93+
94+
tracing::info!("Default device: {:?}", default_device_info);
95+
96+
let other_devices = match daemon.get_devices().await {
97+
Ok(mut devices) => {
98+
if let Some(default_device) = default_device_path {
99+
devices.retain(|device| device != &default_device);
100+
}
101+
let mut devices_info = Vec::new();
102+
for device in devices {
103+
devices_info.push(get_fprint_device_info(device, username).await?)
104+
}
105+
devices_info
106+
}
107+
Err(e) => {
108+
tracing::error!("{e}");
109+
return Err(());
110+
}
111+
};
112+
113+
tracing::info!("Other devices: {:?}", other_devices);
114+
115+
Ok(FprintInfo {
116+
default_device: default_device_info,
117+
other_devices,
118+
})
119+
}

cosmic-settings/src/pages/system/users/mod.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright 2024 System76 <[email protected]>
22
// SPDX-License-Identifier: GPL-3.0-only
33

4+
mod fprint;
45
mod getent;
56

67
use cosmic::{
@@ -10,6 +11,7 @@ use cosmic::{
1011
Apply, Element,
1112
};
1213
use cosmic_settings_page::{self as page, section, Section};
14+
use fprint::FprintInfo;
1315
use slab::Slab;
1416
use slotmap::SlotMap;
1517
use std::{
@@ -37,6 +39,7 @@ pub struct User {
3739
full_name_edit: bool,
3840
password_edit: bool,
3941
username_edit: bool,
42+
fprint_info: FprintInfo,
4043
is_admin: bool,
4144
}
4245

@@ -278,6 +281,9 @@ impl Page {
278281
admin_group.map_or(false, |group| group.users.contains(&user.username))
279282
}
280283
},
284+
fprint_info: fprint::get_fprint_info(&user.username)
285+
.await
286+
.unwrap_or_default(),
281287
username: String::from(user.username),
282288
full_name: String::from(user.full_name),
283289
password: String::new(),
@@ -634,19 +640,34 @@ fn user_list() -> Section<crate::pages::Message> {
634640
let mut details_list = widget::list_column()
635641
.add(settings::item(&page.fullname_label, fullname))
636642
.add(settings::item(&page.username_label, username))
637-
.add(settings::item(&page.password_label, password))
638-
.add(settings::item_row(vec![
639-
column::with_capacity(2)
640-
.push(text::body(crate::fl!("administrator")))
641-
.push(text::caption(crate::fl!("administrator", "desc")))
642-
.into(),
643-
widget::horizontal_space().width(Length::Fill).into(),
644-
widget::toggler(user.is_admin)
645-
.on_toggle(|enabled| {
646-
Message::SelectedUserSetAdmin(user.id, enabled)
647-
})
648-
.into(),
649-
]));
643+
.add(settings::item(&page.password_label, password));
644+
645+
if let Some(device) = &user.fprint_info.default_device {
646+
details_list = details_list.add(settings::item(
647+
format!("{} (Default Device)", device.name.clone()),
648+
widget::text(format!("{:?}", device.enrolled_fingers)),
649+
));
650+
}
651+
652+
for device in &user.fprint_info.other_devices {
653+
details_list = details_list.add(settings::item(
654+
device.name.clone(),
655+
widget::text(format!("{:?}", device.enrolled_fingers)),
656+
));
657+
}
658+
659+
details_list = details_list.add(settings::item_row(vec![
660+
column::with_capacity(2)
661+
.push(text::body(crate::fl!("administrator")))
662+
.push(text::caption(crate::fl!("administrator", "desc")))
663+
.into(),
664+
widget::horizontal_space().width(Length::Fill).into(),
665+
widget::toggler(user.is_admin)
666+
.on_toggle(|enabled| {
667+
Message::SelectedUserSetAdmin(user.id, enabled)
668+
})
669+
.into(),
670+
]));
650671

651672
if page.users.len() > 1 {
652673
details_list = details_list.add(settings::item_row(vec![

0 commit comments

Comments
 (0)