1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.console;
9
10 makeColor = i: lib.concatMapStringsSep "," (x: "0x" + lib.substring (2 * i) 2 x);
11
12 isUnicode = lib.hasSuffix "UTF-8" (lib.toUpper config.i18n.defaultLocale);
13
14 optimizedKeymap =
15 pkgs.runCommand "keymap"
16 {
17 nativeBuildInputs = [ pkgs.buildPackages.kbd ];
18 LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
19 preferLocalBuild = true;
20 }
21 ''
22 loadkeys -b ${lib.optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
23 '';
24
25 # Sadly, systemd-vconsole-setup doesn't support binary keymaps.
26 vconsoleConf = pkgs.writeText "vconsole.conf" ''
27 KEYMAP=${cfg.keyMap}
28 ${lib.optionalString (cfg.font != null) "FONT=${cfg.font}"}
29 '';
30
31 consoleEnv =
32 kbd:
33 pkgs.buildEnv {
34 name = "console-env";
35 paths = [ kbd ] ++ cfg.packages;
36 pathsToLink = [
37 "/share/consolefonts"
38 "/share/consoletrans"
39 "/share/keymaps"
40 "/share/unimaps"
41 ];
42 };
43in
44
45{
46 ###### interface
47
48 options.console = {
49 enable = lib.mkEnableOption "virtual console" // {
50 default = true;
51 };
52
53 font = lib.mkOption {
54 type = with lib.types; nullOr (either str path);
55 default = null;
56 example = "LatArCyrHeb-16";
57 description = ''
58 The font used for the virtual consoles.
59 Can be `null`, a font name, or a path to a PSF font file.
60
61 Use `null` to let the kernel choose a built-in font.
62 The default is 8x16, and, as of Linux 5.3, Terminus 32 bold for display
63 resolutions of 2560x1080 and higher.
64 These fonts cover the [IBM437][] character set.
65
66 [IBM437]: https://en.wikipedia.org/wiki/Code_page_437
67 '';
68 };
69
70 keyMap = lib.mkOption {
71 type = with lib.types; either str path;
72 default = "us";
73 example = "fr";
74 description = ''
75 The keyboard mapping table for the virtual consoles.
76 '';
77 };
78
79 colors = lib.mkOption {
80 type = with lib.types; listOf (strMatching "[[:xdigit:]]{6}");
81 default = [ ];
82 example = [
83 "002b36"
84 "dc322f"
85 "859900"
86 "b58900"
87 "268bd2"
88 "d33682"
89 "2aa198"
90 "eee8d5"
91 "002b36"
92 "cb4b16"
93 "586e75"
94 "657b83"
95 "839496"
96 "6c71c4"
97 "93a1a1"
98 "fdf6e3"
99 ];
100 description = ''
101 The 16 colors palette used by the virtual consoles.
102 Leave empty to use the default colors.
103 Colors must be in hexadecimal format and listed in
104 order from color 0 to color 15.
105 '';
106
107 };
108
109 packages = lib.mkOption {
110 type = lib.types.listOf lib.types.package;
111 default = [ ];
112 description = ''
113 List of additional packages that provide console fonts, keymaps and
114 other resources for virtual consoles use.
115 '';
116 };
117
118 useXkbConfig = lib.mkOption {
119 type = lib.types.bool;
120 default = false;
121 description = ''
122 If set, configure the virtual console keymap from the xserver
123 keyboard settings.
124 '';
125 };
126
127 earlySetup = lib.mkOption {
128 default = false;
129 type = lib.types.bool;
130 description = ''
131 Enable setting virtual console options as early as possible (in initrd).
132 '';
133 };
134
135 };
136
137 ###### implementation
138
139 config = lib.mkMerge [
140 {
141 console.keyMap =
142 with config.services.xserver;
143 lib.mkIf cfg.useXkbConfig (
144 pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
145 '${pkgs.buildPackages.ckbcomp}/bin/ckbcomp' \
146 ${
147 lib.optionalString (
148 config.environment.sessionVariables ? XKB_CONFIG_ROOT
149 ) "-I${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
150 } \
151 -model '${xkb.model}' -layout '${xkb.layout}' \
152 -option '${xkb.options}' -variant '${xkb.variant}' > "$out"
153 ''
154 );
155 }
156
157 (lib.mkIf cfg.enable (
158 lib.mkMerge [
159 {
160 environment.systemPackages = [ pkgs.kbd ];
161
162 # Let systemd-vconsole-setup.service do the work of setting up the
163 # virtual consoles.
164 environment.etc."vconsole.conf".source = vconsoleConf;
165 # Provide kbd with additional packages.
166 environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
167
168 boot.initrd.preLVMCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (
169 lib.mkBefore ''
170 kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
171 printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
172 loadkmap < ${optimizedKeymap}
173
174 ${lib.optionalString (cfg.earlySetup && cfg.font != null) ''
175 setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
176 ''}
177 ''
178 );
179
180 boot.initrd.systemd.contents = {
181 "/etc/vconsole.conf".source = vconsoleConf;
182 # Add everything if we want full console setup...
183 "/etc/kbd" = lib.mkIf cfg.earlySetup {
184 source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share";
185 };
186 # ...but only the keymaps if we don't
187 "/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) {
188 source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps";
189 };
190 };
191 boot.initrd.systemd.additionalUpstreamUnits = [
192 "systemd-vconsole-setup.service"
193 ];
194 boot.initrd.systemd.storePaths =
195 [
196 "${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
197 "${config.boot.initrd.systemd.package.kbd}/bin/setfont"
198 "${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
199 "${config.boot.initrd.systemd.package.kbd.gzip}/bin/gzip" # Fonts and keyboard layouts are compressed
200 ]
201 ++ lib.optionals (cfg.font != null && lib.hasPrefix builtins.storeDir cfg.font) [
202 "${cfg.font}"
203 ]
204 ++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
205 "${cfg.keyMap}"
206 ];
207
208 systemd.additionalUpstreamSystemUnits = [
209 "systemd-vconsole-setup.service"
210 ];
211
212 systemd.services.reload-systemd-vconsole-setup = {
213 description = "Reset console on configuration changes";
214 wantedBy = [ "multi-user.target" ];
215 restartTriggers = [
216 vconsoleConf
217 (consoleEnv pkgs.kbd)
218 ];
219 reloadIfChanged = true;
220 serviceConfig = {
221 RemainAfterExit = true;
222 ExecStart = "${pkgs.coreutils}/bin/true";
223 ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
224 };
225 };
226 }
227
228 (lib.mkIf (cfg.colors != [ ]) {
229 boot.kernelParams = [
230 "vt.default_red=${makeColor 0 cfg.colors}"
231 "vt.default_grn=${makeColor 1 cfg.colors}"
232 "vt.default_blu=${makeColor 2 cfg.colors}"
233 ];
234 })
235
236 (lib.mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
237 boot.initrd.extraUtilsCommands = ''
238 mkdir -p $out/share/consolefonts
239 ${
240 if lib.substring 0 1 cfg.font == "/" then
241 ''
242 font="${cfg.font}"
243 ''
244 else
245 ''
246 font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
247 ''
248 }
249 if [[ $font == *.gz ]]; then
250 gzip -cd $font > $out/share/consolefonts/font.psf
251 else
252 cp -L $font $out/share/consolefonts/font.psf
253 fi
254 '';
255 })
256 ]
257 ))
258 ];
259
260 imports = [
261 (lib.mkRenamedOptionModule [ "i18n" "consoleFont" ] [ "console" "font" ])
262 (lib.mkRenamedOptionModule [ "i18n" "consoleKeyMap" ] [ "console" "keyMap" ])
263 (lib.mkRenamedOptionModule [ "i18n" "consoleColors" ] [ "console" "colors" ])
264 (lib.mkRenamedOptionModule [ "i18n" "consolePackages" ] [ "console" "packages" ])
265 (lib.mkRenamedOptionModule [ "i18n" "consoleUseXkbConfig" ] [ "console" "useXkbConfig" ])
266 (lib.mkRenamedOptionModule [ "boot" "earlyVconsoleSetup" ] [ "console" "earlySetup" ])
267 (lib.mkRenamedOptionModule [ "boot" "extraTTYs" ] [ "console" "extraTTYs" ])
268 (lib.mkRemovedOptionModule [ "console" "extraTTYs" ] ''
269 Since NixOS switched to systemd (circa 2012), TTYs have been spawned on
270 demand, so there is no need to configure them manually.
271 '')
272 ];
273}