From 94ce797784c3d5e9b96382598f9ab084814b7e19 Mon Sep 17 00:00:00 2001
From: matt1432 <matt@nelim.org>
Date: Fri, 19 Apr 2024 17:01:09 -0400
Subject: [PATCH] feat(ags): replace hyprlock with ags

---
 devices/wim/modules/security.nix              |   2 +-
 flake.lock                                    |  77 +-------
 flake.nix                                     |   8 -
 modules/ags/config/lockscreen.js              |   3 +
 modules/ags/config/lockscreen.ts              |   8 +
 modules/ags/config/scss/lockscreen.scss       |   5 +
 modules/ags/config/ts/lockscreen/main.ts      | 175 ++++++++++++++++++
 modules/ags/default.nix                       |  10 +-
 .../ags/gtk-session-lock-types/default.nix    |   4 +-
 modules/hyprland/hyprlock.nix                 |  64 -------
 modules/hyprland/security.nix                 |   5 +-
 11 files changed, 202 insertions(+), 159 deletions(-)
 create mode 100644 modules/ags/config/lockscreen.js
 create mode 100644 modules/ags/config/lockscreen.ts
 create mode 100644 modules/ags/config/scss/lockscreen.scss
 create mode 100644 modules/ags/config/ts/lockscreen/main.ts
 delete mode 100644 modules/hyprland/hyprlock.nix

diff --git a/devices/wim/modules/security.nix b/devices/wim/modules/security.nix
index 37b826ff..c0009b03 100644
--- a/devices/wim/modules/security.nix
+++ b/devices/wim/modules/security.nix
@@ -36,6 +36,6 @@ in {
     polkit-1.text = mkDefault (mkBefore grosshackConf);
 
     # FIXME: sometimes can't use password
-    hyprlock.text = mkDefault (mkBefore grosshackConf);
+    ags.text = mkDefault (mkBefore grosshackConf);
   };
 }
diff --git a/flake.lock b/flake.lock
index 6590569c..d6fe4a47 100644
--- a/flake.lock
+++ b/flake.lock
@@ -330,7 +330,7 @@
     },
     "flake-utils_3": {
       "inputs": {
-        "systems": "systems_8"
+        "systems": "systems_6"
       },
       "locked": {
         "lastModified": 1710146030,
@@ -669,50 +669,6 @@
         "type": "github"
       }
     },
-    "hyprlang_3": {
-      "inputs": {
-        "nixpkgs": [
-          "hyprlock",
-          "nixpkgs"
-        ],
-        "systems": "systems_6"
-      },
-      "locked": {
-        "lastModified": 1713121246,
-        "narHash": "sha256-502X0Q0fhN6tJK7iEUA8CghONKSatW/Mqj4Wappd++0=",
-        "owner": "hyprwm",
-        "repo": "hyprlang",
-        "rev": "78fcaa27ae9e1d782faa3ff06c8ea55ddce63706",
-        "type": "github"
-      },
-      "original": {
-        "owner": "hyprwm",
-        "repo": "hyprlang",
-        "type": "github"
-      }
-    },
-    "hyprlock": {
-      "inputs": {
-        "hyprlang": "hyprlang_3",
-        "nixpkgs": [
-          "nixpkgs"
-        ],
-        "systems": "systems_7"
-      },
-      "locked": {
-        "lastModified": 1713214544,
-        "narHash": "sha256-36qa6MOhCBd39YPC0FgapwGRHZXjstw8BQuKdFzwQ4k=",
-        "owner": "hyprwm",
-        "repo": "hyprlock",
-        "rev": "2bce52f094c49109520ad37fc8f0d051acaace55",
-        "type": "github"
-      },
-      "original": {
-        "owner": "hyprwm",
-        "repo": "hyprlock",
-        "type": "github"
-      }
-    },
     "jellyfin-flake": {
       "inputs": {
         "nixpkgs": [
@@ -1541,7 +1497,6 @@
         "home-manager": "home-manager",
         "hypridle": "hypridle",
         "hyprland": "hyprland",
-        "hyprlock": "hyprlock",
         "jellyfin-flake": "jellyfin-flake",
         "jellyfin-ultrachromic-src": "jellyfin-ultrachromic-src",
         "modernx-src": "modernx-src",
@@ -1743,36 +1698,6 @@
       }
     },
     "systems_6": {
-      "locked": {
-        "lastModified": 1689347949,
-        "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
-        "owner": "nix-systems",
-        "repo": "default-linux",
-        "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "default-linux",
-        "type": "github"
-      }
-    },
-    "systems_7": {
-      "locked": {
-        "lastModified": 1689347949,
-        "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
-        "owner": "nix-systems",
-        "repo": "default-linux",
-        "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "default-linux",
-        "type": "github"
-      }
-    },
-    "systems_8": {
       "locked": {
         "lastModified": 1681028828,
         "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
diff --git a/flake.nix b/flake.nix
index ccdcd84e..f196dda6 100644
--- a/flake.nix
+++ b/flake.nix
@@ -244,14 +244,6 @@
       inputs.nixpkgs.follows = "nixpkgs";
     };
 
-    hyprlock = {
-      type = "github";
-      owner = "hyprwm";
-      repo = "hyprlock";
-
-      inputs.nixpkgs.follows = "nixpkgs";
-    };
-
     Hyprspace = {
       type = "github";
       owner = "KZDKM";
diff --git a/modules/ags/config/lockscreen.js b/modules/ags/config/lockscreen.js
new file mode 100644
index 00000000..ebbbbe29
--- /dev/null
+++ b/modules/ags/config/lockscreen.js
@@ -0,0 +1,3 @@
+import { transpileTypeScript } from './js/utils.js';
+
+export default (await transpileTypeScript('lockscreen')).default;
diff --git a/modules/ags/config/lockscreen.ts b/modules/ags/config/lockscreen.ts
new file mode 100644
index 00000000..25d76e51
--- /dev/null
+++ b/modules/ags/config/lockscreen.ts
@@ -0,0 +1,8 @@
+import LaunchLockscreen from './ts/lockscreen/main.ts';
+
+
+LaunchLockscreen();
+
+App.config({
+    icons: './icons',
+});
diff --git a/modules/ags/config/scss/lockscreen.scss b/modules/ags/config/scss/lockscreen.scss
new file mode 100644
index 00000000..0162217e
--- /dev/null
+++ b/modules/ags/config/scss/lockscreen.scss
@@ -0,0 +1,5 @@
+@import './common';
+
+window {
+  background-color: transparent;
+}
diff --git a/modules/ags/config/ts/lockscreen/main.ts b/modules/ags/config/ts/lockscreen/main.ts
new file mode 100644
index 00000000..a8c9770f
--- /dev/null
+++ b/modules/ags/config/ts/lockscreen/main.ts
@@ -0,0 +1,175 @@
+const { Box, Entry, Label, Separator, Window } = Widget;
+
+import Gdk from 'gi://Gdk?version=3.0';
+import Gtk from 'gi://Gtk?version=3.0';
+import Lock from 'gi://GtkSessionLock?version=0.1';
+
+/* Types */
+import { Box as AgsBox } from 'types/widgets/box';
+
+
+const lock = Lock.prepare_lock();
+const windows: Gtk.Window[] = [];
+const blurBGs: AgsBox<Gtk.Widget, { geometry: { w: number, h: number }; }>[] = [];
+
+const transition_duration = 1000;
+const windowMargins = -2;
+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 unlock = () => {
+    blurBGs.forEach((b) => {
+        b.css = bgCSS({
+            w: b.attribute.geometry.w,
+            h: 1,
+        });
+
+        Utils.timeout(transition_duration / 2, () => {
+            b.css = bgCSS({
+                w: 1,
+                h: 1,
+            });
+        });
+    });
+    Utils.timeout(transition_duration, () => {
+        lock.unlock_and_destroy();
+        Gdk.Display.get_default()?.sync();
+        App.quit();
+    });
+};
+
+const PasswordPrompt = (monitor: Gdk.Monitor) => {
+    const rev = Box({
+        css: bgCSS(),
+        attribute: {
+            geometry: {} as { w: number, h: number },
+        },
+
+        setup: (self) => Utils.idle(() => {
+            self.attribute.geometry = {
+                w: monitor.geometry.width,
+                h: monitor.geometry.height,
+            };
+
+            self.css = bgCSS({
+                w: self.attribute.geometry.w,
+                h: 1,
+            });
+
+            Utils.timeout(transition_duration / 2, () => {
+                self.css = bgCSS({
+                    w: self.attribute.geometry.w,
+                    h: self.attribute.geometry.h,
+                });
+            });
+        }),
+    });
+
+    blurBGs.push(rev);
+
+    Window({
+        name: `blur-bg-${monitor.get_model()}`,
+        gdkmonitor: monitor,
+        layer: 'overlay',
+        anchor: ['top', 'bottom', 'right', 'left'],
+        margins: [windowMargins],
+        exclusivity: 'ignore',
+
+        child: Box({
+            hexpand: false,
+            vexpand: false,
+            hpack: 'center',
+            vpack: 'center',
+            child: rev,
+        }),
+    });
+
+    const label = Label('Enter password:');
+
+    return new Gtk.Window({
+        child: Widget.Box({
+            vertical: true,
+            vpack: 'center',
+            hpack: 'center',
+            spacing: 16,
+
+            children: [
+                Box({
+                    hpack: 'center',
+                    class_name: 'avatar',
+                }),
+
+                Box({
+                    class_name: 'entry-box',
+                    vertical: true,
+                    children: [
+                        label,
+
+                        Separator(),
+
+                        Entry({
+                            hpack: 'center',
+                            xalign: 0.5,
+                            visibility: false,
+                            placeholder_text: 'password',
+
+                            on_accept: (self) => {
+                                self.sensitive = false;
+
+                                Utils.authenticate(self.text ?? '')
+                                    .then(() => unlock())
+                                    .catch((e) => {
+                                        self.text = '';
+                                        label.label = e.message;
+                                        self.sensitive = true;
+                                    });
+                            },
+                        }).on('realize', (entry) => entry.grab_focus()),
+                    ],
+                }),
+            ],
+        }),
+    });
+};
+
+const createWindow = (monitor: Gdk.Monitor) => {
+    const win = PasswordPrompt(monitor);
+
+    windows.push(win);
+    // @ts-expect-error should be fine
+    lock.new_surface(win, monitor);
+    win.show();
+};
+
+const on_locked = () => {
+    const display = Gdk.Display.get_default();
+
+    for (let m = 0; m < (display?.get_n_monitors() ?? 0); m++) {
+        const monitor = display?.get_monitor(m);
+
+        if (monitor) {
+            createWindow(monitor);
+        }
+    }
+    display?.connect('monitor-added', (_, monitor) => {
+        createWindow(monitor);
+    });
+};
+
+const on_finished = () => {
+    lock.destroy();
+    Gdk.Display.get_default()?.sync();
+    App.quit();
+};
+
+lock.connect('locked', on_locked);
+lock.connect('finished', on_finished);
+
+
+export default () => lock.lock_lock();
diff --git a/modules/ags/default.nix b/modules/ags/default.nix
index ecc7e6b8..09b6efa9 100644
--- a/modules/ags/default.nix
+++ b/modules/ags/default.nix
@@ -10,7 +10,7 @@
 
   flakeDir = config.environment.variables.FLAKE;
   isTouchscreen = config.hardware.sensor.iio.enable;
-  gtk-lock = gtk-session-lock.packages.${pkgs.system}.default;
+  gtkSessionLock = gtk-session-lock.packages.${pkgs.system}.default;
 in {
   # Enable pam for ags and astal
   security.pam.services.ags = {};
@@ -57,14 +57,14 @@ in {
         enable = true;
         extraPackages = with pkgs; [
           libadwaita
-          gtk-lock
+          gtkSessionLock
         ];
       };
 
       programs.ags = {
         enable = true;
         extraPackages = [
-          gtk-lock
+          gtkSessionLock
         ];
       };
 
@@ -82,7 +82,7 @@ in {
               source = "${config.programs.ags.finalPackage}/share/com.github.Aylur.ags/types";
               recursive = true;
             };
-            "${agsConfigDir}/config/types/gtk-session-lock".source = pkgs.callPackage ./gtk-session-lock-types {inherit gtk-lock;};
+            "${agsConfigDir}/config/types/gtk-session-lock".source = pkgs.callPackage ./gtk-session-lock-types {inherit gtkSessionLock;};
             "${agsConfigDir}/config/config.js".text = configJs;
           }
           // (import ./icons.nix {inherit pkgs agsConfigDir;});
@@ -120,6 +120,8 @@ in {
 
           layerrule = [
             "noanim, ^(?!win-).*"
+            "blur, ^(blur-bg.*)"
+            "ignorealpha 0.19, ^(blur-bg.*)"
           ];
 
           exec-once = [
diff --git a/modules/ags/gtk-session-lock-types/default.nix b/modules/ags/gtk-session-lock-types/default.nix
index d9720cee..9e8b97cf 100644
--- a/modules/ags/gtk-session-lock-types/default.nix
+++ b/modules/ags/gtk-session-lock-types/default.nix
@@ -4,7 +4,7 @@
   gdk-pixbuf,
   gobject-introspection,
   gtk3,
-  gtk-lock,
+  gtkSessionLock,
   harfbuzz,
   pango,
   ...
@@ -20,7 +20,7 @@ buildNpmPackage {
 
   installPhase = ''
     npx @ts-for-gir/cli generate ${builtins.concatStringsSep " " [
-      "-g ${gtk-lock.dev}/share/gir-1.0"
+      "-g ${gtkSessionLock.dev}/share/gir-1.0"
       "-g ${gobject-introspection.dev}/share/gir-1.0"
       "-g ${gtk3.dev}/share/gir-1.0"
       "-g ${pango.dev}/share/gir-1.0"
diff --git a/modules/hyprland/hyprlock.nix b/modules/hyprland/hyprlock.nix
deleted file mode 100644
index e372585d..00000000
--- a/modules/hyprland/hyprlock.nix
+++ /dev/null
@@ -1,64 +0,0 @@
-{
-  config,
-  hyprlock,
-  lib,
-  ...
-}: let
-  inherit (lib) optionalString;
-  inherit (config.vars) mainMonitor;
-
-  monitor = optionalString (mainMonitor != null) mainMonitor;
-in {
-  imports = [hyprlock.homeManagerModules.default];
-
-  programs.hyprlock = {
-    enable = true;
-    general = {
-      hide_cursor = false;
-    };
-
-    backgrounds = [
-      {
-        path = "screenshot";
-        blur_size = 5;
-        blur_passes = 2;
-        vibrancy_darkness = 0.0;
-        brightness = 1.0;
-      }
-    ];
-
-    input-fields = [
-      {
-        inherit monitor;
-        size.height = 70;
-        fade_on_empty = false;
-        outer_color = "rgba(10, 10, 10, 1.0)";
-        inner_color = "rgb(151515)";
-        font_color = "rgba(240, 240, 240, 1.0)"; # This is the dot color
-        capslock_color = "rgba(171, 12, 8, 1.0)";
-        placeholder_text = let
-          span = "<span foreground='##cccccc' style='italic' size='15pt' allow_breaks='true'>";
-          mkSpan = s: "${span}${s}</span>";
-        in
-          mkSpan "\r$PROMPT";
-      }
-    ];
-
-    labels = [
-      {
-        inherit monitor;
-        text = "$TIME";
-        font_size = 80;
-        font_family = "Ubuntu Mono";
-        position.y = 240;
-        shadow_passes = 3;
-      }
-      {
-        inherit monitor;
-        text = "<i> Groovy </i>";
-        font_family = "Ubuntu Mono";
-        shadow_passes = 3;
-      }
-    ];
-  };
-}
diff --git a/modules/hyprland/security.nix b/modules/hyprland/security.nix
index 8f42b78d..bc1149d7 100644
--- a/modules/hyprland/security.nix
+++ b/modules/hyprland/security.nix
@@ -14,7 +14,6 @@ in {
     ../greetd
   ];
 
-  security.pam.services.hyprlock = {};
   services.gnome.gnome-keyring.enable = true;
 
   home-manager.users.${mainUser} = let
@@ -23,16 +22,14 @@ in {
       name = "lock";
       runtimeInputs = [
         hmCfg.programs.ags.finalPackage
-        hmCfg.programs.hyprlock.package
       ];
       text = ''
         ags -r 'Tablet.setLaptopMode()'
-        hyprlock
+        ags -b lockscreen -c ~/.config/ags/lockscreen.js
       '';
     };
   in {
     imports = [
-      ./hyprlock.nix
       hypridle.homeManagerModules.default
     ];