summaryrefslogtreecommitdiff
path: root/modules/home-manager
diff options
context:
space:
mode:
authoraristote <quentin.aristote@irif.fr>2025-07-30 00:13:04 +0200
committeraristote <quentin.aristote@irif.fr>2025-07-30 00:13:04 +0200
commit9c088d044873ed84a8ae411e815c41ec558eed29 (patch)
tree65d0bc0af6813e5d6c3ec7f85d65cffdc9f20bbd /modules/home-manager
parent07cb81a1d95e15e7c864dcfd912fc2a22d65d115 (diff)
parent414e9b5ae2c3a565c5826027c7e1f775cdafc32a (diff)
Merge branch 'i3bar'
Diffstat (limited to 'modules/home-manager')
-rw-r--r--modules/home-manager/personal/gui/x/i3/bar.nix101
-rw-r--r--modules/home-manager/personal/gui/x/i3/bar/default.nix32
-rw-r--r--modules/home-manager/personal/gui/x/i3/bar/i3status.go281
-rw-r--r--modules/home-manager/personal/gui/x/i3/default.nix2
4 files changed, 102 insertions, 314 deletions
diff --git a/modules/home-manager/personal/gui/x/i3/bar.nix b/modules/home-manager/personal/gui/x/i3/bar.nix
new file mode 100644
index 0000000..2389a8a
--- /dev/null
+++ b/modules/home-manager/personal/gui/x/i3/bar.nix
@@ -0,0 +1,101 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.personal.x.i3;
+ cfgStylix = config.stylix.targets.i3;
+ mkDeviceOption = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ };
+ ifDevice = device: attrs: lib.optional (device != null) ({ inherit device; } // attrs);
+in
+{
+ options.personal.x.i3 = {
+ devices = {
+ wifi = mkDeviceOption;
+ eth = mkDeviceOption;
+ vpn = mkDeviceOption // {
+ default = "tun0";
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ xsession.windowManager.i3.config.bars = [
+ {
+ statusCommand = "${pkgs.i3status-rust}/bin/i3status-rs ~/.config/i3status-rust/config-default.toml";
+ fonts = { inherit (cfgStylix.exportedBarConfig.fonts) size; };
+ }
+ ];
+
+ programs.i3status-rust = {
+ enable = true;
+ bars = {
+ default = {
+ icons = "material-nf";
+ blocks =
+ (ifDevice cfg.devices.wifi {
+ block = "net";
+ device = cfg.devices.wifi;
+ format = " ^icon_net_wireless $ssid ";
+ })
+ ++ (ifDevice cfg.devices.eth {
+ block = "net";
+ device = cfg.devices.eth;
+ format = " ^icon_net_wired $ip ";
+ inactive_format = " ^icon_net_wired × ";
+ missing_format = " ^icon_net_wired × ";
+ })
+ ++ (ifDevice cfg.devices.vpn {
+ block = "net";
+ device = cfg.devices.vpn;
+ format = " ^icon_net_vpn $ip ";
+ inactive_format = " ^icon_net_vpn × ";
+ missing_format = " ^icon_net_vpn × ";
+ })
+ ++ [
+ {
+ block = "disk_space";
+ info_type = "used";
+ path = "/";
+ warning = 50.0;
+ alert = 90.0;
+ format = " $icon $percentage ";
+ }
+ (
+ let
+ format = " $icon $percentage ";
+ in
+ {
+ block = "battery";
+ inherit format;
+ full_format = format;
+ charging_format = format;
+ empty_format = format;
+ not_charging_format = format;
+ }
+ )
+ {
+ block = "backlight";
+ }
+ {
+ block = "sound";
+ }
+ {
+ block = "sound";
+ }
+ {
+ block = "time";
+ interval = 1;
+ format = " $timestamp.datetime(f:'%a. %b. %d, %H:%M:%S') ";
+ }
+ ];
+ };
+ };
+ };
+ };
+}
diff --git a/modules/home-manager/personal/gui/x/i3/bar/default.nix b/modules/home-manager/personal/gui/x/i3/bar/default.nix
deleted file mode 100644
index 132e850..0000000
--- a/modules/home-manager/personal/gui/x/i3/bar/default.nix
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
-let
- statusPackage = pkgs.personal.barista.override { i3statusGo = ./i3status.go; };
-in
-{
- xsession.windowManager.i3.config.bars = [
- (
- {
- statusCommand = "${statusPackage}/bin/i3status";
- }
- // (config.lib.stylix.i3.targets.i3.exportedBarConfig or { colors.background = "#111111"; })
- // {
- fonts = {
- names = [ "roboto" ];
- size = 11.0;
- };
- }
- )
- ];
-
- home.packages =
- with pkgs;
- lib.optionals (config.xsession.enable && config.xsession.windowManager.i3.enable) [
- material-design-icons
- roboto
- ];
-}
diff --git a/modules/home-manager/personal/gui/x/i3/bar/i3status.go b/modules/home-manager/personal/gui/x/i3/bar/i3status.go
deleted file mode 100644
index 4e1b0f5..0000000
--- a/modules/home-manager/personal/gui/x/i3/bar/i3status.go
+++ /dev/null
@@ -1,281 +0,0 @@
-package main
-
-import (
- "io"
- "net/http"
- "os"
- "regexp"
- "strconv"
- "time"
-
- "barista.run"
- "barista.run/bar"
- "barista.run/base/click"
- "barista.run/colors"
- "barista.run/group"
- "barista.run/modules/battery"
- "barista.run/modules/clock"
- "barista.run/modules/diskspace"
- "barista.run/modules/funcs"
- "barista.run/modules/netinfo"
- "barista.run/modules/systemd"
- "barista.run/modules/volume"
- "barista.run/modules/volume/pulseaudio"
- "barista.run/modules/wlan"
- "barista.run/outputs"
- "barista.run/pango"
- "barista.run/pango/icons/mdi"
-)
-
-func main() {
- // Constants
- colors.LoadFromMap(map[string]string{
- // Color palette of Cezanne's Vue de la Baie de Marseille
- "good": "#C5D294",
- "degraded": "#E9CC67",
- "bad": "#FFBC88",
- })
- mdi.Load() // repo path will be inserted at build time
-
- // Display status of several services
- outputServiceStatus := func(goodState systemd.State, degradedState systemd.State, goodIcon *pango.Node, degradedIcon *pango.Node, badIcon *pango.Node) func(systemd.ServiceInfo) bar.Output {
- return func(i systemd.ServiceInfo) bar.Output {
- state := i.UnitInfo.State
- var colorScheme string
- var output *pango.Node
- switch {
- case state == goodState:
- colorScheme = "good"
- output = goodIcon
- case state == degradedState:
- colorScheme = "degraded"
- output = degradedIcon
- default:
- colorScheme = "bad"
- output = badIcon
- }
- return outputs.Pango(output).Color(colors.Scheme(colorScheme)).OnClick(click.Right(func() {
- i.Restart()
- }))
- }
- }
- updateSuccessIcon := pango.Icon("mdi-reload")
- updatingIcon := pango.Icon("mdi-update")
- updateFailIcon := pango.Icon("mdi-reload-alert")
- garbageFullIcon := pango.Icon("mdi-delete")
- garbageEmptyingIcon := pango.Icon("mdi-delete-restore")
- garbageEmptyIcon := pango.Icon("mdi-delete-outline")
- barista.Add(group.Simple(systemd.Service("nixos-upgrade").Output(outputServiceStatus(systemd.StateInactive, systemd.StateActivating, updateSuccessIcon, updatingIcon, updateFailIcon)),
- systemd.Service("nix-gc").Output(outputServiceStatus(systemd.StateInactive, systemd.StateActivating, garbageEmptyIcon, garbageEmptyingIcon, garbageFullIcon))))
- emacsIcon := pango.Icon("mdi-alpha-e-circle")
- barista.Add(systemd.UserService("emacs").Output(outputServiceStatus(systemd.StateActive, systemd.StateActivating, emacsIcon, emacsIcon, emacsIcon)))
-
- // Display space left on /
- storageIcon := pango.Icon("mdi-database")
- barista.Add(diskspace.New("/").Output(func(i diskspace.Info) bar.Output {
- used := i.UsedPct()
- var colorScheme string
- if used >= 90 {
- colorScheme = "bad"
- } else if used >= 50 {
- colorScheme = "degraded"
- } else {
- colorScheme = "good"
- }
- return outputs.Pango(storageIcon, pango.Textf(" %d%%", used)).Color(colors.Scheme(colorScheme))
- }))
-
- // Check connection to the Mullvad VPN
- mullvadIsUpRe := regexp.MustCompile(`^You are connected to Mullvad`)
- mullvadServerRe := regexp.MustCompile(`\(server (.*)\)`)
- mullvadIpRe := regexp.MustCompile(`Your IP address is (.*)`)
- client := &http.Client{Timeout: 3 * time.Second}
- incognitoIcon := pango.Icon("mdi-incognito")
- incognitoOffIcon := pango.Icon("mdi-incognito-off")
- barista.Add(funcs.Every(5*time.Second, func(s bar.Sink) {
- icon := incognitoOffIcon
- message := pango.Text("")
- colorScheme := "bad"
- res, err := client.Get("https://am.i.mullvad.net/connected")
- if !s.Error(err) {
- status, err := io.ReadAll(res.Body)
- res.Body.Close()
- if !s.Error(err) {
- var re *regexp.Regexp
- if mullvadIsUpRe.Match(status) {
- re = mullvadServerRe
- colorScheme = "good"
- icon = incognitoIcon
- } else {
- re = mullvadIpRe
- colorScheme = "degraded"
- }
- result := re.FindSubmatch(status)
- if len(result) >= 2 {
- message = pango.Textf(" %s", result[1])
- }
- }
- }
- client.CloseIdleConnections()
- s.Output(outputs.Pango(icon, message).Color(colors.Scheme(colorScheme)))
- }))
-
- // Display the wifi status
- wifiOffIcon := pango.Icon("mdi-wifi-off")
- wifiRefreshIcon := pango.Icon("mdi-wifi-refresh")
- wifiOnIcon := pango.Icon("mdi-wifi")
- barista.Add(wlan.Any().Output(func(w wlan.Info) bar.Output {
- var output *pango.Node
- var colorScheme string
- switch {
- case w.Connected():
- output = pango.New(wifiOnIcon, pango.Textf(" %s", w.SSID))
- colorScheme = "good"
- case w.Connecting():
- output = wifiRefreshIcon
- colorScheme = "degraded"
- default:
- output = wifiOffIcon
- colorScheme = "bad"
- }
- return outputs.Pango(output).Color(colors.Scheme(colorScheme))
- }))
-
- // Display the ethernet status
- ethernetCableOnIcon := pango.Icon("mdi-ethernet-cable")
- ethernetCableOffIcon := pango.Icon("mdi-ethernet-cable-off")
- barista.Add(netinfo.Prefix("e").Output(func(s netinfo.State) bar.Output {
- var output *pango.Node
- var colorScheme string
- switch {
- case s.Connected():
- ip := "<no ip>"
- if len(s.IPs) > 0 {
- ip = s.IPs[0].String()
- }
- output = pango.New(ethernetCableOnIcon, pango.Textf(" %s", ip))
- colorScheme = "good"
- case s.Connecting():
- output = ethernetCableOnIcon
- colorScheme = "degraded"
- default:
- output = ethernetCableOffIcon
- colorScheme = "bad"
- }
- return outputs.Pango(output).Color(colors.Scheme(colorScheme))
- }))
-
- // Display the battery status
- batteryIcons := [11]*pango.Node{pango.Icon("mdi-battery-outline"),
- pango.Icon("mdi-battery-10"),
- pango.Icon("mdi-battery-20"),
- pango.Icon("mdi-battery-30"),
- pango.Icon("mdi-battery-40"),
- pango.Icon("mdi-battery-50"),
- pango.Icon("mdi-battery-60"),
- pango.Icon("mdi-battery-70"),
- pango.Icon("mdi-battery-80"),
- pango.Icon("mdi-battery-90"),
- pango.Icon("mdi-battery")}
- batteryChargingIcons := [11]*pango.Node{pango.Icon("mdi-battery-charging-outline"),
- pango.Icon("mdi-battery-charging-10"),
- pango.Icon("mdi-battery-charging-20"),
- pango.Icon("mdi-battery-charging-30"),
- pango.Icon("mdi-battery-charging-40"),
- pango.Icon("mdi-battery-charging-50"),
- pango.Icon("mdi-battery-charging-60"),
- pango.Icon("mdi-battery-charging-70"),
- pango.Icon("mdi-battery-charging-80"),
- pango.Icon("mdi-battery-charging-90"),
- pango.Icon("mdi-battery-charging-100")}
- barista.Add(battery.All().Output(func(b battery.Info) bar.Output {
- switch b.Status {
- case battery.Disconnected, battery.Unknown:
- return nil
- default:
- var icons [11]*pango.Node
- var colorScheme string
- if b.Status == battery.Charging {
- icons = batteryChargingIcons
- colorScheme = "good"
- } else {
- icons = batteryIcons
- if b.RemainingPct() <= 10 {
- colorScheme = "bad"
- } else if b.RemainingPct() <= 20 {
- colorScheme = "degraded"
- } else {
- colorScheme = "good"
- }
- }
- icon := icons[b.RemainingPct()/10]
- return outputs.Pango(icon, pango.Textf(" %d%%", b.RemainingPct())).Color(colors.Scheme(colorScheme))
- }
- }))
-
- // Display brightness
- brightnessHighIcon := pango.Icon("mdi-lightbulb-on")
- brightnessMidIcon := pango.Icon("mdi-lightbulb-on-outline")
- brightnessLowIcon := pango.Icon("mdi-lightbulb-outline")
- ReadBrightness := func(name string) (int, error) {
- valueStr, err := os.ReadFile("/sys/class/backlight/intel_backlight/" + name)
- if err != nil {
- return 0, err
- }
- return strconv.Atoi(string(valueStr[:len(valueStr)-1]))
- }
- brightnessMax, _ := ReadBrightness("max_brightness") // always non-zero, unless there's an error
- barista.Add(funcs.Every(time.Second, func(s bar.Sink) {
- brightness, err := ReadBrightness("brightness")
- if !s.Error(err) {
- value := (brightness * 100) / brightnessMax
- var icon *pango.Node
- if value <= 30 {
- icon = brightnessLowIcon
- } else if value < 70 {
- icon = brightnessMidIcon
- } else {
- icon = brightnessHighIcon
- }
- s.Output(outputs.Pango(icon, pango.Textf(" %d%%", value)))
- }
- }))
-
- // Display output volume
- volumeOffIcon := pango.Icon("mdi-volume-variant-off")
- volumeLowIcon := pango.Icon("mdi-volume-low")
- volumeMidIcon := pango.Icon("mdi-volume-medium")
- volumeHighIcon := pango.Icon("mdi-volume-high")
- barista.Add(volume.New(pulseaudio.DefaultSink()).Output(func(v volume.Volume) bar.Output {
- volume := v.Pct()
- var icon *pango.Node
- if volume == 0 || v.Mute {
- icon = volumeOffIcon
- } else if volume <= 30 {
- icon = volumeLowIcon
- } else if volume <= 70 {
- icon = volumeMidIcon
- } else {
- icon = volumeHighIcon
- }
- return outputs.Pango(icon, pango.Textf(" %d%%", volume))
- }))
-
- // Display microphone volume
- microphoneOffIcon := pango.Icon("mdi-microphone-off")
- microphoneIcon := pango.Icon("mdi-microphone")
- barista.Add(volume.New(pulseaudio.DefaultSource()).Output(func(v volume.Volume) bar.Output {
- volume := v.Pct() // the value returned by pulseaudio may be weird
- var icon *pango.Node
- if volume == 0 || v.Mute {
- icon = microphoneOffIcon
- } else {
- icon = microphoneIcon
- }
- return outputs.Pango(icon, pango.Textf(" %d%%", volume))
- }))
-
- barista.Add(clock.Local().OutputFormat("2006-01-02 15:04:05"))
-
- panic(barista.Run())
-}
diff --git a/modules/home-manager/personal/gui/x/i3/default.nix b/modules/home-manager/personal/gui/x/i3/default.nix
index 483a0d4..657affc 100644
--- a/modules/home-manager/personal/gui/x/i3/default.nix
+++ b/modules/home-manager/personal/gui/x/i3/default.nix
@@ -9,7 +9,7 @@ let
in
{
imports = [
- ./bar
+ ./bar.nix
./keybindings.nix
./startup.nix
];