oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
15,540 bytes raw
1
{self}: {
2
  config,
3
  lib,
4
  pkgs,
5
  ...
6
}: let
7
  inherit (lib) mkEnableOption mkOption mkIf types concatMapStringsSep concatStringsSep concatMapStrings boolToString optional;
8
  cfg = config.programs.oxwm.settings;
9
10
  # Converts a nix submodule into a single oxwm bar block
11
  blockToLua = block: let
12
    common = ''
13
      interval = ${toString block.interval},
14
      color = "#${block.color}",
15
      underline = ${boolToString block.underline},'';
16
  in
17
    "oxwm.bar.block.${block.kind}({\n"
18
    + (
19
      if block.kind == "static"
20
      then ''
21
        text = "${block.text}",
22
        ${common}
23
      ''
24
      else if block.kind == "shell"
25
      then ''
26
        format = "${block.format}",
27
        command = "${block.command}",
28
        ${common}
29
      ''
30
      else if block.kind == "datetime"
31
      then ''
32
        format = "${block.format}",
33
        date_format = "${block.date_format}",
34
        ${common}
35
      ''
36
      else if block.kind == "battery"
37
      then ''
38
        format = "${block.format}",
39
        charging = "${block.charging}",
40
        discharging = "${block.discharging}",
41
        full = "${block.full}",
42
        ${common}
43
      ''
44
      else ''
45
        format = "${block.format}",
46
        ${common}
47
      ''
48
    )
49
    + "})";
50
51
  # Converts a nix submodule into a single oxwm window rule
52
  ruleToLua = rule: let
53
    fields = concatStringsSep ", " (
54
      optional (rule.match.class != null) ''class = "${rule.match.class}"''
55
      ++ optional (rule.match.instance != null) ''instance = "${rule.match.instance}"''
56
      ++ optional (rule.match.title != null) ''title = "${rule.match.title}"''
57
      ++ optional (rule.match.role != null) ''role = "${rule.match.role}"''
58
      ++ optional (rule.floating != null) ''floating = ${boolToString rule.floating}''
59
      ++ optional (rule.tag != null) ''tag = ${toString rule.tag}''
60
      ++ optional (rule.fullscreen != null) ''fullscreen = ${boolToString rule.fullscreen}''
61
      ++ optional (rule.focus != null) ''focus = ${boolToString rule.fullscreen}''
62
    );
63
  in "oxwm.rule.add({ ${fields} })";
64
in {
65
  options.programs.oxwm = {
66
    enable = mkEnableOption "oxwm window manager";
67
    package = mkOption {
68
      type = types.package;
69
      default = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
70
      description = "The oxwm package to use";
71
    };
72
    extraSessionCommands = mkOption {
73
      type = types.lines;
74
      default = "";
75
      description = "Shell commands executed just before oxwm is started";
76
    };
77
    settings = {
78
      terminal = mkOption {
79
        type = types.str;
80
        default = "alacritty";
81
        description = "Terminal used";
82
      };
83
      modkey = mkOption {
84
        type = types.str;
85
        default = "Mod4";
86
        description = "Modifier key. Used for mouse dragging";
87
      };
88
      tags = mkOption {
89
        type = types.listOf types.str;
90
        default = ["1" "2" "3" "4" "5" "6" "7" "8" "9"];
91
        description = "Workspace tags";
92
        example = ["" "󰊯" "" "" "󰙯" "󱇤" "" "󱘶" "󰧮"];
93
      };
94
      layoutSymbol = {
95
        tiling = mkOption {
96
          type = types.str;
97
          default = "[T]";
98
          description = "Symbol in tiling mode";
99
        };
100
        normie = mkOption {
101
          type = types.str;
102
          default = "[F]";
103
          description = "Symbol in normie mode";
104
        };
105
        tabbed = mkOption {
106
          type = types.str;
107
          default = "[=]";
108
          description = "Symbol in tabbed mode";
109
        };
110
      };
111
      autostart = mkOption {
112
        type = types.listOf types.str;
113
        default = [];
114
        description = "A list of commands to run when oxwm starts";
115
      };
116
      binds = mkOption {
117
        type = types.listOf (types.submodule {
118
          options = {
119
            mods = mkOption {
120
              type = types.listOf types.str;
121
              default = ["${cfg.modkey}"];
122
              description = "The modifier keys to invoke the command";
123
            };
124
            key = mkOption {
125
              type = types.str;
126
              description = "The keystroke to invoke the command";
127
            };
128
            action = mkOption {
129
              type = types.str;
130
              description = "The command to invoke";
131
            };
132
          };
133
        });
134
        default = [];
135
        description = "The list of keybinds";
136
        example = ''          [
137
                    {
138
                      mods = [ "Mod4" "Shift" ];
139
                      key = "Slash";
140
                      action = "oxwm.show_keybinds()";
141
                    }
142
                    {
143
                      mods = [ "Mod4" ];
144
                      key = "D";
145
                      action = "oxwm.spawn({ "sh", "-c", "dmenu_run -l 10" })";
146
                    }
147
                  ];'';
148
      };
149
      chords = mkOption {
150
        type = types.listOf (types.submodule {
151
          options = {
152
            notes = mkOption {
153
              type = types.listOf (types.submodule {
154
                options = {
155
                  mods = mkOption {
156
                    type = types.listOf types.str;
157
                    default = [];
158
                  };
159
                  key = mkOption {type = types.str;};
160
                };
161
              });
162
            };
163
            action = mkOption {type = types.str;};
164
          };
165
        });
166
        default = [];
167
        description = "A list of key chords for OXWM to use";
168
        example = ''          [
169
                   {
170
                     notes = [
171
                       {
172
                         mods = [ "Mod4" ];
173
                         key = "Space";
174
                       }
175
                       {
176
                         mods = [];
177
                         key = "T";
178
                       }
179
                     ];
180
                     action = "oxwm.spawn_terminal()";
181
                   }
182
                 ];
183
        '';
184
      };
185
      border = {
186
        width = mkOption {
187
          type = types.int;
188
          default = 2;
189
          description = "Width of the window borders";
190
        };
191
        focusedColor = mkOption {
192
          type = types.str;
193
          default = "6dade3";
194
          description = "Color of the focused window";
195
        };
196
        unfocusedColor = mkOption {
197
          type = types.str;
198
          default = "bbbbbb";
199
          description = "Color of the unfocused window";
200
        };
201
      };
202
      gaps = {
203
        smart = mkOption {
204
          type = types.enum ["enabled" "disabled"];
205
          default = "enabled";
206
          description = "If enabled, removes border if single window in tag";
207
        };
208
        inner = mkOption {
209
          type = types.listOf types.int;
210
          default = [5 5];
211
          description = "Inner gaps [ horizontal vertical ] in pixels";
212
        };
213
        outer = mkOption {
214
          type = types.listOf types.int;
215
          default = [5 5];
216
          description = "Outer gaps [ horizontal vertical ] in pixels";
217
        };
218
      };
219
      bar = {
220
        font = mkOption {
221
          type = types.str;
222
          default = "monospace 10";
223
          description = "The font displayed on the bar";
224
        };
225
        hideVacantTags = mkOption {
226
          type = types.bool;
227
          default = false;
228
          description = "Whether to hide tags with no windows from the bar";
229
        };
230
        unoccupiedScheme = mkOption {
231
          type = types.listOf types.str;
232
          default = ["bbbbbb" "1a1b26" "444444"];
233
          description = "The colorscheme to use for unoccupied tags as hex colors. Do not put a `#` before each color.";
234
        };
235
        occupiedScheme = mkOption {
236
          type = types.listOf types.str;
237
          default = ["0db9d7" "1a1b26" "0db9d7"];
238
          description = "The colorscheme to use for occupied tags as hex colors. Do not put a `#` before each color.";
239
        };
240
        selectedScheme = mkOption {
241
          type = types.listOf types.str;
242
          default = ["0db9d7" "1a1b26" "ad8ee6"];
243
          description = "The colorscheme to use for selected tags as hex colors. Do not put a `#` before each color.";
244
        };
245
        urgentScheme = mkOption {
246
          type = types.listOf types.str;
247
          default = ["f7768e" "1a1b26" "f7768e"];
248
          description = "The colorscheme to use for tags with a window requesting attention as hex colors. Do not put a `#` before each color.";
249
        };
250
        blocks = mkOption {
251
          type = types.listOf (types.submodule {
252
            options = {
253
              kind = mkOption {
254
                type = types.enum ["ram" "static" "shell" "datetime" "battery"];
255
                default = "static";
256
                description = "The kind of block to be used";
257
              };
258
              interval = mkOption {
259
                type = types.int;
260
                default = 5;
261
              };
262
              color = mkOption {
263
                type = types.str;
264
                default = "";
265
              };
266
              underline = mkOption {
267
                type = types.bool;
268
                default = true;
269
              };
270
              text = mkOption {
271
                type = types.str;
272
                default = "|";
273
              };
274
              format = mkOption {
275
                type = types.str;
276
                default = "{}";
277
              };
278
              command = mkOption {
279
                type = types.str;
280
                default = "uname -r";
281
              };
282
              date_format = mkOption {
283
                type = types.str;
284
                default = "%a, %b %d - %-I:%M %P";
285
              };
286
              charging = mkOption {
287
                type = types.str;
288
                default = "⚡ Bat: {}%";
289
              };
290
              discharging = mkOption {
291
                type = types.str;
292
                default = "- Bat: {}%";
293
              };
294
              full = mkOption {
295
                type = types.str;
296
                default = "✓ Bat: {}%";
297
              };
298
            };
299
          });
300
          description = "The modules to put on the bar";
301
          example = ''            [
302
                       {
303
                         kind = "ram";
304
                         interval = 5;
305
                         format = "Ram: {used}/{total} GB";
306
                         color = "9ece6a";
307
                       }
308
                       {
309
                         kind = "static";
310
                         text = "|";
311
                         interval = 99999999;
312
                         color = "6dade3";
313
                       }
314
                       {
315
                         kind = "shell";
316
                         format = "{}";
317
                         command = "uname -r";
318
                         interval = 9999999;
319
                         color = "f7768e";
320
                         underline = true;
321
                       }
322
                     ];'';
323
        };
324
      };
325
      rules = mkOption {
326
        type = types.listOf (types.submodule {
327
          options = {
328
            match = {
329
              class = mkOption {
330
                type = types.nullOr types.str;
331
                default = null;
332
                description = "The class to match windows with";
333
              };
334
              instance = mkOption {
335
                type = types.nullOr types.str;
336
                default = null;
337
                description = "The instance to match windows with";
338
              };
339
              title = mkOption {
340
                type = types.nullOr types.str;
341
                default = null;
342
                description = "The title to match windows with";
343
              };
344
              role = mkOption {
345
                type = types.nullOr types.str;
346
                default = null;
347
                description = "The role to match windows with";
348
              };
349
            };
350
            floating = mkOption {
351
              type = types.nullOr types.bool;
352
              default = null;
353
              description = "Whether to apply floating to the matched window";
354
            };
355
            tag = mkOption {
356
              type = types.nullOr types.int;
357
              default = null;
358
              description = "What tag the matched window should be opened on";
359
            };
360
            fullscreen = mkOption {
361
              type = types.nullOr types.bool;
362
              default = null;
363
              description = "Whether to apply fullscreen to the matched window";
364
            };
365
            focus = mkOption {
366
              type = types.nullOr types.bool;
367
              default = null;
368
              description = "Whether to apply focus to the matched window";
369
            };
370
          };
371
        });
372
        description = "A list of window rules for the window manager to follow";
373
        example = ''          [
374
                   {
375
                     match.class = "gimp";
376
                     floating = true;
377
                   }
378
                   {
379
                     match.class = "firefox";
380
                     match.title = "Library";
381
                     tag = 9;
382
                     focus = true;
383
                   }
384
                 ];'';
385
      };
386
      extraConfig = mkOption {
387
        type = types.lines;
388
        default = "";
389
        description = "Extra lua confguration that gets inserted at the bottom of the file";
390
      };
391
    };
392
  };
393
394
  config = mkIf config.programs.oxwm.enable {
395
    xdg.configFile."oxwm/config.lua".text = ''
396
      -- @meta
397
      -- @module 'oxwm'
398
399
      oxwm.set_terminal("${cfg.terminal}")
400
      oxwm.set_modkey("${cfg.modkey}")
401
      oxwm.set_tags({${concatMapStringsSep ", " (t: ''"${t}"'') cfg.tags}})
402
403
      local blocks = {
404
        ${concatMapStringsSep ",\n" blockToLua cfg.bar.blocks}
405
      };
406
      oxwm.bar.set_blocks(blocks)
407
      oxwm.bar.set_font("${cfg.bar.font}")
408
      oxwm.bar.set_scheme_normal(${concatMapStringsSep ", " (c: ''"#${c}"'') cfg.bar.unoccupiedScheme})
409
      oxwm.bar.set_scheme_occupied(${concatMapStringsSep ", " (c: ''"#${c}"'') cfg.bar.occupiedScheme})
410
      oxwm.bar.set_scheme_selected(${concatMapStringsSep ", " (c: ''"#${c}"'') cfg.bar.selectedScheme})
411
      oxwm.bar.set_scheme_urgent(${concatMapStringsSep ", " (c: ''"#${c}"'') cfg.bar.urgentScheme})
412
      oxwm.bar.set_hide_vacant_tags(${boolToString cfg.bar.hideVacantTags})
413
414
      oxwm.border.set_width(${toString cfg.border.width})
415
      oxwm.border.set_focused_color("#${cfg.border.focusedColor}")
416
      oxwm.border.set_unfocused_color("#${cfg.border.unfocusedColor}")
417
418
      oxwm.gaps.set_smart(${cfg.gaps.smart})
419
      oxwm.gaps.set_inner(${concatMapStringsSep ", " toString cfg.gaps.inner})
420
      oxwm.gaps.set_outer(${concatMapStringsSep ", " toString cfg.gaps.outer})
421
422
      oxwm.set_layout_symbol("tiling", "${cfg.layoutSymbol.tiling}")
423
      oxwm.set_layout_symbol("normie", "${cfg.layoutSymbol.normie}")
424
      oxwm.set_layout_symbol("tabbed", "${cfg.layoutSymbol.tabbed}")
425
426
      ${concatMapStrings (cmd: ''
427
          oxwm.autostart("${cmd}")
428
        '')
429
        cfg.autostart}
430
      ${concatMapStrings (bind: ''
431
          oxwm.key.bind({ ${concatMapStringsSep ", " (m: ''"${m}"'') bind.mods} }, "${bind.key}", ${bind.action})
432
        '')
433
        cfg.binds}
434
      ${concatMapStrings (chord: ''
435
          oxwm.key.chord({
436
            ${concatMapStringsSep ",\n  " (
437
              note: ''{ { ${concatMapStringsSep ", " (m: ''"${m}"'') note.mods} }, "${note.key}" }''
438
            )
439
            chord.notes}
440
          }, ${chord.action})
441
        '')
442
        cfg.chords}
443
      ${concatMapStrings (rule: ''
444
          ${ruleToLua rule}
445
        '')
446
        cfg.rules}
447
      ${cfg.extraConfig}
448
    '';
449
  };
450
}