summary refs log tree commit diff
path: root/nixos/modules/programs
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/programs')
-rw-r--r--nixos/modules/programs/appgate-sdp.nix25
-rw-r--r--nixos/modules/programs/atop.nix128
-rw-r--r--nixos/modules/programs/bandwhich.nix2
-rw-r--r--nixos/modules/programs/bash/bash-completion.nix37
-rw-r--r--nixos/modules/programs/bash/bash.nix45
-rw-r--r--nixos/modules/programs/bash/ls-colors.nix20
-rw-r--r--nixos/modules/programs/bash/undistract-me.nix36
-rw-r--r--nixos/modules/programs/captive-browser.nix88
-rw-r--r--nixos/modules/programs/ccache.nix2
-rw-r--r--nixos/modules/programs/cdemu.nix3
-rw-r--r--nixos/modules/programs/chromium.nix2
-rw-r--r--nixos/modules/programs/command-not-found/command-not-found.nix6
-rw-r--r--nixos/modules/programs/command-not-found/command-not-found.pl17
-rw-r--r--nixos/modules/programs/dconf.nix2
-rw-r--r--nixos/modules/programs/droidcam.nix16
-rw-r--r--nixos/modules/programs/environment.nix1
-rw-r--r--nixos/modules/programs/feedbackd.nix32
-rw-r--r--nixos/modules/programs/file-roller.nix4
-rw-r--r--nixos/modules/programs/firejail.nix46
-rw-r--r--nixos/modules/programs/fish.nix319
-rw-r--r--nixos/modules/programs/fish_completion-generator.patch19
-rw-r--r--nixos/modules/programs/flashrom.nix26
-rw-r--r--nixos/modules/programs/flexoptix-app.nix25
-rw-r--r--nixos/modules/programs/gamemode.nix96
-rw-r--r--nixos/modules/programs/geary.nix6
-rw-r--r--nixos/modules/programs/gnome-disks.nix4
-rw-r--r--nixos/modules/programs/gnome-documents.nix10
-rw-r--r--nixos/modules/programs/gnome-terminal.nix6
-rw-r--r--nixos/modules/programs/gpaste.nix8
-rw-r--r--nixos/modules/programs/hamster.nix2
-rw-r--r--nixos/modules/programs/kdeconnect.nix35
-rw-r--r--nixos/modules/programs/less.nix2
-rw-r--r--nixos/modules/programs/mininet.nix2
-rw-r--r--nixos/modules/programs/msmtp.nix104
-rw-r--r--nixos/modules/programs/neovim.nix165
-rw-r--r--nixos/modules/programs/nm-applet.nix15
-rw-r--r--nixos/modules/programs/noisetorch.nix25
-rw-r--r--nixos/modules/programs/partition-manager.nix19
-rw-r--r--nixos/modules/programs/phosh.nix163
-rw-r--r--nixos/modules/programs/proxychains.nix165
-rw-r--r--nixos/modules/programs/qt5ct.nix2
-rw-r--r--nixos/modules/programs/seahorse.nix6
-rw-r--r--nixos/modules/programs/ssh.nix4
-rw-r--r--nixos/modules/programs/ssmtp.nix25
-rw-r--r--nixos/modules/programs/steam.nix42
-rw-r--r--nixos/modules/programs/sway.nix41
-rw-r--r--nixos/modules/programs/tilp2.nix28
-rw-r--r--nixos/modules/programs/tsm-client.nix4
-rw-r--r--nixos/modules/programs/turbovnc.nix54
-rw-r--r--nixos/modules/programs/udevil.nix3
-rw-r--r--nixos/modules/programs/venus.nix173
-rw-r--r--nixos/modules/programs/vim.nix14
-rw-r--r--nixos/modules/programs/wshowkeys.nix22
-rw-r--r--nixos/modules/programs/x2goserver.nix149
-rw-r--r--nixos/modules/programs/xss-lock.nix4
-rw-r--r--nixos/modules/programs/xwayland.nix51
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.xml2
-rw-r--r--nixos/modules/programs/zsh/zsh.nix28
58 files changed, 1750 insertions, 630 deletions
diff --git a/nixos/modules/programs/appgate-sdp.nix b/nixos/modules/programs/appgate-sdp.nix
new file mode 100644
index 00000000000..12cb542f4d0
--- /dev/null
+++ b/nixos/modules/programs/appgate-sdp.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    programs.appgate-sdp = {
+      enable = mkEnableOption "AppGate SDP VPN client";
+    };
+  };
+
+  config = mkIf config.programs.appgate-sdp.enable {
+    boot.kernelModules = [ "tun" ];
+    environment.systemPackages = [ pkgs.appgate-sdp ];
+    services.dbus.packages = [ pkgs.appgate-sdp ];
+    systemd = {
+      packages = [ pkgs.appgate-sdp ];
+      # https://github.com/NixOS/nixpkgs/issues/81138
+      services.appgatedriver.wantedBy = [ "multi-user.target" ];
+      services.appgate-dumb-resolver.path = [ pkgs.e2fsprogs ];
+      services.appgate-resolver.path = [ pkgs.procps pkgs.e2fsprogs ];
+      services.appgatedriver.path = [ pkgs.e2fsprogs ];
+    };
+  };
+}
diff --git a/nixos/modules/programs/atop.nix b/nixos/modules/programs/atop.nix
index 7ef8d687ca1..b45eb16e3ea 100644
--- a/nixos/modules/programs/atop.nix
+++ b/nixos/modules/programs/atop.nix
@@ -1,6 +1,6 @@
 # Global configuration for atop.
 
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
 
 with lib;
 
@@ -12,11 +12,85 @@ in
 
   options = {
 
-    programs.atop = {
+    programs.atop = rec {
 
+      enable = mkEnableOption "Atop";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.atop;
+        defaultText = "pkgs.atop";
+        description = ''
+          Which package to use for Atop.
+        '';
+      };
+
+      netatop = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to install and enable the netatop kernel module.
+            Note: this sets the kernel taint flag "O" for loading out-of-tree modules.
+          '';
+        };
+        package = mkOption {
+          type = types.package;
+          default = config.boot.kernelPackages.netatop;
+          defaultText = "config.boot.kernelPackages.netatop";
+          description = ''
+            Which package to use for netatop.
+          '';
+        };
+      };
+
+      atopgpu.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to install and enable the atopgpud daemon to get information about
+          NVIDIA gpus.
+        '';
+      };
+
+      setuidWrapper.enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to install a setuid wrapper for Atop. This is required to use some of
+          the features as non-root user (e.g.: ipc information, netatop, atopgpu).
+          Atop tries to drop the root privileges shortly after starting.
+        '';
+      };
+
+      atopService.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable the atop service responsible for storing statistics for
+          long-term analysis.
+        '';
+      };
+      atopRotateTimer.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable the atop-rotate timer, which restarts the atop service
+          daily to make sure the data files are rotate.
+        '';
+      };
+      atopacctService.enable = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable the atopacct service which manages process accounting.
+          This allows Atop to gather data about processes that disappeared in between
+          two refresh intervals.
+        '';
+      };
       settings = mkOption {
         type = types.attrs;
-        default = {};
+        default = { };
         example = {
           flags = "a1f";
           interval = 5;
@@ -25,12 +99,50 @@ in
           Parameters to be written to <filename>/etc/atoprc</filename>.
         '';
       };
-
     };
   };
 
-  config = mkIf (cfg.settings != {}) {
-    environment.etc.atoprc.text =
-      concatStrings (mapAttrsToList (n: v: "${n} ${toString v}\n") cfg.settings);
-  };
+  config = mkIf cfg.enable (
+    let
+      atop =
+        if cfg.atopgpu.enable then
+          (cfg.package.override { withAtopgpu = true; })
+        else
+          cfg.package;
+    in
+    {
+      environment.etc = mkIf (cfg.settings != { }) {
+        atoprc.text = concatStrings
+          (mapAttrsToList
+            (n: v: ''
+              ${n} ${toString v}
+            '')
+            cfg.settings);
+      };
+      environment.systemPackages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
+      boot.extraModulePackages = [ (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
+      systemd =
+        let
+          mkSystemd = type: cond: name: restartTriggers: {
+            ${name} = lib.mkIf cond {
+              inherit restartTriggers;
+              wantedBy = [ (if type == "services" then "multi-user.target" else if type == "timers" then "timers.target" else null) ];
+            };
+          };
+          mkService = mkSystemd "services";
+          mkTimer = mkSystemd "timers";
+        in
+        {
+          packages = [ atop (lib.mkIf cfg.netatop.enable cfg.netatop.package) ];
+          services =
+            mkService cfg.atopService.enable "atop" [ atop ]
+            // mkService cfg.atopacctService.enable "atopacct" [ atop ]
+            // mkService cfg.netatop.enable "netatop" [ cfg.netatop.package ]
+            // mkService cfg.atopgpu.enable "atopgpu" [ atop ];
+          timers = mkTimer cfg.atopRotateTimer.enable "atop-rotate" [ atop ];
+        };
+      security.wrappers =
+        lib.mkIf cfg.setuidWrapper.enable { atop = { source = "${atop}/bin/atop"; }; };
+    }
+  );
 }
diff --git a/nixos/modules/programs/bandwhich.nix b/nixos/modules/programs/bandwhich.nix
index 5413044f461..1cffb5fa276 100644
--- a/nixos/modules/programs/bandwhich.nix
+++ b/nixos/modules/programs/bandwhich.nix
@@ -4,7 +4,7 @@ with lib;
 
 let cfg = config.programs.bandwhich;
 in {
-  meta.maintainers = with maintainers; [ filalex77 ];
+  meta.maintainers = with maintainers; [ Br1ght0ne ];
 
   options = {
     programs.bandwhich = {
diff --git a/nixos/modules/programs/bash/bash-completion.nix b/nixos/modules/programs/bash/bash-completion.nix
new file mode 100644
index 00000000000..f07b1b636ef
--- /dev/null
+++ b/nixos/modules/programs/bash/bash-completion.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  enable = config.programs.bash.enableCompletion;
+in
+{
+  options = {
+    programs.bash.enableCompletion = mkEnableOption "Bash completion for all interactive bash shells" // {
+      default = true;
+    };
+  };
+
+  config = mkIf enable {
+    programs.bash.promptPluginInit = ''
+      # Check whether we're running a version of Bash that has support for
+      # programmable completion. If we do, enable all modules installed in
+      # the system and user profile in obsolete /etc/bash_completion.d/
+      # directories. Bash loads completions in all
+      # $XDG_DATA_DIRS/bash-completion/completions/
+      # on demand, so they do not need to be sourced here.
+      if shopt -q progcomp &>/dev/null; then
+        . "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
+        nullglobStatus=$(shopt -p nullglob)
+        shopt -s nullglob
+        for p in $NIX_PROFILES; do
+          for m in "$p/etc/bash_completion.d/"*; do
+            . $m
+          done
+        done
+        eval "$nullglobStatus"
+        unset nullglobStatus p m
+      fi
+    '';
+  };
+}
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index 1b3254b54a5..908ab34b08d 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -11,31 +11,6 @@ let
 
   cfg = config.programs.bash;
 
-  bashCompletion = optionalString cfg.enableCompletion ''
-    # Check whether we're running a version of Bash that has support for
-    # programmable completion. If we do, enable all modules installed in
-    # the system and user profile in obsolete /etc/bash_completion.d/
-    # directories. Bash loads completions in all
-    # $XDG_DATA_DIRS/bash-completion/completions/
-    # on demand, so they do not need to be sourced here.
-    if shopt -q progcomp &>/dev/null; then
-      . "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
-      nullglobStatus=$(shopt -p nullglob)
-      shopt -s nullglob
-      for p in $NIX_PROFILES; do
-        for m in "$p/etc/bash_completion.d/"*; do
-          . $m
-        done
-      done
-      eval "$nullglobStatus"
-      unset nullglobStatus p m
-    fi
-  '';
-
-  lsColors = optionalString cfg.enableLsColors ''
-    eval "$(${pkgs.coreutils}/bin/dircolors -b)"
-  '';
-
   bashAliases = concatStringsSep "\n" (
     mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}")
       (filterAttrs (k: v: v != null) cfg.shellAliases)
@@ -123,20 +98,13 @@ in
         type = types.lines;
       };
 
-      enableCompletion = mkOption {
-        default = true;
-        description = ''
-          Enable Bash completion for all interactive bash shells.
-        '';
-        type = types.bool;
-      };
-
-      enableLsColors = mkOption {
-        default = true;
+      promptPluginInit = mkOption {
+        default = "";
         description = ''
-          Enable extra colors in directory listings.
+          Shell script code used to initialise bash prompt plugins.
         '';
-        type = types.bool;
+        type = types.lines;
+        internal = true;
       };
 
     };
@@ -167,8 +135,7 @@ in
         set +h
 
         ${cfg.promptInit}
-        ${bashCompletion}
-        ${lsColors}
+        ${cfg.promptPluginInit}
         ${bashAliases}
 
         ${cfge.interactiveShellInit}
diff --git a/nixos/modules/programs/bash/ls-colors.nix b/nixos/modules/programs/bash/ls-colors.nix
new file mode 100644
index 00000000000..254ee14c477
--- /dev/null
+++ b/nixos/modules/programs/bash/ls-colors.nix
@@ -0,0 +1,20 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  enable = config.programs.bash.enableLsColors;
+in
+{
+  options = {
+    programs.bash.enableLsColors = mkEnableOption "extra colors in directory listings" // {
+      default = true;
+    };
+  };
+
+  config = mkIf enable {
+    programs.bash.promptPluginInit = ''
+      eval "$(${pkgs.coreutils}/bin/dircolors -b)"
+    '';
+  };
+}
diff --git a/nixos/modules/programs/bash/undistract-me.nix b/nixos/modules/programs/bash/undistract-me.nix
new file mode 100644
index 00000000000..0e6465e048a
--- /dev/null
+++ b/nixos/modules/programs/bash/undistract-me.nix
@@ -0,0 +1,36 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.bash.undistractMe;
+in
+{
+  options = {
+    programs.bash.undistractMe = {
+      enable = mkEnableOption "notifications when long-running terminal commands complete";
+
+      playSound = mkEnableOption "notification sounds when long-running terminal commands complete";
+
+      timeout = mkOption {
+        default = 10;
+        description = ''
+          Number of seconds it would take for a command to be considered long-running.
+        '';
+        type = types.int;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    programs.bash.promptPluginInit = ''
+      export LONG_RUNNING_COMMAND_TIMEOUT=${toString cfg.timeout}
+      export UDM_PLAY_SOUND=${if cfg.playSound then "1" else "0"}
+      . "${pkgs.undistract-me}/etc/profile.d/undistract-me.sh"
+    '';
+  };
+
+  meta = {
+    maintainers = with maintainers; [ kira-bruneau ];
+  };
+}
diff --git a/nixos/modules/programs/captive-browser.nix b/nixos/modules/programs/captive-browser.nix
index 26db1675072..1f223e2475c 100644
--- a/nixos/modules/programs/captive-browser.nix
+++ b/nixos/modules/programs/captive-browser.nix
@@ -1,7 +1,6 @@
 { config, lib, pkgs, ... }:
 
 with lib;
-
 let
   cfg = config.programs.captive-browser;
 in
@@ -27,15 +26,17 @@ in
       # the options below are the same as in "captive-browser.toml"
       browser = mkOption {
         type = types.str;
-        default = concatStringsSep " " [ ''${pkgs.chromium}/bin/chromium''
-                                         ''--user-data-dir=''${XDG_DATA_HOME:-$HOME/.local/share}/chromium-captive''
-                                         ''--proxy-server="socks5://$PROXY"''
-                                         ''--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost"''
-                                         ''--no-first-run''
-                                         ''--new-window''
-                                         ''--incognito''
-                                         ''http://cache.nixos.org/''
-                                       ];
+        default = concatStringsSep " " [
+          ''${pkgs.chromium}/bin/chromium''
+          ''--user-data-dir=''${XDG_DATA_HOME:-$HOME/.local/share}/chromium-captive''
+          ''--proxy-server="socks5://$PROXY"''
+          ''--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE localhost"''
+          ''--no-first-run''
+          ''--new-window''
+          ''--incognito''
+          ''-no-default-browser-check''
+          ''http://cache.nixos.org/''
+        ];
         description = ''
           The shell (/bin/sh) command executed once the proxy starts.
           When browser exits, the proxy exits. An extra env var PROXY is available.
@@ -62,7 +63,7 @@ in
       socks5-addr = mkOption {
         type = types.str;
         default = "localhost:1666";
-        description = ''the listen address for the SOCKS5 proxy server'';
+        description = "the listen address for the SOCKS5 proxy server";
       };
 
       bindInterface = mkOption {
@@ -81,42 +82,45 @@ in
 
   config = mkIf cfg.enable {
 
-    programs.captive-browser.dhcp-dns = mkOptionDefault (
-      if config.networking.networkmanager.enable then
-        "${pkgs.networkmanager}/bin/nmcli dev show ${escapeShellArg cfg.interface} | ${pkgs.gnugrep}/bin/fgrep IP4.DNS"
-      else if config.networking.dhcpcd.enable then
-        "${pkgs.dhcpcd}/bin/dhcpcd -U ${escapeShellArg cfg.interface} | ${pkgs.gnugrep}/bin/fgrep domain_name_servers"
-      else if config.networking.useNetworkd then
-        "${cfg.package}/bin/systemd-networkd-dns ${escapeShellArg cfg.interface}"
-      else
-        "${config.security.wrapperDir}/udhcpc --quit --now -f -i ${escapeShellArg cfg.interface} -O dns --script ${
-            pkgs.writeScript "udhcp-script" ''
-              #!/bin/sh
-              if [ "$1" = bound ]; then
-                echo "$dns"
-              fi
-            ''}"
-    );
+    programs.captive-browser.dhcp-dns =
+      let
+        iface = prefix:
+          optionalString cfg.bindInterface (concatStringsSep " " (map escapeShellArg [ prefix cfg.interface ]));
+      in
+      mkOptionDefault (
+        if config.networking.networkmanager.enable then
+          "${pkgs.networkmanager}/bin/nmcli dev show ${iface ""} | ${pkgs.gnugrep}/bin/fgrep IP4.DNS"
+        else if config.networking.dhcpcd.enable then
+          "${pkgs.dhcpcd}/bin/dhcpcd ${iface "-U"} | ${pkgs.gnugrep}/bin/fgrep domain_name_servers"
+        else if config.networking.useNetworkd then
+          "${cfg.package}/bin/systemd-networkd-dns ${iface ""}"
+        else
+          "${config.security.wrapperDir}/udhcpc --quit --now -f ${iface "-i"} -O dns --script ${
+          pkgs.writeShellScript "udhcp-script" ''
+            if [ "$1" = bound ]; then
+              echo "$dns"
+            fi
+          ''}"
+      );
 
     security.wrappers.udhcpc = {
-      capabilities  = "cap_net_raw+p";
-      source        = "${pkgs.busybox}/bin/udhcpc";
+      capabilities = "cap_net_raw+p";
+      source = "${pkgs.busybox}/bin/udhcpc";
     };
 
     security.wrappers.captive-browser = {
-      capabilities  = "cap_net_raw+p";
-      source        = pkgs.writeScript "captive-browser" ''
-                        #!${pkgs.bash}/bin/bash
-                        export XDG_CONFIG_HOME=${pkgs.writeTextDir "captive-browser.toml" ''
-                                                  browser = """${cfg.browser}"""
-                                                  dhcp-dns = """${cfg.dhcp-dns}"""
-                                                  socks5-addr = """${cfg.socks5-addr}"""
-                                                  ${optionalString cfg.bindInterface ''
-                                                    bind-device = """${cfg.interface}"""
-                                                  ''}
-                                                ''}
-                        exec ${cfg.package}/bin/captive-browser
-                      '';
+      capabilities = "cap_net_raw+p";
+      source = pkgs.writeShellScript "captive-browser" ''
+        export XDG_CONFIG_HOME=${pkgs.writeTextDir "captive-browser.toml" ''
+                                  browser = """${cfg.browser}"""
+                                  dhcp-dns = """${cfg.dhcp-dns}"""
+                                  socks5-addr = """${cfg.socks5-addr}"""
+                                  ${optionalString cfg.bindInterface ''
+                                    bind-device = """${cfg.interface}"""
+                                  ''}
+                                ''}
+        exec ${cfg.package}/bin/captive-browser
+      '';
     };
   };
 }
diff --git a/nixos/modules/programs/ccache.nix b/nixos/modules/programs/ccache.nix
index 3c9e64932f1..d672e1da017 100644
--- a/nixos/modules/programs/ccache.nix
+++ b/nixos/modules/programs/ccache.nix
@@ -17,7 +17,7 @@ in {
       type = types.listOf types.str;
       description = "Nix top-level packages to be compiled using CCache";
       default = [];
-      example = [ "wxGTK30" "qt48" "ffmpeg_3_3" "libav_all" ];
+      example = [ "wxGTK30" "ffmpeg" "libav_all" ];
     };
   };
 
diff --git a/nixos/modules/programs/cdemu.nix b/nixos/modules/programs/cdemu.nix
index a59cd93cadf..142e2934240 100644
--- a/nixos/modules/programs/cdemu.nix
+++ b/nixos/modules/programs/cdemu.nix
@@ -16,18 +16,21 @@ in {
         '';
       };
       group = mkOption {
+        type = types.str;
         default = "cdrom";
         description = ''
           Group that users must be in to use <command>cdemu</command>.
         '';
       };
       gui = mkOption {
+        type = types.bool;
         default = true;
         description = ''
           Whether to install the <command>cdemu</command> GUI (gCDEmu).
         '';
       };
       image-analyzer = mkOption {
+        type = types.bool;
         default = true;
         description = ''
           Whether to install the image analyzer.
diff --git a/nixos/modules/programs/chromium.nix b/nixos/modules/programs/chromium.nix
index 3f042913619..b727f850a94 100644
--- a/nixos/modules/programs/chromium.nix
+++ b/nixos/modules/programs/chromium.nix
@@ -29,7 +29,7 @@ in
           page. To install a chromium extension not included in the chrome web
           store, append to the extension id a semicolon ";" followed by a URL
           pointing to an Update Manifest XML file. See
-          <link xlink:href="https://www.chromium.org/administrators/policy-list-3#ExtensionInstallForcelist">ExtensionInstallForcelist</link>
+          <link xlink:href="https://cloud.google.com/docs/chrome-enterprise/policies/?policy=ExtensionInstallForcelist">ExtensionInstallForcelist</link>
           for additional details.
         '';
         default = [];
diff --git a/nixos/modules/programs/command-not-found/command-not-found.nix b/nixos/modules/programs/command-not-found/command-not-found.nix
index 656c255fcb1..79786584c66 100644
--- a/nixos/modules/programs/command-not-found/command-not-found.nix
+++ b/nixos/modules/programs/command-not-found/command-not-found.nix
@@ -14,10 +14,8 @@ let
     dir = "bin";
     src = ./command-not-found.pl;
     isExecutable = true;
-    inherit (pkgs) perl;
     inherit (cfg) dbPath;
-    perlFlags = concatStrings (map (path: "-I ${path}/${pkgs.perl.libPrefix} ")
-      [ pkgs.perlPackages.DBI pkgs.perlPackages.DBDSQLite pkgs.perlPackages.StringShellQuote ]);
+    perl = pkgs.perl.withPackages (p: [ p.DBDSQLite p.StringShellQuote ]);
   };
 
 in
@@ -80,6 +78,8 @@ in
             # Retry the command if we just installed it.
             if [ $? = 126 ]; then
               "$@"
+            else
+              return 127
             fi
           else
             # Indicate than there was an error so ZSH falls back to its default handler
diff --git a/nixos/modules/programs/command-not-found/command-not-found.pl b/nixos/modules/programs/command-not-found/command-not-found.pl
index ab7aa204653..6e275bcc8be 100644
--- a/nixos/modules/programs/command-not-found/command-not-found.pl
+++ b/nixos/modules/programs/command-not-found/command-not-found.pl
@@ -1,4 +1,4 @@
-#! @perl@/bin/perl -w @perlFlags@
+#! @perl@/bin/perl -w
 
 use strict;
 use DBI;
@@ -27,8 +27,8 @@ if (!defined $res || scalar @$res == 0) {
     my $package = @$res[0]->{package};
     if ($ENV{"NIX_AUTO_INSTALL"} // "") {
         print STDERR <<EOF;
-The program ‘$program’ is currently not installed. It is provided by
-the package ‘$package’, which I will now install for you.
+The program '$program' is currently not installed. It is provided by
+the package '$package', which I will now install for you.
 EOF
         ;
         exit 126 if system("nix-env", "-iA", "nixos.$package") == 0;
@@ -36,16 +36,17 @@ EOF
         exec("nix-shell", "-p", $package, "--run", shell_quote("exec", @ARGV));
     } else {
         print STDERR <<EOF;
-The program ‘$program’ is currently not installed. You can install it by typing:
-  nix-env -iA nixos.$package
+The program '$program' is not in your PATH. You can make it available in an
+ephemeral shell by typing:
+  nix-shell -p $package
 EOF
     }
 } else {
     print STDERR <<EOF;
-The program ‘$program’ is currently not installed. It is provided by
-several packages. You can install it by typing one of the following:
+The program '$program' is not in your PATH. It is provided by several packages.
+You can make it available in an ephemeral shell by typing one of the following:
 EOF
-    print STDERR "  nix-env -iA nixos.$_->{package}\n" foreach @$res;
+    print STDERR "  nix-shell -p $_->{package}\n" foreach @$res;
 }
 
 exit 127;
diff --git a/nixos/modules/programs/dconf.nix b/nixos/modules/programs/dconf.nix
index ec85cb9d18c..298abac8afa 100644
--- a/nixos/modules/programs/dconf.nix
+++ b/nixos/modules/programs/dconf.nix
@@ -54,6 +54,8 @@ in
 
     services.dbus.packages = [ pkgs.dconf ];
 
+    systemd.packages = [ pkgs.dconf ];
+
     # For dconf executable
     environment.systemPackages = [ pkgs.dconf ];
 
diff --git a/nixos/modules/programs/droidcam.nix b/nixos/modules/programs/droidcam.nix
new file mode 100644
index 00000000000..9843a1f5be2
--- /dev/null
+++ b/nixos/modules/programs/droidcam.nix
@@ -0,0 +1,16 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+{
+  options.programs.droidcam = {
+    enable = mkEnableOption "DroidCam client";
+  };
+
+  config = lib.mkIf config.programs.droidcam.enable {
+    environment.systemPackages = [ pkgs.droidcam ];
+
+    boot.extraModulePackages = [ config.boot.kernelPackages.v4l2loopback ];
+    boot.kernelModules = [ "v4l2loopback" "snd-aloop" ];
+  };
+}
diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix
index 38bdabb4fa8..8877356360a 100644
--- a/nixos/modules/programs/environment.nix
+++ b/nixos/modules/programs/environment.nix
@@ -33,7 +33,6 @@ in
       { PATH = [ "/bin" ];
         INFOPATH = [ "/info" "/share/info" ];
         KDEDIRS = [ "" ];
-        STRIGI_PLUGIN_PATH = [ "/lib/strigi/" ];
         QT_PLUGIN_PATH = [ "/lib/qt4/plugins" "/lib/kde4/plugins" ];
         QTWEBKIT_PLUGIN_PATH = [ "/lib/mozilla/plugins/" ];
         GTK_PATH = [ "/lib/gtk-2.0" "/lib/gtk-3.0" ];
diff --git a/nixos/modules/programs/feedbackd.nix b/nixos/modules/programs/feedbackd.nix
new file mode 100644
index 00000000000..bb14489a6f4
--- /dev/null
+++ b/nixos/modules/programs/feedbackd.nix
@@ -0,0 +1,32 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.feedbackd;
+in {
+  options = {
+    programs.feedbackd = {
+      enable = mkEnableOption ''
+        Whether to enable the feedbackd D-BUS service and udev rules.
+
+        Your user needs to be in the `feedbackd` group to trigger effects.
+      '';
+      package = mkOption {
+        description = ''
+          Which feedbackd package to use.
+        '';
+        type = types.package;
+        default = pkgs.feedbackd;
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    services.dbus.packages = [ cfg.package ];
+    services.udev.packages = [ cfg.package ];
+
+    users.groups.feedbackd = {};
+  };
+}
diff --git a/nixos/modules/programs/file-roller.nix b/nixos/modules/programs/file-roller.nix
index 64f6a94e764..b939d59909c 100644
--- a/nixos/modules/programs/file-roller.nix
+++ b/nixos/modules/programs/file-roller.nix
@@ -30,9 +30,9 @@ with lib;
 
   config = mkIf config.programs.file-roller.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.file-roller ];
+    environment.systemPackages = [ pkgs.gnome.file-roller ];
 
-    services.dbus.packages = [ pkgs.gnome3.file-roller ];
+    services.dbus.packages = [ pkgs.gnome.file-roller ];
 
   };
 
diff --git a/nixos/modules/programs/firejail.nix b/nixos/modules/programs/firejail.nix
index 484f9eb4440..ad4ef1a3945 100644
--- a/nixos/modules/programs/firejail.nix
+++ b/nixos/modules/programs/firejail.nix
@@ -11,10 +11,20 @@ let
     }
     ''
       mkdir -p $out/bin
-      ${lib.concatStringsSep "\n" (lib.mapAttrsToList (command: binary: ''
+      ${lib.concatStringsSep "\n" (lib.mapAttrsToList (command: value:
+      let
+        opts = if builtins.isAttrs value
+        then value
+        else { executable = value; profile = null; extraArgs = []; };
+        args = lib.escapeShellArgs (
+          (optional (opts.profile != null) "--profile=${toString opts.profile}")
+          ++ opts.extraArgs
+          );
+      in
+      ''
         cat <<_EOF >$out/bin/${command}
         #! ${pkgs.runtimeShell} -e
-        exec /run/wrappers/bin/firejail ${binary} "\$@"
+        exec /run/wrappers/bin/firejail ${args} -- ${toString opts.executable} "\$@"
         _EOF
         chmod 0755 $out/bin/${command}
       '') cfg.wrappedBinaries)}
@@ -25,12 +35,38 @@ in {
     enable = mkEnableOption "firejail";
 
     wrappedBinaries = mkOption {
-      type = types.attrsOf types.path;
+      type = types.attrsOf (types.either types.path (types.submodule {
+        options = {
+          executable = mkOption {
+            type = types.path;
+            description = "Executable to run sandboxed";
+            example = literalExample "''${lib.getBin pkgs.firefox}/bin/firefox";
+          };
+          profile = mkOption {
+            type = types.nullOr types.path;
+            default = null;
+            description = "Profile to use";
+            example = literalExample "''${pkgs.firejail}/etc/firejail/firefox.profile";
+          };
+          extraArgs = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            description = "Extra arguments to pass to firejail";
+            example = [ "--private=~/.firejail_home" ];
+          };
+        };
+      }));
       default = {};
       example = literalExample ''
         {
-          firefox = "''${lib.getBin pkgs.firefox}/bin/firefox";
-          mpv = "''${lib.getBin pkgs.mpv}/bin/mpv";
+          firefox = {
+            executable = "''${lib.getBin pkgs.firefox}/bin/firefox";
+            profile = "''${pkgs.firejail}/etc/firejail/firefox.profile";
+          };
+          mpv = {
+            executable = "''${lib.getBin pkgs.mpv}/bin/mpv";
+            profile = "''${pkgs.firejail}/etc/firejail/mpv.profile";
+          };
         }
       '';
       description = ''
diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix
index 39b92edf2ac..8dd7101947f 100644
--- a/nixos/modules/programs/fish.nix
+++ b/nixos/modules/programs/fish.nix
@@ -8,11 +8,37 @@ let
 
   cfg = config.programs.fish;
 
+  fishAbbrs = concatStringsSep "\n" (
+    mapAttrsFlatten (k: v: "abbr -ag ${k} ${escapeShellArg v}")
+      cfg.shellAbbrs
+  );
+
   fishAliases = concatStringsSep "\n" (
     mapAttrsFlatten (k: v: "alias ${k} ${escapeShellArg v}")
       (filterAttrs (k: v: v != null) cfg.shellAliases)
   );
 
+  envShellInit = pkgs.writeText "shellInit" cfge.shellInit;
+
+  envLoginShellInit = pkgs.writeText "loginShellInit" cfge.loginShellInit;
+
+  envInteractiveShellInit = pkgs.writeText "interactiveShellInit" cfge.interactiveShellInit;
+
+  sourceEnv = file:
+  if cfg.useBabelfish then
+    "source /etc/fish/${file}.fish"
+  else
+    ''
+      set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $fish_function_path
+      fenv source /etc/fish/foreign-env/${file} > /dev/null
+      set -e fish_function_path[1]
+    '';
+
+  babelfishTranslate = path: name:
+    pkgs.runCommand "${name}.fish" {
+      nativeBuildInputs = [ pkgs.babelfish ];
+    } "${pkgs.babelfish}/bin/babelfish < ${path} > $out;";
+
 in
 
 {
@@ -29,6 +55,15 @@ in
         type = types.bool;
       };
 
+      useBabelfish = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, the configured environment will be translated to native fish using <link xlink:href="https://github.com/bouk/babelfish">babelfish</link>.
+          Otherwise, <link xlink:href="https://github.com/oh-my-fish/plugin-foreign-env">foreign-env</link> will be used.
+        '';
+      };
+
       vendor.config.enable = mkOption {
         type = types.bool;
         default = true;
@@ -53,6 +88,18 @@ in
         '';
       };
 
+      shellAbbrs = mkOption {
+        default = {};
+        example = {
+          gco = "git checkout";
+          npu = "nix-prefetch-url";
+        };
+        description = ''
+          Set of fish abbreviations.
+        '';
+        type = with types; attrsOf str;
+      };
+
       shellAliases = mkOption {
         default = {};
         description = ''
@@ -103,74 +150,155 @@ in
     programs.fish.shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases;
 
     # Required for man completions
-    documentation.man.generateCaches = true;
-
-    environment.etc."fish/foreign-env/shellInit".text = cfge.shellInit;
-    environment.etc."fish/foreign-env/loginShellInit".text = cfge.loginShellInit;
-    environment.etc."fish/foreign-env/interactiveShellInit".text = cfge.interactiveShellInit;
-
-    environment.etc."fish/nixos-env-preinit.fish".text = ''
-      # This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
-      # unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish
-      set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $__fish_datadir/functions
-
-      # source the NixOS environment config
-      if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
-          fenv source ${config.system.build.setEnvironment}
-      end
-
-      # clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish
-      set -e fish_function_path
-    '';
-
-    environment.etc."fish/config.fish".text = ''
-      # /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically.
-
-      # if we haven't sourced the general config, do it
-      if not set -q __fish_nixos_general_config_sourced
-        set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path
-        fenv source /etc/fish/foreign-env/shellInit > /dev/null
-        set -e fish_function_path[1]
-
-        ${cfg.shellInit}
-
-        # and leave a note so we don't source this config section again from
-        # this very shell (children will source the general config anew)
-        set -g __fish_nixos_general_config_sourced 1
-      end
-
-      # if we haven't sourced the login config, do it
-      status --is-login; and not set -q __fish_nixos_login_config_sourced
-      and begin
-        set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path
-        fenv source /etc/fish/foreign-env/loginShellInit > /dev/null
-        set -e fish_function_path[1]
-
-        ${cfg.loginShellInit}
-
-        # and leave a note so we don't source this config section again from
-        # this very shell (children will source the general config anew)
-        set -g __fish_nixos_login_config_sourced 1
-      end
-
-      # if we haven't sourced the interactive config, do it
-      status --is-interactive; and not set -q __fish_nixos_interactive_config_sourced
-      and begin
-        ${fishAliases}
-
-        set fish_function_path ${pkgs.fish-foreign-env}/share/fish-foreign-env/functions $fish_function_path
-        fenv source /etc/fish/foreign-env/interactiveShellInit > /dev/null
-        set -e fish_function_path[1]
-
-        ${cfg.promptInit}
-        ${cfg.interactiveShellInit}
-
-        # and leave a note so we don't source this config section again from
-        # this very shell (children will source the general config anew,
-        # allowing configuration changes in, e.g, aliases, to propagate)
-        set -g __fish_nixos_interactive_config_sourced 1
-      end
-    '';
+    documentation.man.generateCaches = lib.mkDefault true;
+
+    environment = mkMerge [
+      (mkIf cfg.useBabelfish
+      {
+        etc."fish/setEnvironment.fish".source = babelfishTranslate config.system.build.setEnvironment "setEnvironment";
+        etc."fish/shellInit.fish".source = babelfishTranslate envShellInit "shellInit";
+        etc."fish/loginShellInit.fish".source = babelfishTranslate envLoginShellInit "loginShellInit";
+        etc."fish/interactiveShellInit.fish".source = babelfishTranslate envInteractiveShellInit "interactiveShellInit";
+     })
+
+      (mkIf (!cfg.useBabelfish)
+      {
+        etc."fish/foreign-env/shellInit".source = envShellInit;
+        etc."fish/foreign-env/loginShellInit".source = envLoginShellInit;
+        etc."fish/foreign-env/interactiveShellInit".source = envInteractiveShellInit;
+      })
+
+      {
+        etc."fish/nixos-env-preinit.fish".text =
+        if cfg.useBabelfish
+        then ''
+          # source the NixOS environment config
+          if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
+            source /etc/fish/setEnvironment.fish
+          end
+        ''
+        else ''
+          # This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
+          # unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish
+          set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $__fish_datadir/functions
+
+          # source the NixOS environment config
+          if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
+            fenv source ${config.system.build.setEnvironment}
+          end
+
+          # clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish
+          set -e fish_function_path
+        '';
+      }
+
+      {
+        etc."fish/config.fish".text = ''
+        # /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically.
+
+        # if we haven't sourced the general config, do it
+        if not set -q __fish_nixos_general_config_sourced
+          ${sourceEnv "shellInit"}
+
+          ${cfg.shellInit}
+
+          # and leave a note so we don't source this config section again from
+          # this very shell (children will source the general config anew)
+          set -g __fish_nixos_general_config_sourced 1
+        end
+
+        # if we haven't sourced the login config, do it
+        status --is-login; and not set -q __fish_nixos_login_config_sourced
+        and begin
+          ${sourceEnv "loginShellInit"}
+
+          ${cfg.loginShellInit}
+
+          # and leave a note so we don't source this config section again from
+          # this very shell (children will source the general config anew)
+          set -g __fish_nixos_login_config_sourced 1
+        end
+
+        # if we haven't sourced the interactive config, do it
+        status --is-interactive; and not set -q __fish_nixos_interactive_config_sourced
+        and begin
+          ${fishAbbrs}
+          ${fishAliases}
+
+          ${sourceEnv "interactiveShellInit"}
+
+          ${cfg.promptInit}
+          ${cfg.interactiveShellInit}
+
+          # and leave a note so we don't source this config section again from
+          # this very shell (children will source the general config anew,
+          # allowing configuration changes in, e.g, aliases, to propagate)
+          set -g __fish_nixos_interactive_config_sourced 1
+        end
+      '';
+      }
+
+      {
+        etc."fish/generated_completions".source =
+        let
+          patchedGenerator = pkgs.stdenv.mkDerivation {
+            name = "fish_patched-completion-generator";
+            srcs = [
+              "${pkgs.fish}/share/fish/tools/create_manpage_completions.py"
+              "${pkgs.fish}/share/fish/tools/deroff.py"
+            ];
+            unpackCmd = "cp $curSrc $(basename $curSrc)";
+            sourceRoot = ".";
+            patches = [ ./fish_completion-generator.patch ]; # to prevent collisions of identical completion files
+            dontBuild = true;
+            installPhase = ''
+              mkdir -p $out
+              cp * $out/
+            '';
+            preferLocalBuild = true;
+            allowSubstitutes = false;
+          };
+          generateCompletions = package: pkgs.runCommand
+            "${package.name}_fish-completions"
+            (
+              {
+                inherit package;
+                preferLocalBuild = true;
+                allowSubstitutes = false;
+              }
+              // optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; }
+            )
+            ''
+              mkdir -p $out
+              if [ -d $package/share/man ]; then
+                find $package/share/man -type f | xargs ${pkgs.python3.interpreter} ${patchedGenerator}/create_manpage_completions.py --directory $out >/dev/null
+              fi
+            '';
+        in
+          pkgs.buildEnv {
+            name = "system_fish-completions";
+            ignoreCollisions = true;
+            paths = map generateCompletions config.environment.systemPackages;
+          };
+      }
+
+      # include programs that bring their own completions
+      {
+        pathsToLink = []
+        ++ optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
+        ++ optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
+        ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
+      }
+
+      { systemPackages = [ pkgs.fish ]; }
+
+      {
+        shells = [
+          "/run/current-system/sw/bin/fish"
+          "${pkgs.fish}/bin/fish"
+        ];
+      }
+    ];
 
     programs.fish.interactiveShellInit = ''
       # add completions generated by NixOS to $fish_complete_path
@@ -187,61 +315,6 @@ in
       end
     '';
 
-    environment.etc."fish/generated_completions".source =
-      let
-        patchedGenerator = pkgs.stdenv.mkDerivation {
-          name = "fish_patched-completion-generator";
-          srcs = [
-            "${pkgs.fish}/share/fish/tools/create_manpage_completions.py"
-            "${pkgs.fish}/share/fish/tools/deroff.py"
-          ];
-          unpackCmd = "cp $curSrc $(basename $curSrc)";
-          sourceRoot = ".";
-          patches = [ ./fish_completion-generator.patch ]; # to prevent collisions of identical completion files
-          dontBuild = true;
-          installPhase = ''
-            mkdir -p $out
-            cp * $out/
-          '';
-          preferLocalBuild = true;
-          allowSubstitutes = false;
-        };
-        generateCompletions = package: pkgs.runCommand
-          "${package.name}_fish-completions"
-          (
-            {
-              inherit package;
-              preferLocalBuild = true;
-              allowSubstitutes = false;
-            }
-            // optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; }
-          )
-          ''
-            mkdir -p $out
-            if [ -d $package/share/man ]; then
-              find $package/share/man -type f | xargs ${pkgs.python3.interpreter} ${patchedGenerator}/create_manpage_completions.py --directory $out >/dev/null
-            fi
-          '';
-      in
-        pkgs.buildEnv {
-          name = "system_fish-completions";
-          ignoreCollisions = true;
-          paths = map generateCompletions config.environment.systemPackages;
-        };
-
-    # include programs that bring their own completions
-    environment.pathsToLink = []
-      ++ optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
-      ++ optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
-      ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
-
-    environment.systemPackages = [ pkgs.fish ];
-
-    environment.shells = [
-      "/run/current-system/sw/bin/fish"
-      "${pkgs.fish}/bin/fish"
-    ];
-
   };
 
 }
diff --git a/nixos/modules/programs/fish_completion-generator.patch b/nixos/modules/programs/fish_completion-generator.patch
index 997f38c5066..fa207e484c9 100644
--- a/nixos/modules/programs/fish_completion-generator.patch
+++ b/nixos/modules/programs/fish_completion-generator.patch
@@ -1,13 +1,14 @@
 --- a/create_manpage_completions.py
 +++ b/create_manpage_completions.py
-@@ -844,10 +844,6 @@ def parse_manpage_at_path(manpage_path, output_directory):
+@@ -879,10 +879,6 @@ def parse_manpage_at_path(manpage_path, output_directory):
+                 )
+                 return False
  
-             built_command_output.insert(0, "# " + CMDNAME)
+-        # Output the magic word Autogenerated so we can tell if we can overwrite this
+-        built_command_output.insert(
+-            0, "# " + CMDNAME + "\n# Autogenerated from man page " + manpage_path
+-        )
+         # built_command_output.insert(2, "# using " + parser.__class__.__name__) # XXX MISATTRIBUTES THE CULPABLE PARSER! Was really using Type2 but reporting TypeDeroffManParser
  
--            # Output the magic word Autogenerated so we can tell if we can overwrite this
--            built_command_output.insert(
--                1, "# Autogenerated from man page " + manpage_path
--            )
-             # built_command_output.insert(2, "# using " + parser.__class__.__name__) # XXX MISATTRIBUTES THE CULPABILE PARSER! Was really using Type2 but reporting TypeDeroffManParser
- 
-             for line in built_command_output:
+         for line in built_command_output:
+
diff --git a/nixos/modules/programs/flashrom.nix b/nixos/modules/programs/flashrom.nix
new file mode 100644
index 00000000000..f026c2e31cd
--- /dev/null
+++ b/nixos/modules/programs/flashrom.nix
@@ -0,0 +1,26 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.flashrom;
+in
+{
+  options.programs.flashrom = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Installs flashrom and configures udev rules for programmers
+        used by flashrom. Grants access to users in the "flashrom"
+        group.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.flashrom ];
+    environment.systemPackages = [ pkgs.flashrom ];
+    users.groups.flashrom = { };
+  };
+}
diff --git a/nixos/modules/programs/flexoptix-app.nix b/nixos/modules/programs/flexoptix-app.nix
new file mode 100644
index 00000000000..93dcdfeb514
--- /dev/null
+++ b/nixos/modules/programs/flexoptix-app.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.flexoptix-app;
+in {
+  options = {
+    programs.flexoptix-app = {
+      enable = mkEnableOption "FLEXOPTIX app + udev rules";
+
+      package = mkOption {
+        description = "FLEXOPTIX app package to use";
+        type = types.package;
+        default = pkgs.flexoptix-app;
+        defaultText = "\${pkgs.flexoptix-app}";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    services.udev.packages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/programs/gamemode.nix b/nixos/modules/programs/gamemode.nix
new file mode 100644
index 00000000000..03949bf98df
--- /dev/null
+++ b/nixos/modules/programs/gamemode.nix
@@ -0,0 +1,96 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.gamemode;
+  settingsFormat = pkgs.formats.ini { };
+  configFile = settingsFormat.generate "gamemode.ini" cfg.settings;
+in
+{
+  options = {
+    programs.gamemode = {
+      enable = mkEnableOption "GameMode to optimise system performance on demand";
+
+      enableRenice = mkEnableOption "CAP_SYS_NICE on gamemoded to support lowering process niceness" // {
+        default = true;
+      };
+
+      settings = mkOption {
+        type = settingsFormat.type;
+        default = {};
+        description = ''
+          System-wide configuration for GameMode (/etc/gamemode.ini).
+          See gamemoded(8) man page for available settings.
+        '';
+        example = literalExample ''
+          {
+            general = {
+              renice = 10;
+            };
+
+            # Warning: GPU optimisations have the potential to damage hardware
+            gpu = {
+              apply_gpu_optimisations = "accept-responsibility";
+              gpu_device = 0;
+              amd_performance_level = "high";
+            };
+
+            custom = {
+              start = "''${pkgs.libnotify}/bin/notify-send 'GameMode started'";
+              end = "''${pkgs.libnotify}/bin/notify-send 'GameMode ended'";
+            };
+          }
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment = {
+      systemPackages = [ pkgs.gamemode ];
+      etc."gamemode.ini".source = configFile;
+    };
+
+    security = {
+      polkit.enable = true;
+      wrappers = mkIf cfg.enableRenice {
+        gamemoded = {
+          source = "${pkgs.gamemode}/bin/gamemoded";
+          capabilities = "cap_sys_nice+ep";
+        };
+      };
+    };
+
+    systemd = {
+      packages = [ pkgs.gamemode ];
+      user.services.gamemoded = {
+        # The upstream service already defines this, but doesn't get applied.
+        # See https://github.com/NixOS/nixpkgs/issues/81138
+        wantedBy = [ "default.target" ];
+
+        # Use pkexec from the security wrappers to allow users to
+        # run libexec/cpugovctl & libexec/gpuclockctl as root with
+        # the the actions defined in share/polkit-1/actions.
+        #
+        # This uses a link farm to make sure other wrapped executables
+        # aren't included in PATH.
+        environment.PATH = mkForce (pkgs.linkFarm "pkexec" [
+          {
+            name = "pkexec";
+            path = "${config.security.wrapperDir}/pkexec";
+          }
+        ]);
+
+        serviceConfig.ExecStart = mkIf cfg.enableRenice [
+          "" # Tell systemd to clear the existing ExecStart list, to prevent appending to it.
+          "${config.security.wrapperDir}/gamemoded"
+        ];
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with maintainers; [ kira-bruneau ];
+  };
+}
diff --git a/nixos/modules/programs/geary.nix b/nixos/modules/programs/geary.nix
index 5e441a75cb6..407680c30dc 100644
--- a/nixos/modules/programs/geary.nix
+++ b/nixos/modules/programs/geary.nix
@@ -15,10 +15,10 @@ in {
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.gnome3.geary ];
+    environment.systemPackages = [ pkgs.gnome.geary ];
     programs.dconf.enable = true;
-    services.gnome3.gnome-keyring.enable = true;
-    services.gnome3.gnome-online-accounts.enable = true;
+    services.gnome.gnome-keyring.enable = true;
+    services.gnome.gnome-online-accounts.enable = true;
   };
 }
 
diff --git a/nixos/modules/programs/gnome-disks.nix b/nixos/modules/programs/gnome-disks.nix
index 80dc2983ea5..4b128b47126 100644
--- a/nixos/modules/programs/gnome-disks.nix
+++ b/nixos/modules/programs/gnome-disks.nix
@@ -41,9 +41,9 @@ with lib;
 
   config = mkIf config.programs.gnome-disks.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.gnome-disk-utility ];
+    environment.systemPackages = [ pkgs.gnome.gnome-disk-utility ];
 
-    services.dbus.packages = [ pkgs.gnome3.gnome-disk-utility ];
+    services.dbus.packages = [ pkgs.gnome.gnome-disk-utility ];
 
   };
 
diff --git a/nixos/modules/programs/gnome-documents.nix b/nixos/modules/programs/gnome-documents.nix
index 9dd53483055..43ad3163efd 100644
--- a/nixos/modules/programs/gnome-documents.nix
+++ b/nixos/modules/programs/gnome-documents.nix
@@ -13,7 +13,7 @@ with lib;
   # Added 2019-08-09
   imports = [
     (mkRenamedOptionModule
-      [ "services" "gnome3" "gnome-documents" "enable" ]
+      [ "services" "gnome" "gnome-documents" "enable" ]
       [ "programs" "gnome-documents" "enable" ])
   ];
 
@@ -41,13 +41,13 @@ with lib;
 
   config = mkIf config.programs.gnome-documents.enable {
 
-    environment.systemPackages = [ pkgs.gnome3.gnome-documents ];
+    environment.systemPackages = [ pkgs.gnome.gnome-documents ];
 
-    services.dbus.packages = [ pkgs.gnome3.gnome-documents ];
+    services.dbus.packages = [ pkgs.gnome.gnome-documents ];
 
-    services.gnome3.gnome-online-accounts.enable = true;
+    services.gnome.gnome-online-accounts.enable = true;
 
-    services.gnome3.gnome-online-miners.enable = true;
+    services.gnome.gnome-online-miners.enable = true;
 
   };
 
diff --git a/nixos/modules/programs/gnome-terminal.nix b/nixos/modules/programs/gnome-terminal.nix
index f2617e5bc03..71a6b217880 100644
--- a/nixos/modules/programs/gnome-terminal.nix
+++ b/nixos/modules/programs/gnome-terminal.nix
@@ -28,9 +28,9 @@ in
   };
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.gnome3.gnome-terminal ];
-    services.dbus.packages = [ pkgs.gnome3.gnome-terminal ];
-    systemd.packages = [ pkgs.gnome3.gnome-terminal ];
+    environment.systemPackages = [ pkgs.gnome.gnome-terminal ];
+    services.dbus.packages = [ pkgs.gnome.gnome-terminal ];
+    systemd.packages = [ pkgs.gnome.gnome-terminal ];
 
     programs.bash.vteIntegration = true;
     programs.zsh.vteIntegration = true;
diff --git a/nixos/modules/programs/gpaste.nix b/nixos/modules/programs/gpaste.nix
index 4f6deb77e5e..cff2fb8d003 100644
--- a/nixos/modules/programs/gpaste.nix
+++ b/nixos/modules/programs/gpaste.nix
@@ -27,8 +27,10 @@ with lib;
 
   ###### implementation
   config = mkIf config.programs.gpaste.enable {
-    environment.systemPackages = [ pkgs.gnome3.gpaste ];
-    services.dbus.packages = [ pkgs.gnome3.gpaste ];
-    systemd.packages = [ pkgs.gnome3.gpaste ];
+    environment.systemPackages = [ pkgs.gnome.gpaste ];
+    services.dbus.packages = [ pkgs.gnome.gpaste ];
+    systemd.packages = [ pkgs.gnome.gpaste ];
+    # gnome-control-center crashes in Keyboard Shortcuts pane without the GSettings schemas.
+    services.xserver.desktopManager.gnome.sessionPath = [ pkgs.gnome.gpaste ];
   };
 }
diff --git a/nixos/modules/programs/hamster.nix b/nixos/modules/programs/hamster.nix
index b2f4a82b260..0bb56ad7ff3 100644
--- a/nixos/modules/programs/hamster.nix
+++ b/nixos/modules/programs/hamster.nix
@@ -6,7 +6,7 @@ with lib;
   meta.maintainers = pkgs.hamster.meta.maintainers;
 
   options.programs.hamster.enable =
-    mkEnableOption "Whether to enable hamster time tracking.";
+    mkEnableOption "hamster, a time tracking program";
 
   config = lib.mkIf config.programs.hamster.enable {
     environment.systemPackages = [ pkgs.hamster ];
diff --git a/nixos/modules/programs/kdeconnect.nix b/nixos/modules/programs/kdeconnect.nix
new file mode 100644
index 00000000000..673449b9f63
--- /dev/null
+++ b/nixos/modules/programs/kdeconnect.nix
@@ -0,0 +1,35 @@
+{ config, pkgs, lib, ... }:
+with lib;
+{
+  options.programs.kdeconnect = {
+    enable = mkEnableOption ''
+      kdeconnect.
+
+      Note that it will open the TCP and UDP port from
+      1714 to 1764 as they are needed for it to function properly.
+      You can use the <option>package</option> to use
+      <code>gnomeExtensions.gsconnect</code> as an alternative
+      implementation if you use Gnome.
+    '';
+    package = mkOption {
+      default = pkgs.kdeconnect;
+      defaultText = "pkgs.kdeconnect";
+      type = types.package;
+      example = literalExample "pkgs.gnomeExtensions.gsconnect";
+      description = ''
+        The package providing the implementation for kdeconnect.
+      '';
+    };
+  };
+  config =
+    let
+      cfg = config.programs.kdeconnect;
+    in
+      mkIf cfg.enable {
+        environment.systemPackages = [ cfg.package ];
+        networking.firewall = rec {
+          allowedTCPPortRanges = [ { from = 1714; to = 1764; } ];
+          allowedUDPPortRanges = allowedTCPPortRanges;
+        };
+      };
+}
diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix
index 75b3e707d57..09cb6030e66 100644
--- a/nixos/modules/programs/less.nix
+++ b/nixos/modules/programs/less.nix
@@ -40,7 +40,7 @@ in
       configFile = mkOption {
         type = types.nullOr types.path;
         default = null;
-        example = literalExample "$${pkgs.my-configs}/lesskey";
+        example = literalExample "\${pkgs.my-configs}/lesskey";
         description = ''
           Path to lesskey configuration file.
 
diff --git a/nixos/modules/programs/mininet.nix b/nixos/modules/programs/mininet.nix
index ecc924325e6..6e90e7669ac 100644
--- a/nixos/modules/programs/mininet.nix
+++ b/nixos/modules/programs/mininet.nix
@@ -8,7 +8,7 @@ let
   cfg  = config.programs.mininet;
 
   generatedPath = with pkgs; makeSearchPath "bin"  [
-    iperf ethtool iproute socat
+    iperf ethtool iproute2 socat
   ];
 
   pyEnv = pkgs.python.withPackages(ps: [ ps.mininet-python ]);
diff --git a/nixos/modules/programs/msmtp.nix b/nixos/modules/programs/msmtp.nix
new file mode 100644
index 00000000000..217060e6b3b
--- /dev/null
+++ b/nixos/modules/programs/msmtp.nix
@@ -0,0 +1,104 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.msmtp;
+
+in {
+  meta.maintainers = with maintainers; [ pacien ];
+
+  options = {
+    programs.msmtp = {
+      enable = mkEnableOption "msmtp - an SMTP client";
+
+      setSendmail = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to set the system sendmail to msmtp's.
+        '';
+      };
+
+      defaults = mkOption {
+        type = types.attrs;
+        default = {};
+        example = {
+          aliases = "/etc/aliases";
+          port = 587;
+          tls = true;
+        };
+        description = ''
+          Default values applied to all accounts.
+          See msmtp(1) for the available options.
+        '';
+      };
+
+      accounts = mkOption {
+        type = with types; attrsOf attrs;
+        default = {};
+        example = {
+          "default" = {
+            host = "smtp.example";
+            auth = true;
+            user = "someone";
+            passwordeval = "cat /secrets/password.txt";
+          };
+        };
+        description = ''
+          Named accounts and their respective configurations.
+          The special name "default" allows a default account to be defined.
+          See msmtp(1) for the available options.
+
+          Use `programs.msmtp.extraConfig` instead of this attribute set-based
+          option if ordered account inheritance is needed.
+
+          It is advised to use the `passwordeval` setting to read the password
+          from a secret file to avoid having it written in the world-readable
+          nix store. The password file must end with a newline (`\n`).
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra lines to add to the msmtp configuration verbatim.
+          See msmtp(1) for the syntax and available options.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.msmtp ];
+
+    services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail {
+      program = "sendmail";
+      source = "${pkgs.msmtp}/bin/sendmail";
+      setuid = false;
+      setgid = false;
+    };
+
+    environment.etc."msmtprc".text = let
+      mkValueString = v:
+        if v == true then "on"
+        else if v == false then "off"
+        else generators.mkValueStringDefault {} v;
+      mkKeyValueString = k: v: "${k} ${mkValueString v}";
+      mkInnerSectionString =
+        attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValueString attrs);
+      mkAccountString = name: attrs: ''
+        account ${name}
+        ${mkInnerSectionString attrs}
+      '';
+    in ''
+      defaults
+      ${mkInnerSectionString cfg.defaults}
+
+      ${concatStringsSep "\n" (mapAttrsToList mkAccountString cfg.accounts)}
+
+      ${cfg.extraConfig}
+    '';
+  };
+}
diff --git a/nixos/modules/programs/neovim.nix b/nixos/modules/programs/neovim.nix
new file mode 100644
index 00000000000..0a1a2ac2b75
--- /dev/null
+++ b/nixos/modules/programs/neovim.nix
@@ -0,0 +1,165 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.neovim;
+
+  runtime' = filter (f: f.enable) (attrValues cfg.runtime);
+
+  # taken from the etc module
+  runtime = pkgs.stdenvNoCC.mkDerivation {
+    name = "runtime";
+
+    builder = ../system/etc/make-etc.sh;
+
+    preferLocalBuild = true;
+    allowSubstitutes = false;
+
+    sources = map (x: x.source) runtime';
+    targets = map (x: x.target) runtime';
+  };
+
+in {
+  options.programs.neovim = {
+    enable = mkEnableOption "Neovim";
+
+    defaultEditor = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        When enabled, installs neovim and configures neovim to be the default editor
+        using the EDITOR environment variable.
+      '';
+    };
+
+    viAlias = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Symlink <command>vi</command> to <command>nvim</command> binary.
+      '';
+    };
+
+    vimAlias = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Symlink <command>vim</command> to <command>nvim</command> binary.
+      '';
+    };
+
+    withRuby = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Enable ruby provider.";
+    };
+
+    configure = mkOption {
+      type = types.attrs;
+      default = {};
+      example = literalExample ''
+        configure = {
+            customRC = $''''
+            " here your custom configuration goes!
+            $'''';
+            packages.myVimPackage = with pkgs.vimPlugins; {
+              # loaded on launch
+              start = [ fugitive ];
+              # manually loadable by calling `:packadd $plugin-name`
+              opt = [ ];
+            };
+          };
+      '';
+      description = ''
+        Generate your init file from your list of plugins and custom commands.
+        Neovim will then be wrapped to load <command>nvim -u /nix/store/<replaceable>hash</replaceable>-vimrc</command>
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.neovim-unwrapped;
+      defaultText = literalExample "pkgs.neovim-unwrapped";
+      description = "The package to use for the neovim binary.";
+    };
+
+    finalPackage = mkOption {
+      type = types.package;
+      visible = false;
+      readOnly = true;
+      description = "Resulting customized neovim package.";
+    };
+
+    runtime = mkOption {
+      default = {};
+      example = literalExample ''
+        runtime."ftplugin/c.vim".text = "setlocal omnifunc=v:lua.vim.lsp.omnifunc";
+      '';
+      description = ''
+        Set of files that have to be linked in <filename>runtime</filename>.
+      '';
+
+      type = with types; attrsOf (submodule (
+        { name, config, ... }:
+        { options = {
+
+            enable = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether this /etc file should be generated.  This
+                option allows specific /etc files to be disabled.
+              '';
+            };
+
+            target = mkOption {
+              type = types.str;
+              description = ''
+                Name of symlink.  Defaults to the attribute
+                name.
+              '';
+            };
+
+            text = mkOption {
+              default = null;
+              type = types.nullOr types.lines;
+              description = "Text of the file.";
+            };
+
+            source = mkOption {
+              type = types.path;
+              description = "Path of the source file.";
+            };
+
+          };
+
+          config = {
+            target = mkDefault name;
+            source = mkIf (config.text != null) (
+              let name' = "neovim-runtime" + baseNameOf name;
+              in mkDefault (pkgs.writeText name' config.text));
+          };
+
+        }));
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      cfg.finalPackage
+    ];
+    environment.variables = { EDITOR = mkOverride 900 "nvim"; };
+
+    programs.neovim.finalPackage = pkgs.wrapNeovim cfg.package {
+      inherit (cfg) viAlias vimAlias;
+      configure = cfg.configure // {
+
+        customRC = (cfg.configure.customRC or "") + ''
+          set runtimepath^=${runtime}/etc
+        '';
+      };
+    };
+  };
+}
diff --git a/nixos/modules/programs/nm-applet.nix b/nixos/modules/programs/nm-applet.nix
index 273a6dec59a..5bcee30125b 100644
--- a/nixos/modules/programs/nm-applet.nix
+++ b/nixos/modules/programs/nm-applet.nix
@@ -5,14 +5,25 @@
     maintainers = lib.teams.freedesktop.members;
   };
 
-  options.programs.nm-applet.enable = lib.mkEnableOption "nm-applet";
+  options.programs.nm-applet = {
+    enable = lib.mkEnableOption "nm-applet";
+
+    indicator = lib.mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = ''
+        Whether to use indicator instead of status icon.
+        It is needed for Appindicator environments, like Enlightenment.
+      '';
+    };
+  };
 
   config = lib.mkIf config.programs.nm-applet.enable {
     systemd.user.services.nm-applet = {
       description = "Network manager applet";
       wantedBy = [ "graphical-session.target" ];
       partOf = [ "graphical-session.target" ];
-      serviceConfig.ExecStart = "${pkgs.networkmanagerapplet}/bin/nm-applet";
+      serviceConfig.ExecStart = "${pkgs.networkmanagerapplet}/bin/nm-applet ${lib.optionalString config.programs.nm-applet.indicator "--indicator"}";
     };
 
     services.dbus.packages = [ pkgs.gcr ];
diff --git a/nixos/modules/programs/noisetorch.nix b/nixos/modules/programs/noisetorch.nix
new file mode 100644
index 00000000000..5f3b0c8f5d1
--- /dev/null
+++ b/nixos/modules/programs/noisetorch.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let cfg = config.programs.noisetorch;
+in {
+  options.programs.noisetorch = {
+    enable = mkEnableOption "noisetorch + setcap wrapper";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.noisetorch;
+      description = ''
+        The noisetorch package to use.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.noisetorch = {
+      source = "${cfg.package}/bin/noisetorch";
+      capabilities = "cap_sys_resource=+ep";
+    };
+  };
+}
diff --git a/nixos/modules/programs/partition-manager.nix b/nixos/modules/programs/partition-manager.nix
new file mode 100644
index 00000000000..1be2f0a69a1
--- /dev/null
+++ b/nixos/modules/programs/partition-manager.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = [ maintainers.oxalica ];
+
+  ###### interface
+  options = {
+    programs.partition-manager.enable = mkEnableOption "KDE Partition Manager";
+  };
+
+  ###### implementation
+  config = mkIf config.programs.partition-manager.enable {
+    services.dbus.packages = [ pkgs.libsForQt5.kpmcore ];
+    # `kpmcore` need to be installed to pull in polkit actions.
+    environment.systemPackages = [ pkgs.libsForQt5.kpmcore pkgs.partition-manager ];
+  };
+}
diff --git a/nixos/modules/programs/phosh.nix b/nixos/modules/programs/phosh.nix
new file mode 100644
index 00000000000..cba3f73768e
--- /dev/null
+++ b/nixos/modules/programs/phosh.nix
@@ -0,0 +1,163 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.phosh;
+
+  # Based on https://source.puri.sm/Librem5/librem5-base/-/blob/4596c1056dd75ac7f043aede07887990fd46f572/default/sm.puri.OSK0.desktop
+  oskItem = pkgs.makeDesktopItem {
+    name = "sm.puri.OSK0";
+    type = "Application";
+    desktopName = "On-screen keyboard";
+    exec = "${pkgs.squeekboard}/bin/squeekboard";
+    categories = "GNOME;Core;";
+    extraEntries = ''
+      OnlyShowIn=GNOME;
+      NoDisplay=true
+      X-GNOME-Autostart-Phase=Panel
+      X-GNOME-Provides=inputmethod
+      X-GNOME-Autostart-Notify=true
+      X-GNOME-AutoRestart=true
+    '';
+  };
+
+  phocConfigType = types.submodule {
+    options = {
+      xwayland = mkOption {
+        description = ''
+          Whether to enable XWayland support.
+
+          To start XWayland immediately, use `immediate`.
+        '';
+        type = types.enum [ "true" "false" "immediate" ];
+        default = "false";
+      };
+      cursorTheme = mkOption {
+        description = ''
+          Cursor theme to use in Phosh.
+        '';
+        type = types.str;
+        default = "default";
+      };
+      outputs = mkOption {
+        description = ''
+          Output configurations.
+        '';
+        type = types.attrsOf phocOutputType;
+        default = {
+          DSI-1 = {
+            scale = 2;
+          };
+        };
+      };
+    };
+  };
+
+  phocOutputType = types.submodule {
+    options = {
+      modeline = mkOption {
+        description = ''
+          One or more modelines.
+        '';
+        type = types.either types.str (types.listOf types.str);
+        default = [];
+        example = [
+          "87.25 720 776 848  976 1440 1443 1453 1493 -hsync +vsync"
+          "65.13 768 816 896 1024 1024 1025 1028 1060 -HSync +VSync"
+        ];
+      };
+      mode = mkOption {
+        description = ''
+          Default video mode.
+        '';
+        type = types.nullOr types.str;
+        default = null;
+        example = "768x1024";
+      };
+      scale = mkOption {
+        description = ''
+          Display scaling factor.
+        '';
+        type = types.nullOr types.ints.unsigned;
+        default = null;
+        example = 2;
+      };
+      rotate = mkOption {
+        description = ''
+          Screen transformation.
+        '';
+        type = types.enum [
+          "90" "180" "270" "flipped" "flipped-90" "flipped-180" "flipped-270" null
+        ];
+        default = null;
+      };
+    };
+  };
+
+  optionalKV = k: v: if v == null then "" else "${k} = ${builtins.toString v}";
+
+  renderPhocOutput = name: output: let
+    modelines = if builtins.isList output.modeline
+      then output.modeline
+      else [ output.modeline ];
+    renderModeline = l: "modeline = ${l}";
+  in ''
+    [output:${name}]
+    ${concatStringsSep "\n" (map renderModeline modelines)}
+    ${optionalKV "mode" output.mode}
+    ${optionalKV "scale" output.scale}
+    ${optionalKV "rotate" output.rotate}
+  '';
+
+  renderPhocConfig = phoc: let
+    outputs = mapAttrsToList renderPhocOutput phoc.outputs;
+  in ''
+    [core]
+    xwayland = ${phoc.xwayland}
+    ${concatStringsSep "\n" outputs}
+    [cursor]
+    theme = ${phoc.cursorTheme}
+  '';
+in {
+  options = {
+    programs.phosh = {
+      enable = mkEnableOption ''
+        Whether to enable, Phosh, related packages and default configurations.
+      '';
+      phocConfig = mkOption {
+        description = ''
+          Configurations for the Phoc compositor.
+        '';
+        type = types.oneOf [ types.lines types.path phocConfigType ];
+        default = {};
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      pkgs.phoc
+      pkgs.phosh
+      pkgs.squeekboard
+      oskItem
+    ];
+
+    systemd.packages = [ pkgs.phosh ];
+
+    programs.feedbackd.enable = true;
+
+    security.pam.services.phosh = {};
+
+    hardware.opengl.enable = mkDefault true;
+
+    services.gnome.core-shell.enable = true;
+    services.gnome.core-os-services.enable = true;
+    services.xserver.displayManager.sessionPackages = [ pkgs.phosh ];
+
+    environment.etc."phosh/phoc.ini".source =
+      if builtins.isPath cfg.phocConfig then cfg.phocConfig
+      else if builtins.isString cfg.phocConfig then pkgs.writeText "phoc.ini" cfg.phocConfig
+      else pkgs.writeText "phoc.ini" (renderPhocConfig cfg.phocConfig);
+  };
+}
diff --git a/nixos/modules/programs/proxychains.nix b/nixos/modules/programs/proxychains.nix
new file mode 100644
index 00000000000..7743f79c1c0
--- /dev/null
+++ b/nixos/modules/programs/proxychains.nix
@@ -0,0 +1,165 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+
+  cfg = config.programs.proxychains;
+
+  configFile = ''
+    ${cfg.chain.type}_chain
+    ${optionalString (cfg.chain.type == "random")
+    "chain_len = ${builtins.toString cfg.chain.length}"}
+    ${optionalString cfg.proxyDNS "proxy_dns"}
+    ${optionalString cfg.quietMode "quiet_mode"}
+    remote_dns_subnet ${builtins.toString cfg.remoteDNSSubnet}
+    tcp_read_time_out ${builtins.toString cfg.tcpReadTimeOut}
+    tcp_connect_time_out ${builtins.toString cfg.tcpConnectTimeOut}
+    localnet ${cfg.localnet}
+    [ProxyList]
+    ${builtins.concatStringsSep "\n"
+      (lib.mapAttrsToList (k: v: "${v.type} ${v.host} ${builtins.toString v.port}")
+        (lib.filterAttrs (k: v: v.enable) cfg.proxies))}
+  '';
+
+  proxyOptions = {
+    options = {
+      enable = mkEnableOption "this proxy";
+
+      type = mkOption {
+        type = types.enum [ "http" "socks4" "socks5" ];
+        description = "Proxy type.";
+      };
+
+      host = mkOption {
+        type = types.str;
+        description = "Proxy host or IP address.";
+      };
+
+      port = mkOption {
+        type = types.port;
+        description = "Proxy port";
+      };
+    };
+  };
+
+in {
+
+  ###### interface
+
+  options = {
+
+    programs.proxychains = {
+
+      enable = mkEnableOption "installing proxychains configuration";
+
+      chain = {
+        type = mkOption {
+          type = types.enum [ "dynamic" "strict" "random" ];
+          default = "strict";
+          description = ''
+            <literal>dynamic</literal> - Each connection will be done via chained proxies
+            all proxies chained in the order as they appear in the list
+            at least one proxy must be online to play in chain
+            (dead proxies are skipped)
+            otherwise <literal>EINTR</literal> is returned to the app.
+
+            <literal>strict</literal> - Each connection will be done via chained proxies
+            all proxies chained in the order as they appear in the list
+            all proxies must be online to play in chain
+            otherwise <literal>EINTR</literal> is returned to the app.
+
+            <literal>random</literal> - Each connection will be done via random proxy
+            (or proxy chain, see <option>programs.proxychains.chain.length</option>) from the list.
+          '';
+        };
+        length = mkOption {
+          type = types.nullOr types.int;
+          default = null;
+          description = ''
+            Chain length for random chain.
+          '';
+        };
+      };
+
+      proxyDNS = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Proxy DNS requests - no leak for DNS data.";
+      };
+
+      quietMode = mkEnableOption "Quiet mode (no output from the library).";
+
+      remoteDNSSubnet = mkOption {
+        type = types.enum [ 10 127 224 ];
+        default = 224;
+        description = ''
+          Set the class A subnet number to use for the internal remote DNS mapping, uses the reserved 224.x.x.x range by default.
+        '';
+      };
+
+      tcpReadTimeOut = mkOption {
+        type = types.int;
+        default = 15000;
+        description = "Connection read time-out in milliseconds.";
+      };
+
+      tcpConnectTimeOut = mkOption {
+        type = types.int;
+        default = 8000;
+        description = "Connection time-out in milliseconds.";
+      };
+
+      localnet = mkOption {
+        type = types.str;
+        default = "127.0.0.0/255.0.0.0";
+        description = "By default enable localnet for loopback address ranges.";
+      };
+
+      proxies = mkOption {
+        type = types.attrsOf (types.submodule proxyOptions);
+        description = ''
+          Proxies to be used by proxychains.
+        '';
+
+        example = literalExample ''
+          { myproxy =
+            { type = "socks4";
+              host = "127.0.0.1";
+              port = 1337;
+            };
+          }
+        '';
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  meta.maintainers = with maintainers; [ sorki ];
+
+  config = mkIf cfg.enable {
+
+    assertions = singleton {
+      assertion = cfg.chain.type != "random" && cfg.chain.length == null;
+      message = ''
+        Option `programs.proxychains.chain.length`
+        only makes sense with `programs.proxychains.chain.type` = "random".
+      '';
+    };
+
+    programs.proxychains.proxies = mkIf config.services.tor.client.enable
+      {
+        torproxy = mkDefault {
+          enable = true;
+          type = "socks4";
+          host = "127.0.0.1";
+          port = 9050;
+        };
+      };
+
+    environment.etc."proxychains.conf".text = configFile;
+    environment.systemPackages = [ pkgs.proxychains ];
+  };
+
+}
diff --git a/nixos/modules/programs/qt5ct.nix b/nixos/modules/programs/qt5ct.nix
index aeb7fc50849..3f2bcf62283 100644
--- a/nixos/modules/programs/qt5ct.nix
+++ b/nixos/modules/programs/qt5ct.nix
@@ -26,6 +26,6 @@ with lib;
   ###### implementation
   config = mkIf config.programs.qt5ct.enable {
     environment.variables.QT_QPA_PLATFORMTHEME = "qt5ct";
-    environment.systemPackages = with pkgs; [ qt5ct libsForQt5.qtstyleplugins ];
+    environment.systemPackages = with pkgs; [ qt5ct ];
   };
 }
diff --git a/nixos/modules/programs/seahorse.nix b/nixos/modules/programs/seahorse.nix
index b229d2a2c0d..c0a356bff57 100644
--- a/nixos/modules/programs/seahorse.nix
+++ b/nixos/modules/programs/seahorse.nix
@@ -31,14 +31,14 @@ with lib;
 
   config = mkIf config.programs.seahorse.enable {
 
-    programs.ssh.askPassword = mkDefault "${pkgs.gnome3.seahorse}/libexec/seahorse/ssh-askpass";
+    programs.ssh.askPassword = mkDefault "${pkgs.gnome.seahorse}/libexec/seahorse/ssh-askpass";
 
     environment.systemPackages = [
-      pkgs.gnome3.seahorse
+      pkgs.gnome.seahorse
     ];
 
     services.dbus.packages = [
-      pkgs.gnome3.seahorse
+      pkgs.gnome.seahorse
     ];
 
   };
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index a983ffa4b89..d4a7769bbd6 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -36,7 +36,7 @@ in
       askPassword = mkOption {
         type = types.str;
         default = "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass";
-        description = ''Program used by SSH to ask for passwords.'';
+        description = "Program used by SSH to ask for passwords.";
       };
 
       forwardX11 = mkOption {
@@ -131,7 +131,7 @@ in
 
       knownHosts = mkOption {
         default = {};
-        type = types.loaOf (types.submodule ({ name, ... }: {
+        type = types.attrsOf (types.submodule ({ name, ... }: {
           options = {
             certAuthority = mkOption {
               type = types.bool;
diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix
index 15d2750c193..8b500f0383f 100644
--- a/nixos/modules/programs/ssmtp.nix
+++ b/nixos/modules/programs/ssmtp.nix
@@ -1,6 +1,6 @@
 # Configuration for `ssmtp', a trivial mail transfer agent that can
 # replace sendmail/postfix on simple systems.  It delivers email
-# directly to an SMTP server defined in its configuration file, wihout
+# directly to an SMTP server defined in its configuration file, without
 # queueing mail locally.
 
 { config, lib, pkgs, ... }:
@@ -124,7 +124,8 @@ in
         example = "/run/keys/ssmtp-authpass";
         description = ''
           Path to a file that contains the password used for SMTP auth. The file
-          should not contain a trailing newline, if the password does not contain one.
+          should not contain a trailing newline, if the password does not contain one
+          (e.g. use <command>echo -n "password" > file</command>).
           This file should be readable by the users that need to execute ssmtp.
         '';
       };
@@ -142,6 +143,13 @@ in
 
   config = mkIf cfg.enable {
 
+    assertions = [
+      {
+        assertion = cfg.useSTARTTLS -> cfg.useTLS;
+        message = "services.ssmtp.useSTARTTLS has no effect without services.ssmtp.useTLS";
+      }
+    ];
+
     services.ssmtp.settings = mkMerge [
       ({
         MailHub = cfg.hostName;
@@ -155,15 +163,16 @@ in
       (mkIf (cfg.authPassFile != null) { AuthPassFile = cfg.authPassFile; })
     ];
 
-    environment.etc."ssmtp/ssmtp.conf".source =
-      let
-        toStr = value:
+    # careful here: ssmtp REQUIRES all config lines to end with a newline char!
+    environment.etc."ssmtp/ssmtp.conf".text = with generators; toKeyValue {
+      mkKeyValue = mkKeyValueDefault {
+        mkValueString = value:
           if value == true then "YES"
           else if value == false then "NO"
-          else builtins.toString value
+          else mkValueStringDefault {} value
         ;
-      in
-        pkgs.writeText "ssmtp.conf" (concatStringsSep "\n" (mapAttrsToList (key: value: "${key}=${toStr value}") cfg.settings));
+      } "=";
+    } cfg.settings;
 
     environment.systemPackages = [pkgs.ssmtp];
 
diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix
index 3c919c47a0c..ff4deba2bf0 100644
--- a/nixos/modules/programs/steam.nix
+++ b/nixos/modules/programs/steam.nix
@@ -4,12 +4,38 @@ with lib;
 
 let
   cfg = config.programs.steam;
+
+  steam = pkgs.steam.override {
+    extraLibraries = pkgs: with config.hardware.opengl;
+      if pkgs.hostPlatform.is64bit
+      then [ package ] ++ extraPackages
+      else [ package32 ] ++ extraPackages32;
+  };
 in {
-  options.programs.steam.enable = mkEnableOption "steam";
+  options.programs.steam = {
+    enable = mkEnableOption "steam";
+
+    remotePlay.openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open ports in the firewall for Steam Remote Play.
+      '';
+    };
+
+    dedicatedServer.openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open ports in the firewall for Source Dedicated Server.
+      '';
+    };
+  };
 
   config = mkIf cfg.enable {
     hardware.opengl = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
       enable = true;
+      driSupport = true;
       driSupport32Bit = true;
     };
 
@@ -18,7 +44,19 @@ in {
 
     hardware.steam-hardware.enable = true;
 
-    environment.systemPackages = [ pkgs.steam ];
+    environment.systemPackages = [ steam steam.run ];
+
+    networking.firewall = lib.mkMerge [
+      (mkIf cfg.remotePlay.openFirewall {
+        allowedTCPPorts = [ 27036 ];
+        allowedUDPPortRanges = [ { from = 27031; to = 27036; } ];
+      })
+
+      (mkIf cfg.dedicatedServer.openFirewall {
+        allowedTCPPorts = [ 27015 ]; # SRCDS Rcon port
+        allowedUDPPorts = [ 27015 ]; # Gameplay traffic
+      })
+    ];
   };
 
   meta.maintainers = with maintainers; [ mkg20001 ];
diff --git a/nixos/modules/programs/sway.nix b/nixos/modules/programs/sway.nix
index 364debddb0f..d5819a08e8f 100644
--- a/nixos/modules/programs/sway.nix
+++ b/nixos/modules/programs/sway.nix
@@ -31,6 +31,7 @@ let
     extraOptions = cfg.extraOptions;
     withBaseWrapper = cfg.wrapperFeatures.base;
     withGtkWrapper = cfg.wrapperFeatures.gtk;
+    isNixOS = true;
   };
 in {
   options.programs.sway = {
@@ -38,9 +39,8 @@ in {
       Sway, the i3-compatible tiling Wayland compositor. You can manually launch
       Sway by executing "exec sway" on a TTY. Copy /etc/sway/config to
       ~/.config/sway/config to modify the default configuration. See
-      https://github.com/swaywm/sway/wiki and "man 5 sway" for more information.
-      Please have a look at the "extraSessionCommands" example for running
-      programs natively under Wayland'';
+      <link xlink:href="https://github.com/swaywm/sway/wiki" /> and
+      "man 5 sway" for more information'';
 
     wrapperFeatures = mkOption {
       type = wrapperOptions;
@@ -55,16 +55,20 @@ in {
       type = types.lines;
       default = "";
       example = ''
+        # SDL:
         export SDL_VIDEODRIVER=wayland
-        # needs qt5.qtwayland in systemPackages
-        export QT_QPA_PLATFORM=wayland
+        # QT (needs qt5.qtwayland in systemPackages):
+        export QT_QPA_PLATFORM=wayland-egl
         export QT_WAYLAND_DISABLE_WINDOWDECORATION="1"
         # Fix for some Java AWT applications (e.g. Android Studio),
         # use this if they aren't displayed properly:
         export _JAVA_AWT_WM_NONREPARENTING=1
       '';
       description = ''
-        Shell commands executed just before Sway is started.
+        Shell commands executed just before Sway is started. See
+        <link xlink:href="https://github.com/swaywm/sway/wiki/Running-programs-natively-under-wayland" />
+        and <link xlink:href="https://github.com/swaywm/wlroots/blob/master/docs/env_vars.md" />
+        for some useful environment variables.
       '';
     };
 
@@ -86,24 +90,25 @@ in {
     extraPackages = mkOption {
       type = with types; listOf package;
       default = with pkgs; [
-        swaylock swayidle
-        xwayland alacritty dmenu
-        rxvt-unicode # For backward compatibility (old default terminal)
+        swaylock swayidle alacritty dmenu
       ];
       defaultText = literalExample ''
-        with pkgs; [ swaylock swayidle xwayland rxvt-unicode dmenu ];
+        with pkgs; [ swaylock swayidle alacritty dmenu ];
       '';
       example = literalExample ''
         with pkgs; [
-          xwayland
           i3status i3status-rust
           termite rofi light
         ]
       '';
       description = ''
-        Extra packages to be installed system wide.
+        Extra packages to be installed system wide. See
+        <link xlink:href="https://github.com/swaywm/sway/wiki/Useful-add-ons-for-sway" /> and
+        <link xlink:href="https://github.com/swaywm/sway/wiki/i3-Migration-Guide#common-x11-apps-used-on-i3-with-wayland-alternatives" />
+        for a list of useful software.
       '';
     };
+
   };
 
   config = mkIf cfg.enable {
@@ -120,8 +125,11 @@ in {
       systemPackages = [ swayPackage ] ++ cfg.extraPackages;
       etc = {
         "sway/config".source = mkOptionDefault "${swayPackage}/etc/sway/config";
-        #"sway/security.d".source = mkOptionDefault "${swayPackage}/etc/sway/security.d/";
-        #"sway/config.d".source = mkOptionDefault "${swayPackage}/etc/sway/config.d/";
+        "sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
+          # Import the most important environment variables into the D-Bus and systemd
+          # user environments (e.g. required for screen sharing and Pinentry prompts):
+          exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
+        '';
       };
     };
     security.pam.services.swaylock = {};
@@ -130,7 +138,10 @@ in {
     programs.dconf.enable = mkDefault true;
     # To make a Sway session available if a display manager like SDDM is enabled:
     services.xserver.displayManager.sessionPackages = [ swayPackage ];
+    programs.xwayland.enable = mkDefault true;
+    # For screen sharing (this option only has an effect with xdg.portal.enable):
+    xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-wlr ];
   };
 
-  meta.maintainers = with lib.maintainers; [ gnidorah primeos colemickens ];
+  meta.maintainers = with lib.maintainers; [ primeos colemickens ];
 }
diff --git a/nixos/modules/programs/tilp2.nix b/nixos/modules/programs/tilp2.nix
new file mode 100644
index 00000000000..da9e32e3e6c
--- /dev/null
+++ b/nixos/modules/programs/tilp2.nix
@@ -0,0 +1,28 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.tilp2;
+
+in {
+  options.programs.tilp2 = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable tilp2 and udev rules for supported calculators.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [
+      pkgs.libticables2
+    ];
+
+    environment.systemPackages = [
+      pkgs.tilp2
+    ];
+  };
+}
diff --git a/nixos/modules/programs/tsm-client.nix b/nixos/modules/programs/tsm-client.nix
index eb6f1247528..7ac4086d5f0 100644
--- a/nixos/modules/programs/tsm-client.nix
+++ b/nixos/modules/programs/tsm-client.nix
@@ -7,7 +7,7 @@ let
   inherit (lib.modules) mkDefault mkIf;
   inherit (lib.options) literalExample mkEnableOption mkOption;
   inherit (lib.strings) concatStringsSep optionalString toLower;
-  inherit (lib.types) addCheck attrsOf lines loaOf nullOr package path port str strMatching submodule;
+  inherit (lib.types) addCheck attrsOf lines nullOr package path port str strMatching submodule;
 
   # Checks if given list of strings contains unique
   # elements when compared without considering case.
@@ -178,7 +178,7 @@ let
       client system-options file "dsm.sys"
     '';
     servers = mkOption {
-      type = loaOf (submodule [ serverOptions ]);
+      type = attrsOf (submodule [ serverOptions ]);
       default = {};
       example.mainTsmServer = {
         server = "tsmserver.company.com";
diff --git a/nixos/modules/programs/turbovnc.nix b/nixos/modules/programs/turbovnc.nix
new file mode 100644
index 00000000000..e6f8836aa36
--- /dev/null
+++ b/nixos/modules/programs/turbovnc.nix
@@ -0,0 +1,54 @@
+# Global configuration for the SSH client.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.turbovnc;
+in
+{
+  options = {
+
+    programs.turbovnc = {
+
+      ensureHeadlessSoftwareOpenGL = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to set up NixOS such that TurboVNC's built-in software OpenGL
+          implementation works.
+
+          This will enable <option>hardware.opengl.enable</option> so that OpenGL
+          programs can find Mesa's llvmpipe drivers.
+
+          Setting this option to <code>false</code> does not mean that software
+          OpenGL won't work; it may still work depending on your system
+          configuration.
+
+          This option is also intended to generate warnings if you are using some
+          configuration that's incompatible with using headless software OpenGL
+          in TurboVNC.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.ensureHeadlessSoftwareOpenGL {
+
+    # TurboVNC has builtin support for Mesa llvmpipe's `swrast`
+    # software rendering to implemnt GLX (OpenGL on Xorg).
+    # However, just building TurboVNC with support for that is not enough
+    # (it only takes care of the X server side part of OpenGL);
+    # the indiviudual applications (e.g. `glxgears`) also need to directly load
+    # the OpenGL libs.
+    # Thus, this creates `/run/opengl-driver` populated by Mesa so that the applications
+    # can find the llvmpipe `swrast.so` software rendering DRI lib via `libglvnd`.
+    # This comment exists to explain why `hardware.` is involved,
+    # even though 100% software rendering is used.
+    hardware.opengl.enable = true;
+
+  };
+}
diff --git a/nixos/modules/programs/udevil.nix b/nixos/modules/programs/udevil.nix
index ba5670f9dfe..25975d88ec8 100644
--- a/nixos/modules/programs/udevil.nix
+++ b/nixos/modules/programs/udevil.nix
@@ -10,5 +10,8 @@ in {
 
   config = mkIf cfg.enable {
     security.wrappers.udevil.source = "${lib.getBin pkgs.udevil}/bin/udevil";
+
+    systemd.packages = [ pkgs.udevil ];
+    systemd.services."devmon@".wantedBy = [ "multi-user.target" ];
   };
 }
diff --git a/nixos/modules/programs/venus.nix b/nixos/modules/programs/venus.nix
deleted file mode 100644
index 58faf38777d..00000000000
--- a/nixos/modules/programs/venus.nix
+++ /dev/null
@@ -1,173 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.venus;
-
-  configFile = pkgs.writeText "venus.ini"
-    ''
-      [Planet]
-      name = ${cfg.name}
-      link = ${cfg.link}
-      owner_name = ${cfg.ownerName}
-      owner_email = ${cfg.ownerEmail}
-      output_theme = ${cfg.cacheDirectory}/theme
-      output_dir = ${cfg.outputDirectory}
-      cache_directory = ${cfg.cacheDirectory}
-      items_per_page = ${toString cfg.itemsPerPage}
-      ${(concatStringsSep "\n\n"
-            (map ({ name, feedUrl, homepageUrl }:
-            ''
-              [${feedUrl}]
-              name = ${name}
-              link = ${homepageUrl}
-            '') cfg.feeds))}
-    '';
-
-in
-{
-
-  options = {
-    services.venus = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Planet Venus is an awesome ‘river of news’ feed reader. It downloads
-          news feeds published by web sites and aggregates their content
-          together into a single combined feed, latest news first.
-        '';
-      };
-
-      dates = mkOption {
-        default = "*:0/15";
-        type = types.str;
-        description = ''
-          Specification (in the format described by
-          <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>7</manvolnum></citerefentry>) of the time at
-          which the Venus will collect feeds.
-        '';
-      };
-
-      user = mkOption {
-        default = "root";
-        type = types.str;
-        description = ''
-          User for running venus script.
-        '';
-      };
-
-      group = mkOption {
-        default = "root";
-        type = types.str;
-        description = ''
-          Group for running venus script.
-        '';
-      };
-
-      name = mkOption {
-        default = "NixOS Planet";
-        type = types.str;
-        description = ''
-          Your planet's name.
-        '';
-      };
-
-      link = mkOption {
-        default = "https://planet.nixos.org";
-        type = types.str;
-        description = ''
-          Link to the main page.
-        '';
-      };
-
-      ownerName = mkOption {
-        default = "Rok Garbas";
-        type = types.str;
-        description = ''
-          Your name.
-        '';
-      };
-
-      ownerEmail = mkOption {
-        default = "some@example.com";
-        type = types.str;
-        description = ''
-          Your e-mail address.
-        '';
-      };
-
-      outputTheme = mkOption {
-        default = "${pkgs.venus}/themes/classic_fancy";
-        type = types.path;
-        description = ''
-          Directory containing a config.ini file which is merged with this one.
-          This is typically used to specify templating and bill of material
-          information.
-        '';
-      };
-
-      outputDirectory = mkOption {
-        type = types.path;
-        description = ''
-          Directory to place output files.
-        '';
-      };
-
-      cacheDirectory = mkOption {
-        default = "/var/cache/venus";
-        type = types.path;
-        description = ''
-            Where cached feeds are stored.
-        '';
-      };
-
-      itemsPerPage = mkOption {
-        default = 15;
-        type = types.int;
-        description = ''
-          How many items to put on each page.
-        '';
-      };
-
-      feeds = mkOption {
-        default = [];
-        example = [
-          {
-            name = "Rok Garbas";
-            feedUrl= "http://url/to/rss/feed.xml";
-            homepageUrl = "http://garbas.si";
-          }
-        ];
-        description = ''
-          List of feeds.
-        '';
-      };
-
-    };
-  };
-
-  config = mkIf cfg.enable {
-
-    system.activationScripts.venus =
-      ''
-        mkdir -p ${cfg.outputDirectory}
-        chown ${cfg.user}:${cfg.group} ${cfg.outputDirectory} -R
-        rm -rf ${cfg.cacheDirectory}/theme
-        mkdir -p ${cfg.cacheDirectory}/theme
-        cp -R ${cfg.outputTheme}/* ${cfg.cacheDirectory}/theme
-        chown ${cfg.user}:${cfg.group} ${cfg.cacheDirectory} -R
-      '';
-
-    systemd.services.venus =
-      { description = "Planet Venus Feed Reader";
-        path  = [ pkgs.venus ];
-        script = "exec venus-planet ${configFile}";
-        serviceConfig.User = "${cfg.user}";
-        serviceConfig.Group = "${cfg.group}";
-        startAt = cfg.dates;
-      };
-
-  };
-}
diff --git a/nixos/modules/programs/vim.nix b/nixos/modules/programs/vim.nix
index fe0e7f2c6d6..9f46dff2a29 100644
--- a/nixos/modules/programs/vim.nix
+++ b/nixos/modules/programs/vim.nix
@@ -14,10 +14,20 @@ in {
         using the EDITOR environment variable.
       '';
     };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.vim;
+      defaultText = "pkgs.vim";
+      example = "pkgs.vimHugeX";
+      description = ''
+        vim package to use.
+      '';
+    };
   };
 
   config = mkIf cfg.defaultEditor {
-        environment.systemPackages = [ pkgs.vim ];
-        environment.variables = { EDITOR = mkOverride 900 "vim"; };
+    environment.systemPackages = [ cfg.package ];
+    environment.variables = { EDITOR = mkOverride 900 "vim"; };
   };
 }
diff --git a/nixos/modules/programs/wshowkeys.nix b/nixos/modules/programs/wshowkeys.nix
new file mode 100644
index 00000000000..09b008af1d5
--- /dev/null
+++ b/nixos/modules/programs/wshowkeys.nix
@@ -0,0 +1,22 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.wshowkeys;
+in {
+  meta.maintainers = with maintainers; [ primeos ];
+
+  options = {
+    programs.wshowkeys = {
+      enable = mkEnableOption ''
+        wshowkeys (displays keypresses on screen on supported Wayland
+        compositors). It requires root permissions to read input events, but
+        these permissions are dropped after startup'';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.wshowkeys.source = "${pkgs.wshowkeys}/bin/wshowkeys";
+  };
+}
diff --git a/nixos/modules/programs/x2goserver.nix b/nixos/modules/programs/x2goserver.nix
deleted file mode 100644
index 7d74231e956..00000000000
--- a/nixos/modules/programs/x2goserver.nix
+++ /dev/null
@@ -1,149 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.programs.x2goserver;
-
-  defaults = {
-    superenicer = { enable = cfg.superenicer.enable; };
-  };
-  confText = generators.toINI {} (recursiveUpdate defaults cfg.settings);
-  x2goServerConf = pkgs.writeText "x2goserver.conf" confText;
-
-  x2goAgentOptions = pkgs.writeText "x2goagent.options" ''
-    X2GO_NXOPTIONS=""
-    X2GO_NXAGENT_DEFAULT_OPTIONS="${concatStringsSep " " cfg.nxagentDefaultOptions}"
-  '';
-
-in {
-  options.programs.x2goserver = {
-    enable = mkEnableOption "x2goserver" // {
-      description = ''
-        Enables the x2goserver module.
-        NOTE: This will create a good amount of symlinks in `/usr/local/bin`
-      '';
-    };
-
-    superenicer = {
-      enable = mkEnableOption "superenicer" // {
-        description = ''
-          Enables the SupeReNicer code in x2gocleansessions, this will renice
-          suspended sessions to nice level 19 and renice them to level 0 if the
-          session becomes marked as running again
-        '';
-      };
-    };
-
-    nxagentDefaultOptions = mkOption {
-      type = types.listOf types.str;
-      default = [ "-extension GLX" "-nolisten tcp" ];
-      example = [ "-extension GLX" "-nolisten tcp" ];
-      description = ''
-        List of default nx agent options.
-      '';
-    };
-
-    settings = mkOption {
-      type = types.attrsOf types.attrs;
-      default = {};
-      description = ''
-        x2goserver.conf ini configuration as nix attributes. See
-        `x2goserver.conf(5)` for details
-      '';
-      example = literalExample ''
-        superenicer = {
-          "enable" = "yes";
-          "idle-nice-level" = 19;
-        };
-        telekinesis = { "enable" = "no"; };
-      '';
-    };
-  };
-
-  config = mkIf cfg.enable {
-
-    environment.systemPackages = [ pkgs.x2goserver ];
-
-    users.groups.x2go = {};
-    users.users.x2go = {
-      home = "/var/lib/x2go/db";
-      group = "x2go";
-      isSystemUser = true;
-    };
-
-    security.wrappers.x2gosqliteWrapper = {
-      source = "${pkgs.x2goserver}/lib/x2go/libx2go-server-db-sqlite3-wrapper.pl";
-      owner = "x2go";
-      group = "x2go";
-      setgid = true;
-    };
-    security.wrappers.x2goprintWrapper = {
-      source = "${pkgs.x2goserver}/bin/x2goprint";
-      owner = "x2go";
-      group = "x2go";
-      setgid = true;
-    };
-
-    systemd.tmpfiles.rules = with pkgs; [
-      "d /var/lib/x2go/ - x2go x2go - -"
-      "d /var/lib/x2go/db - x2go x2go - -"
-      "d /var/lib/x2go/conf - x2go x2go - -"
-      "d /run/x2go 0755 x2go x2go - -"
-    ] ++
-    # x2goclient sends SSH commands with preset PATH set to
-    # "/usr/local/bin;/usr/bin;/bin". Since we cannot filter arbitrary ssh
-    # commands, we have to make the following executables available.
-    map (f: "L+ /usr/local/bin/${f} - - - - ${x2goserver}/bin/${f}") [
-      "x2goagent" "x2gobasepath" "x2gocleansessions" "x2gocmdexitmessage"
-      "x2godbadmin" "x2gofeature" "x2gofeaturelist" "x2gofm" "x2gogetapps"
-      "x2gogetservers" "x2golistdesktops" "x2golistmounts" "x2golistsessions"
-      "x2golistsessions_root" "x2golistshadowsessions" "x2gomountdirs"
-      "x2gopath" "x2goprint" "x2goresume-desktopsharing" "x2goresume-session"
-      "x2goruncommand" "x2goserver-run-extensions" "x2gosessionlimit"
-      "x2gosetkeyboard" "x2goshowblocks" "x2gostartagent"
-      "x2gosuspend-desktopsharing" "x2gosuspend-session"
-      "x2goterminate-desktopsharing" "x2goterminate-session"
-      "x2goumount-session" "x2goversion"
-    ] ++ [
-      "L+ /usr/local/bin/awk - - - - ${gawk}/bin/awk"
-      "L+ /usr/local/bin/chmod - - - - ${coreutils}/bin/chmod"
-      "L+ /usr/local/bin/cp - - - - ${coreutils}/bin/cp"
-      "L+ /usr/local/bin/sed - - - - ${gnused}/bin/sed"
-      "L+ /usr/local/bin/setsid - - - - ${utillinux}/bin/setsid"
-      "L+ /usr/local/bin/xrandr - - - - ${xorg.xrandr}/bin/xrandr"
-      "L+ /usr/local/bin/xmodmap - - - - ${xorg.xmodmap}/bin/xmodmap"
-    ];
-
-    systemd.services.x2goserver = {
-      description = "X2Go Server Daemon";
-      wantedBy = [ "multi-user.target" ];
-      unitConfig.Documentation = "man:x2goserver.conf(5)";
-      serviceConfig = {
-        Type = "forking";
-        ExecStart = "${pkgs.x2goserver}/bin/x2gocleansessions";
-        PIDFile = "/run/x2go/x2goserver.pid";
-        User = "x2go";
-        Group = "x2go";
-        RuntimeDirectory = "x2go";
-        StateDirectory = "x2go";
-      };
-      preStart = ''
-        if [ ! -e /var/lib/x2go/setup_ran ]
-        then
-          mkdir -p /var/lib/x2go/conf
-          cp -r ${pkgs.x2goserver}/etc/x2go/* /var/lib/x2go/conf/
-          ln -sf ${x2goServerConf} /var/lib/x2go/conf/x2goserver.conf
-          ln -sf ${x2goAgentOptions} /var/lib/x2go/conf/x2goagent.options
-          ${pkgs.x2goserver}/bin/x2godbadmin --createdb
-          touch /var/lib/x2go/setup_ran
-        fi
-      '';
-    };
-
-    # https://bugs.x2go.org/cgi-bin/bugreport.cgi?bug=276
-    security.sudo.extraConfig = ''
-      Defaults  env_keep+=QT_GRAPHICSSYSTEM
-    '';
-  };
-}
diff --git a/nixos/modules/programs/xss-lock.nix b/nixos/modules/programs/xss-lock.nix
index a7ad9b89db4..ceb7259b3d7 100644
--- a/nixos/modules/programs/xss-lock.nix
+++ b/nixos/modules/programs/xss-lock.nix
@@ -11,7 +11,7 @@ in
 
     lockerCommand = mkOption {
       default = "${pkgs.i3lock}/bin/i3lock";
-      example = literalExample ''''${pkgs.i3lock-fancy}/bin/i3lock-fancy'';
+      example = literalExample "\${pkgs.i3lock-fancy}/bin/i3lock-fancy";
       type = types.separatedString " ";
       description = "Locker to be used with xsslock";
     };
@@ -34,7 +34,7 @@ in
       partOf = [ "graphical-session.target" ];
       serviceConfig.ExecStart = with lib;
         strings.concatStringsSep " " ([
-            "${pkgs.xss-lock}/bin/xss-lock"
+            "${pkgs.xss-lock}/bin/xss-lock" "--session \${XDG_SESSION_ID}"
           ] ++ (map escapeShellArg cfg.extraOptions) ++ [
             "--"
             cfg.lockerCommand
diff --git a/nixos/modules/programs/xwayland.nix b/nixos/modules/programs/xwayland.nix
new file mode 100644
index 00000000000..cb3c9c5b156
--- /dev/null
+++ b/nixos/modules/programs/xwayland.nix
@@ -0,0 +1,51 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.xwayland;
+
+in
+
+{
+  options.programs.xwayland = {
+
+    enable = mkEnableOption "Xwayland (an X server for interfacing X11 apps with the Wayland protocol)";
+
+    defaultFontPath = mkOption {
+      type = types.str;
+      default = optionalString config.fonts.fontDir.enable
+        "/run/current-system/sw/share/X11/fonts";
+      defaultText = literalExample ''
+        optionalString config.fonts.fontDir.enable
+          "/run/current-system/sw/share/X11/fonts";
+      '';
+      description = ''
+        Default font path. Setting this option causes Xwayland to be rebuilt.
+      '';
+    };
+
+    package = mkOption {
+      type = types.path;
+      default = pkgs.xwayland.override (oldArgs: {
+        inherit (cfg) defaultFontPath;
+      });
+      defaultText = literalExample ''
+        pkgs.xwayland.override (oldArgs: {
+          inherit (config.programs.xwayland) defaultFontPath;
+        });
+      '';
+      description = "The Xwayland package to use.";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    # Needed by some applications for fonts and default settings
+    environment.pathsToLink = [ "/share/X11" ];
+
+    environment.systemPackages = [ cfg.package ];
+
+  };
+}
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.xml b/nixos/modules/programs/zsh/oh-my-zsh.xml
index 568c2de6557..14a7228ad9b 100644
--- a/nixos/modules/programs/zsh/oh-my-zsh.xml
+++ b/nixos/modules/programs/zsh/oh-my-zsh.xml
@@ -73,7 +73,7 @@
 <programlisting>
 { pkgs, ... }:
 {
-  programs.zsh.ohMyZsh.customPkgs = with pkgs; [
+  programs.zsh.ohMyZsh.customPkgs = [
     pkgs.nix-zsh-completions
     # and even more...
   ];
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index 049a315c762..6c824a692b7 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -53,7 +53,7 @@ in
       };
 
       shellAliases = mkOption {
-        default = {};
+        default = { };
         description = ''
           Set of aliases for zsh shell, which overrides <option>environment.shellAliases</option>.
           See <option>environment.shellAliases</option> for an option format description.
@@ -91,7 +91,7 @@ in
           # before setting your PS1 and etc. Otherwise this will likely to interact with
           # your ~/.zshrc configuration in unexpected ways as the default prompt sets
           # a lot of different prompt variables.
-          autoload -U promptinit && promptinit && prompt walters && setopt prompt_sp
+          autoload -U promptinit && promptinit && prompt suse && setopt prompt_sp
         '';
         description = ''
           Shell script code used to initialise the zsh prompt.
@@ -118,7 +118,9 @@ in
       setOptions = mkOption {
         type = types.listOf types.str;
         default = [
-          "HIST_IGNORE_DUPS" "SHARE_HISTORY" "HIST_FCNTL_LOCK"
+          "HIST_IGNORE_DUPS"
+          "SHARE_HISTORY"
+          "HIST_FCNTL_LOCK"
         ];
         example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ];
         description = ''
@@ -278,15 +280,29 @@ in
 
     environment.etc.zinputrc.source = ./zinputrc;
 
-    environment.systemPackages = [ pkgs.zsh ]
-      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions;
+    environment.systemPackages =
+      let
+        completions =
+          if lib.versionAtLeast (lib.getVersion config.nix.package) "2.4pre"
+          then
+            pkgs.nix-zsh-completions.overrideAttrs
+              (_: {
+                postInstall = ''
+                  rm $out/share/zsh/site-functions/_nix
+                '';
+              })
+          else pkgs.nix-zsh-completions;
+      in
+      [ pkgs.zsh ]
+      ++ optional cfg.enableCompletion completions;
 
     environment.pathsToLink = optional cfg.enableCompletion "/share/zsh";
 
     #users.defaultUserShell = mkDefault "/run/current-system/sw/bin/zsh";
 
     environment.shells =
-      [ "/run/current-system/sw/bin/zsh"
+      [
+        "/run/current-system/sw/bin/zsh"
         "${pkgs.zsh}/bin/zsh"
       ];