Files
Modus/src/window/lock.py
T

283 lines
8.6 KiB
Python

import getpass
import pam
from fabric import Application
from fabric.utils import Gdk, GLib, get_relative_path, logger, os
from fabric.widgets.box import Box
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.datetime import DateTime
from fabric.widgets.entry import Entry
from fabric.widgets.label import Label
from fabric.widgets.shapes import Corner
from fabric.widgets.window import Window
from gi.repository import GtkSessionLock # pyright: ignore[reportAttributeAccessIssue]
from shared.widgets.circle_image import CircleImage as Image
from utils.functions import set_process_name
from utils.gtk_utils import svg_file
from window.panel.components.indicators import (
BatteryIndicator,
BluetoothIndicator,
NetworkIndicator,
)
for log in [
"fabric.audio.service",
"fabric.core.application",
"fabric.bluetooth.service",
"fabric.notifications.service",
"services.network",
]:
logger.disable(log)
class IndicatorBox(Box):
def __init__(self, *args, **kwargs):
super().__init__(
h_align="end",
name="indicator-box",
spacing=5,
h_expand=True,
children=[
BatteryIndicator(show_window=False),
BluetoothIndicator(show_window=False),
NetworkIndicator(show_window=False),
],
)
class ContentBox(CenterBox):
def __init__(self, on_activate, *args, **kwargs):
self.password_entry = Entry(
placeholder="Enter Password",
name="password-entry",
h_align="center",
v_align="center",
visible=False,
password=True,
on_activate=on_activate,
)
face_icon = os.path.expanduser("~/.face.icon")
if not os.path.exists(face_icon):
face_icon = get_relative_path("../assets/default.png")
self.password_entry.set_property("xalign", 0.5)
self.username_label = Label(
label=f"{getpass.getuser().title()}",
name="username",
visible=True,
h_align="center",
v_align="center",
)
self.unlock_text = Label(
label="Touch ID or Enter Password",
name="unlock-text",
)
super().__init__(
name="content-box",
h_expand=True,
orientation="vertical",
v_expand=True,
start_children=[
IndicatorBox(),
DateTime(
formatters=["%A,%B %-d"],
interval=10000,
h_expand=False,
v_align="start",
v_expand=False,
name="lock-date",
),
DateTime(formatters=["%I:%M"], name="lock-clock"),
],
end_children=[
Box(
name="profile-box",
h_align="center",
h_expand=True,
v_expand=True,
v_align="end",
children=[
Image(
name="face-icon",
image_file=face_icon,
size=64,
),
],
),
Box(
name="container-box",
orientation="v",
v_align="end",
h_align="center",
h_expand=True,
v_expand=True,
children=[
self.username_label,
self.password_entry,
],
),
Box(
name="unlock-box",
v_align="end",
h_align="center",
h_expand=True,
v_expand=True,
children=[
self.unlock_text,
],
),
],
**kwargs,
)
class LockScreen(Window):
def __init__(self, lock: GtkSessionLock.Lock):
self._hide_timeout_id = None
self.lock = lock
self.content = ContentBox(self.on_activate)
self.lock_notch = self._build_lock_notch()
super().__init__(
title="lock",
visible=False,
all_visible=False,
name="lockscreen-bg",
anchor="center",
child=Box(
orientation="vertical",
children=[self.lock_notch, self.content],
),
)
self.content.password_entry.set_visible(False)
self.connect("key-press-event", self._on_keypress)
bg = os.path.expanduser("~/.current.wall")
if not os.path.exists(bg):
bg = get_relative_path("../assets/wallpapers_example/example-1.png")
self.set_style(f"background-image: url('{bg}');")
def _build_lock_notch(self):
return CenterBox(
name="lock-notch",
orientation="h",
h_align="center",
v_align="center",
start_children=Box(
name="lock-notch-corner-left",
orientation="v",
h_align="start",
children=[
Corner(
name="lock-notch-corner",
orientation="top-right",
size=20,
),
],
),
center_children=CenterBox(
name="lock-notch-stack",
v_expand=True,
h_expand=True,
end_children=Box(
children=[svg_file("notch/lock.svg", size=16)],
style="margin-right: 12px;",
),
),
end_children=Box(
name="lock-notch-corner-right",
orientation="v",
h_align="end",
children=[
Corner(
name="lock-notch-corner",
orientation="top-left",
size=20,
),
],
),
)
def _on_keypress(self, widget, event):
keyval = event.keyval
# ESC pressed → hide entry immediately
if keyval == Gdk.KEY_Escape and self.content.password_entry.get_visible():
self._hide_entry()
return
# Show entry if hidden
if not self.content.password_entry.get_visible():
self.content.username_label.set_visible(False)
self.content.password_entry.set_visible(True)
self.content.password_entry.grab_focus()
self._start_hide_timer()
else:
# Reset timer if already visible
self._restart_hide_timer()
def _start_hide_timer(self):
self._stop_hide_timer() # just in case
self._hide_timeout_id = GLib.timeout_add_seconds(5, self._hide_entry)
# 10 seconds of inactivity before hiding
def _restart_hide_timer(self):
self._start_hide_timer()
def _stop_hide_timer(self):
if self._hide_timeout_id:
GLib.source_remove(self._hide_timeout_id)
self._hide_timeout_id = None
def _hide_entry(self):
self._stop_hide_timer()
self.content.password_entry.set_visible(False)
self.content.username_label.set_visible(True)
return False # stop timeout
def on_activate(self, entry: Entry, *args):
if not pam.authenticate(getpass.getuser(), (entry.get_text() or "").strip()):
entry.set_text("")
entry.set_placeholder_text("Wrong Password")
return
self.lock.unlock_and_destroy()
self.destroy()
GLib.idle_add(app.quit) # schedules quit after unlock
def initialize():
lock = GtkSessionLock.prepare_lock()
lock.lock_lock()
lockscreen = LockScreen(lock)
lock.new_surface(
lockscreen,
Gdk.Display.get_default().get_monitor( # pyright: ignore[reportAttributeAccessIssue, reportOptionalMemberAccess]
0
),
)
lockscreen.show()
return lock, lockscreen
def main():
set_process_name("lockscreen")
_lock, lockscreen = initialize()
global app
app = Application("lock", lockscreen)
def set_css():
app.set_stylesheet_from_file(
get_relative_path("../styles/main.css"),
)
app.set_css = set_css # pyright: ignore[reportAttributeAccessIssue]
app.set_css() # pyright: ignore[reportAttributeAccessIssue]
app.run()
if __name__ == "__main__":
main()