Diagnosticado por el usuario en el OptiPlex 3060 quitando los pipes de say(). Abrir un puerto serie sin portadora BLOQUEA en open() esperando DCD, salvo que el termios tenga CLOCAL. El kernel pone CLOCAL cuando registra el puerto COMO CONSOLA, y eso pasa en QEMU porque el cmdline lleva `console=ttyS0,115200`. En ese metal la firmware NO aplica el CONFIG_CMDLINE horneado —el kernel arranca con `Command line:` VACÍO— así que ttyS0 es un puerto común, sin cable: el PRIMER say() se colgaba ahí. Explica los tres síntomas que no cerraban: - /var/log/cosmic se creaba (el mkdir va antes) pero el .log nunca aparecía; - `cosmic-start > /salida.txt` daba VACÍO ⇒ parecía que el script no se ejecutaba, cuando estaba bloqueado en su primera línea de salida; - la corrida terminaba siempre en «log del compositor (primer tramo)»: no era el último paso, era el dump() bloqueándose en el mismo sitio. El serial no se pierde: cuando ES la consola, /dev/console YA ES ttyS0. Cuando no lo es, no había nadie del otro lado. En metal lo que sirve es el fichero de log. Con say() destrabado, la detección de GL quedó CONFIRMADA en metal por primera vez: == cosmic :: GL por HARDWARE — kernel drm=i915 + iris_dri.so (sin overrides) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
382 lines
24 KiB
Bash
Executable File
382 lines
24 KiB
Bash
Executable File
#!/bin/sh
|
|
# cosmic-start — levanta la sesión COSMIC. Lo corre el getty (o el usuario a mano), y es EL MISMO
|
|
# fichero en la imagen de QEMU y en la de metal. Hermano de gnome-start y plasma-start; hereda de
|
|
# ellos todo lo que ya está pago y sólo diverge en lo que COSMIC necesita.
|
|
#
|
|
# ⚠ SE LLAMABA `cosmic-start-qemu.sh` Y ESO COSTÓ UN VIAJE FÍSICO (2026-08-05). El nombre decía la
|
|
# verdad —forzaba render por software con `LIBGL_ALWAYS_SOFTWARE` y `MESA_LOADER_DRIVER_OVERRIDE=
|
|
# kms_swrast`, que es lo correcto sobre virtio-gpu— pero la imagen de METAL lo instalaba igual, con
|
|
# un comentario que afirmaba que «nada de lo que hace es específico del emulador». En un Coffee Lake
|
|
# real la mesa del corpus es `iris` y NO EXISTE ningún `kms_swrast_dri.so`: el override no degrada a
|
|
# software, hace fallar EGL y el compositor no arranca. Y en el mejor caso habría sido peor: si
|
|
# hubiera arrancado, habría compuesto por software y ScreenCast habría fallado por el mismo motivo
|
|
# que en QEMU ⇒ **el viaje no podía contestar la pregunta que lo motivaba**.
|
|
# Por eso ahora el modo de GL se DETECTA (ver más abajo) en vez de hardcodearse, y el fichero se
|
|
# llama por lo que hace y no por dónde lo probamos.
|
|
#
|
|
# Es `/bin/sh` (busybox ash) a propósito, aunque `start-cosmic` de upstream sea bash: este script
|
|
# corre ANTES, prepara el entorno, y después le pasa la posta. Cuanto menos dependa, más temprano
|
|
# puede reportar.
|
|
set -u
|
|
# PATH ARRIBA DE TODO, y no es cosmético. El getty entrega un PATH que NO trae /usr/bin, y en esta
|
|
# imagen `mkdir` sólo existe ahí (`/usr/bin/mkdir → coreutils`; la base metal no tendió el symlink
|
|
# `/bin/mkdir → busybox` que sí tienen chmod, mount, sed y tee). Resultado: los cuatro `mkdir -p` de
|
|
# la preparación fallaban con «mkdir: not found» y la sesión arrancaba IGUAL, porque los directorios
|
|
# venían horneados en la imagen. Un fallo que no se cobra nada hasta que alguien cambia la imagen.
|
|
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
|
|
|
# ── DÓNDE VAN LOS LOGS: al DISCO, no a /tmp ─────────────────────────────────────────────────────
|
|
# /tmp es tmpfs, o sea RAM. En QEMU da igual porque el serial queda en el terminal del que lanzó la
|
|
# VM; en METAL, si la sesión falla, la evidencia se evapora al apagar y el viaje físico se gasta sin
|
|
# aprender nada — pasó exactamente así. /var/log/cosmic vive en la ext4 de la imagen ⇒ sobrevive al
|
|
# apagón y se puede leer desde otra máquina sin bootear, con `debugfs -R "dump ..." /dev/sdX2`.
|
|
LOG=/var/log/cosmic
|
|
mkdir -p "$LOG" 2>/dev/null
|
|
[ -d "$LOG" ] && [ -w "$LOG" ] || LOG=/tmp
|
|
|
|
# ⚠⚠ NUNCA ESCRIBIR A /dev/ttyS0 A CIEGAS — CUELGA EL ARRANQUE EN METAL ⚠⚠
|
|
# Abrir un puerto serie sin portadora BLOQUEA en open() esperando DCD, salvo que el termios tenga
|
|
# CLOCAL. El kernel pone CLOCAL cuando registra el puerto COMO CONSOLA, y eso ocurre en QEMU porque el
|
|
# cmdline lleva `console=ttyS0,115200`. En el metal del OptiPlex 3060 la firmware NO aplica el
|
|
# CONFIG_CMDLINE horneado (el kernel arranca con `Command line:` VACÍO) ⇒ ttyS0 es un puerto común,
|
|
# sin cable, sin portadora: el PRIMER `say` se colgaba ahí para siempre. Síntoma exacto y desconcertante:
|
|
# /var/log/cosmic se creaba (el mkdir va antes) pero el .log nunca aparecía, y redirigir a fichero
|
|
# daba vacío — o sea que el script parecía no ejecutarse cuando en realidad estaba bloqueado en su
|
|
# primera línea de salida. Diagnosticado por el usuario quitando los pipes, 2026-08-05.
|
|
#
|
|
# El serial no se pierde: cuando ES la consola, /dev/console YA ES ttyS0. Cuando no lo es, no había
|
|
# nada que escuchara del otro lado. Escribir al fichero de log es lo que sirve en metal.
|
|
#
|
|
# Y /dev/console sólo si stdout no es ya un terminal: lanzado a mano desde el getty, stdout ES la
|
|
# pantalla y /dev/console (=tty0 con cmdline vacío) es LA MISMA ⇒ cada línea salía DOS VECES.
|
|
[ -t 1 ] && CONS="" || CONS=/dev/console
|
|
say() { echo "== cosmic :: $*" | tee -a $CONS "$LOG/cosmic-start.log" 2>/dev/null; }
|
|
# `dump` iba SÓLO al serial: en metal eso es a la vez escribir a la nada Y COLGARSE (ver arriba). Por
|
|
# eso el 3er viaje terminaba siempre en «log del compositor (primer tramo)»: no era el último paso,
|
|
# era donde se bloqueaba. Ahora va al fichero de log, que es lo que se puede leer después.
|
|
dump() { { echo "---- $1 ----"; cat "$1" 2>/dev/null; } >> "$LOG/cosmic-start.log" 2>/dev/null; }
|
|
alive() { s=$(sed -n 's/^State:[[:space:]]*//p' /proc/$1/status 2>/dev/null); case "$s" in ""|Z*) return 1;; *) return 0;; esac; }
|
|
|
|
say "arrancando (pid $$)"
|
|
|
|
# El getty ejecuta este script SIN ambiente heredable, así que las perillas de la imagen viajan como
|
|
# FICHERO —mismo truco que `/etc/gnome-mode`—. Hoy sólo `COSMIC_MODE` (bare|session).
|
|
[ -r /etc/cosmic-mode ] && . /etc/cosmic-mode
|
|
|
|
# ── FS virtuales y directorios de runtime ───────────────────────────────────────────────────────
|
|
mount -t proc none /proc 2>/dev/null
|
|
mount -t sysfs none /sys 2>/dev/null
|
|
mount -t devtmpfs none /dev 2>/dev/null
|
|
mkdir -p /dev/pts /dev/shm && mount -t devpts none /dev/pts 2>/dev/null && mount -t tmpfs none /dev/shm 2>/dev/null
|
|
# XDG_RUNTIME_DIR es OBLIGATORIO para Wayland: ahí viven el socket del compositor y los de los
|
|
# clientes. Sin él el compositor arranca y nadie puede conectarse — y el error que da es del cliente,
|
|
# no del compositor, que es lo que hace confuso el diagnóstico.
|
|
export XDG_RUNTIME_DIR=/run/user/0
|
|
mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR"
|
|
# …y COMPROBARLO, porque el primer arranque falló justo acá y el error salía del sitio equivocado:
|
|
# dbus decía «XDG_RUNTIME_DIR /run/user/0 not available» y eso se lee como un problema de dbus. El
|
|
# `mkdir -p` de arriba no garantiza nada si /run no es escribible o si algo lo montó encima.
|
|
if [ -d "$XDG_RUNTIME_DIR" ] && [ -w "$XDG_RUNTIME_DIR" ]; then
|
|
say "XDG_RUNTIME_DIR OK ($XDG_RUNTIME_DIR)"
|
|
else
|
|
say "!! XDG_RUNTIME_DIR NO existe/no escribible — /run es: $(ls -ld /run 2>&1)"
|
|
say " montando tmpfs en /run"
|
|
mount -t tmpfs none /run 2>/dev/null
|
|
mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR"
|
|
[ -w "$XDG_RUNTIME_DIR" ] && say " ✓ ahora sí" || say " ✗ sigue sin andar"
|
|
fi
|
|
mkdir -p /root/.config /root/.cache /var/lib/dbus /run/dbus /tmp && chmod 1777 /tmp
|
|
export HOME=/root SHELL=/bin/bash # PATH ya salió arriba, antes del primer mkdir
|
|
|
|
say "kernel $(uname -r) · drm: $(ls /dev/dri 2>/dev/null | tr '\n' ' ')"
|
|
|
|
# ── GL: HARDWARE si lo hay, software si no — DETECTADO, nunca hardcodeado ───────────────────────
|
|
# La regla tiene DOS patas y hay que cumplir las dos: que el KERNEL exponga un driver DRM real
|
|
# (i915/amdgpu/radeon/nouveau, no virtio_gpu ni vgem), y que MESA traiga el `.so` correspondiente.
|
|
# Con una sola no alcanza, y por eso el hardcodeo anterior era una trampa: la mesa del corpus va con
|
|
# `-Dgallium-drivers=iris -Dllvm=disabled`, así que sobre un AMD real habría kernel pero no userland,
|
|
# y en QEMU hay virtio-gpu pero el camino bueno es swrast. Si no se cumple NINGUNA, se dice fuerte en
|
|
# vez de morir adentro de EGL con un mensaje que no señala la causa.
|
|
DRM_DRV=""
|
|
for _c in /sys/class/drm/card[0-9]*; do
|
|
[ -r "$_c/device/uevent" ] || continue
|
|
DRM_DRV=$(sed -n 's/^DRIVER=//p' "$_c/device/uevent" 2>/dev/null)
|
|
[ -n "$DRM_DRV" ] && break
|
|
done
|
|
DRI=/usr/lib/dri
|
|
case "$DRM_DRV" in
|
|
i915) MESA_HW=iris ;;
|
|
amdgpu) MESA_HW=radeonsi ;;
|
|
radeon) MESA_HW=r600 ;;
|
|
nouveau) MESA_HW=nouveau ;;
|
|
*) MESA_HW="" ;;
|
|
esac
|
|
if [ -n "$MESA_HW" ] && [ -r "$DRI/${MESA_HW}_dri.so" ]; then
|
|
say "GL por HARDWARE — kernel drm=$DRM_DRV + ${MESA_HW}_dri.so (sin overrides de software)"
|
|
elif [ -r "$DRI/kms_swrast_dri.so" ] || [ -r "$DRI/swrast_dri.so" ]; then
|
|
say "GL por SOFTWARE — drm=${DRM_DRV:-ninguno}, no hay userland HW ⇒ kms_swrast"
|
|
# `GBM_ALWAYS_SOFTWARE` es lo que hace que mesa acepte crear un device GBM sobre virtio-gpu sin
|
|
# acelerador; `LP_NUM_THREADS=1` evita que llvmpipe se coma los vcpus y deje al compositor sin
|
|
# planificar. Las dos vienen pagas de las campañas KDE y GNOME.
|
|
export GBM_ALWAYS_SOFTWARE=1 LIBGL_ALWAYS_SOFTWARE=1 MESA_LOADER_DRIVER_OVERRIDE=kms_swrast LP_NUM_THREADS=1
|
|
else
|
|
say "!! NI hardware NI software: drm=${DRM_DRV:-ninguno}; en $DRI sólo hay: $(ls $DRI 2>/dev/null | tr '\n' ' ')"
|
|
say " el compositor va a fallar al inicializar EGL — ESTO es la causa, no un síntoma posterior"
|
|
fi
|
|
# COSMIC pinta con tiny-skia (libcosmic entra sin `wgpu`), pero el compositor SÍ usa EGL/GL por
|
|
# smithay para componer, así que el modo de GL lo decide todo.
|
|
export RUST_BACKTRACE=1
|
|
export RUST_LOG="${RUST_LOG:-info}"
|
|
|
|
# ── seatd: quién le da los devices al compositor ────────────────────────────────────────────────
|
|
# smithay entra con `backend_session_libseat`, y libseat tiene dos backends: hablar con un `seatd`
|
|
# corriendo, o —si el proceso ya es root— abrir los devices él mismo (`builtin`). Acá se levanta
|
|
# seatd de verdad en vez de usar el builtin, porque es el camino que también sirve fuera de QEMU y
|
|
# porque deja un log donde mirar. Si no sube, se cae al builtin: mejor un escritorio con andamio que
|
|
# ninguno, pero se dice en el serial para que no pase por bueno.
|
|
if [ -x /usr/bin/seatd ]; then
|
|
/usr/bin/seatd -g video >$LOG/seatd.log 2>&1 &
|
|
SEATD_PID=$!
|
|
sleep 1
|
|
if alive $SEATD_PID && [ -S /run/seatd.sock ]; then
|
|
say "seatd OK (pid $SEATD_PID, /run/seatd.sock)"
|
|
else
|
|
say "!! seatd no levantó — caigo a LIBSEAT_BACKEND=builtin (somos root)"; dump $LOG/seatd.log
|
|
export LIBSEAT_BACKEND=builtin
|
|
fi
|
|
else
|
|
say "!! sin seatd — LIBSEAT_BACKEND=builtin"
|
|
export LIBSEAT_BACKEND=builtin
|
|
fi
|
|
|
|
# ── bus de SISTEMA ──────────────────────────────────────────────────────────────────────────────
|
|
# cosmic-comp habla logind por zbus (sesión, inhibidores) y el bus de sesión lo abre después
|
|
# `start-cosmic` con dbus-run-session. El de sistema hay que levantarlo acá.
|
|
if [ -x /usr/bin/dbus-daemon ]; then
|
|
# ⚠ El mkdir de /run/dbus va ACÁ, no arriba con los demás. El primer arranque en modo bare falló
|
|
# con «Failed to bind socket /run/dbus/system_bus_socket: No such file or directory» aunque el
|
|
# directorio se creaba al principio del script: entre un punto y otro arje-zero sigue montando, y
|
|
# un tmpfs sobre /run se lleva puesto lo que hubiera. Crear justo antes de usar es la única forma
|
|
# de no depender de ese orden. Mismo motivo que el segundo mkdir de XDG_RUNTIME_DIR.
|
|
mkdir -p /run/dbus /var/lib/dbus
|
|
[ -f /var/lib/dbus/machine-id ] || dbus-uuidgen > /var/lib/dbus/machine-id 2>/dev/null
|
|
[ -f /etc/machine-id ] || cp /var/lib/dbus/machine-id /etc/machine-id 2>/dev/null
|
|
dbus-daemon --system --fork >$LOG/dbus-system.log 2>&1
|
|
sleep 1
|
|
[ -S /run/dbus/system_bus_socket ] && say "bus de sistema OK" || { say "!! sin bus de sistema:"; dump $LOG/dbus-system.log; }
|
|
else
|
|
say "!! sin dbus-daemon"
|
|
fi
|
|
|
|
# ── login1: el shim de arje ─────────────────────────────────────────────────────────────────────
|
|
# Mismo eslabón que en GNOME y KDE. cosmic-comp pregunta por su sesión; quien contesta
|
|
# `org.freedesktop.login1` acá es arje-logind-compat. Se ESPERA el nombre, no el pid: un pid no es un
|
|
# servicio, y esperar sólo al final deja pasar la carrera (lección de gnome-start, 2026-08-03).
|
|
if [ -x /usr/bin/arje-logind-compat ]; then
|
|
ARJE_LOGIN_STATE=1 /usr/bin/arje-logind-compat >$LOG/logind-compat.log 2>&1 &
|
|
say "arje-logind-compat lanzado (pid $!)"
|
|
i=0
|
|
while [ $i -lt 20 ]; do
|
|
if dbus-send --system --dest=org.freedesktop.DBus --print-reply /org/freedesktop/DBus \
|
|
org.freedesktop.DBus.NameHasOwner string:org.freedesktop.login1 2>/dev/null | grep -q "boolean true"; then
|
|
say "bus: org.freedesktop.login1 OK"; break
|
|
fi
|
|
i=$((i+1)); sleep 0.5
|
|
done
|
|
[ $i -lt 20 ] || { say "!! login1 NO apareció en 10s:"; dump $LOG/logind-compat.log; }
|
|
else
|
|
say "!! sin arje-logind-compat — cosmic-comp puede no hallar sesión"
|
|
fi
|
|
|
|
# ── COSMIC ──────────────────────────────────────────────────────────────────────────────────────
|
|
export XDG_CURRENT_DESKTOP=COSMIC XDG_SESSION_DESKTOP=COSMIC XDG_SESSION_TYPE=wayland
|
|
export XDG_SESSION_ID=1 XDG_SEAT=seat0 XDG_VTNR=1
|
|
export XDG_DATA_DIRS=/usr/share XDG_CONFIG_DIRS=/etc/xdg
|
|
|
|
# Y otra vez JUSTO ANTES de arrancar, no sólo al principio: entre el getty y este punto pasan varios
|
|
# segundos en los que arje-zero sigue montando cosas, y un tmpfs sobre /run se lleva puesto el
|
|
# directorio sin avisar. Comprobar al principio y usar al final es cómo se cuela una carrera.
|
|
mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR"
|
|
[ -w "$XDG_RUNTIME_DIR" ] || say "!! XDG_RUNTIME_DIR se perdió entre el arranque y ahora"
|
|
|
|
# ── EL DEMONIO DE PIPEWIRE ──────────────────────────────────────────────────────────────────────
|
|
# Faltaba, y el hueco no se veía: `/usr/bin/pipewire` está en la imagen desde que entró como dep de
|
|
# `cosmic-settings-daemon`, pero NADIE lo arrancaba. En una distro con systemd de usuario lo levanta
|
|
# `pipewire.socket`; acá no hay tal cosa, así que es tarea de este script — el mismo patrón que el
|
|
# bus de sistema y que login1.
|
|
#
|
|
# La consecuencia medida de que faltara: `org.freedesktop.portal.ScreenCast.CreateSession` ACEPTA y
|
|
# devuelve su objeto Request —o sea que la cadena del portal está bien—, pero no puede haber stream,
|
|
# porque el backend negocia el nodo contra un demonio que no existe. Un fallo que se manifiesta como
|
|
# «no pasa nada» tres capas más arriba de la causa.
|
|
#
|
|
# Va ACÁ y no junto al bus de sesión a propósito: pipewire no necesita D-Bus para arrancar, sólo
|
|
# XDG_RUNTIME_DIR (es donde abre su socket). Ponerlo justo después de asegurar ese directorio lo hace
|
|
# válido para los DOS modos —bare y session— sin duplicar el bloque.
|
|
#
|
|
# ⚠ Sin `wireplumber` (el gestor de sesión, que ni siquiera está construido) pipewire arranca y sirve,
|
|
# pero no gestiona dispositivos: no descubre tarjetas ni enruta audio. Para SCREENCAST eso alcanza —
|
|
# el backend del portal crea su propio nodo de video— y para AUDIO no. Son dos preguntas distintas y
|
|
# esta pieza sólo contesta la primera.
|
|
if [ -x /usr/bin/pipewire ]; then
|
|
/usr/bin/pipewire >$LOG/pipewire.log 2>&1 &
|
|
PW_PID=$!
|
|
# El socket, no el pid: misma disciplina que el compositor. `pipewire-0` es el nombre por defecto
|
|
# (`core.name` en pipewire.conf); si el demonio muere al instante, el pid sigue "existiendo" un
|
|
# rato y esperar por él daría un OK falso.
|
|
i=0
|
|
while [ $i -lt 20 ]; do
|
|
[ -S "$XDG_RUNTIME_DIR/pipewire-0" ] && { say "pipewire OK (socket pipewire-0, pid $PW_PID)"; break; }
|
|
alive $PW_PID || { say "!! pipewire MURIÓ antes de abrir el socket:"; dump $LOG/pipewire.log; break; }
|
|
i=$((i+1)); sleep 0.5
|
|
done
|
|
[ -S "$XDG_RUNTIME_DIR/pipewire-0" ] || { say "!! pipewire sin socket en $((i/2))s:"; dump $LOG/pipewire.log; }
|
|
else
|
|
say "!! sin /usr/bin/pipewire — ScreenCast no va a poder crear stream"
|
|
fi
|
|
|
|
# ── EL GESTOR DE SESIÓN: wireplumber ────────────────────────────────────────────────────────────
|
|
# pipewire se construye con `-Dsession-managers=[]`, o sea que arranca y sirve pero NO TIENE
|
|
# POLÍTICA: nada decide qué es entrada, qué es salida, ni quién mueve un nodo de `Paused` a
|
|
# `Streaming`. wireplumber es esa política, escrita en Lua.
|
|
#
|
|
# Por qué está acá y no antes: **necesita el socket de pipewire ya abierto**. Se lanza después a
|
|
# propósito, no por orden estético — un gestor de sesión sin demonio que gestionar sale con error.
|
|
#
|
|
# Acá NO se puede esperar un socket, porque wireplumber no abre ninguno: es un CLIENTE de pipewire,
|
|
# no un servidor. Lo que dice que hizo su trabajo es que aparezca como cliente en el grafo
|
|
# (`pw-cli ls Client`), y eso es una medición del sistema, no una condición de arranque. Así que se
|
|
# espera un instante y se reporta si el proceso sigue vivo — con el log a mano, que es lo que sirve
|
|
# cuando falla.
|
|
if [ -x /usr/bin/wireplumber ] && [ -S "$XDG_RUNTIME_DIR/pipewire-0" ]; then
|
|
/usr/bin/wireplumber >$LOG/wireplumber.log 2>&1 &
|
|
WP_PID=$!
|
|
sleep 2
|
|
if alive $WP_PID; then
|
|
say "wireplumber OK (pid $WP_PID) — hay gestor de sesión"
|
|
else
|
|
say "!! wireplumber MURIÓ:"; dump $LOG/wireplumber.log
|
|
fi
|
|
elif [ -x /usr/bin/wireplumber ]; then
|
|
say "!! wireplumber presente pero sin socket de pipewire — no se lanza"
|
|
else
|
|
say "-- sin wireplumber (pipewire sirve, pero sin política de sesión)"
|
|
fi
|
|
|
|
# ── FONDO: color liso, porque la imagen por defecto NO SE PUEDE EMPAQUETAR ────────────────────
|
|
# El default de cosmic-bg apunta a `/usr/share/backgrounds/cosmic/orion_nebula_nasa_heic0601a.jpg`,
|
|
# que vive en el repo `cosmic-wallpapers`… en **git-lfs**: el tarball de GitHub pesa 20 KB y trae
|
|
# punteros, no imágenes. O sea que la receta obvia produciría un paquete de ficheros de texto.
|
|
# Mientras eso no se resuelva, se escribe la config de USUARIO con una fuente `Color`, que
|
|
# cosmic-bg soporta de fábrica. Va acá y no en el artefacto a propósito: es política de esta
|
|
# imagen de prueba, no algo que el paquete prometa.
|
|
if [ ! -f /usr/share/backgrounds/cosmic/orion_nebula_nasa_heic0601a.jpg ]; then
|
|
mkdir -p "$HOME/.config/cosmic/com.system76.CosmicBackground/v1"
|
|
cat > "$HOME/.config/cosmic/com.system76.CosmicBackground/v1/all" <<'RON'
|
|
(
|
|
output: "all",
|
|
source: Color(Single((0.13, 0.29, 0.53))),
|
|
filter_by_theme: true,
|
|
rotation_frequency: 3600,
|
|
filter_method: Lanczos,
|
|
scaling_mode: Zoom,
|
|
sampling_method: Alphanumeric,
|
|
)
|
|
RON
|
|
say "fondo: color liso (el wallpaper por defecto vive en git-lfs y no se empaqueta)"
|
|
fi
|
|
|
|
# ── SEMILLA DE CONFIG DE qalc, SIN LA CUAL LA CALCULADORA DEL LANZADOR NUNCA ANDA ────────────────
|
|
# En el primer arranque `qalc` hace una PREGUNTA interactiva («¿activar autocalc?») antes de leer
|
|
# nada. El plugin `calc` de pop-launcher le escribe la expresión y cierra el pipe, así que la
|
|
# expresión se consume como respuesta, no es un sí/no válido, y qalc **no llega a guardar config** ⇒
|
|
# la próxima vez vuelve a preguntar. O sea que sin sembrar esto no falla una vez: falla SIEMPRE.
|
|
# Es política de la imagen, no del paquete —igual que el color de fondo—: una distro de verdad lo
|
|
# pondría en el esqueleto de /etc/skel.
|
|
if [ ! -f "$HOME/.config/qalculate/qalc.cfg" ]; then
|
|
mkdir -p "$HOME/.config/qalculate"
|
|
printf '[General]\ncalculate_as_you_type=0\n' > "$HOME/.config/qalculate/qalc.cfg"
|
|
say "qalc: config sembrada (si no, la pregunta de primer arranque se come la cuenta)"
|
|
fi
|
|
|
|
# ── MODO: bare (default) o session ──────────────────────────────────────────────────────────────
|
|
# `cosmic-session` llama a `cosmic-settings-daemon` con
|
|
# `.expect("failed to start settings daemon")` (src/main.rs:255) y PANICKEA si no está — no es
|
|
# opcional ni está detrás de una feature.
|
|
#
|
|
# ⚠ **DESACTUALIZADO Y CORREGIDO (2026-08-05)**: acá decía que ese daemon «todavía no se puede
|
|
# construir acá (arrastra pipewire)». Es falso desde hace tiempo — `cosmic-settings-daemon` está
|
|
# sellado y corre, y `pipewire` está en la imagen. Se deja el rastro porque el comentario viejo
|
|
# mandaba a buscar un bloqueo que ya no existe.
|
|
#
|
|
# Lo que SÍ faltaba, y era otra cosa, **ya está**: nadie arrancaba el demonio de pipewire. Se
|
|
# corrigió arriba (bloque «EL DEMONIO DE PIPEWIRE», justo después de XDG_RUNTIME_DIR). Queda
|
|
# `wireplumber` sin construir, que es lo que separa «hay stream de video» de «hay audio».
|
|
#
|
|
# El modo `bare` es la otra mitad que upstream ya soporta (`make install-bare-session`): el
|
|
# compositor se lanza SOLO y los clientes se levantan a mano contra su WAYLAND_DISPLAY. Sirve para lo
|
|
# que hace falta ahora, que es saber si el escritorio PINTA — y separa las dos preguntas: «¿compone?»
|
|
# es del compositor, «¿arranca la sesión?» es del gestor.
|
|
#
|
|
# COSMIC_MODE=session para probar el camino de producción cuando el daemon exista.
|
|
if [ "${COSMIC_MODE:-bare}" = session ]; then
|
|
say "lanzando cosmic-session vía start-cosmic (modo session)"
|
|
/bin/bash /usr/bin/start-cosmic --in-login-shell >$LOG/cosmic-session.log 2>&1 &
|
|
CS_PID=$!
|
|
else
|
|
say "lanzando cosmic-comp SOLO (modo bare; cosmic-session necesita settings-daemon)"
|
|
dbus-daemon --session --fork --print-address=1 >$LOG/dbus-session-addr 2>$LOG/dbus-session.log
|
|
DBUS_SESSION_BUS_ADDRESS=$(cat $LOG/dbus-session-addr 2>/dev/null)
|
|
[ -n "$DBUS_SESSION_BUS_ADDRESS" ] && export DBUS_SESSION_BUS_ADDRESS && say "bus de sesión OK" \
|
|
|| { say "!! sin bus de sesión:"; dump $LOG/dbus-session.log; }
|
|
/usr/bin/cosmic-comp >$LOG/cosmic-session.log 2>&1 &
|
|
CS_PID=$!
|
|
fi
|
|
|
|
# ── Reporte: el socket, no el pid ───────────────────────────────────────────────────────────────
|
|
# «Un pid no es un servicio» — la lección que costó dos iteraciones en GNOME. Lo que dice que el
|
|
# compositor está VIVO es que exista su socket Wayland, no que el proceso no haya muerto todavía.
|
|
i=0; WD=""
|
|
while [ $i -lt 60 ]; do
|
|
WD=$(ls "$XDG_RUNTIME_DIR" 2>/dev/null | grep -m1 '^wayland-[0-9]$')
|
|
if [ -n "$WD" ]; then
|
|
say "compositor OK ($WD) — cosmic-comp ES el display server"
|
|
break
|
|
fi
|
|
alive $CS_PID || { say "!! la sesión MURIÓ antes de crear el socket:"; dump $LOG/cosmic-session.log; break; }
|
|
i=$((i+1)); sleep 1
|
|
done
|
|
[ -n "$WD" ] || say "!! sin socket wayland en ${i}s:"
|
|
[ -n "$WD" ] || dump $LOG/cosmic-session.log
|
|
|
|
# ── Los clientes, contra el socket que el compositor haya elegido ───────────────────────────────
|
|
# En modo bare no hay quien los levante: `cosmic-session` es el que normalmente hace esto, y por eso
|
|
# cada uno recibe WAYLAND_DISPLAY explícito. Se lanza sólo lo que ESTÉ instalado — la cola COSMIC se
|
|
# construye por partes, y un `[ -x ]` de más es más barato que un arranque perdido.
|
|
if [ -n "$WD" ] && [ "${COSMIC_MODE:-bare}" != session ]; then
|
|
export WAYLAND_DISPLAY="$WD"
|
|
|
|
for c in cosmic-bg cosmic-panel cosmic-osd cosmic-notifications cosmic-app-library cosmic-launcher cosmic-idle; do
|
|
if [ -x "/usr/bin/$c" ]; then
|
|
"/usr/bin/$c" >"$LOG/$c.log" 2>&1 &
|
|
say "cliente $c lanzado (pid $!)"
|
|
sleep 1
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Estado periódico + el log completo al final de cada tramo: el dato que importa no es que siga vivo
|
|
# sino QUÉ componentes levantó la sesión.
|
|
n=0
|
|
while [ $n -lt 12 ]; do
|
|
sleep 15; n=$((n+1))
|
|
# `pgrep -c` de busybox devolvía 0 con el compositor VIVO y su socket abierto — o sea que el
|
|
# contador mentía. Se cuentan los /proc/*/comm, que es el dato del kernel y no una utilería.
|
|
say "STATUS +$((n*15))s: $(for c in /proc/[0-9]*/comm; do cat "$c" 2>/dev/null; done | grep -c '^cosmic') procesos cosmic · $(for c in /proc/[0-9]*/comm; do cat "$c" 2>/dev/null; done | grep '^cosmic' | sort -u | tr '\n' ' ')"
|
|
[ $n = 2 ] && { say "log del compositor (primer tramo)"; dump $LOG/cosmic-session.log; for l in $LOG/cosmic-bg.log $LOG/cosmic-panel.log; do [ -s "$l" ] && dump "$l"; done; }
|
|
done
|
|
|
|
say "fin de la ventana de observación"
|
|
dump $LOG/cosmic-session.log
|
|
exec /bin/sh
|