about summary refs log tree commit diff
path: root/modules/i3
diff options
context:
space:
mode:
authoraszlig <aszlig@redmoonstudios.org>2014-07-14 22:47:14 +0200
committeraszlig <aszlig@redmoonstudios.org>2014-07-14 22:47:14 +0200
commit1018cd7cc704f08e3329cb758a73e6da98d4e087 (patch)
tree7ee1659af7cce5cbd5d0f6c0236b10fac3edb87c /modules/i3
parentc65f5ba3a7eb43e01f098d63f8d22cdc1e78d8bd (diff)
i3: Add NixOS options to configure workspaces.
This allows for a more dynamic workspace assignments, especially when
varying between the number of heads. We now not only can use the NixOS
module system to set workspaces but also assign applications to them.

And the default workspace layout is to evenly spread out the heads among
the available heads.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Diffstat (limited to 'modules/i3')
-rw-r--r--modules/i3/default.nix69
-rw-r--r--modules/i3/i3.conf43
-rw-r--r--modules/i3/workspace.nix106
3 files changed, 160 insertions, 58 deletions
diff --git a/modules/i3/default.nix b/modules/i3/default.nix
index 86ba462c..2c668cc4 100644
--- a/modules/i3/default.nix
+++ b/modules/i3/default.nix
@@ -1,9 +1,56 @@
-{ pkgs, config, ... }:
+{ pkgs, lib, config, ... }:
 
-with pkgs.lib;
+with lib;
 
+let
+  # The symbols if you press shift and a number key.
+  wsNumberSymbols = [
+    "exclam" "at" "numbersign" "dollar" "percent"
+    "asciicircum" "ampersand" "asterisk" "parenleft" "parenright"
+  ];
+
+  wsCount = length wsNumberSymbols;
+
+  headCount = length config.services.xserver.xrandrHeads;
+  wsPerHead = wsCount / headCount;
+  excessWs = wsCount - (headCount * wsPerHead);
+  getHeadAt = elemAt config.services.xserver.xrandrHeads;
+
+  mkDefaultWorkspace = number: numberSymbol: {
+    name = toString number;
+    value = mkDefault {
+      label = null;
+      labelPrefix = "${toString number}: ";
+      keys.switchTo = "$mod+${toString number}";
+      keys.moveTo = "$mod+Shift+${numberSymbol}";
+      head = getHeadAt ((number - (excessWs + 1)) / wsPerHead);
+    };
+  };
+
+  wsCfgList = mapAttrsToList (_: getAttr "config") config.aszlig.i3.workspaces;
+  wsConfig = concatStrings wsCfgList;
+  defaultWorkspaces = listToAttrs (imap mkDefaultWorkspace wsNumberSymbols);
+in
 {
-  services.xserver.windowManager = {
+  options.aszlig.i3 = {
+    workspaces = mkOption {
+      type = types.attrsOf (types.submodule ./workspace.nix);
+      default = listToAttrs (imap mkDefaultWorkspace wsNumberSymbols);
+      description = ''
+        Workspace to monitor assignment.
+
+        Workspaces are by default assigned starting from the leftmost monitor
+        being workspace 1 and the rightmost monitor being workspace 10. The
+        workspaces are divided by the number of available heads, so if you have
+        a dual head system, you'll end up having workspace 1 to 5 on the left
+        monitor and 6 to 10 on the right.
+      '';
+    };
+  };
+
+  config.aszlig.i3.workspaces = defaultWorkspaces;
+
+  config.services.xserver.windowManager = {
     default = "i3";
 
     i3.enable = true;
@@ -17,6 +64,7 @@ with pkgs.lib;
 
       inherit (pkgs) dmenu xterm pvolctrl;
       inherit (pkgs.xorg) xsetroot;
+      inherit wsConfig;
 
       leftHead = head config.services.xserver.xrandrHeads;
       rightHead = last config.services.xserver.xrandrHeads;
@@ -30,19 +78,6 @@ with pkgs.lib;
     in if config.networking.hostName == "mmrnmhrm"
        then { inherit leftHead rightHead; }
        else { leftHead = rightHead; rightHead = leftHead; }
-    ) // (let
-      wsConfig = if config.networking.hostName == "mmrnmhrm"
-                 then [ "XMPP" null "chromium" null null
-                        null   null null       null null ]
-                 else [ null       null null null null
-                        "chromium" null null null null ];
-
-      mkWsName = num: name: let
-        mkPair = nameValuePair "ws${toString num}";
-      in if name == null
-         then mkPair (toString num)
-         else mkPair "${toString num}: ${name}";
-
-    in listToAttrs (imap mkWsName wsConfig)));
+    ));
   };
 }
diff --git a/modules/i3/i3.conf b/modules/i3/i3.conf
index d6273489..b6780f98 100644
--- a/modules/i3/i3.conf
+++ b/modules/i3/i3.conf
@@ -74,30 +74,6 @@ bindsym $mod+a focus parent
 # focus the child container
 bindsym $mod+semicolon focus child
 
-# switch to workspace
-bindsym $mod+1 workspace "@ws1@"
-bindsym $mod+2 workspace "@ws2@"
-bindsym $mod+3 workspace "@ws3@"
-bindsym $mod+4 workspace "@ws4@"
-bindsym $mod+5 workspace "@ws5@"
-bindsym $mod+6 workspace "@ws6@"
-bindsym $mod+7 workspace "@ws7@"
-bindsym $mod+8 workspace "@ws8@"
-bindsym $mod+9 workspace "@ws9@"
-bindsym $mod+0 workspace "@ws10@"
-
-# move focused container to workspace
-bindsym $mod+Shift+exclam move workspace "@ws1@"
-bindsym $mod+Shift+at move workspace "@ws2@"
-bindsym $mod+Shift+numbersign move workspace "@ws3@"
-bindsym $mod+Shift+dollar move workspace "@ws4@"
-bindsym $mod+Shift+percent move workspace "@ws5@"
-bindsym $mod+Shift+asciicircum move workspace "@ws6@"
-bindsym $mod+Shift+ampersand move workspace "@ws7@"
-bindsym $mod+Shift+asterisk move workspace "@ws8@"
-bindsym $mod+Shift+parenleft move workspace "@ws9@"
-bindsym $mod+Shift+parenright move workspace "@ws10@"
-
 # reload the configuration file
 bindsym $mod+Shift+L reload
 # restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
@@ -138,23 +114,8 @@ mode "resize" {
 
 bindsym $mod+r mode "resize"
 
-# workspace assignments
-workspace "@ws1@" output @leftHead@
-workspace "@ws2@" output @leftHead@
-workspace "@ws3@" output @leftHead@
-workspace "@ws4@" output @leftHead@
-workspace "@ws5@" output @leftHead@
-workspace "@ws6@" output @rightHead@
-workspace "@ws7@" output @rightHead@
-workspace "@ws8@" output @rightHead@
-workspace "@ws9@" output @rightHead@
-workspace "@ws10@" output @rightHead@
-
-# default applications
-assign [class="^Tkabber$"] 1: tkabber
-#exec --no-startup-id tkabber
-assign [class="^Chromium(?:-browser)?$"] 3: chromium
-#exec chromium
+# workspace configuration
+@wsConfig@
 
 # ratmenu should be as unintrusive as possible
 for_window [class="^ratmenu$"] floating enable
diff --git a/modules/i3/workspace.nix b/modules/i3/workspace.nix
new file mode 100644
index 00000000..5365bf74
--- /dev/null
+++ b/modules/i3/workspace.nix
@@ -0,0 +1,106 @@
+{ name, lib, config, ... }:
+
+with lib;
+
+let
+  finalLabel =
+    if config.label == null then name
+    else config.labelPrefix + config.label;
+
+  mkDoc = anchor: "http://i3wm.org/docs/userguide.html#${anchor}";
+in
+{
+  options = {
+    labelPrefix = mkOption {
+      type = types.str;
+      default = "";
+      example = "666: ";
+      description = ''
+        The value that will be put in front of the <option>label</option>.
+        So if you have a label called <replaceable>bar</replaceable> and a
+        <option>labelPrefix</option> called <replaceable>foo</replaceable> the
+        label for the workspace will be <replaceable>foobar</replaceable>.
+      '';
+    };
+
+    label = mkOption {
+      type = types.nullOr types.str;
+      default = name;
+      description = ''
+        The label of this workspace, which is its name by default. If the value
+        is <replaceable>null</replaceable>, the resulting label of the workspace
+        is just its name and no <option>labelPrefix</option> is applied.
+      '';
+    };
+
+    assign = mkOption {
+      type = types.listOf types.attrs;
+      default = [];
+      example = [
+        { class = "^Chromium(?:-browser)?\$"; }
+        { instance = "^gajim\$"; }
+      ];
+      description = let
+        anchor = "_automatically_putting_clients_on_specific_workspaces";
+      in ''
+        Assign windows to this specific workspace using the attribute names
+        described by <link xlink:href="${mkDoc anchor}"/>.
+      '';
+    };
+
+    head = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The XRandR head this workspace will be assigned to.
+      '';
+    };
+
+    keys = let
+      commonDesc = ''
+        The <replaceable>$mod</replaceable> placeholder represents the default
+        modifier key. Details about the syntax of key combinations can be found
+        at <link xlink:href="${mkDoc "keybindings"}"/>.
+      '';
+    in {
+      switchTo = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "$mod+1";
+        description = ''
+          Key combination to switch to this workspace.
+        '' + commonDesc;
+      };
+
+      moveTo = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "$mod+Shift+exclam";
+        description = ''
+          Key combination to move a container to this workspace.
+        '' + commonDesc;
+      };
+    };
+
+    config = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Raw configuration options for this workspace.
+      '';
+    };
+  };
+
+  config.config = let
+    maybeOutput = optionalString (config.head != null) " output ${config.head}";
+    mkAssign = mapAttrsToList (criteria: value: "${criteria}=\"${value}\"");
+    mkSym = sym: rest: optionalString (sym != null) "bindsym ${sym} ${rest}";
+  in ''
+    workspace "${finalLabel}"${maybeOutput}
+    ${mkSym config.keys.switchTo "workspace \"${finalLabel}\""}
+    ${mkSym config.keys.moveTo "move workspace \"${finalLabel}\""}
+    ${concatMapStrings (assign: ''
+    assign [${concatStringsSep " " (mkAssign assign)}] ${finalLabel}
+    '') config.assign}
+  '';
+}