{ config, lib, pkgs, ... }: let cfg = config.desktopSessions; availableSessionsConfig = lib.filterAttrs (name: x: x.enable) cfg.instances; availableSessions = lib.attrNames availableSessionsConfig; sessionStartScripts = lib.map (x: x.startSession) (lib.attrValues availableSessionsConfig); desktopSessionType = lib.types.submodule ({ config, name, ... }: { options = { enable = lib.mkEnableOption "desktop session instance enable" // { default = true; }; name = lib.mkOption { type = lib.types.str; default = name; }; startSession = lib.mkOption { type = lib.types.package; }; preStartHook = lib.mkOption { type = lib.types.str; default = ""; }; postStartHook = lib.mkOption { type = lib.types.str; default = ""; description = '' This is executed after the session is started. However, it's started outside of the session, so you won't get any environment variables from the session. If you need that, you need to utilize configuration, or flags of the session executable. There is $pid available to know the pid of the started session process. ''; }; exitHook = lib.mkOption { type = lib.types.str; default = '' systemctl stop --user graphical-session.target ''; }; executable = lib.mkOption { type = lib.types.path; }; arguments = lib.mkOption { type = lib.types.listOf lib.types.str; default = []; }; environment = lib.mkOption { type = lib.types.attrsOf (lib.types.oneOf [ lib.types.str lib.types.number ]); }; }; config = { startSession = pkgs.writeShellScript "start-${name}" '' ${lib.concatStringsSep "\n" (lib.attrValues (lib.mapAttrs (name: value: "export ${name}='${value}'") config.environment))} ${config.preStartHook} ${config.executable} ${lib.concatStringsSep " " config.arguments} & pid=$! ${config.postStartHook} wait $pid ${config.exitHook} ''; }; }); in { options.desktopSessions = { instances = lib.mkOption { type = lib.types.attrsOf desktopSessionType; default = {}; }; enable = lib.mkEnableOption "desktop session configuration"; selectSession = lib.mkOption { type = lib.types.path; }; defaultSession = lib.mkOption { type = lib.types.str; default = "tty"; }; availableSessions = lib.mkOption { type = lib.types.listOf lib.types.str; readOnly = true; }; sessionStartScripts = lib.mkOption { type = lib.types.listOf lib.types.package; readOnly = true; }; sessionStartScriptsPath = lib.mkOption { type = lib.types.package; readOnly = true; }; autoStart = { enable = lib.mkEnableOption "auto start profile selection on tty login" // { default = true; }; tty = lib.mkOption { type = lib.types.int; default = 1; }; profileSelectContent = lib.mkOption { type = lib.types.str; }; }; }; config.desktopSessions = { inherit availableSessions; inherit sessionStartScripts; sessionStartScriptsPath = let sessionsCopy = (lib.concatStringsSep "\n" (lib.map (x: '' cp "${x.startSession}" "$out/bin/start-${x.name}" '') (lib.attrValues availableSessionsConfig))); in pkgs.runCommandNoCCLocal "session-start-scripts" {} '' mkdir -p $out/bin ${sessionsCopy} ''; autoStart.profileSelectContent = '' if [[ "$(tty)" == "/dev/tty${builtins.toString cfg.autoStart.tty}" && "$(id -u)" != 0 ]]; then ${cfg.selectSession} fi ''; selectSession = pkgs.writeShellScript "select-session" '' sessions=(${lib.concatStringsSep " " cfg.availableSessions}) session_indices=(''${!sessions[@]}) timeout=3 selected="${cfg.defaultSession}" # default echo "Default session to start is $selected" echo "Available sessions:" for i in ''${!sessions[@]}; do echo " $((i+1))) ''${sessions[$i]}" done echo " q) Enter tty." echo -n "Choose session to start: " read -t"$timeout" -n1 user_input echo if [[ $user_input == "q" ]]; then exit elif [[ $user_input ]]; then user_input=$((user_input-1)) echo $user_input if [[ " ''${session_indices[@]} " =~ " $user_input " ]]; then selected="''${sessions[$user_input]}" echo "Got $user_input. Going to start $selected" else echo "Got unknown option. Exiting." exit fi else echo "Got no input, starting $selected" fi echo "Going to start $selected" # tty name is treated specially. It just exits. Use default session of tty, # to not start any session. if [[ $selected == "tty" ]]; then exit fi ${cfg.sessionStartScriptsPath}/bin/start-$selected ''; }; }