From 557e4c7a52c3db6b7b665115f5ce65238f7fe6b6 Mon Sep 17 00:00:00 2001 From: matt1432 Date: Sat, 25 Jan 2025 02:28:34 -0500 Subject: [PATCH] feat(ags4): implement lockscreen --- modules/ags/gtk4/app.ts | 6 +- modules/ags/gtk4/lib/windows.ts | 2 +- modules/ags/gtk4/style.scss | 2 + .../{widget => widgets}/astalify/astalify.ts | 0 .../{widget => widgets}/astalify/bindings.ts | 0 .../{widget => widgets}/astalify/construct.ts | 0 .../astalify/controller.ts | 0 .../{widget => widgets}/astalify/generics.ts | 0 .../{widget => widgets}/astalify/index.ts | 0 modules/ags/gtk4/{widget => widgets}/bar.ts | 0 .../ags/gtk4/widgets/lockscreen/_index.scss | 9 + modules/ags/gtk4/widgets/lockscreen/index.ts | 253 ++++++++++++++++++ .../{widget => widgets}/misc/popup-window.ts | 0 .../{widget => widgets}/misc/separator.ts | 0 .../{widget => widgets}/subclasses/box.ts | 0 .../{widget => widgets}/subclasses/button.ts | 0 .../subclasses/calendar.ts | 0 .../subclasses/centerbox.ts | 0 .../{widget => widgets}/subclasses/entry.ts | 0 .../{widget => widgets}/subclasses/image.ts | 0 .../{widget => widgets}/subclasses/index.ts | 0 .../{widget => widgets}/subclasses/label.ts | 0 .../subclasses/levelbar.ts | 0 .../subclasses/menubutton.ts | 0 .../{widget => widgets}/subclasses/overlay.ts | 0 .../{widget => widgets}/subclasses/popover.ts | 0 .../subclasses/revealer.ts | 0 .../{widget => widgets}/subclasses/slider.ts | 0 .../{widget => widgets}/subclasses/stack.ts | 0 .../{widget => widgets}/subclasses/switch.ts | 0 .../{widget => widgets}/subclasses/window.ts | 0 modules/ags/packages.nix | 21 +- 32 files changed, 281 insertions(+), 12 deletions(-) rename modules/ags/gtk4/{widget => widgets}/astalify/astalify.ts (100%) rename modules/ags/gtk4/{widget => widgets}/astalify/bindings.ts (100%) rename modules/ags/gtk4/{widget => widgets}/astalify/construct.ts (100%) rename modules/ags/gtk4/{widget => widgets}/astalify/controller.ts (100%) rename modules/ags/gtk4/{widget => widgets}/astalify/generics.ts (100%) rename modules/ags/gtk4/{widget => widgets}/astalify/index.ts (100%) rename modules/ags/gtk4/{widget => widgets}/bar.ts (100%) create mode 100644 modules/ags/gtk4/widgets/lockscreen/_index.scss create mode 100644 modules/ags/gtk4/widgets/lockscreen/index.ts rename modules/ags/gtk4/{widget => widgets}/misc/popup-window.ts (100%) rename modules/ags/gtk4/{widget => widgets}/misc/separator.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/box.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/button.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/calendar.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/centerbox.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/entry.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/image.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/index.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/label.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/levelbar.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/menubutton.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/overlay.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/popover.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/revealer.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/slider.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/stack.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/switch.ts (100%) rename modules/ags/gtk4/{widget => widgets}/subclasses/window.ts (100%) diff --git a/modules/ags/gtk4/app.ts b/modules/ags/gtk4/app.ts index 5a499189..77207fd8 100644 --- a/modules/ags/gtk4/app.ts +++ b/modules/ags/gtk4/app.ts @@ -2,7 +2,8 @@ import { App } from 'astal/gtk4'; import style from './style.scss'; -import Bar from './widget/bar'; +// import Bar from './widgets/bar'; +import Lockscreen from './widgets/lockscreen'; App.start({ @@ -10,6 +11,7 @@ App.start({ instanceName: 'gtk4', main() { - Bar(); + // Bar(); + Lockscreen(); }, }); diff --git a/modules/ags/gtk4/lib/windows.ts b/modules/ags/gtk4/lib/windows.ts index dc8a5c3f..768e9f07 100644 --- a/modules/ags/gtk4/lib/windows.ts +++ b/modules/ags/gtk4/lib/windows.ts @@ -2,7 +2,7 @@ import { idle } from 'astal'; import { App, Gdk, Gtk } from 'astal/gtk4'; /* Types */ -import PopupWindow from '../widget/misc/popup-window'; +import PopupWindow from '../widgets/misc/popup-window'; export interface Layer { address: string diff --git a/modules/ags/gtk4/style.scss b/modules/ags/gtk4/style.scss index 1d0d3a9f..f22022f3 100644 --- a/modules/ags/gtk4/style.scss +++ b/modules/ags/gtk4/style.scss @@ -1,3 +1,5 @@ +@use './widgets/lockscreen'; + // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss $fg-color: #{"@theme_fg_color"}; $bg-color: #{"@theme_bg_color"}; diff --git a/modules/ags/gtk4/widget/astalify/astalify.ts b/modules/ags/gtk4/widgets/astalify/astalify.ts similarity index 100% rename from modules/ags/gtk4/widget/astalify/astalify.ts rename to modules/ags/gtk4/widgets/astalify/astalify.ts diff --git a/modules/ags/gtk4/widget/astalify/bindings.ts b/modules/ags/gtk4/widgets/astalify/bindings.ts similarity index 100% rename from modules/ags/gtk4/widget/astalify/bindings.ts rename to modules/ags/gtk4/widgets/astalify/bindings.ts diff --git a/modules/ags/gtk4/widget/astalify/construct.ts b/modules/ags/gtk4/widgets/astalify/construct.ts similarity index 100% rename from modules/ags/gtk4/widget/astalify/construct.ts rename to modules/ags/gtk4/widgets/astalify/construct.ts diff --git a/modules/ags/gtk4/widget/astalify/controller.ts b/modules/ags/gtk4/widgets/astalify/controller.ts similarity index 100% rename from modules/ags/gtk4/widget/astalify/controller.ts rename to modules/ags/gtk4/widgets/astalify/controller.ts diff --git a/modules/ags/gtk4/widget/astalify/generics.ts b/modules/ags/gtk4/widgets/astalify/generics.ts similarity index 100% rename from modules/ags/gtk4/widget/astalify/generics.ts rename to modules/ags/gtk4/widgets/astalify/generics.ts diff --git a/modules/ags/gtk4/widget/astalify/index.ts b/modules/ags/gtk4/widgets/astalify/index.ts similarity index 100% rename from modules/ags/gtk4/widget/astalify/index.ts rename to modules/ags/gtk4/widgets/astalify/index.ts diff --git a/modules/ags/gtk4/widget/bar.ts b/modules/ags/gtk4/widgets/bar.ts similarity index 100% rename from modules/ags/gtk4/widget/bar.ts rename to modules/ags/gtk4/widgets/bar.ts diff --git a/modules/ags/gtk4/widgets/lockscreen/_index.scss b/modules/ags/gtk4/widgets/lockscreen/_index.scss new file mode 100644 index 00000000..fa2c2601 --- /dev/null +++ b/modules/ags/gtk4/widgets/lockscreen/_index.scss @@ -0,0 +1,9 @@ +window, +viewport { + all: unset; +} + +.lock-clock { + font-size: 80pt; + font-family: 'Ubuntu Mono'; +} diff --git a/modules/ags/gtk4/widgets/lockscreen/index.ts b/modules/ags/gtk4/widgets/lockscreen/index.ts new file mode 100644 index 00000000..18cce3c2 --- /dev/null +++ b/modules/ags/gtk4/widgets/lockscreen/index.ts @@ -0,0 +1,253 @@ +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(); +}; diff --git a/modules/ags/gtk4/widget/misc/popup-window.ts b/modules/ags/gtk4/widgets/misc/popup-window.ts similarity index 100% rename from modules/ags/gtk4/widget/misc/popup-window.ts rename to modules/ags/gtk4/widgets/misc/popup-window.ts diff --git a/modules/ags/gtk4/widget/misc/separator.ts b/modules/ags/gtk4/widgets/misc/separator.ts similarity index 100% rename from modules/ags/gtk4/widget/misc/separator.ts rename to modules/ags/gtk4/widgets/misc/separator.ts diff --git a/modules/ags/gtk4/widget/subclasses/box.ts b/modules/ags/gtk4/widgets/subclasses/box.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/box.ts rename to modules/ags/gtk4/widgets/subclasses/box.ts diff --git a/modules/ags/gtk4/widget/subclasses/button.ts b/modules/ags/gtk4/widgets/subclasses/button.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/button.ts rename to modules/ags/gtk4/widgets/subclasses/button.ts diff --git a/modules/ags/gtk4/widget/subclasses/calendar.ts b/modules/ags/gtk4/widgets/subclasses/calendar.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/calendar.ts rename to modules/ags/gtk4/widgets/subclasses/calendar.ts diff --git a/modules/ags/gtk4/widget/subclasses/centerbox.ts b/modules/ags/gtk4/widgets/subclasses/centerbox.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/centerbox.ts rename to modules/ags/gtk4/widgets/subclasses/centerbox.ts diff --git a/modules/ags/gtk4/widget/subclasses/entry.ts b/modules/ags/gtk4/widgets/subclasses/entry.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/entry.ts rename to modules/ags/gtk4/widgets/subclasses/entry.ts diff --git a/modules/ags/gtk4/widget/subclasses/image.ts b/modules/ags/gtk4/widgets/subclasses/image.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/image.ts rename to modules/ags/gtk4/widgets/subclasses/image.ts diff --git a/modules/ags/gtk4/widget/subclasses/index.ts b/modules/ags/gtk4/widgets/subclasses/index.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/index.ts rename to modules/ags/gtk4/widgets/subclasses/index.ts diff --git a/modules/ags/gtk4/widget/subclasses/label.ts b/modules/ags/gtk4/widgets/subclasses/label.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/label.ts rename to modules/ags/gtk4/widgets/subclasses/label.ts diff --git a/modules/ags/gtk4/widget/subclasses/levelbar.ts b/modules/ags/gtk4/widgets/subclasses/levelbar.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/levelbar.ts rename to modules/ags/gtk4/widgets/subclasses/levelbar.ts diff --git a/modules/ags/gtk4/widget/subclasses/menubutton.ts b/modules/ags/gtk4/widgets/subclasses/menubutton.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/menubutton.ts rename to modules/ags/gtk4/widgets/subclasses/menubutton.ts diff --git a/modules/ags/gtk4/widget/subclasses/overlay.ts b/modules/ags/gtk4/widgets/subclasses/overlay.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/overlay.ts rename to modules/ags/gtk4/widgets/subclasses/overlay.ts diff --git a/modules/ags/gtk4/widget/subclasses/popover.ts b/modules/ags/gtk4/widgets/subclasses/popover.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/popover.ts rename to modules/ags/gtk4/widgets/subclasses/popover.ts diff --git a/modules/ags/gtk4/widget/subclasses/revealer.ts b/modules/ags/gtk4/widgets/subclasses/revealer.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/revealer.ts rename to modules/ags/gtk4/widgets/subclasses/revealer.ts diff --git a/modules/ags/gtk4/widget/subclasses/slider.ts b/modules/ags/gtk4/widgets/subclasses/slider.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/slider.ts rename to modules/ags/gtk4/widgets/subclasses/slider.ts diff --git a/modules/ags/gtk4/widget/subclasses/stack.ts b/modules/ags/gtk4/widgets/subclasses/stack.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/stack.ts rename to modules/ags/gtk4/widgets/subclasses/stack.ts diff --git a/modules/ags/gtk4/widget/subclasses/switch.ts b/modules/ags/gtk4/widgets/subclasses/switch.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/switch.ts rename to modules/ags/gtk4/widgets/subclasses/switch.ts diff --git a/modules/ags/gtk4/widget/subclasses/window.ts b/modules/ags/gtk4/widgets/subclasses/window.ts similarity index 100% rename from modules/ags/gtk4/widget/subclasses/window.ts rename to modules/ags/gtk4/widgets/subclasses/window.ts diff --git a/modules/ags/packages.nix b/modules/ags/packages.nix index 244d1c16..acc0476c 100644 --- a/modules/ags/packages.nix +++ b/modules/ags/packages.nix @@ -129,6 +129,16 @@ in { buildNodeModules buildGirTypes ; + + lockscreenVars = + # javascript + '' + export default { + mainMonitor: '${cfgDesktop.mainMonitor}', + dupeLockscreen: ${boolToString cfgDesktop.displayManager.duplicateScreen}, + hasFprintd: ${boolToString (hostName == "wim")}, + }; + ''; in ( (buildGirTypes { pname = "ags"; @@ -159,15 +169,8 @@ in { source = buildNodeModules ./config (import ./config).npmDepsHash; }; - "${cfg.configDir}/widgets/lockscreen/vars.ts".text = - # javascript - '' - export default { - mainMonitor: '${cfgDesktop.mainMonitor}', - dupeLockscreen: ${boolToString cfgDesktop.displayManager.duplicateScreen}, - hasFprintd: ${boolToString (hostName == "wim")}, - }; - ''; + "${cfg.configDir}/widgets/lockscreen/vars.ts".text = lockscreenVars; + "${gtk4ConfigDir}/widgets/lockscreen/vars.ts".text = lockscreenVars; } // optionalAttrs cfgDesktop.isTouchscreen { ".config/fcitx5/conf/virtualkeyboardadapter.conf".text = ''