cursores: cosmic y sway corrían con el puntero INVISIBLE — receta propia, y el vigía medía mal

`vigia-imagen.py` daba ✗ en cursores en DOS de los cuatro escritorios. No es cosmético: con el
cursor por software —obligatorio en virtio-gpu y en todo render por CPU— el compositor dibuja la
imagen que le da el TEMA, y sin tema el ratón se mueve invisible. cosmic llegó a 43/43 y sway a
173/173 así, porque un tema de cursor no es dep de build de nadie: sólo entra si se DECLARA.

`adwaita-cursors` (corpus, 48.1, data-only): del mismo tarball que `adwaita-icon-theme` pero SÓLO
`Adwaita/cursors/` — 39 ficheros y 14 MB, sin un icono. Promover el tema entero habría regalado a
sway y a cosmic los iconos de GNOME, que está anotado como decisión pendiente y no como olvido.

Las dos cosas que el tarball no trae y la receta fabrica:
· los nombres X11 heredados (`left_ptr`, `xterm`, `watch`, `hand2`…) son enlaces que genera el
  `meson.build` de upstream. El mapa se PARSEA de ahí, no se copia: copiado envejece en silencio.
  Si el origen de un enlace no existe, la fase falla — upstream pone un `files()` como aserción.
· `/usr/share/icons/default/index.theme` con `Inherits=Adwaita`. Sin `XCURSOR_THEME` en el
  entorno, libXcursor y wlroots buscan el tema llamado literalmente `default`; sin él no hay
  puntero AUNQUE Adwaita esté instalado. Es el eslabón que hace que ande sin configuración.

⚠ no declarar esta receta junto a `adwaita-icon-theme`: chocan en `Adwaita/cursors/*`.

Y el vigía estaba midiendo el invariante de al lado: exigía `index.theme` en el directorio para
contar un tema, que es correcto para ICONOS —la búsqueda XDG recorre `Directories=`— y falso para
CURSORES, porque libXcursor abre `<tema>/cursors/<nombre>` directo y el índice sólo hace falta para
seguir un `Inherits=`. Con la receta instalada seguía diciendo «NINGÚN tema de cursor».

De paso queda anotado en `targets.toml` que el comentario de cosmic decía «sin ellos arranca sin
puntero» sobre `cosmic-icons`, que no trae cursores: describía una protección que no existía.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGyBj4QyeCZiwLMkP9GJp6
This commit is contained in:
Sergio
2026-09-04 01:38:30 +00:00
co-authored by Claude Opus 5
parent a62ec9061d
commit 8c4e1a6b24
6 changed files with 193 additions and 26 deletions
+27 -8
View File
@@ -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",
+27 -8
View File
@@ -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",
+24 -5
View File
@@ -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",
+9
View File
@@ -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
+94
View File
@@ -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 = []
+12 -5
View File
@@ -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 `<tema>/cursors/<nombre>` 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 `<tema>/cursors/<nombre>` 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):