1{
2 config,
3 lib,
4 options,
5 pkgs,
6 ...
7}:
8
9with lib;
10
11let
12
13 plymouth = pkgs.plymouth.override {
14 systemd = config.boot.initrd.systemd.package;
15 };
16
17 cfg = config.boot.plymouth;
18 opt = options.boot.plymouth;
19
20 nixosBreezePlymouth = pkgs.plasma5Packages.breeze-plymouth.override {
21 logoFile = cfg.logo;
22 logoName = "nixos";
23 osName = "NixOS";
24 osVersion = config.system.nixos.release;
25 };
26
27 plymouthLogos = pkgs.runCommand "plymouth-logos" { inherit (cfg) logo; } ''
28 mkdir -p $out
29
30 # For themes that are compiled with PLYMOUTH_LOGO_FILE
31 mkdir -p $out/etc/plymouth
32 ln -s $logo $out/etc/plymouth/logo.png
33
34 # Logo for bgrt theme
35 # Note this is technically an abuse of watermark for the bgrt theme
36 # See: https://gitlab.freedesktop.org/plymouth/plymouth/-/issues/95#note_813768
37 mkdir -p $out/share/plymouth/themes/spinner
38 ln -s $logo $out/share/plymouth/themes/spinner/watermark.png
39
40 # Logo for spinfinity theme
41 # See: https://gitlab.freedesktop.org/plymouth/plymouth/-/issues/106
42 mkdir -p $out/share/plymouth/themes/spinfinity
43 ln -s $logo $out/share/plymouth/themes/spinfinity/header-image.png
44
45 # Logo for catppuccin (two-step) theme
46 for flavour in mocha macchiato latte frappe
47 do
48 mkdir -p $out/share/plymouth/themes/catppuccin-"$flavour"
49 ln -s $logo $out/share/plymouth/themes/catppuccin-"$flavour"/header-image.png
50 done
51 '';
52
53 themesEnv = pkgs.buildEnv {
54 name = "plymouth-themes";
55 paths = [
56 plymouth
57 plymouthLogos
58 ] ++ cfg.themePackages;
59 };
60
61 configFile = pkgs.writeText "plymouthd.conf" ''
62 [Daemon]
63 ShowDelay=0
64 DeviceTimeout=8
65 Theme=${cfg.theme}
66 ${cfg.extraConfig}
67 '';
68
69in
70
71{
72
73 options = {
74
75 boot.plymouth = {
76
77 enable = mkEnableOption "Plymouth boot splash screen";
78
79 font = mkOption {
80 default = "${pkgs.dejavu_fonts.minimal}/share/fonts/truetype/DejaVuSans.ttf";
81 defaultText = literalExpression ''"''${pkgs.dejavu_fonts.minimal}/share/fonts/truetype/DejaVuSans.ttf"'';
82 type = types.path;
83 description = ''
84 Font file made available for displaying text on the splash screen.
85 '';
86 };
87
88 themePackages = mkOption {
89 default = lib.optional (cfg.theme == "breeze") nixosBreezePlymouth;
90 defaultText = literalMD ''
91 A NixOS branded variant of the breeze theme when
92 `config.${opt.theme} == "breeze"`, otherwise
93 `[ ]`.
94 '';
95 type = types.listOf types.package;
96 description = ''
97 Extra theme packages for plymouth.
98 '';
99 };
100
101 theme = mkOption {
102 default = "bgrt";
103 type = types.str;
104 description = ''
105 Splash screen theme.
106 '';
107 };
108
109 logo = mkOption {
110 type = types.path;
111 # Dimensions are 48x48 to match GDM logo
112 default = "${pkgs.nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png";
113 defaultText = literalExpression ''"''${pkgs.nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png"'';
114 example = literalExpression ''
115 pkgs.fetchurl {
116 url = "https://nixos.org/logo/nixos-hires.png";
117 sha256 = "1ivzgd7iz0i06y36p8m5w48fd8pjqwxhdaavc0pxs7w1g7mcy5si";
118 }
119 '';
120 description = ''
121 Logo which is displayed on the splash screen.
122 Currently supports PNG file format only.
123 '';
124 };
125
126 extraConfig = mkOption {
127 type = types.lines;
128 default = "";
129 description = ''
130 Literal string to append to `configFile`
131 and the config file generated by the plymouth module.
132 '';
133 };
134
135 };
136
137 };
138
139 config = mkIf cfg.enable {
140
141 boot.kernelParams = [ "splash" ];
142
143 # To be discoverable by systemd.
144 environment.systemPackages = [ plymouth ];
145
146 environment.etc."plymouth/plymouthd.conf".source = configFile;
147 environment.etc."plymouth/plymouthd.defaults".source =
148 "${plymouth}/share/plymouth/plymouthd.defaults";
149 environment.etc."plymouth/logo.png".source = cfg.logo;
150 environment.etc."plymouth/themes".source = "${themesEnv}/share/plymouth/themes";
151 # XXX: Needed because we supply a different set of plugins in initrd.
152 environment.etc."plymouth/plugins".source = "${plymouth}/lib/plymouth";
153
154 systemd.tmpfiles.rules = [
155 "d /run/plymouth 0755 root root 0 -"
156 "L+ /run/plymouth/plymouthd.defaults - - - - /etc/plymouth/plymouthd.defaults"
157 "L+ /run/plymouth/themes - - - - /etc/plymouth/themes"
158 "L+ /run/plymouth/plugins - - - - /etc/plymouth/plugins"
159 ];
160
161 systemd.packages = [ plymouth ];
162
163 systemd.services.plymouth-kexec.wantedBy = [ "kexec.target" ];
164 systemd.services.plymouth-halt.wantedBy = [ "halt.target" ];
165 systemd.services.plymouth-quit-wait.wantedBy = [ "multi-user.target" ];
166 systemd.services.plymouth-quit.wantedBy = [ "multi-user.target" ];
167 systemd.services.plymouth-poweroff.wantedBy = [ "poweroff.target" ];
168 systemd.services.plymouth-reboot.wantedBy = [ "reboot.target" ];
169 systemd.services.plymouth-read-write.wantedBy = [ "sysinit.target" ];
170 systemd.services.systemd-ask-password-plymouth.wantedBy = [ "multi-user.target" ];
171 systemd.paths.systemd-ask-password-plymouth.wantedBy = [ "multi-user.target" ];
172
173 # Prevent Plymouth taking over the screen during system updates.
174 systemd.services.plymouth-start.restartIfChanged = false;
175
176 boot.initrd.systemd = {
177 extraBin.plymouth = "${plymouth}/bin/plymouth"; # for the recovery shell
178 storePaths = [
179 "${lib.getBin config.boot.initrd.systemd.package}/bin/systemd-tty-ask-password-agent"
180 "${plymouth}/bin/plymouthd"
181 "${plymouth}/sbin/plymouthd"
182 ];
183 packages = [ plymouth ]; # systemd units
184 contents = {
185 # Files
186 "/etc/plymouth/plymouthd.conf".source = configFile;
187 "/etc/plymouth/logo.png".source = cfg.logo;
188 "/etc/plymouth/plymouthd.defaults".source = "${plymouth}/share/plymouth/plymouthd.defaults";
189 # Directories
190 "/etc/plymouth/plugins".source = pkgs.runCommand "plymouth-initrd-plugins" { } ''
191 # Check if the actual requested theme is here
192 if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then
193 echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages"
194 exit 1
195 fi
196
197 moduleName="$(sed -n 's,ModuleName *= *,,p' ${themesEnv}/share/plymouth/themes/${cfg.theme}/${cfg.theme}.plymouth)"
198
199 mkdir -p $out/renderers
200 # module might come from a theme
201 cp ${themesEnv}/lib/plymouth/*.so $out
202 cp ${plymouth}/lib/plymouth/renderers/*.so $out/renderers
203 # useless in the initrd, and adds several megabytes to the closure
204 rm $out/renderers/x11.so
205 '';
206 "/etc/plymouth/themes".source = pkgs.runCommand "plymouth-initrd-themes" { } ''
207 # Check if the actual requested theme is here
208 if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then
209 echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages"
210 exit 1
211 fi
212
213 mkdir -p $out/${cfg.theme}
214 cp -r ${themesEnv}/share/plymouth/themes/${cfg.theme}/* $out/${cfg.theme}
215 # Copy more themes if the theme depends on others
216 for theme in $(grep -hRo '/share/plymouth/themes/.*$' $out | xargs -n1 basename); do
217 if [[ -d "${themesEnv}/share/plymouth/themes/$theme" ]]; then
218 if [[ ! -d "$out/$theme" ]]; then
219 echo "Adding dependent theme: $theme"
220 mkdir -p "$out/$theme"
221 cp -r "${themesEnv}/share/plymouth/themes/$theme"/* "$out/$theme"
222 fi
223 else
224 echo "Missing theme dependency: $theme"
225 fi
226 done
227 # Fixup references
228 for theme in $out/*/*.plymouth; do
229 sed -i "s,${builtins.storeDir}/.*/share/plymouth/themes,$out," "$theme"
230 done
231 '';
232
233 # Fonts
234 "/etc/plymouth/fonts".source = pkgs.runCommand "plymouth-initrd-fonts" { } ''
235 mkdir -p $out
236 cp ${escapeShellArg cfg.font} $out
237 '';
238 "/etc/fonts/fonts.conf".text = ''
239 <?xml version="1.0"?>
240 <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
241 <fontconfig>
242 <dir>/etc/plymouth/fonts</dir>
243 </fontconfig>
244 '';
245 };
246 # Properly enable units. These are the units that arch copies
247 services = {
248 plymouth-halt.wantedBy = [ "halt.target" ];
249 plymouth-kexec.wantedBy = [ "kexec.target" ];
250 plymouth-poweroff.wantedBy = [ "poweroff.target" ];
251 plymouth-quit-wait.wantedBy = [ "multi-user.target" ];
252 plymouth-quit.wantedBy = [ "multi-user.target" ];
253 plymouth-read-write.wantedBy = [ "sysinit.target" ];
254 plymouth-reboot.wantedBy = [ "reboot.target" ];
255 plymouth-start.wantedBy = [
256 "initrd-switch-root.target"
257 "sysinit.target"
258 ];
259 plymouth-switch-root-initramfs.wantedBy = [
260 "halt.target"
261 "kexec.target"
262 "plymouth-switch-root-initramfs.service"
263 "poweroff.target"
264 "reboot.target"
265 ];
266 plymouth-switch-root.wantedBy = [ "initrd-switch-root.target" ];
267 };
268 # Link in runtime files before starting
269 services.plymouth-start.preStart = ''
270 mkdir -p /run/plymouth
271 ln -sf /etc/plymouth/{plymouthd.defaults,themes,plugins} /run/plymouth/
272 '';
273 };
274
275 # Insert required udev rules. We take stage 2 systemd because the udev
276 # rules are only generated when building with logind.
277 boot.initrd.services.udev.packages = [
278 (pkgs.runCommand "initrd-plymouth-udev-rules" { } ''
279 mkdir -p $out/etc/udev/rules.d
280 cp ${config.systemd.package.out}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out/etc/udev/rules.d
281 sed -i '/loginctl/d' $out/etc/udev/rules.d/71-seat.rules
282 '')
283 ];
284
285 boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
286 copy_bin_and_libs ${plymouth}/bin/plymouth
287 copy_bin_and_libs ${plymouth}/bin/plymouthd
288
289 # Check if the actual requested theme is here
290 if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then
291 echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages"
292 exit 1
293 fi
294
295 moduleName="$(sed -n 's,ModuleName *= *,,p' ${themesEnv}/share/plymouth/themes/${cfg.theme}/${cfg.theme}.plymouth)"
296
297 mkdir -p $out/lib/plymouth/renderers
298 # module might come from a theme
299 cp ${themesEnv}/lib/plymouth/*.so $out/lib/plymouth
300 cp ${plymouth}/lib/plymouth/renderers/*.so $out/lib/plymouth/renderers
301 # useless in the initrd, and adds several megabytes to the closure
302 rm $out/lib/plymouth/renderers/x11.so
303
304 mkdir -p $out/share/plymouth/themes
305 cp ${plymouth}/share/plymouth/plymouthd.defaults $out/share/plymouth
306
307 # Copy themes into working directory for patching
308 mkdir themes
309
310 # Use -L to copy the directories proper, not the symlinks to them.
311 # Copy all themes because they're not large assets, and bgrt depends on the ImageDir of
312 # the spinner theme.
313 cp -r -L ${themesEnv}/share/plymouth/themes/* themes
314
315 # Patch out any attempted references to the theme or plymouth's themes directory
316 chmod -R +w themes
317 find themes -type f | while read file
318 do
319 sed -i "s,${builtins.storeDir}/.*/share/plymouth/themes,$out/share/plymouth/themes,g" $file
320 done
321
322 # Install themes
323 cp -r themes/* $out/share/plymouth/themes
324
325 # Install logo
326 mkdir -p $out/etc/plymouth
327 cp -r -L ${themesEnv}/etc/plymouth $out/etc
328
329 # Setup font
330 mkdir -p $out/share/fonts
331 cp ${cfg.font} $out/share/fonts
332 mkdir -p $out/etc/fonts
333 cat > $out/etc/fonts/fonts.conf <<EOF
334 <?xml version="1.0"?>
335 <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
336 <fontconfig>
337 <dir>$out/share/fonts</dir>
338 </fontconfig>
339 EOF
340 '';
341
342 boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) ''
343 $out/bin/plymouthd --help >/dev/null
344 $out/bin/plymouth --help >/dev/null
345 '';
346
347 boot.initrd.extraUdevRulesCommands = mkIf (!config.boot.initrd.systemd.enable) ''
348 cp ${config.systemd.package}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out
349 sed -i '/loginctl/d' $out/71-seat.rules
350 '';
351
352 # We use `mkAfter` to ensure that LUKS password prompt would be shown earlier than the splash screen.
353 boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (mkAfter ''
354 plymouth_enabled=1
355 for o in $(cat /proc/cmdline); do
356 case $o in
357 plymouth.enable=0)
358 plymouth_enabled=0
359 ;;
360 esac
361 done
362
363 if [ "$plymouth_enabled" != 0 ]; then
364 mkdir -p /etc/plymouth
365 mkdir -p /run/plymouth
366 ln -s $extraUtils/etc/plymouth/logo.png /etc/plymouth/logo.png
367 ln -s ${configFile} /etc/plymouth/plymouthd.conf
368 ln -s $extraUtils/share/plymouth/plymouthd.defaults /run/plymouth/plymouthd.defaults
369 ln -s $extraUtils/share/plymouth/themes /run/plymouth/themes
370 ln -s $extraUtils/lib/plymouth /run/plymouth/plugins
371 ln -s $extraUtils/etc/fonts /etc/fonts
372
373 plymouthd --mode=boot --pid-file=/run/plymouth/pid --attach-to-session
374 plymouth show-splash
375 fi
376 '');
377
378 boot.initrd.postMountCommands = mkIf (!config.boot.initrd.systemd.enable) ''
379 if [ "$plymouth_enabled" != 0 ]; then
380 plymouth update-root-fs --new-root-dir="$targetRoot"
381 fi
382 '';
383
384 # `mkBefore` to ensure that any custom prompts would be visible.
385 boot.initrd.preFailCommands = mkIf (!config.boot.initrd.systemd.enable) (mkBefore ''
386 if [ "$plymouth_enabled" != 0 ]; then
387 plymouth quit --wait
388 fi
389 '');
390
391 };
392
393}