about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--machines/dnyarri.nix5
-rw-r--r--machines/mmrnmhrm.nix10
-rw-r--r--modules/i3/default.nix69
-rw-r--r--modules/i3/i3.conf43
-rw-r--r--modules/i3/workspace.nix106
5 files changed, 175 insertions, 58 deletions
diff --git a/machines/dnyarri.nix b/machines/dnyarri.nix
index bda86e1d..378e29db 100644
--- a/machines/dnyarri.nix
+++ b/machines/dnyarri.nix
@@ -132,4 +132,9 @@ with import ../lib;
 
   services.xserver.videoDrivers = [ "ati" ];
   services.xserver.xrandrHeads = [ "HDMI-0" "DVI-0" ];
+
+  aszlig.i3.workspaces."6" = {
+    label = "Chromium";
+    assign = singleton { class = "^Chromium(?:-browser)?\$"; };
+  };
 }
diff --git a/machines/mmrnmhrm.nix b/machines/mmrnmhrm.nix
index 7e299b52..c8866545 100644
--- a/machines/mmrnmhrm.nix
+++ b/machines/mmrnmhrm.nix
@@ -52,4 +52,14 @@ with import ../lib;
 
   services.xserver.videoDrivers = [ "nouveau" ];
   services.xserver.xrandrHeads = [ "DVI-I-1" "VGA-1" ];
+
+  aszlig.i3.workspaces."1" = {
+    label = "XMPP";
+    assign = singleton { class = "^(?:Tkabber|Gajim)\$"; };
+  };
+
+  aszlig.i3.workspaces."3" = {
+    label = "Chromium";
+    assign = singleton { class = "^Chromium(?:-browser)?\$"; };
+  };
 }
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}
+  '';
+}