1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
{ depot, pkgs, ... }:
# The niri lock screen: swaylock with a renderer that draws its background,
# crystal-shader running crystal.frag. See crystal-lock.1 for what it does
# and why.
#
# swaylock is nixpkgs' own with one patch (rather than a fork), so upstream
# fixes keep coming and a nixpkgs bump that touches the patched code fails
# to build instead of silently mixing versions. crystal-swaylock.patch is
# the security-relevant part: the renderer (an absolute path after `--`,
# started without a shell) never talks to the compositor, it hands swaylock
# finished frames as dmabufs, which swaylock checks and shows; plus the
# channel telling it what happens, --unlock-delay with its sleep watcher,
# and the PAM service as a build flag. See its header and crystal-lock.1,
# SECURITY CONSIDERATIONS.
#
# Three Go programs, one module (standard library only):
#
# - crystal-lock (main.go, cache.go): picks the wallpaper and starts
# swaylock by absolute path; the paths are linked in below. cache.go keeps
# the wallpapers scaled down to the screen, so they decode fast. buildGo,
# static.
# - crystal-shader (crystal-shader/): the renderer. Its GPU side (gl.c:
# headless EGL on GBM) needs cgo, which buildGo cannot do (buildGo.7,
# CAVEATS), hence buildGoModule.
# - shader/: the renderer's state (messages to uniforms), pure Go.
#
# Tests (not run by the builds; crystal-shader has none and needs the
# GL headers):
#
# go test ./users/Profpatsch/crystal-lock/ ./users/Profpatsch/crystal-lock/shader/
let
swaylock = pkgs.swaylock.overrideAttrs (old: {
patches = (old.patches or [ ]) ++ [ ./crystal-swaylock.patch ];
# swaylock-sleep-watcher, for --unlock-delay
buildInputs = old.buildInputs ++ [ pkgs.systemd ];
mesonFlags = (old.mesonFlags or [ ]) ++ [ "-Dlogind=enabled" ];
# authenticate like the plain lock screen, against the existing
# security.pam.services.swaylock (niri.nix); also upstream's default,
# pinned here so that the fallback and this one cannot drift apart
env = (old.env or { }) // {
NIX_CFLAGS_COMPILE = toString (old.env.NIX_CFLAGS_COMPILE or "")
+ ''-DSWAYLOCK_PAM_SERVICE="swaylock"'';
};
});
crystal-shader = pkgs.buildGoModule {
pname = "crystal-shader";
version = "0";
# only what the build reads, so stray files (./tmp, binaries) never
# reach the store
src = depot.users.Profpatsch.exactSource ./. [
./go.mod
./shader/state.go
./crystal-shader/main.go
./crystal-shader/gl.c
./crystal-shader/gl.h
];
vendorHash = null;
subPackages = [ "crystal-shader" ];
# the root go.work lists modules this one does not see
env.GOWORK = "off";
nativeBuildInputs = [ pkgs.pkg-config ];
buildInputs = [ pkgs.libgbm pkgs.libglvnd ];
meta.mainProgram = "crystal-shader";
};
shader = pkgs.writeText "crystal.frag" (builtins.readFile ./crystal.frag);
bin = depot.nix.buildGo.program {
name = "crystal-lock";
srcs = [ ./main.go ./cache.go ];
x_defs = {
"main.swaylockBin" = "${swaylock}/bin/swaylock";
"main.crystalShader" = "${crystal-shader}/bin/crystal-shader";
"main.shader" = "${shader}";
"main.swaylock" = "${pkgs.swaylock}/bin/swaylock";
"main.systemctl" = "${pkgs.systemd}/bin/systemctl";
};
};
in
pkgs.runCommand "crystal-lock"
{
nativeBuildInputs = [ pkgs.installShellFiles ];
passthru = { inherit swaylock crystal-shader shader bin; };
meta.mainProgram = "crystal-lock";
} ''
mkdir --parents $out/bin
ln -s ${bin}/bin/crystal-lock $out/bin/crystal-lock
installManPage ${./crystal-lock.1}
''
|