{ config, lib, pkgs, ... }: with lib; let cfg = config.xsession; in { meta.maintainers = [ maintainers.rycee ]; options = { xsession = { enable = mkEnableOption "X Session"; scriptPath = mkOption { type = types.str; default = ".xsession"; example = ".xsession-hm"; description = '' Path, relative HOME, where Home Manager should write the X session script. ''; }; windowManager.command = mkOption { type = types.str; example = literalExample '' let xmonad = pkgs.xmonad-with-packages.override { packages = self: [ self.xmonad-contrib self.taffybar ]; }; in "''${xmonad}/bin/xmonad"; ''; description = '' Window manager start command. ''; }; preferStatusNotifierItems = mkOption { type = types.bool; default = false; example = true; description = '' Whether tray applets should prefer using the Status Notifier Items (SNI) protocol, commonly called App Indicators. Note, not all tray applets or status bars support SNI. ''; }; profileExtra = mkOption { type = types.lines; default = ""; description = "Extra shell commands to run before session start."; }; initExtra = mkOption { type = types.lines; default = ""; description = "Extra shell commands to run during initialization."; }; }; }; config = mkIf cfg.enable { systemd.user = { services = mkIf (config.home.keyboard != null) { setxkbmap = { Unit = { Description = "Set up keyboard in X"; After = [ "graphical-session-pre.target" ]; PartOf = [ "graphical-session.target" ]; }; Install = { WantedBy = [ "graphical-session.target" ]; }; Service = { Type = "oneshot"; RemainAfterExit = true; ExecStart = let args = concatStringsSep " " ( [ "-layout '${config.home.keyboard.layout}'" "-variant '${config.home.keyboard.variant}'" ] ++ (map (v: "-option '${v}'") config.home.keyboard.options) ); in "${pkgs.xorg.setxkbmap}/bin/setxkbmap ${args}"; }; }; }; # A basic graphical session target for Home Manager. targets.hm-graphical-session = { Unit = { Description = "Home Manager X session"; Requires = [ "graphical-session-pre.target" ]; BindsTo = [ "graphical-session.target" ]; }; }; }; home.file.".xprofile".text = '' . "${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh" if [[ -e "$HOME/.profile" ]]; then . "$HOME/.profile" fi # If there are any running services from a previous session. # Need to run this in xprofile because the NixOS xsession # script starts up graphical-session.target. systemctl --user stop graphical-session.target graphical-session-pre.target systemctl --user import-environment DBUS_SESSION_BUS_ADDRESS systemctl --user import-environment DISPLAY systemctl --user import-environment SSH_AUTH_SOCK systemctl --user import-environment XAUTHORITY systemctl --user import-environment XDG_DATA_DIRS systemctl --user import-environment XDG_RUNTIME_DIR systemctl --user import-environment XDG_SESSION_ID ${cfg.profileExtra} export HM_XPROFILE_SOURCED=1 ''; home.file.${cfg.scriptPath} = { executable = true; text = '' if [[ ! -v HM_XPROFILE_SOURCED ]]; then . ~/.xprofile fi unset HM_XPROFILE_SOURCED systemctl --user start hm-graphical-session.target ${cfg.initExtra} ${cfg.windowManager.command} systemctl --user stop graphical-session.target systemctl --user stop graphical-session-pre.target # Wait until the units actually stop. while [[ -n "$(systemctl --user --no-legend --state=deactivating list-units)" ]]; do sleep 0.5 done ''; }; }; }