diff --git a/docs/state/build-state-cosmic.json b/docs/state/build-state-cosmic.json index 170e572c..40ac9e37 100644 --- a/docs/state/build-state-cosmic.json +++ b/docs/state/build-state-cosmic.json @@ -1,23 +1,23 @@ { "schema": "hammer-build-state/1", "totals": { - "recipes": 856, - "nodes": 858, - "sealed": 856, + "recipes": 857, + "nodes": 859, + "sealed": 857, "ajeno": 2 }, "by_class": { "ajeno": 2, "c": 203, "go": 362, - "gui": 35, + "gui": 36, "kernel": 6, "rust": 250 }, "debt_by_class": {}, "by_queue": { "corpus": { - "sealed": 819, + "sealed": 820, "ajeno": 2 }, "incoming-cosmic": { @@ -36,9 +36,9 @@ "sealed": 75 }, "escritorio-cosmic": { - "raices": 39, - "clausura": 133, - "sealed": 133 + "raices": 40, + "clausura": 134, + "sealed": 134 }, "escritorio-mirada": { "raices": 14, @@ -104,6 +104,25 @@ "perfiles": [], "dependientes_total": 0 }, + "adwaita-cursors": { + "iname": "adwaita-cursors", + "version": "48.1", + "link": "dynamic", + "compiler": "zig-cc", + "deps": [], + "cls": "gui", + "queue": "corpus", + "foreign": false, + "hash": "b3:22ae0ff6369e6f15cb6810ddd979d95ebe38307e8da668c1210d9434ef9b5b05", + "state": "sealed", + "blocked_by": [], + "unblocks": 0, + "perfiles": [ + "escritorio-cosmic", + "escritorio-sway" + ], + "dependientes_total": 0 + }, "adwaita-hello": { "iname": "adwaita-hello", "version": "1.7.6", diff --git a/docs/state/build-state-wlr.json b/docs/state/build-state-wlr.json index 90151c43..51eebbd1 100644 --- a/docs/state/build-state-wlr.json +++ b/docs/state/build-state-wlr.json @@ -1,23 +1,23 @@ { "schema": "hammer-build-state/1", "totals": { - "recipes": 834, - "nodes": 836, - "sealed": 834, + "recipes": 835, + "nodes": 837, + "sealed": 835, "ajeno": 2 }, "by_class": { "ajeno": 2, "c": 202, "go": 362, - "gui": 35, + "gui": 36, "kernel": 6, "rust": 229 }, "debt_by_class": {}, "by_queue": { "corpus": { - "sealed": 820, + "sealed": 821, "ajeno": 2 }, "incoming-wlr": { @@ -41,9 +41,9 @@ "sealed": 33 }, "escritorio-sway": { - "raices": 70, - "clausura": 173, - "sealed": 173 + "raices": 71, + "clausura": 174, + "sealed": 174 } }, "sin_perfil": 645, @@ -104,6 +104,25 @@ "perfiles": [], "dependientes_total": 0 }, + "adwaita-cursors": { + "iname": "adwaita-cursors", + "version": "48.1", + "link": "dynamic", + "compiler": "zig-cc", + "deps": [], + "cls": "gui", + "queue": "corpus", + "foreign": false, + "hash": "b3:22ae0ff6369e6f15cb6810ddd979d95ebe38307e8da668c1210d9434ef9b5b05", + "state": "sealed", + "blocked_by": [], + "unblocks": 0, + "perfiles": [ + "escritorio-cosmic", + "escritorio-sway" + ], + "dependientes_total": 0 + }, "adwaita-hello": { "iname": "adwaita-hello", "version": "1.7.6", diff --git a/docs/state/build-state.json b/docs/state/build-state.json index d53783e8..3998381f 100644 --- a/docs/state/build-state.json +++ b/docs/state/build-state.json @@ -1,23 +1,23 @@ { "schema": "hammer-build-state/1", "totals": { - "recipes": 821, - "nodes": 823, - "sealed": 821, + "recipes": 822, + "nodes": 824, + "sealed": 822, "ajeno": 2 }, "by_class": { "ajeno": 2, "c": 189, "go": 362, - "gui": 35, + "gui": 36, "kernel": 6, "rust": 229 }, "debt_by_class": {}, "by_queue": { "corpus": { - "sealed": 821, + "sealed": 822, "ajeno": 2 } }, @@ -96,6 +96,25 @@ "perfiles": [], "dependientes_total": 0 }, + "adwaita-cursors": { + "iname": "adwaita-cursors", + "version": "48.1", + "link": "dynamic", + "compiler": "zig-cc", + "deps": [], + "cls": "gui", + "queue": "corpus", + "foreign": false, + "hash": "b3:22ae0ff6369e6f15cb6810ddd979d95ebe38307e8da668c1210d9434ef9b5b05", + "state": "sealed", + "blocked_by": [], + "unblocks": 0, + "perfiles": [ + "escritorio-cosmic", + "escritorio-sway" + ], + "dependientes_total": 0 + }, "adwaita-hello": { "iname": "adwaita-hello", "version": "1.7.6", diff --git a/docs/state/targets.toml b/docs/state/targets.toml index c7e7939f..76a7e4de 100644 --- a/docs/state/targets.toml +++ b/docs/state/targets.toml @@ -407,6 +407,10 @@ paquetes = [ # tipografía. Ninguno es dep de build de nadie y sin ellos el escritorio arranca roto — sin puntero, # sin teclado o con tofu. "cosmic-icons", "hicolor-icon-theme", "xkeyboard-config", "dejavu-fonts", + # ⚠ «sin puntero» lo decía este comentario desde que se escribió y NO era cierto: `cosmic-icons` + # instala iconos de aplicación, no cursores, así que este perfil llegó a 43/43 con el puntero + # INVISIBLE. Lo cazó `vigia-imagen.py` el 2026-09-04. Los cursores son su propia raíz. + "adwaita-cursors", # `dejavu-fonts-nerd` es la MISMA DejaVu Sans Mono parcheada con Nerd Fonts (~10.000 glifos de # iconos: Powerline, Font Awesome, Devicons…) que usan starship, lsd, eza, neovim y las # statuslines; sin ellos dibujan cuadraditos. Se AÑADE, no reemplaza: la licencia de Bitstream @@ -520,6 +524,11 @@ paquetes = [ "dejavu-fonts-nerd", "xkeyboard-config", # sin datos XKB: «xkbcommon: ERROR: failed to add default include path # /usr/share/X11/xkb», y sway no completa el seat + # Cursores. Mismo caso que las fuentes y el XKB, y con el mismo modo de fallo silencioso: con el + # cursor por software el compositor dibuja lo que le da el TEMA, y sin tema el puntero se mueve + # invisible. Este perfil llegó a 173/173 sin uno. Trae además `/usr/share/icons/default`, que es + # lo que buscan libXcursor y wlroots cuando nadie fijó `XCURSOR_THEME` — o sea, siempre acá. + "adwaita-cursors", # ── LAS TRES `.so` QUE sway PIDE Y EL CORPUS SÓLO PRODUCE COMO `.a` ───────────────────────── # `sway` es `link = "dynamic"` a propósito (dlopea los drivers DRI de mesa) y sale con # `NEEDED libz.so.1 / libexpat.so.1 / libffi.so.8`. Las canónicas son `--disable-shared`, así que diff --git a/recipes/adwaita-cursors.toml b/recipes/adwaita-cursors.toml new file mode 100644 index 00000000..4548e42a --- /dev/null +++ b/recipes/adwaita-cursors.toml @@ -0,0 +1,94 @@ +# adwaita-cursors 48.1 — SÓLO los cursores de Adwaita, en el CORPUS. +# +# ══ EL HUECO QUE TAPA ══════════════════════════════════════════════════════════════════════════ +# `vigia-imagen.py` mide un invariante que ninguna métrica de clausura ve: **≥1 tema de cursor con +# `cursors/` poblado**. El 2026-09-04 daba ✗ en DOS de los cuatro escritorios —`escritorio-cosmic` +# y `escritorio-sway`—, y eso no es cosmético: con el cursor por software (obligatorio en +# virtio-gpu y en cualquier render por CPU) el compositor dibuja la imagen que le da el tema, así +# que sin tema **el puntero se mueve invisible**. Es la misma figura que las fuentes de GNOME y que +# los iconos de KDE: sellado ≠ usable, y el agujero no es dep de build de nadie. +# +# ══ POR QUÉ UNA RECETA NUEVA Y NO PROMOVER `adwaita-icon-theme` ════════════════════════════════ +# Los cursores viven DENTRO de `adwaita-icon-theme` (recipes/incoming-gnome/), que son ~5000 +# ficheros de tema de ICONOS. Promoverla al corpus para conseguir un puntero le regalaría a sway y +# a cosmic el tema de iconos de GNOME — y eso está declarado como una DECISIÓN pendiente, no como +# un olvido (`vigia-imagen.py` lo marca ⊘ en sway a propósito). Esta receta corta por lo sano: +# mismo tarball, misma versión, **sólo `Adwaita/cursors/`** (~1 MB), sin un solo icono. +# +# ⚠ **NO declarar esta receta y `adwaita-icon-theme` en el MISMO perfil**: las dos instalan +# `/usr/share/icons/Adwaita/cursors/*` y chocarían fichero a fichero — la misma colisión por la que +# zathura no entra en `escritorio-kde` (dos poppler). `escritorio-gnome` ya tiene sus cursores por +# el tema completo y NO lleva ésta. +# +# ══ LAS DOS COSAS QUE EL TARBALL NO TRAE Y HAY QUE FABRICAR ════════════════════════════════════ +# 1. **Los nombres X11 heredados.** El tarball trae los nombres CSS (`default`, `pointer`, `text`…); +# `left_ptr`, `hand2`, `xterm`, `watch` y compañía son ENLACES que genera el `meson.build` +# (`cursor_symlinks`). Una app que pida `left_ptr` —y las viejas piden eso— no encontraría nada. +# El mapa se PARSEA de `meson.build`, no se copia a mano: la lista autoritativa es upstream, y +# copiada envejece en silencio. Si el origen del enlace no existe, la fase FALLA: upstream pone +# ahí un `files()` justamente como aserción, y perderla convertiría un cambio de upstream en un +# enlace roto que nadie ve. +# 2. **El tema `default`.** Sin `XCURSOR_THEME` en el entorno, libXcursor y wlroots buscan el tema +# literalmente llamado `default`; si no existe, no hay puntero AUNQUE `Adwaita` esté instalado. +# Por eso se escribe `/usr/share/icons/default/index.theme` con `Inherits=Adwaita`. Es el +# eslabón que hace que esto funcione en una imagen SIN configuración, que es el caso real. +# +# Los `.cur` son los cursores de Windows: upstream los excluye en la instalación de Linux +# (`exclude_files: win32_cursors`) y acá también, por el mismo motivo. +# +# Data-only: no hay un compilador involucrado. +name = "adwaita-cursors" +version = "48.1" +license = "LGPL-3.0-or-later OR CC-BY-SA-3.0" + +[source] +tarball = "https://download.gnome.org/sources/adwaita-icon-theme/48/adwaita-icon-theme-48.1.tar.xz" +sha256 = "cbfe9b86ebcd14b03ba838c49829f7e86a7b132873803b90ac10be7d318a6e12" + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "dynamic" + +[build.phases] +configure = "true" +compile = "true" +install = ''' +set -e +CUR=/out/usr/share/icons/Adwaita/cursors +mkdir -p "$CUR" + +# los cursores propiamente dichos, sin los .cur de Windows +find Adwaita/cursors -maxdepth 1 -type f ! -name '*.cur' -exec install -Dm0644 {} "$CUR"/ \; +# MEDIDO, no supuesto: 48.1 trae 37 cursores reales (nombres CSS) + 31 `.cur` de Windows. El +# primer intento puso el piso en 50 «por si acaso» y la fase falló contra el tarball CORRECTO — +# un guardián calibrado por encima de la escala real no protege: bloquea. +N=$(ls -1 "$CUR" | wc -l) +[ "$N" -ge 30 ] || { echo "sólo $N cursores: el tarball cambió de forma" >&2; exit 1; } + +# los nombres X11 heredados, leídos del meson.build de upstream (no copiados a mano) +sed -n '/cursor_symlinks = {/,/^ }/p' meson.build \ + | sed -n "s/^ *'\([^']*\)': *\[\([^]]*\)\].*/\1 \2/p" \ + | while read -r SRC RESTO; do + [ -e "$CUR/$SRC" ] || { echo "el mapa nombra un cursor ausente: $SRC" >&2; exit 1; } + echo "$RESTO" | tr ',' '\n' | tr -d " '" | while read -r DST; do + [ -n "$DST" ] && ln -sf "$SRC" "$CUR/$DST" + done + done + +# el tema `default`: sin esto, una imagen sin XCURSOR_THEME no encuentra NINGÚN cursor +mkdir -p /out/usr/share/icons/default +cat > /out/usr/share/icons/default/index.theme <<'EOF' +[Icon Theme] +Name=Default +Comment=Alias al tema de cursores real +Inherits=Adwaita +EOF + +# la comprobación que no se puede cumplir por accidente: el nombre que pide TODA app vieja +[ -e "$CUR/left_ptr" ] || { echo "sin left_ptr: el mapa de enlaces no se aplicó" >&2; exit 1; } +[ -e "$CUR/default" ] || { echo "sin el cursor 'default': el tema no sirve" >&2; exit 1; } +''' + +[deps] +build = [] diff --git a/scripts/vigia-imagen.py b/scripts/vigia-imagen.py index be860723..544054ae 100644 --- a/scripts/vigia-imagen.py +++ b/scripts/vigia-imagen.py @@ -30,7 +30,8 @@ # FALLBACK obligatorio del estándar y no trae iconos propios: es el contrato, no el # contenido. # cursores ≥1 tema con `cursors/` poblado. Con el cursor por software —obligatorio en virtio-gpu— -# sin tema el puntero se mueve INVISIBLE. +# sin tema el puntero se mueve INVISIBLE. A diferencia de los iconos, acá NO se exige +# `index.theme`: libXcursor abre `/cursors/` directo. # fuentes ≥1 `.ttf/.otf/.ttc/.pfb`. fontconfig es el motor que BUSCA fuentes, no una fuente. # terminal ≥1 `.desktop` con `TerminalEmulator` en Categories. Un escritorio sin terminal no es # uno incompleto: es uno del que no se puede salir cuando algo falla. @@ -118,8 +119,14 @@ def escanear(nodos): if os.path.isdir(base): for t in sorted(os.listdir(base)): td = os.path.join(base, t) - if not os.path.exists(os.path.join(td, "index.theme")): - continue # un directorio sin index.theme NO es un tema + # ⚠ ICONOS y CURSORES no se buscan igual, y medirlos con la misma regla dio un ✗ + # falso el 2026-09-04: `adwaita-cursors` instalaba `Adwaita/cursors/` poblado y este + # vigía seguía diciendo «NINGÚN tema de cursor» porque el directorio no traía + # `index.theme`. Para un tema de ICONOS ese requisito es correcto —la búsqueda XDG + # recorre `Directories=` y sin el índice el tema no existe—, pero libXcursor y + # wlroots abren `/cursors/` DIRECTO; `index.theme` sólo hace falta + # para seguir un `Inherits=`. Exigirlo también acá medía el invariante de al lado. + tiene_indice = os.path.exists(os.path.join(td, "index.theme")) n_ico = n_cur = 0 for dp, _, fn in os.walk(td): for f in fn: @@ -127,11 +134,11 @@ def escanear(nodos): n_ico += 1 elif os.path.basename(dp) == "cursors": n_cur += 1 - if n_ico: + if n_ico and tiene_indice: temas.setdefault(t, (nombre, n_ico)) if n_cur: cursores.setdefault(t, (nombre, n_cur)) - if t == "hicolor": + if t == "hicolor" and tiene_indice: temas.setdefault("hicolor", (nombre, n_ico)) # ── el resto, en UNA pasada por el árbol ──────────────────────────────────────────────── for dp, _, fn in os.walk(d):