Files
qbz/flake.nix
T
vicrodh 32fd77f75e feat(qt): Wallpaper follows the window on Plasma Wayland; its blurred twin drifts
"Wallpaper" now behaves like a window that sees straight through to the
desktop: it always paints the part of the wallpaper that lies under the
window, wherever the window is moved, with a stronger blur so nothing in
the picture competes with the text. X11, macOS and Windows already told
Qt where the window is. Wayland does not, so on KDE Plasma a tracker
reads the window's geometry from org_kde_plasma_window_management on its
own client connection and thread (wayland-client + the plasma protocol
crate, pure Rust backend). It follows this process's windows only,
drops every other window's object after its initial state, and
republishes only when the geometry list changes; the field picks the
window by size and ignores updates while another background is showing.
Where the protocol is not offered the crop stays centred and one line is
logged.

KWin hands that protocol only to a binary whose desktop entry names it by
an absolute Exec and lists it in X-KDE-Wayland-Interfaces, so the Linux
desktop entries now carry both (the deb/rpm /usr/bin/qbz symlink resolves
to the same binary). The AppImage step restores the bare Exec name that
linuxdeploy resolves inside the AppDir, and the Nix package rewrites Exec
to its store path.

"Wallpaper, blurred" is Blurred art with the wallpaper in the cover's
place, so it now drifts while the transport plays and holds still when it
does not.

cc
2026-09-14 05:22:55 -06:00

137 lines
5.7 KiB
Nix

{
description = "QBZ — Native hi-fi Qobuz desktop player for Linux";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
let
# ──────────────────────────────────────────────
# VERSION BUMP: bump `qbzVersion` and the per-arch tarball hashes AFTER a
# tag publishes its release assets (hashes are computed from the published
# asset at its final URL — see below).
#
# This flake installs the prebuilt Qt binary tarball — the SAME bare-binary
# asset AUR-bin / Gentoo / Flathub repack — instead of compiling the Rust
# + Qt workspace. That matches the project's binary-repack packaging
# doctrine and sidesteps the cxx-qt / qt-build-utils source build entirely
# (github #744: `Could not find qmltyperegistrar`, which needs Qt host
# tools from several outputs on PATH at build time).
# ──────────────────────────────────────────────
qbzVersion = "2.1.1";
# Bare-binary release tarballs, per arch. Layout inside each:
# qbz_<ver>_<arch>/{qbz, qbzd, LICENSE, licenses/, qbz.desktop, icons/}
# Hashes: `nix-prefetch-url <url>` then `nix hash convert --to sri`.
assets = {
"x86_64-linux" = {
name = "qbz_${qbzVersion}_amd64.tar.gz";
hash = "sha256-A8/eHVTTleSlPGgHBW2Qn9CWb95clgf5tifEXwKYvLg=";
};
"aarch64-linux" = {
name = "qbz_${qbzVersion}_aarch64.tar.gz";
hash = "sha256-LbC9KPQOqZ2sDWNoErQiK+jlEr5cNlsgf1Qu3cpFU4w=";
};
};
in
flake-utils.lib.eachSystem (builtins.attrNames assets) (system:
let
pkgs = import nixpkgs { inherit system; };
asset = assets.${system};
# Opened by name at runtime rather than linked; wrapQtAppsHook already
# covers Qt's own plugin/QML closure.
runtimeLibs = with pkgs; [ libjack2 ];
runtimeBins = with pkgs; [ pipewire pulseaudio xdg-utils ];
in
{
packages.default = pkgs.stdenv.mkDerivation {
pname = "qbz";
version = qbzVersion;
src = pkgs.fetchurl {
url = "https://github.com/vicrodh/qbz/releases/download/v${qbzVersion}/${asset.name}";
inherit (asset) hash;
};
# autoPatchelfHook rewrites the prebuilt binary's interpreter + rpath
# against the nixpkgs closure; wrapQtAppsHook sets the Qt plugin / QML
# import paths the binary needs at run time.
nativeBuildInputs = with pkgs; [
autoPatchelfHook
qt6.wrapQtAppsHook
];
# qt6-base/declarative/svg/wayland mirror the AUR `qbz-bin` depends;
# the rest satisfy the bare binary's DT_NEEDED (ALSA, xkbcommon,
# wayland, zlib, libstdc++) plus the dlopen'd JACK backend.
buildInputs = with pkgs; [
alsa-lib
qt6.qtbase
qt6.qtdeclarative
qt6.qtsvg
qt6.qtwayland
libxkbcommon
wayland
zlib
stdenv.cc.cc.lib
] ++ runtimeLibs;
# Only the desktop client — `qbzd` ships as its own package elsewhere,
# matching the source flake's previous scope.
installPhase = ''
runHook preInstall
install -Dm755 qbz "$out/bin/qbz"
install -Dm644 qbz.desktop \
"$out/share/applications/com.blitzfc.qbz.desktop"
# KWin matches the Exec path against /proc/<pid>/exe before it
# offers org_kde_plasma_window_management (Wallpaper background).
substituteInPlace "$out/share/applications/com.blitzfc.qbz.desktop" \
--replace-warn "Exec=/usr/bin/qbz" "Exec=$out/bin/qbz"
if [ -d icons ]; then cp -r icons "$out/share/icons"; fi
install -Dm644 LICENSE "$out/share/licenses/qbz/LICENSE"
if [ -d licenses ]; then
cp -r licenses "$out/share/licenses/qbz/third-party"
fi
runHook postInstall
'';
# Fold the runtime helpers/libraries QBZ opens by name into the SAME
# wrap wrapQtAppsHook performs (no double wrapper).
preFixup = ''
qtWrapperArgs+=(--prefix PATH : ${pkgs.lib.makeBinPath runtimeBins})
qtWrapperArgs+=(--prefix LD_LIBRARY_PATH : ${pkgs.lib.makeLibraryPath runtimeLibs})
'';
meta = with pkgs.lib; {
description = "Native, full-featured hi-fi Qobuz desktop player for Linux";
homepage = "https://qbz.lol";
downloadPage = "https://github.com/vicrodh/qbz/releases";
license = licenses.mit;
mainProgram = "qbz";
platforms = [ "x86_64-linux" "aarch64-linux" ];
sourceProvenance = [ sourceTypes.binaryNativeCode ];
};
};
apps.default = {
type = "app";
program = "${self.packages.${system}.default}/bin/qbz";
};
# Running a locally-compiled debug binary (crates/target/debug/qbz)
# needs the dlopen'd JACK backend on LD_LIBRARY_PATH, same as the
# wrapped package. Rust itself comes from the contributor's rustup.
devShells.default = pkgs.mkShell {
packages = with pkgs; [ rust-analyzer rustfmt clippy ];
shellHook = ''
export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath runtimeLibs}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
'';
};
});
}