nixos/kmscon: RFC42 treatment, support version 10.0.0

This commit includes changes from #483195, #523569, and #523955.
This commit is contained in:
ccicnce113424
2026-05-25 21:15:13 +08:00
parent e1632511d0
commit 9b9e0a021a
2 changed files with 155 additions and 98 deletions

View File

@@ -2,6 +2,7 @@
config,
pkgs,
lib,
utils,
...
}:
let
@@ -10,8 +11,6 @@ let
mkEnableOption
mkOption
mkPackageOption
optional
optionals
types
;
@@ -22,7 +21,12 @@ let
configDir = pkgs.writeTextFile {
name = "kmscon-config";
destination = "/kmscon.conf";
text = cfg.extraConfig;
text =
let
mkKeyValue =
k: v: if lib.isBool v then (lib.optionalString (!v) "no-") + k else "${k}=${toString v}";
in
lib.generators.toKeyValue { inherit mkKeyValue; } (lib.filterAttrs (_: v: v != null) cfg.config);
};
baseLoginOptions = "-p";
@@ -55,58 +59,68 @@ in
Check `services.getty.autologinUser` instead.
'')
(lib.mkRemovedOptionModule [ "services" "kmscon" "fonts" ] ''
`services.kmscon.fonts` is removed.
Add your font to `fonts.packages` and configure it with
`services.kmscon.config.font-name` instead.
'')
(lib.mkRemovedOptionModule [ "services" "kmscon" "extraConfig" ] ''
`services.kmscon.extraConfig` is removed.
Add your configurations to the new `services.kmscon.config` instead.
'')
(lib.mkRenamedOptionModule [ "services" "kmscon" "term" ] [ "services" "kmscon" "config" "term" ])
(lib.mkRenamedOptionModule
[ "services" "kmscon" "hwRender" ]
[ "services" "kmscon" "config" "hwaccel" ]
)
];
options = {
services.kmscon = {
enable = mkEnableOption ''
Use kmscon instead of autovt.
use kmscon instead of autovt.
Kmscon is a simple terminal emulator based on linux kernel mode setting (KMS).
It is an attempt to replace the in-kernel VT implementation with a userspace console.
It is an attempt to replace the in-kernel VT implementation with a userspace console
'';
package = mkPackageOption pkgs "kmscon" { };
hwRender = mkEnableOption "3D hardware acceleration to render the console";
useXkbConfig = mkEnableOption ''
configure keymap from xserver keyboard settings.
fonts = mkOption {
description = "Fonts used by kmscon, in order of priority.";
default = null;
example = lib.literalExpression ''[ { name = "Source Code Pro"; package = pkgs.source-code-pro; } ]'';
type =
with types;
let
fontType = submodule {
options = {
name = mkOption {
type = str;
description = "Font name, as used by fontconfig.";
};
package = mkOption {
type = package;
description = "Package providing the font.";
};
};
If enabled, configurations under `services.xserver.xkb` will be injected into kmscon's configuration
'';
config = mkOption {
description = ''
Configuration for kmscon. See {manpage}`kmscon.conf(5)`
for available options.
'';
default = { };
type = types.submodule {
freeformType =
with types;
attrsOf (oneOf [
bool
int
str
]);
options = {
hwaccel = mkEnableOption "use hardware acceleration for rendering";
libseat = mkOption {
type = types.bool;
default = true;
description = ''
Whether to use libseat for session management.
This is the default for kmscon newer than 10.0.0 and prevents
launching another GUI from kmscon by `kmscon-launch-gui`.
'';
};
in
nullOr (nonEmptyListOf fontType);
};
useXkbConfig = mkEnableOption "configure keymap from xserver keyboard settings.";
term = mkOption {
description = "Value for the TERM environment variable.";
type = types.nullOr types.str;
default = null;
example = "xterm-256color";
};
extraConfig = mkOption {
description = "Extra contents of the kmscon.conf file.";
type = types.lines;
default = "";
example = "font-size=14";
};
};
};
extraOptions = mkOption {
@@ -124,30 +138,54 @@ in
assertion = gettyCfg.loginOptions == null;
message = "services.getty.loginOptions is not supported when services.kmscon is enabled.";
}
{
assertion = (cfg.config ? font-name) -> config.fonts.fontconfig.enable;
message = "Font configuration for kmscon requires fontconfig to be enabled.";
}
{
assertion = cfg.config.hwaccel -> config.hardware.graphics.enable;
message = "Hardware acceleration for kmscon requires `hardware.graphics.enable` to be true.";
}
];
services.kmscon.config = lib.mkIf cfg.useXkbConfig (
lib.mapAttrs (_: lib.mkDefault) (
lib.filterAttrs (_: v: v != "") {
xkb-layout = config.services.xserver.xkb.layout;
xkb-model = config.services.xserver.xkb.model;
xkb-options = config.services.xserver.xkb.options;
xkb-variant = config.services.xserver.xkb.variant;
}
)
);
environment.systemPackages = [ cfg.package ];
systemd.packages = [ cfg.package ];
systemd.services."kmsconvt@" = {
serviceConfig.ExecStart = [
"" # override upstream default with an empty ExecStart
(builtins.concatStringsSep " " (
[
"${cfg.package}/bin/kmscon"
"--configdir"
configDir
"--vt=%I"
"--no-switchvt"
"--login"
]
++ lib.optional (cfg.extraOptions != "") cfg.extraOptions
++ [
"--"
loginScript
]
))
];
serviceConfig = {
User = lib.mkIf (!cfg.config.libseat) "";
PAMName = lib.mkIf (!cfg.config.libseat) "";
Environment = [ "XKB_CONFIG_ROOT=${config.services.xserver.xkb.dir}" ];
ExecStart = [
"" # override upstream default with an empty ExecStart
(builtins.concatStringsSep " " (
[
"${cfg.package}/bin/kmscon"
"--configdir"
configDir
"--vt=%I"
"--no-switchvt"
"--login"
]
++ lib.optional (cfg.extraOptions != "") cfg.extraOptions
++ [
"--"
loginScript
]
))
];
};
restartIfChanged = false;
# logind spawns autovt@ttyN.service on VT switch; point it at kmscon
@@ -156,40 +194,55 @@ in
# tty1 is special: logind does not spawn autovt@tty1, it expects a static
# pull-in via getty.target. With getty@ suppressed, we must replace it.
systemd.services."getty.target".wants = lib.mkIf (!config.services.displayManager.enable) [
systemd.targets.getty.wants = lib.mkIf (!config.services.displayManager.enable) [
"kmsconvt@tty1.service"
];
systemd.suppressedSystemUnits = [ "getty@.service" ];
services.kmscon.extraConfig = lib.concatLines (
optionals cfg.useXkbConfig (
lib.mapAttrsToList (n: v: "xkb-${n}=${v}") (
lib.filterAttrs (
n: v:
builtins.elem n [
"layout"
"model"
"options"
"variant"
]
&& v != ""
) config.services.xserver.xkb
)
)
++ optionals cfg.hwRender [
"drm"
"hwaccel"
]
++ optional (cfg.fonts != null) "font-name=${lib.concatMapStringsSep ", " (f: f.name) cfg.fonts}"
++ optional (cfg.term != null) "term=${cfg.term}"
);
hardware.graphics.enable = mkIf cfg.hwRender true;
fonts = mkIf (cfg.fonts != null) {
fontconfig.enable = true;
packages = map (f: f.package) cfg.fonts;
security.pam.services.kmscon = lib.mkIf cfg.config.libseat {
useDefaultRules = false;
rules = {
auth = utils.pam.autoOrderRules [
{
name = "permit";
control = "required";
modulePath = "${config.security.pam.package}/lib/security/pam_permit.so";
}
];
account = utils.pam.autoOrderRules [
{
name = "unix";
control = "required";
modulePath = "${config.security.pam.package}/lib/security/pam_unix.so";
}
];
session = utils.pam.autoOrderRules [
{
name = "env";
control = "required";
modulePath = "${config.security.pam.package}/lib/security/pam_env.so";
settings = {
conffile = "/etc/pam/environment";
readenv = 0;
};
}
{
name = "unix";
control = "required";
modulePath = "${config.security.pam.package}/lib/security/pam_unix.so";
}
{
name = "systemd";
control = "optional";
modulePath = "${config.systemd.package}/lib/security/pam_systemd.so";
settings = {
type = "tty";
class = "greeter";
};
}
];
};
};
};

View File

@@ -14,17 +14,21 @@
services.getty.autologinUser = "alice";
hardware.graphics.enable = true;
fonts = {
fontconfig.enable = true;
packages = [ pkgs.nerd-fonts.jetbrains-mono ];
};
services.kmscon = {
enable = true;
hwRender = true;
fonts = [
{
name = "JetBrainsMono Nerd Font";
package = pkgs.nerd-fonts.jetbrains-mono;
}
];
term = "xterm-256color";
package = pkgs.kmscon;
config = {
font-name = "JetBrainsMono Nerd Font";
hwaccel = true;
term = "kmscon";
};
};
};
@@ -39,7 +43,7 @@
machine.send_chars("echo $TERM | tee /tmp/term.txt\n")
machine.wait_until_succeeds("test -s /tmp/term.txt")
term = machine.succeed("cat /tmp/term.txt").strip()
assert term == "xterm-256color", f"Unexpected TERM value: {term!r}"
assert term == "kmscon", f"Unexpected TERM value: {term!r}"
machine.screenshot("tty.png")
'';