commit e1146da491ac432388e3cb658edc467c272e6d07 Author: Peter Jones Date: Wed Jun 22 12:10:51 2022 -0700 Initial Import diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f897c76 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Plasma Manager contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d5e1023 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Plasma Manager: Manage KDE Plasma with Home Manager diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..15103d0 --- /dev/null +++ b/flake.lock @@ -0,0 +1,49 @@ +{ + "nodes": { + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1654113405, + "narHash": "sha256-VpK+0QaWG2JRgB00lw77N9TjkE3ec0iMYIX1TzGpxa4=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "ac2287df5a2d6f0a44bbcbd11701dbbf6ec43675", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "release-22.05", + "repo": "home-manager", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1655643053, + "narHash": "sha256-8PMDEr44DwH45PbBijtQcAPyC4Ap+sOO5wK0y5lFvyY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5afb1b7dcf46c4ded5719525a42879b35363862c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "home-manager": "home-manager", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..95db096 --- /dev/null +++ b/flake.nix @@ -0,0 +1,42 @@ +{ + description = "Manage KDE Plasma with Home Manager"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.05"; + + home-manager.url = "github:nix-community/home-manager/release-22.05"; + home-manager.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = inputs@{ self, ... }: + let + # List of systems we run NixOS tests for: + supportedSystems = [ + "x86_64-linux" + "aarch64-linux" + ]; + + # Function to generate a set based on supported systems: + forAllSystems = inputs.nixpkgs.lib.genAttrs supportedSystems; + + # Attribute set of nixpkgs for each system: + nixpkgsFor = forAllSystems (system: + import inputs.nixpkgs { inherit system; }); + in + { + homeManagerModules.plasma = { ... }: { + imports = [ ./modules ]; + }; + + checks = forAllSystems (system: + let test = path: import path { + pkgs = nixpkgsFor.${system}; + home-manager = inputs.home-manager; + module = self.homeManagerModules.plasma; + }; + in + { + default = test ./test/basic.nix; + }); + }; +} diff --git a/lib/kwriteconfig.nix b/lib/kwriteconfig.nix new file mode 100644 index 0000000..1c3775b --- /dev/null +++ b/lib/kwriteconfig.nix @@ -0,0 +1,46 @@ +{ pkgs, lib }: + +let + + ############################################################################## + # Convert a Nix value into a command line argument to kwriteconfig. + toKdeValue = v: + if v == null then + "--delete" + else if builtins.isString v then + lib.escapeShellArg v + else if builtins.isBool v then + "--type bool " + lib.boolToString v + else if builtins.isInt v then + builtins.toString v + else if builtins.isFloat v then + builtins.toString v + else + builtins.abort ("Unknown value type: " ++ builtins.toString v); + + ############################################################################## + # Generate a series of shell commands that will update a + # configuration value. + # + # The given file name should be relative to XDG_CONFIG_HOME. + # + # The group names are used to generate a nested path to the group + # containing the settings in the attribute set. + # + # The attribute set is the settings and values to set. + # + # Type: string -> [string] -> AttrSet -> string + kWriteConfig = file: groups: attrs: + lib.concatStringsSep "\n" (lib.mapAttrsToList + (key: value: '' + ${pkgs.libsForQt5.kconfig}/bin/kwriteconfig5 \ + --file ''${XDG_CONFIG_HOME:-$HOME/.config}/${lib.escapeShellArg file} \ + ${lib.concatMapStringsSep " " (g: "--group " + lib.escapeShellArg g) groups} \ + --key ${lib.escapeShellArg key} \ + ${toKdeValue value} + '') + attrs); +in +{ + inherit kWriteConfig; +} diff --git a/modules/default.nix b/modules/default.nix new file mode 100644 index 0000000..d2fa1c8 --- /dev/null +++ b/modules/default.nix @@ -0,0 +1,13 @@ +{ lib, ... }: + +{ + imports = [ + ./files.nix + ./windows.nix + ./workspace.nix + ]; + + options.programs.plasma.enable = lib.mkEnableOption '' + Enable configuration management for KDE Plasma. + ''; +} diff --git a/modules/files.nix b/modules/files.nix new file mode 100644 index 0000000..651e34b --- /dev/null +++ b/modules/files.nix @@ -0,0 +1,62 @@ +# Low-level access to changing Plasma settings. +{ config, lib, pkgs, ... }: + +let + inherit (import ../lib/kwriteconfig.nix { inherit lib pkgs; }) + kWriteConfig; + + cfg = config.programs.plasma.files; + + ############################################################################## + # A module for storing settings. + settingType = { name, ... }: { + freeformType = with lib.types; + attrsOf (nullOr (oneOf [ bool float int str ])); + + options = { + configGroupNesting = lib.mkOption { + type = lib.types.nonEmptyListOf lib.types.str; + description = "Group name, and sub-group names."; + }; + }; + + config = { + configGroupNesting = lib.mkDefault (lib.splitString "." name); + }; + }; + + ############################################################################## + # Remove reserved options from a settings attribute set. + settingsToConfig = settings: + lib.filterAttrs + (k: v: !(builtins.elem k [ "configGroupNesting" ])) + settings; + + ############################################################################## + # Generate a script that will use kwriteconfig to update all + # settings. + script = pkgs.writeScript "plasma-config" + (lib.concatStrings + (lib.mapAttrsToList + (file: settings: lib.concatMapStringsSep "\n" + (set: kWriteConfig file set.configGroupNesting (settingsToConfig set)) + (builtins.attrValues settings)) + cfg)); +in +{ + options.programs.plasma.files = lib.mkOption { + type = with lib.types; attrsOf (attrsOf (submodule settingType)); + default = { }; + description = '' + An attribute set where the keys are file names (relative to + XDG_CONFIG_HOME) and the values are attribute sets that + represent configuration groups and settings inside those groups. + ''; + }; + + config = lib.mkIf (builtins.length (builtins.attrNames cfg) > 0) { + home.activation.configure-plasma = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + $DRY_RUN_CMD ${script} + ''; + }; +} diff --git a/modules/windows.nix b/modules/windows.nix new file mode 100644 index 0000000..cbde5b1 --- /dev/null +++ b/modules/windows.nix @@ -0,0 +1,28 @@ +# Window configuration: +{ config, lib, ... }: + +let + cfg = config.programs.plasma; +in +{ + options.programs.plasma.windows = { + allowWindowsToRememberPositions = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Allow apps to remember the positions of their own windows, if + they support it. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + programs.plasma.files = { + kdeglobals = { + General.AllowKDEAppsToRememberWindowPositions = + lib.mkDefault cfg.windows.allowWindowsToRememberPositions; + }; + }; + }; +} + diff --git a/modules/workspace.nix b/modules/workspace.nix new file mode 100644 index 0000000..340bc8a --- /dev/null +++ b/modules/workspace.nix @@ -0,0 +1,23 @@ +# General workspace behavior settings: +{ config, lib, ... }: + +let + cfg = config.programs.plasma; +in +{ + options.programs.plasma.workspace = { + clickItemTo = lib.mkOption { + type = lib.types.enum [ "open" "select" ]; + default = "open"; + description = '' + Clicking files or folders should open or select them. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + programs.plasma.files.kdeglobals = { + KDE.SingleClick = lib.mkDefault (cfg.workspace.clickItemTo == "open"); + }; + }; +} diff --git a/test/basic.nix b/test/basic.nix new file mode 100644 index 0000000..50c6402 --- /dev/null +++ b/test/basic.nix @@ -0,0 +1,58 @@ +{ pkgs, home-manager, module }: + +let + script = pkgs.writeShellScriptBin "plasma-basic-test" '' + set -e + set -u + + export XDG_CONFIG_HOME=''${XDG_CONFIG_HOME:-$HOME/.config} + export PATH=${pkgs.libsForQt5.kconfig}/bin:$PATH + + kread_global() { + kreadconfig5 --file $XDG_CONFIG_HOME/kdeglobals --group $1 --key $2 + } + + assert_eq() { + actual=$(kread_global "$1" "$2") + + if [ "$actual" != "$3" ]; then + echo >&2 "ERROR: $1.$2: expected $3 but got $actual" + exit 1 + fi + } + + assert_eq KDE SingleClick false + assert_eq General AllowKDEAppsToRememberWindowPositions true + ''; + + homeConfig = { + home.packages = [ script ]; + + programs.plasma = { + enable = true; + workspace.clickItemTo = "select"; + }; + }; + + user = import ./user.nix { + inherit module home-manager homeConfig; + }; +in +pkgs.nixosTest { + name = "plasma-basic"; + + nodes.machine = { ... }: { + imports = [ user ]; + }; + + testScript = '' + # Boot: + start_all() + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("home-manager-fake.service") + + # Run tests: + machine.succeed("test -e /home/fake/.config/kdeglobals") + machine.succeed("su - fake -c plasma-basic-test") + ''; +} diff --git a/test/user.nix b/test/user.nix new file mode 100644 index 0000000..b412463 --- /dev/null +++ b/test/user.nix @@ -0,0 +1,26 @@ +{ module +, home-manager +, homeConfig +}: + +{ ... }: + +{ + imports = [ home-manager.nixosModules.home-manager ]; + + users.users.fake = { + createHome = true; + isNormalUser = true; + password = "password"; + group = "users"; + }; + + home-manager = { + useGlobalPkgs = true; + + users.fake = { ... }: { + imports = [ module ]; + config = homeConfig; + }; + }; +}