mirror of
https://github.com/zoriya/flake.git
synced 2026-06-02 10:45:59 +00:00
Add lockscreen
This commit is contained in:
Generated
+42
@@ -1,5 +1,25 @@
|
||||
{
|
||||
"nodes": {
|
||||
"astal-auth": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1718556609,
|
||||
"narHash": "sha256-EeYnp9HEpQXNGweF/Ea6ZfjTSV6biHFSsZV010nDHaM=",
|
||||
"owner": "astal-sh",
|
||||
"repo": "auth",
|
||||
"rev": "b35a38aa93670f6be06202f2ad3066a227f0a1b9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "astal-sh",
|
||||
"repo": "auth",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"astal-river": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -306,6 +326,26 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gtk-session-lock": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1713713322,
|
||||
"narHash": "sha256-A9U/BnzdypE1rt53uhw5X4JQkayR8CkD2Qhn/vhmUSU=",
|
||||
"owner": "Cu3PO42",
|
||||
"repo": "gtk-session-lock",
|
||||
"rev": "b9ddb2792b613d14622acada73c64f16a2635b40",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "Cu3PO42",
|
||||
"repo": "gtk-session-lock",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hercules-ci-effects": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_2",
|
||||
@@ -523,9 +563,11 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"astal-auth": "astal-auth",
|
||||
"astal-river": "astal-river",
|
||||
"flood": "flood",
|
||||
"ghostty": "ghostty",
|
||||
"gtk-session-lock": "gtk-session-lock",
|
||||
"home-manager": "home-manager",
|
||||
"impermanence": "impermanence",
|
||||
"neovim-nightly": "neovim-nightly",
|
||||
|
||||
@@ -29,7 +29,15 @@
|
||||
flake = false;
|
||||
};
|
||||
astal-river = {
|
||||
url ="github:zoriya/astal-river";
|
||||
url = "github:zoriya/astal-river";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
astal-auth = {
|
||||
url = "github:astal-sh/auth";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
gtk-session-lock = {
|
||||
url = "github:Cu3PO42/gtk-session-lock";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
@@ -44,7 +52,6 @@
|
||||
impermanence,
|
||||
nixos-hardware,
|
||||
nix-index-database,
|
||||
astal-river,
|
||||
...
|
||||
} @ inputs: let
|
||||
user = "zoriya";
|
||||
@@ -81,7 +88,7 @@
|
||||
home-manager = {
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
extraSpecialArgs = { inherit inputs; };
|
||||
extraSpecialArgs = {inherit inputs;};
|
||||
users.${user} = {
|
||||
imports = [
|
||||
./modules/cli/home.nix
|
||||
|
||||
+86
-83
@@ -40,92 +40,95 @@ in {
|
||||
# For rider
|
||||
FLATPAK_ENABLE_SDK_EXT = "*";
|
||||
};
|
||||
xdg.enable = true;
|
||||
xdg.mimeApps = {
|
||||
xdg = {
|
||||
enable = true;
|
||||
defaultApplications = {
|
||||
"x-scheme-handler/http" = browser;
|
||||
"x-scheme-handler/https" = browser;
|
||||
"x-scheme-handler/about" = browser;
|
||||
"x-scheme-handler/unknown" = browser;
|
||||
"x-scheme-handler/magnet" = browser;
|
||||
"application/oxps" = pdf;
|
||||
"application/pdf" = pdf;
|
||||
"application/epub+zip" = pdf;
|
||||
"application/x-fictionbook+xml" = pdf;
|
||||
"text/tcl" = editor;
|
||||
"text/html" = editor;
|
||||
"text/x-makefile" = editor;
|
||||
"text/vbscript" = editor;
|
||||
"text/spreadsheet" = editor;
|
||||
"text/x-tex" = editor;
|
||||
"text/x-c++hdr" = editor;
|
||||
"text/x-pascal" = editor;
|
||||
"text/x-moc" = editor;
|
||||
"text/x-chdr" = editor;
|
||||
"text/tab-separated-values" = editor;
|
||||
"text/x-python" = editor;
|
||||
"text/x-csrc" = editor;
|
||||
"text/x-c++src" = editor;
|
||||
"text/x-java" = editor;
|
||||
"text/plain" = editor;
|
||||
"text/csv" = editor;
|
||||
"video/x-flic" = player;
|
||||
"video/mpeg" = player;
|
||||
"video/x-ms-wmv" = player;
|
||||
"video/vnd.rn-realvideo" = player;
|
||||
"video/x-theora+ogg" = player;
|
||||
"video/dv" = player;
|
||||
"video/webm" = player;
|
||||
"video/ogg" = player;
|
||||
"video/quicktime" = player;
|
||||
"video/x-flv" = player;
|
||||
"video/x-ogm+ogg" = player;
|
||||
"video/3gpp2" = player;
|
||||
"video/mp2t" = player;
|
||||
"video/x-msvideo" = player;
|
||||
"video/3gpp" = player;
|
||||
"video/x-matroska" = player;
|
||||
"video/vnd.mpegurl" = player;
|
||||
"video/mp4" = player;
|
||||
"audio/aac" = player;
|
||||
"audio/ac3" = player;
|
||||
"audio/x-wavpack" = player;
|
||||
"audio/webm" = player;
|
||||
"audio/x-ms-wma" = player;
|
||||
"audio/flac" = player;
|
||||
"audio/x-scpls" = player;
|
||||
"audio/mpeg" = player;
|
||||
"audio/x-mpegurl" = player;
|
||||
"audio/x-ms-asx" = player;
|
||||
"audio/vnd.rn-realaudio" = player;
|
||||
"audio/x-wav" = player;
|
||||
"audio/vnd.dts" = player;
|
||||
"audio/x-adpcm" = player;
|
||||
"audio/x-vorbis+ogg" = player;
|
||||
"audio/mp4" = player;
|
||||
"audio/x-tta" = player;
|
||||
"audio/x-musepack" = player;
|
||||
"audio/AMR" = player;
|
||||
"audio/x-matroska" = player;
|
||||
"audio/x-ape" = player;
|
||||
"audio/x-aiff" = player;
|
||||
"audio/vnd.dts.hd" = player;
|
||||
"audio/ogg" = player;
|
||||
"audio/mp2" = player;
|
||||
mime.enable = true;
|
||||
mimeApps = {
|
||||
enable = true;
|
||||
defaultApplications = {
|
||||
"x-scheme-handler/http" = browser;
|
||||
"x-scheme-handler/https" = browser;
|
||||
"x-scheme-handler/about" = browser;
|
||||
"x-scheme-handler/unknown" = browser;
|
||||
"x-scheme-handler/magnet" = browser;
|
||||
"application/oxps" = pdf;
|
||||
"application/pdf" = pdf;
|
||||
"application/epub+zip" = pdf;
|
||||
"application/x-fictionbook+xml" = pdf;
|
||||
"text/tcl" = editor;
|
||||
"text/html" = editor;
|
||||
"text/x-makefile" = editor;
|
||||
"text/vbscript" = editor;
|
||||
"text/spreadsheet" = editor;
|
||||
"text/x-tex" = editor;
|
||||
"text/x-c++hdr" = editor;
|
||||
"text/x-pascal" = editor;
|
||||
"text/x-moc" = editor;
|
||||
"text/x-chdr" = editor;
|
||||
"text/tab-separated-values" = editor;
|
||||
"text/x-python" = editor;
|
||||
"text/x-csrc" = editor;
|
||||
"text/x-c++src" = editor;
|
||||
"text/x-java" = editor;
|
||||
"text/plain" = editor;
|
||||
"text/csv" = editor;
|
||||
"video/x-flic" = player;
|
||||
"video/mpeg" = player;
|
||||
"video/x-ms-wmv" = player;
|
||||
"video/vnd.rn-realvideo" = player;
|
||||
"video/x-theora+ogg" = player;
|
||||
"video/dv" = player;
|
||||
"video/webm" = player;
|
||||
"video/ogg" = player;
|
||||
"video/quicktime" = player;
|
||||
"video/x-flv" = player;
|
||||
"video/x-ogm+ogg" = player;
|
||||
"video/3gpp2" = player;
|
||||
"video/mp2t" = player;
|
||||
"video/x-msvideo" = player;
|
||||
"video/3gpp" = player;
|
||||
"video/x-matroska" = player;
|
||||
"video/vnd.mpegurl" = player;
|
||||
"video/mp4" = player;
|
||||
"audio/aac" = player;
|
||||
"audio/ac3" = player;
|
||||
"audio/x-wavpack" = player;
|
||||
"audio/webm" = player;
|
||||
"audio/x-ms-wma" = player;
|
||||
"audio/flac" = player;
|
||||
"audio/x-scpls" = player;
|
||||
"audio/mpeg" = player;
|
||||
"audio/x-mpegurl" = player;
|
||||
"audio/x-ms-asx" = player;
|
||||
"audio/vnd.rn-realaudio" = player;
|
||||
"audio/x-wav" = player;
|
||||
"audio/vnd.dts" = player;
|
||||
"audio/x-adpcm" = player;
|
||||
"audio/x-vorbis+ogg" = player;
|
||||
"audio/mp4" = player;
|
||||
"audio/x-tta" = player;
|
||||
"audio/x-musepack" = player;
|
||||
"audio/AMR" = player;
|
||||
"audio/x-matroska" = player;
|
||||
"audio/x-ape" = player;
|
||||
"audio/x-aiff" = player;
|
||||
"audio/vnd.dts.hd" = player;
|
||||
"audio/ogg" = player;
|
||||
"audio/mp2" = player;
|
||||
};
|
||||
};
|
||||
};
|
||||
xdg.userDirs = {
|
||||
enable = true;
|
||||
download = "${config.home.homeDirectory}/downloads";
|
||||
desktop = config.home.homeDirectory;
|
||||
userDirs = {
|
||||
enable = true;
|
||||
download = "${config.home.homeDirectory}/downloads";
|
||||
desktop = config.home.homeDirectory;
|
||||
|
||||
documents = "${config.home.homeDirectory}/stuff";
|
||||
music = "${config.home.homeDirectory}/stuff";
|
||||
templates = "${config.home.homeDirectory}/stuff";
|
||||
videos = "${config.home.homeDirectory}/stuff";
|
||||
pictures = "${config.home.homeDirectory}/stuff";
|
||||
publicShare = "${config.home.homeDirectory}/stuff";
|
||||
documents = "${config.home.homeDirectory}/stuff";
|
||||
music = "${config.home.homeDirectory}/stuff";
|
||||
templates = "${config.home.homeDirectory}/stuff";
|
||||
videos = "${config.home.homeDirectory}/stuff";
|
||||
pictures = "${config.home.homeDirectory}/stuff";
|
||||
publicShare = "${config.home.homeDirectory}/stuff";
|
||||
};
|
||||
};
|
||||
home.file.".face".source = ../../face.png;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import Gdk from "gi://Gdk";
|
||||
|
||||
import { Bar } from "./layouts/bar.js";
|
||||
import { Notifications } from "./layouts/notifications.js";
|
||||
import { OSD } from "./layouts/osd.js";
|
||||
import { Quicksettings } from "./layouts/quicksettings.js";
|
||||
import { lockscreen } from "./lockscreen.js";
|
||||
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import Gdk from "gi://Gdk";
|
||||
// @ts-ignore
|
||||
App.lock = lockscreen;
|
||||
|
||||
/**
|
||||
* @param {Array<(monitor: number) => Gtk.Window>} widgets
|
||||
@@ -26,6 +30,7 @@ export function forMonitors(widgets) {
|
||||
|
||||
display?.connect("monitor-removed", (disp, monitor) => {
|
||||
App.windows.forEach((win) => {
|
||||
// @ts-ignore
|
||||
if (win.gdkmonitor === monitor) App.removeWindow(win);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
++ [
|
||||
pkgs.libdbusmenu-gtk3
|
||||
inputs.astal-river.packages.x86_64-linux.default
|
||||
inputs.astal-auth.packages.x86_64-linux.default
|
||||
inputs.gtk-session-lock.packages.x86_64-linux.default
|
||||
];
|
||||
});
|
||||
in {
|
||||
@@ -43,7 +45,7 @@ in {
|
||||
|
||||
Service = {
|
||||
Type = "simple";
|
||||
ExecStart = "${pkgs.ags}/bin/ags";
|
||||
ExecStart = "${ags}/bin/ags";
|
||||
Restart = "always";
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
import Gdk from "gi://Gdk?version=3.0";
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import Lock from "gi://GtkSessionLock";
|
||||
import AstalAuth from "gi://AstalAuth";
|
||||
|
||||
import { Clock } from "./modules/clock.js";
|
||||
|
||||
export const DELAY = 500;
|
||||
|
||||
export const isLocked = Variable(false);
|
||||
export const prompt = Variable("");
|
||||
export const inputVisible = Variable(false);
|
||||
export const inputNeeded = Variable(false);
|
||||
export const error = Variable(
|
||||
/** @type {{type: "info" | "error" | "fail", message: string} | null} */ (
|
||||
null
|
||||
),
|
||||
);
|
||||
|
||||
const auth = new AstalAuth.Pam();
|
||||
let lock;
|
||||
const windows = [];
|
||||
|
||||
let hasInit = false;
|
||||
|
||||
function init() {
|
||||
if (hasInit) return;
|
||||
hasInit = true;
|
||||
auth.connect("auth-prompt-visible", (auth, msg) => {
|
||||
prompt.setValue(msg);
|
||||
inputVisible.setValue(true);
|
||||
inputNeeded.setValue(true);
|
||||
});
|
||||
auth.connect("auth-prompt-hidden", (auth, msg) => {
|
||||
prompt.setValue(msg);
|
||||
inputVisible.setValue(false);
|
||||
inputNeeded.setValue(true);
|
||||
});
|
||||
|
||||
auth.connect("auth-error", (_, msg) => {
|
||||
error.setValue({ message: msg, type: "error" });
|
||||
auth.supply_secret(null);
|
||||
});
|
||||
auth.connect("auth-info", (_, msg) => {
|
||||
error.setValue({ message: msg, type: "info" });
|
||||
auth.supply_secret(null);
|
||||
});
|
||||
|
||||
auth.connect("success", unlock);
|
||||
auth.connect("fail", (p, msg) => {
|
||||
error.setValue({ message: msg, type: "fail" });
|
||||
auth.start_authenticate();
|
||||
});
|
||||
|
||||
const display = Gdk.Display.get_default();
|
||||
display?.connect("monitor-added", (disp, monitor) => {
|
||||
if (!isLocked.value) return;
|
||||
const w = createWindow(monitor);
|
||||
lock.new_surface(w.window, w.monitor);
|
||||
w.window.show();
|
||||
});
|
||||
display?.connect("monitor-removed", (disp, monitor) => {
|
||||
if (!isLocked.value) return;
|
||||
windows.forEach((win) => {
|
||||
lock.unmap_lock_window(win.window);
|
||||
win.window.destroy();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function lockscreen() {
|
||||
init();
|
||||
lock = Lock.prepare_lock();
|
||||
lock.connect("locked", () => {});
|
||||
lock.connect("finished", unlock);
|
||||
|
||||
const display = Gdk.Display.get_default();
|
||||
const n = display?.get_n_monitors() || 1;
|
||||
for (let m = 0; m < n; m++) {
|
||||
const monitor = display?.get_monitor(m);
|
||||
// @ts-ignore
|
||||
createWindow(monitor);
|
||||
}
|
||||
|
||||
lock.lock_lock();
|
||||
isLocked.value = true;
|
||||
windows.map((w) => {
|
||||
lock.new_surface(w.window, w.monitor);
|
||||
w.window.show();
|
||||
});
|
||||
auth.start_authenticate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} password
|
||||
*/
|
||||
export function login(password) {
|
||||
inputNeeded.setValue(false);
|
||||
auth.supply_secret(password);
|
||||
}
|
||||
|
||||
function unlock() {
|
||||
isLocked.value = false;
|
||||
|
||||
// Wait for window's hide animations to finish
|
||||
Utils.timeout(DELAY, () => {
|
||||
windows.forEach((w) => {
|
||||
Lock.unmap_lock_window(w.window);
|
||||
});
|
||||
lock.unlock_and_destroy();
|
||||
windows.forEach((w) => {
|
||||
w.window.destroy();
|
||||
});
|
||||
lock = undefined;
|
||||
|
||||
Gdk.Display.get_default()?.sync();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Gdk.Monitor} monitor
|
||||
*/
|
||||
function createWindow(monitor) {
|
||||
const window = LockWindow();
|
||||
const win = { window, monitor };
|
||||
windows.push(win);
|
||||
return win;
|
||||
}
|
||||
|
||||
const LoginBox = () =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
spacing: 16,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hpack: "center",
|
||||
className: "avatar",
|
||||
css: `background-image: url("/home/${Utils.USER}/.face");`,
|
||||
}),
|
||||
Widget.Box({
|
||||
className: inputNeeded
|
||||
.bind()
|
||||
.as((n) => `entry-box ${n ? "" : "hidden"}`),
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
label: prompt.bind(),
|
||||
}),
|
||||
Widget.Separator(),
|
||||
Widget.Entry({
|
||||
hpack: "center",
|
||||
xalign: 0.5,
|
||||
visibility: inputVisible.bind(),
|
||||
sensitive: inputNeeded.bind(),
|
||||
onAccept: (self) => {
|
||||
login(self.text || "");
|
||||
self.text = "";
|
||||
},
|
||||
}).on("realize", (entry) => entry.grab_focus()),
|
||||
Widget.Label({
|
||||
label: error.bind().as((x) => x?.message || ""),
|
||||
visible: error.bind().as((x) => !!x),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const LockWindow = () =>
|
||||
new Gtk.Window({
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Revealer({
|
||||
reveal_child: isLocked.bind(),
|
||||
transition: "crossfade",
|
||||
transition_duration: DELAY,
|
||||
child: Widget.Box({
|
||||
css: `
|
||||
background-image: url("/home/${Utils.USER}/.cache/current-wallpaper");
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
`,
|
||||
vertical: true,
|
||||
child: LoginBox(),
|
||||
}),
|
||||
}),
|
||||
// .on("realize", (self) =>
|
||||
// Utils.idle(() => {
|
||||
// self.reveal_child = true;
|
||||
// }),
|
||||
// ),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -2,16 +2,18 @@ import GLib from "gi://GLib?version=2.0";
|
||||
|
||||
/**
|
||||
* @param {string | null | undefined} name
|
||||
* @param {string | null | undefined} fallback
|
||||
* @param {string | null | undefined} [fallback]
|
||||
*/
|
||||
export function icon(name, fallback) {
|
||||
if (!name) return fallback || "";
|
||||
|
||||
if (GLib.file_test(name, GLib.FileTest.EXISTS)) return name;
|
||||
if (typeof name !== "string") return name;
|
||||
|
||||
const sub = substitutes[name];
|
||||
if (sub && Utils.lookUpIcon(sub)) return sub;
|
||||
if (Utils.lookUpIcon(name)) return name;
|
||||
|
||||
if (GLib.file_test(name, GLib.FileTest.EXISTS)) return name;
|
||||
|
||||
return fallback || "";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { icon } from "../misc/utils.js";
|
||||
import { ArrowToggleButton, Menu } from "../misc/menu.js";
|
||||
|
||||
const systemtray = await Service.import("systemtray");
|
||||
@@ -36,7 +37,7 @@ const SysTrayItem = (item) =>
|
||||
css: "margin: 12px;",
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon({ icon: item.bind("icon") }),
|
||||
Widget.Icon({ icon: item.bind("icon").as(icon) }),
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
maxWidthChars: 28,
|
||||
|
||||
@@ -34,7 +34,8 @@ export const Tags = ({ monitor, labels, ...props }) =>
|
||||
);
|
||||
self.children.forEach((button, i) => {
|
||||
// We need to set this here because assigning children to self calls show_all() and ignore visibility
|
||||
button.visible = !!(occupied & (1 << i));
|
||||
// @ts-ignore
|
||||
button.visible = button.attribute;
|
||||
});
|
||||
},
|
||||
"changed",
|
||||
@@ -51,7 +52,7 @@ export const Tags = ({ monitor, labels, ...props }) =>
|
||||
const TagItem = ({ occupied, selected, urgent, i, output, label }) =>
|
||||
Widget.EventBox({
|
||||
classNames: [selected ? "accent" : "", urgent ? "secondary" : ""],
|
||||
visible: occupied,
|
||||
attribute: occupied || selected,
|
||||
onPrimaryClickRelease: () => {
|
||||
river.run_command_async(["set-focused-tags", `${1 << i}`], null);
|
||||
},
|
||||
|
||||
@@ -39,4 +39,6 @@
|
||||
gnome.gnome-bluetooth
|
||||
polkit_gnome
|
||||
];
|
||||
|
||||
security.pam.services.astal-auth = {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user