diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index 4a4dba0fdafb..5707e4564991 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -18,6 +18,11 @@ - NixOS now has support for *automatic boot assessment* (see [here](https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/)) for detailed description of the feature) for `systemd-boot` users. Available as [boot.loader.systemd-boot.bootCounting](#opt-boot.loader.systemd-boot.bootCounting.enable). +- A new display-manager `services.displayManager.ly` was added. + It is a tui based replacement of sddm and lightdm for window manager users. + Users can use it by `services.displayManager.ly.enable` and config it by + `services.displayManager.ly.settings` to generate `/etc/ly/config.ini` + ## New Services {#sec-release-24.11-new-services} - [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr), proxy server to bypass Cloudflare protection. Available as [services.flaresolverr](#opt-services.flaresolverr.enable) service. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 436aa850cd52..9c878e02647f 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -542,6 +542,7 @@ ./services/display-managers/default.nix ./services/display-managers/greetd.nix ./services/display-managers/sddm.nix + ./services/display-managers/ly.nix ./services/editors/emacs.nix ./services/editors/haste.nix ./services/editors/infinoted.nix diff --git a/nixos/modules/services/display-managers/default.nix b/nixos/modules/services/display-managers/default.nix index 9a7bd6c84b15..894370199e79 100644 --- a/nixos/modules/services/display-managers/default.nix +++ b/nixos/modules/services/display-managers/default.nix @@ -204,7 +204,8 @@ in noDmUsed = !(dmConf.gdm.enable || cfg.sddm.enable || dmConf.xpra.enable - || dmConf.lightdm.enable); + || dmConf.lightdm.enable + || cfg.ly.enable); in lib.mkIf noDmUsed (lib.mkDefault false); systemd.services.display-manager = { diff --git a/nixos/modules/services/display-managers/ly.nix b/nixos/modules/services/display-managers/ly.nix new file mode 100644 index 000000000000..5d42467a6cd8 --- /dev/null +++ b/nixos/modules/services/display-managers/ly.nix @@ -0,0 +1,147 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + dmcfg = config.services.displayManager; + xcfg = config.services.xserver; + xdmcfg = xcfg.displayManager; + cfg = config.services.displayManager.ly; + xEnv = config.systemd.services.display-manager.environment; + + ly = cfg.package; + + iniFmt = pkgs.formats.iniWithGlobalSection { }; + + inherit (lib) + concatMapStrings + attrNames + getAttr + mkIf + mkOption + mkEnableOption + mkPackageOption + ; + + xserverWrapper = pkgs.writeShellScript "xserver-wrapper" '' + ${concatMapStrings (n: '' + export ${n}="${getAttr n xEnv}" + '') (attrNames xEnv)} + exec systemd-cat -t xserver-wrapper ${xdmcfg.xserverBin} ${toString xdmcfg.xserverArgs} "$@" + ''; + + defaultConfig = { + shutdown_cmd = "/run/current-system/systemd/bin/systemctl poweroff"; + restart_cmd = "/run/current-system/systemd/bin/systemctl reboot"; + tty = 2; + service_name = "ly"; + path = "/run/current-system/sw/bin"; + term_reset_cmd = "${pkgs.ncurses}/bin/tput reset"; + term_restore_cursor_cmd = "${pkgs.ncurses}/bin/tput cnorm"; + mcookie_cmd = "/run/current-system/sw/bin/mcookie"; + waylandsessions = "${dmcfg.sessionData.desktops}/share/wayland-sessions"; + wayland_cmd = dmcfg.sessionData.wrapper; + xsessions = "${dmcfg.sessionData.desktops}/share/xsessions"; + xauth_cmd = lib.optionalString xcfg.enable "${pkgs.xorg.xauth}/bin/xauth"; + x_cmd = lib.optionalString xcfg.enable xserverWrapper; + x_cmd_setup = dmcfg.sessionData.wrapper; + }; + + finalConfig = defaultConfig // cfg.settings; + + cfgFile = iniFmt.generate "config.ini" { globalSection = finalConfig; }; + +in +{ + options = { + services.displayManager.ly = { + enable = mkEnableOption "ly as the display manager"; + + package = mkPackageOption pkgs [ "ly" ] { }; + + settings = mkOption { + type = + with lib.types; + attrsOf (oneOf [ + str + int + bool + ]); + default = { }; + example = { + load = false; + save = false; + }; + description = '' + Extra settings merged in and overwriting defaults in config.ini. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + assertions = [ + { + assertion = !dmcfg.autoLogin.enable; + message = '' + ly doesn't support auto login. + ''; + } + ]; + + security.pam.services.ly = { + startSession = true; + unixAuth = true; + }; + + environment = { + etc."ly/config.ini".source = cfgFile; + systemPackages = [ ly ]; + + pathsToLink = [ "/share/ly" ]; + }; + + services = { + dbus.packages = [ ly ]; + + displayManager = { + enable = true; + execCmd = "exec /run/current-system/sw/bin/ly"; + }; + + xserver = { + # To enable user switching, allow ly to allocate TTYs/displays dynamically. + tty = null; + display = null; + }; + }; + + systemd = { + # We're not using the upstream unit, so copy these: + # https://github.com/fairyglade/ly/blob/master/res/ly.service + services.display-manager = { + after = [ + "systemd-user-sessions.service" + "plymouth-quit-wait.service" + "getty@tty${toString finalConfig.tty}.service" + ]; + + conflicts = [ "getty@tty7.service" ]; + + serviceConfig = { + Type = "idle"; + StandardInput = "tty"; + TTYPath = "/dev/tty${toString finalConfig.tty}"; + TTYReset = "yes"; + TTYVHangup = "yes"; + }; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ vonfry ]; +} diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix index 57e83399eded..5cec0ee0f1d5 100644 --- a/nixos/modules/services/x11/xserver.nix +++ b/nixos/modules/services/x11/xserver.nix @@ -648,7 +648,8 @@ in || dmConf.xpra.enable || dmConf.sx.enable || dmConf.startx.enable - || config.services.greetd.enable); + || config.services.greetd.enable + || config.services.displayManager.ly.enable); in mkIf (default) (mkDefault true); services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ]; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 1fade3d88a7b..0a5bde003519 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -544,6 +544,7 @@ in { lomiri-filemanager-app = runTest ./lomiri-filemanager-app.nix; lomiri-system-settings = handleTest ./lomiri-system-settings.nix {}; lorri = handleTest ./lorri/default.nix {}; + ly = handleTest ./ly.nix {}; maddy = discoverTests (import ./maddy { inherit handleTest; }); maestral = handleTest ./maestral.nix {}; magic-wormhole-mailbox-server = handleTest ./magic-wormhole-mailbox-server.nix {}; diff --git a/nixos/tests/ly.nix b/nixos/tests/ly.nix new file mode 100644 index 000000000000..04c6ed9c7774 --- /dev/null +++ b/nixos/tests/ly.nix @@ -0,0 +1,44 @@ +import ./make-test-python.nix ( + { ... }: + + { + name = "ly"; + + nodes.machine = + { ... }: + { + imports = [ ./common/user-account.nix ]; + services.displayManager.ly = { + enable = true; + settings = { + load = false; + save = false; + }; + }; + services.xserver.enable = true; + services.displayManager.defaultSession = "none+icewm"; + services.xserver.windowManager.icewm.enable = true; + }; + + testScript = + { nodes, ... }: + let + user = nodes.machine.users.users.alice; + in + '' + start_all() + machine.wait_until_tty_matches("2", "password:") + machine.send_key("ctrl-alt-f2") + machine.sleep(1) + machine.screenshot("ly") + machine.send_chars("alice") + machine.send_key("tab") + machine.send_chars("${user.password}") + machine.send_key("ret") + machine.wait_for_file("/run/user/${toString user.uid}/lyxauth") + machine.succeed("xauth merge /run/user/${toString user.uid}/lyxauth") + machine.wait_for_window("^IceWM ") + machine.screenshot("icewm") + ''; + } +)