about summary refs log tree commit diff stats
path: root/pkgs/by-name/i3
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/i3')
-rw-r--r--pkgs/by-name/i3/i3bar-river-patched/0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch110
-rw-r--r--pkgs/by-name/i3/i3bar-river-patched/package.nix54
-rw-r--r--pkgs/by-name/i3/i3status-rust-patched/package.nix22
-rw-r--r--pkgs/by-name/i3/i3status-rust-patched/patches/0001-disk_space-Support-btrfs-backend.patch190
4 files changed, 376 insertions, 0 deletions
diff --git a/pkgs/by-name/i3/i3bar-river-patched/0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch b/pkgs/by-name/i3/i3bar-river-patched/0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch
new file mode 100644
index 00000000..7bfdd7bc
--- /dev/null
+++ b/pkgs/by-name/i3/i3bar-river-patched/0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch
@@ -0,0 +1,110 @@
+From 8ae692a461fad2f23231d50b78bb706408facfe6 Mon Sep 17 00:00:00 2001
+From: Benedikt Peetz <benedikt.peetz@b-peetz.de>
+Date: Tue, 20 May 2025 19:58:57 +0200
+Subject: [PATCH] feat(crate::bar): Put the leftmost block in the middle of the
+ bar
+
+This is a workaround for the limitation in the i3 blocks protocol, as
+this does not allow for centred blocks.
+---
+ src/bar.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 52 insertions(+), 11 deletions(-)
+
+diff --git a/src/bar.rs b/src/bar.rs
+index 96533e3..76f8025 100644
+--- a/src/bar.rs
++++ b/src/bar.rs
+@@ -338,16 +338,55 @@ impl Bar {
+         }
+ 
+         // Display the blocks
+-        render_blocks(
+-            &cairo_ctx,
+-            &ss.config,
+-            palette,
+-            ss.blocks_cache.get_computed(),
+-            &mut self.blocks_btns,
+-            offset_left,
+-            width_f,
+-            height_f,
+-        );
++        {
++            if !ss.blocks_cache.get_computed().is_empty() {
++                let first_block = &ss.blocks_cache.get_computed()[0];
++
++                let blocks = &ss.blocks_cache.get_computed()[1..];
++
++                let other_start = render_blocks(
++                    &cairo_ctx,
++                    &ss.config,
++                    palette,
++                    blocks,
++                    &mut self.blocks_btns,
++                    offset_left,
++                    width_f,
++                    height_f,
++                );
++
++                // Draw the first block _after_ the other ones, so that we can nudge it more to the
++                // left, if the others are spanning over the middle.
++                let mut start = (width_f / 2.0) - (first_block.full.width / 2.0);
++                if start + first_block.full.width > other_start {
++                    start = other_start
++                        - first_block.full.width
++                        - first_block.block.separator_block_width as f64;
++                }
++
++                first_block.full.render(
++                    &cairo_ctx,
++                    RenderOptions {
++                        x_offset: start,
++                        bar_height: height_f,
++                        fg_color: first_block.block.color.unwrap_or(palette.color),
++                        bg_color: first_block.block.background,
++                        r_left: ss.config.blocks_r,
++                        r_right: ss.config.blocks_r,
++                        overlap: ss.config.blocks_overlap,
++                    },
++                );
++
++                self.blocks_btns.push(
++                    start,
++                    first_block.full.width,
++                    (
++                        first_block.block.name.clone(),
++                        first_block.block.instance.clone(),
++                    ),
++                );
++            }
++        }
+ 
+         self.viewport
+             .set_destination(conn, self.width as i32, self.height as i32);
+@@ -422,7 +461,7 @@ fn render_blocks(
+     offset_left: f64,
+     full_width: f64,
+     full_height: f64,
+-) {
++) -> f64 {
+     context.rectangle(offset_left, 0.0, full_width - offset_left, full_height);
+     context.clip();
+ 
+@@ -507,6 +546,7 @@ fn render_blocks(
+     }
+ 
+     // Render blocks
++    let leftmost_start = full_width - blocks_width;
+     buttons.clear();
+     for series in blocks_computed {
+         let s_len = series.blocks.len();
+@@ -550,6 +590,7 @@ fn render_blocks(
+     }
+ 
+     context.reset_clip();
++    leftmost_start
+ }
+ 
+ fn layer_surface_cb(ctx: EventCtx<State, ZwlrLayerSurfaceV1>) {
+-- 
+2.49.0
+
diff --git a/pkgs/by-name/i3/i3bar-river-patched/package.nix b/pkgs/by-name/i3/i3bar-river-patched/package.nix
new file mode 100644
index 00000000..26f11ab3
--- /dev/null
+++ b/pkgs/by-name/i3/i3bar-river-patched/package.nix
@@ -0,0 +1,54 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  lib,
+  fetchFromGitHub,
+  rustPlatform,
+  pkg-config,
+  pango,
+}:
+rustPlatform.buildRustPackage {
+  pname = "i3bar-river-patched";
+  version = "1.1.0-unstable-2025-05-20";
+
+  src = fetchFromGitHub {
+    owner = "bpeetz";
+    repo = "i3bar-river";
+    rev = "d460a9a283426e9474a0034a146d09816e92f571";
+    hash = "sha256-E04b2FzEhOX5NyE/VpEGdg27Sg+1+lSSRZbGyX6PXrk=";
+  };
+
+  cargoHash = "sha256-jIB4XH67FmtPxAatHkuW8v5mNgr/KsyriaBNZ5t2dLo=";
+
+  cargoPatches = [
+    # TODO(@bpeetz): Open an issues, whether something like that could be up-streamed. <2025-05-20>
+    ./0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch
+  ];
+
+  # Remove the WMs that I don't use.
+  buildNoDefaultFeatures = true;
+  buildFeatures = [
+    # "hyprland"
+    # "niri"
+    "river"
+  ];
+
+  nativeBuildInputs = [pkg-config];
+  buildInputs = [pango];
+
+  meta = with lib; {
+    description = "Port of i3bar for river";
+    homepage = "https://github.com/MaxVerevkin/i3bar-river";
+    license = licenses.gpl3Only;
+    maintainers = with maintainers; [nicegamer7];
+    mainProgram = "i3bar-river";
+    platforms = platforms.linux;
+  };
+}
diff --git a/pkgs/by-name/i3/i3status-rust-patched/package.nix b/pkgs/by-name/i3/i3status-rust-patched/package.nix
new file mode 100644
index 00000000..a103e275
--- /dev/null
+++ b/pkgs/by-name/i3/i3status-rust-patched/package.nix
@@ -0,0 +1,22 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  i3status-rust,
+}:
+i3status-rust.overrideAttrs (final: prev: {
+  pname = "${prev.pname}-patched";
+
+  patches =
+    (prev.patches or [])
+    ++ [
+      # Btrfs support for disk_space block.
+      ./patches/0001-disk_space-Support-btrfs-backend.patch
+    ];
+})
diff --git a/pkgs/by-name/i3/i3status-rust-patched/patches/0001-disk_space-Support-btrfs-backend.patch b/pkgs/by-name/i3/i3status-rust-patched/patches/0001-disk_space-Support-btrfs-backend.patch
new file mode 100644
index 00000000..8ef0af2e
--- /dev/null
+++ b/pkgs/by-name/i3/i3status-rust-patched/patches/0001-disk_space-Support-btrfs-backend.patch
@@ -0,0 +1,190 @@
+From 78d2936b67064e3b5e700a2859d00ea3dd6eda4c Mon Sep 17 00:00:00 2001
+From: Benedikt Peetz <benedikt.peetz@b-peetz.de>
+Date: Sun, 18 May 2025 20:22:04 +0200
+Subject: [PATCH 1/3] disk_space: Support btrfs backend
+
+Btrfs is too smart for the statvfs based backend (i.e., only counting
+blocks leads to wrong numbers).
+
+For example, a btrfs disk with a lot of de-duplicated blocks (via the copy
+on write mechanism) might have a drastically over-reported disk usage.
+
+The btrfs backend is currently implemented by parsing the output of the
+`btrfs filesystem usage --raw` command. This is suboptimal, as this now
+relies on the command output not changing.
+
+Vendoring the algorithm used internally by the `btrfs` command does not
+seem to be a reasonable alternative, considering that the code[1] is
+rather complex, low level and would require semi-constant maintenance.
+Additionally, the c code would need bindings to be usable from rust.
+
+I assume, that the `btrfs` command output will stay rather similar in
+the future, as a lot of tools rely on directly parsing it (see the
+various scripts in the issue, this commit fixes).
+
+[1]: https://github.com/kdave/btrfs-progs/blob/eeab081e9d9fbdf4583122ed1caedf541383cf2d/cmds/filesystem-usage.c#L442
+
+Fixes: #1654
+---
+ src/blocks/disk_space.rs | 112 +++++++++++++++++++++++++++++++++++----
+ 1 file changed, 101 insertions(+), 11 deletions(-)
+
+diff --git a/src/blocks/disk_space.rs b/src/blocks/disk_space.rs
+index 79bfebd27..da0d3f518 100644
+--- a/src/blocks/disk_space.rs
++++ b/src/blocks/disk_space.rs
+@@ -12,6 +12,7 @@
+ //! `alert` | A value which will trigger critical block state | `10.0`
+ //! `info_type` | Determines which information will affect the block state. Possible values are `"available"`, `"free"` and `"used"` | `"available"`
+ //! `alert_unit` | The unit of `alert` and `warning` options. If not set, percents are used. Possible values are `"B"`, `"KB"`, `"KiB"`, `"MB"`, `"MiB"`, `"GB"`, `"Gib"`, `"TB"` and `"TiB"` | `None`
++//! `backend` | The backend to use when querying disk usage. Possible values are `"vfs"` (like `du(1)`) and `"btrfs"` | `"vfs"`
+ //!
+ //! Placeholder  | Value                                                              | Type   | Unit
+ //! -------------|--------------------------------------------------------------------|--------|-------
+@@ -63,9 +64,12 @@
+ 
+ // make_log_macro!(debug, "disk_space");
+ 
++use std::cell::OnceCell;
++
+ use super::prelude::*;
+ use crate::formatting::prefix::Prefix;
+ use nix::sys::statvfs::statvfs;
++use tokio::process::Command;
+ 
+ #[derive(Copy, Clone, Debug, Deserialize, SmartDefault)]
+ #[serde(rename_all = "lowercase")]
+@@ -76,11 +80,20 @@ pub enum InfoType {
+     Used,
+ }
+ 
++#[derive(Copy, Clone, Debug, Deserialize, SmartDefault)]
++#[serde(rename_all = "lowercase")]
++pub enum Backend {
++    #[default]
++    Vfs,
++    Btrfs,
++}
++
+ #[derive(Deserialize, Debug, SmartDefault)]
+ #[serde(deny_unknown_fields, default)]
+ pub struct Config {
+     #[default("/".into())]
+     pub path: ShellString,
++    pub backend: Backend,
+     pub info_type: InfoType,
+     pub format: FormatConfig,
+     pub format_alt: Option<FormatConfig>,
+@@ -128,17 +141,9 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
+     loop {
+         let mut widget = Widget::new().with_format(format.clone());
+ 
+-        let statvfs = statvfs(&*path).error("failed to retrieve statvfs")?;
+-
+-        // Casting to be compatible with 32-bit systems
+-        #[allow(clippy::unnecessary_cast)]
+-        let (total, used, available, free) = {
+-            let total = (statvfs.blocks() as u64) * (statvfs.fragment_size() as u64);
+-            let used = ((statvfs.blocks() as u64) - (statvfs.blocks_free() as u64))
+-                * (statvfs.fragment_size() as u64);
+-            let available = (statvfs.blocks_available() as u64) * (statvfs.block_size() as u64);
+-            let free = (statvfs.blocks_free() as u64) * (statvfs.block_size() as u64);
+-            (total, used, available, free)
++        let (total, used, available, free) = match config.backend {
++            Backend::Vfs => get_vfs(&*path)?,
++            Backend::Btrfs => get_btrfs(&path).await?,
+         };
+ 
+         let result = match config.info_type {
+@@ -205,3 +210,88 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
+         }
+     }
+ }
++
++fn get_vfs<P>(path: &P) -> Result<(u64, u64, u64, u64)>
++where
++    P: ?Sized + nix::NixPath,
++{
++    let statvfs = statvfs(path).error("failed to retrieve statvfs")?;
++
++    // Casting to be compatible with 32-bit systems
++    #[allow(clippy::unnecessary_cast)]
++    {
++        let total = (statvfs.blocks() as u64) * (statvfs.fragment_size() as u64);
++        let used = ((statvfs.blocks() as u64) - (statvfs.blocks_free() as u64))
++            * (statvfs.fragment_size() as u64);
++        let available = (statvfs.blocks_available() as u64) * (statvfs.block_size() as u64);
++        let free = (statvfs.blocks_free() as u64) * (statvfs.block_size() as u64);
++
++        Ok((total, used, available, free))
++    }
++}
++
++async fn get_btrfs(path: &str) -> Result<(u64, u64, u64, u64)> {
++    const OUTPUT_CHANGED: &str = "Btrfs filesystem usage output format changed";
++
++    fn remove_estimate_min(estimate_str: &str) -> Result<&str> {
++        estimate_str.trim_matches('\t')
++            .split_once("\t")
++            .ok_or(Error::new(OUTPUT_CHANGED))
++            .map(|v| v.0)
++    }
++
++    macro_rules! get {
++        ($source:expr, $name:expr, $variable:ident) => {
++            get!(@pre_op (|a| {Ok::<_, Error>(a)}), $source, $name, $variable)
++        };
++        (@pre_op $function:expr, $source:expr, $name:expr, $variable:ident) => {
++            if $source.starts_with(concat!($name, ":")) {
++                let (found_name, variable_str) =
++                    $source.split_once(":").ok_or(Error::new(OUTPUT_CHANGED))?;
++
++                let variable_str = $function(variable_str)?;
++
++                debug_assert_eq!(found_name, $name);
++                $variable
++                    .set(variable_str.trim().parse().error(OUTPUT_CHANGED)?)
++                    .map_err(|_| Error::new(OUTPUT_CHANGED))?;
++            }
++        };
++    }
++
++    let filesystem_usage = Command::new("btrfs")
++        .args(["filesystem", "usage", "--raw", path])
++        .output()
++        .await
++        .error("Failed to collect btrfs filesystem usage info")?
++        .stdout;
++
++    {
++        let final_total = OnceCell::new();
++        let final_used = OnceCell::new();
++        let final_free = OnceCell::new();
++
++        let mut lines = filesystem_usage.lines();
++        while let Some(line) = lines
++            .next_line()
++            .await
++            .error("Failed to read output of btrfs filesystem usage")?
++        {
++            let line = line.trim();
++
++            // See btrfs-filesystem(8) for an explanation for the rows.
++            get!(line, "Device size", final_total);
++            get!(line, "Used", final_used);
++            get!(@pre_op remove_estimate_min, line, "Free (estimated)", final_free);
++        }
++
++        Ok((
++            *final_total.get().ok_or(Error::new(OUTPUT_CHANGED))?,
++            *final_used.get().ok_or(Error::new(OUTPUT_CHANGED))?,
++            // HACK(@bpeetz): We also return the free disk space as the available one, because btrfs
++            // does not tell us which disk space is reserved for the fs. <2025-05-18>
++            *final_free.get().ok_or(Error::new(OUTPUT_CHANGED))?,
++            *final_free.get().ok_or(Error::new(OUTPUT_CHANGED))?,
++        ))
++    }
++}
+-- 
+2.49.0
+