aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorNicolas Berbiche <nic.berbiche@gmail.com>2020-07-29 16:29:51 -0400
committerRobert Helgesson <robert@rycee.net>2020-08-14 00:20:49 +0200
commitf4f9f1a618eb7c19b765b02c235e13657b5aa150 (patch)
tree5843f7504ea42f568ecf3cd11d743a2f3b191a8c /modules
parent2a54c353a9ee4c06866047b0abf0a418ab91a475 (diff)
waybar: add module
PR #1329
Diffstat (limited to 'modules')
-rw-r--r--modules/lib/maintainers.nix10
-rw-r--r--modules/misc/news.nix8
-rw-r--r--modules/modules.nix1
-rw-r--r--modules/programs/waybar.nix363
4 files changed, 382 insertions, 0 deletions
diff --git a/modules/lib/maintainers.nix b/modules/lib/maintainers.nix
index 49d14ef0638..ff046360071 100644
--- a/modules/lib/maintainers.nix
+++ b/modules/lib/maintainers.nix
@@ -25,4 +25,14 @@
github = "cwyc";
githubId = 16950437;
};
+ berbiche = {
+ name = "Nicolas Berbiche";
+ email = "berbiche@users.noreply.github.com";
+ github = "berbiche";
+ githubId = 20448408;
+ keys = [{
+ longkeyid = "rsa4096/0xB461292445C6E696";
+ fingerprint = "D446 E58D 87A0 31C7 EC15 88D7 B461 2924 45C6 E696";
+ }];
+ };
}
diff --git a/modules/misc/news.nix b/modules/misc/news.nix
index 62f57a44cec..4ec6fc2d6dd 100644
--- a/modules/misc/news.nix
+++ b/modules/misc/news.nix
@@ -1619,6 +1619,14 @@ in
A new module is available: 'services.dropbox'.
'';
}
+
+ {
+ time = "2020-08-13T22:15:27+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'programs.waybar'
+ '';
+ }
];
};
}
diff --git a/modules/modules.nix b/modules/modules.nix
index 76ff5aaa8c0..41877c5e21a 100644
--- a/modules/modules.nix
+++ b/modules/modules.nix
@@ -118,6 +118,7 @@ let
(loadModule ./programs/vim.nix { })
(loadModule ./programs/vscode.nix { })
(loadModule ./programs/vscode/haskell.nix { })
+ (loadModule ./programs/waybar.nix { condition = hostPlatform.isLinux; })
(loadModule ./programs/z-lua.nix { })
(loadModule ./programs/zathura.nix { })
(loadModule ./programs/zoxide.nix { })
diff --git a/modules/programs/waybar.nix b/modules/programs/waybar.nix
new file mode 100644
index 00000000000..369f6e32aba
--- /dev/null
+++ b/modules/programs/waybar.nix
@@ -0,0 +1,363 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+ cfg = config.programs.waybar;
+
+ # Used when generating warnings
+ modulesPath = "programs.waybar.settings.[].modules";
+
+ # Taken from <https://github.com/Alexays/Waybar/blob/adaf84304865e143e4e83984aaea6f6a7c9d4d96/src/factory.cpp>
+ defaultModuleNames = [
+ "sway/mode"
+ "sway/workspaces"
+ "sway/window"
+ "wlr/taskbar"
+ "idle_inhibitor"
+ "memory"
+ "cpu"
+ "clock"
+ "disk"
+ "tray"
+ "network"
+ "backlight"
+ "pulseaudio"
+ "mpd"
+ "temperature"
+ "bluetooth"
+ "battery"
+ ];
+
+ isValidCustomModuleName = x:
+ elem x defaultModuleNames || (hasPrefix "custom/" x && stringLength x > 7);
+
+ margins = let
+ mkMargin = name: {
+ "margin-${name}" = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ example = 10;
+ description = "Margins value without unit.";
+ };
+ };
+ margins = map mkMargin [ "top" "left" "bottom" "right" ];
+ in foldl' mergeAttrs { } margins;
+
+ waybarBarConfig = with lib.types;
+ submodule {
+ options = {
+ layer = mkOption {
+ type = nullOr (enum [ "top" "bottom" ]);
+ default = null;
+ description = ''
+ Decide if the bar is displayed in front (<code>"top"</code>)
+ of the windows or behind (<code>"bottom"</code>).
+ '';
+ example = "top";
+ };
+
+ output = mkOption {
+ type = nullOr (either str (listOf str));
+ default = null;
+ example = literalExample ''
+ [ "DP-1" "!DP-2" "!DP-3" ]
+ '';
+ description = ''
+ Specifies on which screen this bar will be displayed.
+ Exclamation mark(!) can be used to exclude specific output.
+ '';
+ };
+
+ position = mkOption {
+ type = nullOr (enum [ "top" "bottom" "left" "right" ]);
+ default = null;
+ example = "right";
+ description = "Bar position relative to the output.";
+ };
+
+ height = mkOption {
+ type = nullOr ints.unsigned;
+ default = null;
+ example = 5;
+ description =
+ "Height to be used by the bar if possible. Leave blank for a dynamic value.";
+ };
+
+ width = mkOption {
+ type = nullOr ints.unsigned;
+ default = null;
+ example = 5;
+ description =
+ "Width to be used by the bar if possible. Leave blank for a dynamic value.";
+ };
+
+ modules-left = mkOption {
+ type = nullOr (listOf str);
+ default = null;
+ description = "Modules that will be displayed on the left.";
+ example = literalExample ''
+ [ "sway/workspaces" "sway/mode" "wlr/taskbar" ]
+ '';
+ };
+
+ modules-center = mkOption {
+ type = nullOr (listOf str);
+ default = null;
+ description = "Modules that will be displayed in the center.";
+ example = literalExample ''
+ [ "sway/window" ]
+ '';
+ };
+
+ modules-right = mkOption {
+ type = nullOr (listOf str);
+ default = null;
+ description = "Modules that will be displayed on the right.";
+ example = literalExample ''
+ [ "mpd" "custom/mymodule#with-css-id" "temperature" ]
+ '';
+ };
+
+ modules = mkOption {
+ type = attrsOf unspecified;
+ default = { };
+ description = "Modules configuration.";
+ example = literalExample ''
+ {
+ "sway/window" = {
+ max-length = 50;
+ };
+ "clock" = {
+ format-alt = "{:%a, %d. %b %H:%M}";
+ };
+ }
+ '';
+ };
+
+ margin = mkOption {
+ type = nullOr str;
+ default = null;
+ description = "Margins value using the CSS format without units.";
+ example = "20 5";
+ };
+
+ inherit (margins) margin-top margin-left margin-bottom margin-right;
+
+ name = mkOption {
+ type = nullOr str;
+ default = null;
+ description =
+ "Optional name added as a CSS class, for styling multiple waybars.";
+ example = "waybar-1";
+ };
+
+ gtk-layer-shell = mkOption {
+ type = nullOr bool;
+ default = null;
+ example = false;
+ description =
+ "Option to disable the use of gtk-layer-shell for popups.";
+ };
+ };
+ };
+in {
+ meta.maintainers = [ hm.maintainers.berbiche ];
+
+ options.programs.waybar = with lib.types; {
+ enable = mkEnableOption "Waybar";
+
+ package = mkOption {
+ type = package;
+ default = pkgs.waybar;
+ defaultText = literalExample "${pkgs.waybar}";
+ description = ''
+ Waybar package to use. Set to <code>null</code> to use the default module.
+ '';
+ };
+
+ settings = mkOption {
+ type = listOf waybarBarConfig;
+ default = [ ];
+ description = ''
+ Configuration for Waybar, see <link
+ xlink:href="https://github.com/Alexays/Waybar/wiki/Configuration"/>
+ for supported values.
+ '';
+ example = literalExample ''
+ [
+ {
+ layer = "top";
+ position = "top";
+ height = 30;
+ output = [
+ "eDP-1"
+ "HDMI-A-1"
+ ];
+ modules-left = [ "sway/workspaces" "sway/mode" "wlr/taskbar" ];
+ modules-center = [ "sway/window" "custom/hello-from-waybar" ];
+ modules-right = [ "mpd" "custom/mymodule#with-css-id" "temperature" ];
+ modules = {
+ "sway/workspaces" = {
+ disable-scroll = true;
+ all-outputs = true;
+ };
+ "custom/hello-from-waybar" = {
+ format = "hello {}";
+ max-length = 40;
+ interval = "once";
+ exec = pkgs.writeShellScript "hello-from-waybar" '''
+ echo "from within waybar"
+ ''';
+ };
+ };
+ }
+ ]
+ '';
+ };
+
+ systemd.enable = mkEnableOption "Waybar systemd integration";
+
+ style = mkOption {
+ type = nullOr str;
+ default = null;
+ description = ''
+ CSS style of the bar.
+ See <link xlink:href="https://github.com/Alexays/Waybar/wiki/Configuration"/>
+ for the documentation.
+ '';
+ example = ''
+ * {
+ border: none;
+ border-radius: 0;
+ font-family: Source Code Pro;
+ }
+ window#waybar {
+ background: #16191C;
+ color: #AAB2BF;
+ }
+ #workspaces button {
+ padding: 0 5px;
+ }
+ '';
+ };
+ };
+
+ config = let
+ # Inspired by https://github.com/NixOS/nixpkgs/pull/89781
+ writePrettyJSON = name: x:
+ pkgs.runCommandLocal name { } ''
+ ${pkgs.jq}/bin/jq . > $out <<<${escapeShellArg (builtins.toJSON x)}
+ '';
+
+ configSource = let
+ # Removes nulls because Waybar ignores them for most values
+ removeNulls = filterAttrs (_: v: v != null);
+
+ # Makes the actual valid configuration Waybar accepts
+ # (strips our custom settings before converting to JSON)
+ makeConfiguration = configuration:
+ let
+ # The "modules" option is not valid in the JSON
+ # as its descendants have to live at the top-level
+ settingsWithoutModules =
+ filterAttrs (n: _: n != "modules") configuration;
+ settingsModules =
+ optionalAttrs (configuration.modules != { }) configuration.modules;
+ in removeNulls (settingsWithoutModules // settingsModules);
+ # The clean list of configurations
+ finalConfiguration = map makeConfiguration cfg.settings;
+ in writePrettyJSON "waybar-config.json" finalConfiguration;
+
+ warnings = let
+ mkUnreferencedModuleWarning = name:
+ "The module '${name}' defined in '${modulesPath}' is not referenced "
+ + "in either `modules-left`, `modules-center` or `modules-right` of Waybar's options";
+ mkUndefinedModuleWarning = settings: name:
+ let
+ # Locations where the module is undefined (a combination modules-{left,center,right})
+ locations = flip filter [ "left" "center" "right" ]
+ (x: elem name settings."modules-${x}");
+ mkPath = loc: "'${modulesPath}-${loc}'";
+ # The modules-{left,center,right} configuration that includes
+ # an undefined module
+ path = concatMapStringsSep " and " mkPath locations;
+ in "The module '${name}' defined in ${path} is neither "
+ + "a default module or a custom module declared in '${modulesPath}'";
+ mkInvalidModuleNameWarning = name:
+ "The custom module '${name}' defined in '${modulesPath}' is not a valid "
+ + "module name. A custom module's name must start with 'custom/' "
+ + "like 'custom/mymodule' for instance";
+
+ # Find all modules in `modules-{left,center,right}` and `modules` not declared/referenced.
+ # `cfg.settings` is a list of Waybar configurations
+ # and we need to preserve the index for appropriate warnings
+ allFaultyModules = flip map cfg.settings (settings:
+ let
+ allModules = unique
+ (concatMap (x: attrByPath [ "modules-${x}" ] [ ] settings) [
+ "left"
+ "center"
+ "right"
+ ]);
+ declaredModules = attrNames settings.modules;
+ # Modules declared in `modules` but not referenced in `modules-{left,center,right}`
+ unreferencedModules = subtractLists allModules declaredModules;
+ # Modules listed in modules-{left,center,right} that are not default modules
+ nonDefaultModules = subtractLists defaultModuleNames allModules;
+ # Modules referenced in `modules-{left,center,right}` but not declared in `modules`
+ undefinedModules = subtractLists declaredModules nonDefaultModules;
+ # Check for invalid module names
+ invalidModuleNames =
+ filter (m: !isValidCustomModuleName m) (attrNames settings.modules);
+ in {
+ # The Waybar bar configuration (since config.settings is a list)
+ settings = settings;
+ undef = undefinedModules;
+ unref = unreferencedModules;
+ invalidName = invalidModuleNames;
+ });
+
+ allWarnings = flip concatMap allFaultyModules
+ ({ settings, undef, unref, invalidName }:
+ let
+ unreferenced = map mkUnreferencedModuleWarning unref;
+ undefined = map (mkUndefinedModuleWarning settings) undef;
+ invalid = map mkInvalidModuleNameWarning invalidName;
+ in undefined ++ unreferenced ++ invalid);
+ in allWarnings;
+
+ in mkIf cfg.enable (mkMerge [
+ { home.packages = [ cfg.package ]; }
+ (mkIf (cfg.settings != [ ]) {
+ # Generate warnings about defined but unreferenced modules
+ inherit warnings;
+
+ xdg.configFile."waybar/config".source = configSource;
+ })
+ (mkIf (cfg.style != null) {
+ xdg.configFile."waybar/style.css".text = cfg.style;
+ })
+ (mkIf cfg.systemd.enable {
+ systemd.user.services.waybar = {
+ Unit = {
+ Description =
+ "Highly customizable Wayland bar for Sway and Wlroots based compositors.";
+ Documentation = "https://github.com/Alexays/Waybar/wiki";
+ PartOf = [ "graphical-session.target" ];
+ Requisite = [ "dbus.service" ];
+ After = [ "dbus.service" ];
+ };
+
+ Service = {
+ Type = "dbus";
+ BusName = "fr.arouillard.waybar";
+ ExecStart = "${cfg.package}/bin/waybar";
+ Restart = "always";
+ RestartSec = "1sec";
+ };
+
+ Install = { WantedBy = [ "graphical-session.target" ]; };
+ };
+ })
+ ]);
+}