Skip to content

Commit 16766c2

Browse files
committed
desktops/i3: init module
1 parent d627460 commit 16766c2

File tree

2 files changed

+224
-0
lines changed
  • modules

2 files changed

+224
-0
lines changed

modules/collection/desktops/i3.nix

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
{
2+
config,
3+
lib,
4+
osConfig,
5+
pkgs,
6+
...
7+
}: let
8+
inherit (lib.attrsets) mapAttrs' nameValuePair;
9+
inherit (lib.meta) getExe;
10+
inherit (lib.modules) mkIf;
11+
inherit (lib.options) literalExpression mkOption mkEnableOption mkPackageOption;
12+
inherit (lib.strings) optionalString;
13+
inherit (lib.types) attrsOf lines listOf;
14+
15+
json = pkgs.formats.json {};
16+
17+
cfg = config.rum.desktops.i3;
18+
in {
19+
options.rum.desktops.i3 = {
20+
enable = mkEnableOption "i3";
21+
22+
package =
23+
mkPackageOption pkgs "i3" {
24+
nullable = true;
25+
extraDescription = ''
26+
Only used to validate the generated config file. Set to `null` to
27+
disable the check phase.
28+
'';
29+
}
30+
// {
31+
# This is not above because mkPackageOption's implementation will evaluate `osConfig`, causing eval failures for documentation and checks.
32+
default = osConfig.services.xserver.windowManager.i3.package;
33+
defaultText = literalExpression "osConfig.services.xserver.windowManager.i3.package";
34+
};
35+
36+
commands = mkOption {
37+
type = lines;
38+
default = "";
39+
example = ''
40+
# Launch on startup
41+
exec --no-startup-id i3-msg 'workspace 1; exec ''${lib.getExe pkgs.firefox}'
42+
43+
# Key bindings - Workspaces
44+
''${lib.concatStrings (
45+
map (n: '''
46+
bindsym $mod+''${toString n} workspace number ''${if n == 0 then "10" else toString n}
47+
bindsym $mod+Shift+''${toString n} move container to workspace number ''${
48+
if n == 0 then "10" else toString n
49+
}
50+
''') (builtins.genList (i: i) 10)
51+
)}
52+
'';
53+
description = ''
54+
Commands that will be run to configure i3 written to
55+
{file}`$HOME/.config/i3/config`. Please reference [i3's documentation]
56+
for possible commands.
57+
58+
[i3's documentation]: https://i3wm.org/docs/userguide.html#configuring
59+
'';
60+
};
61+
62+
layouts = mkOption {
63+
type = attrsOf (listOf json.type);
64+
default = {};
65+
example = {
66+
main = [
67+
{
68+
layout = "splitv";
69+
percent = 0.4;
70+
type = "con";
71+
nodes = [
72+
{
73+
border = "none";
74+
name = "irssi";
75+
percent = 0.5;
76+
type = "con";
77+
swallows = [
78+
{
79+
class = "^URxvt$";
80+
instance = "^irssi$";
81+
}
82+
];
83+
}
84+
{
85+
layout = "stacked";
86+
percent = 0.5;
87+
type = "con";
88+
nodes = [
89+
{
90+
name = "notmuch";
91+
percent = 0.5;
92+
type = "con";
93+
swallows = [
94+
{
95+
class = "^Emacs$";
96+
instance = "^notmuch$";
97+
}
98+
];
99+
}
100+
{
101+
name = "midna: -";
102+
percent = 0.5;
103+
type = "con";
104+
}
105+
];
106+
}
107+
];
108+
}
109+
{
110+
layout = "stacked";
111+
percent = 0.6;
112+
type = "con";
113+
nodes = [
114+
{
115+
name = "chrome";
116+
type = "con";
117+
swallows = [
118+
{
119+
class = "^Google-chrome$";
120+
}
121+
];
122+
}
123+
];
124+
}
125+
];
126+
};
127+
description = ''
128+
Workspace layouts written to {file}`$HOME/.config/i3/*.json`.
129+
Read more about i3's layouts [here].
130+
131+
Setting this option doesn't automatically load the defined layouts
132+
allowing users to load them in their preferred way. Reference i3's
133+
documentation on [restoring the layout] for possible options.
134+
135+
[here]: https://i3wm.org/docs/layout-saving.html
136+
[restoring the layout]: https://i3wm.org/docs/layout-saving.html#_restoring_the_layout
137+
'';
138+
};
139+
};
140+
141+
config = mkIf cfg.enable {
142+
xdg.config.files =
143+
{
144+
"i3/config" = mkIf (cfg.commands != "") {
145+
source = pkgs.writeTextFile {
146+
name = "i3-config";
147+
text = cfg.commands;
148+
checkPhase = optionalString (cfg.package != null) ''
149+
${getExe cfg.package} -c "$target" -C -d all
150+
'';
151+
};
152+
};
153+
}
154+
// (mapAttrs' (name: value:
155+
nameValuePair "i3/${name}.json" {
156+
source = json.generate "i3-${name}-layout.json" value;
157+
})
158+
cfg.layouts);
159+
};
160+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
lib,
3+
pkgs,
4+
...
5+
}: {
6+
name = "desktops-i3";
7+
nodes.machine = {
8+
hjem.users.bob.rum = {
9+
desktops.i3 = {
10+
enable = true;
11+
commands = ''
12+
# Launch on startup
13+
exec --no-startup-id i3-msg 'workspace 1; exec ${lib.getExe pkgs.firefox}'
14+
15+
# Key bindings - Workspaces
16+
${lib.concatStrings (
17+
map (n: ''
18+
bindsym $mod+${toString n} workspace number ${
19+
if n == 0
20+
then "10"
21+
else toString n
22+
}
23+
bindsym $mod+Shift+${toString n} move container to workspace number ${
24+
if n == 0
25+
then "10"
26+
else toString n
27+
}
28+
'') (builtins.genList (i: i) 10)
29+
)}
30+
'';
31+
layouts.main = [
32+
{
33+
layout = "stacked";
34+
percent = 0.6;
35+
type = "con";
36+
nodes = [
37+
{
38+
name = "chrome";
39+
type = "con";
40+
swallows = [
41+
{
42+
class = "^Google-chrome$";
43+
}
44+
];
45+
}
46+
];
47+
}
48+
];
49+
};
50+
};
51+
};
52+
53+
testScript = ''
54+
# Waiting for our user to load.
55+
machine.succeed("loginctl enable-linger bob")
56+
machine.wait_for_unit("default.target")
57+
58+
# Assert that the i3 config is valid
59+
machine.succeed("${lib.getExe pkgs.i3} -c %s -C -d all" % "/home/bob/.config/i3/config")
60+
61+
# Check if the main layout file exists in the expected place.
62+
machine.succeed("[ -r %s ]" % "/home/bob/.config/i3/main.json")
63+
'';
64+
}

0 commit comments

Comments
 (0)