gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 939Rust 57Shell 37Markdown 331282 Definiciones

⎇ 4f9ee5430f0731c9ed82e118acd6649e772509a3 scripts/wlr/dunst-headless.sh Shell · 120 líneas · 7.0 KB Historia crudo

dunst: sway ya atiende las notificaciones, y hay un píxel que lo prueba 4f9ee5430 · Sergio ·

1#!/bin/sh
2# dunst-headless.sh — ¿la imagen de sway ATIENDE una notificación, y se VE?
3#
4#   scripts/wlr/dunst-headless.sh                     # sale 0 si la notificación se dibuja
5#   scripts/wlr/dunst-headless.sh --negative-control  # borra el .service y exige que NO se vea
6#
7# ── QUÉ PRUEBA, QUE NO ES LO MISMO QUE «ESTÁ INSTALADO» ───────────────────────────────────────
8# Tener `libnotify` en la imagen es media función: el que la completa es un daemon escuchando
9# `org.freedesktop.Notifications`. KDE lo tiene por plasma-workspace, GNOME por gnome-shell, COSMIC
10# por cosmic-notifications; sway no tenía a nadie, y por eso entró `dunst`. Que la receta selle no
11# prueba nada de eso: hay que ver el bus ACTIVAR el daemon y el daemon DIBUJAR.
12#
13# El camino que se ejercita es el de verdad, no un atajo:
14#
15#     notify-send → bus de sesión → activación por el .service → dunst arranca → pinta en sway
16#
17# **Nadie lanza dunst a mano acá**, a propósito: si el `.service` no estuviera, o el `Exec` apuntara
18# a otro lado, o dunst no supiera hablar wayland, este guion no vería nada. Es el mismo camino que
19# recorre una página web pidiendo notificar en `atuq`.
20#
21# ── POR QUÉ notify-send Y NO dunstify ─────────────────────────────────────────────────────────
22# Porque `dunstify` no se shipea: mezcla la GLib ESTÁTICA del corpus con la COMPARTIDA que arrastra
23# `libnotify.so.4` y se cae con SIGSEGV hasta en `--help` (dos copias de GObject en un proceso — el
24# cuadro ya conocido de este repo). El emisor de la casa es `notify-send`, que viene dentro del
25# artefacto de `libnotify` y enlaza toda la cadena compartida. El porqué largo está en la receta.
26#
27# ── LA EVIDENCIA ES UN DIFF DE PÍXELES, NO UN LOG ─────────────────────────────────────────────
28# Se captura ANTES y DESPUÉS de mandar la notificación y se cuenta cuántos píxeles cambiaron en el
29# cuadrante donde dunst dibuja. Un log lleno de líneas verdes es compatible con una pantalla vacía —
30# ya pasó en este mismo andamio— y el color solo tampoco alcanza: swaynag pinta su barra de error en
31# el MISMO rojo que dunst usa para `critical`, así que «hay rojo» no distingue nada. Lo que no se
32# puede falsear es que aparezca algo donde antes no había nada.
33set -eu
34ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
35RFS="${RFS:-/mnt/cosecha/escritorios/sway-rootfs}"
36ALPINE="$ROOT/.dev-fs/alpine"
37CONTROL=0
38[ "${1:-}" = "--negative-control" ] && CONTROL=1
39
40[ -d "$RFS" ] || { echo "✗ no está el rootfs en $RFS — corré antes:" >&2
41                   echo "  scripts/hydrate-profile.py escritorio-sway --into $RFS" >&2; exit 1; }
42command -v bwrap >/dev/null || { echo "✗ falta bwrap" >&2; exit 1; }
43
44OUT="$(mktemp -d)"
45trap 'rm -rf "$OUT"' EXIT
46
47cat > "$OUT/dentro.sh" <<'ADENTRO'
48#!/bin/sh
49export XDG_RUNTIME_DIR=/run/user/0 HOME=/root LANG=C XDG_DATA_DIRS=/usr/share
50export WLR_BACKENDS=headless WLR_HEADLESS_OUTPUTS=1 WLR_RENDERER=pixman
51mkdir -p "$XDG_RUNTIME_DIR" /root; chmod 700 "$XDG_RUNTIME_DIR"
52# El control negativo saca el fichero que hace posible la activación. La jaula corre sobre un
53# overlay temporal, así que borrar acá no toca el rootfs de verdad.
54[ "${CONTROL:-0}" = "1" ] && rm -f /usr/share/dbus-1/services/org.knopwob.dunst.service
55(
56  i=0
57  while [ ! -S /run/user/0/wayland-1 ] && [ $i -lt 40 ]; do sleep 1; i=$((i+1)); done
58  [ -S /run/user/0/wayland-1 ] || { echo "@@ sin socket wayland"; exit 1; }
59  export WAYLAND_DISPLAY=wayland-1
60  /usr/bin/swaybg -c '#1a4b8c' & sleep 3
61  # El bus se levanta CON el entorno wayland puesto: lo que él active lo hereda, igual que en una
62  # sesión de verdad. Al revés, dunst arrancaría sin display y saldría con status 1.
63  eval "$(/usr/bin/dbus-daemon --session --fork --print-address=1 | { read a; echo "DBUS_SESSION_BUS_ADDRESS=$a"; })"
64  export DBUS_SESSION_BUS_ADDRESS
65  /usr/bin/grim /salida/antes.png
66  echo "@@ dueño ANTES: $(timeout 10 /usr/bin/dbus-send --session --dest=org.freedesktop.DBus \
67      --print-reply=literal /org/freedesktop/DBus org.freedesktop.DBus.NameHasOwner \
68      string:org.freedesktop.Notifications 2>&1 | tr -d ' \n')"
69  timeout 25 /usr/bin/notify-send -u critical "hammer" "dunst lo activó el bus" 2>&1
70  echo "@@ notify-send rc=$?"
71  sleep 5
72  echo "@@ servidor: $(timeout 10 /usr/bin/dbus-send --session --dest=org.freedesktop.Notifications \
73      --print-reply=literal /org/freedesktop/Notifications \
74      org.freedesktop.Notifications.GetServerInformation 2>&1 | tr -s ' ')"
75  /usr/bin/grim /salida/despues.png && echo "@@ capturas listas"
76  sleep 1
77  /usr/bin/swaymsg exit 2>/dev/null
78) &
79exec /usr/bin/sway -d 2>&1
80ADENTRO
81
82# `--unshare-pid` para que un `ps` de adentro no vea la máquina entera, y el lab de alpine DEBAJO
83# porque el corpus no produce el intérprete musl (`ld-musl-x86_64.so.1`): sin él ni sway arranca.
84timeout 180 bwrap --overlay-src "$ALPINE" --overlay-src "$RFS" --tmp-overlay / \
85  --bind "$OUT" /salida --dev /dev --proc /proc --tmpfs /run --tmpfs /tmp --unshare-pid \
86  --uid 0 --gid 0 --setenv PATH /usr/bin:/bin:/usr/sbin:/sbin --setenv CONTROL "$CONTROL" \
87  /bin/sh /salida/dentro.sh > "$OUT/log.txt" 2>&1 || true
88
89grep -a '@@' "$OUT/log.txt" || true
90[ -f "$OUT/antes.png" ] && [ -f "$OUT/despues.png" ] || {
91  echo "✗ no hubo capturas — mirá $OUT/log.txt" >&2; cp "$OUT/log.txt" work/dunst-headless.log 2>/dev/null || true; exit 1; }
92
93# El diff se hace acá afuera: dentro de la imagen no hay python ni con qué mirar un PNG.
94CAMBIADOS="$(python3 - "$OUT/antes.png" "$OUT/despues.png" <<'PY'
95import sys
96from PIL import Image, ImageChops
97a, b = (Image.open(p).convert("RGB") for p in sys.argv[1:3])
98# Sólo el cuadrante donde dunst dibuja por defecto (arriba a la derecha). Acotarlo saca del medio la
99# barra de swaynag y el reloj de swaybar, que cambian solos y darían un falso positivo.
100w, h = a.size
101caja = (w // 2, h // 12, w, h // 2)
102d = ImageChops.difference(a.crop(caja), b.crop(caja)).convert("L")
103# Por histograma y no recorriendo píxeles: el bin 0 son los que NO cambiaron.
104print(d.width * d.height - d.histogram()[0])
105PY
106)"
107echo "  píxeles cambiados en el cuadrante de dunst: $CAMBIADOS"
108
109if [ "$CONTROL" = "1" ]; then
110  if [ "$CAMBIADOS" -lt 500 ]; then
111    echo "✓ control negativo: sin el .service no se dibuja nada (el guardián SÍ lo vería)"; exit 0
112  fi
113  echo "✗ control negativo: se dibujó una notificación SIN el .service — este guion no prueba lo que dice" >&2
114  exit 1
115fi
116[ "$CAMBIADOS" -ge 500 ] || {
117  echo "✗ la notificación no se dibujó ($CAMBIADOS píxeles) — capturas en $OUT" >&2
118  cp "$OUT"/*.png "$OUT/log.txt" work/ 2>/dev/null || true; exit 1; }
119cp "$OUT/despues.png" work/dunst-headless.png 2>/dev/null || true
120echo "✓ el bus activó dunst y la notificación se dibujó (captura en work/dunst-headless.png)"