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