mirror of
https://github.com/foo-dogsquared/nixos-config.git
synced 2025-02-07 12:19:07 +00:00
programs/gnome-session: generate systemd units in desktop session package
Didn't know it was possible to simply have it generated which is a nicer solution.
This commit is contained in:
parent
922680d131
commit
6cd1d0baa1
@ -1,9 +1,9 @@
|
|||||||
{ config, lib, pkgs, utils, ... }:
|
{ config, lib, pkgs, utils, ... }:
|
||||||
|
|
||||||
# TODO: Generate the systemd units and place them in the desktop session package.
|
|
||||||
let
|
let
|
||||||
cfg = config.programs.gnome-session;
|
cfg = config.programs.gnome-session;
|
||||||
|
|
||||||
|
# TODO: Modularize these types, it's getting too big.
|
||||||
componentsType = { name, config, options, session, ... }: {
|
componentsType = { name, config, options, session, ... }: {
|
||||||
options = {
|
options = {
|
||||||
description = lib.mkOption {
|
description = lib.mkOption {
|
||||||
@ -16,8 +16,8 @@ let
|
|||||||
script = lib.mkOption {
|
script = lib.mkOption {
|
||||||
type = lib.types.lines;
|
type = lib.types.lines;
|
||||||
description = ''
|
description = ''
|
||||||
The script of the component. Take note this will be wrapped in a
|
Shell script fragment of the component. Take note this will be
|
||||||
script for proper integration with `gnome-session`.
|
wrapped in a script for proper integration with `gnome-session`.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,24 +45,53 @@ let
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# 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`.
|
||||||
serviceConfig = lib.mkOption {
|
serviceConfig = lib.mkOption {
|
||||||
type = lib.types.attrsOf utils.systemdUtils.unitOptions.unitOption;
|
type =
|
||||||
|
let
|
||||||
|
inherit (utils.systemdUtils.lib) unitConfig serviceConfig;
|
||||||
|
inherit (utils.systemdUtils.unitOptions) commonUnitOptions serviceOptions;
|
||||||
|
in
|
||||||
|
lib.types.submodule [
|
||||||
|
commonUnitOptions
|
||||||
|
serviceOptions
|
||||||
|
serviceConfig
|
||||||
|
unitConfig
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
systemd service configuration to be used in
|
systemd service configuration to be generated. This should be
|
||||||
{option}`systemd.user.services.<name>`.
|
configured if the session is managed by systemd.
|
||||||
|
|
||||||
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`.
|
||||||
|
:::
|
||||||
'';
|
'';
|
||||||
default = {};
|
default = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
targetConfig = lib.mkOption {
|
targetConfig = lib.mkOption {
|
||||||
type = lib.types.attrsOf utils.systemdUtils.unitOptions.unitOption;
|
type =
|
||||||
|
let
|
||||||
|
inherit (utils.systemdUtils.lib) unitConfig;
|
||||||
|
inherit (utils.systemdUtils.unitOptions) commonUnitOptions;
|
||||||
|
in
|
||||||
|
lib.types.submodule [
|
||||||
|
commonUnitOptions
|
||||||
|
unitConfig
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
systemd target configuration to be used in
|
systemd target configuration to be generated. This should be
|
||||||
{option}`systemd.user.target.<name>`.
|
configured if the session is managed by systemd.
|
||||||
|
|
||||||
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`.
|
||||||
|
:::
|
||||||
'';
|
'';
|
||||||
default = {};
|
default = {};
|
||||||
};
|
};
|
||||||
@ -73,7 +102,7 @@ let
|
|||||||
The identifier of the component used in generating filenames for its
|
The identifier of the component used in generating filenames for its
|
||||||
`.desktop` files and as part of systemd unit names.
|
`.desktop` files and as part of systemd unit names.
|
||||||
'';
|
'';
|
||||||
defaultText = "$${session.name}.$${name}";
|
defaultText = "\${session.name}.\${name}";
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -120,23 +149,29 @@ let
|
|||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
script = lib.mkAfter "${config.scriptPackage}/bin/${session.prefix}-${name}-script";
|
script = lib.mkAfter "${config.scriptPackage}/bin/${session.prefix}-${name}-script";
|
||||||
description = lib.mkDefault config.description;
|
description = lib.mkDefault config.description;
|
||||||
|
before = [ "${config.id}.target" ];
|
||||||
|
partOf = [ "${config.id}.target" ];
|
||||||
|
|
||||||
path = [ cfg.package ];
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Slice = lib.mkDefault "session.slice";
|
Slice = lib.mkDefault "session.slice";
|
||||||
Restart = lib.mkDefault "on-failure";
|
Restart = lib.mkDefault "on-failure";
|
||||||
TimeoutStopSec = lib.mkDefault 5;
|
TimeoutStopSec = lib.mkDefault 5;
|
||||||
};
|
};
|
||||||
|
|
||||||
unitConfig = {
|
unitConfig = {
|
||||||
# Units managed by gnome-session are required to have CollectMode=
|
# Units managed by gnome-session are required to have CollectMode=
|
||||||
# set to this value.
|
# set to this value.
|
||||||
CollectMode = lib.mkForce "inactive-or-failed";
|
CollectMode = lib.mkForce "inactive-or-failed";
|
||||||
|
|
||||||
|
# Some sane unit configurations for systemd-managed desktop
|
||||||
|
# components.
|
||||||
RefuseManualStart = lib.mkDefault true;
|
RefuseManualStart = lib.mkDefault true;
|
||||||
RefuseManualStop = lib.mkDefault true;
|
RefuseManualStop = lib.mkDefault true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
targetConfig = {
|
targetConfig = {
|
||||||
|
wants = [ "${config.id}.service" ];
|
||||||
description = lib.mkDefault config.description;
|
description = lib.mkDefault config.description;
|
||||||
documentation = [
|
documentation = [
|
||||||
"man:gnome-session(1)"
|
"man:gnome-session(1)"
|
||||||
@ -190,7 +225,7 @@ let
|
|||||||
A one-sentence description of the desktop environment.
|
A one-sentence description of the desktop environment.
|
||||||
'';
|
'';
|
||||||
default = "${config.fullName} desktop environment";
|
default = "${config.fullName} desktop environment";
|
||||||
defaultText = lib.literalExpression "$${<name>.fullName} desktop environment";
|
defaultText = lib.literalExpression "\${<name>.fullName} desktop environment";
|
||||||
example = "A desktop environment featuring a scrolling compositor.";
|
example = "A desktop environment featuring a scrolling compositor.";
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -211,13 +246,13 @@ let
|
|||||||
{
|
{
|
||||||
window-manager = {
|
window-manager = {
|
||||||
script = '''
|
script = '''
|
||||||
$${lib.getExe' config.programs.sway.package "sway"}
|
''${lib.getExe' config.programs.sway.package "sway"}
|
||||||
''';
|
''';
|
||||||
description = "An i3 clone for Wayland.";
|
description = "An i3 clone for Wayland.";
|
||||||
};
|
};
|
||||||
|
|
||||||
desktop-widgets.script = '''
|
desktop-widgets.script = '''
|
||||||
$${lib.getExe' pkgs.ags "ags"} --config $${./config.js}
|
''${lib.getExe' pkgs.ags "ags"} --config ''${./config.js}
|
||||||
''';
|
''';
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
@ -241,22 +276,29 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
targetConfig = lib.mkOption {
|
targetConfig = lib.mkOption {
|
||||||
type = lib.types.attrsOf utils.systemdUtils.unitOptions.unitOption;
|
type =
|
||||||
|
let
|
||||||
|
inherit (utils.systemdUtils.lib) unitConfig;
|
||||||
|
inherit (utils.systemdUtils.unitOptions) commonUnitOptions;
|
||||||
|
in
|
||||||
|
lib.types.submodule [
|
||||||
|
commonUnitOptions
|
||||||
|
unitConfig
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
systemd target configuration to be used in
|
systemd target configuration to be generated. This should be
|
||||||
{option}`systemd.user.target."gnome-session@<name>"`.
|
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.).
|
||||||
|
|
||||||
This should be configured if the session is managed by systemd and
|
:::{.note}
|
||||||
you want to control the session further (which is recommended since
|
This has the same options as {option}`systemd.user.targets.<name>`
|
||||||
this module don't know what components are more important, etc.).
|
but without certain options from stage 2 counterparts such as
|
||||||
|
`reloadTriggers` and `restartTriggers`.
|
||||||
|
:::
|
||||||
'';
|
'';
|
||||||
default = {
|
|
||||||
description = "${config.fullName} desktop environment";
|
|
||||||
wants = lib.mapAttrsToList (_: component: "${component.id}.target") config.components;
|
|
||||||
};
|
|
||||||
defaultText = ''
|
defaultText = ''
|
||||||
{
|
{
|
||||||
description = "$${config.fullName} desktop environment";
|
|
||||||
wants = ... # All of the components.
|
wants = ... # All of the components.
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
@ -265,9 +307,21 @@ let
|
|||||||
sessionPackage = lib.mkOption {
|
sessionPackage = lib.mkOption {
|
||||||
type = lib.types.package;
|
type = lib.types.package;
|
||||||
description = ''
|
description = ''
|
||||||
The collective package containing everything (except the systemd
|
The collective package containing everything desktop-related
|
||||||
units) desktop-related files such as the Wayland session file,
|
such as:
|
||||||
gnome-session `.session` file, and the components `.desktop` file.
|
|
||||||
|
* The Wayland session file.
|
||||||
|
* gnome-session `.session` file.
|
||||||
|
* The components `.desktop` file.
|
||||||
|
* The components' systemd unit files.
|
||||||
|
'';
|
||||||
|
readOnly = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemdUserUnits = lib.mkOption {
|
||||||
|
type = utils.systemdUtils.types.units;
|
||||||
|
description = ''
|
||||||
|
A set of systemd user units to be generated.
|
||||||
'';
|
'';
|
||||||
internal = true;
|
internal = true;
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
@ -275,14 +329,39 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
# Append the session argument.
|
||||||
|
extraArgs = [ "--session=${name}" ];
|
||||||
|
|
||||||
|
# While it is tempting to have this delegated to `systemd.user.services`
|
||||||
|
# and the like, it does have a future problem regarding how the generated
|
||||||
|
# units will handle reload on change since NixOS systemd units lets you
|
||||||
|
# have that option. Restricting it ourselves prevent it from doing so.
|
||||||
|
#
|
||||||
|
# As a (HUGE) bonus, it also leads to a more elegant solution of making
|
||||||
|
# an entire package of the desktop environment and simply linking them
|
||||||
|
# with various NixOS options like `systemd.packages` and the like.
|
||||||
|
systemdUserUnits =
|
||||||
|
let
|
||||||
|
inherit (utils.systemdUtils.lib) serviceToUnit targetToUnit;
|
||||||
|
componentsUnits =
|
||||||
|
lib.foldlAttrs (acc: name: component:
|
||||||
|
acc // {
|
||||||
|
"${component.id}.service" = serviceToUnit component.id component.serviceConfig;
|
||||||
|
"${component.id}.target" = targetToUnit component.id component.targetConfig;
|
||||||
|
})
|
||||||
|
{} config.components;
|
||||||
|
in
|
||||||
|
componentsUnits // {
|
||||||
|
"gnome-session@${name}.target" = targetToUnit "gnome-session@${name}" config.targetConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
targetConfig = {
|
||||||
|
overrideStrategy = lib.mkForce "asDropin";
|
||||||
|
wants = lib.mkDefault (lib.mapAttrsToList (_: component: "${component.id}.target") config.components);
|
||||||
|
};
|
||||||
|
|
||||||
sessionPackage =
|
sessionPackage =
|
||||||
let
|
let
|
||||||
installDesktops = lib.mapAttrsToList
|
|
||||||
(_: p: ''
|
|
||||||
install -Dm0644 ${p.desktopPackage}/share/applications/*.desktop -t $out/share/applications
|
|
||||||
'')
|
|
||||||
config.components;
|
|
||||||
|
|
||||||
requiredComponents = lib.mapAttrsToList
|
requiredComponents = lib.mapAttrsToList
|
||||||
(_: component: component.id)
|
(_: component: component.id)
|
||||||
config.components;
|
config.components;
|
||||||
@ -312,6 +391,23 @@ let
|
|||||||
|
|
||||||
${lib.getExe' cfg.package "gnome-session"} ${lib.escapeShellArgs config.extraArgs}
|
${lib.getExe' cfg.package "gnome-session"} ${lib.escapeShellArgs config.extraArgs}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
installSystemdUserUnits = lib.mapAttrsToList (n: v:
|
||||||
|
if (v ? overrideStrategy && v.overrideStrategy == "asDropin") then ''
|
||||||
|
(
|
||||||
|
unit="${v.unit}/${n}"
|
||||||
|
unit_filename=$(basename "$unit")
|
||||||
|
install -Dm0644 "$unit" "$out/share/systemd/user/''${unit_filename}.d/session.conf"
|
||||||
|
)
|
||||||
|
'' else ''
|
||||||
|
install -Dm0644 "${v.unit}/${n}" -t "$out/share/systemd/user"
|
||||||
|
'') config.systemdUserUnits;
|
||||||
|
|
||||||
|
installDesktops = lib.mapAttrsToList
|
||||||
|
(_: p: ''
|
||||||
|
install -Dm0644 ${p.desktopPackage}/share/applications/*.desktop -t $out/share/applications
|
||||||
|
'')
|
||||||
|
config.components;
|
||||||
in
|
in
|
||||||
pkgs.runCommandLocal "${name}-desktop-session-files"
|
pkgs.runCommandLocal "${name}-desktop-session-files"
|
||||||
{
|
{
|
||||||
@ -333,6 +429,9 @@ let
|
|||||||
install -Dm0644 "$waylandSessionPath" "$WAYLAND_SESSION_FILE"
|
install -Dm0644 "$waylandSessionPath" "$WAYLAND_SESSION_FILE"
|
||||||
substituteAllInPlace "$WAYLAND_SESSION_FILE"
|
substituteAllInPlace "$WAYLAND_SESSION_FILE"
|
||||||
|
|
||||||
|
${lib.concatStringsSep "\n" installSystemdUserUnits}
|
||||||
|
mkdir -p "$out/lib/systemd" && ln -sfn "$out/share/systemd/user" "$out/lib/systemd/user"
|
||||||
|
|
||||||
${lib.concatStringsSep "\n" installDesktops}
|
${lib.concatStringsSep "\n" installDesktops}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
@ -367,21 +466,21 @@ in
|
|||||||
components = {
|
components = {
|
||||||
window-manager = {
|
window-manager = {
|
||||||
script = '''
|
script = '''
|
||||||
$${lib.getExe' config.programs.sway.package "sway"}
|
''${lib.getExe' config.programs.sway.package "sway"}
|
||||||
''';
|
''';
|
||||||
description = "An i3 clone for Wayland.";
|
description = "An i3 clone for Wayland.";
|
||||||
};
|
};
|
||||||
|
|
||||||
desktop-widgets = {
|
desktop-widgets = {
|
||||||
script = '''
|
script = '''
|
||||||
$${lib.getExe' pkgs.ags "ags"} --config $${./config.js}
|
''${lib.getExe' pkgs.ags "ags"} --config ''${./config.js}
|
||||||
''';
|
''';
|
||||||
description = "A desktop widget system using layer-shell protocol.";
|
description = "A desktop widget system using layer-shell protocol.";
|
||||||
};
|
};
|
||||||
|
|
||||||
auth-agent = {
|
auth-agent = {
|
||||||
script = '''
|
script = '''
|
||||||
$${lib.getExe' pkgs.polkit_gnome "polkit-gnome-authentication-agent-1"}
|
''${lib.getExe' pkgs.polkit_gnome "polkit-gnome-authentication-agent-1"}
|
||||||
''';
|
''';
|
||||||
description = "Polkit authentication agent";
|
description = "Polkit authentication agent";
|
||||||
};
|
};
|
||||||
@ -399,49 +498,6 @@ in
|
|||||||
(_: session:
|
(_: session:
|
||||||
session.sessionPackage)
|
session.sessionPackage)
|
||||||
cfg.sessions;
|
cfg.sessions;
|
||||||
|
|
||||||
generateServiceBundle = acc: name: session:
|
|
||||||
let
|
|
||||||
services =
|
|
||||||
lib.mapAttrs'
|
|
||||||
generateComponentService
|
|
||||||
session.components;
|
|
||||||
|
|
||||||
generateComponentService = name: component:
|
|
||||||
let
|
|
||||||
serviceConfig = lib.mkMerge [
|
|
||||||
{
|
|
||||||
before = [ "${component.id}.target" ];
|
|
||||||
partOf = [ "${component.id}.target" ];
|
|
||||||
}
|
|
||||||
component.serviceConfig
|
|
||||||
];
|
|
||||||
in
|
|
||||||
lib.nameValuePair component.id serviceConfig;
|
|
||||||
in
|
|
||||||
acc // services;
|
|
||||||
|
|
||||||
generateTargetBundle = acc: name: session:
|
|
||||||
let
|
|
||||||
targets =
|
|
||||||
lib.mapAttrs'
|
|
||||||
generateComponentTarget
|
|
||||||
session.components;
|
|
||||||
|
|
||||||
generateComponentTarget = name: component:
|
|
||||||
let
|
|
||||||
targetConfig = lib.mkMerge [
|
|
||||||
{
|
|
||||||
wants = [ "${component.id}.service" ];
|
|
||||||
}
|
|
||||||
component.targetConfig
|
|
||||||
];
|
|
||||||
in
|
|
||||||
lib.nameValuePair component.id targetConfig;
|
|
||||||
in
|
|
||||||
acc // targets // {
|
|
||||||
"gnome-session@${name}" = session.targetConfig;
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Install all of the desktop session files.
|
# Install all of the desktop session files.
|
||||||
@ -452,12 +508,7 @@ in
|
|||||||
environment.pathsToLink = [ "/share/gnome-session" ];
|
environment.pathsToLink = [ "/share/gnome-session" ];
|
||||||
|
|
||||||
# Import those systemd units from gnome-session as well.
|
# Import those systemd units from gnome-session as well.
|
||||||
systemd.packages = [ cfg.package ]; #++ sessionPackages;
|
systemd.packages = [ cfg.package ] ++ sessionPackages;
|
||||||
|
|
||||||
# Most importantly for systemd-managed gnome-session sessions, generate
|
|
||||||
# those services.
|
|
||||||
systemd.user.services = lib.foldlAttrs generateServiceBundle { } cfg.sessions;
|
|
||||||
systemd.user.targets = lib.foldlAttrs generateTargetBundle { } cfg.sessions;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user