import { idle, timeout, Variable } from 'astal'; import { App, Astal, Gdk, Gtk } from 'astal/gtk4'; import { register } from 'astal/gobject'; import AstalAuth from 'gi://AstalAuth'; import Gtk4SessionLock from 'gi://Gtk4SessionLock'; import { Box, BoxClass, Entry, Label, Window } from '../subclasses'; import Separator from '../misc/separator'; import { get_hyprland_monitor_desc } from '../../lib'; // This file is generated by Nix import Vars from './vars'; /* Types */ declare global { function authFinger(): void; } @register() class BlurredBox extends BoxClass { geometry = {} as { w: number, h: number }; } export default () => { const windows = new Map(); const blurBGs: BlurredBox[] = []; const transition_duration = 1000; const WINDOW_MARGINS = -2; const ENTRY_SPACING = 20; const CLOCK_SPACING = 60; const bgCSS = ({ w = 1, h = 1 } = {}) => `* { border: 2px solid rgba(189, 147, 249, 0.8); background: rgba(0, 0, 0, 0.2); min-height: ${h}px; min-width: ${w}px; transition: min-height ${transition_duration / 2}ms, min-width ${transition_duration / 2}ms; }`; const lock = Gtk4SessionLock.get_singleton(); const unlock = () => { blurBGs.forEach((b) => { b.css = bgCSS({ w: b.geometry.w, h: 1, }); timeout(transition_duration / 2, () => { b.css = bgCSS({ w: 1, h: 1, }); }); }); timeout(transition_duration, () => { Gtk4SessionLock.unlock(); Gdk.Display.get_default()?.sync(); App.quit(); }); }; const Clock = () => { const time = Variable('').poll(1000, () => { return (new Date().toLocaleString([], { hour: 'numeric', minute: 'numeric', hour12: true, }) ?? '') .replace('a.m.', 'AM') .replace('p.m.', 'PM'); }); return Label({ cssClasses: ['lock-clock'], label: time(), }); }; const PasswordPrompt = (monitor: Gdk.Monitor, visible: boolean) => { const rev = new BlurredBox({ css: bgCSS() }); idle(() => { rev.geometry = { w: monitor.get_geometry().width, h: monitor.get_geometry().height, }; rev.css = bgCSS({ w: rev.geometry.w, h: 1, }); timeout(transition_duration / 2, () => { rev.css = bgCSS({ w: rev.geometry.w, h: rev.geometry.h, }); }); }); blurBGs.push(rev); Window({ name: `blur-bg-${monitor.get_model()}`, namespace: `blur-bg-${monitor.get_model()}`, gdkmonitor: monitor, layer: Astal.Layer.OVERLAY, visible: true, anchor: Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT | Astal.WindowAnchor.BOTTOM, margin: WINDOW_MARGINS, exclusivity: Astal.Exclusivity.IGNORE, child: Box({ halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER, children: [rev], }), }); const label = Label({ label: 'Enter password:' }); return new Gtk.Window({ child: visible ? Box({ vertical: true, halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER, spacing: 16, children: [ Clock(), Separator({ size: CLOCK_SPACING, vertical: true, }), Box({ halign: Gtk.Align.CENTER, cssClasses: ['avatar'], }), Box({ cssClasses: ['entry-box'], vertical: true, children: [ label, Separator({ size: ENTRY_SPACING, vertical: true, }), Entry({ halign: Gtk.Align.CENTER, xalign: 0.5, visibility: false, placeholder_text: 'password', onRealize: (self) => self.grab_focus(), onActivate: (self) => { self.set_sensitive(false); AstalAuth.Pam.authenticate(self.get_text() ?? '', (_, task) => { try { AstalAuth.Pam.authenticate_finish(task); unlock(); } catch (e) { self.set_text(''); label.set_label((e as Error).message); self.set_sensitive(true); } }); }, }), ], }), ], }) : Box(), }); }; const createWindow = (monitor: Gdk.Monitor) => { const hyprDesc = get_hyprland_monitor_desc(monitor); const entryVisible = Vars.mainMonitor === hyprDesc || Vars.dupeLockscreen; const win = PasswordPrompt(monitor, entryVisible); windows.set(monitor, win); }; const lock_screen = () => { const display = Gdk.Display.get_default(); for (let m = 0; m < (display?.get_monitors().get_n_items() ?? 0); m++) { const monitor = display?.get_monitors().get_item(m) as Gdk.Monitor; if (monitor) { createWindow(monitor); } } display?.get_monitors()?.connect('items-changed', () => { for (let m = 0; m < (display?.get_monitors().get_n_items() ?? 0); m++) { const monitor = display?.get_monitors().get_item(m) as Gdk.Monitor; if (monitor && !windows.has(monitor)) { createWindow(monitor); } } }); Gtk4SessionLock.lock(); windows.forEach((win, monitor) => { Gtk4SessionLock.assign_window_to_monitor(win, monitor); win.show(); }); }; const on_finished = () => { Gdk.Display.get_default()?.sync(); App.quit(); }; lock.connect('finished', on_finished); if (Vars.hasFprintd) { globalThis.authFinger = () => AstalAuth.Pam.authenticate('', (_, task) => { try { AstalAuth.Pam.authenticate_finish(task); unlock(); } catch (e) { console.error((e as Error).message); } }); globalThis.authFinger(); } lock_screen(); };