From 0f9ad0d0b810def89705d4c7fbbff962b16ba4c4 Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Tue, 24 Sep 2024 19:57:24 -0400 Subject: dpkg build recipe and assets --- Makefile | 61 ++++++++++ aux/79-fprintd.rules | 22 ++++ aux/80-fprintd.rules | 24 ---- aux/fprintd-tk.desktop | 8 ++ aux/fprintd_tk.1 | 25 ++++ aux/fprintd_tk.1.txt | 23 ++++ debian/README.Debian | 5 + debian/changelog | 5 + debian/control | 15 +++ debian/copyright | 42 +++++++ debian/fprintd-tk.install | 4 + debian/fprintd-tk.manpages | 1 + debian/patches/devuanize.patch | 57 +++++++++ debian/patches/series | 2 + debian/rules | 22 ++++ debian/source/format | 1 + debian/source/local-options | 4 + debian/source/options | 4 + debian/source/patch-header | 22 ++++ debian/upstream/metadata | 16 +++ debian/watch | 2 + fprintd_tk | 262 ++++++++++++++++++++++++++++++++++++++++ fprintd_tk.py | 266 ----------------------------------------- fprintd_tk_lib.py | 1 + images/fingerprint-gui.png | Bin 0 -> 1555 bytes 25 files changed, 604 insertions(+), 290 deletions(-) create mode 100644 Makefile create mode 100644 aux/79-fprintd.rules delete mode 100644 aux/80-fprintd.rules create mode 100644 aux/fprintd-tk.desktop create mode 100644 aux/fprintd_tk.1 create mode 100644 aux/fprintd_tk.1.txt create mode 100644 debian/README.Debian create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/fprintd-tk.install create mode 100644 debian/fprintd-tk.manpages create mode 100644 debian/patches/devuanize.patch create mode 100644 debian/patches/series create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/source/local-options create mode 100644 debian/source/options create mode 100644 debian/source/patch-header create mode 100644 debian/upstream/metadata create mode 100644 debian/watch create mode 100755 fprintd_tk delete mode 100755 fprintd_tk.py create mode 100644 images/fingerprint-gui.png diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..33a658d --- /dev/null +++ b/Makefile @@ -0,0 +1,61 @@ +# File: minimal Makefile for dependency resolution only +# Author: bgstack15 +# Startdate: 2018-11-24 +# Dependencies: + +APPNAME = fprintd-tk +APPVERSION = 0.0.1 +SRCDIR = $(CURDIR) +prefix = /usr +SYSCONFDIR = $(DESTDIR)/etc +DEFAULTDIR = $(DESTDIR)/etc/sysconfig # for debian use '$(DESTDIR)/etc/default' +BINDIR = $(DESTDIR)$(prefix)/bin +LIBEXECDIR = $(DESTDIR)$(prefix)/libexec +SBINDIR = $(DESTDIR)$(prefix)/sbin +SHAREDIR = $(DESTDIR)$(prefix)/share +DOCDIR = $(SHAREDIR)/doc/$(APPNAME) +APPDIR = $(SHAREDIR)/$(APPNAME) +APPSDIR = $(SHAREDIR)/applications +ICONSDIR = $(SHAREDIR)/icons +MIMEDIR = $(SHAREDIR)/mime +MANDIR = $(SHAREDIR)/man +XDGAUTODIR = $(SYSCONFDIR)/xdg/autostart +SYSVDIR = $(SYSCONFDIR)/init.d +SYSDDIR = $(DESTDIR)$(prefix)/lib/systemd/system + +# variables for deplist +DEPTYPE = dep +SEPARATOR = , + +awkbin :=$(shell which awk) +chmodbin :=$(shell which chmod) +cpbin :=$(shell which cp) +echobin :=$(shell which echo) +falsebin :=$(shell which false) +findbin :=$(shell which find) +grepbin :=$(shell which grep) +gzipbin :=$(shell which gzip) +installbin :=$(shell which install) +rmbin :=$(shell which rm) +rmdirbin :=$(shell which rmdir) +sedbin :=$(shell which sed) +sortbin :=$(shell which sort) +truebin :=$(shell which true) +uniqbin :=$(shell which uniq) +xargsbin :=$(shell which xargs) + +.PHONY: clean list deplist deplist_opts + +list: + @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | ${awkbin} -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | ${sortbin} | ${grepbin} -E -v -e '^[^[:alnum:]]' -e '^$@$$' + +deplist: + @# deplist 2020-04-18 input must be comma separated + @# DEPTYPE( dep , rec , sug ) for depends, recommends, or suggests + @if test -z "${DISTRO}" ; then ${echobin} "Please run \`make deplist\` with DISTRO= one of: `make deplist_opts 2>&1 1>/dev/null | ${xargsbin}`. Aborted." 1>&2 ; exit 1 ; fi + @if ! ${echobin} "${DEPTYPE}" | grep -qE "^(dep|rec|sug)$$" ; then ${echobin} "Please run \`make deplist\` with DEPTYPE= one of: dep, rec, sug. Undefined will use \`dep\`. Aborted." 1>&2 ; exit 1; fi + @${grepbin} -h --exclude-dir='doc' -riIE "\<${DEPTYPE}-" ${SRCDIR} | ${awkbin} -v "domain=${DISTRO}" -v "deptype=${DEPTYPE}" 'tolower($$2) ~ deptype"-"domain {$$1="";$$2="";print}' | tr ',' '\n' | ${sortbin} | ${uniqbin} | ${sedbin} -r -e 's/^\s*//' -e "s/\s*\$$/${SEPARATOR}/" | ${xargsbin} + +deplist_opts: + @# deplist_opts 2020-04-18 find all available dependency domains + @${grepbin} -h -o -riIE '\<(dep|rec|sug)-[^\ :]+:' ${SRCDIR} | ${sedbin} -r -e 's/(dep|rec|sug)-//;' -e 's/:$$//;' | ${sortbin} | ${uniqbin} 1>&2 diff --git a/aux/79-fprintd.rules b/aux/79-fprintd.rules new file mode 100644 index 0000000..5b03f07 --- /dev/null +++ b/aux/79-fprintd.rules @@ -0,0 +1,22 @@ +/* +.. File: 80-fprintd.rules +*/ +polkit.addRule(function(action, subject) { + if (( + action.id.match("net.reactivated.fprint.device.enroll") || + action.id.match("net.reactivated.fprint.device.verify") + ) && subject.active) { + polkit.log("action=" + action); + polkit.log("subject=" + subject); + return polkit.Result.YES; + } +}); +polkit.addRule(function(action, subject) { + if (( + action.id.match("net.reactivated.fprint.device.setusername") + ) && subject.active && subject.isInGroup("admins")) { + polkit.log("action=" + action); + polkit.log("subject=" + subject); + return polkit.Result.YES; + } +}); diff --git a/aux/80-fprintd.rules b/aux/80-fprintd.rules deleted file mode 100644 index 080334f..0000000 --- a/aux/80-fprintd.rules +++ /dev/null @@ -1,24 +0,0 @@ -/* -.. File: 80-fprintd.rules -*/ -polkit.addRule(function(action, subject) { - if ( - ( - action.id.match("net.reactivated.fprint.device.enroll") || - action.id.match("net.reactivated.fprint.device.verify") - ) && subject.active) { - polkit.log("action=" + action); - polkit.log("subject=" + subject); - return polkit.Result.YES; - } -}); -polkit.addRule(function(action, subject) { - if ( - ( - action.id.match("net.reactivated.fprint.device.setusername") - ) && subject.active && subject.isInGroup("admins")) { - polkit.log("action=" + action); - polkit.log("subject=" + subject); - return polkit.Result.YES; - } -}); diff --git a/aux/fprintd-tk.desktop b/aux/fprintd-tk.desktop new file mode 100644 index 0000000..064094b --- /dev/null +++ b/aux/fprintd-tk.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=Gui for fprintd +Comment=Control enrolled fingerprints +Exec=/usr/bin/fprintd_tk +Icon=fingerprint-gui +Terminal=false +Type=Application +Categories=Settings; diff --git a/aux/fprintd_tk.1 b/aux/fprintd_tk.1 new file mode 100644 index 0000000..c080708 --- /dev/null +++ b/aux/fprintd_tk.1 @@ -0,0 +1,25 @@ +.\" Text automatically generated by txt2man-wrapper, and stored in repository so the build process does not depend on my wrapper script. +.TH fprintd_tk 1 "September 2024" "fprintd-tk" "General Commands Manual" +.SH NAME +\fBfprintd_tk \fP- gui for fprintd +.SH SYNOPSIS +.nf +.fam C +\fBfprintd_tk\fP +.fam T +.fi +.fam T +.fi +.SH OPTIONS +No options. Configuration is in gui. +.SH ENVIRONMENT +None. +.SH DESCRIPTION +\fBfprintd_tk\fP has a simple graphical representation of the 10 fingers and makes it easy to enroll fingers, and verify them. Users in group `\fBadmins\fP` can also control other users' enrolled fingerprints. +This project is not directly related to fprintd at all. This is just a separate wrapper program to make life easier for users of window managers. +.SH AUTHOR + +.SH COPYRIGHT +GPL-3.0-only +.SH SEE ALSO +`fprintd(1)` diff --git a/aux/fprintd_tk.1.txt b/aux/fprintd_tk.1.txt new file mode 100644 index 0000000..a6e84f2 --- /dev/null +++ b/aux/fprintd_tk.1.txt @@ -0,0 +1,23 @@ +title fprintd_tk +section 1 +project fprintd-tk +volume General Commands Manual +date September 2024 +===== +NAME + fprintd_tk - gui for fprintd +SYNOPSIS + fprintd_tk +OPTIONS + No options. Configuration is in gui. +ENVIRONMENT + None. +DESCRIPTION + fprintd_tk has a simple graphical representation of the 10 fingers and makes it easy to enroll fingers, and verify them. Users in group `admins` can also control other users' enrolled fingerprints. + This project is not directly related to fprintd at all. This is just a separate wrapper program to make life easier for users of window managers. +AUTHOR + +COPYRIGHT + GPL-3.0-only +SEE ALSO + `fprintd(1)` diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..7a82510 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,5 @@ +fprintd-tk for Devuan + +Initial release. + + -- B. Stack Tue, 24 Sep 2024 17:20:45 -0400 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..ece8e4c --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +fprintd-tk (0.0.1-1) obs; urgency=low + + * Initial release. Closes: packages-want#34 + + -- B. Stack Tue, 24 Sep 2024 17:20:45 -0400 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..58a67ab --- /dev/null +++ b/debian/control @@ -0,0 +1,15 @@ +Source: fprintd-tk +Section: misc +Priority: optional +Maintainer: B. Stack +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.6.1 +Homepage: https://bgstack15.ddns.net/cgit/fprintd-tk +Rules-Requires-Root: no + +Package: fprintd-tk +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends}, ${shlibs:Depends}, +Description: gui for fprintd + A gui for managing enrolled fingerprints diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..7d6e10d --- /dev/null +++ b/debian/copyright @@ -0,0 +1,42 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: fprintd-tk +Upstream-Contact: B. Stack +Source: +# +# Please double check copyright with the licensecheck(1) command. + +Files: .gitignore + .images/screenshot-fprintd-tk-window.png + README.md + Makefile + aux/* + fprintd_tk + fprintd_tk_lib.py + debian/* + images/* +Copyright: 2024 +License: GPL-3.0+ + 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 3 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, see . + . + On Debian systems, the complete text of the GNU General Public License + Version 3 can be found in `/usr/share/common-licenses/GPL-3'. + +#---------------------------------------------------------------------------- +# xml and html files (skipped): +# images/fingerprint-enrolled.svg +# images/fingerprint-gui.svg + +#---------------------------------------------------------------------------- +# Files marked as NO_LICENSE_TEXT_FOUND may be covered by the following +# license/copyright files. diff --git a/debian/fprintd-tk.install b/debian/fprintd-tk.install new file mode 100644 index 0000000..0e7147b --- /dev/null +++ b/debian/fprintd-tk.install @@ -0,0 +1,4 @@ +fprintd_tk_lib.py /usr/libexec/fprintd-tk +images/*.svg /usr/share/fprintd-tk +aux/fprintd-tk.desktop /usr/share/applications +images/fingerprint-gui.png /usr/share/icons/hicolor/48x48/apps diff --git a/debian/fprintd-tk.manpages b/debian/fprintd-tk.manpages new file mode 100644 index 0000000..25ab9c6 --- /dev/null +++ b/debian/fprintd-tk.manpages @@ -0,0 +1 @@ +aux/fprintd_tk.1 diff --git a/debian/patches/devuanize.patch b/debian/patches/devuanize.patch new file mode 100644 index 0000000..e38a57e --- /dev/null +++ b/debian/patches/devuanize.patch @@ -0,0 +1,57 @@ +Message: set path for library +Version: 0.0.1 +--- a/aux/fprintd_tk.1 ++++ b/aux/fprintd_tk.1 +@@ -1,11 +1,11 @@ + .\" Text automatically generated by txt2man-wrapper, and stored in repository so the build process does not depend on my wrapper script. +-.TH fprintd_tk 1 "September 2024" "fprintd-tk" "General Commands Manual" ++.TH fprintd-tk 1 "September 2024" "fprintd-tk" "General Commands Manual" + .SH NAME +-\fBfprintd_tk \fP- gui for fprintd ++\fBfprintd-tk \fP- gui for fprintd + .SH SYNOPSIS + .nf + .fam C +-\fBfprintd_tk\fP ++\fBfprintd-tk\fP + .fam T + .fi + .fam T +@@ -15,7 +15,7 @@ No options. Configuration is in gui. + .SH ENVIRONMENT + None. + .SH DESCRIPTION +-\fBfprintd_tk\fP has a simple graphical representation of the 10 fingers and makes it easy to enroll fingers, and verify them. Users in group `\fBadmins\fP` can also control other users' enrolled fingerprints. ++\fBfprintd-tk\fP has a simple graphical representation of the 10 fingers and makes it easy to enroll fingers, and verify them. Users in group `\fBadmins\fP` can also control other users' enrolled fingerprints. + This project is not directly related to fprintd at all. This is just a separate wrapper program to make life easier for users of window managers. + .SH AUTHOR + +--- a/fprintd_tk ++++ b/fprintd_tk +@@ -21,6 +21,7 @@ + + import tkinter as tk, os, tkinter.simpledialog, sys, threading, time + import tkstackrpms as stk ++sys.path.append("/usr/libexec/fprintd-tk") + import fprintd_tk_lib as lib + from PIL import Image, ImageTk + +@@ -48,6 +49,7 @@ class App(tk.Frame): + + # configurable by admin or installation + img_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"images") ++ img_path = "/usr/share/fprintd-tk" + + self.master.title("Gui for fprintd") + imgicon = stk.get_scaled_icon("fingerprint-gui",24,"default", "","apps") +--- a/aux/fprintd-tk.desktop ++++ b/aux/fprintd-tk.desktop +@@ -1,7 +1,7 @@ + [Desktop Entry] + Name=Gui for fprintd + Comment=Control enrolled fingerprints +-Exec=/usr/bin/fprintd_tk ++Exec=/usr/bin/fprintd-tk + Icon=fingerprint-gui + Terminal=false + Type=Application diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..e52bee1 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,2 @@ +# You must remove unused comment lines for the released package. +devuanize.patch diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..c50b392 --- /dev/null +++ b/debian/rules @@ -0,0 +1,22 @@ +#!/usr/bin/make -f +# You must remove unused comment lines for the released package. +#export DH_VERBOSE = 1 +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,-O1 + +export APPNAME=fprintd-tk + +%: + dh $@ + +override_dh_gencontrol: + printf "misc:Depends=" > debian/${APPNAME}.substvars + make deplist DISTRO=devuan DEPTYPE=dep SEPARATOR=',' | grep -vE 'make\[[0-9]' >> debian/${APPNAME}.substvars + dh_gencontrol + +execute_after_dh_auto_install: + install -m0755 -D fprintd_tk debian/${APPNAME}/usr/bin/fprintd-tk + +execute_after_dh_installman: + mv -f debian/${APPNAME}/usr/share/man/man1/fprintd_tk.1 debian/${APPNAME}/usr/share/man/man1/fprintd-tk.1 diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/source/local-options b/debian/source/local-options new file mode 100644 index 0000000..77effbe --- /dev/null +++ b/debian/source/local-options @@ -0,0 +1,4 @@ +# +# ### gbp-buildpackage(1) user may like this. ### +#abort-on-upstream-changes +#unapply-patches diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 0000000..077de7d --- /dev/null +++ b/debian/source/options @@ -0,0 +1,4 @@ +# +# ### dgit-maint-merge(7) workflow user need this ### +# single-debian-patch +# auto-commit diff --git a/debian/source/patch-header b/debian/source/patch-header new file mode 100644 index 0000000..6ff0a40 --- /dev/null +++ b/debian/source/patch-header @@ -0,0 +1,22 @@ +The automatically generated patch puts this free form text on top of it. + +If you are using gbp-buildpackage(1), you probably don't need this file. + +If you are using dgit-maint-merge(7), please consider text as follows. + +The Debian packaging of foo is maintained in git, using the merging workflow +described in dgit-maint-merge(7). There isn't a patch queue that can be +represented as a quilt series. + +A detailed breakdown of the changes is available from their canonical +representation - git commits in the packaging repository. For example, to see +the changes made by the Debian maintainer in the first upload of upstream +version 1.2.3, you could use: + + % git clone https://git.dgit.debian.org/foo + % cd foo + % git log --oneline 1.2.3..debian/1.2.3-1 -- . ':!debian' + +(If you have dgit, use `dgit clone foo`, rather than plain `git clone`.) + +A single combined diff, containing all the changes, follows. diff --git a/debian/upstream/metadata b/debian/upstream/metadata new file mode 100644 index 0000000..dc2feb0 --- /dev/null +++ b/debian/upstream/metadata @@ -0,0 +1,16 @@ +# +# DEP-12: Per-package machine-readable metadata about Upstream +# Please check * https://dep-team.pages.debian.net/deps/dep12/ +# * https://wiki.debian.org/UpstreamMetadata +Reference: + Author: + Title: + Journal: + Year: + Volume: + Number: + Pages: + DOI: + PMID: + URL: + eprint: diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..76575dc --- /dev/null +++ b/debian/watch @@ -0,0 +1,2 @@ +# You must remove unused comment lines for the released package. +version=3 diff --git a/fprintd_tk b/fprintd_tk new file mode 100755 index 0000000..0f4f947 --- /dev/null +++ b/fprintd_tk @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +# File: fprintd_tk.py +# Location: https://bgstack15.cgit/fprintd-tk +# Author: bgstack15 +# Startdate: 2024-09-22-1 14:26 +# SPDX-License-Identifier: GPL-3.0-only +# Title: Gui for fprintd +# Purpose: tkinter desktop gui app for management fingerprint enrollments +# Project: fprintd-tk +# History: +# Usage: +# Run from window manager application menu +# Reference: +# stackrpms_tk.py +# Improve: +# Dependencies: +# dep-devuan: python3:any, python3-tkstackrpms, python3-tk, python3-pil +# fprintd_tk_lib.py +# Documentation: +# README.md + +import tkinter as tk, os, tkinter.simpledialog, sys, threading, time +import tkstackrpms as stk +import fprintd_tk_lib as lib +from PIL import Image, ImageTk + +ABOUT_TEXT = """ +fprintd_tk \"Gui for fprintd\" +(C) 2024 bgstack15 +SPDX-License-Identifier: GPL-3.0-only +Icons adapted from Numix-Icon-Theme-Circle (GPL 3) +""" + +# These will be used a lot in the program. +str_hands = ["left","right"] +str_fingers = ["thumb","index-finger","middle-finger","ring-finger","little-finger"] + +class App(tk.Frame): + def __init__(self, master): + super().__init__(master) + # variables + self.statustext = tk.StringVar() + self.advanced = tk.BooleanVar(value=False) + self.advanced.trace_add("write",self.load_data_into_form) + self.current_username = os.getenv("USER") + self.username = tk.StringVar(value=self.current_username) + self.verbose = tk.BooleanVar(value=False) + + # configurable by admin or installation + img_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"images") + + self.master.title("Gui for fprintd") + imgicon = stk.get_scaled_icon("fingerprint-gui",24,"default", "","apps") + self.master.tk.call("wm","iconphoto",self.master._w,imgicon) + menu = tk.Menu(self.master) + menu_file = tk.Menu(menu,tearoff=0) + menu_file.add_checkbutton(label="Advanced features", variable=self.advanced, underline=0, state="disabled") + menu_file.add_checkbutton(label="Verbose", variable=self.verbose, underline=0) + menu_file.add_command(label="Delete...", command=self.func_delete, underline=0) + menu_file.add_separator() + menu_file.add_command(label="Exit", command=self.func_exit, underline=1) + menu.add_cascade(label="File",menu=menu_file,underline=0) + menu_help = tk.Menu(menu,tearoff=0) + menu_help.add_command(label="About", command=self.func_about, underline=0) + menu.add_cascade(label="Help",menu=menu_help,underline=0) + self.master.config(menu=menu) + self.grid() # use this instead of pack() + self.background_color = self.master.cget("bg") + + # prepare finger images + try: + img_path + "" + except: + img_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"images") + self.img_notenrolled = ImageTk.PhotoImage(stk.image_from_svg(os.path.join(img_path,"fingerprint-gui.svg"),32)) + self.img_enrolled = ImageTk.PhotoImage(stk.image_from_svg(os.path.join(img_path,"fingerprint-enrolled.svg"),32)) + + # advanced pane, top + self.frm_advanced = tk.Frame(self.master) + # start hidden + #self.frm_advanced.grid(row=0,column=0,columnspan=100) + tk.Label(self.frm_advanced,text="User").grid(row=0,column=0) + self.ent_username = stk.Entry(self.frm_advanced,textvariable=self.username,func=self.load_data_into_form) + self.ent_username.grid(row=0,column=1) + + # action pane, left side + self.frm_actions = tk.Frame(self.master) + self.frm_actions.grid(row=1,column=0) + self.action = tk.IntVar(value=1) + self.last_fingerbutton_used = tk.StringVar() + stk.Radiobutton(self.frm_actions,value=1,text="enroll",variable=self.action,underline=0).grid(row=0,column=0) + stk.Radiobutton(self.frm_actions,value=2,text="verify",variable=self.action,underline=0).grid(row=1,column=0) + tk.Button(self.frm_actions,text="Refresh",underline=0,command=self.load_data_into_form).grid(row=2,column=0) + + # array of fingers + # we want 2 rows, 5 columns + self.frm_fingers = tk.Frame(self.master) + self.frm_fingers.grid(row=1,column=1) + self.fingers = [] + hand = 0 + while hand < 2: + finger = 0 + while finger < 5: + fnum = finger if hand==1 else len(str_fingers)-finger-1 + #print(f"While preparing hand {hand}, evaluating finger {finger}, which might need to be number {fnum}") + tf = str_hands[hand] + "-" + str_fingers[fnum] + self.fingers.append(tk.Button(self.frm_fingers,text=tf,padx=0,pady=0,command=lambda f=tf: self.func_finger_button(f),image=self.img_notenrolled,compound="top")) + self.fingers[-1].grid(row=hand,column=finger) + finger = finger + 1 + hand = hand + 1 + # because the underline business does not work on the radio button itself + # somehow the keypress takes over the lambda first var, so just set a dummy value, and pass our useful value as second parameter. + self.master.bind("",lambda a=1,b=1: self.set_action(a,b)) + self.master.bind("",lambda a=1,b=2: self.set_action(a,b)) + self.master.bind("",self.load_data_into_form) + + # status bar + stk.StatusBar(self.master,var=self.statustext) + # check if user has setusername polkit-1 permission, which lets him control fingerprint enrollments for other users. + temp1 = lib.check_setusername_permission(self.func_update_status) + if self.verbose.get(): + print(f"DEBUG (init): has set_username: {temp1}") + if temp1: + #self.chk_advanced.configure(state="enabled") + #menu_file.child[0].child[0].configure(state="enabled") + menu_file.entryconfigure(0,state="normal") + #print(menu_file.children) + # and now, load data into form for the first time + self.load_data_into_form() + + def set_action(self, keypress, a): + #print(f"DEBUG: got keypress {keypress}, a={a}") + # this is used by Alt+E, Alt+V to select the radio buttons for enroll, verify, etc. + self.action.set(a) + + def get_used_user(self): + advanced = self.advanced.get() + if advanced: + used_user = self.username.get() + else: + used_user = self.current_username + return used_user + + def load_data_into_form(self, a = None, b = None, c = None): + time.sleep(0.05) + advanced = self.advanced.get() + # show advanced toolbar + used_user = self.get_used_user() + if advanced: + if self.verbose.get(): + print(f"DEBUG: showing advanced toolbar") + self.frm_advanced.grid(row=0,column=0,columnspan=100) + else: + if self.verbose.get(): + print(f"DEBUG: hiding advanced toolbar") + self.frm_advanced.grid_forget() + # update enrolled fingers icons + try: + self.old_enrolled_fingers = self.enrolled_fingers + except: + # will happen if self.enrolled_fingers is not present + self.old_enrolled_fingers = [] + temp1 = lib.get_enrolled_fingers(used_user) + if temp1: + self.enrolled_fingers = temp1 + else: + if self.verbose.get(): + print(f"DEBUG (load_data_into_form): having to skip empty response from get_enrolled_fingers") + self.func_update_status(f"Unable to read enrolled fingers for user {used_user}.", reload = False) + if self.verbose.get(): + print(f"DEBUG (load_data_into_form): got user {used_user} enrolled fingers {self.enrolled_fingers}") + for i in self.fingers: + if str(i.cget('text')) in self.enrolled_fingers: + i.config(image=self.img_enrolled) + else: + i.config(image=self.img_notenrolled) + + # functions + def func_about(self): + """ Display about dialog. """ + tk.messagebox.Message(title="About",message=ABOUT_TEXT,icon="info").show() + + def func_exit(self): + # in case we need to manually do stuff + # otherwise command=self.client_exit would have sufficed. + self.master.quit() + + def func_finger_button(self, finger): + if self.verbose.get(): + print(f"DEBUG: func_finger_button finger {finger}, action {self.action.get()}") + action = self.action.get() + used_user = self.get_used_user() + # position in array is same as the value coming from radio button for actions. + available_actions = ["none","enroll","verify"] + try: + action_str = available_actions[action] + except ValueError: + action_str = "OFF" + if action_str in available_actions: + self.last_fingerbutton_used.set(finger) + try: + t1 = threading.Thread(target=lib.fprintd_action, args=(action_str, finger, self.func_update_status, used_user)) + t1.start() + except Exception as e: + self.statustext.set(e) + else: + self.statustext.set(f"Invalid action {action}, string {action_str}.") + # This blocks everything! Do not use this. + #t1.join() + # unfortunately useless here, because of the threading. + #self.load_data_into_form() + + def func_update_status(self, msg, reload = True): + msg = msg.strip() + if self.verbose.get(): + print(f"DEBUG (func_update_status): msg {msg}",file=sys.stderr) + self.statustext.set(msg) + try: + this_finger = [i for i in self.fingers if i.cget("text") == self.last_fingerbutton_used.get()][0] + except Exception as e: + this_finger = None + if self.verbose.get(): + print(f"DEBUG (func_update_status): while evaluating last-used finger, got {e}") + # flash these red + failure_messages = [ + "enroll-duplicate", + "verify-no-match" + ] + # flash these green + success_messages = [ + "verify-match", + "enroll-completed" + ] + flashed = False + for i in failure_messages: + if i in msg: + flashed = True + if this_finger: + stk.flash_entry(this_finger,["red",self.background_color]*3,300) + break + if not flashed: + for i in success_messages: + if i in msg: + if this_finger: + stk.flash_entry(this_finger,["green",self.background_color]*3,300) + break + if reload: + self.load_data_into_form() + + def func_delete(self): + used_user = self.get_used_user() + sure = tkinter.messagebox.askokcancel(title="Delete all enrolled fingerprints",message=f"Are you sure you want to delete all enrolled fingerprints for user {used_user}?") + if sure: + t1 = threading.Thread(target=lib.fprintd_action, args=("delete", "none", self.func_update_status, used_user)) + t1.start() + else: + self.statustext.set("Cancelled the delete action.") + +# main +root = tk.Tk() +fprintd_tk = App(root) +fprintd_tk.mainloop() diff --git a/fprintd_tk.py b/fprintd_tk.py deleted file mode 100755 index f7442d8..0000000 --- a/fprintd_tk.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env python3 -# File: fprintd_tk.py -# Location: https://bgstack15.cgit/fprintd-tk -# Author: bgstack15 -# Startdate: 2024-09-22-1 14:26 -# SPDX-License-Identifier: GPL-3.0-only -# Title: Gui for fprintd -# Purpose: tkinter desktop gui app for management fingerprint enrollments -# Project: fprintd-tk -# History: -# Usage: -# Run from window manager application menu -# Reference: -# stackrpms_tk.py -# Improve: -# Dependencies: -# dep-devuan: python3-tkstackrpms, python3-tk, python3-pil -# fprintd_tk_lib.py -# Documentation: -# README.md - -import tkinter as tk, os, tkinter.simpledialog, sys, threading, time -import tkstackrpms as stk -import fprintd_tk_lib as lib -from PIL import Image, ImageTk - -ABOUT_TEXT = """ -fprintd_tk \"Gui for fprintd\" -(C) 2024 bgstack15 -SPDX-License-Identifier: GPL-3.0-only -Icons adapted from Numix-Icon-Theme-Circle (GPL 3) -""" - -# configurable by admin or installation -img_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"images") - -# These will be used a lot in the program. -str_hands = ["left","right"] -str_fingers = ["thumb","index-finger","middle-finger","ring-finger","little-finger"] -str_all = [] -for h in str_hands: - for f in str_fingers: - str_all.append(f"{h}-{f}") - -class App(tk.Frame): - def __init__(self, master): - super().__init__(master) - # variables - self.statustext = tk.StringVar() - self.advanced = tk.BooleanVar(value=False) - self.advanced.trace_add("write",self.load_data_into_form) - self.current_username = os.getenv("USER") - self.username = tk.StringVar(value=self.current_username) - self.verbose = tk.BooleanVar(value=False) - - self.master.title("Gui for fprintd") - imgicon = stk.get_scaled_icon("fingerprint-gui",24,"default", "","apps") - self.master.tk.call("wm","iconphoto",self.master._w,imgicon) - menu = tk.Menu(self.master) - menu_file = tk.Menu(menu,tearoff=0) - menu_file.add_checkbutton(label="Advanced features", variable=self.advanced, underline=0, state="disabled") - menu_file.add_checkbutton(label="Verbose", variable=self.verbose, underline=0) - menu_file.add_command(label="Delete...", command=self.func_delete, underline=0) - menu_file.add_separator() - menu_file.add_command(label="Exit", command=self.func_exit, underline=1) - menu.add_cascade(label="File",menu=menu_file,underline=0) - menu_help = tk.Menu(menu,tearoff=0) - menu_help.add_command(label="About", command=self.func_about, underline=0) - menu.add_cascade(label="Help",menu=menu_help,underline=0) - self.master.config(menu=menu) - self.grid() # use this instead of pack() - self.background_color = self.master.cget("bg") - - # prepare finger images - try: - img_path + "" - except: - img_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"images") - self.img_notenrolled = ImageTk.PhotoImage(stk.image_from_svg(os.path.join(img_path,"fingerprint-gui.svg"),32)) - self.img_enrolled = ImageTk.PhotoImage(stk.image_from_svg(os.path.join(img_path,"fingerprint-enrolled.svg"),32)) - - # advanced pane, top - self.frm_advanced = tk.Frame(self.master) - # start hidden - #self.frm_advanced.grid(row=0,column=0,columnspan=100) - tk.Label(self.frm_advanced,text="User").grid(row=0,column=0) - self.ent_username = stk.Entry(self.frm_advanced,textvariable=self.username,func=self.load_data_into_form) - self.ent_username.grid(row=0,column=1) - - # action pane, left side - self.frm_actions = tk.Frame(self.master) - self.frm_actions.grid(row=1,column=0) - self.action = tk.IntVar(value=1) - self.last_fingerbutton_used = tk.StringVar() - stk.Radiobutton(self.frm_actions,value=1,text="enroll",variable=self.action,underline=0).grid(row=0,column=0) - stk.Radiobutton(self.frm_actions,value=2,text="verify",variable=self.action,underline=0).grid(row=1,column=0) - tk.Button(self.frm_actions,text="Refresh",underline=0,command=self.load_data_into_form).grid(row=2,column=0) - - # array of fingers - # we want 2 rows, 5 columns - self.frm_fingers = tk.Frame(self.master) - self.frm_fingers.grid(row=1,column=1) - self.fingers = [] - hand = 0 - while hand < 2: - finger = 0 - while finger < 5: - fnum = finger if hand==1 else len(str_fingers)-finger-1 - #print(f"While preparing hand {hand}, evaluating finger {finger}, which might need to be number {fnum}") - tf = str_hands[hand] + "-" + str_fingers[fnum] - self.fingers.append(tk.Button(self.frm_fingers,text=tf,padx=0,pady=0,command=lambda f=tf: self.func_finger_button(f),image=self.img_notenrolled,compound="top")) - self.fingers[-1].grid(row=hand,column=finger) - finger = finger + 1 - hand = hand + 1 - # because the underline business does not work on the radio button itself - # somehow the keypress takes over the lambda first var, so just set a dummy value, and pass our useful value as second parameter. - self.master.bind("",lambda a=1,b=1: self.set_action(a,b)) - self.master.bind("",lambda a=1,b=2: self.set_action(a,b)) - self.master.bind("",self.load_data_into_form) - - # status bar - stk.StatusBar(self.master,var=self.statustext) - # check if user has setusername polkit-1 permission, which lets him control fingerprint enrollments for other users. - temp1 = lib.check_setusername_permission(self.func_update_status) - if self.verbose.get(): - print(f"DEBUG (init): has set_username: {temp1}") - if temp1: - #self.chk_advanced.configure(state="enabled") - #menu_file.child[0].child[0].configure(state="enabled") - menu_file.entryconfigure(0,state="normal") - #print(menu_file.children) - # and now, load data into form for the first time - self.load_data_into_form() - - def set_action(self, keypress, a): - #print(f"DEBUG: got keypress {keypress}, a={a}") - # this is used by Alt+E, Alt+V to select the radio buttons for enroll, verify, etc. - self.action.set(a) - - def get_used_user(self): - advanced = self.advanced.get() - if advanced: - used_user = self.username.get() - else: - used_user = self.current_username - return used_user - - def load_data_into_form(self, a = None, b = None, c = None): - time.sleep(0.05) - advanced = self.advanced.get() - # show advanced toolbar - used_user = self.get_used_user() - if advanced: - if self.verbose.get(): - print(f"DEBUG: showing advanced toolbar") - self.frm_advanced.grid(row=0,column=0,columnspan=100) - else: - if self.verbose.get(): - print(f"DEBUG: hiding advanced toolbar") - self.frm_advanced.grid_forget() - # update enrolled fingers icons - try: - self.old_enrolled_fingers = self.enrolled_fingers - except: - # will happen if self.enrolled_fingers is not present - self.old_enrolled_fingers = [] - temp1 = lib.get_enrolled_fingers(used_user) - if temp1: - self.enrolled_fingers = temp1 - else: - if self.verbose.get(): - print(f"DEBUG (load_data_into_form): having to skip empty response from get_enrolled_fingers") - self.func_update_status(f"Unable to read enrolled fingers for user {used_user}.", reload = False) - if self.verbose.get(): - print(f"DEBUG (load_data_into_form): got user {used_user} enrolled fingers {self.enrolled_fingers}") - for i in self.fingers: - if str(i.cget('text')) in self.enrolled_fingers: - i.config(image=self.img_enrolled) - else: - i.config(image=self.img_notenrolled) - - # functions - def func_about(self): - """ Display about dialog. """ - tk.messagebox.Message(title="About",message=ABOUT_TEXT,icon="info").show() - - def func_exit(self): - # in case we need to manually do stuff - # otherwise command=self.client_exit would have sufficed. - self.master.quit() - - def func_finger_button(self, finger): - if self.verbose.get(): - print(f"DEBUG: func_finger_button finger {finger}, action {self.action.get()}") - action = self.action.get() - used_user = self.get_used_user() - # position in array is same as the value coming from radio button for actions. - available_actions = ["none","enroll","verify"] - try: - action_str = available_actions[action] - except ValueError: - action_str = "OFF" - if action_str in available_actions: - self.last_fingerbutton_used.set(finger) - try: - t1 = threading.Thread(target=lib.fprintd_action, args=(action_str, finger, self.func_update_status, used_user)) - t1.start() - except Exception as e: - self.statustext.set(e) - else: - self.statustext.set(f"Invalid action {action}, string {action_str}.") - # This blocks everything! Do not use this. - #t1.join() - # unfortunately useless here, because of the threading. - #self.load_data_into_form() - - def func_update_status(self, msg, reload = True): - msg = msg.strip() - if self.verbose.get(): - print(f"DEBUG (func_update_status): msg {msg}",file=sys.stderr) - self.statustext.set(msg) - try: - this_finger = [i for i in self.fingers if i.cget("text") == self.last_fingerbutton_used.get()][0] - except Exception as e: - this_finger = None - if self.verbose.get(): - print(f"DEBUG (func_update_status): while evaluating last-used finger, got {e}") - # flash these red - failure_messages = [ - "enroll-duplicate", - "verify-no-match" - ] - # flash these green - success_messages = [ - "verify-match", - "enroll-completed" - ] - flashed = False - for i in failure_messages: - if i in msg: - flashed = True - if this_finger: - stk.flash_entry(this_finger,["red",self.background_color]*3,300) - break - if not flashed: - for i in success_messages: - if i in msg: - if this_finger: - stk.flash_entry(this_finger,["green",self.background_color]*3,300) - break - if reload: - self.load_data_into_form() - - def func_delete(self): - used_user = self.get_used_user() - sure = tkinter.messagebox.askokcancel(title="Delete all enrolled fingerprints",message=f"Are you sure you want to delete all enrolled fingerprints for user {used_user}?") - if sure: - t1 = threading.Thread(target=lib.fprintd_action, args=("delete", "none", self.func_update_status, used_user)) - t1.start() - else: - self.statustext.set("Cancelled the delete action.") - -# main -root = tk.Tk() -fprintd_tk = App(root) -fprintd_tk.mainloop() diff --git a/fprintd_tk_lib.py b/fprintd_tk_lib.py index 18470cb..71820c2 100644 --- a/fprintd_tk_lib.py +++ b/fprintd_tk_lib.py @@ -12,6 +12,7 @@ # Reference: # Improve: # Dependencies: +# dep-devuan: fprintd # /usr/bin/fprintd-* # Documentation: # README.md diff --git a/images/fingerprint-gui.png b/images/fingerprint-gui.png new file mode 100644 index 0000000..aaab9e6 Binary files /dev/null and b/images/fingerprint-gui.png differ -- cgit