zaphyra's git: tgcNUR

fork of https://git.transgirl.cafe/zaphoid/tgc-nix-user-repository

commit c5b76619e7584c1239709cd4201e223147d93581
parent 8ad3bef5e673e56c9b8d19b6d5980f286fd07e3a
Author: Katja Ramona Sophie Kwast (zaphyra) <git@zaphyra.eu>
Date: Tue, 26 Aug 2025 12:47:39 +0200

overlays: add `swaylock-plugin-fprintd` swaylock-plugin with fprint-support via dbus instead of the wonky pam fuckery
3 files changed, 600 insertions(+), 2 deletions(-)
M
flake.nix
|
9
+++++++--
A
overlays/swaylock-plugin-fprintd/default.nix
|
16
++++++++++++++++
A
overlays/swaylock-plugin-fprintd/fprintd-support.patch
|
577
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/flake.nix b/flake.nix
@@ -34,7 +34,12 @@
 
         packages = lib.forAllSystems {
           overlays = builtins.attrValues inputs.self.overlays;
-          body = pkgs: import ./packages pkgs;
+          body =
+            pkgs:
+            (import ./packages pkgs)
+            // {
+              inherit (pkgs) swaylock-plugin-fprintd;
+            };
         };
 
         overlays = (import ./overlays) // {

@@ -54,7 +59,7 @@
         homeManagerModules = modulesTemplate (import ./homeManagerModules inputs.self);
         nixosModules = (modulesTemplate (import ./nixosModules inputs.self)) // {
           packages = {
-            nixpkgs.overlays = [ inputs.self.overlays.default ];
+            nixpkgs.overlays = [ inputs.self.overlays.packages ];
           };
         };
 
diff --git a/overlays/swaylock-plugin-fprintd/default.nix b/overlays/swaylock-plugin-fprintd/default.nix
@@ -0,0 +1,16 @@
+final: prev: {
+  swaylock-plugin-fprintd = prev.swaylock-plugin.overrideAttrs (prevAttrs: {
+    src = prev.applyPatches {
+      src = prevAttrs.src;
+      patches = [ ./fprintd-support.patch ];
+      postPatch = ''
+        substituteInPlace ./fingerprint/meson.build \
+          --replace-fail "/usr" "${prev.fprintd}"
+      '';
+    };
+
+    buildInputs = prevAttrs.buildInputs ++ [
+      prev.dbus
+    ];
+  });
+}
diff --git a/overlays/swaylock-plugin-fprintd/fprintd-support.patch b/overlays/swaylock-plugin-fprintd/fprintd-support.patch
@@ -0,0 +1,577 @@
+From 81ae315c1f85f45a374444ed8a35a58ce1b7d50b Mon Sep 17 00:00:00 2001
+From: "Katja Ramona Sophie Kwast (zaphyra)" <git@zaphyra.eu>
+Date: Tue, 26 Aug 2025 11:14:52 +0200
+Subject: [PATCH] 
+ `https://github.com/swaywm/swaylock/compare/master...SL-RU:swaylock-fprintd:fprintd
+ adapted` for swaylock-plugin
+
+---
+ completions/bash/swaylock      |   2 +
+ completions/fish/swaylock.fish |   1 +
+ completions/zsh/_swaylock      |   1 +
+ fingerprint/fingerprint.c      | 251 +++++++++++++++++++++++++++++++++
+ fingerprint/fingerprint.h      |  43 ++++++
+ fingerprint/meson.build        |  33 +++++
+ include/swaylock.h             |   3 +
+ main.c                         |  32 ++++-
+ meson.build                    |   2 +
+ render.c                       |   2 +
+ 10 files changed, 369 insertions(+), 1 deletion(-)
+ create mode 100644 fingerprint/fingerprint.c
+ create mode 100644 fingerprint/fingerprint.h
+ create mode 100644 fingerprint/meson.build
+
+diff --git a/completions/bash/swaylock b/completions/bash/swaylock
+index f8f411f..d13c679 100644
+--- a/completions/bash/swaylock
++++ b/completions/bash/swaylock
+@@ -14,6 +14,7 @@ _swaylock-plugin()
+     -F
+     -h
+     -i
++    -p
+     -k
+     -K
+     -L
+@@ -41,6 +42,7 @@ _swaylock-plugin()
+     --hide-keyboard-layout
+     --ignore-empty-password
+     --image
++    --fingerprint
+     --indicator-caps-lock
+     --indicator-idle-visible
+     --indicator-radius
+diff --git a/completions/fish/swaylock.fish b/completions/fish/swaylock.fish
+index 320ed6b..4ed4e62 100644
+--- a/completions/fish/swaylock.fish
++++ b/completions/fish/swaylock.fish
+@@ -14,6 +14,7 @@ complete -c swaylock-plugin -l help                   -s h --description "Show h
+ complete -c swaylock-plugin -l hide-keyboard-layout   -s K --description "Hide the current xkb layout while typing."
+ complete -c swaylock-plugin -l ignore-empty-password  -s e --description "When an empty password is provided, do not validate it."
+ complete -c swaylock-plugin -l image                  -s i --description "Display the given image, optionally only on the given output."
++complete -c swaylock-plugin -l fingerprint            -s p --description "Enable fingerprint scanning. Fprint is required."
+ complete -c swaylock-plugin -l indicator-caps-lock    -s l --description "Show the current Caps Lock state also on the indicator."
+ complete -c swaylock-plugin -l indicator-idle-visible      --description "Sets the indicator to show even if idle."
+ complete -c swaylock-plugin -l indicator-radius            --description "Sets the indicator radius."
+diff --git a/completions/zsh/_swaylock b/completions/zsh/_swaylock
+index 3854ced..1a659a0 100644
+--- a/completions/zsh/_swaylock
++++ b/completions/zsh/_swaylock
+@@ -18,6 +18,7 @@ _arguments -s \
+ 	'(--hide-keyboard-layout -K)'{--hide-keyboard-layout,-K}'[Hide the current xkb layout while typing]' \
+ 	'(--ignore-empty-password -e)'{--ignore-empty-password,-e}'[When an empty password is provided, do not validate it]' \
+ 	'(--image -i)'{--image,-i}'[Display the given image, optionally only on the given output]:filename:_files' \
++	'(--fingerprint -p)'{--fingerprint,-p}'[Enable fingerprint scanning. Fprint is required]' \
+ 	'(--indicator-caps-lock -l)'{--indicator-caps-lock,-l}'[Show the current Caps Lock state also on the indicator]' \
+ 	'(--indicator-idle-visible)'--indicator-idle-visible'[Sets the indicator to show even if idle]' \
+ 	'(--indicator-radius)'--indicator-radius'[Sets the indicator radius]:radius:' \
+diff --git a/fingerprint/fingerprint.c b/fingerprint/fingerprint.c
+new file mode 100644
+index 0000000..4e9477a
+--- /dev/null
++++ b/fingerprint/fingerprint.c
+@@ -0,0 +1,251 @@
++/*
++ * Based on fprintd util to verify a fingerprint
++ * Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
++ * Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
++ * Copyright (C) 2023 Alexandr Lutsai <s.lyra@ya.ru>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <stdbool.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <locale.h>
++#include <gio/gio.h>
++
++#include "fingerprint.h"
++#include "log.h"
++
++static void display_message(struct FingerprintState *state, const char *fmt, ...) {
++	va_list(args);
++	va_start(args, fmt);
++	vsnprintf(state->status, sizeof(state->status), fmt, args);
++	va_end(args);
++
++	state->sw_state->auth_state = AUTH_STATE_FINGERPRINT;
++	state->sw_state->fingerprint_msg = state->status;
++	damage_state(state->sw_state);
++	schedule_auth_idle(state->sw_state);
++}
++
++static void create_manager(struct FingerprintState *state) {
++	g_autoptr(GError) error = NULL;
++	state->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
++	if (state->connection == NULL) {
++		swaylock_log(LOG_ERROR, "Failed to connect to session bus: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++
++	state->manager = fprint_dbus_manager_proxy_new_sync(
++		state->connection,
++		G_DBUS_PROXY_FLAGS_NONE,
++		"net.reactivated.Fprint",
++		"/net/reactivated/Fprint/Manager",
++		NULL, &error);
++	if (state->manager == NULL) {
++		swaylock_log(LOG_ERROR, "Failed to get Fprintd manager: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++
++	swaylock_log(LOG_DEBUG, "FPrint manager created");
++}
++
++static void open_device(struct FingerprintState *state) {
++	state->device = NULL;
++	g_autoptr(FprintDBusDevice) dev = NULL;
++	g_autoptr(GError) error = NULL;
++	g_autofree char *path = NULL;
++	if (!fprint_dbus_manager_call_get_default_device_sync(state->manager, &path, NULL, &error)) {
++		swaylock_log(LOG_ERROR, "Impossible to verify: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++
++	swaylock_log(LOG_DEBUG, "Fingerprint: using device %s", path);
++	dev = fprint_dbus_device_proxy_new_sync(
++		state->connection,
++		G_DBUS_PROXY_FLAGS_NONE,
++		"net.reactivated.Fprint",
++		path, NULL, &error);
++	if (error) {
++		swaylock_log(LOG_ERROR, "failed to connect to device: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++
++	if (!fprint_dbus_device_call_claim_sync(dev, "", NULL, &error)) {
++		// we need to wait while device can be claimed
++		swaylock_log(LOG_DEBUG, "failed to claim the device: %s(%d)", error->message, error->code);
++		return;
++	}
++
++	swaylock_log(LOG_DEBUG, "FPrint device opened %s", path);
++	state->device = g_steal_pointer (&dev);
++}
++
++static void verify_result(GObject *object, const char *result, gboolean done, void *user_data) {
++	struct FingerprintState *state = user_data;
++	swaylock_log(LOG_INFO, "Verify result: %s (%s)", result, done ? "done" : "not done");
++	state->match = g_str_equal (result, "verify-match");
++	if (g_str_equal (result, "verify-retry-scan")) {
++		display_message(state, "Retry");
++		return;
++	} else if(g_str_equal (result, "verify-swipe-too-short")) {
++		display_message(state, "Retry, too short");
++		return;
++	} else if(g_str_equal (result, "verify-finger-not-centered")) {
++		display_message(state, "Retry, not centered");
++		return;
++	} else if(g_str_equal (result, "verify-remove-and-retry")) {
++		display_message(state, "Remove and retry");
++		return;
++	}
++
++	if(state->match) {
++		display_message(state, "Fingerprint OK");
++	} else {
++		display_message(state, "Retry");
++	}
++
++	state->completed = TRUE;
++	g_autoptr(GError) error = NULL;
++	if (!fprint_dbus_device_call_verify_stop_sync(state->device, NULL, &error)) {
++		swaylock_log(LOG_ERROR, "VerifyStop failed: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++}
++
++static void verify_started_cb(GObject *obj, GAsyncResult *res, gpointer user_data) {
++	struct FingerprintState *state = user_data;
++	if (!fprint_dbus_device_call_verify_start_finish(FPRINT_DBUS_DEVICE(obj), res, &state->error)) {
++		return;
++	}
++
++	swaylock_log(LOG_DEBUG, "Verify started!");
++	state->started = TRUE;
++}
++
++static void proxy_signal_cb(GDBusProxy	*proxy,
++							const gchar *sender_name,
++							const gchar *signal_name,
++							GVariant	*parameters,
++							gpointer	 user_data)
++{
++	struct FingerprintState *state = user_data;
++	if (!state->started) {
++		return;
++	}
++
++	if (!g_str_equal(signal_name, "VerifyStatus")) {
++		return;
++	}
++
++	const gchar *result;
++	gboolean done;
++	g_variant_get(parameters, "(&sb)", &result, &done);
++	verify_result(G_OBJECT (proxy), result, done, user_data);
++}
++
++static void start_verify(struct FingerprintState *state) {
++	/* This one is funny. We connect to the signal immediately to avoid
++	 * race conditions. However, we must ignore any authentication results
++	 * that happen before our start call returns.
++	 * This is because the verify call itself may internally try to verify
++	 * against fprintd (possibly using a separate account).
++	 *
++	 * To do so, we *must* use the async version of the verify call, as the
++	 * sync version would cause the signals to be queued and only processed
++	 * after it returns.
++	 */
++	fprint_dbus_device_call_verify_start(state->device, "any", NULL,
++										 verify_started_cb,
++										 state);
++
++	/* Wait for verify start while discarding any VerifyStatus signals */
++	while (!state->started && !state->error) {
++		g_main_context_iteration(NULL, TRUE);
++	}
++
++	if (state->error) {
++		swaylock_log(LOG_ERROR, "VerifyStart failed: %s", state->error->message);
++		display_message(state, "Fingerprint error");
++		g_clear_error(&state->error);
++		return;
++	}
++}
++
++static void release_callback(GObject *source_object, GAsyncResult *res,
++							 gpointer user_data) {
++}
++
++void fingerprint_init(struct FingerprintState *fingerprint_state,
++					  struct swaylock_state *swaylock_state) {
++	memset(fingerprint_state, 0, sizeof(struct FingerprintState));
++	fingerprint_state->sw_state = swaylock_state;
++	create_manager(fingerprint_state);
++	if(fingerprint_state->manager == NULL || fingerprint_state->connection == NULL) {
++		return;
++	}
++}
++
++int fingerprint_verify(struct FingerprintState *fingerprint_state) {
++	/* VerifyStatus signals are processing, do not wait for completion. */
++	g_main_context_iteration (NULL, FALSE);
++	if (fingerprint_state->manager == NULL ||
++		fingerprint_state->connection == NULL) {
++		return false;
++	}
++
++	if (fingerprint_state->device == NULL) {
++		open_device(fingerprint_state);
++		if (fingerprint_state->device == NULL) {
++			return false;
++		}
++
++		g_signal_connect (fingerprint_state->device, "g-signal", G_CALLBACK (proxy_signal_cb),
++						  fingerprint_state);
++		start_verify(fingerprint_state);
++	}
++
++
++
++	if (!fingerprint_state->completed) {
++		return false;
++	}
++
++	if (!fingerprint_state->match) {
++		fingerprint_state->completed = 0;
++		fingerprint_state->match = 0;
++		start_verify(fingerprint_state);
++		return false;
++	}
++
++	return true;
++}
++
++void fingerprint_deinit(struct FingerprintState *fingerprint_state) {
++	if (!fingerprint_state->device) {
++		return;
++	}
++
++	g_signal_handlers_disconnect_by_func(fingerprint_state->device, proxy_signal_cb,
++										 fingerprint_state);
++	fprint_dbus_device_call_release(fingerprint_state->device, NULL, release_callback, NULL);
++	fingerprint_state->device = NULL;
++}
+diff --git a/fingerprint/fingerprint.h b/fingerprint/fingerprint.h
+new file mode 100644
+index 0000000..d030a43
+--- /dev/null
++++ b/fingerprint/fingerprint.h
+@@ -0,0 +1,43 @@
++/*
++ * Copyright (C) 2023 Alexandr Lutsai <s.lyra@ya.ru>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#ifndef _FINGERPRINT_H
++#define _FINGERPRINT_H
++
++#include "swaylock.h"
++#include "fingerprint/fprintd-dbus.h"
++
++struct FingerprintState {
++	GError	*error;
++	gboolean started;
++	gboolean completed;
++	gboolean match;
++
++	char status[128];
++
++	FprintDBusManager *manager;
++	GDBusConnection *connection;
++	FprintDBusDevice *device;
++	struct swaylock_state *sw_state;
++};
++
++void fingerprint_init(struct FingerprintState *fingerprint_state, struct swaylock_state *state);
++int fingerprint_verify(struct FingerprintState *fingerprint_state);
++void fingerprint_deinit(struct FingerprintState *fingerprint_state);
++
++#endif
+\ No newline at end of file
+diff --git a/fingerprint/meson.build b/fingerprint/meson.build
+new file mode 100644
+index 0000000..2239952
+--- /dev/null
++++ b/fingerprint/meson.build
+@@ -0,0 +1,33 @@
++gnome = import('gnome')
++
++dbus = dependency('dbus-1')
++glib = dependency('glib-2.0', version: '>=2.64.0')
++gio_dep = dependency('gio-2.0')
++
++fprintd_dbus_interfaces = files(
++    '/usr/share/dbus-1/interfaces/net.reactivated.Fprint.Manager.xml',
++    '/usr/share/dbus-1/interfaces/net.reactivated.Fprint.Device.xml',
++)
++
++fprintd_dbus_sources = gnome.gdbus_codegen('fprintd-dbus',
++    sources: fprintd_dbus_interfaces,
++    autocleanup: 'all',
++    interface_prefix: 'net.reactivated.Fprint.',
++    namespace: 'FprintDBus',
++    object_manager: true,
++)
++
++fingerprint = declare_dependency(
++    include_directories: [
++        include_directories('..'),
++    ],
++    sources: [
++        fprintd_dbus_sources,
++        'fingerprint.c' ,
++    ],
++    dependencies: [
++        gio_dep,
++        glib,
++        dbus
++    ],
++)
+\ No newline at end of file
+diff --git a/include/swaylock.h b/include/swaylock.h
+index 233c663..6d7bf6d 100644
+--- a/include/swaylock.h
++++ b/include/swaylock.h
+@@ -18,6 +18,7 @@ enum auth_state {
+ 	AUTH_STATE_IDLE, // nothing happening
+ 	AUTH_STATE_VALIDATING, // currently validating password
+ 	AUTH_STATE_INVALID, // displaying message: password was wrong
++	AUTH_STATE_FINGERPRINT,
+ };
+ 
+ // Indicator state: status of password buffer / typing letters
+@@ -74,6 +75,7 @@ struct swaylock_args {
+ 	bool daemonize;
+ 	int ready_fd;
+ 	bool indicator_idle_visible;
++	bool fingerprint;
+ 	char *plugin_command;
+ 	bool plugin_per_output;
+ 	/* negative values = no grace; unit: seconds */
+@@ -304,6 +306,7 @@ struct swaylock_state {
+ 	bool run_display, locked;
+ 	struct ext_session_lock_manager_v1 *ext_session_lock_manager_v1;
+ 	struct ext_session_lock_v1 *ext_session_lock_v1;
++	char *fingerprint_msg;
+ 	struct zxdg_output_manager_v1 *zxdg_output_manager;
+ 	struct forward_state forward;
+ 	struct swaylock_bg_server server;
+diff --git a/main.c b/main.c
+index 1300fc8..6cc33a5 100644
+--- a/main.c
++++ b/main.c
+@@ -29,6 +29,7 @@
+ #include "seat.h"
+ #include "swaylock.h"
+ #include "ext-session-lock-v1-client-protocol.h"
++#include "fingerprint/fingerprint.h"
+ #include "xdg-output-unstable-v1-client-protocol.h"
+ #include "xdg-output-unstable-v1-server-protocol.h"
+ #include "fullscreen-shell-unstable-v1-client-protocol.h"
+@@ -876,6 +877,7 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
+ 		{"disable-caps-lock-text", no_argument, NULL, 'L'},
+ 		{"indicator-caps-lock", no_argument, NULL, 'l'},
+ 		{"line-uses-inside", no_argument, NULL, 'n'},
++		{"fingerprint", no_argument, NULL, 'p'},
+ 		{"line-uses-ring", no_argument, NULL, 'r'},
+ 		{"scaling", required_argument, NULL, 's'},
+ 		{"tiling", no_argument, NULL, 't'},
+@@ -955,6 +957,8 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
+ 			"Disable the Caps Lock text.\n"
+ 		"  -l, --indicator-caps-lock        "
+ 			"Show the current Caps Lock state also on the indicator.\n"
++		"  -p, --fingerprint				"
++			"Enable fingerprint scanning. Fprint is required.\n"
+ 		"  -s, --scaling <mode>             "
+ 			"Image scaling mode: stretch, fill, fit, center, tile, solid_color.\n"
+ 		"  -t, --tiling                     "
+@@ -1060,7 +1064,7 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
+ 	optind = 1;
+ 	while (1) {
+ 		int opt_idx = 0;
+-		c = getopt_long(argc, argv, "c:deFfhi:kKLlnrs:tuvC:R:", long_options,
++		c = getopt_long(argc, argv, "c:deFfhi:kKLlnprs:tuvC:R:", long_options,
+ 				&opt_idx);
+ 		if (c == -1) {
+ 			break;
+@@ -1104,6 +1108,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
+ 				load_image(optarg, state);
+ 			}
+ 			break;
++		case 'p':
++			if(state) {
++				state->args.fingerprint = true;
++			}
++			break;
+ 		case 'k':
+ 			if (state) {
+ 				state->args.show_keyboard_layout = true;
+@@ -2257,6 +2266,17 @@ void log_init(int argc, char **argv) {
+ 	swaylock_log_init(LOG_ERROR);
+ }
+ 
++static void check_fingerprint(void *d) {
++	struct FingerprintState *fingerprint_state = d;
++	if (fingerprint_verify(fingerprint_state)) {
++		do_sigusr(1);
++	} else {
++		(void)write(sigusr_fds[1], NULL, 0);
++	}
++
++	loop_add_timer(state.eventloop, 300, check_fingerprint, fingerprint_state);
++}
++
+ int main(int argc, char **argv) {
+ 	/* Initially ignore SIGUSR1 + SIGUSR2 to prevent stray signals (like
+ 	 * `killall -SIGUSR2`) from affecting the pw backend subprocess */
+@@ -2291,6 +2311,7 @@ int main(int argc, char **argv) {
+ 		.hide_keyboard_layout = false,
+ 		.show_failed_attempts = false,
+ 		.indicator_idle_visible = false,
++		.fingerprint = false,
+ 		.ready_fd = -1,
+ 		.plugin_command = NULL,
+ 		.grace_time = 0.0f,
+@@ -2608,6 +2629,12 @@ int main(int argc, char **argv) {
+ 	sa2.sa_flags = 0;
+ 	sigaction(SIGCHLD, &sa2, NULL);
+ 
++	struct FingerprintState fingerprint_state;
++	if(state.args.fingerprint) {
++		fingerprint_init(&fingerprint_state, &state);
++		loop_add_timer(state.eventloop, 100, check_fingerprint, &fingerprint_state);
++	}
++
+ 	state.run_display = true;
+ 	while (state.run_display) {
+ 		errno = 0;
+@@ -2624,6 +2651,9 @@ int main(int argc, char **argv) {
+ 	ext_session_lock_v1_unlock_and_destroy(state.ext_session_lock_v1);
+ 	wl_display_roundtrip(state.display);
+ 
++	if(state.args.fingerprint) {
++		fingerprint_deinit(&fingerprint_state);
++	}
+ 	free(state.args.font);
+ 	cairo_destroy(state.test_cairo);
+ 	cairo_surface_destroy(state.test_surface);
+diff --git a/meson.build b/meson.build
+index e72aff7..6babe5c 100644
+--- a/meson.build
++++ b/meson.build
+@@ -116,6 +116,7 @@ if logind.found()
+ endif
+ 
+ subdir('include')
++subdir('fingerprint')
+ 
+ dependencies = [
+ 	cairo,
+@@ -125,6 +126,7 @@ dependencies = [
+ 	xkbcommon,
+ 	wayland_client,
+ 	wayland_server,
++	fingerprint,
+ ]
+ 
+ sources = [
+diff --git a/render.c b/render.c
+index 5d48017..123847b 100644
+--- a/render.c
++++ b/render.c
+@@ -109,6 +109,8 @@ static bool render_frame(struct swaylock_surface *surface) {
+ 		if (state->input_state == INPUT_STATE_CLEAR) {
+ 			// This message has highest priority
+ 			text = "Cleared";
++		} else if (state->auth_state == AUTH_STATE_FINGERPRINT) {
++			text = state->fingerprint_msg;
+ 		} else if (state->auth_state == AUTH_STATE_VALIDATING) {
+ 			text = "Verifying";
+ 		} else if (state->auth_state == AUTH_STATE_INVALID) {
+-- 
+2.50.1
+