Skip to content

Commit 2417364

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

File tree

6 files changed

+362
-16
lines changed

6 files changed

+362
-16
lines changed

Cargo.lock

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cosmic-settings/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ derive_setters = "0.1.6"
3232
dirs = "5.0.1"
3333
downcast-rs = "1.2.1"
3434
eyre = "0.6.12"
35+
fprint-zbus = { git = "https://github.com/TitouanReal/dbus-settings-bindings", branch = "fprint-support", optional = true }
3536
freedesktop-desktop-entry = "0.7.5"
3637
futures = "0.3.30"
3738
hostname-validator = "1.1.1"
@@ -133,7 +134,7 @@ page-networking = [
133134
page-power = ["dep:upower_dbus", "dep:zbus"]
134135
page-region = ["dep:lichen-system", "dep:locale1"]
135136
page-sound = ["dep:cosmic-settings-subscriptions"]
136-
page-users = ["dep:accounts-zbus"]
137+
page-users = ["dep:accounts-zbus", "dep:fprint-zbus"]
137138
page-window-management = ["dep:cosmic-settings-config"]
138139
page-workspaces = ["dep:cosmic-comp-config"]
139140

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
// Copyright 2025 Titouan Real <[email protected]>
2+
// SPDX-License-Identifier: GPL-3.0-only
3+
4+
use std::{process::Output, str::FromStr};
5+
6+
use fprint_zbus::{FprintDeviceProxy, FprintManagerProxy};
7+
use zbus::Connection;
8+
9+
#[derive(Clone, Debug, PartialEq, Eq)]
10+
pub enum FingerName {
11+
Any,
12+
LeftThumb,
13+
LeftIndexFinger,
14+
LeftMiddleFinger,
15+
LeftRingFinger,
16+
LeftLittleFinger,
17+
RightThumb,
18+
RightIndexFinger,
19+
RightMiddleFinger,
20+
RightRingFinger,
21+
RightLittleFinger,
22+
}
23+
24+
impl FingerName {
25+
pub const ALL: &[FingerName] = &[
26+
Self::LeftThumb,
27+
Self::LeftIndexFinger,
28+
Self::LeftMiddleFinger,
29+
Self::LeftRingFinger,
30+
Self::LeftLittleFinger,
31+
Self::RightThumb,
32+
Self::RightIndexFinger,
33+
Self::RightMiddleFinger,
34+
Self::RightRingFinger,
35+
Self::RightLittleFinger,
36+
];
37+
}
38+
39+
impl FromStr for FingerName {
40+
type Err = ();
41+
42+
fn from_str(finger: &str) -> Result<Self, Self::Err> {
43+
match finger.as_ref() {
44+
"any" => Ok(Self::Any),
45+
"left-thumb" => Ok(Self::LeftThumb),
46+
"left-index-finger" => Ok(Self::LeftIndexFinger),
47+
"left-middle-finger" => Ok(Self::LeftMiddleFinger),
48+
"left-ring-finger" => Ok(Self::LeftRingFinger),
49+
"left-little-finger" => Ok(Self::LeftLittleFinger),
50+
"right-thumb" => Ok(Self::RightThumb),
51+
"right-index-finger" => Ok(Self::RightIndexFinger),
52+
"right-middle-finger" => Ok(Self::RightMiddleFinger),
53+
"right-ring-finger" => Ok(Self::RightRingFinger),
54+
"right-little-finger" => Ok(Self::RightLittleFinger),
55+
other => Err(()),
56+
}
57+
}
58+
}
59+
60+
impl ToString for FingerName {
61+
fn to_string(&self) -> String {
62+
match self {
63+
Self::Any => fl!("finger", "any"),
64+
Self::LeftThumb => fl!("finger", "left-thumb"),
65+
Self::LeftIndexFinger => fl!("finger", "left-index-finger"),
66+
Self::LeftMiddleFinger => fl!("finger", "left-middle-finger"),
67+
Self::LeftRingFinger => fl!("finger", "left-ring-finger"),
68+
Self::LeftLittleFinger => fl!("finger", "left-little-finger"),
69+
Self::RightThumb => fl!("finger", "right-thumb"),
70+
Self::RightIndexFinger => fl!("finger", "right-index-finger"),
71+
Self::RightMiddleFinger => fl!("finger", "right-middle-finger"),
72+
Self::RightRingFinger => fl!("finger", "right-ring-finger"),
73+
Self::RightLittleFinger => fl!("finger", "right-little-finger"),
74+
}
75+
.to_string()
76+
}
77+
}
78+
79+
#[derive(Clone, Debug, Default)]
80+
pub struct FprintDeviceInfo {
81+
pub name: String,
82+
pub path: zbus::zvariant::OwnedObjectPath,
83+
pub enrolled_fingers: Vec<FingerName>,
84+
pub ongoing_enroll: bool,
85+
}
86+
87+
#[derive(Clone, Debug, Default)]
88+
pub struct FprintInfo {
89+
pub default_device: Option<FprintDeviceInfo>,
90+
pub other_devices: Vec<FprintDeviceInfo>,
91+
}
92+
93+
pub async fn start_enroll_finger(
94+
device: zbus::zvariant::OwnedObjectPath,
95+
username: &str,
96+
) -> Result<(), ()> {
97+
let daemon = get_fprint_device_proxy(&device).await?;
98+
99+
if let Err(e) = daemon.claim(username).await {
100+
tracing::error!("{e}");
101+
return Err(());
102+
}
103+
104+
Ok(())
105+
}
106+
107+
pub async fn stop_enroll_finger(
108+
device: zbus::zvariant::OwnedObjectPath,
109+
username: &str,
110+
) -> Result<(), ()> {
111+
let daemon = get_fprint_device_proxy(&device).await?;
112+
113+
if let Err(e) = daemon.release().await {
114+
tracing::error!("{e}");
115+
return Err(());
116+
}
117+
118+
Ok(())
119+
}
120+
121+
async fn get_fprint_device_info(
122+
object_path: zbus::zvariant::OwnedObjectPath,
123+
username: &str,
124+
) -> Result<FprintDeviceInfo, ()> {
125+
let daemon = get_fprint_device_proxy(&object_path).await?;
126+
let name = match daemon.name().await {
127+
Ok(name) => name,
128+
Err(e) => {
129+
tracing::error!("{e}");
130+
return Err(());
131+
}
132+
};
133+
134+
let fingers = match daemon.list_enrolled_fingers(username).await {
135+
Ok(fingers) => {
136+
let mut fingers_vec = Vec::new();
137+
for finger in fingers {
138+
fingers_vec.push(match finger.parse() {
139+
Ok(FingerName::Any) | Err(_) => {
140+
tracing::error!("Received unexpected finger name: {finger}");
141+
return Err(());
142+
}
143+
Ok(finger) => finger,
144+
});
145+
}
146+
fingers_vec
147+
}
148+
Err(e) => match e.to_string().as_str() {
149+
"net.reactivated.Fprint.Error.NoEnrolledPrints: Failed to discover prints" => {
150+
Vec::new()
151+
}
152+
_ => {
153+
tracing::error!("{e}");
154+
return Err(());
155+
}
156+
},
157+
};
158+
159+
Ok(FprintDeviceInfo {
160+
name,
161+
path: object_path,
162+
enrolled_fingers: fingers,
163+
ongoing_enroll: false,
164+
})
165+
}
166+
167+
pub async fn get_fprint_info(username: &str) -> Result<FprintInfo, ()> {
168+
let daemon = get_fprint_manager_proxy().await?;
169+
170+
let default_device_path = match daemon.get_default_device().await {
171+
Ok(device) => Some(device),
172+
Err(zbus::Error::MethodError(_, _, _)) => None,
173+
Err(e) => {
174+
tracing::error!("{e}");
175+
return Err(());
176+
}
177+
};
178+
179+
let default_device_info = match default_device_path {
180+
Some(ref path) => Some(get_fprint_device_info(path.clone(), username).await?),
181+
None => None,
182+
};
183+
184+
tracing::info!("Default device: {:?}", default_device_info);
185+
186+
let other_devices = match daemon.get_devices().await {
187+
Ok(mut devices) => {
188+
if let Some(default_device) = default_device_path {
189+
devices.retain(|device| device != &default_device);
190+
}
191+
let mut devices_info = Vec::new();
192+
for device in devices {
193+
devices_info.push(get_fprint_device_info(device, username).await?)
194+
}
195+
devices_info
196+
}
197+
Err(e) => {
198+
tracing::error!("{e}");
199+
return Err(());
200+
}
201+
};
202+
203+
tracing::info!("Other devices: {:?}", other_devices);
204+
205+
Ok(FprintInfo {
206+
default_device: default_device_info,
207+
other_devices,
208+
})
209+
}
210+
211+
async fn get_fprint_manager_proxy<'a>() -> Result<FprintManagerProxy<'a>, ()> {
212+
let connection = match Connection::system().await {
213+
Ok(c) => c,
214+
Err(e) => {
215+
tracing::error!("zbus connection failed. {e}");
216+
return Err(());
217+
}
218+
};
219+
220+
match FprintManagerProxy::new(&connection).await {
221+
Ok(d) => Ok(d),
222+
Err(e) => {
223+
tracing::error!("Fprint daemon proxy can't be created. Is it installed? {e}");
224+
Err(())
225+
}
226+
}
227+
}
228+
229+
async fn get_fprint_device_proxy<'a>(
230+
device_object_path: &'a zbus::zvariant::OwnedObjectPath,
231+
) -> Result<FprintDeviceProxy<'a>, ()> {
232+
let connection = match Connection::system().await {
233+
Ok(c) => c,
234+
Err(e) => {
235+
tracing::error!("zbus connection failed. {e}");
236+
return Err(());
237+
}
238+
};
239+
240+
let proxy_builder = match FprintDeviceProxy::builder(&connection).path(device_object_path) {
241+
Ok(d) => d,
242+
Err(e) => {
243+
tracing::error!("{e}");
244+
return Err(());
245+
}
246+
};
247+
248+
match proxy_builder.build().await {
249+
Ok(d) => Ok(d),
250+
Err(e) => {
251+
tracing::error!("Fprint daemon proxy can't be created. Is it installed? {e}");
252+
Err(())
253+
}
254+
}
255+
}

0 commit comments

Comments
 (0)