mirror of
https://github.com/foo-dogsquared/nixos-config.git
synced 2025-04-18 18:19:12 +00:00
nixos/programs/gnome-session: properly modularize and refactor
This commit is contained in:
parent
c12f59cad8
commit
d1dc2953c7
@ -1,8 +1,141 @@
|
||||
{ config, lib, pkgs, utils, ... }@moduleArgs:
|
||||
{ config, lib, pkgs, utils, ... }:
|
||||
|
||||
let
|
||||
cfg = config.programs.gnome-session;
|
||||
inherit (import ./submodules.nix moduleArgs) sessionType;
|
||||
|
||||
# The bulk of the work. Pretty much the main purpose of this module.
|
||||
sessionPackages = lib.mapAttrsToList
|
||||
(name: session:
|
||||
let
|
||||
gnomeSession = ''
|
||||
[GNOME Session]
|
||||
Name=${session.fullName} session
|
||||
RequiredComponents=${lib.concatStringsSep ";" session.requiredComponents};
|
||||
'';
|
||||
|
||||
displaySession = ''
|
||||
[Desktop Entry]
|
||||
Name=@fullName@
|
||||
Comment=${session.description}
|
||||
Exec="@out@/libexec/${name}-session"
|
||||
Type=Application
|
||||
DesktopNames=${lib.concatStringsSep ";" session.desktopNames};
|
||||
'';
|
||||
|
||||
sessionScript = ''
|
||||
#!${pkgs.runtimeShell}
|
||||
|
||||
# gnome-session is also looking for RequiredComponents in here.
|
||||
XDG_CONFIG_DIRS=@out@/etc/xdg''${XDG_CONFIG_DIRS:-:$XDG_CONFIG_DIRS}
|
||||
|
||||
# We'll have to force gnome-session to detect our session.
|
||||
XDG_DATA_DIRS=@out@/share''${XDG_DATA_DIRS:-:$XDG_DATA_DIRS}
|
||||
|
||||
${lib.getExe' cfg.package "gnome-session"} ${lib.escapeShellArgs session.extraArgs}
|
||||
'';
|
||||
|
||||
displayScripts =
|
||||
let
|
||||
hasMoreDisplays = protocol: lib.optionalString (lib.length session.display > 1) "fullName='${session.fullName} (${protocol})'";
|
||||
in
|
||||
{
|
||||
wayland = ''
|
||||
(
|
||||
DISPLAY_SESSION_FILE="$out/share/wayland-sessions/${name}.desktop"
|
||||
install -Dm0644 "$displaySessionPath" "$DISPLAY_SESSION_FILE"
|
||||
${hasMoreDisplays "Wayland"} substituteAllInPlace "$DISPLAY_SESSION_FILE"
|
||||
)
|
||||
'';
|
||||
x11 = ''
|
||||
(
|
||||
DISPLAY_SESSION_FILE="$out/share/xsessions/${name}.desktop"
|
||||
install -Dm0644 "$displaySessionPath" "$DISPLAY_SESSION_FILE"
|
||||
${hasMoreDisplays "X11"} substituteAllInPlace "$DISPLAY_SESSION_FILE"
|
||||
)
|
||||
'';
|
||||
};
|
||||
|
||||
installDesktopSessions = builtins.map
|
||||
(display:
|
||||
displayScripts.${display})
|
||||
session.display;
|
||||
|
||||
installDesktops =
|
||||
let
|
||||
sessionName = name;
|
||||
in
|
||||
lib.mapAttrsToList
|
||||
(name: component:
|
||||
let
|
||||
scriptName = "${sessionName}-${name}-script";
|
||||
|
||||
# There's already a lot of bad bad things in this world, we don't to add
|
||||
# more of it here (only a fraction of it, though).
|
||||
scriptPackage = pkgs.writeShellApplication {
|
||||
name = scriptName;
|
||||
text = component.script;
|
||||
};
|
||||
|
||||
script = "${scriptPackage}/bin/${scriptName}";
|
||||
desktopConfig = lib.mergeAttrs component.desktopConfig { exec = script; };
|
||||
desktopPackage = pkgs.makeDesktopItem desktopConfig;
|
||||
in
|
||||
''
|
||||
install -Dm0644 ${desktopPackage}/share/applications/*.desktop -t $out/share/applications
|
||||
'')
|
||||
session.components;
|
||||
in
|
||||
pkgs.runCommandLocal "${name}-desktop-session-files"
|
||||
{
|
||||
env = {
|
||||
inherit (session) fullName;
|
||||
};
|
||||
inherit displaySession gnomeSession sessionScript;
|
||||
passAsFile = [ "displaySession" "gnomeSession" "sessionScript" ];
|
||||
passthru.providedSessions = [ name ];
|
||||
}
|
||||
''
|
||||
SESSION_SCRIPT="$out/libexec/${name}-session"
|
||||
install -Dm0755 "$sessionScriptPath" "$SESSION_SCRIPT"
|
||||
substituteAllInPlace "$SESSION_SCRIPT"
|
||||
|
||||
GNOME_SESSION_FILE="$out/share/gnome-session/sessions/${name}.session"
|
||||
install -Dm0644 "$gnomeSessionPath" "$GNOME_SESSION_FILE"
|
||||
substituteAllInPlace "$GNOME_SESSION_FILE"
|
||||
|
||||
${lib.concatStringsSep "\n" installDesktopSessions}
|
||||
|
||||
${lib.concatStringsSep "\n" installDesktops}
|
||||
''
|
||||
)
|
||||
cfg.sessions;
|
||||
|
||||
sessionSystemdUnits = lib.mapAttrsToList
|
||||
(name: session:
|
||||
let
|
||||
inherit (utils.systemdUtils.lib)
|
||||
pathToUnit serviceToUnit targetToUnit timerToUnit socketToUnit;
|
||||
componentsUnits =
|
||||
lib.foldlAttrs
|
||||
(acc: name: component:
|
||||
acc // {
|
||||
"${component.id}.service" = serviceToUnit component.id component.serviceUnit;
|
||||
"${component.id}.target" = targetToUnit component.id component.targetUnit;
|
||||
} // lib.optionalAttrs (component.socketUnit != null) {
|
||||
"${component.id}.socket" = socketToUnit component.id component.socketUnit;
|
||||
} // lib.optionalAttrs (component.timerUnit != null) {
|
||||
"${component.id}.timer" = timerToUnit component.id component.timerUnit;
|
||||
} // lib.optionalAttrs (component.pathUnit != null) {
|
||||
"${component.id}.path" = pathToUnit component.id component.pathUnit;
|
||||
})
|
||||
{ }
|
||||
session.components;
|
||||
in
|
||||
componentsUnits // {
|
||||
"gnome-session@${name}.target" = targetToUnit "gnome-session@${name}" session.targetUnit;
|
||||
}
|
||||
)
|
||||
cfg.sessions;
|
||||
in
|
||||
{
|
||||
options.programs.gnome-session = {
|
||||
@ -18,7 +151,10 @@ in
|
||||
};
|
||||
|
||||
sessions = lib.mkOption {
|
||||
type = with lib.types; attrsOf (submodule sessionType);
|
||||
type = with lib.types; attrsOf (submoduleWith {
|
||||
specialArgs = { inherit utils; };
|
||||
modules = [ ./submodules/session-type.nix ];
|
||||
});
|
||||
description = ''
|
||||
A set of desktop sessions to be created with
|
||||
{manpage}`gnome-session(1)`. This gnome-session configuration generates
|
||||
@ -156,30 +292,16 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (cfg.sessions != { })
|
||||
(
|
||||
let
|
||||
sessionPackages = lib.mapAttrsToList
|
||||
(_: session:
|
||||
session.sessionPackage)
|
||||
cfg.sessions;
|
||||
config = lib.mkIf (cfg.sessions != { }) {
|
||||
# Install all of the desktop session files.
|
||||
services.xserver.displayManager.sessionPackages = sessionPackages;
|
||||
environment.systemPackages = [ cfg.package ] ++ sessionPackages;
|
||||
|
||||
sessionSystemdUnits = lib.mapAttrsToList
|
||||
(_: session:
|
||||
session.systemdUserUnits)
|
||||
cfg.sessions;
|
||||
in
|
||||
{
|
||||
# Install all of the desktop session files.
|
||||
services.xserver.displayManager.sessionPackages = sessionPackages;
|
||||
environment.systemPackages = [ cfg.package ] ++ sessionPackages;
|
||||
# Make sure it is searchable within gnome-session.
|
||||
environment.pathsToLink = [ "/share/gnome-session" ];
|
||||
|
||||
# Make sure it is searchable within gnome-session.
|
||||
environment.pathsToLink = [ "/share/gnome-session" ];
|
||||
|
||||
# Import those systemd units from gnome-session as well.
|
||||
systemd.packages = [ cfg.package ];
|
||||
systemd.user.units = lib.mkMerge sessionSystemdUnits;
|
||||
}
|
||||
);
|
||||
# Import those systemd units from gnome-session as well.
|
||||
systemd.packages = [ cfg.package ];
|
||||
systemd.user.units = lib.mkMerge sessionSystemdUnits;
|
||||
};
|
||||
}
|
||||
|
@ -1,663 +0,0 @@
|
||||
{ config, lib, pkgs, utils, ... }:
|
||||
|
||||
let
|
||||
cfg = config.programs.gnome-session;
|
||||
|
||||
# For an updated list, see `menu/menu-spec.xml` from
|
||||
# https://gitlab.freedesktop.org/xdg/xdg-specs.
|
||||
validDesktopNames = [
|
||||
"GNOME"
|
||||
"GNOME-Classic"
|
||||
"GNOME-Flashback"
|
||||
"KDE"
|
||||
"LXDE"
|
||||
"LXQt"
|
||||
"MATE"
|
||||
"Razor"
|
||||
"ROX"
|
||||
"TDE"
|
||||
"Unity"
|
||||
"XFCE"
|
||||
"EDE"
|
||||
"Cinnamon"
|
||||
"Pantheon"
|
||||
"Budgie"
|
||||
"Enlightenment"
|
||||
"DDE"
|
||||
"Endless"
|
||||
"Old"
|
||||
];
|
||||
in
|
||||
rec {
|
||||
componentsType = { name, config, options, session, ... }: {
|
||||
options = {
|
||||
description = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
description = "One-sentence description of the component.";
|
||||
default = "";
|
||||
example = "Desktop widgets";
|
||||
};
|
||||
|
||||
script = lib.mkOption {
|
||||
type = lib.types.lines;
|
||||
description = ''
|
||||
Shell script fragment of the component. Take note this will be
|
||||
wrapped in a script for proper integration with `gnome-session`.
|
||||
'';
|
||||
};
|
||||
|
||||
desktopConfig = lib.mkOption {
|
||||
type = with lib.types; attrsOf anything;
|
||||
description = ''
|
||||
The configuration for the gnome-session desktop file. For more
|
||||
information, look into `makeDesktopItem` nixpkgs builder.
|
||||
|
||||
You should configure this is if you use the built-in service
|
||||
management to be able to customize the session.
|
||||
|
||||
::: {.note}
|
||||
This module appends several options for the desktop item builder such
|
||||
as the script path and `X-GNOME-HiddenUnderSystemd` which is set to
|
||||
`true`.
|
||||
:::
|
||||
'';
|
||||
default = { };
|
||||
example = {
|
||||
extraConfig = {
|
||||
X-GNOME-Autostart-Phase = "WindowManager";
|
||||
X-GNOME-AutoRestart = "true";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Most of the systemd config types are trying to eliminate as much of the
|
||||
# NixOS systemd extensions as much as possible. For more details, see
|
||||
# `config` attribute of the `sessionType`.
|
||||
serviceUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.lib) unitConfig serviceConfig;
|
||||
inherit (utils.systemdUtils.unitOptions) commonUnitOptions serviceOptions;
|
||||
in
|
||||
lib.types.submodule [
|
||||
commonUnitOptions
|
||||
serviceOptions
|
||||
serviceConfig
|
||||
unitConfig
|
||||
];
|
||||
description = ''
|
||||
systemd service configuration to be generated. This should be
|
||||
configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.services.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
|
||||
On a typical case, you shouldn't mess with much of the dependency
|
||||
ordering of the service unit. By default, this module sets the
|
||||
service unit as part of the respective target unit (i.e.,
|
||||
`PartOf=$COMPONENTID.target`).
|
||||
:::
|
||||
'';
|
||||
default = { };
|
||||
};
|
||||
|
||||
targetUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
inherit (utils.systemdUtils.unitOptions) commonUnitOptions;
|
||||
in
|
||||
lib.types.submodule [
|
||||
commonUnitOptions
|
||||
unitConfig
|
||||
];
|
||||
description = ''
|
||||
systemd target configuration to be generated. This should be
|
||||
configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.targets.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
|
||||
This module doesn't set the typical dependency ordering relative to
|
||||
gnome-session targets. This is on the user to manually set them.
|
||||
:::
|
||||
'';
|
||||
default = { };
|
||||
};
|
||||
|
||||
timerUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.unitOptions) timerOptions commonUnitOptions;
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
in
|
||||
with lib.types; nullOr (submodule [
|
||||
commonUnitOptions
|
||||
timerOptions
|
||||
unitConfig
|
||||
]);
|
||||
description = ''
|
||||
An optional systemd timer configuration to be generated. This should
|
||||
be configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.timers.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
:::
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
socketUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.unitOptions) socketOptions commonUnitOptions;
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
in
|
||||
with lib.types; nullOr (submodule [
|
||||
commonUnitOptions
|
||||
socketOptions
|
||||
unitConfig
|
||||
]);
|
||||
description = ''
|
||||
An optional systemd socket configuration to be generated. This should
|
||||
be configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.sockets.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
:::
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
pathUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.unitOptions) pathOptions commonUnitOptions;
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
in
|
||||
with lib.types; nullOr (submodule [
|
||||
commonUnitOptions
|
||||
pathOptions
|
||||
unitConfig
|
||||
]);
|
||||
description = ''
|
||||
An optional systemd path configuration to be generated. This should
|
||||
be configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.paths.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
:::
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
id = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = ''
|
||||
The identifier of the component used in generating filenames for its
|
||||
`.desktop` files and as part of systemd unit names.
|
||||
'';
|
||||
default = "${session.name}.${name}";
|
||||
defaultText = "\${session-name}.\${name}";
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
scriptPackage = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
readOnly = true;
|
||||
internal = true;
|
||||
description = ''
|
||||
The package containing a wrapped script of the component script.
|
||||
'';
|
||||
};
|
||||
|
||||
desktopPackage = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
A package containing the desktop item set with
|
||||
{option}`$SESSION.components.<name>.desktopConfig`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
let
|
||||
scriptName = "${session.name}-${name}-script";
|
||||
script = "${config.scriptPackage}/bin/${scriptName}";
|
||||
in
|
||||
{
|
||||
# Make with the default configurations for the built-in-managed
|
||||
# components.
|
||||
desktopConfig = {
|
||||
name = lib.mkForce config.id;
|
||||
desktopName = lib.mkDefault "${session.fullName} - ${config.description}";
|
||||
exec = lib.mkForce script;
|
||||
noDisplay = lib.mkForce true;
|
||||
onlyShowIn = session.desktopNames;
|
||||
extraConfig = {
|
||||
X-GNOME-AutoRestart = lib.mkDefault "false";
|
||||
X-GNOME-Autostart-Notify = lib.mkDefault "true";
|
||||
X-GNOME-Autostart-Phase = lib.mkDefault "Application";
|
||||
X-GNOME-HiddenUnderSystemd = lib.mkDefault "true";
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Setting some recommendation and requirements for systemd-managed
|
||||
gnome-session components. Note there are the missing directives that
|
||||
COULD include some sane defaults here.
|
||||
|
||||
* The `Unit.OnFailure=` and `Unit.OnFailureJobMode=` directives. Since
|
||||
different components don't have the same priority and don't handle
|
||||
failures the same way, we didn't set it here. This is on the user to
|
||||
know how different desktop components interact with each other
|
||||
especially if one of them failed.
|
||||
|
||||
* Even if we have a way to limit starting desktop components with
|
||||
`systemd-xdg-autostart-condition`, using `Service.ExecCondition=`
|
||||
would severely limit possible reuse of desktop components with other
|
||||
NixOS-module-generated gnome-session sessions so we're not bothering
|
||||
with those.
|
||||
|
||||
TODO: Is `Type=notify` a good default?
|
||||
* `Service.Type=` is obviously not included since not all desktop
|
||||
components are the same either. Some of them could a D-Bus service,
|
||||
some of them are oneshots, etc. Not to mention, this is already implied
|
||||
to be `Type=simple` by systemd anyways.
|
||||
|
||||
TODO: A good balance for this value, probably?
|
||||
* `Service.OOMScoreAdjust=` have different values for different
|
||||
components so it isn't included.
|
||||
|
||||
* Most sandboxing options. Aside from the fact we're dealing with a
|
||||
systemd user unit, much of them are unnecessary and rarely needed (if
|
||||
ever like `Service.PrivateTmp=`?) so we didn't set such defaults here.
|
||||
|
||||
As you can tell, this module does not provide a framework for the user
|
||||
to easily compose their own desktop environment. THIS MODULE ALREADY
|
||||
DOES A LOT, ALRIGHT! CUT ME SOME SLACK!
|
||||
|
||||
Take note that the default service configuration is leaning on the
|
||||
desktop component being a simple type of service like how most NixOS
|
||||
service modules are deployed.
|
||||
*/
|
||||
serviceUnit = {
|
||||
script = lib.mkAfter script;
|
||||
description = lib.mkDefault config.description;
|
||||
|
||||
# The typical workflow for service units to have them set as part of
|
||||
# the respective target unit.
|
||||
requisite = [ "${config.id}.target" ];
|
||||
before = [ "${config.id}.target" ];
|
||||
partOf = [ "${config.id}.target" ];
|
||||
|
||||
# Some sane service configuration for a desktop component.
|
||||
serviceConfig = {
|
||||
Slice = lib.mkDefault "session.slice";
|
||||
Restart = lib.mkDefault "on-failure";
|
||||
TimeoutStopSec = lib.mkDefault 5;
|
||||
};
|
||||
|
||||
startLimitBurst = lib.mkDefault 3;
|
||||
startLimitIntervalSec = lib.mkDefault 15;
|
||||
|
||||
unitConfig = {
|
||||
# Units managed by gnome-session are required to have CollectMode=
|
||||
# set to this value.
|
||||
CollectMode = lib.mkForce "inactive-or-failed";
|
||||
|
||||
# We leave those up to the target units to start the services.
|
||||
RefuseManualStart = lib.mkDefault true;
|
||||
RefuseManualStop = lib.mkDefault true;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Similarly, there are things that COULD make it here but didn't for a
|
||||
variety of reasons.
|
||||
|
||||
* `Unit.PartOf=`, `Unit.Requisite=`, and the like since some components
|
||||
require starting up earlier than the others. We could include it here
|
||||
if we make it clear in the documentation or if it proves to be a
|
||||
painful experience to configure this by a first-timer. For now, this is
|
||||
on the user to know.
|
||||
*/
|
||||
targetUnit = {
|
||||
wants = [ "${config.id}.service" ];
|
||||
description = lib.mkDefault config.description;
|
||||
documentation = [
|
||||
"man:gnome-session(1)"
|
||||
"man:systemd.special(7)"
|
||||
];
|
||||
unitConfig.CollectMode = lib.mkForce "inactive-or-failed";
|
||||
};
|
||||
|
||||
scriptPackage = pkgs.writeShellApplication {
|
||||
name = scriptName;
|
||||
runtimeInputs = [ cfg.package pkgs.dbus ];
|
||||
text = config.script;
|
||||
};
|
||||
|
||||
desktopPackage = pkgs.makeDesktopItem config.desktopConfig;
|
||||
};
|
||||
};
|
||||
|
||||
sessionType = { name, config, options, ... }: {
|
||||
options = {
|
||||
fullName = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
description = "The display name of the desktop environment.";
|
||||
default = name;
|
||||
example = "Mosey Branch";
|
||||
};
|
||||
|
||||
desktopNames = lib.mkOption {
|
||||
type = with lib.types; listOf nonEmptyStr;
|
||||
description = ''
|
||||
Names to be used as `DesktopNames=` entry of the session `.desktop`
|
||||
file. Useful if you're creating a customized version of an already
|
||||
existing desktop session.
|
||||
|
||||
::: {.note}
|
||||
This module sanitizes the values by prepending the given names with
|
||||
`X-` if they aren't part of the registered values from XDG spec. This
|
||||
is because the desktop components' `.desktop` file are being
|
||||
validated with `desktop-file-validate` from xdg-file-utils.
|
||||
:::
|
||||
'';
|
||||
default = [ config.fullName ];
|
||||
defaultText = "[ <session>.fullName ]";
|
||||
apply = names:
|
||||
builtins.map
|
||||
(name:
|
||||
if (lib.elem name validDesktopNames) || (lib.hasPrefix "X-" name) then
|
||||
name
|
||||
else
|
||||
"X-${name}")
|
||||
names;
|
||||
example = [ "GNOME" "Garden" ];
|
||||
};
|
||||
|
||||
display = lib.mkOption {
|
||||
type = with lib.types; listOf (enum [ "wayland" "x11" ]);
|
||||
description = ''
|
||||
A list of display server protocols supported by the desktop
|
||||
environment.
|
||||
'';
|
||||
default = [ "wayland" ];
|
||||
example = [ "wayland" "x11" ];
|
||||
};
|
||||
|
||||
description = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
description = ''
|
||||
A one-sentence description of the desktop environment.
|
||||
'';
|
||||
default = "${config.fullName} desktop environment";
|
||||
defaultText = lib.literalExpression "\${<name>.fullName} desktop environment";
|
||||
example = "A desktop environment featuring a scrolling compositor.";
|
||||
};
|
||||
|
||||
components = lib.mkOption {
|
||||
type = with lib.types; attrsOf (submoduleWith {
|
||||
specialArgs.session = {
|
||||
inherit (config) fullName desktopNames description;
|
||||
inherit name;
|
||||
};
|
||||
modules = [ componentsType ];
|
||||
});
|
||||
description = ''
|
||||
The individual components to be launched with the desktop session.
|
||||
'';
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
window-manager = {
|
||||
script = '''
|
||||
''${lib.getExe' config.programs.sway.package "sway"}
|
||||
''';
|
||||
description = "An i3 clone for Wayland.";
|
||||
};
|
||||
|
||||
desktop-widgets.script = '''
|
||||
''${lib.getExe' pkgs.ags "ags"} --config ''${./config.js}
|
||||
''';
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
extraArgs = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
description = ''
|
||||
A list of arguments from {program}`gnome-session` to be added for the session
|
||||
script.
|
||||
|
||||
::: {.note}
|
||||
An argument `--session=<name>` will always be appended into the
|
||||
configuration.
|
||||
:::
|
||||
'';
|
||||
example = [
|
||||
"--systemd"
|
||||
"--disable-acceleration-check"
|
||||
];
|
||||
};
|
||||
|
||||
requiredComponents = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
description = ''
|
||||
A list of desktop components as part of `RequiredComponents=` for the
|
||||
gnome-session configuration.
|
||||
|
||||
::: {.note}
|
||||
For the most part, this shouldn't require manually configuring it if
|
||||
you set {option}`<session>.components` as this module already sets
|
||||
them for you.
|
||||
|
||||
The only time you manually set this if you want to require components
|
||||
from other desktop such as when creating a customized version of
|
||||
GNOME.
|
||||
:::
|
||||
'';
|
||||
example = [
|
||||
"org.gnome.Shell"
|
||||
"org.gnome.SettingsDaemon.A11ySettings"
|
||||
"org.gnome.SettingsDaemon.Power"
|
||||
"org.gnome.SettingsDaemon.Wacom"
|
||||
];
|
||||
};
|
||||
|
||||
targetUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
inherit (utils.systemdUtils.unitOptions) commonUnitOptions;
|
||||
in
|
||||
lib.types.submodule [
|
||||
commonUnitOptions
|
||||
unitConfig
|
||||
];
|
||||
description = ''
|
||||
systemd target configuration to be generated for
|
||||
`gnome-session@<name>.target`. This should be configured if the
|
||||
session is managed by systemd and you want to control the session
|
||||
further (which is recommended since this module don't know what
|
||||
components are more important, etc.).
|
||||
|
||||
By default, the session target will have all of its components from
|
||||
{option}`<session>.requiredComponents` under `Wants=` directive. It
|
||||
also assumes all of them have a target unit at
|
||||
`''${requiredComponent}.target`.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.targets.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
:::
|
||||
'';
|
||||
defaultText = ''
|
||||
{
|
||||
wants = ... # All of the required components as a target unit.
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
sessionPackage = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
description = ''
|
||||
The collective package containing everything desktop-related
|
||||
such as:
|
||||
|
||||
* The display session (`<name>.desktop`) files.
|
||||
* gnome-session `.session` file.
|
||||
* The components `.desktop` file.
|
||||
'';
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
systemdUserUnits = lib.mkOption {
|
||||
type = utils.systemdUtils.types.units;
|
||||
description = ''
|
||||
A set of systemd user units to be generated.
|
||||
'';
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# Append the session argument.
|
||||
extraArgs = [ "--session=${name}" ];
|
||||
|
||||
systemdUserUnits =
|
||||
let
|
||||
inherit (utils.systemdUtils.lib)
|
||||
pathToUnit serviceToUnit targetToUnit timerToUnit socketToUnit;
|
||||
componentsUnits =
|
||||
lib.foldlAttrs
|
||||
(acc: name: component:
|
||||
acc // {
|
||||
"${component.id}.service" = serviceToUnit component.id component.serviceUnit;
|
||||
"${component.id}.target" = targetToUnit component.id component.targetUnit;
|
||||
} // lib.optionalAttrs (component.socketUnit != null) {
|
||||
"${component.id}.socket" = socketToUnit component.id component.socketUnit;
|
||||
} // lib.optionalAttrs (component.timerUnit != null) {
|
||||
"${component.id}.timer" = timerToUnit component.id component.timerUnit;
|
||||
} // lib.optionalAttrs (component.pathUnit != null) {
|
||||
"${component.id}.path" = pathToUnit component.id component.pathUnit;
|
||||
})
|
||||
{ }
|
||||
config.components;
|
||||
in
|
||||
componentsUnits // {
|
||||
"gnome-session@${name}.target" = targetToUnit "gnome-session@${name}" config.targetUnit;
|
||||
};
|
||||
|
||||
# By default. set the required components from the given desktop
|
||||
# components.
|
||||
requiredComponents = lib.mapAttrsToList (_: component: component.id) config.components;
|
||||
|
||||
targetUnit = {
|
||||
overrideStrategy = lib.mkForce "asDropin";
|
||||
wants = lib.mkDefault (builtins.map (c: "${c}.target") config.requiredComponents);
|
||||
};
|
||||
|
||||
# The bulk of the work. Pretty much the main purpose of this module.
|
||||
sessionPackage =
|
||||
let
|
||||
gnomeSession = ''
|
||||
[GNOME Session]
|
||||
Name=${config.fullName} session
|
||||
RequiredComponents=${lib.concatStringsSep ";" config.requiredComponents};
|
||||
'';
|
||||
|
||||
displaySession = ''
|
||||
[Desktop Entry]
|
||||
Name=@fullName@
|
||||
Comment=${config.description}
|
||||
Exec="@out@/libexec/${name}-session"
|
||||
Type=Application
|
||||
DesktopNames=${lib.concatStringsSep ";" config.desktopNames};
|
||||
'';
|
||||
|
||||
sessionScript = ''
|
||||
#!${pkgs.runtimeShell}
|
||||
|
||||
# gnome-session is also looking for RequiredComponents in here.
|
||||
XDG_CONFIG_DIRS=@out@/etc/xdg''${XDG_CONFIG_DIRS:-:$XDG_CONFIG_DIRS}
|
||||
|
||||
# We'll have to force gnome-session to detect our session.
|
||||
XDG_DATA_DIRS=@out@/share''${XDG_DATA_DIRS:-:$XDG_DATA_DIRS}
|
||||
|
||||
${lib.getExe' cfg.package "gnome-session"} ${lib.escapeShellArgs config.extraArgs}
|
||||
'';
|
||||
|
||||
displayScripts =
|
||||
let
|
||||
hasMoreDisplays = protocol: lib.optionalString (lib.length config.display > 1) "fullName='${config.fullName} (${protocol})'";
|
||||
in
|
||||
{
|
||||
wayland = ''
|
||||
(
|
||||
DISPLAY_SESSION_FILE="$out/share/wayland-sessions/${name}.desktop"
|
||||
install -Dm0644 "$displaySessionPath" "$DISPLAY_SESSION_FILE"
|
||||
${hasMoreDisplays "Wayland"} substituteAllInPlace "$DISPLAY_SESSION_FILE"
|
||||
)
|
||||
'';
|
||||
x11 = ''
|
||||
(
|
||||
DISPLAY_SESSION_FILE="$out/share/xsessions/${name}.desktop"
|
||||
install -Dm0644 "$displaySessionPath" "$DISPLAY_SESSION_FILE"
|
||||
${hasMoreDisplays "X11"} substituteAllInPlace "$DISPLAY_SESSION_FILE"
|
||||
)
|
||||
'';
|
||||
};
|
||||
|
||||
installDesktopSessions = builtins.map
|
||||
(display:
|
||||
displayScripts.${display})
|
||||
config.display;
|
||||
|
||||
installDesktops = lib.mapAttrsToList
|
||||
(_: p: ''
|
||||
install -Dm0644 ${p.desktopPackage}/share/applications/*.desktop -t $out/share/applications
|
||||
'')
|
||||
config.components;
|
||||
in
|
||||
pkgs.runCommandLocal "${name}-desktop-session-files"
|
||||
{
|
||||
env = {
|
||||
inherit (config) fullName;
|
||||
};
|
||||
inherit displaySession gnomeSession sessionScript;
|
||||
passAsFile = [ "displaySession" "gnomeSession" "sessionScript" ];
|
||||
passthru.providedSessions = [ name ];
|
||||
}
|
||||
''
|
||||
SESSION_SCRIPT="$out/libexec/${name}-session"
|
||||
install -Dm0755 "$sessionScriptPath" "$SESSION_SCRIPT"
|
||||
substituteAllInPlace "$SESSION_SCRIPT"
|
||||
|
||||
GNOME_SESSION_FILE="$out/share/gnome-session/sessions/${name}.session"
|
||||
install -Dm0644 "$gnomeSessionPath" "$GNOME_SESSION_FILE"
|
||||
substituteAllInPlace "$GNOME_SESSION_FILE"
|
||||
|
||||
${lib.concatStringsSep "\n" installDesktopSessions}
|
||||
|
||||
${lib.concatStringsSep "\n" installDesktops}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
@ -0,0 +1,290 @@
|
||||
{ name, config, lib, utils, session, ... }: {
|
||||
options = {
|
||||
description = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
description = "One-sentence description of the component.";
|
||||
example = "Desktop widgets";
|
||||
};
|
||||
|
||||
script = lib.mkOption {
|
||||
type = lib.types.lines;
|
||||
description = ''
|
||||
Shell script fragment of the component. Take note this will be
|
||||
wrapped in a script for proper integration with `gnome-session`.
|
||||
'';
|
||||
};
|
||||
|
||||
desktopConfig = lib.mkOption {
|
||||
type = with lib.types; attrsOf anything;
|
||||
description = ''
|
||||
The configuration for the gnome-session desktop file. For more
|
||||
information, look into `makeDesktopItem` nixpkgs builder.
|
||||
|
||||
You should configure this is if you use the built-in service
|
||||
management to be able to customize the session.
|
||||
|
||||
::: {.note}
|
||||
This module appends several options for the desktop item builder such
|
||||
as the script path and `X-GNOME-HiddenUnderSystemd` which is set to
|
||||
`true`.
|
||||
:::
|
||||
'';
|
||||
default = { };
|
||||
example = {
|
||||
extraConfig = {
|
||||
X-GNOME-Autostart-Phase = "WindowManager";
|
||||
X-GNOME-AutoRestart = "true";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Most of the systemd config types are trying to eliminate as much of the
|
||||
# NixOS systemd extensions as much as possible. For more details, see
|
||||
# `config` attribute of the `sessionType`.
|
||||
serviceUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.lib) unitConfig serviceConfig;
|
||||
inherit (utils.systemdUtils.unitOptions) commonUnitOptions serviceOptions;
|
||||
in
|
||||
lib.types.submodule [
|
||||
commonUnitOptions
|
||||
serviceOptions
|
||||
serviceConfig
|
||||
unitConfig
|
||||
];
|
||||
description = ''
|
||||
systemd service configuration to be generated. This should be
|
||||
configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.services.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
|
||||
On a typical case, you shouldn't mess with much of the dependency
|
||||
ordering of the service unit. By default, this module sets the
|
||||
service unit as part of the respective target unit (i.e.,
|
||||
`PartOf=$COMPONENTID.target`).
|
||||
:::
|
||||
'';
|
||||
default = { };
|
||||
};
|
||||
|
||||
targetUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
inherit (utils.systemdUtils.unitOptions) commonUnitOptions;
|
||||
in
|
||||
lib.types.submodule [
|
||||
commonUnitOptions
|
||||
unitConfig
|
||||
];
|
||||
description = ''
|
||||
systemd target configuration to be generated. This should be
|
||||
configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.targets.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
|
||||
This module doesn't set the typical dependency ordering relative to
|
||||
gnome-session targets. This is on the user to manually set them.
|
||||
:::
|
||||
'';
|
||||
default = { };
|
||||
};
|
||||
|
||||
timerUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.unitOptions) timerOptions commonUnitOptions;
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
in
|
||||
with lib.types; nullOr (submodule [
|
||||
commonUnitOptions
|
||||
timerOptions
|
||||
unitConfig
|
||||
]);
|
||||
description = ''
|
||||
An optional systemd timer configuration to be generated. This should
|
||||
be configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.timers.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
:::
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
socketUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.unitOptions) socketOptions commonUnitOptions;
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
in
|
||||
with lib.types; nullOr (submodule [
|
||||
commonUnitOptions
|
||||
socketOptions
|
||||
unitConfig
|
||||
]);
|
||||
description = ''
|
||||
An optional systemd socket configuration to be generated. This should
|
||||
be configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.sockets.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
:::
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
pathUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.unitOptions) pathOptions commonUnitOptions;
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
in
|
||||
with lib.types; nullOr (submodule [
|
||||
commonUnitOptions
|
||||
pathOptions
|
||||
unitConfig
|
||||
]);
|
||||
description = ''
|
||||
An optional systemd path configuration to be generated. This should
|
||||
be configured if the session is managed by systemd.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.paths.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
:::
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
id = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = ''
|
||||
The identifier of the component used in generating filenames for its
|
||||
`.desktop` files and as part of systemd unit names.
|
||||
'';
|
||||
default = "${session.name}.${name}";
|
||||
defaultText = "\${session-name}.\${name}";
|
||||
readOnly = true;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# Make with the default configurations for the built-in-managed
|
||||
# components.
|
||||
desktopConfig = {
|
||||
name = lib.mkForce config.id;
|
||||
desktopName = lib.mkDefault "${session.fullName} - ${config.description}";
|
||||
noDisplay = lib.mkForce true;
|
||||
onlyShowIn = session.desktopNames;
|
||||
extraConfig = {
|
||||
X-GNOME-AutoRestart = lib.mkDefault "false";
|
||||
X-GNOME-Autostart-Notify = lib.mkDefault "true";
|
||||
X-GNOME-Autostart-Phase = lib.mkDefault "Application";
|
||||
X-GNOME-HiddenUnderSystemd = lib.mkDefault "true";
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Setting some recommendation and requirements for systemd-managed
|
||||
gnome-session components. Note there are the missing directives that
|
||||
COULD include some sane defaults here.
|
||||
|
||||
* The `Unit.OnFailure=` and `Unit.OnFailureJobMode=` directives. Since
|
||||
different components don't have the same priority and don't handle
|
||||
failures the same way, we didn't set it here. This is on the user to
|
||||
know how different desktop components interact with each other
|
||||
especially if one of them failed.
|
||||
|
||||
* Even if we have a way to limit starting desktop components with
|
||||
`systemd-xdg-autostart-condition`, using `Service.ExecCondition=`
|
||||
would severely limit possible reuse of desktop components with other
|
||||
NixOS-module-generated gnome-session sessions so we're not bothering
|
||||
with those.
|
||||
|
||||
TODO: Is `Type=notify` a good default?
|
||||
* `Service.Type=` is obviously not included since not all desktop
|
||||
components are the same either. Some of them could a D-Bus service,
|
||||
some of them are oneshots, etc. Not to mention, this is already implied
|
||||
to be `Type=simple` by systemd anyways.
|
||||
|
||||
TODO: A good balance for this value, probably?
|
||||
* `Service.OOMScoreAdjust=` have different values for different
|
||||
components so it isn't included.
|
||||
|
||||
* Most sandboxing options. Aside from the fact we're dealing with a
|
||||
systemd user unit, much of them are unnecessary and rarely needed (if
|
||||
ever like `Service.PrivateTmp=`?) so we didn't set such defaults here.
|
||||
|
||||
As you can tell, this module does not provide a framework for the user
|
||||
to easily compose their own desktop environment. THIS MODULE ALREADY
|
||||
DOES A LOT, ALRIGHT! CUT ME SOME SLACK!
|
||||
|
||||
Take note that the default service configuration is leaning on the
|
||||
desktop component being a simple type of service like how most NixOS
|
||||
service modules are deployed.
|
||||
*/
|
||||
serviceUnit = {
|
||||
script = lib.mkAfter config.script;
|
||||
description = lib.mkDefault config.description;
|
||||
|
||||
# The typical workflow for service units to have them set as part of
|
||||
# the respective target unit.
|
||||
requisite = [ "${config.id}.target" ];
|
||||
before = [ "${config.id}.target" ];
|
||||
partOf = [ "${config.id}.target" ];
|
||||
|
||||
# Some sane service configuration for a desktop component.
|
||||
serviceConfig = {
|
||||
Slice = lib.mkDefault "session.slice";
|
||||
Restart = lib.mkDefault "on-failure";
|
||||
TimeoutStopSec = lib.mkDefault 5;
|
||||
};
|
||||
|
||||
startLimitBurst = lib.mkDefault 3;
|
||||
startLimitIntervalSec = lib.mkDefault 15;
|
||||
|
||||
unitConfig = {
|
||||
# Units managed by gnome-session are required to have CollectMode=
|
||||
# set to this value.
|
||||
CollectMode = lib.mkForce "inactive-or-failed";
|
||||
|
||||
# We leave those up to the target units to start the services.
|
||||
RefuseManualStart = lib.mkDefault true;
|
||||
RefuseManualStop = lib.mkDefault true;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Similarly, there are things that COULD make it here but didn't for a
|
||||
variety of reasons.
|
||||
|
||||
* `Unit.PartOf=`, `Unit.Requisite=`, and the like since some components
|
||||
require starting up earlier than the others. We could include it here
|
||||
if we make it clear in the documentation or if it proves to be a
|
||||
painful experience to configure this by a first-timer. For now, this is
|
||||
on the user to know.
|
||||
*/
|
||||
targetUnit = {
|
||||
wants = [ "${config.id}.service" ];
|
||||
description = lib.mkDefault config.description;
|
||||
documentation = [
|
||||
"man:gnome-session(1)"
|
||||
"man:systemd.special(7)"
|
||||
];
|
||||
unitConfig.CollectMode = lib.mkForce "inactive-or-failed";
|
||||
};
|
||||
};
|
||||
}
|
219
modules/nixos/programs/gnome-session/submodules/session-type.nix
Normal file
219
modules/nixos/programs/gnome-session/submodules/session-type.nix
Normal file
@ -0,0 +1,219 @@
|
||||
{ name, config, lib, utils, ... }:
|
||||
|
||||
let
|
||||
# For an updated list, see `menu/menu-spec.xml` from
|
||||
# https://gitlab.freedesktop.org/xdg/xdg-specs.
|
||||
validDesktopNames = [
|
||||
"GNOME"
|
||||
"GNOME-Classic"
|
||||
"GNOME-Flashback"
|
||||
"KDE"
|
||||
"LXDE"
|
||||
"LXQt"
|
||||
"MATE"
|
||||
"Razor"
|
||||
"ROX"
|
||||
"TDE"
|
||||
"Unity"
|
||||
"XFCE"
|
||||
"EDE"
|
||||
"Cinnamon"
|
||||
"Pantheon"
|
||||
"Budgie"
|
||||
"Enlightenment"
|
||||
"DDE"
|
||||
"Endless"
|
||||
"Old"
|
||||
];
|
||||
in
|
||||
{
|
||||
options = {
|
||||
fullName = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
description = "The display name of the desktop environment.";
|
||||
default = name;
|
||||
example = "Mosey Branch";
|
||||
};
|
||||
|
||||
desktopNames = lib.mkOption {
|
||||
type = with lib.types; listOf nonEmptyStr;
|
||||
description = ''
|
||||
Names to be used as `DesktopNames=` entry of the session `.desktop`
|
||||
file. Useful if you're creating a customized version of an already
|
||||
existing desktop session.
|
||||
|
||||
::: {.note}
|
||||
This module sanitizes the values by prepending the given names with
|
||||
`X-` if they aren't part of the registered values from XDG spec. This
|
||||
is because the desktop components' `.desktop` file are being
|
||||
validated with `desktop-file-validate` from xdg-file-utils.
|
||||
:::
|
||||
'';
|
||||
default = [ config.fullName ];
|
||||
defaultText = "[ <session>.fullName ]";
|
||||
apply = names:
|
||||
builtins.map
|
||||
(name:
|
||||
if (lib.elem name validDesktopNames) || (lib.hasPrefix "X-" name) then
|
||||
name
|
||||
else
|
||||
"X-${name}")
|
||||
names;
|
||||
example = [ "GNOME" "Garden" ];
|
||||
};
|
||||
|
||||
display = lib.mkOption {
|
||||
type = with lib.types; listOf (enum [ "wayland" "x11" ]);
|
||||
description = ''
|
||||
A list of display server protocols supported by the desktop
|
||||
environment.
|
||||
'';
|
||||
default = [ "wayland" ];
|
||||
example = [ "wayland" "x11" ];
|
||||
};
|
||||
|
||||
description = lib.mkOption {
|
||||
type = lib.types.nonEmptyStr;
|
||||
description = ''
|
||||
A one-sentence description of the desktop environment.
|
||||
'';
|
||||
default = "${config.fullName} desktop environment";
|
||||
defaultText = lib.literalExpression "\${<name>.fullName} desktop environment";
|
||||
example = "A desktop environment featuring a scrolling compositor.";
|
||||
};
|
||||
|
||||
components = lib.mkOption {
|
||||
type = with lib.types; attrsOf (submoduleWith {
|
||||
specialArgs = {
|
||||
inherit utils;
|
||||
session = {
|
||||
inherit (config) fullName desktopNames description;
|
||||
inherit name;
|
||||
};
|
||||
};
|
||||
modules = [ ./component-type.nix ];
|
||||
});
|
||||
description = ''
|
||||
The individual components to be launched with the desktop session.
|
||||
'';
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
window-manager = {
|
||||
script = '''
|
||||
''${lib.getExe' config.programs.sway.package "sway"}
|
||||
''';
|
||||
description = "An i3 clone for Wayland.";
|
||||
};
|
||||
|
||||
desktop-widgets.script = '''
|
||||
''${lib.getExe' pkgs.ags "ags"} --config ''${./config.js}
|
||||
''';
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
extraArgs = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
description = ''
|
||||
A list of arguments from {program}`gnome-session` to be added for the session
|
||||
script.
|
||||
|
||||
::: {.note}
|
||||
An argument `--session=<name>` will always be appended into the
|
||||
configuration.
|
||||
:::
|
||||
'';
|
||||
example = [
|
||||
"--systemd"
|
||||
"--disable-acceleration-check"
|
||||
];
|
||||
};
|
||||
|
||||
requiredComponents = lib.mkOption {
|
||||
type = with lib.types; listOf str;
|
||||
description = ''
|
||||
A list of desktop components as part of `RequiredComponents=` for the
|
||||
gnome-session configuration.
|
||||
|
||||
::: {.note}
|
||||
For the most part, this shouldn't require manually configuring it if
|
||||
you set {option}`<session>.components` as this module already sets
|
||||
them for you.
|
||||
|
||||
The only time you manually set this if you want to require components
|
||||
from other desktop such as when creating a customized version of
|
||||
GNOME.
|
||||
:::
|
||||
'';
|
||||
example = [
|
||||
"org.gnome.Shell"
|
||||
"org.gnome.SettingsDaemon.A11ySettings"
|
||||
"org.gnome.SettingsDaemon.Power"
|
||||
"org.gnome.SettingsDaemon.Wacom"
|
||||
];
|
||||
};
|
||||
|
||||
targetUnit = lib.mkOption {
|
||||
type =
|
||||
let
|
||||
inherit (utils.systemdUtils.lib) unitConfig;
|
||||
inherit (utils.systemdUtils.unitOptions) commonUnitOptions;
|
||||
in
|
||||
lib.types.submodule [
|
||||
commonUnitOptions
|
||||
unitConfig
|
||||
];
|
||||
description = ''
|
||||
systemd target configuration to be generated for
|
||||
`gnome-session@<name>.target`. This should be configured if the
|
||||
session is managed by systemd and you want to control the session
|
||||
further (which is recommended since this module don't know what
|
||||
components are more important, etc.).
|
||||
|
||||
By default, the session target will have all of its components from
|
||||
{option}`<session>.requiredComponents` under `Wants=` directive. It
|
||||
also assumes all of them have a target unit at
|
||||
`''${requiredComponent}.target`.
|
||||
|
||||
:::{.note}
|
||||
This has the same options as {option}`systemd.user.targets.<name>`
|
||||
but without certain options from stage 2 counterparts such as
|
||||
`reloadTriggers` and `restartTriggers`.
|
||||
:::
|
||||
'';
|
||||
defaultText = ''
|
||||
{
|
||||
wants = ... # All of the required components as a target unit.
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
sessionPackage = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
description = ''
|
||||
The collective package containing everything desktop-related
|
||||
such as:
|
||||
|
||||
* The display session (`<name>.desktop`) files.
|
||||
* gnome-session `.session` file.
|
||||
* The components `.desktop` file.
|
||||
'';
|
||||
readOnly = true;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# Append the session argument.
|
||||
extraArgs = [ "--session=${name}" ];
|
||||
|
||||
# By default. set the required components from the given desktop
|
||||
# components.
|
||||
requiredComponents = lib.mapAttrsToList (_: component: component.id) config.components;
|
||||
|
||||
targetUnit = {
|
||||
overrideStrategy = lib.mkForce "asDropin";
|
||||
wants = lib.mkDefault (builtins.map (c: "${c}.target") config.requiredComponents);
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user