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 "${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
196 "${config.boot.initrd.systemd.package.kbd}/bin/setfont"
197 "${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
198 ]
199 ++ lib.optionals (cfg.font != null && lib.hasPrefix builtins.storeDir cfg.font) [
200 "${cfg.font}"
201 ]
202 ++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
203 "${cfg.keyMap}"
204 ];
205
206 systemd.additionalUpstreamSystemUnits = [
207 "systemd-vconsole-setup.service"
208 ];
209
210 systemd.services.reload-systemd-vconsole-setup = {
211 description = "Reset console on configuration changes";
212 wantedBy = [ "multi-user.target" ];
213 restartTriggers = [
214 vconsoleConf
215 (consoleEnv pkgs.kbd)
216 ];
217 reloadIfChanged = true;
218 serviceConfig = {
219 RemainAfterExit = true;
220 ExecStart = "${pkgs.coreutils}/bin/true";
221 ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
222 };
223 };
224 }
225
226 (lib.mkIf (cfg.colors != [ ]) {
227 boot.kernelParams = [
228 "vt.default_red=${makeColor 0 cfg.colors}"
229 "vt.default_grn=${makeColor 1 cfg.colors}"
230 "vt.default_blu=${makeColor 2 cfg.colors}"
231 ];
232 })
233
234 (lib.mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
235 boot.initrd.extraUtilsCommands = ''
236 mkdir -p $out/share/consolefonts
237 ${
238 if lib.substring 0 1 cfg.font == "/" then
239 ''
240 font="${cfg.font}"
241 ''
242 else
243 ''
244 font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
245 ''
246 }
247 if [[ $font == *.gz ]]; then
248 gzip -cd $font > $out/share/consolefonts/font.psf
249 else
250 cp -L $font $out/share/consolefonts/font.psf
251 fi
252 '';
253 })
254 ]
255 ))
256 ];
257
258 imports = [
259 (lib.mkRenamedOptionModule [ "i18n" "consoleFont" ] [ "console" "font" ])
260 (lib.mkRenamedOptionModule [ "i18n" "consoleKeyMap" ] [ "console" "keyMap" ])
261 (lib.mkRenamedOptionModule [ "i18n" "consoleColors" ] [ "console" "colors" ])
262 (lib.mkRenamedOptionModule [ "i18n" "consolePackages" ] [ "console" "packages" ])
263 (lib.mkRenamedOptionModule [ "i18n" "consoleUseXkbConfig" ] [ "console" "useXkbConfig" ])
264 (lib.mkRenamedOptionModule [ "boot" "earlyVconsoleSetup" ] [ "console" "earlySetup" ])
265 (lib.mkRenamedOptionModule [ "boot" "extraTTYs" ] [ "console" "extraTTYs" ])
266 (lib.mkRemovedOptionModule [ "console" "extraTTYs" ] ''
267 Since NixOS switched to systemd (circa 2012), TTYs have been spawned on
268 demand, so there is no need to configure them manually.
269 '')
270 ];
271}