refactor!: rename to face-unlock

This commit is contained in:
Felitendo committed 2026-09-25 09:27:15 +02:00
1 parent 44449f103b
commit 6e6a6e6d03
66 files changed
+1709 -1709

No files matched your search

+3 -3
View File
@@ -60,8 +60,8 @@ body:
id: version
attributes:
label: Version
description: The output of `plasma-face-unlock --version`.
placeholder: plasma-face-unlock 1.0.1
description: The output of `face-unlock --version`.
placeholder: face-unlock 1.0.1
validations:
required: true
- type: dropdown
@@ -88,7 +88,7 @@ body:
id: logs
attributes:
label: Status and logs
description: The output of `plasma-face-unlock status`. If a scan goes wrong, also `journalctl -b -u plasma-face-unlockd` and `journalctl --user -b -u plasma-face-unlock-agent`.
description: The output of `face-unlock status`. If a scan goes wrong, also `journalctl -b -u face-unlockd` and `journalctl --user -b -u face-unlock-agent`.
render: shell
- type: textarea
id: more
+1 -1
View File
@@ -1,5 +1,5 @@
name: 💡 Feature request
description: An idea to make plasma-face-unlock better.
description: An idea to make face-unlock better.
labels:
- enhancement
body:
+12 -12
View File
@@ -116,8 +116,8 @@ jobs:
- name: Look inside what was built
run: |
rpm -qip dist/plasma-face-unlock-[0-9]*.rpm
rpm -qlp dist/plasma-face-unlock-[0-9]*.rpm
rpm -qip dist/face-unlock-[0-9]*.rpm
rpm -qlp dist/face-unlock-[0-9]*.rpm
- uses: actions/upload-artifact@v7
with:
@@ -254,13 +254,13 @@ jobs:
run: |
apt-get update -qq && apt-get install -y --no-install-recommends gpg
install -d -m 0755 /etc/apt/keyrings
gpg --dearmor -o /etc/apt/keyrings/plasma-face-unlock.gpg < pages/KEY.gpg
echo "deb [signed-by=/etc/apt/keyrings/plasma-face-unlock.gpg] file://$PWD/pages/deb/${{ matrix.codename }} ./" \
> /etc/apt/sources.list.d/plasma-face-unlock.list
gpg --dearmor -o /etc/apt/keyrings/face-unlock.gpg < pages/KEY.gpg
echo "deb [signed-by=/etc/apt/keyrings/face-unlock.gpg] file://$PWD/pages/deb/${{ matrix.codename }} ./" \
> /etc/apt/sources.list.d/face-unlock.list
apt-get update
apt-get install -y plasma-face-unlock
apt-get install -y face-unlock
useradd -m tester
runuser -u tester -- plasma-face-unlock --version
runuser -u tester -- face-unlock --version
verify-dnf:
name: Install from the RPM repository
@@ -281,15 +281,15 @@ jobs:
run: |
rpm --import pages/KEY.gpg
rpm --import signer/rpm-signer.asc
cat > /etc/yum.repos.d/plasma-face-unlock.repo <<EOF
[plasma-face-unlock]
name=plasma-face-unlock
cat > /etc/yum.repos.d/face-unlock.repo <<EOF
[face-unlock]
name=face-unlock
baseurl=file://$PWD/pages/rpm
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=file://$PWD/pages/KEY.gpg
EOF
dnf install -y plasma-face-unlock util-linux
dnf install -y face-unlock util-linux
useradd -m tester
runuser -u tester -- plasma-face-unlock --version
runuser -u tester -- face-unlock --version
+2 -2
View File
@@ -1,6 +1,6 @@
# Changelog
What each release brings, newest first. The [GitHub releases](https://github.com/LoonixTools/plasma-face-unlock/releases) add every commit that went into it.
What each release brings, newest first. The [GitHub releases](https://github.com/LoonixTools/face-unlock/releases) add every commit that went into it.
## v1.0.1
@@ -17,7 +17,7 @@ _2026-09-23_
Welcome to the very first release of plasma-face-unlock! It brings Face ID to KDE Plasma: look at the screen and it unlocks. The lock screen, sudo and admin prompts can all take your face instead of a password.
<p align="center">
<img width="480" alt="The bubble drops down over the lock screen, finds the face and shows a green tick" src="https://raw.githubusercontent.com/LoonixTools/plasma-face-unlock/v1.0.0/res/screenshots/unlock.webp">
<img width="480" alt="The bubble drops down over the lock screen, finds the face and shows a green tick" src="https://raw.githubusercontent.com/LoonixTools/face-unlock/v1.0.0/res/screenshots/unlock.webp">
</p>
### Highlights
+5 -5
View File
@@ -20,7 +20,7 @@
## Layout
- `src/plasma-face-unlock` and `src/lib/*.sh`: the command and its menu, plain bash.
- `src/face-unlock` and `src/lib/*.sh`: the command and its menu, plain bash.
- `src/core`: camera, detection, recognition, liveness, face store. Used by the daemon and the tests.
- `src/daemon`: the root service. It owns the camera and the face data.
- `src/agent`: the Qt/QML program in the session: bubble, lock screen, setup window.
@@ -48,10 +48,10 @@ A minor or major release (has `### Highlights`, gets a heading and the support s
_2026-09-24_
Welcome to plasma-face-unlock `v1.4.0`! One or two sentences on what this release is about.
Welcome to face-unlock `v1.4.0`! One or two sentences on what this release is about.
<p align="center">
<img width="480" alt="What the picture shows" src="https://raw.githubusercontent.com/LoonixTools/plasma-face-unlock/v1.4.0/<path>">
<img width="480" alt="What the picture shows" src="https://raw.githubusercontent.com/LoonixTools/face-unlock/v1.4.0/<path>">
</p>
### 🚨 Breaking changes
@@ -85,7 +85,7 @@ Never test against the real setup: enrolling and the PAM files belong to the use
PAM file editing against copies of real PAM files. No camera, no root.
- Without a camera, point the daemon at pictures or a video: `Camera=images:<dir>` or
`Camera=file:<video>` in the config passed to `--config`.
- A development daemon needs no root: `plasma-face-unlockd --socket $XDG_RUNTIME_DIR/pfu/socket
- A development daemon needs no root: `face-unlockd --socket $XDG_RUNTIME_DIR/fu/socket
--state-dir DIR --config FILE --models DIR`. It skips polkit when it does not run as root. Point
the other parts at it with `PFU_SOCKET`.
the other parts at it with `FU_SOCKET`.
- `make check` after every change. New strings: `po/update-pot.sh`, then translate them in every `po/*.po`.
+56 -56
View File
@@ -1,4 +1,4 @@
# plasma-face-unlock: the compiled half
# face-unlock: the compiled half
#
# The Makefile is the entry point and calls this. Everything that has to be
# compiled lives here: the daemon, the agent, the client the shell code uses,
@@ -6,9 +6,9 @@
# installed by the Makefile, which is where the paths come from.
cmake_minimum_required(VERSION 3.22)
project(plasma-face-unlock VERSION 1.0.1 LANGUAGES C CXX)
project(face-unlock VERSION 1.0.1 LANGUAGES C CXX)
set(PFU_VERSION "${PROJECT_VERSION}" CACHE STRING "Version reported by every binary")
set(FU_VERSION "${PROJECT_VERSION}" CACHE STRING "Version reported by every binary")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -21,19 +21,19 @@ endif()
include(GNUInstallDirs)
option(PFU_BUILD_AGENT "Build the Plasma agent (bubble, lock screen, set up window)" ON)
option(PFU_BUILD_PAM "Build the PAM module" ON)
option(PFU_BUILD_TESTS "Build the tests" ON)
option(FU_BUILD_AGENT "Build the Plasma agent (bubble, lock screen, set up window)" ON)
option(FU_BUILD_PAM "Build the PAM module" ON)
option(FU_BUILD_TESTS "Build the tests" ON)
# Where things go at run time. The defaults suit a normal install into /usr;
# the Makefile passes its own values so the two never disagree.
set(PFU_LIBEXECDIR "${CMAKE_INSTALL_PREFIX}/lib/plasma-face-unlock" CACHE PATH "Helper programs")
set(PFU_MODELDIR "${CMAKE_INSTALL_FULL_DATADIR}/plasma-face-unlock/models" CACHE PATH "Neural network models")
set(PFU_LOCALEDIR "${CMAKE_INSTALL_FULL_LOCALEDIR}" CACHE PATH "Translations")
set(PFU_SOCKET "/run/plasma-face-unlock/socket" CACHE STRING "The daemon's socket")
set(PFU_STATEDIR "/var/lib/plasma-face-unlock" CACHE PATH "Face data")
set(PFU_CONFIG "/etc/plasma-face-unlock/config" CACHE FILEPATH "System settings")
set(PFU_PAMDIR "${CMAKE_INSTALL_PREFIX}/lib/security" CACHE PATH "Where PAM modules live")
set(FU_LIBEXECDIR "${CMAKE_INSTALL_PREFIX}/lib/face-unlock" CACHE PATH "Helper programs")
set(FU_MODELDIR "${CMAKE_INSTALL_FULL_DATADIR}/face-unlock/models" CACHE PATH "Neural network models")
set(FU_LOCALEDIR "${CMAKE_INSTALL_FULL_LOCALEDIR}" CACHE PATH "Translations")
set(FU_SOCKET "/run/face-unlock/socket" CACHE STRING "The daemon's socket")
set(FU_STATEDIR "/var/lib/face-unlock" CACHE PATH "Face data")
set(FU_CONFIG "/etc/face-unlock/config" CACHE FILEPATH "System settings")
set(FU_PAMDIR "${CMAKE_INSTALL_PREFIX}/lib/security" CACHE PATH "Where PAM modules live")
configure_file(src/shared/buildconfig.h.in ${CMAKE_CURRENT_BINARY_DIR}/buildconfig.h @ONLY)
include_directories(${CMAKE_CURRENT_BINARY_DIR} src/shared)
@@ -47,35 +47,35 @@ add_compile_options(-Wall -Wextra -Wno-unused-parameter)
find_package(Qt6 6.5 REQUIRED COMPONENTS Core DBus Network)
# OpenCV 5 split the contour and transform helpers into "geometry".
find_package(OpenCV REQUIRED COMPONENTS core)
set(PFU_OPENCV_MODULES core imgproc imgcodecs videoio objdetect dnn)
set(FU_OPENCV_MODULES core imgproc imgcodecs videoio objdetect dnn)
if(OpenCV_VERSION_MAJOR GREATER_EQUAL 5)
list(APPEND PFU_OPENCV_MODULES geometry)
list(APPEND FU_OPENCV_MODULES geometry)
endif()
find_package(OpenCV REQUIRED COMPONENTS ${PFU_OPENCV_MODULES})
find_package(OpenCV REQUIRED COMPONENTS ${FU_OPENCV_MODULES})
# The settings file reader, shared with the agent (which has no use for
# OpenCV).
add_library(pfu_common STATIC src/core/keyvalue.cpp)
target_include_directories(pfu_common PUBLIC src/core)
target_link_libraries(pfu_common PUBLIC Qt6::Core)
set_target_properties(pfu_common PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_library(fu_common STATIC src/core/keyvalue.cpp)
target_include_directories(fu_common PUBLIC src/core)
target_link_libraries(fu_common PUBLIC Qt6::Core)
set_target_properties(fu_common PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_library(pfu_core STATIC
add_library(fu_core STATIC
src/core/settings.cpp
src/core/camera.cpp
src/core/vision.cpp
src/core/liveness.cpp
src/core/store.cpp
)
target_include_directories(pfu_core PUBLIC src/core ${OpenCV_INCLUDE_DIRS})
target_link_libraries(pfu_core PUBLIC pfu_common Qt6::Core ${OpenCV_LIBS})
set_target_properties(pfu_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(fu_core PUBLIC src/core ${OpenCV_INCLUDE_DIRS})
target_link_libraries(fu_core PUBLIC fu_common Qt6::Core ${OpenCV_LIBS})
set_target_properties(fu_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
# ---------------------------------------------------------------------------
# The daemon
# ---------------------------------------------------------------------------
add_executable(plasma-face-unlockd
add_executable(face-unlockd
src/daemon/job.h
src/daemon/agentlink.cpp
src/daemon/main.cpp
@@ -87,38 +87,38 @@ add_executable(plasma-face-unlockd
src/daemon/userstate.cpp
src/daemon/system.cpp
)
target_link_libraries(plasma-face-unlockd PRIVATE pfu_core Qt6::DBus Qt6::Network)
install(TARGETS plasma-face-unlockd DESTINATION ${PFU_LIBEXECDIR})
target_link_libraries(face-unlockd PRIVATE fu_core Qt6::DBus Qt6::Network)
install(TARGETS face-unlockd DESTINATION ${FU_LIBEXECDIR})
# ---------------------------------------------------------------------------
# The client the shell code uses
# ---------------------------------------------------------------------------
add_executable(plasma-face-unlock-ctl src/ctl/main.cpp)
target_link_libraries(plasma-face-unlock-ctl PRIVATE Qt6::Core Qt6::Network)
install(TARGETS plasma-face-unlock-ctl DESTINATION ${PFU_LIBEXECDIR})
add_executable(face-unlock-ctl src/ctl/main.cpp)
target_link_libraries(face-unlock-ctl PRIVATE Qt6::Core Qt6::Network)
install(TARGETS face-unlock-ctl DESTINATION ${FU_LIBEXECDIR})
# ---------------------------------------------------------------------------
# The PAM module
# ---------------------------------------------------------------------------
if(PFU_BUILD_PAM)
if(FU_BUILD_PAM)
find_path(PAM_INCLUDE_DIR security/pam_modules.h REQUIRED)
find_library(PAM_LIBRARY pam REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SYSTEMD IMPORTED_TARGET libsystemd)
add_library(pam_plasma_face_unlock MODULE src/pam/pam_plasma_face_unlock.c)
set_target_properties(pam_plasma_face_unlock PROPERTIES PREFIX "" C_VISIBILITY_PRESET hidden)
target_include_directories(pam_plasma_face_unlock PRIVATE ${PAM_INCLUDE_DIR})
target_link_libraries(pam_plasma_face_unlock PRIVATE ${PAM_LIBRARY})
add_library(pam_face_unlock MODULE src/pam/pam_face_unlock.c)
set_target_properties(pam_face_unlock PROPERTIES PREFIX "" C_VISIBILITY_PRESET hidden)
target_include_directories(pam_face_unlock PRIVATE ${PAM_INCLUDE_DIR})
target_link_libraries(pam_face_unlock PRIVATE ${PAM_LIBRARY})
if(SYSTEMD_FOUND)
target_compile_definitions(pam_plasma_face_unlock PRIVATE HAVE_SYSTEMD=1)
target_link_libraries(pam_plasma_face_unlock PRIVATE PkgConfig::SYSTEMD)
target_compile_definitions(pam_face_unlock PRIVATE HAVE_SYSTEMD=1)
target_link_libraries(pam_face_unlock PRIVATE PkgConfig::SYSTEMD)
endif()
install(TARGETS pam_plasma_face_unlock DESTINATION ${PFU_PAMDIR})
install(TARGETS pam_face_unlock DESTINATION ${FU_PAMDIR})
if(PFU_BUILD_TESTS)
if(FU_BUILD_TESTS)
add_executable(pam_harness tests/pam_harness.c)
target_include_directories(pam_harness PRIVATE ${PAM_INCLUDE_DIR})
target_link_libraries(pam_harness PRIVATE ${PAM_LIBRARY})
@@ -129,7 +129,7 @@ endif()
# The agent
# ---------------------------------------------------------------------------
if(PFU_BUILD_AGENT)
if(FU_BUILD_AGENT)
find_package(Qt6 6.5 REQUIRED COMPONENTS Gui Quick WaylandClient)
# The bubble needs the Wayland surface of its window, which only the
# private API hands out. It is the same one Plasma itself uses for this.
@@ -142,7 +142,7 @@ if(PFU_BUILD_AGENT)
find_package(LayerShellQt REQUIRED)
find_package(KF6I18n REQUIRED)
qt_add_executable(plasma-face-unlock-agent
qt_add_executable(face-unlock-agent
src/agent/main.cpp
src/agent/daemonclient.cpp
src/agent/userconfig.cpp
@@ -153,27 +153,27 @@ if(PFU_BUILD_AGENT)
src/agent/inputwatcher.cpp
src/agent/enrollcontroller.cpp
)
target_include_directories(plasma-face-unlock-agent PRIVATE src/agent)
target_include_directories(face-unlock-agent PRIVATE src/agent)
if(LayerShellQt_VERSION VERSION_GREATER_EQUAL 6.6)
target_compile_definitions(plasma-face-unlock-agent PRIVATE PFU_LAYERSHELL_HAS_SCREEN)
target_compile_definitions(face-unlock-agent PRIVATE FU_LAYERSHELL_HAS_SCREEN)
endif()
qt6_generate_wayland_protocol_client_sources(plasma-face-unlock-agent
qt6_generate_wayland_protocol_client_sources(face-unlock-agent
FILES ${CMAKE_CURRENT_SOURCE_DIR}/protocols/kde-lockscreen-overlay-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/protocols/ext-idle-notify-v1.xml)
# The QML files sit next to the module's qmldir in the resources, so they
# see each other (and the Theme singleton) without importing anything.
# Under /qt/qml, which the engine searches, or an installed agent finds no
# types (the build directory only works by its PlasmaFaceUnlock/qmldir).
set(PFU_QML_FILES Theme FaceGlyph LockGlyph Checkmark Bubble TickRing Enroll PillButton)
foreach(f ${PFU_QML_FILES})
# types (the build directory only works by its FaceUnlock/qmldir).
set(FU_QML_FILES Theme FaceGlyph LockGlyph Checkmark Bubble TickRing Enroll PillButton)
foreach(f ${FU_QML_FILES})
set_source_files_properties(src/agent/qml/${f}.qml PROPERTIES QT_RESOURCE_ALIAS ${f}.qml)
endforeach()
set_source_files_properties(src/agent/qml/Theme.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
qt_add_qml_module(plasma-face-unlock-agent
URI PlasmaFaceUnlock
qt_add_qml_module(face-unlock-agent
URI FaceUnlock
VERSION 1.0
RESOURCE_PREFIX /qt/qml
SOURCES
@@ -190,31 +190,31 @@ if(PFU_BUILD_AGENT)
src/agent/qml/PillButton.qml
)
target_link_libraries(plasma-face-unlock-agent PRIVATE
target_link_libraries(face-unlock-agent PRIVATE
Qt6::Gui Qt6::GuiPrivate Qt6::Quick Qt6::DBus Qt6::Network
Qt6::WaylandClient Qt6::WaylandClientPrivate
LayerShellQt::Interface
KF6::I18n KF6::I18nQml
pfu_common
fu_common
)
install(TARGETS plasma-face-unlock-agent DESTINATION ${PFU_LIBEXECDIR})
install(TARGETS face-unlock-agent DESTINATION ${FU_LIBEXECDIR})
endif()
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
if(PFU_BUILD_TESTS)
if(FU_BUILD_TESTS)
enable_testing()
add_executable(test_liveness tests/test_liveness.cpp)
target_link_libraries(test_liveness PRIVATE pfu_core)
target_link_libraries(test_liveness PRIVATE fu_core)
add_test(NAME liveness COMMAND test_liveness)
add_executable(test_store tests/test_store.cpp)
target_link_libraries(test_store PRIVATE pfu_core)
target_link_libraries(test_store PRIVATE fu_core)
add_test(NAME store COMMAND test_store)
add_executable(test_images tests/test_images.cpp)
target_link_libraries(test_images PRIVATE pfu_core)
target_link_libraries(test_images PRIVATE fu_core)
endif()
+41 -41
View File
@@ -1,4 +1,4 @@
# plasma-face-unlock: build and install
# face-unlock: build and install
#
# The shell front end installs as it is, like middleclick-autoscroll. The rest
# is compiled by CMake (the daemon, the agent, the PAM module, the client the
@@ -15,9 +15,9 @@ PREFIX ?= /usr
DESTDIR ?=
BINDIR ?= $(PREFIX)/bin
DATADIR ?= $(PREFIX)/share
LIBDIR ?= $(DATADIR)/plasma-face-unlock/lib
LIBEXECDIR ?= $(PREFIX)/lib/plasma-face-unlock
MODELDIR ?= $(DATADIR)/plasma-face-unlock/models
LIBDIR ?= $(DATADIR)/face-unlock/lib
LIBEXECDIR ?= $(PREFIX)/lib/face-unlock
MODELDIR ?= $(DATADIR)/face-unlock/models
LOCALEDIR ?= $(DATADIR)/locale
MANDIR ?= $(DATADIR)/man
APPDIR ?= $(DATADIR)/applications
@@ -48,7 +48,7 @@ CMAKE_FLAGS ?=
LINGUAS := de es fr it ja ko nl pl pt_BR ru tr uk zh_CN
MOFILES := $(patsubst %,po/%.mo,$(LINGUAS))
MANPAGE := doc/plasma-face-unlock.1
MANPAGE := doc/face-unlock.1
LIBS := $(wildcard src/lib/*.sh)
@@ -85,11 +85,11 @@ native:
$(CMAKE) -S . -B $(BUILDDIR) \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$(PREFIX) \
-DPFU_VERSION=$(VERSION) \
-DPFU_LIBEXECDIR=$(LIBEXECDIR) \
-DPFU_MODELDIR=$(MODELDIR) \
-DPFU_LOCALEDIR=$(LOCALEDIR) \
-DPFU_PAMDIR=$(PAMDIR) \
-DFU_VERSION=$(VERSION) \
-DFU_LIBEXECDIR=$(LIBEXECDIR) \
-DFU_MODELDIR=$(MODELDIR) \
-DFU_LOCALEDIR=$(LOCALEDIR) \
-DFU_PAMDIR=$(PAMDIR) \
$(CMAKE_FLAGS)
$(CMAKE) --build $(BUILDDIR) --parallel
@@ -108,7 +108,7 @@ else
@echo "msgfmt not found, skipping $@"
endif
$(MANPAGE): doc/plasma-face-unlock.1.scd
$(MANPAGE): doc/face-unlock.1.scd
ifdef SCDOC
$(SCDOC) < $< > $@
else
@@ -117,11 +117,11 @@ endif
# Syntax-check every shell file, and run shellcheck when it is available.
check:
@set -e; for f in src/plasma-face-unlock $(LIBS) tests/*.sh; do \
@set -e; for f in src/face-unlock $(LIBS) tests/*.sh; do \
bash -n "$$f" && echo "ok $$f"; \
done
@if command -v shellcheck >/dev/null 2>&1; then \
shellcheck -x -e SC1090,SC1091 src/plasma-face-unlock $(LIBS) tests/*.sh; \
shellcheck -x -e SC1090,SC1091 src/face-unlock $(LIBS) tests/*.sh; \
echo "ok shellcheck"; \
else \
echo "shellcheck not found, skipped"; \
@@ -144,7 +144,7 @@ install: build
DESTDIR="$(DESTDIR)" $(CMAKE) --install $(BUILDDIR)
# the command
install -Dm755 src/plasma-face-unlock "$(DESTDIR)$(BINDIR)/plasma-face-unlock"
install -Dm755 src/face-unlock "$(DESTDIR)$(BINDIR)/face-unlock"
install -d "$(DESTDIR)$(LIBDIR)"
install -Dm644 -t "$(DESTDIR)$(LIBDIR)" $(LIBS)
sed -i -e 's|@VERSION@|$(VERSION)|g' \
@@ -152,7 +152,7 @@ install: build
-e 's|@LIBEXECDIR@|$(LIBEXECDIR)|g' \
-e 's|@LOCALEDIR@|$(LOCALEDIR)|g' \
-e 's|@PAMDIR@|$(PAMDIR)|g' \
"$(DESTDIR)$(BINDIR)/plasma-face-unlock" \
"$(DESTDIR)$(BINDIR)/face-unlock" \
"$(DESTDIR)$(LIBDIR)"/*.sh
# the models
@@ -161,50 +161,50 @@ install: build
done
# the daemon's socket and service, the agent's user service
install -Dm644 res/systemd/plasma-face-unlockd.socket "$(DESTDIR)$(SYSTEMUNITDIR)/plasma-face-unlockd.socket"
install -Dm644 res/systemd/plasma-face-unlockd.service "$(DESTDIR)$(SYSTEMUNITDIR)/plasma-face-unlockd.service"
install -Dm644 res/systemd/plasma-face-unlock-agent.service "$(DESTDIR)$(USERUNITDIR)/plasma-face-unlock-agent.service"
install -Dm644 res/systemd/face-unlockd.socket "$(DESTDIR)$(SYSTEMUNITDIR)/face-unlockd.socket"
install -Dm644 res/systemd/face-unlockd.service "$(DESTDIR)$(SYSTEMUNITDIR)/face-unlockd.service"
install -Dm644 res/systemd/face-unlock-agent.service "$(DESTDIR)$(USERUNITDIR)/face-unlock-agent.service"
sed -i -e 's|@LIBEXECDIR@|$(LIBEXECDIR)|g' \
"$(DESTDIR)$(SYSTEMUNITDIR)/plasma-face-unlockd.service" \
"$(DESTDIR)$(USERUNITDIR)/plasma-face-unlock-agent.service"
"$(DESTDIR)$(SYSTEMUNITDIR)/face-unlockd.service" \
"$(DESTDIR)$(USERUNITDIR)/face-unlock-agent.service"
# the desktop file KWin looks for before it lets the bubble above the
# lock screen, the polkit action, the icon
install -Dm644 res/applications/io.github.loonixtools.plasma-face-unlock-agent.desktop \
"$(DESTDIR)$(APPDIR)/io.github.loonixtools.plasma-face-unlock-agent.desktop"
sed -i -e 's|@LIBEXECDIR@|$(LIBEXECDIR)|g' "$(DESTDIR)$(APPDIR)/io.github.loonixtools.plasma-face-unlock-agent.desktop"
install -Dm644 res/polkit/io.github.loonixtools.plasma-face-unlock.policy \
"$(DESTDIR)$(POLKITDIR)/io.github.loonixtools.plasma-face-unlock.policy"
install -Dm644 res/plasma-face-unlock.svg "$(DESTDIR)$(ICONDIR)/plasma-face-unlock.svg"
install -Dm644 res/applications/io.github.loonixtools.face-unlock-agent.desktop \
"$(DESTDIR)$(APPDIR)/io.github.loonixtools.face-unlock-agent.desktop"
sed -i -e 's|@LIBEXECDIR@|$(LIBEXECDIR)|g' "$(DESTDIR)$(APPDIR)/io.github.loonixtools.face-unlock-agent.desktop"
install -Dm644 res/polkit/io.github.loonixtools.face-unlock.policy \
"$(DESTDIR)$(POLKITDIR)/io.github.loonixtools.face-unlock.policy"
install -Dm644 res/face-unlock.svg "$(DESTDIR)$(ICONDIR)/face-unlock.svg"
# translations
@for l in $(LINGUAS); do \
if [ -f "po/$$l.mo" ]; then \
install -Dm644 "po/$$l.mo" \
"$(DESTDIR)$(LOCALEDIR)/$$l/LC_MESSAGES/plasma-face-unlock.mo"; \
"$(DESTDIR)$(LOCALEDIR)/$$l/LC_MESSAGES/face-unlock.mo"; \
fi; \
done
# documentation
@if [ -f $(MANPAGE) ]; then \
install -Dm644 $(MANPAGE) "$(DESTDIR)$(MANDIR)/man1/plasma-face-unlock.1"; \
install -Dm644 $(MANPAGE) "$(DESTDIR)$(MANDIR)/man1/face-unlock.1"; \
fi
install -Dm644 README.md "$(DESTDIR)$(DATADIR)/doc/plasma-face-unlock/README.md"
install -Dm644 README.md "$(DESTDIR)$(DATADIR)/doc/face-unlock/README.md"
uninstall:
rm -f "$(DESTDIR)$(BINDIR)/plasma-face-unlock"
rm -rf "$(DESTDIR)$(DATADIR)/plasma-face-unlock"
rm -f "$(DESTDIR)$(BINDIR)/face-unlock"
rm -rf "$(DESTDIR)$(DATADIR)/face-unlock"
rm -rf "$(DESTDIR)$(LIBEXECDIR)"
rm -f "$(DESTDIR)$(PAMDIR)/pam_plasma_face_unlock.so"
rm -f "$(DESTDIR)$(SYSTEMUNITDIR)/plasma-face-unlockd.socket"
rm -f "$(DESTDIR)$(SYSTEMUNITDIR)/plasma-face-unlockd.service"
rm -f "$(DESTDIR)$(USERUNITDIR)/plasma-face-unlock-agent.service"
rm -f "$(DESTDIR)$(APPDIR)/io.github.loonixtools.plasma-face-unlock-agent.desktop"
rm -f "$(DESTDIR)$(POLKITDIR)/io.github.loonixtools.plasma-face-unlock.policy"
rm -f "$(DESTDIR)$(ICONDIR)/plasma-face-unlock.svg"
rm -f "$(DESTDIR)$(MANDIR)/man1/plasma-face-unlock.1"
rm -rf "$(DESTDIR)$(DATADIR)/doc/plasma-face-unlock"
@for l in $(LINGUAS); do rm -f "$(DESTDIR)$(LOCALEDIR)/$$l/LC_MESSAGES/plasma-face-unlock.mo"; done
rm -f "$(DESTDIR)$(PAMDIR)/pam_face_unlock.so"
rm -f "$(DESTDIR)$(SYSTEMUNITDIR)/face-unlockd.socket"
rm -f "$(DESTDIR)$(SYSTEMUNITDIR)/face-unlockd.service"
rm -f "$(DESTDIR)$(USERUNITDIR)/face-unlock-agent.service"
rm -f "$(DESTDIR)$(APPDIR)/io.github.loonixtools.face-unlock-agent.desktop"
rm -f "$(DESTDIR)$(POLKITDIR)/io.github.loonixtools.face-unlock.policy"
rm -f "$(DESTDIR)$(ICONDIR)/face-unlock.svg"
rm -f "$(DESTDIR)$(MANDIR)/man1/face-unlock.1"
rm -rf "$(DESTDIR)$(DATADIR)/doc/face-unlock"
@for l in $(LINGUAS); do rm -f "$(DESTDIR)$(LOCALEDIR)/$$l/LC_MESSAGES/face-unlock.mo"; done
clean:
rm -rf $(BUILDDIR) po/*.mo $(MANPAGE)
+19 -19
View File
@@ -1,8 +1,8 @@
<p align="center">
<img width="200" src="res/plasma-face-unlock.svg" alt="plasma-face-unlock">
<img width="200" src="res/face-unlock.svg" alt="face-unlock">
</p>
<h1 align="center">plasma-face-unlock</h1>
<h1 align="center">face-unlock</h1>
<h3 align="center">Face ID for KDE Plasma.</h3>
@@ -14,7 +14,7 @@
<a href="#install">Install</a> |
<a href="#how-to-use">How to use</a> |
<a href="#is-it-safe">Is it safe?</a> |
<a href="https://github.com/LoonixTools/plasma-face-unlock/issues">Report a bug</a>
<a href="https://github.com/LoonixTools/face-unlock/issues">Report a bug</a>
</h5>
<p align="center">
@@ -35,7 +35,7 @@
<summary><b>Arch</b>, CachyOS, EndeavourOS, Manjaro</summary>
```bash
yay -S plasma-face-unlock
yay -S face-unlock
```
</details>
@@ -44,9 +44,9 @@ yay -S plasma-face-unlock
<summary><b>Fedora</b></summary>
```bash
sudo curl -fsSL -o /etc/yum.repos.d/plasma-face-unlock.repo \
https://loonixtools.github.io/plasma-face-unlock/plasma-face-unlock.repo
sudo dnf install plasma-face-unlock
sudo curl -fsSL -o /etc/yum.repos.d/face-unlock.repo \
https://loonixtools.github.io/face-unlock/face-unlock.repo
sudo dnf install face-unlock
```
</details>
@@ -57,11 +57,11 @@ sudo dnf install plasma-face-unlock
```bash
codename="$(sed -n 's/^VERSION_CODENAME=//p' /etc/os-release)"
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://loonixtools.github.io/plasma-face-unlock/KEY.gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/plasma-face-unlock.gpg
echo "deb [signed-by=/etc/apt/keyrings/plasma-face-unlock.gpg] https://loonixtools.github.io/plasma-face-unlock/deb/$codename ./" \
| sudo tee /etc/apt/sources.list.d/plasma-face-unlock.list
sudo apt update && sudo apt install plasma-face-unlock
curl -fsSL https://loonixtools.github.io/face-unlock/KEY.gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/face-unlock.gpg
echo "deb [signed-by=/etc/apt/keyrings/face-unlock.gpg] https://loonixtools.github.io/face-unlock/deb/$codename ./" \
| sudo tee /etc/apt/sources.list.d/face-unlock.list
sudo apt update && sudo apt install face-unlock
```
</details>
@@ -71,11 +71,11 @@ Needs Plasma 6 on Wayland and a camera. Updates come with your system updates.
## How to use
```bash
plasma-face-unlock
face-unlock
```
<p align="center">
<img src="res/screenshots/menu.png" alt="The plasma-face-unlock menu in Konsole: face unlock on, one face, lock screen, sudo and admin prompts on" width="560">
<img src="res/screenshots/menu.png" alt="The face-unlock menu in Konsole: face unlock on, one face, lock screen, sudo and admin prompts on" width="560">
</p>
Press **1** and look at the camera. Then lock the screen and look at it.
@@ -109,13 +109,13 @@ A convenience, not extra security. A webcam only sees a flat picture.
| | |
|---|---|
| `plasma-face-unlockd` | The root service. Owns the camera and the face data. |
| `plasma-face-unlock-agent` | Runs in your session. Watches the lock screen, draws the bubble. |
| `pam_plasma_face_unlock.so` | Lets sudo and admin prompts ask the service. |
| `plasma-face-unlock` | The menu. |
| `face-unlockd` | The root service. Owns the camera and the face data. |
| `face-unlock-agent` | Runs in your session. Watches the lock screen, draws the bubble. |
| `pam_face_unlock.so` | Lets sudo and admin prompts ask the service. |
| `face-unlock` | The menu. |
Two small networks from the OpenCV model zoo run on the CPU: YuNet finds the face, SFace turns it
into numbers. All details: `man plasma-face-unlock`.
into numbers. All details: `man face-unlock`.
</details>
@@ -1,12 +1,12 @@
plasma-face-unlock(1)
face-unlock(1)
# NAME
plasma-face-unlock - face unlock for KDE Plasma
face-unlock - face unlock for KDE Plasma
# SYNOPSIS
*plasma-face-unlock* [_command_]
*face-unlock* [_command_]
# DESCRIPTION
@@ -64,23 +64,23 @@ It is a convenience, not a security upgrade. See *HOW SAFE IS THIS*.
# THE PIECES
*plasma-face-unlockd*
*face-unlockd*
The daemon, running as root and started by its socket
(_plasma-face-unlockd.socket_). It is the only thing that opens the camera
(_face-unlockd.socket_). It is the only thing that opens the camera
and the only thing that can read the face data. It exits after a minute
with nothing to do.
*plasma-face-unlock-agent*
*face-unlock-agent*
Runs in the Plasma session as a user service
(_plasma-face-unlock-agent.service_). It watches the lock screen, asks the
(_face-unlock-agent.service_). It watches the lock screen, asks the
daemon to scan when somebody comes back, unlocks the session when the face
matches, draws the bubble, and is the setup window.
*pam_plasma_face_unlock.so*
*pam_face_unlock.so*
The PAM module for sudo and admin prompts. It asks the daemon and turns
the answer into a PAM result.
*plasma-face-unlock-ctl*
*face-unlock-ctl*
How this command talks to the daemon.
# RECOGNITION
@@ -196,14 +196,14 @@ never lock the account.
The bubble is a layer-shell surface that KWin keeps above the lock screen
(kde_lockscreen_overlay_v1). KWin only allows that for a program whose desktop
file asks for it, which is
_io.github.loonixtools.plasma-face-unlock-agent.desktop_.
_io.github.loonixtools.face-unlock-agent.desktop_.
# SUDO AND ADMIN PROMPTS
Turned on under *Settings*, one line goes in front of the service's PAM stack:
```
-auth sufficient /usr/lib/security/pam_plasma_face_unlock.so
-auth sufficient /usr/lib/security/pam_face_unlock.so
```
A match lets the person in; anything else falls through to the password as if
@@ -232,27 +232,27 @@ A laptop with its lid shut is not scanned (*Not when the lid is closed*).
# FILES
_~/.config/plasma-face-unlock/config_
_~/.config/face-unlock/config_
This user's settings: the lock screen, the bubble and its animation
speed. Written by the menu.
_/etc/plasma-face-unlock/config_
_/etc/face-unlock/config_
The system settings: camera, photo check, strictness, attention, scan
length. Written by the menu through sudo.
_/var/lib/plasma-face-unlock/users/<uid>.json_
_/var/lib/face-unlock/users/<uid>.json_
The face data: numbers, no pictures. Root only.
_/var/lib/plasma-face-unlock/users/<uid>.state_
_/var/lib/face-unlock/users/<uid>.state_
Failed scans in a row, the pause they lead to, the last unlock.
_/usr/share/plasma-face-unlock/models/_
_/usr/share/face-unlock/models/_
The two networks.
_/run/plasma-face-unlock/socket_
_/run/face-unlock/socket_
The daemon's socket.
_$XDG_RUNTIME_DIR/plasma-face-unlock/agent.socket_
_$XDG_RUNTIME_DIR/face-unlock/agent.socket_
Where the daemon tells the agent about scans it did not start, for the
bubble.
@@ -273,7 +273,7 @@ Qt 6, LayerShellQt, KI18n, systemd, polkit and Linux-PAM.
# AUTHORS
Felitendo. Source and issue tracker at
https://github.com/LoonixTools/plasma-face-unlock
https://github.com/LoonixTools/face-unlock
The liveness model and the look of the bubble follow Glance by Jonathan Zhou
(https://github.com/jonnyoo/glance, MIT). The models are YuNet and SFace from
+6 -6
View File
@@ -7,13 +7,13 @@ description of the layout, and two descriptions drift.
| | |
|---|---|
| `deb/control`, `deb/copyright` | metadata for the Debian binary package |
| `rpm/plasma-face-unlock.spec` | the RPM spec |
| `rpm/face-unlock.spec` | the RPM spec |
| `build-deb.sh`, `build-rpm.sh` | build one package into `dist/` |
| `check-version.sh` | refuses a tag that disagrees with the Makefile |
| `publish-repos.sh` | regenerates the APT and RPM repositories |
| `pages/` | the landing page and the `.repo` file served from GitHub Pages |
The AUR package lives in [Felitendo/PKGBUILDS](https://github.com/Felitendo/PKGBUILDS/tree/main/plasma-face-unlock).
The AUR package lives in [Felitendo/PKGBUILDS](https://github.com/Felitendo/PKGBUILDS/tree/main/face-unlock).
Its CI notices a new GitHub release, updates the checksum and pushes to the AUR.
Unlike the shell-only LoonixTools, this one is compiled. The packages are per
@@ -34,7 +34,7 @@ checksums in the Makefile. The RPM spec and the PKGBUILD list them as sources
of their own, with the same checksums.
Neither the deb nor the rpm switches anything on at install time. The daemon's
socket is enabled by `plasma-face-unlock` the first time somebody turns it on,
socket is enabled by `face-unlock` the first time somebody turns it on,
the agent is a user service each user enables, and the PAM files are only
touched when somebody asks for sudo or admin prompts. Removing a package
disables the socket.
@@ -52,7 +52,7 @@ argument.
## Making a release
1. Bump `VERSION` in the Makefile. The compiled programs get it from there
too (`-DPFU_VERSION`).
too (`-DFU_VERSION`).
2. Add the release to `CHANGELOG.md`, in the format CLAUDE.md describes.
3. Commit, then `git tag vX.Y.Z && git push --tags`.
@@ -75,9 +75,9 @@ repositories. Nothing is pushed and no release is made.
```bash
gpg --batch --passphrase '' --quick-generate-key \
'plasma-face-unlock repository <felitendoyt@gmail.com>' rsa4096 sign never
'face-unlock repository <felitendoyt@gmail.com>' rsa4096 sign never
gpg --armor --export-secret-keys 'plasma-face-unlock repository' \
gpg --armor --export-secret-keys 'face-unlock repository' \
| gh secret set GPG_PRIVATE_KEY
```
+2 -2
View File
@@ -21,7 +21,7 @@ version="${1:-$(make -s -C "$here" version)}"
# The package depends on the exact Qt of the distribution it is built on, so
# each one gets a build of its own, told apart by a suffix: ~deb13, ~ubuntu26.04.
debversion="$version${DEB_SUFFIX:-}"
name=plasma-face-unlock
name=face-unlock
# A package without its man page or its translations is not a package this
# should be quietly willing to produce.
@@ -63,7 +63,7 @@ cat > "$root/DEBIAN/prerm" <<'SH'
#!/bin/sh
set -e
if [ "$1" = remove ] && [ -d /run/systemd/system ]; then
systemctl disable --now plasma-face-unlockd.socket plasma-face-unlockd.service >/dev/null 2>&1 || true
systemctl disable --now face-unlockd.socket face-unlockd.service >/dev/null 2>&1 || true
fi
SH
chmod 755 "$root/DEBIAN/prerm"
+1 -1
View File
@@ -14,7 +14,7 @@ set -euo pipefail
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
version="${1:-$(make -s -C "$here" version)}"
name=plasma-face-unlock
name=face-unlock
command -v rpmbuild > /dev/null || { echo "$0: rpmbuild is not installed" >&2; exit 1; }
+3 -3
View File
@@ -1,4 +1,4 @@
Package: plasma-face-unlock
Package: face-unlock
Version: @VERSION@
Section: admin
Priority: optional
@@ -6,7 +6,7 @@ Architecture: @ARCH@
Maintainer: Felitendo <felitendoyt@gmail.com>
Depends: @DEPENDS@, bash (>= 4.2), coreutils, grep, sed, mawk | gawk, systemd, polkitd | policykit-1, qml6-module-qtquick, qml6-module-qtquick-shapes, qml6-module-qtquick-effects, qml6-module-qtquick-window, qml6-module-qtqml-workerscript
Recommends: gettext-base, kscreenlocker
Homepage: https://github.com/LoonixTools/plasma-face-unlock
Homepage: https://github.com/LoonixTools/face-unlock
Description: face unlock for KDE Plasma
Look at the screen and it unlocks, the way a phone does. The lock screen,
sudo in a terminal and the admin password prompts of Plasma can take a face
@@ -21,5 +21,5 @@ Description: face unlock for KDE Plasma
through.
.
Everything runs on the machine. Faces are stored as numbers readable only by
root, never as pictures. Run "plasma-face-unlock disable" before removing
root, never as pictures. Run "face-unlock disable" before removing
this package, so that sudo and polkit go back to the password alone.
+4 -4
View File
@@ -1,17 +1,17 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: plasma-face-unlock
Source: https://github.com/LoonixTools/plasma-face-unlock
Upstream-Name: face-unlock
Source: https://github.com/LoonixTools/face-unlock
Files: *
Copyright: 2026 Felitendo
License: GPL-3+
Files: usr/share/plasma-face-unlock/models/face_detection_yunet_2023mar.onnx
Files: usr/share/face-unlock/models/face_detection_yunet_2023mar.onnx
Copyright: 2021-2023 Shiqi Yu, Wei Wu and the YuNet authors
License: MIT
Comment: https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet
Files: usr/share/plasma-face-unlock/models/face_recognition_sface_2021dec.onnx
Files: usr/share/face-unlock/models/face_recognition_sface_2021dec.onnx
Copyright: 2021 Yaoyao Zhong and Weihong Deng
License: Apache-2.0
Comment: https://github.com/opencv/opencv_zoo/tree/main/models/face_recognition_sface
@@ -1,5 +1,5 @@
[plasma-face-unlock]
name=plasma-face-unlock
[face-unlock]
name=face-unlock
baseurl=@BASEURL@/rpm
enabled=1
gpgcheck=1
+17 -17
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>plasma-face-unlock</title>
<title>face-unlock</title>
<style>
:root {
--bg: #ffffff;
@@ -62,7 +62,7 @@
</head>
<body>
<h1>plasma-face-unlock</h1>
<h1>face-unlock</h1>
<p class="lead">
Face ID for KDE Plasma: look at the screen and it unlocks. The lock screen,
sudo and the admin prompts can take a face instead of a password, and a photo
@@ -72,42 +72,42 @@
<p>
This page is the package repository. Set it up once and every new version
arrives with the rest of your system updates. The
<a href="https://github.com/LoonixTools/plasma-face-unlock">source and the
<a href="https://github.com/LoonixTools/face-unlock">source and the
documentation</a> are on GitHub.
</p>
<h2>Debian 13</h2>
<pre><code>sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL @BASEURL@/KEY.gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/plasma-face-unlock.gpg
echo "deb [signed-by=/etc/apt/keyrings/plasma-face-unlock.gpg] @BASEURL@/deb/trixie ./" \
| sudo tee /etc/apt/sources.list.d/plasma-face-unlock.list
| sudo gpg --dearmor -o /etc/apt/keyrings/face-unlock.gpg
echo "deb [signed-by=/etc/apt/keyrings/face-unlock.gpg] @BASEURL@/deb/trixie ./" \
| sudo tee /etc/apt/sources.list.d/face-unlock.list
sudo apt update
sudo apt install plasma-face-unlock</code></pre>
sudo apt install face-unlock</code></pre>
<h2>Kubuntu 26.04</h2>
<pre><code>sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL @BASEURL@/KEY.gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/plasma-face-unlock.gpg
echo "deb [signed-by=/etc/apt/keyrings/plasma-face-unlock.gpg] @BASEURL@/deb/resolute ./" \
| sudo tee /etc/apt/sources.list.d/plasma-face-unlock.list
| sudo gpg --dearmor -o /etc/apt/keyrings/face-unlock.gpg
echo "deb [signed-by=/etc/apt/keyrings/face-unlock.gpg] @BASEURL@/deb/resolute ./" \
| sudo tee /etc/apt/sources.list.d/face-unlock.list
sudo apt update
sudo apt install plasma-face-unlock</code></pre>
sudo apt install face-unlock</code></pre>
<h2>Fedora 44 (KDE Plasma)</h2>
<pre><code>sudo curl -fsSL -o /etc/yum.repos.d/plasma-face-unlock.repo \
@BASEURL@/plasma-face-unlock.repo
sudo dnf install plasma-face-unlock</code></pre>
<pre><code>sudo curl -fsSL -o /etc/yum.repos.d/face-unlock.repo \
@BASEURL@/face-unlock.repo
sudo dnf install face-unlock</code></pre>
<h2>Arch, CachyOS, EndeavourOS, Manjaro</h2>
<pre><code>paru -S plasma-face-unlock</code></pre>
<pre><code>paru -S face-unlock</code></pre>
<h2>Then, once</h2>
<pre><code>plasma-face-unlock</code></pre>
<pre><code>face-unlock</code></pre>
<p>
Press 1. It sets up your face (look at the camera, move your head in a
circle) and turns face unlock on. sudo and the admin prompts are off until
you switch them on under Settings. Run <code>plasma-face-unlock disable</code>
you switch them on under Settings. Run <code>face-unlock disable</code>
before removing the package: it takes face unlock back out of sudo and
polkit.
</p>
+5 -5
View File
@@ -19,7 +19,7 @@ pages="$(cd -- "$1" && pwd)"
incoming="$(cd -- "$2" && pwd)"
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
base_url="${PFU_REPO_URL:-https://loonixtools.github.io/plasma-face-unlock}"
base_url="${FU_REPO_URL:-https://loonixtools.github.io/face-unlock}"
keyid="$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/ { print $5; exit }')"
[[ -n $keyid ]] || { echo "$0: no secret key in the keyring" >&2; exit 1; }
@@ -40,8 +40,8 @@ for suite in trixie:deb13 resolute:ubuntu26.04; do
dpkg-scanpackages --multiversion . > Packages
gzip -9kf Packages
apt-ftparchive \
-o APT::FTPArchive::Release::Origin=plasma-face-unlock \
-o APT::FTPArchive::Release::Label=plasma-face-unlock \
-o APT::FTPArchive::Release::Origin=face-unlock \
-o APT::FTPArchive::Release::Label=face-unlock \
-o APT::FTPArchive::Release::Suite="$codename" \
-o APT::FTPArchive::Release::Codename="$codename" \
-o APT::FTPArchive::Release::Architectures=amd64 \
@@ -65,8 +65,8 @@ done
gpg --armor --export "$keyid" > "$pages/KEY.gpg"
sed "s|@BASEURL@|$base_url|g" "$here/packaging/pages/index.html" > "$pages/index.html"
sed "s|@BASEURL@|$base_url|g" "$here/packaging/pages/plasma-face-unlock.repo" \
> "$pages/plasma-face-unlock.repo"
sed "s|@BASEURL@|$base_url|g" "$here/packaging/pages/face-unlock.repo" \
> "$pages/face-unlock.repo"
# Pages would otherwise hand the whole directory to Jekyll, which drops every
# file whose name starts with an underscore and can rewrite the rest.
@@ -2,7 +2,7 @@
# editing this file: the Makefile is where the version is written down.
%global upstream_version %{?_version}%{!?_version:1.0.1}
Name: plasma-face-unlock
Name: face-unlock
Version: %{upstream_version}
Release: 1%{?dist}
Summary: Face unlock for KDE Plasma
@@ -10,7 +10,7 @@ Summary: Face unlock for KDE Plasma
# The program is GPL; the two networks it ships are MIT (YuNet) and
# Apache-2.0 (SFace).
License: GPL-3.0-or-later AND MIT AND Apache-2.0
URL: https://github.com/LoonixTools/plasma-face-unlock
URL: https://github.com/LoonixTools/face-unlock
Source0: %{name}-%{version}.tar.gz
Source1: https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx
Source2: https://github.com/opencv/opencv_zoo/raw/main/models/face_recognition_sface/face_recognition_sface_2021dec.onnx
@@ -59,7 +59,7 @@ or the nose moving the way a real nose does when the head turns), which stops a
printed photo too. It is a convenience, not a security upgrade: a webcam sees a
flat picture, and a video of the person can get through.
Run "plasma-face-unlock disable" before removing this package, so that sudo
Run "face-unlock disable" before removing this package, so that sudo
and polkit go back to the password alone.
%prep
@@ -78,10 +78,10 @@ make install DESTDIR=%{buildroot} PREFIX=%{_prefix} VERSION=%{upstream_version}
%find_lang %{name}
%preun
%systemd_preun plasma-face-unlockd.socket plasma-face-unlockd.service
%systemd_preun face-unlockd.socket face-unlockd.service
%postun
%systemd_postun plasma-face-unlockd.socket plasma-face-unlockd.service
%systemd_postun face-unlockd.socket face-unlockd.service
%files -f %{name}.lang
%license LICENSE
@@ -89,13 +89,13 @@ make install DESTDIR=%{buildroot} PREFIX=%{_prefix} VERSION=%{upstream_version}
%{_bindir}/%{name}
%{_prefix}/lib/%{name}/
%{_datadir}/%{name}/
%{_libdir}/security/pam_plasma_face_unlock.so
%{_unitdir}/plasma-face-unlockd.socket
%{_unitdir}/plasma-face-unlockd.service
%{_userunitdir}/plasma-face-unlock-agent.service
%{_datadir}/applications/io.github.loonixtools.plasma-face-unlock-agent.desktop
%{_datadir}/polkit-1/actions/io.github.loonixtools.plasma-face-unlock.policy
%{_datadir}/icons/hicolor/scalable/apps/plasma-face-unlock.svg
%{_libdir}/security/pam_face_unlock.so
%{_unitdir}/face-unlockd.socket
%{_unitdir}/face-unlockd.service
%{_userunitdir}/face-unlock-agent.service
%{_datadir}/applications/io.github.loonixtools.face-unlock-agent.desktop
%{_datadir}/polkit-1/actions/io.github.loonixtools.face-unlock.policy
%{_datadir}/icons/hicolor/scalable/apps/face-unlock.svg
%{_mandir}/man1/%{name}.1*
%changelog
+44 -44
View File
@@ -1,11 +1,11 @@
# German translation for plasma-face-unlock.
# German translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.0\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/"
"Project-Id-Version: face-unlock 1.0.0\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/"
"issues\n"
"POT-Creation-Date: 2026-09-24 10:46+0200\n"
"PO-Revision-Date: 2026-09-23 22:32+0200\n"
@@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid ""
"Face unlock's service has to be switched on once for this computer. That "
"needs your password."
@@ -24,12 +24,12 @@ msgstr ""
"Der Dienst für die Gesichtsentsperrung muss auf diesem Computer einmal "
"eingeschaltet werden. Dafür wird dein Passwort gebraucht."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Prüfen mit: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid ""
"Setting up a face needs the camera picture on screen. Run this inside your "
"Plasma session."
@@ -37,136 +37,136 @@ msgstr ""
"Zum Einrichten eines Gesichts muss das Kamerabild auf dem Bildschirm zu "
"sehen sein. Starte das in deiner Plasma-Sitzung."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "Das Einrichtungsfenster ist offen. Folge den Schritten dort."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "Das Gesicht ist eingerichtet."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr ""
"Die Gesichtsentsperrung ist noch aus. Schalte sie mit [1] oder `%s enable` "
"ein."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "Es wurde kein Gesicht hinzugefügt."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Richte zuerst dein Gesicht ein."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "Einstellung konnte nicht gespeichert werden."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "Der Sperrbildschirm-Dienst konnte nicht gestartet werden."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr ""
"Keine systemd-Benutzersitzung, daher wurde der Sperrbildschirm-Dienst nicht "
"gestartet."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr ""
"Die Gesichtsentsperrung ist an. Sperre den Bildschirm und schau ihn an, um "
"es auszuprobieren."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "Sie kann auch sudo und Admin-Abfragen übernehmen. Siehe Einstellungen."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr ""
"Die Gesichtsentsperrung ist aus. Deine Gesichter bleiben erhalten; löschen "
"kannst du sie unter Gesichter."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Es ist noch kein Gesicht eingerichtet."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "an"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "aus"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "%s Aufnahmen, %s dazugelernt"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "Welches Gesicht? Die IDs zeigt `%s faces`."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Gelöscht."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Aufruf: plasma-face-unlock [Befehl]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Aufruf: face-unlock [Befehl]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Befehle:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Gesichtsentsperrung einschalten"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Ausschalten (Gesichter bleiben erhalten)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Gesicht hinzufügen"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "Gesichter auflisten"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Ein Gesicht löschen"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "In die Kamera schauen und sehen, was sie sieht"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Anzeigen, was eingeschaltet ist"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Diese Hilfe anzeigen"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "Die Version anzeigen"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Ohne Befehl wird ein interaktives Menü angezeigt."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid ""
"Run this as your own user, not as root. It asks for your password when it "
"needs to."
@@ -174,7 +174,7 @@ msgstr ""
"Starte das als dein eigener Benutzer, nicht als root. Wenn nötig, wird nach "
"deinem Passwort gefragt."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Unbekannter Befehl: %s"
@@ -880,21 +880,21 @@ msgstr "Die Animationen der Bubble einmal abspielen."
msgid "Bubble style for the demo: full or minimal."
msgstr "Bubble-Stil für die Demo: full oder minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Schau zum Entsperren in die Kamera."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Blinzle oder dreh den Kopf ein wenig."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr ""
"Die Gesichtsentsperrung ist nach zu vielen Versuchen pausiert. Nutze dein "
"Passwort."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Gesicht nicht erkannt."
+44 -44
View File
@@ -1,11 +1,11 @@
# Spanish translation for plasma-face-unlock.
# Spanish translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:55+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "El servicio de desbloqueo facial tiene que activarse una vez en este equipo. Para ello hace falta tu contraseña."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Compruébalo con: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "Para configurar una cara, la imagen de la cámara tiene que verse en pantalla. Ejecuta esto dentro de tu sesión de Plasma."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "La ventana de configuración está abierta. Sigue los pasos allí."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "La cara está configurada."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "El desbloqueo facial sigue desactivado. Actívalo con [1] o `%s enable`."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "No se añadió ninguna cara."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Primero, configura tu cara."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "No se pudo guardar el ajuste."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "No se pudo iniciar el agente de la pantalla de bloqueo."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "No hay sesión de usuario de systemd, así que no se inició el agente de la pantalla de bloqueo."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "El desbloqueo facial está activado. Bloquea la pantalla y mírala para probarlo."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "También funciona con sudo y las solicitudes de administrador. Consulta Ajustes."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "El desbloqueo facial está desactivado. Tus caras se conservan; bórralas en Caras."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Todavía no hay ninguna cara configurada."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "activado"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "desactivado"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "%s muestras, %s aprendidas"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "¿Qué cara? Consulta `%s faces` para ver los ID."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Borrado."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Uso: plasma-face-unlock [comando]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Uso: face-unlock [comando]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Comandos:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Activar el desbloqueo facial"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Desactivarlo (las caras se conservan)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Añadir una cara"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "Listar las caras"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Borrar una cara"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "Mirar a la cámara y ver lo que ve"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Mostrar qué está activado"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Mostrar esta ayuda"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "Mostrar la versión"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Sin comando se muestra un menú interactivo."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "Ejecuta esto con tu propio usuario, no como root. Pide tu contraseña cuando la necesita."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Comando desconocido: %s"
@@ -795,19 +795,19 @@ msgstr "Reproducir una vez las animaciones de la burbuja."
msgid "Bubble style for the demo: full or minimal."
msgstr "Estilo de burbuja para la demo: full o minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Mira a la cámara para desbloquear."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Parpadea o gira un poco la cabeza."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "El desbloqueo facial está en pausa tras demasiados intentos. Usa tu contraseña."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Cara no reconocida."
@@ -1,12 +1,12 @@
# Translation template for plasma-face-unlock.
# Translation template for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/"
"issues\n"
"POT-Creation-Date: 2026-09-24 10:56+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
@@ -17,151 +17,151 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid ""
"Face unlock's service has to be switched on once for this computer. That "
"needs your password."
msgstr ""
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr ""
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid ""
"Setting up a face needs the camera picture on screen. Run this inside your "
"Plasma session."
msgstr ""
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr ""
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr ""
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr ""
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr ""
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr ""
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr ""
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr ""
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr ""
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr ""
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr ""
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr ""
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr ""
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr ""
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr ""
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr ""
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr ""
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr ""
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr ""
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr ""
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr ""
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr ""
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr ""
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr ""
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr ""
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr ""
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr ""
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr ""
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr ""
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr ""
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid ""
"Run this as your own user, not as root. It asks for your password when it "
"needs to."
msgstr ""
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr ""
@@ -828,19 +828,19 @@ msgstr ""
msgid "Bubble style for the demo: full or minimal."
msgstr ""
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr ""
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr ""
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr ""
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr ""
+44 -44
View File
@@ -1,11 +1,11 @@
# French translation for plasma-face-unlock.
# French translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:55+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "Le service de déverrouillage facial doit être activé une fois sur cet ordinateur. Cela demande votre mot de passe."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Vérifiez avec : systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "Pour configurer un visage, l'image de la caméra doit s'afficher à l'écran. Lancez ceci dans votre session Plasma."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "La fenêtre de configuration est ouverte. Suivez les étapes qui s'y trouvent."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "Le visage est configuré."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "Le déverrouillage facial est encore désactivé. Activez-le avec [1] ou `%s enable`."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "Aucun visage n'a été ajouté."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Configurez d'abord votre visage."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "Impossible d'enregistrer le réglage."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "Impossible de démarrer l'agent de l'écran de verrouillage."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "Pas de session utilisateur systemd, l'agent de l'écran de verrouillage n'a donc pas été démarré."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "Le déverrouillage facial est activé. Verrouillez l'écran et regardez-le pour essayer."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "Il fonctionne aussi pour sudo et les demandes d'administration. Voir Réglages."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "Le déverrouillage facial est désactivé. Vos visages sont conservés ; supprimez-les dans Visages."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Aucun visage n'est encore configuré."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "activé"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "désactivé"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "%s échantillons, %s appris"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "Quel visage ? Voir `%s faces` pour les identifiants."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Supprimé."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Utilisation : plasma-face-unlock [commande]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Utilisation : face-unlock [commande]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Commandes :"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Activer le déverrouillage facial"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Le désactiver (les visages sont conservés)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Ajouter un visage"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "Lister les visages"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Supprimer un visage"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "Regarder la caméra et voir ce qu'elle voit"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Afficher ce qui est activé"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Afficher cette aide"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "Afficher la version"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Sans commande, un menu interactif s'affiche."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "Lancez ceci avec votre propre utilisateur, pas en root. Le mot de passe est demandé quand il le faut."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Commande inconnue : %s"
@@ -795,19 +795,19 @@ msgstr "Jouer une fois les animations de la bulle."
msgid "Bubble style for the demo: full or minimal."
msgstr "Style de bulle pour la démo : full ou minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Regardez la caméra pour déverrouiller."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Clignez des yeux ou tournez un peu la tête."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "Le déverrouillage facial est en pause après trop d'essais. Utilisez votre mot de passe."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Visage non reconnu."
+44 -44
View File
@@ -1,11 +1,11 @@
# Italian translation for plasma-face-unlock.
# Italian translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:55+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "Il servizio di sblocco con il volto va attivato una volta su questo computer. Serve la tua password."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Controlla con: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "Per configurare un volto, l'immagine della fotocamera deve comparire sullo schermo. Esegui questo comando nella tua sessione Plasma."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "La finestra di configurazione è aperta. Segui i passaggi lì."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "Il volto è configurato."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "Lo sblocco con il volto è ancora disattivato. Attivalo con [1] o `%s enable`."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "Nessun volto aggiunto."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Per prima cosa, configura il tuo volto."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "Impossibile salvare l'impostazione."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "Impossibile avviare l'agente della schermata di blocco."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "Nessuna sessione utente di systemd, quindi l'agente della schermata di blocco non è stato avviato."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "Lo sblocco con il volto è attivo. Blocca lo schermo e guardalo per provarlo."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "Funziona anche con sudo e le richieste di amministratore. Vedi Impostazioni."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "Lo sblocco con il volto è disattivato. I tuoi volti restano salvati; eliminali in Volti."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Nessun volto ancora configurato."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "attivo"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "disattivo"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "%s campioni, %s appresi"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "Quale volto? Vedi `%s faces` per gli ID."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Eliminato."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Uso: plasma-face-unlock [comando]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Uso: face-unlock [comando]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Comandi:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Attiva lo sblocco con il volto"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Disattivalo (i volti restano salvati)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Aggiungi un volto"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "Elenca i volti"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Elimina un volto"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "Guarda la fotocamera e vedi cosa vede"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Mostra cosa è attivo"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Mostra questo aiuto"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "Mostra la versione"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Senza comando viene mostrato un menu interattivo."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "Esegui questo con il tuo utente, non come root. Chiede la password quando serve."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Comando sconosciuto: %s"
@@ -795,19 +795,19 @@ msgstr "Riproduci una volta le animazioni della bolla."
msgid "Bubble style for the demo: full or minimal."
msgstr "Stile della bolla per la demo: full o minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Guarda la fotocamera per sbloccare."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Sbatti le palpebre o gira un po' la testa."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "Lo sblocco con il volto è in pausa dopo troppi tentativi. Usa la tua password."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Volto non riconosciuto."
+44 -44
View File
@@ -1,11 +1,11 @@
# Japanese translation for plasma-face-unlock.
# Japanese translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:46+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "顔認証のサービスを、このコンピューターで一度オンにする必要があります。パスワードが必要です。"
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "確認するには: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "顔を登録するには、カメラの映像を画面に表示する必要があります。Plasma セッションの中で実行してください。"
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "登録ウィンドウが開いています。そちらの手順に従ってください。"
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "顔を登録しました。"
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "顔認証はまだオフです。[1] または `%s enable` でオンにしてください。"
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "顔は追加されませんでした。"
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "まず顔を登録してください。"
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "設定を保存できませんでした。"
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "ロック画面エージェントを起動できませんでした。"
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "systemd のユーザーセッションがないため、ロック画面エージェントは起動されませんでした。"
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "顔認証はオンです。画面をロックして、画面を見て試してください。"
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "sudo や管理者の認証にも使えます。「設定」を参照してください。"
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "顔認証はオフです。顔データは残っています。「顔」から削除できます。"
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "まだ顔が登録されていません。"
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "オン"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "オフ"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "サンプル %s 件、学習 %s 件"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "どの顔ですか? ID は `%s faces` で確認できます。"
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "削除しました。"
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "使い方: plasma-face-unlock [コマンド]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "使い方: face-unlock [コマンド]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "コマンド:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "顔認証をオンにする"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "オフにする(顔データは残ります)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "顔を追加"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "顔の一覧"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "顔を削除"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "カメラを見て、カメラに映るものを確認する"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "オンになっている機能を表示"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "このヘルプを表示"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "バージョンを表示"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "コマンドなしで起動すると、対話式メニューが表示されます。"
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "root ではなく、自分のユーザーで実行してください。必要なときはパスワードを尋ねます。"
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "不明なコマンド: %s"
@@ -795,19 +795,19 @@ msgstr "バブルのアニメーションを一度再生します。"
msgid "Bubble style for the demo: full or minimal."
msgstr "デモのバブルスタイル: full または minimal。"
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "カメラを見てロックを解除してください。"
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "まばたきするか、少し顔を動かしてください。"
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "試行回数が多すぎるため、顔認証を一時停止しています。パスワードを使ってください。"
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "顔を認識できませんでした。"
+44 -44
View File
@@ -1,11 +1,11 @@
# Korean translation for plasma-face-unlock.
# Korean translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:46+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "얼굴 잠금 해제 서비스를 이 컴퓨터에서 한 번 켜야 합니다. 이때 비밀번호가 필요합니다."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "확인 방법: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "얼굴을 등록하려면 카메라 화면이 보여야 합니다. Plasma 세션 안에서 실행하세요."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "등록 창이 열려 있습니다. 창의 안내를 따르세요."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "얼굴이 등록되었습니다."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "얼굴 잠금 해제가 아직 꺼져 있습니다. [1] 또는 `%s enable`로 켜세요."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "얼굴이 추가되지 않았습니다."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "먼저 얼굴을 등록하세요."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "설정을 저장하지 못했습니다."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "잠금 화면 에이전트를 시작하지 못했습니다."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "systemd 사용자 세션이 없어 잠금 화면 에이전트를 시작하지 않았습니다."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "얼굴 잠금 해제가 켜졌습니다. 화면을 잠그고 화면을 바라보며 사용해 보세요."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "sudo와 관리자 인증 창에서도 쓸 수 있습니다. 설정을 참고하세요."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "얼굴 잠금 해제가 꺼졌습니다. 얼굴 데이터는 남아 있으며, 얼굴 메뉴에서 삭제할 수 있습니다."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "아직 등록된 얼굴이 없습니다."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "켜짐"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "꺼짐"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "샘플 %s개, 학습 %s개"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "어떤 얼굴인가요? ID는 `%s faces`에서 확인하세요."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "삭제했습니다."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "사용법: plasma-face-unlock [명령]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "사용법: face-unlock [명령]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "명령:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "얼굴 잠금 해제 켜기"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "끄기 (얼굴 데이터는 유지)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "얼굴 추가"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "얼굴 목록"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "얼굴 삭제"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "카메라를 보고 카메라에 보이는 것을 확인"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "켜져 있는 기능 보기"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "이 도움말 보기"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "버전 보기"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "명령 없이 실행하면 대화형 메뉴가 나옵니다."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "root가 아닌 본인 사용자로 실행하세요. 필요하면 비밀번호를 묻습니다."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "알 수 없는 명령: %s"
@@ -795,19 +795,19 @@ msgstr "버블 애니메이션을 한 번 재생합니다."
msgid "Bubble style for the demo: full or minimal."
msgstr "데모용 버블 스타일: full 또는 minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "잠금을 해제하려면 카메라를 보세요."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "눈을 깜빡이거나 고개를 살짝 돌리세요."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "시도가 너무 많아 얼굴 잠금 해제가 일시 중지되었습니다. 비밀번호를 사용하세요."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "얼굴을 인식하지 못했습니다."
+44 -44
View File
@@ -1,11 +1,11 @@
# Dutch translation for plasma-face-unlock.
# Dutch translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:46+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "De dienst voor gezichtsontgrendeling moet één keer op deze computer worden ingeschakeld. Daarvoor is je wachtwoord nodig."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Controleer met: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "Voor het instellen van een gezicht moet het camerabeeld op het scherm staan. Voer dit uit in je Plasma-sessie."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "Het instelvenster is open. Volg daar de stappen."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "Het gezicht is ingesteld."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "Gezichtsontgrendeling staat nog uit. Zet het aan met [1] of `%s enable`."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "Er is geen gezicht toegevoegd."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Stel eerst je gezicht in."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "Kon de instelling niet opslaan."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "Kon de agent voor het vergrendelscherm niet starten."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "Geen systemd-gebruikerssessie, dus de agent voor het vergrendelscherm is niet gestart."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "Gezichtsontgrendeling staat aan. Vergrendel het scherm en kijk ernaar om het te proberen."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "Het werkt ook voor sudo en beheerdersvragen. Zie Instellingen."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "Gezichtsontgrendeling staat uit. Je gezichten blijven bewaard; verwijder ze onder Gezichten."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Er is nog geen gezicht ingesteld."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "aan"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "uit"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "%s opnames, %s bijgeleerd"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "Welk gezicht? Zie `%s faces` voor de ID's."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Verwijderd."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Gebruik: plasma-face-unlock [opdracht]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Gebruik: face-unlock [opdracht]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Opdrachten:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Gezichtsontgrendeling aanzetten"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Uitzetten (gezichten blijven bewaard)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Een gezicht toevoegen"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "De gezichten tonen"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Een gezicht verwijderen"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "In de camera kijken en zien wat hij ziet"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Tonen wat er aan staat"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Deze hulp tonen"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "De versie tonen"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Zonder opdracht verschijnt een interactief menu."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "Voer dit uit als je eigen gebruiker, niet als root. Het vraagt om je wachtwoord als dat nodig is."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Onbekende opdracht: %s"
@@ -795,19 +795,19 @@ msgstr "De animaties van de bubbel één keer afspelen."
msgid "Bubble style for the demo: full or minimal."
msgstr "Bubbelstijl voor de demo: full of minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Kijk in de camera om te ontgrendelen."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Knipper, of draai je hoofd een beetje."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "Gezichtsontgrendeling is gepauzeerd na te veel pogingen. Gebruik je wachtwoord."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Gezicht niet herkend."
+44 -44
View File
@@ -1,11 +1,11 @@
# Polish translation for plasma-face-unlock.
# Polish translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:46+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "Usługę odblokowywania twarzą trzeba raz włączyć na tym komputerze. Potrzebne jest do tego twoje hasło."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Sprawdź poleceniem: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "Do skonfigurowania twarzy obraz z kamery musi być widoczny na ekranie. Uruchom to w swojej sesji Plasmy."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "Okno konfiguracji jest otwarte. Postępuj zgodnie z nim."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "Twarz jest skonfigurowana."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "Odblokowywanie twarzą jest nadal wyłączone. Włącz je przez [1] lub `%s enable`."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "Nie dodano żadnej twarzy."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Najpierw skonfiguruj swoją twarz."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "Nie udało się zapisać ustawienia."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "Nie udało się uruchomić agenta ekranu blokady."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "Brak sesji użytkownika systemd, więc agent ekranu blokady nie został uruchomiony."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "Odblokowywanie twarzą jest włączone. Zablokuj ekran i spójrz na niego, aby wypróbować."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "Działa też dla sudo i okien administratora. Zobacz Ustawienia."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "Odblokowywanie twarzą jest wyłączone. Twoje twarze zostają zachowane; usuniesz je w Twarzach."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Nie skonfigurowano jeszcze żadnej twarzy."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "wł."
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "wył."
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "próbki: %s, wyuczone: %s"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "Która twarz? Identyfikatory pokazuje `%s faces`."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Usunięto."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Użycie: plasma-face-unlock [polecenie]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Użycie: face-unlock [polecenie]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Polecenia:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Włącz odblokowywanie twarzą"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Wyłącz je (twarze zostają zachowane)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Dodaj twarz"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "Pokaż listę twarzy"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Usuń twarz"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "Spójrz w kamerę i zobacz, co widzi"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Pokaż, co jest włączone"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Pokaż tę pomoc"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "Pokaż wersję"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Bez polecenia wyświetla się interaktywne menu."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "Uruchom to jako swój użytkownik, nie jako root. Hasło zostanie zapytane, gdy będzie potrzebne."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Nieznane polecenie: %s"
@@ -795,19 +795,19 @@ msgstr "Odtwórz raz animacje dymka."
msgid "Bubble style for the demo: full or minimal."
msgstr "Styl dymka w demo: full lub minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Spójrz w kamerę, aby odblokować."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Mrugnij albo lekko obróć głowę."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "Odblokowywanie twarzą jest wstrzymane po zbyt wielu próbach. Użyj hasła."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Nie rozpoznano twarzy."
+44 -44
View File
@@ -1,11 +1,11 @@
# Brazilian Portuguese translation for plasma-face-unlock.
# Brazilian Portuguese translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:55+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "O serviço de desbloqueio facial precisa ser ativado uma vez neste computador. Para isso, é preciso a sua senha."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Verifique com: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "Para configurar um rosto, a imagem da câmera precisa aparecer na tela. Execute isto dentro da sua sessão do Plasma."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "A janela de configuração está aberta. Siga os passos nela."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "O rosto está configurado."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "O desbloqueio facial ainda está desativado. Ative com [1] ou `%s enable`."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "Nenhum rosto foi adicionado."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Primeiro, configure o seu rosto."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "Não foi possível salvar a configuração."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "Não foi possível iniciar o agente da tela de bloqueio."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "Sem sessão de usuário do systemd, então o agente da tela de bloqueio não foi iniciado."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "O desbloqueio facial está ativado. Bloqueie a tela e olhe para ela para testar."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "Ele também funciona com sudo e pedidos de administrador. Veja Configurações."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "O desbloqueio facial está desativado. Seus rostos são mantidos; apague-os em Rostos."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Nenhum rosto configurado ainda."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "ativado"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "desativado"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "%s amostras, %s aprendidas"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "Qual rosto? Veja `%s faces` para os IDs."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Apagado."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Uso: plasma-face-unlock [comando]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Uso: face-unlock [comando]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Comandos:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Ativar o desbloqueio facial"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Desativar (os rostos são mantidos)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Adicionar um rosto"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "Listar os rostos"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Apagar um rosto"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "Olhar para a câmera e ver o que ela vê"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Mostrar o que está ativado"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Mostrar esta ajuda"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "Mostrar a versão"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Sem comando, um menu interativo é exibido."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "Execute isto com o seu próprio usuário, não como root. Ele pede a sua senha quando precisa."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Comando desconhecido: %s"
@@ -795,19 +795,19 @@ msgstr "Reproduzir as animações da bolha uma vez."
msgid "Bubble style for the demo: full or minimal."
msgstr "Estilo da bolha para a demonstração: full ou minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Olhe para a câmera para desbloquear."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Pisque ou vire um pouco a cabeça."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "O desbloqueio facial está pausado após tentativas demais. Use a sua senha."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Rosto não reconhecido."
+44 -44
View File
@@ -1,11 +1,11 @@
# Russian translation for plasma-face-unlock.
# Russian translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:46+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "Службу разблокировки по лицу нужно один раз включить на этом компьютере. Для этого нужен ваш пароль."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Проверьте командой: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "Для настройки лица изображение с камеры должно быть видно на экране. Запустите это в своём сеансе Plasma."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "Окно настройки открыто. Следуйте шагам в нём."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "Лицо настроено."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "Разблокировка по лицу всё ещё выключена. Включите её через [1] или `%s enable`."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "Лицо не добавлено."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Сначала настройте своё лицо."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "Не удалось сохранить настройку."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "Не удалось запустить агент экрана блокировки."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "Нет пользовательского сеанса systemd, поэтому агент экрана блокировки не запущен."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "Разблокировка по лицу включена. Заблокируйте экран и посмотрите на него, чтобы попробовать."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "Она работает и для sudo, и для запросов администратора. См. Настройки."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "Разблокировка по лицу выключена. Ваши лица сохранены; удалить их можно в разделе Лица."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Ещё не настроено ни одного лица."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "вкл."
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "выкл."
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "образцов: %s, выучено: %s"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "Какое лицо? Номера показывает `%s faces`."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Удалено."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Использование: plasma-face-unlock [команда]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Использование: face-unlock [команда]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Команды:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Включить разблокировку по лицу"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Выключить её (лица сохраняются)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Добавить лицо"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "Показать список лиц"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Удалить лицо"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "Посмотреть в камеру и увидеть, что она видит"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Показать, что включено"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Показать эту справку"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "Показать версию"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Без команды открывается интерактивное меню."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "Запускайте это от своего пользователя, не от root. Пароль будет запрошен, когда он понадобится."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Неизвестная команда: %s"
@@ -795,19 +795,19 @@ msgstr "Один раз проиграть анимации пузыря."
msgid "Bubble style for the demo: full or minimal."
msgstr "Стиль пузыря для демо: full или minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Посмотрите в камеру, чтобы разблокировать."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Моргните или слегка поверните голову."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "Разблокировка по лицу приостановлена после слишком многих попыток. Введите пароль."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Лицо не распознано."
+44 -44
View File
@@ -1,11 +1,11 @@
# Turkish translation for plasma-face-unlock.
# Turkish translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:46+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "Yüzle kilit açma hizmetinin bu bilgisayarda bir kez açılması gerekiyor. Bunun için parolanız gerekli."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Şununla kontrol edin: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "Yüz kurmak için kamera görüntüsünün ekranda görünmesi gerekir. Bunu Plasma oturumunuzda çalıştırın."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "Kurulum penceresi açık. Oradaki adımları izleyin."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "Yüz kuruldu."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "Yüzle kilit açma hâlâ kapalı. [1] ya da `%s enable` ile açın."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "Hiç yüz eklenmedi."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Önce yüzünüzü kurun."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "Ayar kaydedilemedi."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "Kilit ekranı aracısı başlatılamadı."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "systemd kullanıcı oturumu yok, bu yüzden kilit ekranı aracısı başlatılmadı."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "Yüzle kilit açma açık. Denemek için ekranı kilitleyin ve ona bakın."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "sudo ve yönetici istemlerinde de çalışabilir. Ayarlar'a bakın."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "Yüzle kilit açma kapalı. Yüzleriniz saklanıyor; Yüzler bölümünden silebilirsiniz."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Henüz hiç yüz kurulmadı."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "açık"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "kapalı"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "%s örnek, %s öğrenilen"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "Hangi yüz? Kimlikler için `%s faces` komutuna bakın."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Silindi."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Kullanım: plasma-face-unlock [komut]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Kullanım: face-unlock [komut]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Komutlar:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Yüzle kilit açmayı aç"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Kapat (yüzler saklanır)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Yüz ekle"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "Yüzleri listele"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Bir yüzü sil"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "Kameraya bak ve onun ne gördüğünü gör"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Neyin açık olduğunu göster"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Bu yardımı göster"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "Sürümü göster"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Komut verilmezse etkileşimli bir menü gösterilir."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "Bunu root olarak değil, kendi kullanıcınızla çalıştırın. Gerektiğinde parolanızı sorar."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Bilinmeyen komut: %s"
@@ -795,19 +795,19 @@ msgstr "Baloncuğun animasyonlarını bir kez oynat."
msgid "Bubble style for the demo: full or minimal."
msgstr "Demo için baloncuk stili: full ya da minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Kilidi açmak için kameraya bakın."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Göz kırpın ya da başınızı biraz çevirin."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "Çok fazla denemeden sonra yüzle kilit açma duraklatıldı. Parolanızı kullanın."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Yüz tanınmadı."
+44 -44
View File
@@ -1,11 +1,11 @@
# Ukrainian translation for plasma-face-unlock.
# Ukrainian translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:46+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "Службу розблокування обличчям треба один раз увімкнути на цьому комп'ютері. Для цього потрібен ваш пароль."
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "Перевірте командою: systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "Для налаштування обличчя зображення з камери має бути видно на екрані. Запустіть це у своєму сеансі Plasma."
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "Вікно налаштування відкрито. Виконайте кроки в ньому."
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "Обличчя налаштовано."
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "Розблокування обличчям досі вимкнено. Увімкніть його через [1] або `%s enable`."
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "Обличчя не додано."
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "Спершу налаштуйте своє обличчя."
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "Не вдалося зберегти налаштування."
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "Не вдалося запустити агент екрана блокування."
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "Немає користувацького сеансу systemd, тому агент екрана блокування не запущено."
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "Розблокування обличчям увімкнено. Заблокуйте екран і подивіться на нього, щоб спробувати."
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "Воно працює і для sudo, і для запитів адміністратора. Див. Налаштування."
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "Розблокування обличчям вимкнено. Ваші обличчя збережено; видалити їх можна в розділі Обличчя."
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "Ще не налаштовано жодного обличчя."
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "увімк."
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "вимк."
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "зразків: %s, вивчено: %s"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "Яке обличчя? Номери показує `%s faces`."
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "Видалено."
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "Використання: plasma-face-unlock [команда]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "Використання: face-unlock [команда]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "Команди:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "Увімкнути розблокування обличчям"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "Вимкнути його (обличчя зберігаються)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "Додати обличчя"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "Показати список облич"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "Видалити обличчя"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "Подивитися в камеру і побачити, що вона бачить"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "Показати, що увімкнено"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "Показати цю довідку"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "Показати версію"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "Без команди відкривається інтерактивне меню."
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "Запускайте це від свого користувача, не від root. Пароль буде запитано, коли він знадобиться."
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "Невідома команда: %s"
@@ -795,19 +795,19 @@ msgstr "Один раз програти анімації бульбашки."
msgid "Bubble style for the demo: full or minimal."
msgstr "Стиль бульбашки для демо: full або minimal."
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "Подивіться в камеру, щоб розблокувати."
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "Кліпніть або трохи поверніть голову."
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "Розблокування обличчям призупинено після надто багатьох спроб. Введіть пароль."
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "Обличчя не розпізнано."
+13 -13
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
#
# Collects every translatable string into po/plasma-face-unlock.pot and brings
# Collects every translatable string into po/face-unlock.pot and brings
# the translations up to date with it. One catalog serves all four parts:
# the shell code (pfu_msg), the agent (i18n in C++ and QML) and the PAM module
# the shell code (fu_msg), the agent (i18n in C++ and QML) and the PAM module
# (dgettext), so a word is translated once wherever it shows up.
#
# The settings labels and headings live in an array in menu.sh and reach
@@ -18,23 +18,23 @@ trap 'rm -rf -- "$tmp"' EXIT
version="$(make -s version)"
# The labels, as calls xgettext understands, pointing back at menu.sh.
awk -F'|' '/^\t"(user|sys|pam)\|/ { sub(/"$/, "", $5); print "pfu_msg \"" $5 "\"" }
/^\t"group\|/ { sub(/"$/, "", $2); print "pfu_msg \"" $2 "\"" }' src/lib/menu.sh > "$tmp/settings.sh"
awk -F'|' '/^\t"(user|sys|pam)\|/ { sub(/"$/, "", $5); print "fu_msg \"" $5 "\"" }
/^\t"group\|/ { sub(/"$/, "", $2); print "fu_msg \"" $2 "\"" }' src/lib/menu.sh > "$tmp/settings.sh"
common=(--from-code=UTF-8 --add-comments=TRANSLATORS --package-name=plasma-face-unlock --package-version="$version"
--msgid-bugs-address=https://github.com/LoonixTools/plasma-face-unlock/issues)
common=(--from-code=UTF-8 --add-comments=TRANSLATORS --package-name=face-unlock --package-version="$version"
--msgid-bugs-address=https://github.com/LoonixTools/face-unlock/issues)
xgettext "${common[@]}" -L Shell -k --keyword=pfu_msg --keyword=pfu_msg_into:2 --keyword=pfu_msg_in:2 \
-o "$tmp/shell.pot" src/plasma-face-unlock src/lib/*.sh "$tmp/settings.sh"
xgettext "${common[@]}" -L Shell -k --keyword=fu_msg --keyword=fu_msg_into:2 --keyword=fu_msg_in:2 \
-o "$tmp/shell.pot" src/face-unlock src/lib/*.sh "$tmp/settings.sh"
sed -i "s|$tmp/settings.sh:[0-9]*|src/lib/menu.sh|g" "$tmp/shell.pot"
xgettext "${common[@]}" -L C++ --keyword=i18n --keyword=_ -o "$tmp/native.pot" src/agent/*.cpp src/pam/*.c
xgettext "${common[@]}" -L JavaScript --keyword=i18n -o "$tmp/qml.pot" src/agent/qml/*.qml
msgcat --use-first -o po/plasma-face-unlock.pot "$tmp/shell.pot" "$tmp/native.pot" "$tmp/qml.pot"
sed -i -e '1i # Translation template for plasma-face-unlock.\n# Copyright (C) 2026 Felitendo\n# This file is distributed under the same license as plasma-face-unlock.' -e '1,4d' \
po/plasma-face-unlock.pot
msgcat --use-first -o po/face-unlock.pot "$tmp/shell.pot" "$tmp/native.pot" "$tmp/qml.pot"
sed -i -e '1i # Translation template for face-unlock.\n# Copyright (C) 2026 Felitendo\n# This file is distributed under the same license as face-unlock.' -e '1,4d' \
po/face-unlock.pot
for po in po/*.po; do
msgmerge --quiet --update --backup=none --no-fuzzy-matching "$po" po/plasma-face-unlock.pot
msgmerge --quiet --update --backup=none --no-fuzzy-matching "$po" po/face-unlock.pot
done
echo "po/plasma-face-unlock.pot: $(grep -c '^msgid' po/plasma-face-unlock.pot) strings"
echo "po/face-unlock.pot: $(grep -c '^msgid' po/face-unlock.pot) strings"
+44 -44
View File
@@ -1,11 +1,11 @@
# Chinese (simplified) translation for plasma-face-unlock.
# Chinese (simplified) translation for face-unlock.
# Copyright (C) 2026 Felitendo
# This file is distributed under the same license as plasma-face-unlock.
# This file is distributed under the same license as face-unlock.
#
msgid ""
msgstr ""
"Project-Id-Version: plasma-face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/plasma-face-unlock/issues\n"
"Project-Id-Version: face-unlock 1.0.1\n"
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/face-unlock/issues\n"
"POT-Creation-Date: 2026-09-24 10:46+0200\n"
"PO-Revision-Date: 2026-09-24 12:00+0200\n"
"Last-Translator: Felitendo\n"
@@ -15,145 +15,145 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/plasma-face-unlock:50
#: src/face-unlock:50
msgid "Face unlock's service has to be switched on once for this computer. That needs your password."
msgstr "人脸解锁服务需要在这台电脑上开启一次。这需要你的密码。"
#: src/plasma-face-unlock:57
#: src/face-unlock:57
#, sh-printf-format
msgid "Check it with: systemctl status %s"
msgstr "用以下命令检查:systemctl status %s"
#: src/plasma-face-unlock:67
#: src/face-unlock:67
msgid "Setting up a face needs the camera picture on screen. Run this inside your Plasma session."
msgstr "设置人脸需要在屏幕上显示摄像头画面。请在你的 Plasma 会话中运行。"
#: src/plasma-face-unlock:71
#: src/face-unlock:71
msgid "The setup window is open. Follow it there."
msgstr "设置窗口已打开。请按窗口中的步骤操作。"
#: src/plasma-face-unlock:80
#: src/face-unlock:80
msgid "The face is set up."
msgstr "人脸已设置。"
#: src/plasma-face-unlock:83
#: src/face-unlock:83
#, sh-printf-format
msgid "Face unlock is still off. Turn it on with [1] or `%s enable`."
msgstr "人脸解锁仍处于关闭状态。用 [1] 或 `%s enable` 开启。"
#: src/plasma-face-unlock:87
#: src/face-unlock:87
msgid "No face was added."
msgstr "没有添加人脸。"
#: src/plasma-face-unlock:96
#: src/face-unlock:96
msgid "First, set up your face."
msgstr "请先设置你的人脸。"
#: src/plasma-face-unlock:100 src/plasma-face-unlock:120 src/lib/menu.sh:434
#: src/face-unlock:100 src/face-unlock:120 src/lib/menu.sh:434
#: src/lib/menu.sh:438 src/lib/menu.sh:444 src/lib/menu.sh:446
msgid "Could not save the setting."
msgstr "无法保存设置。"
#: src/plasma-face-unlock:104
#: src/face-unlock:104
msgid "Could not start the lock screen agent."
msgstr "无法启动锁屏代理。"
#: src/plasma-face-unlock:106
#: src/face-unlock:106
msgid "No systemd user session, so the lock screen agent was not started."
msgstr "没有 systemd 用户会话,因此未启动锁屏代理。"
#: src/plasma-face-unlock:113
#: src/face-unlock:113
msgid "Face unlock is on. Lock the screen and look at it to try."
msgstr "人脸解锁已开启。锁定屏幕并看着它来试试。"
#: src/plasma-face-unlock:115
#: src/face-unlock:115
msgid "It can do sudo and admin prompts too. See Settings."
msgstr "它也可以用于 sudo 和管理员授权。见“设置”。"
#: src/plasma-face-unlock:130
#: src/face-unlock:130
msgid "Face unlock is off. Your faces are kept; delete them under Faces."
msgstr "人脸解锁已关闭。你的人脸会保留;可在“人脸”中删除。"
#: src/plasma-face-unlock:144 src/lib/daemon.sh:109
#: src/face-unlock:144 src/lib/daemon.sh:109
msgid "No face is set up yet."
msgstr "尚未设置任何人脸。"
#: src/plasma-face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
#: src/face-unlock:148 src/lib/menu.sh:180 src/lib/menu.sh:594
msgid "on"
msgstr "开"
#: src/plasma-face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/face-unlock:148 src/lib/menu.sh:182 src/lib/menu.sh:192
#: src/lib/menu.sh:595
msgid "off"
msgstr "关"
#: src/plasma-face-unlock:150 src/lib/menu.sh:612
#: src/face-unlock:150 src/lib/menu.sh:612
#, sh-printf-format
msgid "%s samples, %s learned"
msgstr "%s 个样本,已学习 %s 个"
#: src/plasma-face-unlock:156
#: src/face-unlock:156
#, sh-printf-format
msgid "Which face? See `%s faces` for the ids."
msgstr "哪张人脸?用 `%s faces` 查看 ID。"
#: src/plasma-face-unlock:160
#: src/face-unlock:160
msgid "Deleted."
msgstr "已删除。"
#: src/plasma-face-unlock:171
msgid "Usage: plasma-face-unlock [command]"
msgstr "用法:plasma-face-unlock [命令]"
#: src/face-unlock:171
msgid "Usage: face-unlock [command]"
msgstr "用法:face-unlock [命令]"
#: src/plasma-face-unlock:173
#: src/face-unlock:173
msgid "Commands:"
msgstr "命令:"
#: src/plasma-face-unlock:174
#: src/face-unlock:174
msgid "Turn face unlock on"
msgstr "开启人脸解锁"
#: src/plasma-face-unlock:175
#: src/face-unlock:175
msgid "Turn it off (faces are kept)"
msgstr "关闭(保留人脸)"
#: src/plasma-face-unlock:176 src/lib/menu.sh:678
#: src/face-unlock:176 src/lib/menu.sh:678
msgid "Add a face"
msgstr "添加人脸"
#: src/plasma-face-unlock:177
#: src/face-unlock:177
msgid "List the faces"
msgstr "列出人脸"
#: src/plasma-face-unlock:178
#: src/face-unlock:178
msgid "Delete a face"
msgstr "删除人脸"
#: src/plasma-face-unlock:179
#: src/face-unlock:179
msgid "Look at the camera and see what it sees"
msgstr "看着摄像头,查看它看到的画面"
#: src/plasma-face-unlock:180
#: src/face-unlock:180
msgid "Show what is on"
msgstr "显示已开启的功能"
#: src/plasma-face-unlock:181
#: src/face-unlock:181
msgid "Show this help"
msgstr "显示此帮助"
#: src/plasma-face-unlock:182
#: src/face-unlock:182
msgid "Show the version"
msgstr "显示版本"
#: src/plasma-face-unlock:184
#: src/face-unlock:184
msgid "Without a command an interactive menu is shown."
msgstr "不带命令时显示交互式菜单。"
#: src/plasma-face-unlock:203
#: src/face-unlock:203
msgid "Run this as your own user, not as root. It asks for your password when it needs to."
msgstr "请以你自己的用户运行,不要用 root。需要时会询问你的密码。"
#: src/plasma-face-unlock:221 src/lib/system.sh:89
#: src/face-unlock:221 src/lib/system.sh:89
#, sh-printf-format
msgid "Unknown command: %s"
msgstr "未知命令:%s"
@@ -795,19 +795,19 @@ msgstr "播放一次气泡动画。"
msgid "Bubble style for the demo: full or minimal."
msgstr "演示用的气泡样式:full 或 minimal。"
#: src/pam/pam_plasma_face_unlock.c:311
#: src/pam/pam_face_unlock.c:311
msgid "Look at the camera to unlock."
msgstr "看着摄像头来解锁。"
#: src/pam/pam_plasma_face_unlock.c:314
#: src/pam/pam_face_unlock.c:314
msgid "Blink, or turn your head a little."
msgstr "眨眼,或稍微转一下头。"
#: src/pam/pam_plasma_face_unlock.c:330
#: src/pam/pam_face_unlock.c:330
msgid "Face unlock is paused after too many tries. Use your password."
msgstr "尝试次数过多,人脸解锁已暂停。请使用密码。"
#: src/pam/pam_plasma_face_unlock.c:334
#: src/pam/pam_face_unlock.c:334
msgid "Face not recognised."
msgstr "未识别出人脸。"
@@ -1,6 +1,6 @@
[Desktop Entry]
Type=Application
Name=Plasma Face Unlock
Name=Face Unlock
Name[de]=Plasma-Gesichtsentsperrung
Name[fr]=Déverrouillage facial Plasma
Name[es]=Desbloqueo facial de Plasma
@@ -28,8 +28,8 @@ Comment[tr]=Kilit ekranını, sudo'yu ve yönetici istemlerini yüzünüzle aça
Comment[zh_CN]=用你的脸解锁锁屏、sudo 和管理员授权
Comment[ja]=顔でロック画面、sudo、管理者の認証を解除します
Comment[ko]=얼굴로 잠금 화면, sudo, 관리자 인증 창의 잠금을 해제합니다
Exec=@LIBEXECDIR@/plasma-face-unlock-agent
Icon=plasma-face-unlock
Exec=@LIBEXECDIR@/face-unlock-agent
Icon=face-unlock
NoDisplay=true
OnlyShowIn=KDE;
# KWin only lets a program show above the lock screen when its desktop file
File renamed without changes.
@@ -4,13 +4,13 @@
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<vendor>LoonixTools</vendor>
<vendor_url>https://github.com/LoonixTools/plasma-face-unlock</vendor_url>
<icon_name>plasma-face-unlock</icon_name>
<vendor_url>https://github.com/LoonixTools/face-unlock</vendor_url>
<icon_name>face-unlock</icon_name>
<!-- A face is a way in, so adding, changing or deleting one takes the
password, the way a phone asks for its code before it sets up a face.
The daemon refuses to let a face answer this particular question. -->
<action id="io.github.loonixtools.plasma-face-unlock.manage">
<action id="io.github.loonixtools.face-unlock.manage">
<description>Change the faces that can unlock your account</description>
<description xml:lang="de">Gesichter ändern, die dein Konto entsperren können</description>
<description xml:lang="fr">Modifier les visages qui peuvent déverrouiller votre compte</description>
@@ -1,6 +1,6 @@
[Unit]
Description=Face unlock for KDE Plasma (lock screen and bubble)
Documentation=man:plasma-face-unlock(1)
Documentation=man:face-unlock(1)
PartOf=graphical-session.target
After=graphical-session.target
# Plasma on Wayland. The bubble is a layer-shell surface, and there is no such
@@ -8,7 +8,7 @@ After=graphical-session.target
ConditionEnvironment=WAYLAND_DISPLAY
[Service]
ExecStart=@LIBEXECDIR@/plasma-face-unlock-agent
ExecStart=@LIBEXECDIR@/face-unlock-agent
Restart=on-failure
RestartSec=3
Slice=session.slice
@@ -1,14 +1,14 @@
[Unit]
Description=Face unlock for KDE Plasma
Documentation=man:plasma-face-unlock(1)
Requires=plasma-face-unlockd.socket
After=plasma-face-unlockd.socket
Documentation=man:face-unlock(1)
Requires=face-unlockd.socket
After=face-unlockd.socket
[Service]
Type=simple
# Started by the socket, and gone again after a minute with nothing to do.
ExecStart=@LIBEXECDIR@/plasma-face-unlockd
StateDirectory=plasma-face-unlock
ExecStart=@LIBEXECDIR@/face-unlockd
StateDirectory=face-unlock
StateDirectoryMode=0700
UMask=0077
@@ -1,11 +1,11 @@
[Unit]
Description=Face unlock for KDE Plasma (socket)
Documentation=man:plasma-face-unlock(1)
Documentation=man:face-unlock(1)
[Socket]
# Everybody may connect. Who may ask for what is decided per request, by the
# daemon, from the credentials the kernel reports for the other end.
ListenStream=/run/plasma-face-unlock/socket
ListenStream=/run/face-unlock/socket
SocketMode=0666
DirectoryMode=0755
RemoveOnStop=yes
+1 -1
View File
@@ -48,7 +48,7 @@ AgentSocket::AgentSocket(QObject *parent)
QString AgentSocket::path()
{
return QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QStringLiteral("/plasma-face-unlock/agent.socket");
return QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QStringLiteral("/face-unlock/agent.socket");
}
bool AgentSocket::listen()
+1 -1
View File
@@ -2,7 +2,7 @@
//
// Where the daemon tells this session about scans it did not ask for: sudo in
// a terminal, an admin prompt, a test from the menu. The daemon connects to
// $XDG_RUNTIME_DIR/plasma-face-unlock/agent.socket when such a scan starts,
// $XDG_RUNTIME_DIR/face-unlock/agent.socket when such a scan starts,
// so neither side has to keep a connection open (and the daemon can exit when
// it is idle).
//
+2 -2
View File
@@ -74,13 +74,13 @@ void BubbleWindow::create()
// faint box with sharp corners around the bubble. An on-screen
// display only fades, which a clear window does not show.
layer->setScope(QStringLiteral("on-screen-display"));
#ifdef PFU_LAYERSHELL_HAS_SCREEN
#ifdef FU_LAYERSHELL_HAS_SCREEN
layer->setScreen(QGuiApplication::primaryScreen());
#endif
}
m_view->setInitialProperties({{QStringLiteral("bubble"), QVariant::fromValue(m_controller)}});
m_view->loadFromModule(QStringLiteral("PlasmaFaceUnlock"), QStringLiteral("Bubble"));
m_view->loadFromModule(QStringLiteral("FaceUnlock"), QStringLiteral("Bubble"));
if (m_view->status() == QQuickView::Error) {
for (const QQmlError &e : m_view->errors()) {
qWarning("%s", qPrintable(e.toString()));
+2 -2
View File
@@ -38,8 +38,8 @@ DaemonRequest::~DaemonRequest()
QString DaemonRequest::socketPath()
{
const QByteArray env = qgetenv("PFU_SOCKET");
return env.isEmpty() ? QStringLiteral(PFU_SOCKET) : QString::fromLocal8Bit(env);
const QByteArray env = qgetenv("FU_SOCKET");
return env.isEmpty() ? QStringLiteral(FU_SOCKET) : QString::fromLocal8Bit(env);
}
void DaemonRequest::send(const QJsonObject &message)
+6 -6
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
// plasma-face-unlock-agent: the part in the user's session.
// face-unlock-agent: the part in the user's session.
//
// (no arguments) stay in the background: unlock the lock screen by face,
// and show the bubble for every scan
@@ -39,7 +39,7 @@ int runEnroll(QGuiApplication &app, const QString &name)
QQmlApplicationEngine engine;
KLocalization::setupLocalizedContext(&engine);
engine.setInitialProperties({{QStringLiteral("controller"), QVariant::fromValue(&controller)}});
engine.loadFromModule(QStringLiteral("PlasmaFaceUnlock"), QStringLiteral("Enroll"));
engine.loadFromModule(QStringLiteral("FaceUnlock"), QStringLiteral("Enroll"));
if (engine.rootObjects().isEmpty()) {
return 2;
}
@@ -76,10 +76,10 @@ void scheduleDemo(BubbleController *bubble)
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
app.setApplicationName(QStringLiteral("plasma-face-unlock-agent"));
app.setApplicationVersion(QStringLiteral(PFU_VERSION));
app.setDesktopFileName(QStringLiteral("io.github.loonixtools.plasma-face-unlock-agent"));
KLocalizedString::setApplicationDomain(PFU_NAME);
app.setApplicationName(QStringLiteral("face-unlock-agent"));
app.setApplicationVersion(QStringLiteral(FU_VERSION));
app.setDesktopFileName(QStringLiteral("io.github.loonixtools.face-unlock-agent"));
KLocalizedString::setApplicationDomain(FU_NAME);
QCommandLineParser parser;
parser.setApplicationDescription(i18n("Face unlock for KDE Plasma"));
+1 -1
View File
@@ -6,7 +6,7 @@
import QtQuick
import QtQuick.Window
import PlasmaFaceUnlock
import FaceUnlock
Window {
id: window
+1 -1
View File
@@ -23,7 +23,7 @@ UserConfig::UserConfig(QObject *parent)
QString UserConfig::path()
{
return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/plasma-face-unlock/config");
return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/face-unlock/config");
}
void UserConfig::load()
+1 -1
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
// ~/.config/plasma-face-unlock/config: what this user wants from the agent.
// ~/.config/face-unlock/config: what this user wants from the agent.
// Written by the menu, read here, and read again whenever it changes.
#pragma once
+1 -1
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
// The system settings, /etc/plasma-face-unlock/config.
// The system settings, /etc/face-unlock/config.
//
// These are the ones that decide how hard it is to get in: which camera, how
// strict the match is, whether a photo is checked for. They belong to root
+1 -1
View File
@@ -2,7 +2,7 @@
//
// The face data.
//
// One file per user under /var/lib/plasma-face-unlock/users, named after the
// One file per user under /var/lib/face-unlock/users, named after the
// numeric user id so a rename cannot hand one person's faces to another. Only
// root can read or write the directory. There are no pictures in it: every
// sample is the 128 numbers the recognizer made of one frame, and the frame
+4 -4
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
// plasma-face-unlock-ctl: the shell code's way to the daemon.
// face-unlock-ctl: the shell code's way to the daemon.
//
// bash has no Unix sockets, so this sends one request and prints every
// message that comes back as one line of tab separated key=value pairs:
@@ -68,7 +68,7 @@ void printObject(const QJsonObject &o, const QString &event)
int usage()
{
std::fprintf(stderr,
"usage: plasma-face-unlock-ctl [--socket PATH] COMMAND\n"
"usage: face-unlock-ctl [--socket PATH] COMMAND\n"
" hello | status | cameras | list | watch | unlocked\n"
" verify [USER] [PURPOSE] | test\n"
" remove ID | rename ID NAME | enable ID | disable ID | clear\n");
@@ -81,12 +81,12 @@ int main(int argc, char **argv)
QCoreApplication app(argc, argv);
QStringList args = app.arguments().mid(1);
QString socketPath = QStringLiteral(PFU_SOCKET);
QString socketPath = QStringLiteral(FU_SOCKET);
if (args.size() >= 2 && args.first() == u"--socket") {
socketPath = args.at(1);
args = args.mid(2);
}
if (const QByteArray env = qgetenv("PFU_SOCKET"); !env.isEmpty()) {
if (const QByteArray env = qgetenv("FU_SOCKET"); !env.isEmpty()) {
socketPath = QString::fromLocal8Bit(env);
}
if (args.isEmpty()) {
+1 -1
View File
@@ -25,7 +25,7 @@ AgentLink::AgentLink(uid_t uid, QObject *parent)
: QObject(parent)
, m_uid(uid)
{
const QString dir = QStringLiteral("/run/user/%1/plasma-face-unlock").arg(uid);
const QString dir = QStringLiteral("/run/user/%1/face-unlock").arg(uid);
const QString path = dir + QStringLiteral("/agent.socket");
if (!ownedBy(dir, uid, false) || !ownedBy(path, uid, true)) {
// No agent in that session, or not one of this user's making.
+1 -1
View File
@@ -3,7 +3,7 @@
// Telling a user's session about a scan it did not start itself (sudo, an
// admin prompt, a test from the menu), so the bubble can show it.
//
// The agent listens on /run/user/UID/plasma-face-unlock/agent.socket. That is
// The agent listens on /run/user/UID/face-unlock/agent.socket. That is
// a place the user controls, so nothing is taken for granted: the socket has
// to belong to the user, and so does the process that answers on it, checked
// by the kernel before a single byte is written. What is written is only
+7 -7
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
// plasma-face-unlockd: the part that runs as root.
// face-unlockd: the part that runs as root.
//
// It owns the camera and the face data, and it is the only thing that does.
// Everything else (the lock screen agent, sudo, the setup window, the menu)
@@ -66,8 +66,8 @@ int main(int argc, char **argv)
umask(077);
QCoreApplication app(argc, argv);
app.setApplicationName(QStringLiteral("plasma-face-unlockd"));
app.setApplicationVersion(QStringLiteral(PFU_VERSION));
app.setApplicationName(QStringLiteral("face-unlockd"));
app.setApplicationVersion(QStringLiteral(FU_VERSION));
qSetMessagePattern(QStringLiteral("%{if-warning}warning: %{endif}%{if-critical}error: %{endif}%{message}"));
QCommandLineParser parser;
@@ -75,12 +75,12 @@ int main(int argc, char **argv)
parser.addHelpOption();
parser.addVersionOption();
const QCommandLineOption socketOpt(QStringLiteral("socket"), QStringLiteral("Listen here instead of the system socket."), QStringLiteral("path"),
QStringLiteral(PFU_SOCKET));
QStringLiteral(FU_SOCKET));
const QCommandLineOption stateOpt(QStringLiteral("state-dir"), QStringLiteral("Where face data is kept."), QStringLiteral("dir"),
QStringLiteral(PFU_STATEDIR));
const QCommandLineOption configOpt(QStringLiteral("config"), QStringLiteral("The settings file."), QStringLiteral("file"), QStringLiteral(PFU_CONFIG));
QStringLiteral(FU_STATEDIR));
const QCommandLineOption configOpt(QStringLiteral("config"), QStringLiteral("The settings file."), QStringLiteral("file"), QStringLiteral(FU_CONFIG));
const QCommandLineOption modelOpt(QStringLiteral("models"), QStringLiteral("Where the networks are."), QStringLiteral("dir"),
QStringLiteral(PFU_MODELDIR));
QStringLiteral(FU_MODELDIR));
const QCommandLineOption idleOpt(QStringLiteral("idle-exit"), QStringLiteral("Exit after this many idle seconds (0: never)."), QStringLiteral("seconds"));
parser.addOptions({socketOpt, stateOpt, configOpt, modelOpt, idleOpt});
parser.process(app);
+1 -1
View File
@@ -17,7 +17,7 @@
namespace Polkit
{
inline constexpr char ManageAction[] = "io.github.loonixtools.plasma-face-unlock.manage";
inline constexpr char ManageAction[] = "io.github.loonixtools.face-unlock.manage";
// Calls done(true) when the process may go ahead. Interactive: this can take
// as long as it takes somebody to type a password.
+2 -2
View File
@@ -282,7 +282,7 @@ void Server::onRequest(Client *client, const QJsonObject &request)
client->role = cmd;
if (cmd == u"hello") {
reply(client, {{QStringLiteral("event"), QStringLiteral("result")}, {QStringLiteral("ok"), true}, {QStringLiteral("version"), QStringLiteral(PFU_VERSION)}});
reply(client, {{QStringLiteral("event"), QStringLiteral("result")}, {QStringLiteral("ok"), true}, {QStringLiteral("version"), QStringLiteral(FU_VERSION)}});
} else if (cmd == u"status") {
handleStatus(client);
} else if (cmd == u"cameras") {
@@ -344,7 +344,7 @@ void Server::handleStatus(Client *client)
reply(client,
{{QStringLiteral("event"), QStringLiteral("result")},
{QStringLiteral("ok"), true},
{QStringLiteral("version"), QStringLiteral(PFU_VERSION)},
{QStringLiteral("version"), QStringLiteral(FU_VERSION)},
{QStringLiteral("user"), System::nameOf(uid)},
{QStringLiteral("faces"), enabled},
{QStringLiteral("identities"), int(faces.size())},
+229
View File
@@ -0,0 +1,229 @@
#!/usr/bin/env bash
#
# face-unlock: face unlock for KDE Plasma
#
# Look at the screen and it unlocks, the way a phone does. The lock screen,
# sudo in a terminal and the admin password prompts can all take a face
# instead of a password, with a check that it is a face and not a photo of one.
#
# This is the front end: the menu and the commands. The camera, the face data
# and the decision are the daemon's (face-unlockd, running as root),
# the lock screen and the bubble at the top of the screen are the agent's
# (face-unlock-agent, in the session), and sudo and polkit reach the
# daemon through a PAM module. See the man page for how the pieces fit.
#
# Copyright (C) 2026 Felitendo
# SPDX-License-Identifier: GPL-3.0-or-later
set -uo pipefail
FU_SELF="$(readlink -f "${BASH_SOURCE[0]}")"
# Run as root (through sudo from the menu), only what was installed counts.
# An environment that points the libraries somewhere else is ignored then.
if [[ $EUID -eq 0 ]]; then
unset FU_LIBDIR FU_LIBEXECDIR FU_LOCALEDIR FU_PAMDIR FU_CTL FU_AGENT FU_PAM_MODULE FU_SYSCONFIG \
FU_PAM_ETC_DIR FU_PAM_VENDOR_DIRS
fi
FU_LIBDIR="${FU_LIBDIR:-@LIBDIR@}"
for _mod in common config daemon pam system menu; do
# shellcheck source=/dev/null
if ! source "$FU_LIBDIR/$_mod.sh"; then
printf 'face-unlock: cannot load %s/%s.sh\n' "$FU_LIBDIR" "$_mod" >&2
exit 14
fi
done
unset _mod
# ---------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------
# The daemon is started by its socket. Enabling the socket is the one thing
# that needs root once per machine.
fu_ensure_service() {
fu_status_load && return 0
if ! fu_socket_enabled; then
fu_say " $(fu_msg "Face unlock's service has to be switched on once for this computer. That needs your password.")"
fu_root socket-enable || return 1
sleep 0.3
fi
fu_status_load && return 0
fu_bad "$(fu_reason_text unreachable)"
fu_note "$(fu_msg "Check it with: systemctl status %s" "$FU_UNIT_SOCKET")"
return 1
}
fu_do_setup() {
local name="${1:-}" rc
fu_ensure_service || return 1
if [[ -z ${WAYLAND_DISPLAY:-} && -z ${DISPLAY:-} ]]; then
fu_bad "$(fu_msg "Setting up a face needs the camera picture on screen. Run this inside your Plasma session.")"
return 1
fi
fu_say " $(fu_msg "The setup window is open. Follow it there.")"
if [[ -n $name ]]; then
"$FU_AGENT" --enroll --name "$name" > /dev/null 2>&1
else
"$FU_AGENT" --enroll > /dev/null 2>&1
fi
rc=$?
if (( rc == 0 )); then
fu_ok "$(fu_msg "The face is set up.")"
fu_config_load
if [[ $CFG_ENABLED != yes ]]; then
fu_note "$(fu_msg "Face unlock is still off. Turn it on with [1] or \`%s enable\`." "$FU_NAME")"
fi
return 0
fi
fu_note "$(fu_msg "No face was added.")"
return 1
}
fu_do_enable() {
fu_ensure_service || return 1
fu_faces_load
if (( ${#FU_FACE_IDS[@]} == 0 )); then
fu_say " $(fu_msg "First, set up your face.")"
fu_do_setup || return 1
fi
fu_config_set Enabled yes || { fu_bad "$(fu_msg "Could not save the setting.")"; return 1; }
fu_config_load
if fu_agent_available; then
fu_agent_enable || fu_bad "$(fu_msg "Could not start the lock screen agent.")"
else
fu_note "$(fu_msg "No systemd user session, so the lock screen agent was not started.")"
fi
# What was on the last time face unlock was on.
[[ $CFG_SUDO == yes ]] && ! fu_pam_enabled sudo && fu_root pam-enable sudo
[[ $CFG_POLKIT == yes ]] && ! fu_pam_enabled polkit-1 && fu_root pam-enable polkit-1
fu_ok "$(fu_msg "Face unlock is on. Lock the screen and look at it to try.")"
if [[ $CFG_SUDO != yes && $CFG_POLKIT != yes ]]; then
fu_note "$(fu_msg "It can do sudo and admin prompts too. See Settings.")"
fi
}
fu_do_disable() {
fu_config_set Enabled no || { fu_bad "$(fu_msg "Could not save the setting.")"; return 1; }
fu_agent_available && fu_agent_disable
local service
for service in "${FU_PAM_SERVICES[@]}"; do
if fu_pam_enabled "$service"; then
fu_root pam-disable "$service" || true
fi
done
fu_ok "$(fu_msg "Face unlock is off. Your faces are kept; delete them under Faces.")"
}
fu_do_status() {
fu_head " $FU_PRETTY"
fu_ui_status
printf '\n'
}
fu_do_faces() {
local i state
fu_status_load || { fu_bad "$(fu_reason_text unreachable)"; return 1; }
fu_faces_load
if (( ${#FU_FACE_IDS[@]} == 0 )); then
fu_note "$(fu_msg "No face is set up yet.")"
return 0
fi
for i in "${!FU_FACE_IDS[@]}"; do
if [[ ${FU_FACE_ON[i]} == true ]]; then state="$(fu_msg "on")"; else state="$(fu_msg "off")"; fi
printf ' %s %-24s %-4s %s\n' "${FU_FACE_IDS[i]}" "${FU_FACE_NAMES[i]}" "$state" \
"$(fu_msg "%s samples, %s learned" "${FU_FACE_SAMPLES[i]}" "${FU_FACE_LEARNED[i]}")"
done
}
fu_do_remove() {
local id="${1:-}" line
[[ -n $id ]] || { fu_bad "$(fu_msg "Which face? See \`%s faces\` for the ids." "$FU_NAME")"; return 1; }
line="$(fu_ctl remove "$id" | tail -n1)"
_fu_fields "$line"
if [[ ${FU_F[ok]:-} == true ]]; then
fu_ok "$(fu_msg "Deleted.")"
else
fu_bad "$(fu_reason_text "${FU_F[reason]:-unreachable}")"
return 1
fi
}
fu_do_help() {
cat <<- EOF
$FU_PRETTY $FU_VERSION
$(fu_msg "Usage: face-unlock [command]")
$(fu_msg "Commands:")
enable $(fu_msg "Turn face unlock on")
disable $(fu_msg "Turn it off (faces are kept)")
setup [NAME] $(fu_msg "Add a face")
faces $(fu_msg "List the faces")
remove ID $(fu_msg "Delete a face")
test $(fu_msg "Look at the camera and see what it sees")
status $(fu_msg "Show what is on")
-h, --help $(fu_msg "Show this help")
-V, --version $(fu_msg "Show the version")
$(fu_msg "Without a command an interactive menu is shown.")
EOF
}
# ---------------------------------------------------------------------------
# Dispatch
# ---------------------------------------------------------------------------
main() {
local cmd="${1:-}"
[[ $# -gt 0 ]] && shift
case "$cmd" in
--root) fu_root_verb "$@"; return ;;
esac
# Face data and settings are per user; root has neither a lock screen
# nor a face of its own to set up.
if [[ $EUID -eq 0 && -z ${FU_ALLOW_ROOT:-} ]]; then
fu_bad "$(fu_msg "Run this as your own user, not as root. It asks for your password when it needs to.")"
exit 2
fi
case "$cmd" in
enable) fu_do_enable ;;
disable) fu_do_disable ;;
setup|add|enroll) fu_do_setup "$@" ;;
faces|list) fu_do_faces ;;
remove|delete) fu_do_remove "$@" ;;
test|try) fu_ensure_service && fu_test ;;
status) fu_do_status ;;
-h|--help|help) fu_do_help ;;
-V|--version) printf '%s %s\n' "$FU_NAME" "$FU_VERSION" ;;
'') fu_ui_menu ;;
*)
fu_bad "$(fu_msg "Unknown command: %s" "$cmd")"
printf '\n'
fu_do_help
exit 1
;;
esac
}
main "$@"
+70 -70
View File
@@ -4,42 +4,42 @@
#
# The shell side never touches the camera or the face data. Those belong to
# the daemon, and everything here that needs them asks it through
# plasma-face-unlock-ctl. What this side does write is the user's own settings
# face-unlock-ctl. What this side does write is the user's own settings
# file, and (through sudo, and only when asked) the system settings and the
# PAM files of sudo and polkit.
PFU_VERSION="@VERSION@"
PFU_NAME="plasma-face-unlock"
PFU_PRETTY="Plasma Face Unlock"
FU_VERSION="@VERSION@"
FU_NAME="face-unlock"
FU_PRETTY="Face Unlock"
PFU_LIBDIR="${PFU_LIBDIR:-@LIBDIR@}"
PFU_LIBEXECDIR="${PFU_LIBEXECDIR:-@LIBEXECDIR@}"
PFU_LOCALEDIR="${PFU_LOCALEDIR:-@LOCALEDIR@}"
PFU_PAMDIR="${PFU_PAMDIR:-@PAMDIR@}"
FU_LIBDIR="${FU_LIBDIR:-@LIBDIR@}"
FU_LIBEXECDIR="${FU_LIBEXECDIR:-@LIBEXECDIR@}"
FU_LOCALEDIR="${FU_LOCALEDIR:-@LOCALEDIR@}"
FU_PAMDIR="${FU_PAMDIR:-@PAMDIR@}"
PFU_CTL="${PFU_CTL:-$PFU_LIBEXECDIR/plasma-face-unlock-ctl}"
PFU_AGENT="${PFU_AGENT:-$PFU_LIBEXECDIR/plasma-face-unlock-agent}"
PFU_PAM_MODULE="${PFU_PAM_MODULE:-$PFU_PAMDIR/pam_plasma_face_unlock.so}"
FU_CTL="${FU_CTL:-$FU_LIBEXECDIR/face-unlock-ctl}"
FU_AGENT="${FU_AGENT:-$FU_LIBEXECDIR/face-unlock-agent}"
FU_PAM_MODULE="${FU_PAM_MODULE:-$FU_PAMDIR/pam_face_unlock.so}"
PFU_XDG_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}"
PFU_CONFDIR="${PFU_CONFDIR:-${PFU_XDG_CONFIG}/${PFU_NAME}}"
PFU_CONFIG="${PFU_CONFIG:-${PFU_CONFDIR}/config}"
FU_XDG_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}"
FU_CONFDIR="${FU_CONFDIR:-${FU_XDG_CONFIG}/${FU_NAME}}"
FU_CONFIG="${FU_CONFIG:-${FU_CONFDIR}/config}"
# The system settings. Only root writes them; see system.sh.
PFU_SYSCONFIG="${PFU_SYSCONFIG:-/etc/${PFU_NAME}/config}"
FU_SYSCONFIG="${FU_SYSCONFIG:-/etc/${FU_NAME}/config}"
PFU_UNIT_SOCKET="plasma-face-unlockd.socket"
PFU_UNIT_AGENT="plasma-face-unlock-agent.service"
FU_UNIT_SOCKET="face-unlockd.socket"
FU_UNIT_AGENT="face-unlock-agent.service"
# ---------------------------------------------------------------------------
# Translations
# ---------------------------------------------------------------------------
export TEXTDOMAIN="plasma-face-unlock"
export TEXTDOMAINDIR="${PFU_LOCALEDIR}"
export TEXTDOMAIN="face-unlock"
export TEXTDOMAINDIR="${FU_LOCALEDIR}"
pfu_ui_locale() {
local l="${PFU_UI_LOCALE:-}"
fu_ui_locale() {
local l="${FU_UI_LOCALE:-}"
if [[ -z $l ]]; then
l="${LC_ALL:-}"
@@ -63,53 +63,53 @@ pfu_ui_locale() {
# Every gettext lookup is a fork and the settings screen redraws a screenful of
# labels per keypress, so results are memoized.
declare -A PFU_MSG_CACHE=()
declare -A FU_MSG_CACHE=()
PFU_MSG_RESULT=''
FU_MSG_RESULT=''
# pfu_msg_into <locale> <msgid>
# Plain lookup with the result in PFU_MSG_RESULT and no printf formatting, for
# fu_msg_into <locale> <msgid>
# Plain lookup with the result in FU_MSG_RESULT and no printf formatting, for
# callers that would otherwise pay a fork per label per frame.
pfu_msg_into() {
fu_msg_into() {
local locale="$1" msgid="$2" cachekey
cachekey="${locale}"$'\x1f'"${msgid}"
if [[ -n ${PFU_MSG_CACHE[$cachekey]+set} ]]; then
PFU_MSG_RESULT="${PFU_MSG_CACHE[$cachekey]}"
if [[ -n ${FU_MSG_CACHE[$cachekey]+set} ]]; then
FU_MSG_RESULT="${FU_MSG_CACHE[$cachekey]}"
return 0
fi
PFU_MSG_RESULT="$(LC_ALL="$locale" LANGUAGE="${locale%%.*}" gettext -- "$msgid" 2>/dev/null)"
[[ -n $PFU_MSG_RESULT ]] || PFU_MSG_RESULT="$msgid"
PFU_MSG_CACHE[$cachekey]="$PFU_MSG_RESULT"
FU_MSG_RESULT="$(LC_ALL="$locale" LANGUAGE="${locale%%.*}" gettext -- "$msgid" 2>/dev/null)"
[[ -n $FU_MSG_RESULT ]] || FU_MSG_RESULT="$msgid"
FU_MSG_CACHE[$cachekey]="$FU_MSG_RESULT"
return 0
}
# pfu_msg_in <locale> <msgid> [printf args...]
pfu_msg_in() {
# fu_msg_in <locale> <msgid> [printf args...]
fu_msg_in() {
local locale="$1" msgid="$2"
shift 2
pfu_msg_into "$locale" "$msgid"
fu_msg_into "$locale" "$msgid"
# With no arguments the message is plain text, not a format string. Feeding
# it to printf anyway would turn a literal percent sign in a translation
# into an invalid conversion.
if (( $# == 0 )); then
printf '%s' "$PFU_MSG_RESULT"
printf '%s' "$FU_MSG_RESULT"
return
fi
# shellcheck disable=SC2059 # the format string is the translated message
printf -- "$PFU_MSG_RESULT" "$@"
printf -- "$FU_MSG_RESULT" "$@"
}
PFU_LOCALE_CACHED=''
FU_LOCALE_CACHED=''
# pfu_msg <msgid> [printf args...]
pfu_msg() {
[[ -n $PFU_LOCALE_CACHED ]] || PFU_LOCALE_CACHED="$(pfu_ui_locale)"
pfu_msg_in "$PFU_LOCALE_CACHED" "$@"
# fu_msg <msgid> [printf args...]
fu_msg() {
[[ -n $FU_LOCALE_CACHED ]] || FU_LOCALE_CACHED="$(fu_ui_locale)"
fu_msg_in "$FU_LOCALE_CACHED" "$@"
}
# ---------------------------------------------------------------------------
@@ -119,45 +119,45 @@ pfu_msg() {
# Decided once, while stdout is still whatever the process was started with:
# testing -t 1 at the point of use is wrong for anything called through $(...),
# which sees a pipe and would conclude nobody is watching.
PFU_INTERACTIVE=''
[[ -t 1 ]] && PFU_INTERACTIVE=1
FU_INTERACTIVE=''
[[ -t 1 ]] && FU_INTERACTIVE=1
if [[ -n $PFU_INTERACTIVE && -z ${NO_COLOR:-} ]]; then
PFU_C_RESET=$'\033[0m'
PFU_C_BOLD=$'\033[1m'
PFU_C_DIM=$'\033[2m'
PFU_C_BLUE=$'\033[38;2;52;153;255m'
PFU_C_GREEN=$'\033[32m'
PFU_C_YELLOW=$'\033[33m'
PFU_C_RED=$'\033[31m'
if [[ -n $FU_INTERACTIVE && -z ${NO_COLOR:-} ]]; then
FU_C_RESET=$'\033[0m'
FU_C_BOLD=$'\033[1m'
FU_C_DIM=$'\033[2m'
FU_C_BLUE=$'\033[38;2;52;153;255m'
FU_C_GREEN=$'\033[32m'
FU_C_YELLOW=$'\033[33m'
FU_C_RED=$'\033[31m'
else
PFU_C_RESET='' PFU_C_BOLD='' PFU_C_DIM='' PFU_C_BLUE=''
PFU_C_GREEN='' PFU_C_YELLOW='' PFU_C_RED=''
FU_C_RESET='' FU_C_BOLD='' FU_C_DIM='' FU_C_BLUE=''
FU_C_GREEN='' FU_C_YELLOW='' FU_C_RED=''
fi
# What pfu_ok, pfu_bad and pfu_note printed. The menu redraws straight after an
# What fu_ok, fu_bad and fu_note printed. The menu redraws straight after an
# action, which wipes the screen, so it shows these again under the new frame
# rather than holding everything up for a key press.
PFU_UI_NOTICES=()
FU_UI_NOTICES=()
pfu_say() { printf '%s\n' "$*"; }
pfu_head() { printf '\n%s%s%s\n\n' "$PFU_C_BOLD$PFU_C_BLUE" "$*" "$PFU_C_RESET"; }
pfu_ok() { PFU_UI_NOTICES+=("$PFU_C_GREEN✔$PFU_C_RESET $*"); printf '%s✔%s %s\n' "$PFU_C_GREEN" "$PFU_C_RESET" "$*"; }
pfu_bad() { PFU_UI_NOTICES+=("$PFU_C_RED✘$PFU_C_RESET $*"); printf '%s✘%s %s\n' "$PFU_C_RED" "$PFU_C_RESET" "$*" >&2; }
pfu_note() { PFU_UI_NOTICES+=("$PFU_C_DIM•$PFU_C_RESET $*"); printf '%s•%s %s\n' "$PFU_C_DIM" "$PFU_C_RESET" "$*"; }
fu_say() { printf '%s\n' "$*"; }
fu_head() { printf '\n%s%s%s\n\n' "$FU_C_BOLD$FU_C_BLUE" "$*" "$FU_C_RESET"; }
fu_ok() { FU_UI_NOTICES+=("$FU_C_GREEN✔$FU_C_RESET $*"); printf '%s✔%s %s\n' "$FU_C_GREEN" "$FU_C_RESET" "$*"; }
fu_bad() { FU_UI_NOTICES+=("$FU_C_RED✘$FU_C_RESET $*"); printf '%s✘%s %s\n' "$FU_C_RED" "$FU_C_RESET" "$*" >&2; }
fu_note() { FU_UI_NOTICES+=("$FU_C_DIM•$FU_C_RESET $*"); printf '%s•%s %s\n' "$FU_C_DIM" "$FU_C_RESET" "$*"; }
pfu_have() { command -v "$1" > /dev/null 2>&1; }
fu_have() { command -v "$1" > /dev/null 2>&1; }
# Human-readable "x minutes ago" for a unix timestamp. 0 or empty yields the
# translated "never".
#
# Written with [[ ]] and a variable for each number on purpose: xgettext reads
# the < of an (( )) as a redirection and loses every string after it.
pfu_time_ago() {
fu_time_ago() {
local ts="$1" now delta n
if [[ ! $ts =~ ^[0-9]+$ || $ts -eq 0 ]]; then
pfu_msg "never"
fu_msg "never"
printf '\n'
return
fi
@@ -167,22 +167,22 @@ pfu_time_ago() {
[[ $delta -lt 0 ]] && delta=0
if [[ $delta -lt 60 ]]; then
pfu_msg "just now"
fu_msg "just now"
elif [[ $delta -lt 120 ]]; then
pfu_msg "1 minute ago"
fu_msg "1 minute ago"
elif [[ $delta -lt 3600 ]]; then
n=$(( delta / 60 ))
pfu_msg "%d minutes ago" "$n"
fu_msg "%d minutes ago" "$n"
elif [[ $delta -lt 7200 ]]; then
pfu_msg "1 hour ago"
fu_msg "1 hour ago"
elif [[ $delta -lt 86400 ]]; then
n=$(( delta / 3600 ))
pfu_msg "%d hours ago" "$n"
fu_msg "%d hours ago" "$n"
elif [[ $delta -lt 172800 ]]; then
pfu_msg "1 day ago"
fu_msg "1 day ago"
else
n=$(( delta / 86400 ))
pfu_msg "%d days ago" "$n"
fu_msg "%d days ago" "$n"
fi
printf '\n'
}
+34 -34
View File
@@ -2,9 +2,9 @@
#
# Reading and writing the settings files.
#
# Two of them, in the same format: ~/.config/plasma-face-unlock/config for
# Two of them, in the same format: ~/.config/face-unlock/config for
# what this user wants from the lock screen and the bubble, and
# /etc/plasma-face-unlock/config for what the daemon does (which camera, how
# /etc/face-unlock/config for what the daemon does (which camera, how
# strict), which only root writes. Neither is meant to be edited by hand:
# every option is in the menu.
#
@@ -12,25 +12,25 @@
# comments. The daemon and the agent read them with the same rules (see
# src/core/keyvalue.cpp).
declare -A PFU_KV_CACHE=()
declare -A FU_KV_CACHE=()
# _pfu_kv_lookup <file> <Key> [default]
# Result in PFU_KV_VALUE. Assigning rather than printing matters on the
# _fu_kv_lookup <file> <Key> [default]
# Result in FU_KV_VALUE. Assigning rather than printing matters on the
# settings screen, which reads every key on every frame: a command
# substitution there is a fork, and forks are the whole cost of a redraw.
PFU_KV_VALUE=''
FU_KV_VALUE=''
_pfu_kv_lookup() {
_fu_kv_lookup() {
local file="$1" key="$2" default="${3:-}" val='' line content
PFU_KV_VALUE="$default"
FU_KV_VALUE="$default"
[[ -r $file ]] || return 0
if [[ -n ${PFU_KV_CACHE[$file]+set} ]]; then
content="${PFU_KV_CACHE[$file]}"
if [[ -n ${FU_KV_CACHE[$file]+set} ]]; then
content="${FU_KV_CACHE[$file]}"
else
content="$(< "$file")"
PFU_KV_CACHE[$file]="$content"
FU_KV_CACHE[$file]="$content"
fi
while IFS= read -r line; do
@@ -45,19 +45,19 @@ _pfu_kv_lookup() {
val="${val%\"}"
val="${val#\"}"
[[ -n $val ]] && PFU_KV_VALUE="$val"
[[ -n $val ]] && FU_KV_VALUE="$val"
return 0
}
pfu_is_true() {
fu_is_true() {
case "${1,,}" in
yes|y|true|1|on|enabled) return 0 ;;
*) return 1 ;;
esac
}
# pfu_kv_set <file> <Key> <Value> <header line>
pfu_kv_set() {
# fu_kv_set <file> <Key> <Value> <header line>
fu_kv_set() {
local file="$1" key="$2" value="$3" header="$4" tmp
if [[ ! -e $file ]]; then
@@ -65,7 +65,7 @@ pfu_kv_set() {
{
printf '# %s\n' "$header"
printf '#\n'
printf '# Written by `%s`. Nothing here needs editing by hand:\n' "$PFU_NAME"
printf '# Written by `%s`. Nothing here needs editing by hand:\n' "$FU_NAME"
printf '# every option is in the menu.\n'
} > "$file" || return 1
fi
@@ -91,42 +91,42 @@ pfu_kv_set() {
# Replaced, not rewritten in place: the agent watches the directory and
# picks up the new file the moment it lands.
mv -f "$tmp" "$file"
unset 'PFU_KV_CACHE[$file]'
unset 'FU_KV_CACHE[$file]'
}
# ---------------------------------------------------------------------------
# This user's settings
# ---------------------------------------------------------------------------
pfu_config_get() {
_pfu_kv_lookup "$PFU_CONFIG" "$@"
printf '%s\n' "$PFU_KV_VALUE"
fu_config_get() {
_fu_kv_lookup "$FU_CONFIG" "$@"
printf '%s\n' "$FU_KV_VALUE"
}
pfu_config_set() {
pfu_kv_set "$PFU_CONFIG" "$1" "$2" "$PFU_PRETTY"
fu_config_set() {
fu_kv_set "$FU_CONFIG" "$1" "$2" "$FU_PRETTY"
}
# ---------------------------------------------------------------------------
# The system settings (read by anybody, written by root)
# ---------------------------------------------------------------------------
pfu_sys_get() {
_pfu_kv_lookup "$PFU_SYSCONFIG" "$@"
printf '%s\n' "$PFU_KV_VALUE"
fu_sys_get() {
_fu_kv_lookup "$FU_SYSCONFIG" "$@"
printf '%s\n' "$FU_KV_VALUE"
}
# pfu_config_load
# fu_config_load
# Everything the menu shows, resolved once per screen.
pfu_config_load() {
PFU_KV_CACHE=()
fu_config_load() {
FU_KV_CACHE=()
_pfu_kv_lookup "$PFU_CONFIG" Enabled no; CFG_ENABLED=no; pfu_is_true "$PFU_KV_VALUE" && CFG_ENABLED=yes
_pfu_kv_lookup "$PFU_CONFIG" LockScreen yes; CFG_LOCK=no; pfu_is_true "$PFU_KV_VALUE" && CFG_LOCK=yes
_pfu_kv_lookup "$PFU_CONFIG" Sudo no; CFG_SUDO=no; pfu_is_true "$PFU_KV_VALUE" && CFG_SUDO=yes
_pfu_kv_lookup "$PFU_CONFIG" Polkit no; CFG_POLKIT=no; pfu_is_true "$PFU_KV_VALUE" && CFG_POLKIT=yes
_fu_kv_lookup "$FU_CONFIG" Enabled no; CFG_ENABLED=no; fu_is_true "$FU_KV_VALUE" && CFG_ENABLED=yes
_fu_kv_lookup "$FU_CONFIG" LockScreen yes; CFG_LOCK=no; fu_is_true "$FU_KV_VALUE" && CFG_LOCK=yes
_fu_kv_lookup "$FU_CONFIG" Sudo no; CFG_SUDO=no; fu_is_true "$FU_KV_VALUE" && CFG_SUDO=yes
_fu_kv_lookup "$FU_CONFIG" Polkit no; CFG_POLKIT=no; fu_is_true "$FU_KV_VALUE" && CFG_POLKIT=yes
_pfu_kv_lookup "$PFU_SYSCONFIG" Liveness light; CFG_LIVENESS="$PFU_KV_VALUE"
_pfu_kv_lookup "$PFU_SYSCONFIG" Camera auto; CFG_CAMERA="$PFU_KV_VALUE"
_fu_kv_lookup "$FU_SYSCONFIG" Liveness light; CFG_LIVENESS="$FU_KV_VALUE"
_fu_kv_lookup "$FU_SYSCONFIG" Camera auto; CFG_CAMERA="$FU_KV_VALUE"
return 0
}
+103 -103
View File
@@ -2,184 +2,184 @@
#
# Asking the daemon.
#
# Through plasma-face-unlock-ctl, which prints every message as a line of tab
# Through face-unlock-ctl, which prints every message as a line of tab
# separated key=value pairs (see src/ctl/main.cpp). What comes back is parsed
# into plain variables and arrays here, so the rest of the shell code never
# sees the wire format.
# _pfu_fields <line>
# Splits one line of ctl output into the associative array PFU_F.
declare -A PFU_F=()
# _fu_fields <line>
# Splits one line of ctl output into the associative array FU_F.
declare -A FU_F=()
_pfu_fields() {
_fu_fields() {
local line="$1" field
local -a parts
PFU_F=()
FU_F=()
IFS=$'\t' read -r -a parts <<< "$line"
for field in "${parts[@]}"; do
PFU_F["${field%%=*}"]="${field#*=}"
FU_F["${field%%=*}"]="${field#*=}"
done
}
pfu_ctl() {
"$PFU_CTL" "$@"
fu_ctl() {
"$FU_CTL" "$@"
}
# pfu_status_load
# PFU_ST_REACHABLE is yes when the daemon answered at all. Everything else
# fu_status_load
# FU_ST_REACHABLE is yes when the daemon answered at all. Everything else
# is only filled in then.
pfu_status_load() {
fu_status_load() {
local out
PFU_ST_REACHABLE=no
PFU_ST_FACES=0
PFU_ST_LOCKOUT=0
PFU_ST_LAST=0
PFU_ST_PURPOSE=''
PFU_ST_CAMERA=''
PFU_ST_CAMERA_NAME=''
PFU_ST_CAMERA_PRESENT=no
PFU_ST_MODELS=no
FU_ST_REACHABLE=no
FU_ST_FACES=0
FU_ST_LOCKOUT=0
FU_ST_LAST=0
FU_ST_PURPOSE=''
FU_ST_CAMERA=''
FU_ST_CAMERA_NAME=''
FU_ST_CAMERA_PRESENT=no
FU_ST_MODELS=no
out="$(pfu_ctl status 2>/dev/null | tail -n1)"
_pfu_fields "$out"
[[ ${PFU_F[ok]:-} == true ]] || return 1
out="$(fu_ctl status 2>/dev/null | tail -n1)"
_fu_fields "$out"
[[ ${FU_F[ok]:-} == true ]] || return 1
PFU_ST_REACHABLE=yes
PFU_ST_FACES="${PFU_F[faces]:-0}"
PFU_ST_LOCKOUT="${PFU_F[lockout]:-0}"
PFU_ST_LAST="${PFU_F[lastUnlock]:-0}"
PFU_ST_PURPOSE="${PFU_F[lastPurpose]:-}"
PFU_ST_CAMERA="${PFU_F[cameraPath]:-}"
PFU_ST_CAMERA_NAME="${PFU_F[cameraName]:-}"
PFU_ST_CAMERA_PRESENT="${PFU_F[cameraPresent]:-false}"
PFU_ST_MODELS="${PFU_F[models]:-false}"
FU_ST_REACHABLE=yes
FU_ST_FACES="${FU_F[faces]:-0}"
FU_ST_LOCKOUT="${FU_F[lockout]:-0}"
FU_ST_LAST="${FU_F[lastUnlock]:-0}"
FU_ST_PURPOSE="${FU_F[lastPurpose]:-}"
FU_ST_CAMERA="${FU_F[cameraPath]:-}"
FU_ST_CAMERA_NAME="${FU_F[cameraName]:-}"
FU_ST_CAMERA_PRESENT="${FU_F[cameraPresent]:-false}"
FU_ST_MODELS="${FU_F[models]:-false}"
return 0
}
# pfu_faces_load
# fu_faces_load
# The caller's faces, into parallel arrays.
pfu_faces_load() {
fu_faces_load() {
local line
PFU_FACE_IDS=() PFU_FACE_NAMES=() PFU_FACE_ON=() PFU_FACE_SAMPLES=() PFU_FACE_LEARNED=() PFU_FACE_CREATED=()
FU_FACE_IDS=() FU_FACE_NAMES=() FU_FACE_ON=() FU_FACE_SAMPLES=() FU_FACE_LEARNED=() FU_FACE_CREATED=()
while IFS= read -r line; do
_pfu_fields "$line"
[[ ${PFU_F[event]:-} == item ]] || continue
PFU_FACE_IDS+=("${PFU_F[id]}")
PFU_FACE_NAMES+=("${PFU_F[name]}")
PFU_FACE_ON+=("${PFU_F[enabled]}")
PFU_FACE_SAMPLES+=("${PFU_F[samples]:-0}")
PFU_FACE_LEARNED+=("${PFU_F[adaptive]:-0}")
PFU_FACE_CREATED+=("${PFU_F[created]:-0}")
done < <(pfu_ctl list 2>/dev/null)
_fu_fields "$line"
[[ ${FU_F[event]:-} == item ]] || continue
FU_FACE_IDS+=("${FU_F[id]}")
FU_FACE_NAMES+=("${FU_F[name]}")
FU_FACE_ON+=("${FU_F[enabled]}")
FU_FACE_SAMPLES+=("${FU_F[samples]:-0}")
FU_FACE_LEARNED+=("${FU_F[adaptive]:-0}")
FU_FACE_CREATED+=("${FU_F[created]:-0}")
done < <(fu_ctl list 2>/dev/null)
}
# pfu_cameras_load
pfu_cameras_load() {
# fu_cameras_load
fu_cameras_load() {
local line
PFU_CAM_PATHS=() PFU_CAM_NAMES=() PFU_CAM_IR=()
PFU_CAM_AUTO=''
FU_CAM_PATHS=() FU_CAM_NAMES=() FU_CAM_IR=()
FU_CAM_AUTO=''
while IFS= read -r line; do
_pfu_fields "$line"
case "${PFU_F[event]:-}" in
_fu_fields "$line"
case "${FU_F[event]:-}" in
item)
PFU_CAM_PATHS+=("${PFU_F[path]}")
PFU_CAM_NAMES+=("${PFU_F[name]}")
PFU_CAM_IR+=("${PFU_F[infrared]}")
FU_CAM_PATHS+=("${FU_F[path]}")
FU_CAM_NAMES+=("${FU_F[name]}")
FU_CAM_IR+=("${FU_F[infrared]}")
;;
result)
PFU_CAM_AUTO="${PFU_F[auto]:-}"
FU_CAM_AUTO="${FU_F[auto]:-}"
;;
esac
done < <(pfu_ctl cameras 2>/dev/null)
done < <(fu_ctl cameras 2>/dev/null)
}
# pfu_reason_text <reason>
# fu_reason_text <reason>
# What a daemon answer means, for people.
pfu_reason_text() {
fu_reason_text() {
case "$1" in
mismatch) pfu_msg "The face did not match." ;;
spoof) pfu_msg "That looked like a photo or a screen." ;;
liveness) pfu_msg "The face matched, but did not blink or move." ;;
attention) pfu_msg "The face was not looking at the screen." ;;
quality) pfu_msg "The picture was too dark, too blurry or too far away." ;;
no-face) pfu_msg "No face in view." ;;
lockout) pfu_msg "Face unlock is paused after too many tries. Unlock once with your password." ;;
not-enrolled) pfu_msg "No face is set up yet." ;;
camera) pfu_msg "The camera could not be used." ;;
models) pfu_msg "The recognition models are missing. Reinstall the package." ;;
lid-closed) pfu_msg "The lid is closed." ;;
busy) pfu_msg "The camera is busy with another scan." ;;
unreachable) pfu_msg "The face unlock service is not running." ;;
denied) pfu_msg "Not allowed." ;;
*) pfu_msg "Something went wrong (%s)." "$1" ;;
mismatch) fu_msg "The face did not match." ;;
spoof) fu_msg "That looked like a photo or a screen." ;;
liveness) fu_msg "The face matched, but did not blink or move." ;;
attention) fu_msg "The face was not looking at the screen." ;;
quality) fu_msg "The picture was too dark, too blurry or too far away." ;;
no-face) fu_msg "No face in view." ;;
lockout) fu_msg "Face unlock is paused after too many tries. Unlock once with your password." ;;
not-enrolled) fu_msg "No face is set up yet." ;;
camera) fu_msg "The camera could not be used." ;;
models) fu_msg "The recognition models are missing. Reinstall the package." ;;
lid-closed) fu_msg "The lid is closed." ;;
busy) fu_msg "The camera is busy with another scan." ;;
unreachable) fu_msg "The face unlock service is not running." ;;
denied) fu_msg "Not allowed." ;;
*) fu_msg "Something went wrong (%s)." "$1" ;;
esac
printf '\n'
}
# pfu_test
# fu_test
# One scan, with everything the checks see on one line that keeps updating.
# The bubble shows it too, if the agent is running.
pfu_test() {
fu_test() {
local line started=0 shown='' score='-' matched=0
pfu_say " $(pfu_msg "Look at the camera. Blink once, or turn your head a little.")"
fu_say " $(fu_msg "Look at the camera. Blink once, or turn your head a little.")"
printf '\n'
while IFS= read -r line; do
_pfu_fields "$line"
case "${PFU_F[event]:-}" in
_fu_fields "$line"
case "${FU_F[event]:-}" in
started)
started=1
;;
face)
printf '\r\033[K %s\n' "$(pfu_msg "Face found.")"
printf '\r\033[K %s\n' "$(fu_msg "Face found.")"
;;
hint)
case "${PFU_F[hint]}" in
blink) printf '\r\033[K %s\n' "$(pfu_msg "Recognised. Now blink once, or turn your head a little.")" ;;
look) printf '\r\033[K %s\n' "$(pfu_msg "Look at the screen.")" ;;
closer) printf '\r\033[K %s\n' "$(pfu_msg "Move closer to the camera.")" ;;
light) printf '\r\033[K %s\n' "$(pfu_msg "It is too dark to see your face.")" ;;
case "${FU_F[hint]}" in
blink) printf '\r\033[K %s\n' "$(fu_msg "Recognised. Now blink once, or turn your head a little.")" ;;
look) printf '\r\033[K %s\n' "$(fu_msg "Look at the screen.")" ;;
closer) printf '\r\033[K %s\n' "$(fu_msg "Move closer to the camera.")" ;;
light) printf '\r\033[K %s\n' "$(fu_msg "It is too dark to see your face.")" ;;
esac
;;
frame)
[[ -n ${PFU_F[score]:-} ]] && score="${PFU_F[score]}"
matched="${PFU_F[matched]:-0}"
[[ -n ${FU_F[score]:-} ]] && score="${FU_F[score]}"
matched="${FU_F[matched]:-0}"
# match, turn of the head, eyes, and how close each photo check is
# to firing, as numbers for anybody tuning it.
printf -v shown ' %s %-6s %s %5s° %s %-5s %s %-4s %s %-4s %s %-4s %s %-4s' \
"$(pfu_msg "match")" "$score" "$(pfu_msg "turn")" "${PFU_F[yaw]:-0}" \
"$(pfu_msg "eyes")" "${PFU_F[eyes]:-0}" \
"$(pfu_msg "depth")" "${PFU_F[depth]:-0}" "$(pfu_msg "blink")" "${PFU_F[blink]:-0}" \
"$(pfu_msg "glare")" "${PFU_F[glare]:-0}" "$(pfu_msg "edge")" "${PFU_F[device]:-0}"
[[ -n $PFU_INTERACTIVE ]] && printf '\r\033[K%s%s%s' "$PFU_C_DIM" "$shown" "$PFU_C_RESET"
"$(fu_msg "match")" "$score" "$(fu_msg "turn")" "${FU_F[yaw]:-0}" \
"$(fu_msg "eyes")" "${FU_F[eyes]:-0}" \
"$(fu_msg "depth")" "${FU_F[depth]:-0}" "$(fu_msg "blink")" "${FU_F[blink]:-0}" \
"$(fu_msg "glare")" "${FU_F[glare]:-0}" "$(fu_msg "edge")" "${FU_F[device]:-0}"
[[ -n $FU_INTERACTIVE ]] && printf '\r\033[K%s%s%s' "$FU_C_DIM" "$shown" "$FU_C_RESET"
;;
result)
printf '\r\033[K'
if [[ ${PFU_F[ok]} == true ]]; then
if [[ ${FU_F[ok]} == true ]]; then
local how=''
case "${PFU_F[liveness]:-}" in
blink) how="$(pfu_msg "blink")" ;;
depth) how="$(pfu_msg "head turn")" ;;
case "${FU_F[liveness]:-}" in
blink) how="$(fu_msg "blink")" ;;
depth) how="$(fu_msg "head turn")" ;;
esac
pfu_ok "$(pfu_msg "Recognised as %s (match %s) in %s ms." "${PFU_F[name]}" "${PFU_F[score]}" "${PFU_F[ms]}")"
[[ -n $how ]] && pfu_say " $(pfu_msg "Sign of life: %s" "$how")"
fu_ok "$(fu_msg "Recognised as %s (match %s) in %s ms." "${FU_F[name]}" "${FU_F[score]}" "${FU_F[ms]}")"
[[ -n $how ]] && fu_say " $(fu_msg "Sign of life: %s" "$how")"
else
pfu_bad "$(pfu_reason_text "${PFU_F[reason]}")"
[[ -n ${PFU_F[message]:-} ]] && pfu_say " ${PFU_F[message]}"
if [[ -n ${PFU_F[lockout]:-} ]]; then
pfu_note "$(pfu_msg "That was too many tries. Face unlock is paused until you unlock with your password.")"
fu_bad "$(fu_reason_text "${FU_F[reason]}")"
[[ -n ${FU_F[message]:-} ]] && fu_say " ${FU_F[message]}"
if [[ -n ${FU_F[lockout]:-} ]]; then
fu_note "$(fu_msg "That was too many tries. Face unlock is paused until you unlock with your password.")"
fi
fi
(( started )) || true
return 0
;;
esac
done < <(pfu_ctl test 2>/dev/null)
done < <(fu_ctl test 2>/dev/null)
printf '\n'
pfu_bad "$(pfu_reason_text unreachable)"
fu_bad "$(fu_reason_text unreachable)"
return 1
}
+265 -265
View File
@@ -14,39 +14,39 @@
# character, so the line discipline eats it instead of delivering it, and a
# backspace typed while the interface was between reads simply vanishes.
# Holding non-canonical mode for the whole interface removes the gap.
PFU_TERM_SAVED=''
FU_TERM_SAVED=''
pfu_ui_term_raw() {
pfu_have stty || return 0
fu_ui_term_raw() {
fu_have stty || return 0
[[ -t 0 ]] || return 0
[[ -n $PFU_TERM_SAVED ]] && return 0
[[ -n $FU_TERM_SAVED ]] && return 0
PFU_TERM_SAVED="$(stty -g 2>/dev/null)" || { PFU_TERM_SAVED=''; return 0; }
FU_TERM_SAVED="$(stty -g 2>/dev/null)" || { FU_TERM_SAVED=''; return 0; }
stty -icanon -echo min 1 time 0 2>/dev/null || true
}
pfu_ui_term_restore() {
[[ -n $PFU_TERM_SAVED ]] || return 0
stty "$PFU_TERM_SAVED" 2>/dev/null || true
PFU_TERM_SAVED=''
fu_ui_term_restore() {
[[ -n $FU_TERM_SAVED ]] || return 0
stty "$FU_TERM_SAVED" 2>/dev/null || true
FU_TERM_SAVED=''
}
# Runs an action with the terminal handed back to normal line mode, so anything
# it prints or prompts for (sudo's password prompt, above all) behaves the way
# a program expects.
pfu_ui_cooked() {
pfu_ui_term_restore
fu_ui_cooked() {
fu_ui_term_restore
"$@"
local rc=$?
pfu_ui_term_raw
fu_ui_term_raw
return $rc
}
# pfu_read_key
# fu_read_key
# One keypress, resolved to a symbolic name. Arrow keys arrive as ESC [ A, so
# the tail of the sequence is consumed here rather than being mistaken for
# three separate presses.
pfu_read_key() {
fu_read_key() {
local k rest
IFS= read -rsn1 k || return 1
@@ -72,26 +72,26 @@ pfu_read_key() {
esac
}
# pfu_ui_read_line <initial>
# A minimal line editor built on pfu_read_key, with the result in
# PFU_LINE_RESULT. This exists instead of bash's own `read -r` because mixing
# fu_ui_read_line <initial>
# A minimal line editor built on fu_read_key, with the result in
# FU_LINE_RESULT. This exists instead of bash's own `read -r` because mixing
# line mode into a single-key interface breaks it: after one cooked-mode read
# the following `read -sn1` stops receiving keystrokes entirely.
PFU_LINE_RESULT=''
FU_LINE_RESULT=''
pfu_ui_read_line() {
fu_ui_read_line() {
local buf="${1:-}" key
PFU_LINE_RESULT=''
FU_LINE_RESULT=''
printf '%s' "$buf"
while true; do
key="$(pfu_read_key)" || { printf '\n'; return 1; }
key="$(fu_read_key)" || { printf '\n'; return 1; }
case "$key" in
enter)
printf '\n'
PFU_LINE_RESULT="$buf"
FU_LINE_RESULT="$buf"
return 0
;;
escape)
@@ -118,88 +118,88 @@ pfu_ui_read_line() {
done
}
# _pfu_ui_take_notices
# _fu_ui_take_notices
# The messages the last action left, as lines for under a frame, in
# PFU_UI_NOTICE_TEXT. Each is shown once.
PFU_UI_NOTICE_TEXT=''
# FU_UI_NOTICE_TEXT. Each is shown once.
FU_UI_NOTICE_TEXT=''
_pfu_ui_take_notices() {
_fu_ui_take_notices() {
local n
PFU_UI_NOTICE_TEXT=''
(( ${#PFU_UI_NOTICES[@]} )) || return 0
PFU_UI_NOTICE_TEXT=$'\n'
for n in "${PFU_UI_NOTICES[@]}"; do
PFU_UI_NOTICE_TEXT+=" $n"$'\n'
FU_UI_NOTICE_TEXT=''
(( ${#FU_UI_NOTICES[@]} )) || return 0
FU_UI_NOTICE_TEXT=$'\n'
for n in "${FU_UI_NOTICES[@]}"; do
FU_UI_NOTICE_TEXT+=" $n"$'\n'
done
PFU_UI_NOTICES=()
FU_UI_NOTICES=()
}
# pfu_ui_confirm <question>
pfu_ui_confirm() {
# fu_ui_confirm <question>
fu_ui_confirm() {
local key hint yes
# TRANSLATORS: the letter after "[" is the key for yes. y works too.
hint="$(pfu_msg "[y/N]")"
hint="$(fu_msg "[y/N]")"
yes="${hint:1:1}"
printf '\n %s %s ' "$1" "$hint"
key="$(pfu_read_key)" || return 1
key="$(fu_read_key)" || return 1
printf '%s\n' "$key"
[[ ${key,,} == y || ${key,,} == "${yes,,}" ]]
}
# _pfu_width <text>
# The columns the text takes, into PFU_WIDTH. ${#s} counts characters, but
# _fu_width <text>
# The columns the text takes, into FU_WIDTH. ${#s} counts characters, but
# Chinese, Japanese and Korean ones take two columns each.
PFU_WIDTH=0
FU_WIDTH=0
_pfu_width() {
_fu_width() {
local wide="${1//[^ -〿぀-ヿ㐀-䶿一-鿿가-힯豈-﫿＀-⦆]/}"
PFU_WIDTH=$(( ${#1} + ${#wide} ))
FU_WIDTH=$(( ${#1} + ${#wide} ))
}
# _pfu_row <label> <value>
# _fu_row <label> <value>
# printf's %-28s pads by bytes, so a label containing "ü" comes out one column
# short. The padding is computed here instead.
_pfu_row() {
_fu_row() {
local label="$1" value="$2" pad
_pfu_width "$label"
pad=$(( 30 - PFU_WIDTH ))
_fu_width "$label"
pad=$(( 30 - FU_WIDTH ))
(( pad < 0 )) && pad=0
printf ' %s%*s %s\n' "$label" "$pad" '' "$value"
}
_pfu_onoff() {
_fu_onoff() {
if [[ $1 == yes ]]; then
printf '%s%s%s' "$PFU_C_GREEN" "$(pfu_msg "ON")" "$PFU_C_RESET"
printf '%s%s%s' "$FU_C_GREEN" "$(fu_msg "ON")" "$FU_C_RESET"
else
printf '%s%s%s' "$PFU_C_DIM" "$(pfu_msg "OFF")" "$PFU_C_RESET"
printf '%s%s%s' "$FU_C_DIM" "$(fu_msg "OFF")" "$FU_C_RESET"
fi
}
_pfu_small_onoff() {
_fu_small_onoff() {
if [[ $1 == yes ]]; then
printf '%s%s%s' "$PFU_C_GREEN" "$(pfu_msg "on")" "$PFU_C_RESET"
printf '%s%s%s' "$FU_C_GREEN" "$(fu_msg "on")" "$FU_C_RESET"
else
printf '%s%s%s' "$PFU_C_DIM" "$(pfu_msg "off")" "$PFU_C_RESET"
printf '%s%s%s' "$FU_C_DIM" "$(fu_msg "off")" "$FU_C_RESET"
fi
}
# pfu_value_label <Key> <value>
# fu_value_label <Key> <value>
# How a setting's value reads in the menu.
pfu_value_label() {
fu_value_label() {
case "$1:$2" in
Liveness:heavy) pfu_msg "strict" ;;
Liveness:light) pfu_msg "basic" ;;
Liveness:off) pfu_msg "off" ;;
Strictness:normal) pfu_msg "normal" ;;
Strictness:strict) pfu_msg "strict" ;;
Strictness:relaxed) pfu_msg "relaxed" ;;
BubbleStyle:full) pfu_msg "island with the face" ;;
BubbleStyle:minimal) pfu_msg "small pill with a lock" ;;
AnimationSpeed:normal) pfu_msg "normal" ;;
AnimationSpeed:fast) pfu_msg "fast" ;;
AnimationSpeed:slow) pfu_msg "slow" ;;
ScanSeconds:*) pfu_msg "%s seconds" "$2" ;;
Camera:auto) pfu_msg "automatic" ;;
Liveness:heavy) fu_msg "strict" ;;
Liveness:light) fu_msg "basic" ;;
Liveness:off) fu_msg "off" ;;
Strictness:normal) fu_msg "normal" ;;
Strictness:strict) fu_msg "strict" ;;
Strictness:relaxed) fu_msg "relaxed" ;;
BubbleStyle:full) fu_msg "island with the face" ;;
BubbleStyle:minimal) fu_msg "small pill with a lock" ;;
AnimationSpeed:normal) fu_msg "normal" ;;
AnimationSpeed:fast) fu_msg "fast" ;;
AnimationSpeed:slow) fu_msg "slow" ;;
ScanSeconds:*) fu_msg "%s seconds" "$2" ;;
Camera:auto) fu_msg "automatic" ;;
*) printf '%s' "$2" ;;
esac
}
@@ -208,55 +208,55 @@ pfu_value_label() {
# Status
# ---------------------------------------------------------------------------
# pfu_ui_status
# fu_ui_status
# Shared by the `status` subcommand and the menu header.
pfu_ui_status() {
fu_ui_status() {
local names='' i camera
pfu_config_load
pfu_status_load
fu_config_load
fu_status_load
_pfu_row "$(pfu_msg "Face unlock")" "$(_pfu_onoff "$CFG_ENABLED")"
_fu_row "$(fu_msg "Face unlock")" "$(_fu_onoff "$CFG_ENABLED")"
printf '\n'
if [[ $PFU_ST_REACHABLE != yes ]]; then
_pfu_row "$(pfu_msg "Service")" "${PFU_C_YELLOW}$(pfu_msg "not running")${PFU_C_RESET}"
if [[ $FU_ST_REACHABLE != yes ]]; then
_fu_row "$(fu_msg "Service")" "${FU_C_YELLOW}$(fu_msg "not running")${FU_C_RESET}"
if [[ $CFG_ENABLED == yes ]]; then
printf '\n %s%s%s\n' "$PFU_C_DIM" "$(pfu_msg "Turning face unlock on again starts it.")" "$PFU_C_RESET"
printf '\n %s%s%s\n' "$FU_C_DIM" "$(fu_msg "Turning face unlock on again starts it.")" "$FU_C_RESET"
fi
return 0
fi
pfu_faces_load
for i in "${!PFU_FACE_NAMES[@]}"; do
[[ ${PFU_FACE_ON[i]} == true ]] || continue
names="${names:+$names, }${PFU_FACE_NAMES[i]}"
fu_faces_load
for i in "${!FU_FACE_NAMES[@]}"; do
[[ ${FU_FACE_ON[i]} == true ]] || continue
names="${names:+$names, }${FU_FACE_NAMES[i]}"
done
if (( ${#PFU_FACE_IDS[@]} == 0 )); then
_pfu_row "$(pfu_msg "Faces")" "${PFU_C_YELLOW}$(pfu_msg "none set up yet")${PFU_C_RESET}"
if (( ${#FU_FACE_IDS[@]} == 0 )); then
_fu_row "$(fu_msg "Faces")" "${FU_C_YELLOW}$(fu_msg "none set up yet")${FU_C_RESET}"
else
_pfu_row "$(pfu_msg "Faces")" "${PFU_ST_FACES} ${PFU_C_DIM}(${names:-$(pfu_msg "all turned off")})${PFU_C_RESET}"
_fu_row "$(fu_msg "Faces")" "${FU_ST_FACES} ${FU_C_DIM}(${names:-$(fu_msg "all turned off")})${FU_C_RESET}"
fi
if [[ $PFU_ST_CAMERA_PRESENT == true ]]; then
camera="${PFU_ST_CAMERA_NAME:-$PFU_ST_CAMERA}"
if [[ $FU_ST_CAMERA_PRESENT == true ]]; then
camera="${FU_ST_CAMERA_NAME:-$FU_ST_CAMERA}"
else
camera="${PFU_C_YELLOW}$(pfu_msg "none found")${PFU_C_RESET}"
camera="${FU_C_YELLOW}$(fu_msg "none found")${FU_C_RESET}"
fi
_pfu_row "$(pfu_msg "Camera")" "$camera"
_fu_row "$(fu_msg "Camera")" "$camera"
_pfu_row "$(pfu_msg "Lock screen")" "$(_pfu_small_onoff "$( [[ $CFG_ENABLED == yes && $CFG_LOCK == yes ]] && echo yes || echo no)")"
_pfu_row "$(pfu_msg "sudo")" "$(_pfu_small_onoff "$(pfu_pam_enabled sudo && echo yes || echo no)")"
_pfu_row "$(pfu_msg "Admin prompts")" "$(_pfu_small_onoff "$(pfu_pam_enabled polkit-1 && echo yes || echo no)")"
_pfu_row "$(pfu_msg "Photo check")" "$(pfu_value_label Liveness "$CFG_LIVENESS")"
_fu_row "$(fu_msg "Lock screen")" "$(_fu_small_onoff "$( [[ $CFG_ENABLED == yes && $CFG_LOCK == yes ]] && echo yes || echo no)")"
_fu_row "$(fu_msg "sudo")" "$(_fu_small_onoff "$(fu_pam_enabled sudo && echo yes || echo no)")"
_fu_row "$(fu_msg "Admin prompts")" "$(_fu_small_onoff "$(fu_pam_enabled polkit-1 && echo yes || echo no)")"
_fu_row "$(fu_msg "Photo check")" "$(fu_value_label Liveness "$CFG_LIVENESS")"
if (( PFU_ST_LOCKOUT > 0 )); then
_pfu_row "$(pfu_msg "Paused")" "${PFU_C_YELLOW}$(pfu_msg "for %d more minutes, or until the password is used" "$(( (PFU_ST_LOCKOUT + 59) / 60 ))")${PFU_C_RESET}"
if (( FU_ST_LOCKOUT > 0 )); then
_fu_row "$(fu_msg "Paused")" "${FU_C_YELLOW}$(fu_msg "for %d more minutes, or until the password is used" "$(( (FU_ST_LOCKOUT + 59) / 60 ))")${FU_C_RESET}"
fi
_pfu_row "$(pfu_msg "Last unlock")" "$(pfu_time_ago "$PFU_ST_LAST")"
_fu_row "$(fu_msg "Last unlock")" "$(fu_time_ago "$FU_ST_LAST")"
if [[ $PFU_ST_MODELS != true ]]; then
printf '\n %s%s%s\n' "$PFU_C_RED" "$(pfu_msg "The recognition models are missing. Reinstall the package.")" "$PFU_C_RESET"
if [[ $FU_ST_MODELS != true ]]; then
printf '\n %s%s%s\n' "$FU_C_RED" "$(fu_msg "The recognition models are missing. Reinstall the package.")" "$FU_C_RESET"
fi
}
@@ -270,7 +270,7 @@ pfu_ui_status() {
# type bool, choice (steps through the choices) or camera
# needs a bool setting this one does nothing without; it is dimmed while
# that is off
PFU_SETTINGS=(
FU_SETTINGS=(
"group|Lock screen"
"user|LockScreen|bool|yes|Unlock with your face"
"user|ScanOnWake|bool|yes|Scan when you come back||LockScreen"
@@ -293,85 +293,85 @@ PFU_SETTINGS=(
"user|BubbleForPrompts|bool|yes|Also for sudo and admin prompts||Bubble"
)
# pfu_setting_help <Key> <value>
# fu_setting_help <Key> <value>
# What a setting does, shown under the list for the selected one.
pfu_setting_help() {
fu_setting_help() {
case "$1:$2" in
LockScreen:*) pfu_msg "Unlocks the lock screen when it sees your face. Off: only your password works there." ;;
ScanOnWake:*) pfu_msg "Scans when you press a key or move the mouse on the lock screen, and when the computer wakes up." ;;
ScanOnLock:*) pfu_msg "Scans as soon as the screen locks. Off by default: if you lock it yourself, it would unlock again right away." ;;
sudo:*) pfu_msg "sudo takes your face instead of the password. No match: you type the password as usual." ;;
polkit-1:*) pfu_msg "The password windows of Plasma and apps, for example when you install software. No match: you type the password." ;;
Liveness:heavy) pfu_msg "You have to blink or turn your head a little. This also stops printed photos." ;;
Liveness:off) pfu_msg "No check at all. Only for trying out a camera." ;;
Liveness:*) pfu_msg "Stops photos on a phone, a tablet or glossy paper. You do not have to blink. A matte printed photo can get through." ;;
Strictness:strict) pfu_msg "Fewer wrong matches, but it may not know you with glasses or in bad light." ;;
Strictness:relaxed) pfu_msg "Knows you more easily, but also somebody who looks a lot like you." ;;
Strictness:*) pfu_msg "The default. Right for most people." ;;
Attention:*) pfu_msg "Your eyes have to be open and on the screen. So it does not unlock while you look away or sleep." ;;
Camera:*) pfu_msg "Automatic takes the first normal camera. After a change, set up your face again. An infrared camera needs its light on (linux-enable-ir-emitter)." ;;
ScanSeconds:*) pfu_msg "How long the camera looks for your face before it gives up." ;;
Adapt:*) pfu_msg "After a sure match it keeps how you look now. So a new haircut or glasses need no new setup." ;;
SkipLidClosed:*) pfu_msg "No scan while the laptop is closed, for example at a desk with an external screen." ;;
Bubble:*) pfu_msg "The bubble at the top of the screen shows what the camera is doing." ;;
BubbleStyle:minimal) pfu_msg "A small pill with a lock that opens." ;;
BubbleStyle:*) pfu_msg "An island with a face that looks around, then rings and a tick." ;;
AnimationSpeed:*) pfu_msg "How fast the bubble moves." ;;
BubbleForPrompts:*) pfu_msg "Shows the bubble when sudo or an admin prompt scans your face, too." ;;
LockScreen:*) fu_msg "Unlocks the lock screen when it sees your face. Off: only your password works there." ;;
ScanOnWake:*) fu_msg "Scans when you press a key or move the mouse on the lock screen, and when the computer wakes up." ;;
ScanOnLock:*) fu_msg "Scans as soon as the screen locks. Off by default: if you lock it yourself, it would unlock again right away." ;;
sudo:*) fu_msg "sudo takes your face instead of the password. No match: you type the password as usual." ;;
polkit-1:*) fu_msg "The password windows of Plasma and apps, for example when you install software. No match: you type the password." ;;
Liveness:heavy) fu_msg "You have to blink or turn your head a little. This also stops printed photos." ;;
Liveness:off) fu_msg "No check at all. Only for trying out a camera." ;;
Liveness:*) fu_msg "Stops photos on a phone, a tablet or glossy paper. You do not have to blink. A matte printed photo can get through." ;;
Strictness:strict) fu_msg "Fewer wrong matches, but it may not know you with glasses or in bad light." ;;
Strictness:relaxed) fu_msg "Knows you more easily, but also somebody who looks a lot like you." ;;
Strictness:*) fu_msg "The default. Right for most people." ;;
Attention:*) fu_msg "Your eyes have to be open and on the screen. So it does not unlock while you look away or sleep." ;;
Camera:*) fu_msg "Automatic takes the first normal camera. After a change, set up your face again. An infrared camera needs its light on (linux-enable-ir-emitter)." ;;
ScanSeconds:*) fu_msg "How long the camera looks for your face before it gives up." ;;
Adapt:*) fu_msg "After a sure match it keeps how you look now. So a new haircut or glasses need no new setup." ;;
SkipLidClosed:*) fu_msg "No scan while the laptop is closed, for example at a desk with an external screen." ;;
Bubble:*) fu_msg "The bubble at the top of the screen shows what the camera is doing." ;;
BubbleStyle:minimal) fu_msg "A small pill with a lock that opens." ;;
BubbleStyle:*) fu_msg "An island with a face that looks around, then rings and a tick." ;;
AnimationSpeed:*) fu_msg "How fast the bubble moves." ;;
BubbleForPrompts:*) fu_msg "Shows the bubble when sudo or an admin prompt scans your face, too." ;;
esac
}
# _pfu_wrap <width> <text>
# Word wrap, into PFU_WRAP_LINES.
PFU_WRAP_LINES=()
# _fu_wrap <width> <text>
# Word wrap, into FU_WRAP_LINES.
FU_WRAP_LINES=()
_pfu_wrap() {
_fu_wrap() {
local width="$1" word line='' used=0 c i
local -a words
PFU_WRAP_LINES=()
FU_WRAP_LINES=()
read -r -a words <<< "$2"
for word in "${words[@]}"; do
_pfu_width "$word"
if (( used > 0 && used + 1 + PFU_WIDTH > width )); then
PFU_WRAP_LINES+=("$line")
_fu_width "$word"
if (( used > 0 && used + 1 + FU_WIDTH > width )); then
FU_WRAP_LINES+=("$line")
line='' used=0
fi
(( used > 0 )) && line+=' ' used=$(( used + 1 ))
if (( used + PFU_WIDTH <= width )); then
line+="$word" used=$(( used + PFU_WIDTH ))
if (( used + FU_WIDTH <= width )); then
line+="$word" used=$(( used + FU_WIDTH ))
continue
fi
# Longer than a line: Chinese and Japanese have no spaces, so break
# between any two characters.
for (( i = 0; i < ${#word}; i++ )); do
c="${word:i:1}"
_pfu_width "$c"
if (( used + PFU_WIDTH > width )); then
PFU_WRAP_LINES+=("$line")
_fu_width "$c"
if (( used + FU_WIDTH > width )); then
FU_WRAP_LINES+=("$line")
line='' used=0
fi
line+="$c" used=$(( used + PFU_WIDTH ))
line+="$c" used=$(( used + FU_WIDTH ))
done
done
[[ -n $line ]] && PFU_WRAP_LINES+=("$line")
[[ -n $line ]] && FU_WRAP_LINES+=("$line")
return 0
}
# _pfu_setting_value <scope> <Key> <default>
# The current value, in PFU_SETTING_VALUE.
PFU_SETTING_VALUE=''
# _fu_setting_value <scope> <Key> <default>
# The current value, in FU_SETTING_VALUE.
FU_SETTING_VALUE=''
_pfu_setting_value() {
_fu_setting_value() {
case "$1" in
user) _pfu_kv_lookup "$PFU_CONFIG" "$2" "$3"; PFU_SETTING_VALUE="$PFU_KV_VALUE" ;;
sys) _pfu_kv_lookup "$PFU_SYSCONFIG" "$2" "$3"; PFU_SETTING_VALUE="$PFU_KV_VALUE" ;;
pam) if pfu_pam_enabled "$2"; then PFU_SETTING_VALUE=yes; else PFU_SETTING_VALUE=no; fi ;;
user) _fu_kv_lookup "$FU_CONFIG" "$2" "$3"; FU_SETTING_VALUE="$FU_KV_VALUE" ;;
sys) _fu_kv_lookup "$FU_SYSCONFIG" "$2" "$3"; FU_SETTING_VALUE="$FU_KV_VALUE" ;;
pam) if fu_pam_enabled "$2"; then FU_SETTING_VALUE=yes; else FU_SETTING_VALUE=no; fi ;;
esac
}
# _pfu_step <current> <step> <choice>...
# _fu_step <current> <step> <choice>...
# The choice <step> (1 or -1) away from the current one, round at the ends.
_pfu_step() {
_fu_step() {
local current="$1" step="$2" i
shift 2
local -a all=("$@")
@@ -384,100 +384,100 @@ _pfu_step() {
printf '%s\n' "${all[0]}"
}
# _pfu_next_choice <current> <a,b,c> <step>
_pfu_next_choice() {
# _fu_next_choice <current> <a,b,c> <step>
_fu_next_choice() {
local -a choices
IFS=',' read -r -a choices <<< "$2"
_pfu_step "$1" "$3" "${choices[@]}"
_fu_step "$1" "$3" "${choices[@]}"
}
# _pfu_next_camera <current> <step>
_pfu_next_camera() {
pfu_cameras_load
_pfu_step "$1" "$2" auto "${PFU_CAM_PATHS[@]}"
# _fu_next_camera <current> <step>
_fu_next_camera() {
fu_cameras_load
_fu_step "$1" "$2" auto "${FU_CAM_PATHS[@]}"
}
_pfu_camera_label() {
_fu_camera_label() {
local value="$1" i
if [[ $value == auto ]]; then
for i in "${!PFU_CAM_PATHS[@]}"; do
if [[ ${PFU_CAM_PATHS[i]} == "$PFU_CAM_AUTO" ]]; then
pfu_msg "automatic (%s)" "${PFU_CAM_NAMES[i]}"
for i in "${!FU_CAM_PATHS[@]}"; do
if [[ ${FU_CAM_PATHS[i]} == "$FU_CAM_AUTO" ]]; then
fu_msg "automatic (%s)" "${FU_CAM_NAMES[i]}"
return
fi
done
pfu_msg "automatic"
fu_msg "automatic"
return
fi
for i in "${!PFU_CAM_PATHS[@]}"; do
if [[ ${PFU_CAM_PATHS[i]} == "$value" ]]; then
printf '%s' "${PFU_CAM_NAMES[i]}"
[[ ${PFU_CAM_IR[i]} == true ]] && printf ' %s' "$(pfu_msg "(infrared)")"
for i in "${!FU_CAM_PATHS[@]}"; do
if [[ ${FU_CAM_PATHS[i]} == "$value" ]]; then
printf '%s' "${FU_CAM_NAMES[i]}"
[[ ${FU_CAM_IR[i]} == true ]] && printf ' %s' "$(fu_msg "(infrared)")"
return
fi
done
printf '%s %s' "$value" "$(pfu_msg "(not connected)")"
printf '%s %s' "$value" "$(fu_msg "(not connected)")"
}
# _pfu_setting_change <scope> <Key> <type> <current> <choices> <step>
_pfu_setting_change() {
# _fu_setting_change <scope> <Key> <type> <current> <choices> <step>
_fu_setting_change() {
local scope="$1" key="$2" type="$3" current="$4" choices="$5" step="$6" next
case "$type" in
bool) if pfu_is_true "$current"; then next=no; else next=yes; fi ;;
choice) next="$(_pfu_next_choice "$current" "$choices" "$step")" ;;
camera) next="$(_pfu_next_camera "$current" "$step")" ;;
bool) if fu_is_true "$current"; then next=no; else next=yes; fi ;;
choice) next="$(_fu_next_choice "$current" "$choices" "$step")" ;;
camera) next="$(_fu_next_camera "$current" "$step")" ;;
esac
case "$scope" in
user)
pfu_config_set "$key" "$next" || pfu_bad "$(pfu_msg "Could not save the setting.")"
fu_config_set "$key" "$next" || fu_bad "$(fu_msg "Could not save the setting.")"
;;
sys)
printf '\n'
pfu_ui_cooked pfu_root set "$key" "$next" || pfu_bad "$(pfu_msg "Could not save the setting.")"
PFU_KV_CACHE=()
fu_ui_cooked fu_root set "$key" "$next" || fu_bad "$(fu_msg "Could not save the setting.")"
FU_KV_CACHE=()
;;
pam)
printf '\n'
if [[ $next == yes ]]; then
pfu_ui_cooked pfu_root pam-enable "$key" || pfu_bad "$(pfu_msg "Could not save the setting.")"
fu_ui_cooked fu_root pam-enable "$key" || fu_bad "$(fu_msg "Could not save the setting.")"
else
pfu_ui_cooked pfu_root pam-disable "$key" || pfu_bad "$(pfu_msg "Could not save the setting.")"
fu_ui_cooked fu_root pam-disable "$key" || fu_bad "$(fu_msg "Could not save the setting.")"
fi
# Remembered, so that turning face unlock off and on again brings
# it back.
case "$key" in
sudo) pfu_config_set Sudo "$next" ;;
polkit-1) pfu_config_set Polkit "$next" ;;
sudo) fu_config_set Sudo "$next" ;;
polkit-1) fu_config_set Polkit "$next" ;;
esac
;;
esac
}
# pfu_ui_settings
# fu_ui_settings
# A cursor list in groups, with what the selected setting does under it. The
# frame is assembled in memory and written once, and everything constant is
# resolved before the loop.
pfu_ui_settings() {
fu_ui_settings() {
local -a scopes=() keys=() types=() defaults=() labels=() widths=() choices=() needs=() values=() rows=()
local spec scope key type default label choice need locale i j frame row pad dirty=1 cursor=0 shown
local width=0 wrap cols
locale="$(pfu_ui_locale)"
for spec in "${PFU_SETTINGS[@]}"; do
locale="$(fu_ui_locale)"
for spec in "${FU_SETTINGS[@]}"; do
IFS='|' read -r scope key type default label choice need <<< "$spec"
# A heading has its label where the key would be.
[[ $scope == group ]] && label="$key" key=''
scopes+=("$scope"); keys+=("$key"); types+=("$type"); defaults+=("$default")
choices+=("$choice"); needs+=("$need")
pfu_msg_into "$locale" "$label"
labels+=("$PFU_MSG_RESULT")
_pfu_width "$PFU_MSG_RESULT"
widths+=("$PFU_WIDTH")
fu_msg_into "$locale" "$label"
labels+=("$FU_MSG_RESULT")
_fu_width "$FU_MSG_RESULT"
widths+=("$FU_WIDTH")
if [[ $scope != group ]]; then
rows+=($(( ${#keys[@]} - 1 )))
(( PFU_WIDTH > width )) && width=$PFU_WIDTH
(( FU_WIDTH > width )) && width=$FU_WIDTH
fi
done
local count=${#rows[@]}
@@ -491,77 +491,77 @@ pfu_ui_settings() {
rule="${rule// /─}"
local title hint shared l_on l_off
pfu_msg_into "$locale" "Settings"; title="$PFU_MSG_RESULT"
pfu_msg_into "$locale" "↑↓ select ←→ or Space: change q: back"; hint="$PFU_MSG_RESULT"
pfu_msg_into "$locale" "for all users, asks for your password"; shared="$PFU_MSG_RESULT"
pfu_msg_into "$locale" "ON"; l_on="$PFU_MSG_RESULT"
pfu_msg_into "$locale" "OFF"; l_off="$PFU_MSG_RESULT"
fu_msg_into "$locale" "Settings"; title="$FU_MSG_RESULT"
fu_msg_into "$locale" "↑↓ select ←→ or Space: change q: back"; hint="$FU_MSG_RESULT"
fu_msg_into "$locale" "for all users, asks for your password"; shared="$FU_MSG_RESULT"
fu_msg_into "$locale" "ON"; l_on="$FU_MSG_RESULT"
fu_msg_into "$locale" "OFF"; l_off="$FU_MSG_RESULT"
local clearseq
clearseq="$(clear 2>/dev/null)" || clearseq=$'\033[H\033[2J'
pfu_cameras_load
fu_cameras_load
while true; do
if (( dirty )); then
PFU_KV_CACHE=()
FU_KV_CACHE=()
declare -A current=()
for i in "${rows[@]}"; do
_pfu_setting_value "${scopes[i]}" "${keys[i]}" "${defaults[i]}"
values[i]="$PFU_SETTING_VALUE"
current[${keys[i]}]="$PFU_SETTING_VALUE"
_fu_setting_value "${scopes[i]}" "${keys[i]}" "${defaults[i]}"
values[i]="$FU_SETTING_VALUE"
current[${keys[i]}]="$FU_SETTING_VALUE"
done
dirty=0
fi
frame="$clearseq"$'\n'"${PFU_C_BOLD}${PFU_C_BLUE} ${title}${PFU_C_RESET}"$'\n'
frame="$clearseq"$'\n'"${FU_C_BOLD}${FU_C_BLUE} ${title}${FU_C_RESET}"$'\n'
local selected=${rows[cursor]} marker before after dim
for i in "${!keys[@]}"; do
if [[ ${scopes[i]} == group ]]; then
frame+=$'\n'" ${PFU_C_BOLD}${labels[i]}${PFU_C_RESET}"
frame+=$'\n'" ${FU_C_BOLD}${labels[i]}${FU_C_RESET}"
# The next row says whose settings these are.
[[ ${scopes[i + 1]} != user ]] && frame+=" ${PFU_C_DIM}${shared}${PFU_C_RESET}"
[[ ${scopes[i + 1]} != user ]] && frame+=" ${FU_C_DIM}${shared}${FU_C_RESET}"
frame+=$'\n'
continue
fi
case "${types[i]}" in
bool)
if pfu_is_true "${values[i]}"; then
shown="${PFU_C_GREEN}${l_on}${PFU_C_RESET}"
if fu_is_true "${values[i]}"; then
shown="${FU_C_GREEN}${l_on}${FU_C_RESET}"
else
shown="${PFU_C_DIM}${l_off}${PFU_C_RESET}"
shown="${FU_C_DIM}${l_off}${FU_C_RESET}"
fi
;;
camera) shown="$(_pfu_camera_label "${values[i]}")" ;;
*) shown="$(pfu_value_label "${keys[i]}" "${values[i]}")" ;;
camera) shown="$(_fu_camera_label "${values[i]}")" ;;
*) shown="$(fu_value_label "${keys[i]}" "${values[i]}")" ;;
esac
# Arrows on the selected choice: left and right step through it.
before=' ' after=''
if (( i == selected )) && [[ ${types[i]} != bool ]]; then
before="${PFU_C_BLUE}◂${PFU_C_RESET} " after=" ${PFU_C_BLUE}▸${PFU_C_RESET}"
before="${FU_C_BLUE}◂${FU_C_RESET} " after=" ${FU_C_BLUE}▸${FU_C_RESET}"
fi
dim=''
[[ -n ${needs[i]} ]] && ! pfu_is_true "${current[${needs[i]}]}" && dim="$PFU_C_DIM"
[[ -n ${needs[i]} ]] && ! fu_is_true "${current[${needs[i]}]}" && dim="$FU_C_DIM"
pad=$(( width + 2 - widths[i] ))
if (( i == selected )); then marker="${PFU_C_BLUE}▸${PFU_C_RESET} "; else marker=' '; fi
printf -v row ' %s%s%s%s%*s%s%s%s' "$marker" "$dim" "${labels[i]}" "$PFU_C_RESET" "$pad" '' "$before" "$dim$shown$PFU_C_RESET" "$after"
if (( i == selected )); then marker="${FU_C_BLUE}▸${FU_C_RESET} "; else marker=' '; fi
printf -v row ' %s%s%s%s%*s%s%s%s' "$marker" "$dim" "${labels[i]}" "$FU_C_RESET" "$pad" '' "$before" "$dim$shown$FU_C_RESET" "$after"
frame+="$row"$'\n'
done
# What the selected setting does, in a box of fixed height so the
# screen does not jump while moving through the list.
_pfu_wrap "$wrap" "$(pfu_setting_help "${keys[selected]}" "${values[selected]}")"
frame+=$'\n'" ${PFU_C_DIM}${rule}${PFU_C_RESET}"$'\n'
_fu_wrap "$wrap" "$(fu_setting_help "${keys[selected]}" "${values[selected]}")"
frame+=$'\n'" ${FU_C_DIM}${rule}${FU_C_RESET}"$'\n'
for j in 0 1 2; do
frame+=" ${PFU_WRAP_LINES[j]:-}"$'\n'
frame+=" ${FU_WRAP_LINES[j]:-}"$'\n'
done
frame+=$'\n'" ${PFU_C_DIM}${hint}${PFU_C_RESET}"$'\n'
_pfu_ui_take_notices
frame+="$PFU_UI_NOTICE_TEXT"
frame+=$'\n'" ${FU_C_DIM}${hint}${FU_C_RESET}"$'\n'
_fu_ui_take_notices
frame+="$FU_UI_NOTICE_TEXT"
printf '%s' "$frame"
key="$(pfu_read_key)" || return 0
key="$(fu_read_key)" || return 0
case "$key" in
up|k) cursor=$(( (cursor - 1 + count) % count )) ;;
@@ -569,7 +569,7 @@ pfu_ui_settings() {
space|enter|right|l|left|h)
local step=1
[[ $key == left || $key == h ]] && step=-1
_pfu_setting_change "${scopes[selected]}" "${keys[selected]}" "${types[selected]}" "${values[selected]}" "${choices[selected]}" "$step"
_fu_setting_change "${scopes[selected]}" "${keys[selected]}" "${types[selected]}" "${values[selected]}" "${choices[selected]}" "$step"
dirty=1
;;
q|Q|escape) return 0 ;;
@@ -582,72 +582,72 @@ pfu_ui_settings() {
# Faces
# ---------------------------------------------------------------------------
pfu_ui_faces() {
fu_ui_faces() {
local key frame row pad i cursor=0 count locale shown
locale="$(pfu_ui_locale)"
locale="$(fu_ui_locale)"
local title hint empty l_on l_off
pfu_msg_into "$locale" "Faces"; title="$PFU_MSG_RESULT"
fu_msg_into "$locale" "Faces"; title="$FU_MSG_RESULT"
# TRANSLATORS: keep the letters, they are the keys.
pfu_msg_into "$locale" "Up/Down: select, Space: on/off, r: rename, d: delete, a: add, q: back"; hint="$PFU_MSG_RESULT"
pfu_msg_into "$locale" "No face is set up yet. Press a to add one."; empty="$PFU_MSG_RESULT"
pfu_msg_into "$locale" "on"; l_on="$PFU_MSG_RESULT"
pfu_msg_into "$locale" "off"; l_off="$PFU_MSG_RESULT"
fu_msg_into "$locale" "Up/Down: select, Space: on/off, r: rename, d: delete, a: add, q: back"; hint="$FU_MSG_RESULT"
fu_msg_into "$locale" "No face is set up yet. Press a to add one."; empty="$FU_MSG_RESULT"
fu_msg_into "$locale" "on"; l_on="$FU_MSG_RESULT"
fu_msg_into "$locale" "off"; l_off="$FU_MSG_RESULT"
local clearseq
clearseq="$(clear 2>/dev/null)" || clearseq=$'\033[H\033[2J'
while true; do
pfu_faces_load
count=${#PFU_FACE_IDS[@]}
fu_faces_load
count=${#FU_FACE_IDS[@]}
(( cursor >= count )) && cursor=$(( count > 0 ? count - 1 : 0 ))
frame="$clearseq"$'\n'"${PFU_C_BOLD}${PFU_C_BLUE} ${title}${PFU_C_RESET}"$'\n\n'
frame="$clearseq"$'\n'"${FU_C_BOLD}${FU_C_BLUE} ${title}${FU_C_RESET}"$'\n\n'
if (( count == 0 )); then
frame+=" ${PFU_C_DIM}${empty}${PFU_C_RESET}"$'\n'
frame+=" ${FU_C_DIM}${empty}${FU_C_RESET}"$'\n'
fi
local marker selected="${PFU_C_BLUE}▸${PFU_C_RESET} " samples
for i in "${!PFU_FACE_IDS[@]}"; do
if [[ ${PFU_FACE_ON[i]} == true ]]; then shown="${PFU_C_GREEN}${l_on}${PFU_C_RESET}"; else shown="${PFU_C_DIM}${l_off}${PFU_C_RESET}"; fi
samples="$(pfu_msg "%s samples, %s learned" "${PFU_FACE_SAMPLES[i]}" "${PFU_FACE_LEARNED[i]}")"
_pfu_width "${PFU_FACE_NAMES[i]}"
pad=$(( 30 - PFU_WIDTH ))
local marker selected="${FU_C_BLUE}▸${FU_C_RESET} " samples
for i in "${!FU_FACE_IDS[@]}"; do
if [[ ${FU_FACE_ON[i]} == true ]]; then shown="${FU_C_GREEN}${l_on}${FU_C_RESET}"; else shown="${FU_C_DIM}${l_off}${FU_C_RESET}"; fi
samples="$(fu_msg "%s samples, %s learned" "${FU_FACE_SAMPLES[i]}" "${FU_FACE_LEARNED[i]}")"
_fu_width "${FU_FACE_NAMES[i]}"
pad=$(( 30 - FU_WIDTH ))
(( pad < 0 )) && pad=0
if (( i == cursor )); then marker="$selected"; else marker=' '; fi
printf -v row ' %s%s%*s %s %s%s%s' "$marker" "${PFU_FACE_NAMES[i]}" "$pad" '' "$shown" "$PFU_C_DIM" "$samples" "$PFU_C_RESET"
printf -v row ' %s%s%*s %s %s%s%s' "$marker" "${FU_FACE_NAMES[i]}" "$pad" '' "$shown" "$FU_C_DIM" "$samples" "$FU_C_RESET"
frame+="$row"$'\n'
done
frame+=$'\n'" ${PFU_C_DIM}${hint}${PFU_C_RESET}"$'\n'
_pfu_ui_take_notices
frame+="$PFU_UI_NOTICE_TEXT"
frame+=$'\n'" ${FU_C_DIM}${hint}${FU_C_RESET}"$'\n'
_fu_ui_take_notices
frame+="$FU_UI_NOTICE_TEXT"
printf '%s' "$frame"
key="$(pfu_read_key)" || return 0
key="$(fu_read_key)" || return 0
case "$key" in
up|k) (( count )) && cursor=$(( (cursor - 1 + count) % count )) ;;
down|j) (( count )) && cursor=$(( (cursor + 1) % count )) ;;
space|enter)
(( count )) || continue
if [[ ${PFU_FACE_ON[cursor]} == true ]]; then
pfu_ctl disable "${PFU_FACE_IDS[cursor]}" > /dev/null
if [[ ${FU_FACE_ON[cursor]} == true ]]; then
fu_ctl disable "${FU_FACE_IDS[cursor]}" > /dev/null
else
pfu_ctl enable "${PFU_FACE_IDS[cursor]}" > /dev/null
fu_ctl enable "${FU_FACE_IDS[cursor]}" > /dev/null
fi
;;
r|R)
(( count )) || continue
printf '\n %s ' "$(pfu_msg "New name:")"
if pfu_ui_read_line "${PFU_FACE_NAMES[cursor]}" && [[ -n $PFU_LINE_RESULT ]]; then
pfu_ctl rename "${PFU_FACE_IDS[cursor]}" "$PFU_LINE_RESULT" > /dev/null
printf '\n %s ' "$(fu_msg "New name:")"
if fu_ui_read_line "${FU_FACE_NAMES[cursor]}" && [[ -n $FU_LINE_RESULT ]]; then
fu_ctl rename "${FU_FACE_IDS[cursor]}" "$FU_LINE_RESULT" > /dev/null
fi
;;
d|D)
(( count )) || continue
if pfu_ui_confirm "$(pfu_msg "Delete \"%s\"?" "${PFU_FACE_NAMES[cursor]}")"; then
pfu_ctl remove "${PFU_FACE_IDS[cursor]}" > /dev/null
if fu_ui_confirm "$(fu_msg "Delete \"%s\"?" "${FU_FACE_NAMES[cursor]}")"; then
fu_ctl remove "${FU_FACE_IDS[cursor]}" > /dev/null
fi
;;
a|A) pfu_ui_cooked pfu_do_setup ;;
a|A) fu_ui_cooked fu_do_setup ;;
q|Q|escape) return 0 ;;
*) ;;
esac
@@ -658,11 +658,11 @@ pfu_ui_faces() {
# The menu
# ---------------------------------------------------------------------------
pfu_ui_menu() {
fu_ui_menu() {
local choice keep=0
pfu_ui_term_raw
trap 'pfu_ui_term_restore' EXIT INT TERM
fu_ui_term_raw
trap 'fu_ui_term_restore' EXIT INT TERM
while true; do
# After a test the menu goes under what the camera saw instead.
@@ -671,21 +671,21 @@ pfu_ui_menu() {
else
clear 2>/dev/null || true
fi
pfu_head " $PFU_PRETTY"
pfu_ui_status
fu_head " $FU_PRETTY"
fu_ui_status
printf '\n'
printf ' [1] %s\n' "$(pfu_msg "Turn face unlock on or off")"
printf ' [2] %s\n' "$(pfu_msg "Add a face")"
printf ' [3] %s\n' "$(pfu_msg "Faces")"
printf ' [4] %s\n' "$(pfu_msg "Settings")"
printf ' [5] %s\n' "$(pfu_msg "Try it")"
printf ' [q] %s\n' "$(pfu_msg "Quit")"
_pfu_ui_take_notices
printf '%s' "$PFU_UI_NOTICE_TEXT"
printf ' [1] %s\n' "$(fu_msg "Turn face unlock on or off")"
printf ' [2] %s\n' "$(fu_msg "Add a face")"
printf ' [3] %s\n' "$(fu_msg "Faces")"
printf ' [4] %s\n' "$(fu_msg "Settings")"
printf ' [5] %s\n' "$(fu_msg "Try it")"
printf ' [q] %s\n' "$(fu_msg "Quit")"
_fu_ui_take_notices
printf '%s' "$FU_UI_NOTICE_TEXT"
printf '\n > '
choice="$(pfu_read_key)" || {
printf '\n'; pfu_ui_term_restore; trap - EXIT INT TERM; return 0
choice="$(fu_read_key)" || {
printf '\n'; fu_ui_term_restore; trap - EXIT INT TERM; return 0
}
case "$choice" in
enter|space|up|down|left|right|escape) choice='' ;;
@@ -694,24 +694,24 @@ pfu_ui_menu() {
case "$choice" in
1)
pfu_config_load
fu_config_load
if [[ $CFG_ENABLED == yes ]]; then
pfu_ui_cooked pfu_do_disable
fu_ui_cooked fu_do_disable
else
pfu_ui_cooked pfu_do_enable
fu_ui_cooked fu_do_enable
fi
;;
2) pfu_ui_cooked pfu_do_setup ;;
3) pfu_ui_faces ;;
4) pfu_ui_settings ;;
2) fu_ui_cooked fu_do_setup ;;
3) fu_ui_faces ;;
4) fu_ui_settings ;;
5)
printf '\n'
pfu_ui_cooked pfu_test
fu_ui_cooked fu_test
# Already on screen, with the rest of the test.
PFU_UI_NOTICES=()
FU_UI_NOTICES=()
keep=1
;;
q|Q) pfu_ui_term_restore; trap - EXIT INT TERM; return 0 ;;
q|Q) fu_ui_term_restore; trap - EXIT INT TERM; return 0 ;;
# Anything else (Enter, arrow keys, stray characters) just
# redraws. Escape is deliberately not a quit key, so a mistyped
# arrow key cannot close the menu.
+49 -49
View File
@@ -10,7 +10,7 @@
#
# The line that goes in:
#
# -auth sufficient /usr/lib/security/pam_plasma_face_unlock.so
# -auth sufficient /usr/lib/security/pam_face_unlock.so
#
# "sufficient": a match lets the person in, anything else falls through to the
# lines below as if this one were not there. The dash makes PAM skip it
@@ -26,24 +26,24 @@
# an update to that file still counts.
# Undoing takes out exactly those lines, or that file.
PFU_PAM_MARK="# plasma-face-unlock: the face first, the password if that does not work"
PFU_PAM_WRAPPER_MARK="# Written by plasma-face-unlock."
PFU_PAM_SERVICES=(sudo polkit-1)
FU_PAM_MARK="# face-unlock: the face first, the password if that does not work"
FU_PAM_WRAPPER_MARK="# Written by face-unlock."
FU_PAM_SERVICES=(sudo polkit-1)
# Overridable for the tests only; the root side never takes them from the
# environment (see the top of plasma-face-unlock).
PFU_PAM_ETC_DIR="${PFU_PAM_ETC_DIR:-/etc/pam.d}"
read -r -a PFU_PAM_VENDOR_DIRS <<< "${PFU_PAM_VENDOR_DIRS:-/usr/lib/pam.d /usr/share/pam.d /lib/pam.d}"
# environment (see the top of face-unlock).
FU_PAM_ETC_DIR="${FU_PAM_ETC_DIR:-/etc/pam.d}"
read -r -a FU_PAM_VENDOR_DIRS <<< "${FU_PAM_VENDOR_DIRS:-/usr/lib/pam.d /usr/share/pam.d /lib/pam.d}"
pfu_pam_etc() {
printf '%s/%s\n' "$PFU_PAM_ETC_DIR" "$1"
fu_pam_etc() {
printf '%s/%s\n' "$FU_PAM_ETC_DIR" "$1"
}
# pfu_pam_vendor <service>
# fu_pam_vendor <service>
# The distribution's own copy, when it keeps one outside /etc.
pfu_pam_vendor() {
fu_pam_vendor() {
local dir
for dir in "${PFU_PAM_VENDOR_DIRS[@]}"; do
for dir in "${FU_PAM_VENDOR_DIRS[@]}"; do
if [[ -f $dir/$1 ]]; then
printf '%s\n' "$dir/$1"
return 0
@@ -52,24 +52,24 @@ pfu_pam_vendor() {
return 1
}
pfu_pam_line() {
printf -- '-auth sufficient %s\n' "$PFU_PAM_MODULE"
fu_pam_line() {
printf -- '-auth sufficient %s\n' "$FU_PAM_MODULE"
}
# pfu_pam_enabled <service>
pfu_pam_enabled() {
# fu_pam_enabled <service>
fu_pam_enabled() {
local file
file="$(pfu_pam_etc "$1")"
[[ -r $file ]] && grep -q 'pam_plasma_face_unlock\.so' "$file"
file="$(fu_pam_etc "$1")"
[[ -r $file ]] && grep -q 'pam_face_unlock\.so' "$file"
}
# pfu_pam_insert <file>
# fu_pam_insert <file>
# Prints the file with the line added before its first auth line. Debian and
# Ubuntu have none in sudo's file, only "@include common-auth", and that
# counts as one: after it the password has already been asked for. A file
# with neither (which would be odd for either service) gets it at the end.
pfu_pam_insert() {
awk -v mark="$PFU_PAM_MARK" -v line="$(pfu_pam_line)" '
fu_pam_insert() {
awk -v mark="$FU_PAM_MARK" -v line="$(fu_pam_line)" '
!done && ($0 ~ /^[[:space:]]*-?auth[[:space:]]/ || $0 ~ /^[[:space:]]*@include[[:space:]]+common-auth([[:space:]]|$)/) {
print mark
print line
@@ -85,35 +85,35 @@ pfu_pam_insert() {
' "$1"
}
# pfu_pam_remove_lines <file>
# fu_pam_remove_lines <file>
# Prints the file without our comment and our line.
pfu_pam_remove_lines() {
awk -v mark="$PFU_PAM_MARK" '
fu_pam_remove_lines() {
awk -v mark="$FU_PAM_MARK" '
$0 == mark { next }
/pam_plasma_face_unlock\.so/ { next }
/pam_face_unlock\.so/ { next }
{ print }
' "$1"
}
pfu_pam_wrapper() {
fu_pam_wrapper() {
local vendor="$1"
printf '#%%PAM-1.0\n'
printf '%s The face first, then everything\n' "$PFU_PAM_WRAPPER_MARK"
printf '%s The face first, then everything\n' "$FU_PAM_WRAPPER_MARK"
printf '# %s does for this service. Removed again by\n' "$vendor"
printf '# "plasma-face-unlock disable" or from its settings.\n\n'
pfu_pam_line
printf '# "face-unlock disable" or from its settings.\n\n'
fu_pam_line
printf 'auth include %s\n' "$vendor"
printf 'account include %s\n' "$vendor"
printf 'password include %s\n' "$vendor"
printf 'session include %s\n' "$vendor"
}
# _pfu_pam_replace <file> <content>
# _fu_pam_replace <file> <content>
# Writes the new file next to the old one and swaps it in, with the old one's
# owner and mode. A half-written PAM file is a locked-out machine.
_pfu_pam_replace() {
_fu_pam_replace() {
local file="$1" content="$2" tmp
tmp="$(mktemp "${file}.pfu.XXXXXX")" || return 1
tmp="$(mktemp "${file}.fu.XXXXXX")" || return 1
printf '%s\n' "$content" > "$tmp" || { rm -f "$tmp"; return 1; }
if [[ -e $file ]]; then
chown --reference="$file" "$tmp" 2>/dev/null
@@ -124,40 +124,40 @@ _pfu_pam_replace() {
mv -f "$tmp" "$file"
}
# pfu_pam_enable <service> (root)
pfu_pam_enable() {
# fu_pam_enable <service> (root)
fu_pam_enable() {
local service="$1" file vendor content
file="$(pfu_pam_etc "$service")"
file="$(fu_pam_etc "$service")"
[[ -e $PFU_PAM_MODULE ]] || { pfu_bad "$(pfu_msg "The PAM module is not installed at %s." "$PFU_PAM_MODULE")"; return 1; }
pfu_pam_enabled "$service" && return 0
[[ -e $FU_PAM_MODULE ]] || { fu_bad "$(fu_msg "The PAM module is not installed at %s." "$FU_PAM_MODULE")"; return 1; }
fu_pam_enabled "$service" && return 0
if [[ -f $file ]]; then
content="$(pfu_pam_insert "$file")" || return 1
elif vendor="$(pfu_pam_vendor "$service")"; then
content="$(pfu_pam_wrapper "$vendor")"
content="$(fu_pam_insert "$file")" || return 1
elif vendor="$(fu_pam_vendor "$service")"; then
content="$(fu_pam_wrapper "$vendor")"
else
pfu_bad "$(pfu_msg "There is no PAM configuration for %s on this system." "$service")"
fu_bad "$(fu_msg "There is no PAM configuration for %s on this system." "$service")"
return 1
fi
_pfu_pam_replace "$file" "$content"
_fu_pam_replace "$file" "$content"
}
# pfu_pam_disable <service> (root)
pfu_pam_disable() {
# fu_pam_disable <service> (root)
fu_pam_disable() {
local service="$1" file content
file="$(pfu_pam_etc "$service")"
file="$(fu_pam_etc "$service")"
pfu_pam_enabled "$service" || return 0
fu_pam_enabled "$service" || return 0
if head -n 3 "$file" | grep -qF "$PFU_PAM_WRAPPER_MARK"; then
if head -n 3 "$file" | grep -qF "$FU_PAM_WRAPPER_MARK"; then
# Ours from the first line to the last. Without it the distribution's
# own copy is in charge again.
rm -f -- "$file"
return
fi
content="$(pfu_pam_remove_lines "$file")" || return 1
_pfu_pam_replace "$file" "$content"
content="$(fu_pam_remove_lines "$file")" || return 1
_fu_pam_replace "$file" "$content"
}
+34 -34
View File
@@ -8,17 +8,17 @@
# is already looking at. The verbs are all there is: there is no way to hand
# the root side a command of one's own.
#
# --root set <Key> <Value> one line of /etc/plasma-face-unlock/config
# --root set <Key> <Value> one line of /etc/face-unlock/config
# --root pam-enable <service> sudo or polkit-1, see pam.sh
# --root pam-disable <service>
# --root socket-enable start the daemon's socket, and at boot
# --root socket-disable
PFU_SELF="${PFU_SELF:-$(readlink -f "${BASH_SOURCE[1]:-$0}")}"
FU_SELF="${FU_SELF:-$(readlink -f "${BASH_SOURCE[1]:-$0}")}"
# What each system setting may be set to. Anything else is refused on the root
# side, whatever the menu sent.
declare -A PFU_SYS_VALID=(
declare -A FU_SYS_VALID=(
[Camera]='^(auto|/dev/video[0-9]+)$'
[Liveness]='^(off|light|heavy)$'
[Strictness]='^(relaxed|normal|strict)$'
@@ -30,63 +30,63 @@ declare -A PFU_SYS_VALID=(
[LockoutMinutes]='^[1-9][0-9]{0,3}$'
)
# pfu_root <verb> [args...]
pfu_root() {
# fu_root <verb> [args...]
fu_root() {
local candidate
if [[ $EUID -eq 0 ]]; then
pfu_root_verb "$@"
fu_root_verb "$@"
return
fi
for candidate in sudo run0 doas; do
if pfu_have "$candidate"; then
"$candidate" "$PFU_SELF" --root "$@"
if fu_have "$candidate"; then
"$candidate" "$FU_SELF" --root "$@"
return
fi
done
pfu_bad "$(pfu_msg "This needs root, and neither sudo, run0 nor doas is installed.")"
fu_bad "$(fu_msg "This needs root, and neither sudo, run0 nor doas is installed.")"
return 1
}
pfu_root_verb() {
fu_root_verb() {
local verb="${1:-}"
[[ $# -gt 0 ]] && shift
if [[ $EUID -ne 0 ]]; then
pfu_bad "$(pfu_msg "This command has to run as root.")"
fu_bad "$(fu_msg "This command has to run as root.")"
return 2
fi
case "$verb" in
set)
local key="${1:-}" value="${2:-}"
if [[ -z ${PFU_SYS_VALID[$key]+set} || ! $value =~ ${PFU_SYS_VALID[$key]} ]]; then
pfu_bad "$(pfu_msg "Not a valid setting: %s=%s" "$key" "$value")"
if [[ -z ${FU_SYS_VALID[$key]+set} || ! $value =~ ${FU_SYS_VALID[$key]} ]]; then
fu_bad "$(fu_msg "Not a valid setting: %s=%s" "$key" "$value")"
return 1
fi
pfu_kv_set "$PFU_SYSCONFIG" "$key" "$value" "$PFU_PRETTY (system settings)" || return 1
chmod 0644 "$PFU_SYSCONFIG"
fu_kv_set "$FU_SYSCONFIG" "$key" "$value" "$FU_PRETTY (system settings)" || return 1
chmod 0644 "$FU_SYSCONFIG"
;;
pam-enable|pam-disable)
local service="${1:-}" known=0 s
for s in "${PFU_PAM_SERVICES[@]}"; do
for s in "${FU_PAM_SERVICES[@]}"; do
[[ $s == "$service" ]] && known=1
done
(( known )) || { pfu_bad "$(pfu_msg "Not a service this can be used for: %s" "$service")"; return 1; }
(( known )) || { fu_bad "$(fu_msg "Not a service this can be used for: %s" "$service")"; return 1; }
if [[ $verb == pam-enable ]]; then
pfu_pam_enable "$service"
fu_pam_enable "$service"
else
pfu_pam_disable "$service"
fu_pam_disable "$service"
fi
;;
socket-enable)
systemctl enable --now "$PFU_UNIT_SOCKET" > /dev/null 2>&1
systemctl enable --now "$FU_UNIT_SOCKET" > /dev/null 2>&1
;;
socket-disable)
systemctl disable --now "$PFU_UNIT_SOCKET" > /dev/null 2>&1
systemctl disable --now "$FU_UNIT_SOCKET" > /dev/null 2>&1
;;
*)
pfu_bad "$(pfu_msg "Unknown command: %s" "$verb")"
fu_bad "$(fu_msg "Unknown command: %s" "$verb")"
return 1
;;
esac
@@ -96,27 +96,27 @@ pfu_root_verb() {
# The two services
# ---------------------------------------------------------------------------
pfu_socket_enabled() {
systemctl is-enabled --quiet "$PFU_UNIT_SOCKET" 2>/dev/null
fu_socket_enabled() {
systemctl is-enabled --quiet "$FU_UNIT_SOCKET" 2>/dev/null
}
pfu_agent_available() {
pfu_have systemctl && [[ -n ${XDG_RUNTIME_DIR:-} ]]
fu_agent_available() {
fu_have systemctl && [[ -n ${XDG_RUNTIME_DIR:-} ]]
}
pfu_agent_enabled() {
systemctl --user is-enabled --quiet "$PFU_UNIT_AGENT" 2>/dev/null
fu_agent_enabled() {
systemctl --user is-enabled --quiet "$FU_UNIT_AGENT" 2>/dev/null
}
pfu_agent_running() {
systemctl --user is-active --quiet "$PFU_UNIT_AGENT" 2>/dev/null
fu_agent_running() {
systemctl --user is-active --quiet "$FU_UNIT_AGENT" 2>/dev/null
}
pfu_agent_enable() {
fu_agent_enable() {
systemctl --user daemon-reload > /dev/null 2>&1 || true
systemctl --user enable --now "$PFU_UNIT_AGENT" > /dev/null 2>&1
systemctl --user enable --now "$FU_UNIT_AGENT" > /dev/null 2>&1
}
pfu_agent_disable() {
systemctl --user disable --now "$PFU_UNIT_AGENT" > /dev/null 2>&1 || true
fu_agent_disable() {
systemctl --user disable --now "$FU_UNIT_AGENT" > /dev/null 2>&1 || true
}
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
// pam_plasma_face_unlock: face unlock for sudo and for admin prompts.
// pam_face_unlock: face unlock for sudo and for admin prompts.
//
// All the vision is in the daemon. This asks it to scan for the user and
// turns the answer into a PAM result, and it is meant to sit first in a stack
@@ -49,7 +49,7 @@
#include <systemd/sd-login.h>
#endif
#define DOMAIN PFU_NAME
#define DOMAIN FU_NAME
// Marks a message for translation. say() translates it.
#define _(s) (s)
@@ -62,7 +62,7 @@ struct options {
static void parse_options(struct options *o, int argc, const char **argv)
{
o->socket = PFU_SOCKET;
o->socket = FU_SOCKET;
o->purpose = NULL;
o->timeout = 25;
o->debug = false;
@@ -229,7 +229,7 @@ __attribute__((visibility("default"))) PAM_EXTERN int pam_sm_authenticate(pam_ha
{
struct options o;
parse_options(&o, argc, argv);
bindtextdomain(DOMAIN, PFU_LOCALEDIR);
bindtextdomain(DOMAIN, FU_LOCALEDIR);
const char *user = NULL;
if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || !nonempty(user)) {
-229
View File
@@ -1,229 +0,0 @@
#!/usr/bin/env bash
#
# plasma-face-unlock: face unlock for KDE Plasma
#
# Look at the screen and it unlocks, the way a phone does. The lock screen,
# sudo in a terminal and the admin password prompts can all take a face
# instead of a password, with a check that it is a face and not a photo of one.
#
# This is the front end: the menu and the commands. The camera, the face data
# and the decision are the daemon's (plasma-face-unlockd, running as root),
# the lock screen and the bubble at the top of the screen are the agent's
# (plasma-face-unlock-agent, in the session), and sudo and polkit reach the
# daemon through a PAM module. See the man page for how the pieces fit.
#
# Copyright (C) 2026 Felitendo
# SPDX-License-Identifier: GPL-3.0-or-later
set -uo pipefail
PFU_SELF="$(readlink -f "${BASH_SOURCE[0]}")"
# Run as root (through sudo from the menu), only what was installed counts.
# An environment that points the libraries somewhere else is ignored then.
if [[ $EUID -eq 0 ]]; then
unset PFU_LIBDIR PFU_LIBEXECDIR PFU_LOCALEDIR PFU_PAMDIR PFU_CTL PFU_AGENT PFU_PAM_MODULE PFU_SYSCONFIG \
PFU_PAM_ETC_DIR PFU_PAM_VENDOR_DIRS
fi
PFU_LIBDIR="${PFU_LIBDIR:-@LIBDIR@}"
for _mod in common config daemon pam system menu; do
# shellcheck source=/dev/null
if ! source "$PFU_LIBDIR/$_mod.sh"; then
printf 'plasma-face-unlock: cannot load %s/%s.sh\n' "$PFU_LIBDIR" "$_mod" >&2
exit 14
fi
done
unset _mod
# ---------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------
# The daemon is started by its socket. Enabling the socket is the one thing
# that needs root once per machine.
pfu_ensure_service() {
pfu_status_load && return 0
if ! pfu_socket_enabled; then
pfu_say " $(pfu_msg "Face unlock's service has to be switched on once for this computer. That needs your password.")"
pfu_root socket-enable || return 1
sleep 0.3
fi
pfu_status_load && return 0
pfu_bad "$(pfu_reason_text unreachable)"
pfu_note "$(pfu_msg "Check it with: systemctl status %s" "$PFU_UNIT_SOCKET")"
return 1
}
pfu_do_setup() {
local name="${1:-}" rc
pfu_ensure_service || return 1
if [[ -z ${WAYLAND_DISPLAY:-} && -z ${DISPLAY:-} ]]; then
pfu_bad "$(pfu_msg "Setting up a face needs the camera picture on screen. Run this inside your Plasma session.")"
return 1
fi
pfu_say " $(pfu_msg "The setup window is open. Follow it there.")"
if [[ -n $name ]]; then
"$PFU_AGENT" --enroll --name "$name" > /dev/null 2>&1
else
"$PFU_AGENT" --enroll > /dev/null 2>&1
fi
rc=$?
if (( rc == 0 )); then
pfu_ok "$(pfu_msg "The face is set up.")"
pfu_config_load
if [[ $CFG_ENABLED != yes ]]; then
pfu_note "$(pfu_msg "Face unlock is still off. Turn it on with [1] or \`%s enable\`." "$PFU_NAME")"
fi
return 0
fi
pfu_note "$(pfu_msg "No face was added.")"
return 1
}
pfu_do_enable() {
pfu_ensure_service || return 1
pfu_faces_load
if (( ${#PFU_FACE_IDS[@]} == 0 )); then
pfu_say " $(pfu_msg "First, set up your face.")"
pfu_do_setup || return 1
fi
pfu_config_set Enabled yes || { pfu_bad "$(pfu_msg "Could not save the setting.")"; return 1; }
pfu_config_load
if pfu_agent_available; then
pfu_agent_enable || pfu_bad "$(pfu_msg "Could not start the lock screen agent.")"
else
pfu_note "$(pfu_msg "No systemd user session, so the lock screen agent was not started.")"
fi
# What was on the last time face unlock was on.
[[ $CFG_SUDO == yes ]] && ! pfu_pam_enabled sudo && pfu_root pam-enable sudo
[[ $CFG_POLKIT == yes ]] && ! pfu_pam_enabled polkit-1 && pfu_root pam-enable polkit-1
pfu_ok "$(pfu_msg "Face unlock is on. Lock the screen and look at it to try.")"
if [[ $CFG_SUDO != yes && $CFG_POLKIT != yes ]]; then
pfu_note "$(pfu_msg "It can do sudo and admin prompts too. See Settings.")"
fi
}
pfu_do_disable() {
pfu_config_set Enabled no || { pfu_bad "$(pfu_msg "Could not save the setting.")"; return 1; }
pfu_agent_available && pfu_agent_disable
local service
for service in "${PFU_PAM_SERVICES[@]}"; do
if pfu_pam_enabled "$service"; then
pfu_root pam-disable "$service" || true
fi
done
pfu_ok "$(pfu_msg "Face unlock is off. Your faces are kept; delete them under Faces.")"
}
pfu_do_status() {
pfu_head " $PFU_PRETTY"
pfu_ui_status
printf '\n'
}
pfu_do_faces() {
local i state
pfu_status_load || { pfu_bad "$(pfu_reason_text unreachable)"; return 1; }
pfu_faces_load
if (( ${#PFU_FACE_IDS[@]} == 0 )); then
pfu_note "$(pfu_msg "No face is set up yet.")"
return 0
fi
for i in "${!PFU_FACE_IDS[@]}"; do
if [[ ${PFU_FACE_ON[i]} == true ]]; then state="$(pfu_msg "on")"; else state="$(pfu_msg "off")"; fi
printf ' %s %-24s %-4s %s\n' "${PFU_FACE_IDS[i]}" "${PFU_FACE_NAMES[i]}" "$state" \
"$(pfu_msg "%s samples, %s learned" "${PFU_FACE_SAMPLES[i]}" "${PFU_FACE_LEARNED[i]}")"
done
}
pfu_do_remove() {
local id="${1:-}" line
[[ -n $id ]] || { pfu_bad "$(pfu_msg "Which face? See \`%s faces\` for the ids." "$PFU_NAME")"; return 1; }
line="$(pfu_ctl remove "$id" | tail -n1)"
_pfu_fields "$line"
if [[ ${PFU_F[ok]:-} == true ]]; then
pfu_ok "$(pfu_msg "Deleted.")"
else
pfu_bad "$(pfu_reason_text "${PFU_F[reason]:-unreachable}")"
return 1
fi
}
pfu_do_help() {
cat <<- EOF
$PFU_PRETTY $PFU_VERSION
$(pfu_msg "Usage: plasma-face-unlock [command]")
$(pfu_msg "Commands:")
enable $(pfu_msg "Turn face unlock on")
disable $(pfu_msg "Turn it off (faces are kept)")
setup [NAME] $(pfu_msg "Add a face")
faces $(pfu_msg "List the faces")
remove ID $(pfu_msg "Delete a face")
test $(pfu_msg "Look at the camera and see what it sees")
status $(pfu_msg "Show what is on")
-h, --help $(pfu_msg "Show this help")
-V, --version $(pfu_msg "Show the version")
$(pfu_msg "Without a command an interactive menu is shown.")
EOF
}
# ---------------------------------------------------------------------------
# Dispatch
# ---------------------------------------------------------------------------
main() {
local cmd="${1:-}"
[[ $# -gt 0 ]] && shift
case "$cmd" in
--root) pfu_root_verb "$@"; return ;;
esac
# Face data and settings are per user; root has neither a lock screen
# nor a face of its own to set up.
if [[ $EUID -eq 0 && -z ${PFU_ALLOW_ROOT:-} ]]; then
pfu_bad "$(pfu_msg "Run this as your own user, not as root. It asks for your password when it needs to.")"
exit 2
fi
case "$cmd" in
enable) pfu_do_enable ;;
disable) pfu_do_disable ;;
setup|add|enroll) pfu_do_setup "$@" ;;
faces|list) pfu_do_faces ;;
remove|delete) pfu_do_remove "$@" ;;
test|try) pfu_ensure_service && pfu_test ;;
status) pfu_do_status ;;
-h|--help|help) pfu_do_help ;;
-V|--version) printf '%s %s\n' "$PFU_NAME" "$PFU_VERSION" ;;
'') pfu_ui_menu ;;
*)
pfu_bad "$(pfu_msg "Unknown command: %s" "$cmd")"
printf '\n'
pfu_do_help
exit 1
;;
esac
}
main "$@"
+8 -8
View File
@@ -2,11 +2,11 @@
// rest of the installation is.
#pragma once
#define PFU_VERSION "@PFU_VERSION@"
#define PFU_NAME "plasma-face-unlock"
#define PFU_LIBEXECDIR "@PFU_LIBEXECDIR@"
#define PFU_MODELDIR "@PFU_MODELDIR@"
#define PFU_LOCALEDIR "@PFU_LOCALEDIR@"
#define PFU_SOCKET "@PFU_SOCKET@"
#define PFU_STATEDIR "@PFU_STATEDIR@"
#define PFU_CONFIG "@PFU_CONFIG@"
#define FU_VERSION "@FU_VERSION@"
#define FU_NAME "face-unlock"
#define FU_LIBEXECDIR "@FU_LIBEXECDIR@"
#define FU_MODELDIR "@FU_MODELDIR@"
#define FU_LOCALEDIR "@FU_LOCALEDIR@"
#define FU_SOCKET "@FU_SOCKET@"
#define FU_STATEDIR "@FU_STATEDIR@"
#define FU_CONFIG "@FU_CONFIG@"
+27 -27
View File
@@ -17,19 +17,19 @@ here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
tmp="$(mktemp -d)"
trap 'rm -rf -- "$tmp"' EXIT
export PFU_PAM_ETC_DIR="$tmp/etc"
export PFU_PAM_VENDOR_DIRS="$tmp/vendor"
PFU_LIBDIR="$here/src/lib"
export FU_PAM_ETC_DIR="$tmp/etc"
export FU_PAM_VENDOR_DIRS="$tmp/vendor"
FU_LIBDIR="$here/src/lib"
# shellcheck source=/dev/null
source "$PFU_LIBDIR/common.sh"
source "$FU_LIBDIR/common.sh"
# shellcheck source=/dev/null
source "$PFU_LIBDIR/config.sh"
source "$FU_LIBDIR/config.sh"
# shellcheck source=/dev/null
source "$PFU_LIBDIR/pam.sh"
source "$FU_LIBDIR/pam.sh"
PFU_PAM_MODULE="$tmp/lib/pam_plasma_face_unlock.so"
FU_PAM_MODULE="$tmp/lib/pam_face_unlock.so"
mkdir -p "$tmp/lib" "$tmp/etc" "$tmp/vendor"
: > "$PFU_PAM_MODULE"
: > "$FU_PAM_MODULE"
failures=0
check() {
@@ -86,33 +86,33 @@ for layout in arch_sudo debian_sudo fedora_sudo; do
printf '%s\n' "${!layout}" > "$tmp/etc/sudo"
cp "$tmp/etc/sudo" "$tmp/original"
pfu_pam_enable sudo
check "$layout: turned on" 'pfu_pam_enabled sudo'
check "$layout: the face comes before the password" '[[ "$(first_auth "$tmp/etc/sudo")" == *pam_plasma_face_unlock.so* ]]'
check "$layout: with the dash and as sufficient" 'grep -qE "^-auth[[:space:]]+sufficient[[:space:]]+$PFU_PAM_MODULE\$" "$tmp/etc/sudo"'
fu_pam_enable sudo
check "$layout: turned on" 'fu_pam_enabled sudo'
check "$layout: the face comes before the password" '[[ "$(first_auth "$tmp/etc/sudo")" == *pam_face_unlock.so* ]]'
check "$layout: with the dash and as sufficient" 'grep -qE "^-auth[[:space:]]+sufficient[[:space:]]+$FU_PAM_MODULE\$" "$tmp/etc/sudo"'
check "$layout: the header stays first" '[[ "$(head -n1 "$tmp/etc/sudo")" == "#%PAM-1.0" ]]'
pfu_pam_enable sudo
check "$layout: turning it on twice adds it once" '[[ $(grep -c pam_plasma_face_unlock "$tmp/etc/sudo") -eq 1 ]]'
fu_pam_enable sudo
check "$layout: turning it on twice adds it once" '[[ $(grep -c pam_face_unlock "$tmp/etc/sudo") -eq 1 ]]'
pfu_pam_disable sudo
fu_pam_disable sudo
check "$layout: turning it off gives back the same file" 'cmp -s "$tmp/etc/sudo" "$tmp/original"'
check "$layout: turned off" '! pfu_pam_enabled sudo'
check "$layout: turned off" '! fu_pam_enabled sudo'
rm -f "$tmp/etc/sudo"
done
# Only the distribution's copy, in /usr/lib/pam.d.
printf '%s\n' "$arch_polkit" > "$tmp/vendor/polkit-1"
pfu_pam_enable polkit-1
check "vendor polkit-1: a small file of our own in /etc" '[[ -f $tmp/etc/polkit-1 ]] && grep -qF "$PFU_PAM_WRAPPER_MARK" "$tmp/etc/polkit-1"'
fu_pam_enable polkit-1
check "vendor polkit-1: a small file of our own in /etc" '[[ -f $tmp/etc/polkit-1 ]] && grep -qF "$FU_PAM_WRAPPER_MARK" "$tmp/etc/polkit-1"'
check "vendor polkit-1: it includes the distribution's file" 'grep -qE "^auth[[:space:]]+include[[:space:]]+$tmp/vendor/polkit-1\$" "$tmp/etc/polkit-1"'
check "vendor polkit-1: face first" '[[ "$(first_auth "$tmp/etc/polkit-1")" == *pam_plasma_face_unlock.so* ]]'
check "vendor polkit-1: face first" '[[ "$(first_auth "$tmp/etc/polkit-1")" == *pam_face_unlock.so* ]]'
check "vendor polkit-1: the distribution's file is left alone" '[[ "$(cat "$tmp/vendor/polkit-1")" == "$arch_polkit" ]]'
pfu_pam_disable polkit-1
fu_pam_disable polkit-1
check "vendor polkit-1: turning it off removes our file" '[[ ! -e $tmp/etc/polkit-1 ]]'
# No configuration at all.
check "an unknown service is refused" '! pfu_pam_enable nosuchservice 2>/dev/null'
check "an unknown service is refused" '! fu_pam_enable nosuchservice 2>/dev/null'
# ---------------------------------------------------------------------------
# Through real PAM
@@ -122,12 +122,12 @@ harness="$here/build/pam_harness"
if [[ -x $harness ]]; then
mkdir -p "$tmp/real/vendor" "$tmp/real/etc"
printf 'auth required pam_permit.so\naccount required pam_permit.so\n' > "$tmp/real/vendor/svc"
PFU_PAM_ETC_DIR="$tmp/real/etc"
PFU_PAM_VENDOR_DIRS=("$tmp/real/vendor")
PFU_PAM_MODULE="$tmp/real/missing/pam_plasma_face_unlock.so"
mkdir -p "$tmp/real/missing" && : > "$PFU_PAM_MODULE"
pfu_pam_enable svc
rm -f "$PFU_PAM_MODULE"
FU_PAM_ETC_DIR="$tmp/real/etc"
FU_PAM_VENDOR_DIRS=("$tmp/real/vendor")
FU_PAM_MODULE="$tmp/real/missing/pam_face_unlock.so"
mkdir -p "$tmp/real/missing" && : > "$FU_PAM_MODULE"
fu_pam_enable svc
rm -f "$FU_PAM_MODULE"
check "a missing module is skipped, the rest of the stack still decides" '"$harness" "$tmp/real/etc" svc "$(id -un)" > /dev/null 2>&1'
printf 'auth required pam_deny.so\n' > "$tmp/real/vendor/svc"