From cd8248f2f5d8d3a6a8e2a96cfa5416ed3a4c5437 Mon Sep 17 00:00:00 2001 From: mozempk Date: Sat, 25 Apr 2026 18:27:06 +0200 Subject: [PATCH] feat: NVIDIA PRIME, audio fix, timezone, dmesg error cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add nvidia/nvidia-dkms/nvidia-libs-32bit/nvidia-vaapi-driver to niri live and installed profiles; wireless-regdb and sof-firmware to all profiles (fixes regulatory.db and SOF firmware dmesg errors) - iso/postsetup-nvidia.sh: new mklive -x hook that re-runs dracut inside the rootfs chroot after the overlay is applied; ensures the squashfs initramfs includes nvidia.ko and omits nouveau.ko at build time — no driver install needed at runtime (fixes /run tmpfs overflow that was killing wireplumber by corrupting D-Bus sockets) - Both ISO inner build scripts gain -x postsetup-nvidia.sh and the nonfree repo flag so nvidia packages resolve correctly - niri config: wireplumber started via supervisor loop (waits for PipeWire socket, auto-restarts on crash) replacing the one-shot exec — survives any D-Bus or pipewire disruption - build-niri-live-iso.sh: NVIDIA modprobe blacklist-nouveau.conf, btusb-quirks.conf, modules-load.d/nvidia.conf, dracut/10-nvidia.conf, Xorg intel/nvidia configs, prime-run helper, elogind run script loop guard, timezone Europe/Zurich overlay, updated BOOT_CMDLINE - build-live-iso.sh: same NVIDIA + timezone + sound udev rule overlays; live-setup.sh timezone and audio group fix - installer/lib/grub.sh: GRUB_CMDLINE_LINUX_DEFAULT gains nvidia-drm.modeset=1 rd.driver.blacklist=nouveau btusb.enable_autosuspend=0 - installer/lib/postinstall.sh: configure_nvidia_prime() adds blacklist-nouveau.conf, btusb-quirks.conf, dracut omit_drivers nouveau, modules-load.d with all four nvidia modules --- .../mainline-niri/customizations/niri.sh | 191 ++++++++++++- config/profiles/mainline-niri/packages.list | 9 +- .../mainline-niri/packages.live-desktop.list | 16 +- config/profiles/stable-cinnamon/packages.list | 2 + .../packages.live-desktop.list | 2 + installer/lib/grub.sh | 2 +- installer/lib/postinstall.sh | 19 +- iso/_inner-build-niri-live.sh | 1 + iso/_inner-build.sh | 2 + iso/build-live-iso.sh | 72 ++++- iso/build-niri-live-iso.sh | 269 ++++++++++++++++-- iso/postsetup-nvidia.sh | 81 ++++++ 12 files changed, 637 insertions(+), 29 deletions(-) create mode 100755 iso/postsetup-nvidia.sh diff --git a/config/profiles/mainline-niri/customizations/niri.sh b/config/profiles/mainline-niri/customizations/niri.sh index 372f2be..5970a6c 100644 --- a/config/profiles/mainline-niri/customizations/niri.sh +++ b/config/profiles/mainline-niri/customizations/niri.sh @@ -48,6 +48,7 @@ prefer-no-csd spawn-at-startup "swaybg" "-i" "/usr/share/backgrounds/void-installer/pxfuel.jpg" spawn-at-startup "mako" spawn-at-startup "/usr/libexec/polkit-gnome-authentication-agent-1" +spawn-at-startup "sh" "-c" "command -v gnome-keyring-daemon >/dev/null 2>&1 && gnome-keyring-daemon --start --components=secrets,pkcs11 >/dev/null 2>&1; true" spawn-at-startup "sh" "-c" "i=0; while [ \$i -lt 30 ] && ! dbus-send --system --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.bluez >/dev/null 2>&1; do sleep 1; i=\$((i+1)); done; exec noctalia-shell" cursor { @@ -57,7 +58,7 @@ cursor { binds { Mod+T { spawn "alacritty"; } - Mod+D { spawn "fuzzel"; } + Mod+D { spawn "sh" "-c" "quickshell msg -c noctalia-shell launcher toggle"; } Mod+Q { close-window; } Mod+Shift+E { quit; } Print { screenshot; } @@ -97,9 +98,74 @@ export MOZ_ENABLE_WAYLAND=1 export _JAVA_AWT_WM_NONREPARENTING=1 export XDG_CURRENT_DESKTOP=niri export XDG_SESSION_TYPE=wayland +export GTK_USE_PORTAL=1 +export ELECTRON_OZONE_PLATFORM_HINT=auto EOF chmod 0644 "$TARGET/etc/profile.d/wayland.sh" log "wayland environment installed at /etc/profile.d/wayland.sh" + + # Expose nix .desktop files and icons (installed via first-boot-nix) + cat > "$TARGET/etc/profile.d/nix-xdg.sh" <<'NIXEOF' +# Add nix profile share directory so launchers and icon themes pick up nix apps. +if [[ -d "${HOME:-}/.nix-profile/share" ]]; then + case ":${XDG_DATA_DIRS:-}:" in + *":$HOME/.nix-profile/share:"*) ;; + *) export XDG_DATA_DIRS="$HOME/.nix-profile/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" ;; + esac +fi +NIXEOF + chmod 0644 "$TARGET/etc/profile.d/nix-xdg.sh" + + # /etc/environment: baseline XDG_DATA_DIRS loaded by pam_env for ALL session + # types (TTY login, greetd). The nix profile share path must be absolute here + # because pam_env does not expand $HOME when the key uses = (not DEFAULT=). + # The installed user's home is /home/$USERNAME, so we hardcode it. + cat > "$TARGET/etc/environment" < "$TARGET/etc/dconf/db/local.d/01-dark-theme" <<'EOF' +[org/gnome/desktop/interface] +color-scheme='prefer-dark' +gtk-theme='Gruvbox-Dark' +icon-theme='Gruvbox-Plus-Dark' +cursor-theme='Bibata-Modern-Ice' +cursor-size=24 +EOF + echo 'user-db:user +system-db:local' > "$TARGET/etc/dconf/profile/user" + run_chroot "dconf update 2>/dev/null || true" + log "dconf dark theme profile installed" +} + +_niri_write_session_wrapper() { + local TARGET="$1" + # Create /usr/local/bin/niri-session: sources /etc/profile before exec'ing + # niri --session so that all /etc/profile.d/* scripts (nix paths, + # XDG_DATA_DIRS with ~/.nix-profile/share, wayland env, etc.) are in effect + # for the compositor and every app it spawns. + install -d -m 0755 "$TARGET/usr/local/bin" + cat > "$TARGET/usr/local/bin/niri-session" <<'EOF' +#!/bin/bash +# niri-session — wrapper started by greetd/tuigreet. +# Sources /etc/profile so that all /etc/profile.d/* scripts run +# (nix paths, wayland env, XDG_DATA_DIRS with ~/.nix-profile/share, etc.) +# before handing off to the compositor. +[ -f /etc/profile ] && . /etc/profile +exec niri --session "$@" +EOF + chmod 0755 "$TARGET/usr/local/bin/niri-session" + log "niri-session wrapper installed at /usr/local/bin/niri-session" } _niri_setup_greetd() { @@ -141,7 +207,122 @@ EOF fi } -_niri_write_kdl "$TARGET" -_niri_write_env "$TARGET" -_niri_setup_greetd "$TARGET" -_niri_install_noctalia "$TARGET" +_niri_write_portal_config() { + local TARGET="$1" + # Tell xdg-desktop-portal to route all portals through the GTK backend when + # running under niri. Without this the dispatcher has no match for + # XDG_CURRENT_DESKTOP=niri and file-picker / open-with calls fail silently. + install -d -m 0755 "$TARGET/etc/xdg/xdg-desktop-portal" + cat > "$TARGET/etc/xdg/xdg-desktop-portal/niri-portals.conf" <<'EOF' +[preferred] +default=gtk +org.freedesktop.impl.portal.FileChooser=gtk +org.freedesktop.impl.portal.AppChooser=gtk +org.freedesktop.impl.portal.OpenURI=gtk +org.freedesktop.impl.portal.Print=gtk +org.freedesktop.impl.portal.Screenshot=gtk +org.freedesktop.impl.portal.Inhibit=gtk +org.freedesktop.impl.portal.Notification=gtk +org.freedesktop.impl.portal.Settings=gtk +EOF + log "niri portal config installed (gtk backend for all portals)" +} + +_niri_write_sound_udev_rules() { + local TARGET="$1" + # PCM/control nodes are created root:root on some kernels before the audio + # group is provisioned. This persistent rule ensures correct ownership. + install -d -m 0755 "$TARGET/etc/udev/rules.d" + cat > "$TARGET/etc/udev/rules.d/70-sound-perms.rules" <<'EOF' +# Allow the audio group to access ALSA PCM and control devices. +SUBSYSTEM=="sound", GROUP="audio", MODE="0660" +EOF + log "sound udev rules installed" +} + +_niri_write_noctalia_defaults() { + local TARGET="$1" + # Write a baseline noctalia settings.json into skel so every new user + # (including the installed user) gets the correct wallpaper directory, + # dark mode, and Gruvbox colour scheme out of the box. + local skel_noc="$TARGET/etc/skel/.config/noctalia" + install -d -m 0755 "$skel_noc" + cat > "$skel_noc/settings.json" < "$TARGET/etc/xdg/mimeapps.list" <<'EOF' +[Default Applications] +text/html=google-chrome.desktop +x-scheme-handler/http=google-chrome.desktop +x-scheme-handler/https=google-chrome.desktop +x-scheme-handler/about=google-chrome.desktop +x-scheme-handler/unknown=google-chrome.desktop +application/pdf=google-chrome.desktop +application/xhtml+xml=google-chrome.desktop +application/xml=google-chrome.desktop +EOF + # Also set in user skel so ~/.config/mimeapps.list is populated on first login + install -d -m 0755 "$TARGET/etc/skel/.config" + cp "$TARGET/etc/xdg/mimeapps.list" "$TARGET/etc/skel/.config/mimeapps.list" + # Mirror into installed user home + install -d -m 0755 "$TARGET/home/$USERNAME/.config" + cp "$TARGET/etc/xdg/mimeapps.list" "$TARGET/home/$USERNAME/.config/mimeapps.list" + run_chroot "chown $USERNAME:$USERNAME /home/$USERNAME/.config/mimeapps.list" || true + log "google-chrome set as default browser (mimeapps.list)" +} + +_niri_write_kdl "$TARGET" +_niri_write_env "$TARGET" +_niri_write_session_wrapper "$TARGET" +_niri_write_portal_config "$TARGET" +_niri_write_sound_udev_rules "$TARGET" +_niri_setup_greetd "$TARGET" +_niri_install_noctalia "$TARGET" +_niri_write_noctalia_defaults "$TARGET" +_niri_set_default_browser "$TARGET" diff --git a/config/profiles/mainline-niri/packages.list b/config/profiles/mainline-niri/packages.list index f60c0e4..7c6dfd3 100644 --- a/config/profiles/mainline-niri/packages.list +++ b/config/profiles/mainline-niri/packages.list @@ -49,6 +49,7 @@ openssh iwd nftables chrony +wireless-regdb # --- audio (pipewire stack) --- pipewire @@ -57,6 +58,7 @@ alsa-pipewire pavucontrol alsa-utils playerctl +sof-firmware # --- graphics / wayland --- wayland @@ -75,7 +77,6 @@ nvidia-vaapi-driver # --- niri compositor + wayland ecosystem --- niri -fuzzel mako swaybg swayidle @@ -89,6 +90,12 @@ xdg-desktop-portal-wlr polkit-gnome brightnessctl +# --- file manager --- +nautilus + +# --- keyring (Chrome / VSCode secret storage) --- +gnome-keyring + # --- noctalia shell runtime deps (noctalia-shell itself is installed in # niri.sh from the third-party XBPS repo at universalrepo.r1xelelo.workers.dev). ImageMagick diff --git a/config/profiles/mainline-niri/packages.live-desktop.list b/config/profiles/mainline-niri/packages.live-desktop.list index d671e7c..ec594a2 100644 --- a/config/profiles/mainline-niri/packages.live-desktop.list +++ b/config/profiles/mainline-niri/packages.live-desktop.list @@ -46,6 +46,7 @@ NetworkManager-openvpn openssh iwd chrony +wireless-regdb # --- audio (pipewire stack) --- pipewire @@ -54,6 +55,7 @@ alsa-pipewire pavucontrol alsa-utils playerctl +sof-firmware # --- Wayland session --- mesa-dri @@ -63,14 +65,17 @@ elogind seatd dbus +# --- nvidia PRIME (from nonfree repo) --- +nvidia +nvidia-libs-32bit +nvidia-vaapi-driver + # --- display manager --- greetd tuigreet # --- terminal + launcher --- alacritty -fuzzel -foot # --- notification + background --- mako @@ -95,10 +100,17 @@ zenity # --- XDG portals --- xdg-desktop-portal +xdg-desktop-portal-gtk xdg-desktop-portal-gnome xdg-utils xdg-user-dirs +# --- file manager --- +nautilus + +# --- keyring (Chrome / VSCode secret storage) --- +gnome-keyring + # --- nix (for prebaked packages — spotify, discord, google-chrome, vscode, fastfetch, etc.) --- nix diff --git a/config/profiles/stable-cinnamon/packages.list b/config/profiles/stable-cinnamon/packages.list index 06aa917..3ea1a08 100644 --- a/config/profiles/stable-cinnamon/packages.list +++ b/config/profiles/stable-cinnamon/packages.list @@ -50,6 +50,7 @@ iwd wpa_supplicant nftables chrony +wireless-regdb # --- audio --- pipewire @@ -57,6 +58,7 @@ wireplumber alsa-pipewire pavucontrol alsa-utils +sof-firmware # --- graphics / xorg --- xorg-minimal diff --git a/config/profiles/stable-cinnamon/packages.live-desktop.list b/config/profiles/stable-cinnamon/packages.live-desktop.list index 3ce3567..4e2e6cd 100644 --- a/config/profiles/stable-cinnamon/packages.live-desktop.list +++ b/config/profiles/stable-cinnamon/packages.live-desktop.list @@ -47,6 +47,7 @@ openssh iwd wpa_supplicant chrony +wireless-regdb # --- audio --- pipewire @@ -54,6 +55,7 @@ wireplumber alsa-pipewire pavucontrol alsa-utils +sof-firmware # --- graphics / xorg --- xorg-minimal diff --git a/installer/lib/grub.sh b/installer/lib/grub.sh index 701239f..a7351d5 100755 --- a/installer/lib/grub.sh +++ b/installer/lib/grub.sh @@ -18,7 +18,7 @@ install_grub() { GRUB_DEFAULT=0 GRUB_TIMEOUT=5 GRUB_DISTRIBUTOR="Void" -GRUB_CMDLINE_LINUX_DEFAULT="loglevel=4 nvidia-drm.modeset=1" +GRUB_CMDLINE_LINUX_DEFAULT="loglevel=4 nvidia-drm.modeset=1 rd.driver.blacklist=nouveau modprobe.blacklist=nouveau btusb.enable_autosuspend=0" GRUB_CMDLINE_LINUX="" GRUB_DISABLE_OS_PROBER=false GRUB_TERMINAL_OUTPUT="gfxterm" diff --git a/installer/lib/postinstall.sh b/installer/lib/postinstall.sh index 4591950..8e24980 100755 --- a/installer/lib/postinstall.sh +++ b/installer/lib/postinstall.sh @@ -160,7 +160,19 @@ Section "OutputClass" EndSection EOF - # 2) Modules to load early for KMS. + # 2) Blacklist nouveau so it doesn't grab the GPU before nvidia loads. + install -d -m 0755 "$TARGET/etc/modprobe.d" + cat > "$TARGET/etc/modprobe.d/blacklist-nouveau.conf" <<'EOF' +blacklist nouveau +options nouveau modeset=0 +EOF + + # btusb USB autosuspend causes firmware download failures on Intel BT. + cat > "$TARGET/etc/modprobe.d/btusb-quirks.conf" <<'EOF' +options btusb enable_autosuspend=0 +EOF + + # 3) Modules to load early for KMS. install -d -m 0755 "$TARGET/etc/modules-load.d" cat > "$TARGET/etc/modules-load.d/nvidia.conf" <<'EOF' nvidia @@ -169,7 +181,7 @@ nvidia_uvm nvidia_drm EOF - # 3) Wrapper script + desktop file: run any app on NVIDIA via `prime-run`. + # 4) Wrapper script + desktop file: run any app on NVIDIA via `prime-run`. install -d -m 0755 "$TARGET/usr/local/bin" cat > "$TARGET/usr/local/bin/prime-run" <<'EOF' #!/bin/sh @@ -181,10 +193,11 @@ exec env __NV_PRIME_RENDER_OFFLOAD=1 \ EOF chmod 0755 "$TARGET/usr/local/bin/prime-run" - # 4) Make sure dracut picks up nvidia modules. + # 5) dracut: include nvidia modules in initramfs; explicitly omit nouveau. install -d -m 0755 "$TARGET/etc/dracut.conf.d" cat > "$TARGET/etc/dracut.conf.d/10-nvidia.conf" <<'EOF' add_drivers+=" nvidia nvidia_modeset nvidia_uvm nvidia_drm " +omit_drivers+=" nouveau " EOF ok "NVIDIA PRIME offload configured (use 'prime-run ')" diff --git a/iso/_inner-build-niri-live.sh b/iso/_inner-build-niri-live.sh index 780fb91..e0eb980 100755 --- a/iso/_inner-build-niri-live.sh +++ b/iso/_inner-build-niri-live.sh @@ -221,6 +221,7 @@ trap _cleanup_mklive_builds EXIT -T "$ISO_TITLE" \ -p "$ISO_PKGS" \ -I "$INCLUDE_DIR" \ + -x "$PROJECT_DIR/iso/postsetup-nvidia.sh" \ -C "${BOOT_CMDLINE:-}" \ -o "$OUT_ISO" diff --git a/iso/_inner-build.sh b/iso/_inner-build.sh index f4cc9c2..6479dbe 100755 --- a/iso/_inner-build.sh +++ b/iso/_inner-build.sh @@ -47,6 +47,7 @@ trap _cleanup_mklive_builds EXIT ./mklive.sh \ -a "$ARCH" \ -r "$REPO_URL" \ + -r "${REPO_URL%/current}/current/nonfree" \ -c "$CACHE_DIR/xbps-live-pkgs" \ -H "$CACHE_DIR/xbps-host-pkgs" \ -k "$KEYMAP" \ @@ -54,6 +55,7 @@ trap _cleanup_mklive_builds EXIT -T "$ISO_TITLE" \ -p "$ISO_PKGS" \ -I "$INCLUDE_DIR" \ + -x "$PROJECT_DIR/iso/postsetup-nvidia.sh" \ -C "${BOOT_CMDLINE:-}" \ -o "$OUT_ISO" diff --git a/iso/build-live-iso.sh b/iso/build-live-iso.sh index a822d4f..c340164 100755 --- a/iso/build-live-iso.sh +++ b/iso/build-live-iso.sh @@ -135,6 +135,17 @@ if [ -f /etc/nsswitch.conf ]; then echo "live-setup: removed mdns from nsswitch.conf (hosts line)" fi +# ── Timezone ────────────────────────────────────────────────────────────── +ln -sf /usr/share/zoneinfo/Europe/Zurich /etc/localtime 2>/dev/null || true +echo "live-setup: timezone set to Europe/Zurich" + +# ── Fix sound device permissions ─────────────────────────────────────────── +# PCM/control nodes may be root:root at early boot before the audio group +# exists. Re-apply correct ownership so PipeWire/ALSA can open the devices. +chown root:audio /dev/snd/pcmC* /dev/snd/controlC* /dev/snd/hwC* 2>/dev/null || true +chmod 660 /dev/snd/pcmC* /dev/snd/controlC* /dev/snd/hwC* 2>/dev/null || true +udevadm trigger --subsystem-match=sound 2>/dev/null || true + # ── DNS: ensure a working nameserver is configured ─────────────────────── # NetworkManager will overwrite resolv.conf once DHCP completes. # If the DHCP-provided nameserver is broken (e.g. QEMU's 10.0.2.3), add @@ -264,6 +275,16 @@ chmod 0755 "$INCLUDE_DIR/etc/runit/2" install -d -m 0755 "$INCLUDE_DIR/etc/runit/runsvdir/default" +# Write all repos to xbps.d so they persist in the live squashfs. +# The nonfree repo is needed for nvidia and other proprietary drivers. +install -d -m 0755 "$INCLUDE_DIR/etc/xbps.d" +cat > "$INCLUDE_DIR/etc/xbps.d/00-void-repos.conf" < "$INCLUDE_DIR/etc/environment" <<'ENVEOF' XDG_DATA_DIRS=/home/live/.nix-profile/share:/usr/local/share:/usr/share ENVEOF +# ── Timezone ────────────────────────────────────────────────────────────── +ln -sf /usr/share/zoneinfo/Europe/Zurich "$INCLUDE_DIR/etc/localtime" +cat > "$INCLUDE_DIR/etc/rc.conf" <<'RCEOF' +KEYMAP="ch" +HARDWARECLOCK="UTC" +TIMEZONE="Europe/Zurich" +RCEOF + +# ── Sound device udev rules ────────────────────────────────────────────── +# PCM/control nodes are created root:root at early boot before the audio group +# is provisioned; this rule ensures correct ownership on every boot. +install -d -m 0755 "$INCLUDE_DIR/etc/udev/rules.d" +cat > "$INCLUDE_DIR/etc/udev/rules.d/70-sound-perms.rules" <<'EOF' +SUBSYSTEM=="sound", GROUP="audio", MODE="0660" +EOF + +# ── NVIDIA PRIME overlay ──────────────────────────────────────────────── +# Blacklist nouveau — the live-setup.sh PRIME detection block already writes +# Xorg config, but the kernel must also not load nouveau. +install -d -m 0755 "$INCLUDE_DIR/etc/modprobe.d" +cat > "$INCLUDE_DIR/etc/modprobe.d/blacklist-nouveau.conf" <<'EOF' +blacklist nouveau +options nouveau modeset=0 +EOF +cat > "$INCLUDE_DIR/etc/modprobe.d/btusb-quirks.conf" <<'EOF' +options btusb enable_autosuspend=0 +EOF +install -d -m 0755 "$INCLUDE_DIR/etc/modules-load.d" +cat > "$INCLUDE_DIR/etc/modules-load.d/nvidia.conf" <<'EOF' +nvidia +nvidia_modeset +nvidia_uvm +nvidia_drm +EOF +install -d -m 0755 "$INCLUDE_DIR/etc/dracut.conf.d" +cat > "$INCLUDE_DIR/etc/dracut.conf.d/10-nvidia.conf" <<'EOF' +add_drivers+=" nvidia nvidia_modeset nvidia_uvm nvidia_drm " +omit_drivers+=" nouveau " +EOF +install -d -m 0755 "$INCLUDE_DIR/usr/local/bin" +cat > "$INCLUDE_DIR/usr/local/bin/prime-run" <<'EOF' +#!/bin/sh +exec env __NV_PRIME_RENDER_OFFLOAD=1 \ + __VK_LAYER_NV_optimus=NVIDIA_only \ + __GLX_VENDOR_LIBRARY_NAME=nvidia \ + "$@" +EOF +chmod 0755 "$INCLUDE_DIR/usr/local/bin/prime-run" + # /etc/profile.d: PATH for interactive terminals (alacritty, etc.) install -d -m 0755 "$INCLUDE_DIR/etc/profile.d" cat > "$INCLUDE_DIR/etc/profile.d/nix-prebaked.sh" <<'NIXEOF' @@ -696,7 +766,7 @@ TS="$(date -u +%Y%m%d)" OUT_ISO="${OUTPUT_ISO:-$OUT_DIR/void-live-stable-${TS}.iso}" # live.user=live → vmklive dracut hook creates user 'live' (default would be 'anon') # console=ttyS0 → serial output for QEMU/real hardware debugging -BOOT_CMDLINE="${BOOT_CMDLINE:-live.user=${LIVE_USER} console=tty0 console=ttyS0,115200}" +BOOT_CMDLINE="${BOOT_CMDLINE:-live.user=${LIVE_USER} console=tty0 console=ttyS0,115200 nvidia-drm.modeset=1 rd.driver.blacklist=nouveau modprobe.blacklist=nouveau btusb.enable_autosuspend=0}" echo ">>> running mklive.sh inside docker — output: $OUT_ISO" "$DOCKER" run --rm --privileged \ diff --git a/iso/build-niri-live-iso.sh b/iso/build-niri-live-iso.sh index e60a6c9..e4bd2cf 100755 --- a/iso/build-niri-live-iso.sh +++ b/iso/build-niri-live-iso.sh @@ -84,7 +84,7 @@ cat > "$INCLUDE_DIR/etc/greetd/config.toml" <<'EOF' vt = 2 [default_session] -command = "tuigreet --time --greeting 'Void Linux Live (niri)' --cmd 'niri --session'" +command = "tuigreet --time --greeting 'Void Linux Live (niri)' --cmd niri-session" user = "_greeter" EOF @@ -102,8 +102,16 @@ BAUD_RATE=38400 TERM_NAME=linux EOF -# ── 3b) noctalia XBPS repo ────────────────────────────────────────────── +# ── 3b) xbps repo configuration ───────────────────────────────────────── +# Write all repos into xbps.d so they are available in the live session +# (not just at build time). The nonfree repo is needed for nvidia et al. install -d -m 0755 "$INCLUDE_DIR/etc/xbps.d" +cat > "$INCLUDE_DIR/etc/xbps.d/00-void-repos.conf" < "$INCLUDE_DIR/etc/xbps.d/10-noctalia.conf" <<'EOF' repository=https://universalrepo.r1xelelo.workers.dev/void EOF @@ -145,13 +153,16 @@ XDG_RUN="/run/user/$(id -u "$LIVE_USER" 2>/dev/null || echo 1000)" install -d -m 0700 "$XDG_RUN" 2>/dev/null || true chown "$LIVE_USER" "$XDG_RUN" 2>/dev/null || true +# Fix sound device permissions — PCM/control nodes are created root:root during +# early boot before the audio group is guaranteed to exist. Re-apply the correct +# ownership now that the group is present, then trigger udev to re-evaluate the +# sound subsystem so the persistent 70-sound-perms.rules rule is applied too. +chown root:audio /dev/snd/pcmC* /dev/snd/controlC* /dev/snd/hwC* 2>/dev/null || true +chmod 660 /dev/snd/pcmC* /dev/snd/controlC* /dev/snd/hwC* 2>/dev/null || true +udevadm trigger --subsystem-match=sound 2>/dev/null || true -# Start elogind here, once, before runsvdir brings up greetd. -# Runit does NOT supervise it — this avoids cgroup-race restart spam. -# We wait until dbus is available (dbus service starts first in runsvdir), -# then start elogind as a background daemon. -# NOTE: live-setup.sh runs BEFORE runsvdir, so dbus isn't up yet here. -# We start elogind from a one-shot at-startup hook instead — see runit/2. +# Timezone +ln -sf /usr/share/zoneinfo/Europe/Zurich /etc/localtime 2>/dev/null || true echo "niri live-setup: done (user=$LIVE_USER)" SV_EOF @@ -194,6 +205,12 @@ install -d -m 0755 "$INCLUDE_DIR/etc/sv/elogind" cat > "$INCLUDE_DIR/etc/sv/elogind/run" <<'EOF' #!/bin/sh exec 2>&1 +# If elogind is already running (e.g. from a previous attempt), stay up +# permanently rather than letting runit restart the wrapper every 3 s. +if [ -f /run/elogind.pid ] && kill -0 "$(cat /run/elogind.pid 2>/dev/null)" 2>/dev/null; then + echo "elogind-sv: already running (PID $(cat /run/elogind.pid)), yielding" + exec tail -f /dev/null +fi # Wait for dbus socket — poll every second, up to 30s i=0 while [ $i -lt 30 ] && [ ! -S /run/dbus/system_bus_socket ]; do @@ -226,6 +243,9 @@ export XDG_CURRENT_DESKTOP=niri export XDG_SESSION_TYPE=wayland # Use elogind's logind backend — works correctly on Void/runit via audit session IDs export LIBSEAT_BACKEND=logind +# Force GTK apps (and Electron/VSCode) to use XDG portal file dialogs +export GTK_USE_PORTAL=1 +export ELECTRON_OZONE_PLATFORM_HINT=auto EOF chmod 0644 "$INCLUDE_DIR/etc/profile.d/wayland.sh" @@ -238,6 +258,13 @@ if [[ -d "${HOME:-}/.nix-profile/bin" ]]; then *) export PATH="$HOME/.nix-profile/bin:$PATH" ;; esac fi +# Expose nix .desktop files and icons to XDG-compliant launchers/shells +if [[ -d "${HOME:-}/.nix-profile/share" ]]; then + case ":${XDG_DATA_DIRS:-}:" in + *":$HOME/.nix-profile/share:"*) ;; + *) export XDG_DATA_DIRS="$HOME/.nix-profile/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" ;; + esac +fi export NIXPKGS_ALLOW_UNFREE=1 # Pre-baked nix is single-user (no daemon) — bypass daemon connection attempt export NIX_REMOTE=local @@ -344,7 +371,9 @@ cursor { // Audio / screen session services — started by niri as the live user spawn-at-startup "pipewire" spawn-at-startup "pipewire-pulse" -spawn-at-startup "wireplumber" +// Keep WirePlumber running: wait for the PipeWire socket then start it, +// restart on any crash or pipewire restart (e.g. after package installs). +spawn-at-startup "sh" "-c" "while true; do while [ ! -S /run/user/\$(id -u)/pipewire-0 ]; do sleep 0.2; done; wireplumber; sleep 1; done" // Background, notifications and auth spawn-at-startup "swaybg" "-i" "/usr/share/backgrounds/void-installer/pxfuel.jpg" "-m" "fill" @@ -360,7 +389,7 @@ spawn-at-startup "sh" "-c" "[ -f ~/.first-login-done ] || alacritty -T 'Void Set binds { Mod+T { spawn "alacritty"; } - Mod+D { spawn "fuzzel"; } + Mod+D { spawn "sh" "-c" "quickshell msg -c noctalia-shell launcher toggle"; } Mod+Q { close-window; } Mod+Shift+E { quit; } Print { screenshot; } @@ -474,11 +503,27 @@ install -d -m 0755 "$INCLUDE_DIR/usr/share/themes" install -d -m 0755 "$INCLUDE_DIR/usr/share/icons" [[ -d "$OVERLAY/icons" ]] && cp -a "$OVERLAY/icons"/. "$INCLUDE_DIR/usr/share/icons/" 2>/dev/null || true -# ── 3g) GTK settings for Wayland apps ─────────────────────────────────── +# ── 3g) GTK settings + dark theme dconf ──────────────────────────────── # Write GTK2/3/4 settings to skel so the live user picks them up. install -d -m 0755 "$INCLUDE_DIR/etc/skel/.config/gtk-3.0" install -d -m 0755 "$INCLUDE_DIR/etc/skel/.config/gtk-4.0" +# dconf system keyfile: ensures GTK dark theme is reported to all apps via +# xdg-desktop-portal-gtk regardless of whether the user has a dconf DB yet. +install -d -m 0755 "$INCLUDE_DIR/etc/dconf/db/local.d" +install -d -m 0755 "$INCLUDE_DIR/etc/dconf/profile" +cat > "$INCLUDE_DIR/etc/dconf/db/local.d/01-dark-theme" <<'EOF' +[org/gnome/desktop/interface] +color-scheme='prefer-dark' +gtk-theme='${GTK_THEME}' +icon-theme='${ICON_THEME}' +cursor-theme='${CURSOR_THEME:-Bibata-Modern-Ice}' +cursor-size=24 +EOF +sed -i "s/'\${GTK_THEME}'/'${GTK_THEME}'/; s/'\${ICON_THEME}'/'${ICON_THEME}'/; s/'\${CURSOR_THEME:-Bibata-Modern-Ice}'/'${CURSOR_THEME:-Bibata-Modern-Ice}'/" \ + "$INCLUDE_DIR/etc/dconf/db/local.d/01-dark-theme" +printf 'user-db:user\nsystem-db:local\n' > "$INCLUDE_DIR/etc/dconf/profile/user" + cat > "$INCLUDE_DIR/etc/skel/.config/gtk-3.0/settings.ini" < "$INCLUDE_DIR/etc/environment" <<'ENVEOF' -XDG_DATA_DIRS=/usr/local/share:/usr/share +# XDG_DATA_DIRS: include the pre-baked nix profile share dir so launchers +# and icon themes pick up nix-installed apps on first boot. +# /etc/environment is loaded by pam_env for ALL session types (tty autologin +# and greetd), so this is the most reliable place to set this baseline. +cat > "$INCLUDE_DIR/etc/environment" < "$INCLUDE_DIR/etc/skel/.config/noctalia/settings.json" < "$INCLUDE_DIR/etc/xdg/mimeapps.list" <<'EOF' +[Default Applications] +text/html=google-chrome.desktop +x-scheme-handler/http=google-chrome.desktop +x-scheme-handler/https=google-chrome.desktop +x-scheme-handler/about=google-chrome.desktop +x-scheme-handler/unknown=google-chrome.desktop +application/pdf=google-chrome.desktop +application/xhtml+xml=google-chrome.desktop +application/xml=google-chrome.desktop +EOF +install -d -m 0755 "$INCLUDE_DIR/etc/skel/.config" +cp "$INCLUDE_DIR/etc/xdg/mimeapps.list" "$INCLUDE_DIR/etc/skel/.config/mimeapps.list" + +# ── Portal backend configuration for niri ───────────────────────────────── +# Without this, xdg-desktop-portal doesn't know which backend to use for +# XDG_CURRENT_DESKTOP=niri, causing file-picker / portal calls to fail. +install -d -m 0755 "$INCLUDE_DIR/etc/xdg/xdg-desktop-portal" +cat > "$INCLUDE_DIR/etc/xdg/xdg-desktop-portal/niri-portals.conf" <<'EOF' +[preferred] +default=gtk +org.freedesktop.impl.portal.FileChooser=gtk +org.freedesktop.impl.portal.AppChooser=gtk +org.freedesktop.impl.portal.OpenURI=gtk +org.freedesktop.impl.portal.Print=gtk +org.freedesktop.impl.portal.Screenshot=gtk +org.freedesktop.impl.portal.Inhibit=gtk +org.freedesktop.impl.portal.Notification=gtk +org.freedesktop.impl.portal.Settings=gtk +EOF + +# ── Sound device udev rules ──────────────────────────────────────── +# PCM/control nodes are created root:root at early boot before the audio +# group is provisioned; this rule ensures correct ownership on every boot. +install -d -m 0755 "$INCLUDE_DIR/etc/udev/rules.d" +cat > "$INCLUDE_DIR/etc/udev/rules.d/70-sound-perms.rules" <<'EOF' +# Allow the audio group to access ALSA PCM and control devices. +SUBSYSTEM=="sound", GROUP="audio", MODE="0660" +EOF + +# ── Timezone ────────────────────────────────────────────────────────── +# Create the /etc/localtime symlink and rc.conf TIMEZONE setting so the live +# session starts in the correct timezone without requiring a user step. +ln -sf /usr/share/zoneinfo/Europe/Zurich "$INCLUDE_DIR/etc/localtime" +cat > "$INCLUDE_DIR/etc/rc.conf" <<'RCEOF' +KEYMAP="ch" +HARDWARECLOCK="UTC" +TIMEZONE="Europe/Zurich" +RCEOF + +# ── NVIDIA PRIME overlay ──────────────────────────────────────────────── +echo ">>> staging NVIDIA PRIME overlay" + +# Blacklist nouveau — prevents the open-source driver from grabbing the GPU +# before the proprietary nvidia driver. +install -d -m 0755 "$INCLUDE_DIR/etc/modprobe.d" +cat > "$INCLUDE_DIR/etc/modprobe.d/blacklist-nouveau.conf" <<'EOF' +blacklist nouveau +options nouveau modeset=0 +EOF + +# Intel BT on this platform suffers firmware download failures when USB +# autosuspend is active for the btusb adapter. +cat > "$INCLUDE_DIR/etc/modprobe.d/btusb-quirks.conf" <<'EOF' +options btusb enable_autosuspend=0 +EOF + +# Load nvidia modules early so the DRM device node exists before niri starts. +install -d -m 0755 "$INCLUDE_DIR/etc/modules-load.d" +cat > "$INCLUDE_DIR/etc/modules-load.d/nvidia.conf" <<'EOF' +nvidia +nvidia_modeset +nvidia_uvm +nvidia_drm +EOF + +# dracut: include nvidia modules in initramfs; omit nouveau. +install -d -m 0755 "$INCLUDE_DIR/etc/dracut.conf.d" +cat > "$INCLUDE_DIR/etc/dracut.conf.d/10-nvidia.conf" <<'EOF' +add_drivers+=" nvidia nvidia_modeset nvidia_uvm nvidia_drm " +omit_drivers+=" nouveau " +EOF + +# Xorg output-class configs: intel=modesetting (primary), nvidia=PRIME offload. +# Needed by xwayland-satellite for X11 clients running under niri. +install -d -m 0755 "$INCLUDE_DIR/etc/X11/xorg.conf.d" +cat > "$INCLUDE_DIR/etc/X11/xorg.conf.d/10-intel.conf" <<'EOF' +Section "OutputClass" + Identifier "intel" + MatchDriver "i915" + Driver "modesetting" +EndSection +EOF +cat > "$INCLUDE_DIR/etc/X11/xorg.conf.d/20-nvidia.conf" <<'EOF' +Section "OutputClass" + Identifier "nvidia" + MatchDriver "nvidia-drm" + Driver "nvidia" + Option "AllowEmptyInitialConfiguration" + Option "PrimaryGPU" "no" + ModulePath "/usr/lib/nvidia/xorg" + ModulePath "/usr/lib/xorg/modules" +EndSection +EOF + +# prime-run: launch any app on the NVIDIA dGPU via PRIME render-offload. +cat > "$INCLUDE_DIR/usr/local/bin/prime-run" <<'EOF' +#!/bin/sh +# Run a program on the NVIDIA dGPU via PRIME render offload. +exec env __NV_PRIME_RENDER_OFFLOAD=1 \ + __VK_LAYER_NV_optimus=NVIDIA_only \ + __GLX_VENDOR_LIBRARY_NAME=nvidia \ + "$@" +EOF +chmod 0755 "$INCLUDE_DIR/usr/local/bin/prime-run" + +# ── 3g2) niri-session wrapper ───────────────────────────────────────────── +# greetd/tuigreet starts niri-session (not niri --session directly) so that +# /etc/profile is sourced first, ensuring /etc/profile.d/* scripts run and +# XDG_DATA_DIRS gets ~/.nix-profile/share prepended for the compositor and +# all apps it spawns (noctalia-shell, fuzzel, etc.). +install -d -m 0755 "$INCLUDE_DIR/usr/local/bin" +cat > "$INCLUDE_DIR/usr/local/bin/niri-session" <<'EOF' +#!/bin/bash +# niri-session — wrapper started by greetd/tuigreet. +# Sources /etc/profile so that all /etc/profile.d/* scripts run +# (nix paths, wayland env, XDG_DATA_DIRS with ~/.nix-profile/share, etc.) +# before handing off to the compositor. +[ -f /etc/profile ] && . /etc/profile +exec niri --session "$@" +EOF +chmod 0755 "$INCLUDE_DIR/usr/local/bin/niri-session" + # ── 3h) .bash_profile: source .bashrc + launch niri on tty1 ─────────── # agetty autologin on tty1 runs a login shell; .bash_profile execs niri # via dbus-run-session so it gets a D-Bus session bus. cat > "$INCLUDE_DIR/etc/skel/.bash_profile" <<'EOF' [[ -f ~/.bashrc ]] && . ~/.bashrc if [[ "$(tty)" == /dev/tty1 ]] && [[ -z "$WAYLAND_DISPLAY" ]] && [[ -z "$NIRI_SOCKET" ]]; then - exec dbus-run-session -- niri --session + # Wrap niri startup: start GNOME Keyring daemon first (inside the D-Bus session) + # so Chrome/VSCode/apps can store secrets via org.freedesktop.secrets. + exec dbus-run-session -- sh -c ' + if command -v gnome-keyring-daemon >/dev/null 2>&1; then + eval "$(gnome-keyring-daemon --start --components=secrets,pkcs11 2>/dev/null)" || true + export GNOME_KEYRING_CONTROL GNOME_KEYRING_PID SSH_AUTH_SOCK + fi + exec niri --session + ' fi EOF @@ -595,7 +832,7 @@ ISO_PKGS=$(grep -vE '^\s*(#|$)' \ | tr '\n' ' ') TS="$(date -u +%Y%m%d)" OUT_ISO="${OUTPUT_ISO:-$OUT_DIR/void-live-niri-${TS}.iso}" -BOOT_CMDLINE="${BOOT_CMDLINE:-live.user=${LIVE_USER} console=tty0 console=ttyS0,115200}" +BOOT_CMDLINE="${BOOT_CMDLINE:-live.user=${LIVE_USER} console=tty0 console=ttyS0,115200 nvidia-drm.modeset=1 rd.driver.blacklist=nouveau modprobe.blacklist=nouveau btusb.enable_autosuspend=0}" echo ">>> running mklive.sh inside docker — output: $OUT_ISO" "$DOCKER" run --rm --privileged \ diff --git a/iso/postsetup-nvidia.sh b/iso/postsetup-nvidia.sh new file mode 100755 index 0000000..65d2010 --- /dev/null +++ b/iso/postsetup-nvidia.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# void-mklive postsetup hook (-x flag). +# +# Runs AFTER copy_include_directories (overlay applied) but BEFORE +# generate_initramfs (boot initramfs). The xbps initramfs-regenerate trigger +# fires during install_packages — BEFORE the overlay — so the squashfs rootfs's +# own /boot/initramfs-KVER.img is missing our etc/dracut.conf.d/10-nvidia.conf. +# +# This script re-runs dracut inside the rootfs chroot for every installed kernel +# so the squashfs initramfs: +# • includes nvidia.ko (add_drivers from 10-nvidia.conf) +# • excludes nouveau.ko (omit_drivers from 10-nvidia.conf) +# +# $1 = absolute path to the rootfs directory being built. + +set -euo pipefail + +ROOTFS="$1" + +if [[ ! -d "$ROOTFS/usr/lib/modules" ]]; then + echo "[postsetup-nvidia] no modules dir — skipping" + exit 0 +fi + +if [[ ! -x "$ROOTFS/usr/bin/dracut" ]]; then + echo "[postsetup-nvidia] dracut not in rootfs — skipping" + exit 0 +fi + +if [[ ! -f "$ROOTFS/etc/dracut.conf.d/10-nvidia.conf" ]]; then + echo "[postsetup-nvidia] WARNING: 10-nvidia.conf not found in rootfs overlay — skipping" + exit 0 +fi + +# Mount pseudo-filesystems needed by dracut inside the chroot. +_umount_pseudo() { + for f in dev proc sys; do + [[ -d "$ROOTFS/$f" ]] && \ + { umount -R -f "$ROOTFS/$f" 2>/dev/null || umount -R -l "$ROOTFS/$f" 2>/dev/null || true; } + done +} +trap _umount_pseudo EXIT + +for f in dev proc sys; do + mkdir -p "$ROOTFS/$f" + mount --rbind "/$f" "$ROOTFS/$f" +done + +echo "[postsetup-nvidia] regenerating initramfs with nvidia config" +echo "[postsetup-nvidia] 10-nvidia.conf:" +sed 's/^/ /' "$ROOTFS/etc/dracut.conf.d/10-nvidia.conf" + +found=0 +for kdir in "$ROOTFS/usr/lib/modules"/*/; do + [[ -d "${kdir}/kernel" ]] || continue + kver="${kdir##*/}" + found=1 + + # Verify nvidia.ko is present for this kernel version. + nv_ko=$(find "$ROOTFS/usr/lib/modules/$kver" -name 'nvidia.ko' -o -name 'nvidia.ko.zst' 2>/dev/null | head -1) + if [[ -z "$nv_ko" ]]; then + echo "[postsetup-nvidia] WARNING: nvidia.ko not found for kernel $kver — initramfs regeneration skipped" + continue + fi + echo "[postsetup-nvidia] kernel $kver: nvidia.ko at ${nv_ko#"$ROOTFS"}" + + echo "[postsetup-nvidia] running dracut --force for $kver ..." + chroot "$ROOTFS" env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin \ + /usr/bin/dracut --force \ + "/boot/initramfs-${kver}.img" \ + "$kver" 2>&1 | grep -v '^$' | tail -5 || { + echo "[postsetup-nvidia] WARNING: dracut failed for $kver — continuing" + continue + } + + sz=$(ls -lh "$ROOTFS/boot/initramfs-${kver}.img" 2>/dev/null | awk '{print $5}') + echo "[postsetup-nvidia] done: /boot/initramfs-${kver}.img ($sz)" +done + +[[ "$found" -eq 1 ]] || echo "[postsetup-nvidia] WARNING: no kernels found in rootfs" +echo "[postsetup-nvidia] complete"