Files
takana/recipes/incoming-wlr/dunst.toml
T
SergioandClaude Opus 5 4f9ee5430f dunst: sway ya atiende las notificaciones, y hay un píxel que lo prueba
Era la única mitad que quedaba abierta del mapa del §6.10: KDE atiende
`org.freedesktop.Notifications` con plasma-workspace, GNOME con gnome-shell y
COSMIC con cosmic-notifications; sway no tenía a NADIE, así que una página que
pedía notificar mandaba el mensaje al bus y se lo comía el silencio.

`dunst` 1.12.2 y no `mako` —que es el de la casa wlroots— por dos razones
medidas: el nombre `mako` YA ESTÁ OCUPADO en el corpus por el motor de plantillas
de mesa, y el cierre del perfil incluye las herramientas de build, así que la
imagen terminaría con dos artefactos homónimos peleando por las mismas rutas; y
mako habla D-Bus por sd-bus, que en musl es una receta nueva (basu), mientras
dunst habla por GDBus, que ya está sellado. Cero dependencias nuevas.

DOS GUARDIANES EN LA PROPIA RECETA, porque las dos cosas que importan no se ven
en que compile:

- `X11=0` es una intención; el hecho se lee en el ELF. dunst compila los dos
  backends por defecto y su config.mk avisa: sin wayland «forzará xwayland». En
  esta distro eso sella un binario que arranca, toma el bus y NO PINTA NUNCA. La
  fase exige `libwayland-client` entre los NEEDED y prohíbe `libX11`.
- El `.service` de D-Bus es la mitad que importa: sin systemd, al daemon lo
  levanta EL BUS. Se comprueba que reclame `org.freedesktop.Notifications` y se
  le saca la línea `SystemdService=`, que acá no puede significar nada.

Y `DUNSTIFY=0`: entró primero en 1 y el binario **segfaultea hasta en
`--help`** (rc=139 en las tres pruebas). La causa es el cuadro que este repo ya
tiene escrito: nuestro `libnotify.so.4` es compartido y arrastra la cadena glib
`.so`, mientras esta cola enlaza la GLib estática ⇒ dos copias de GObject en un
proceso. No se pierde nada: el emisor de la casa es `notify-send`, que viene
DENTRO del artefacto de libnotify y ya está en las cuatro imágenes. Hay un
guardián que frena el día que alguien vuelva a poner DUNSTIFY=1 sin leer.

LA PRUEBA, en `scripts/wlr/dunst-headless.sh`: sway headless + bus de sesión +
`notify-send`, y NADIE lanza dunst a mano — lo activa el bus por el .service,
que es el mismo camino de una página web en atuq. La evidencia es un diff de
píxeles antes/después acotado al cuadrante donde dunst dibuja, porque un log en
verde es compatible con una pantalla vacía y el color no distingue (swaynag pinta
su barra de error en el mismo rojo).

    positivo  14832 píxeles cambiados · servidor: dunst knopwob 1.12.2 1.2
    control   0 píxeles · ServiceUnknown · notify-send rc=1

El control negativo borra el .service dentro del overlay temporal y EXIGE que no
se dibuje nada: probado en los dos sentidos.

Captura en docs/evidencia/dunst-sway-notificacion-2026-09-07.png.
Vigía de sonames: escritorio-sway 203 → 204 nodos, 512 sonames, 0 sin proveedor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
2026-09-07 18:15:29 +00:00

144 lines
9.5 KiB
TOML

# dunst 1.12.2 — el daemon de notificaciones que le faltaba a la imagen de sway.
#
# ── POR QUÉ EXISTE ESTA RECETA, Y POR QUÉ ACÁ ──────────────────────────────────────────────────
# El SDD 26 §6.10 midió perfil por perfil quién ATIENDE `org.freedesktop.Notifications`, que es la
# otra mitad de tener `libnotify`:
#
# escritorio-kde plasma-workspace ✓ completa
# escritorio-gnome gnome-shell ✓ completa
# escritorio-cosmic cosmic-notifications ✓ completa
# escritorio-sway — ✗ MEDIA: nadie atiende
#
# Con `libnotify` en la imagen y nadie del otro lado, una página que pide notificar no falla: el
# navegador manda el mensaje al bus y no lo recoge nadie. Media función, cero errores — la forma que
# este proyecto ya se cansó de pagar.
#
# ── POR QUÉ dunst Y NO mako, QUE ES EL DE LA CASA wlroots ──────────────────────────────────────
# Por dos razones medidas, no por gusto:
#
# 1. **El nombre `mako` YA ESTÁ OCUPADO en el corpus** por el motor de plantillas de Python que usa
# Mesa para su codegen. Un `recipes/incoming-wlr/mako.toml` no lo pisaría en el disco —la
# resolución es sibling-first— pero el cierre del perfil incluye las herramientas de build, así
# que la imagen de sway terminaría con DOS artefactos llamados `mako` peleando por las mismas
# rutas al hidratar. Es exactamente la colisión de homónimos que la granja ya nos cobró una vez.
# 2. mako habla D-Bus por `sd-bus`, que en musl significa una receta nueva (`basu`). dunst habla por
# **GDBus**, o sea `gio-2.0`, que ya está sellado. Cero recetas nuevas de dependencia.
#
# ── WAYLAND=1 X11=0, Y EL GUARDIÁN QUE LO EXIGE ───────────────────────────────────────────────
# dunst compila los dos backends por defecto y el `config.mk` avisa de la trampa con todas las
# letras: «Disable dependency on wayland. This will force dunst to use xwayland». O sea que un build
# descuidado sella un binario que en esta distro —Wayland-only, sin GLX y sin Xwayland nativo— NO
# PINTA NADA y tampoco falla al arrancar. Por eso el `X11=0` no alcanza como intención: la fase
# `install` comprueba en el ELF que `libwayland-client` esté entre los NEEDED y que `libX11` NO
# esté. La intención se declara en el flag; el hecho se lee en el binario.
#
# ── EL FICHERO DE SERVICIO D-BUS ES LA MITAD QUE IMPORTA ──────────────────────────────────────
# Sin systemd (`SYSTEMD=0`, que es la postura de la distro) el daemon lo arranca **el bus**: el
# `.service` declara `Name=org.freedesktop.Notifications` con su `Exec`, y cualquier app que pida ese
# nombre lo levanta sola. Sin ese fichero habría que acordarse de lanzar dunst a mano en la config de
# sway, que es justo el tipo de «lo instalé y no anda» que este proyecto trata de no dejar. Se le
# quita la línea `SystemdService=`, que acá no puede significar nada, y la fase comprueba que el
# nombre bien escrito quedó adentro.
#
# ── `DUNSTIFY=0`: EL EMISOR QUE TRAE dunst SEGFAULTEA, Y NO ES CULPA DE dunst ─────────────────
# Entró primero con `DUNSTIFY=0` —parecía gratis, `libnotify` ya está en el corpus— y el binario
# resultante **muere con SIGSEGV hasta en `dunstify --help`**, o sea antes de tocar el bus. Medido en
# la jaula: rc=139 en las tres pruebas.
#
# La causa es el cuadro que este repo ya tiene escrito: **dos GLib en el mismo proceso**. Nuestro
# `libnotify.so.4` es COMPARTIDO y arrastra `libgobject/libglib/libgio/libgdk_pixbuf` `.so`
# (`readelf -d` lo dice), mientras que dunst y sus clientes de esta cola enlazan la GLib **estática**
# del corpus. Un dunstify que mezcla las dos termina con dos copias del sistema de tipos de GObject y
# se cae al arrancar. El daemon NO tiene el problema: no toca libnotify.
#
# Y no se pierde nada, que es lo que hace que ésta sea la salida correcta y no una rendición: el
# emisor de la casa es **`notify-send`, que viene DENTRO del artefacto de `libnotify`** y ya está en
# las cuatro imágenes, enlazado de forma consistente contra la cadena compartida. Un binario que
# segfaultea shipeado en la imagen es peor que no shipearlo.
#
# ── EL BINARIO PESA 46 MB, Y LA PALANCA NO ESTÁ EN ESTA RECETA ───────────────────────────────
# Anotado porque el reflejo se equivoca dos veces. `config.mk` hardcodea `-g`, así que uno prueba
# `EXTRA_CFLAGS=-g0` esperando bajar decenas de megas: medido, 46.296.624 → 45.933.472 bytes, o sea
# 0,8%. Y sin embargo la grasa SÍ es depuración — `strip --strip-debug` deja el binario en 7.627.464
# bytes, los mismos con `-g0` y sin él. Son 38 MB de `.debug_*` que vienen de los `.a` de
# glib/gio/pango/cairo/gdk-pixbuf, no de compilar dunst.
# Por eso acá no se toca: strippear en el enlace sería inventar una política en UNA receta cuando el
# corpus entero tiene la misma forma (swaybg 22 MB, fuzzel 10 MB) y ya hay un documento para eso, el
# SDD 23. Lo que corresponde es medirlo y dejarlo escrito donde se ve.
#
# `VERSION` se pasa a mano: el Makefile la saca de `git describe` y cae a un literal si no hay `.git`.
# Con el commit pineado las dos ramas son deterministas, pero cuál de las dos corre depende de si el
# árbol de fuentes trae `.git` — un detalle del build system que no tiene por qué entrar al binario.
name = "dunst"
version = "1.12.2"
license = "BSD-3-Clause"
[source]
# Por commit y no por `/archive/<tag>.tar.gz`, misma razón que `fuzzel` y `foot`: el tarball lo
# genera la forge al vuelo y su sha256 se muere solo cuando GitHub actualiza su git/gzip.
# ⚠ El tag `v1.12.2` es anotado; éste es el commit desreferenciado (`v1.12.2^{}`).
repo = "https://github.com/dunst-project/dunst.git"
commit = "fc78b9f11aa8b63f4d81c96466206a9b63a22f07"
[build]
compiler = "zig-cc"
target = "x86_64-linux-musl"
link = "dynamic"
zig_version = "0.13.0"
[build.phases]
configure = '''
# ── EL `-lexpat` TIENE QUE IR AL FINAL, Y EL Makefile NO DEJA ────────────────────────────────
# `libfontconfig.a` referencia XML_* y el expat estático hay que darlo DESPUÉS: el linker resuelve
# de izquierda a derecha. En las recetas meson de esta cola eso se hace con `-Dc_link_args=-lexpat`,
# pero acá manda un Makefile que arma la línea como `${LDFLAGS} = DEFAULT + <lo que pasás> + LIBS`,
# o sea que cualquier `-lexpat` nuestro cae ANTES de fontconfig y no sirve de nada. Sobreescribir
# `LIBS` significaría reproducir a mano toda la salida de pkg-config.
# La salida es un wrapper de un solo token que agrega la librería al final de CADA invocación —el
# mismo truco que usa `recipes/ffmpeg.toml` para sus `--cc/--ar`—. En las compilaciones `-c` el
# `-lexpat` sobra y clang lo ignora con un aviso.
ZW="$PWD/.zwrap"; mkdir -p "$ZW"
printf '#!/bin/sh\nexec %s "$@" -lexpat\n' "$CC" > "$ZW/cc"
chmod +x "$ZW/cc"
'''
compile = '''
make -j"$(nproc)" CC="$PWD/.zwrap/cc" WAYLAND=1 X11=0 SYSTEMD=0 DUNSTIFY=0 \
PREFIX=/usr SYSCONFDIR=/etc/xdg VERSION="1.12.2"
'''
install = '''
make DESTDIR=/out CC="$PWD/.zwrap/cc" WAYLAND=1 X11=0 SYSTEMD=0 DUNSTIFY=0 \
PREFIX=/usr SYSCONFDIR=/etc/xdg VERSION="1.12.2" install
# ── GUARDIÁN 1: ¿es el backend de wayland, o quedó el de X11? ────────────────────────────────
# «Compilé con X11=0» es una intención; esto es el hecho. Un dunst con backend X11 en una distro
# sin Xwayland arranca, toma el bus y no dibuja nunca.
readelf -d /out/usr/bin/dunst | grep -q 'libwayland-client' || {
echo "guardián: dunst NO enlaza libwayland-client — el backend wayland no entró" >&2; exit 1; }
if readelf -d /out/usr/bin/dunst | grep -q 'libX11'; then
echo "guardián: dunst enlaza libX11 — X11=0 no agarró" >&2; exit 1
fi
# ── GUARDIÁN 2: el nombre del bus, escrito en el fichero que lo activa ───────────────────────
SVC=/out/usr/share/dbus-1/services/org.knopwob.dunst.service
[ -f "$SVC" ] || { echo "guardián: no se instaló el .service de D-Bus — nadie lo activaría" >&2; exit 1; }
grep -q '^Name=org.freedesktop.Notifications$' "$SVC" || {
echo "guardián: el .service no reclama org.freedesktop.Notifications" >&2; exit 1; }
# Sin systemd esa línea no puede significar nada; se va para que el bus no la mire siquiera.
sed -i '/^SystemdService=/d' "$SVC"
test -x /out/usr/bin/dunstctl || { echo "guardián: falta dunstctl" >&2; exit 1; }
# Y que NO se haya colado el emisor roto: si alguien vuelve a poner DUNSTIFY=1 sin leer el porqué
# de arriba, esto lo frena acá y no en la pantalla de un usuario.
if [ -e /out/usr/bin/dunstify ]; then
echo "guardián: se instaló dunstify — segfaultea por las dos GLib; el emisor es notify-send" >&2
exit 1
fi
echo "guardián: dunst wayland-only, y el bus lo puede activar por org.freedesktop.Notifications"
'''
[deps]
build = ["pkgconf", "perl", "wayland", "wayland-protocols", "glib", "pango", "cairo",
"gdk-pixbuf", "libnotify", "pixman", "fontconfig", "freetype", "harfbuzz", "fribidi",
"libpng", "expat", "libffi", "pcre2", "zlib"]
run = ["wayland", "glib", "pango", "cairo", "gdk-pixbuf"]