#!/bin/sh
# plasma-start-qemu.sh — arranca Plasma en una VM QEMU con render por SOFTWARE (kms_swrast/llvmpipe)
# sobre el DRM de virtio-gpu (/dev/dri/card0). Derivado de plasma-start-metal.sh pero SIN hardware iris:
# en QEMU no hay GPU real, así que kwin presenta por el DRM de virtio con EGL/GBM de software.
#
# Se instala como /usr/bin/plasma-start en la imagen QEMU y lo AUTO-LANZA /etc/profile (console-getty
# autologin). Trae los arreglos de la campaña KDE 2026-07: QT_QUICK_BACKEND=software (plasmashell no crea
# contexto GL sin él), QT_QPA_FONTDIR (mata los tofu; qtbase se construyó sin fontconfig), kdeglobals +
# QT_QPA_PLATFORMTHEME=kde (iconos + estilo Breeze), kactivitymanagerd (plasmashell aborta sin él).
#
# Logs: /tmp/{kwin,kamd,plasmashell}.log + tee a /dev/console.  Salir: matar kwin.
set -u
# getty ejecuta este script DIRECTO (no vía login shell) ⇒ PATH viene vacío: mkdir/tr/cat/dbus-daemon
# no se hallan y kwin nunca levanta. Seteamos PATH explícito (busybox + userland KDE) ANTES de todo.
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
# tee a la consola gráfica (tty0) Y al serial (ttyS0) — el host captura ttyS0 con -serial file:
say() { echo "== plasma-qemu :: $*" | tee -a /dev/console /dev/ttyS0 2>/dev/null; }
dump() { echo "---- $1 ----" > /dev/ttyS0 2>/dev/null; cat "$1" > /dev/ttyS0 2>/dev/null; }

export LD_LIBRARY_PATH=/usr/lib64:/usr/lib/qt6:/usr/lib:/lib
export QT_PLUGIN_PATH=/usr/lib64/qt6/plugins:/usr/lib/qt6/plugins
export QML2_IMPORT_PATH=/usr/lib64/qt6/qml:/usr/lib/qt6/qml
export QML_IMPORT_PATH=$QML2_IMPORT_PATH
export XDG_DATA_DIRS=/usr/share
export XDG_RUNTIME_DIR=/run/user/0
export XDG_CONFIG_HOME=/root/.config
export XDG_CACHE_HOME=/root/.cache
export HOME=/root

# --- render por software sobre virtio-gpu ---
export LIBGL_ALWAYS_SOFTWARE=1
export GALLIUM_DRIVER=llvmpipe
# GBM_ALWAYS_SOFTWARE=1 — EL FIX del segfault (MURO 2, verificado en fuente mesa 24.0.9). virtio-gpu-pci
# SIN 3D expone un nodo KMS-only (no render-capable). Con MESA_LOADER_DRIVER_OVERRIDE solo, gbm carga
# kms_swrast pero deja gbm_dri->software=FALSE (dri_screen_create tiene éxito ⇒ nunca cae al fallback
# dri_screen_create_sw, el único que setea software=true). Entonces dri2_initialize_drm entra al bloque
# `if(!software)` y llama gbm_dri->mesa->queryCompatibleRenderOnlyDeviceFd() — NULL en el driver software
# ⇒ SIGSEGV. GBM_ALWAYS_SOFTWARE=1 fuerza dri_screen_create_sw ⇒ software=true ⇒ se salta ese bloque.
export GBM_ALWAYS_SOFTWARE=1
# kms_swrast (NO swrast): kwin abre card0 como dispositivo KMS y crea un gbm device sobre él ⇒ necesita
# el driver de software para KMS (dumb buffers). Con 'swrast' a secas gbm_create_device falla ("Failed
# to create gbm device"). kms_swrast_dri.so está en /usr/lib/dri.
export MESA_LOADER_DRIVER_OVERRIDE=kms_swrast
export LIBGL_DRIVERS_PATH=/usr/lib/dri
export QT_QUICK_BACKEND=software
# LP_NUM_THREADS=1: llvmpipe SINCRÓNICO (sin pool de threads del rasterizador) — el JIT multi-thread de
# llvmpipe segfaultea al componer la 1ra superficie en esta VM. KWIN_DRM_NO_AMS=1: desactiva atomic
# modesetting (algunos virtio-gpu lo rompen). Ambos son fixes conocidos de kwin/llvmpipe en VMs.
export LP_NUM_THREADS=1
# KWIN_DRM_NO_AMS (legacy modeset) fue una corazonada del segfault (que resultó ser GBM_ALWAYS_SOFTWARE).
# Dejamos atomic (virtio soporta atomic nativo; legacy es el frágil).
# export KWIN_DRM_NO_AMS=1
#
# EL FIX DEL NEGRO: virtio-gpu ADVIERTE soportar drmModeAddFB2WithModifiers pero RECHAZA el modifier
# explícito (aún LINEAR=0) con EINVAL ⇒ "Failed to create a framebuffer" ⇒ sin output layer ⇒ NEGRO.
# El modeset inicial usa un buffer dumb (sin modifiers) y SÍ pinta (por eso se ve el wallpaper un instante),
# pero los buffers del compositor van por AddFB2WithModifiers y fallan ⇒ vuelve a negro. KWIN_DRM_USE_MODIFIERS=0
# fuerza a kwin a usar drmModeAddFB2 plano (modifier implícito) que virtio-gpu sí acepta.
export KWIN_DRM_USE_MODIFIERS=0
# KWIN_COMPOSE=Q — compositor QPainter (raster de Qt directo al buffer, SIN escena llvmpipe). El
# compositor GL por defecto usa el rasterizador de llvmpipe, cuyo hilo SEGFAULTEA en lp_rast_clear_color
# → util_fill_rect al limpiar el color buffer (backtrace del core.llvmpipe-0): kwin pinta unos frames y
# muere → "vuelve a negro". QPainter no arma escenas llvmpipe ⇒ esquiva ese crash. (El segfault viejo con
# =Q era el de gbm-init, ya resuelto por GBM_ALWAYS_SOFTWARE.) llvmpipe sigue disponible para gbm/EGL.
export KWIN_COMPOSE=Q
export QT_QPA_PLATFORM=wayland
export QT_QPA_PLATFORMTHEME=kde
export QT_QPA_FONTDIR=/usr/share/fonts/dejavu
# cursor: kwin busca el tema "default" y no lo halla ("Failed to load cursor theme") ⇒ cursor invisible.
# Apuntamos al breeze_cursors del rootfs (115 cursores) por env + kcminputrc (kwin lee [Mouse] cursorTheme).
export XCURSOR_THEME=breeze_cursors
export XCURSOR_PATH=/usr/share/icons:/usr/share/pixmaps
export XCURSOR_SIZE=24
# El tema ya carga (sin "Failed to load cursor theme") pero el cursor SIGUE invisible: kwin usa el plano
# de cursor por HARDWARE del DRM, y en virtio-gpu con render software ese plano no se presenta (aparece y
# desaparece). KWIN_FORCE_SW_CURSOR=1 ⇒ kwin dibuja el cursor DENTRO del framebuffer principal (software)
# ⇒ visible y estable en VMs sin GPU real.
export KWIN_FORCE_SW_CURSOR=1

# --- DIAGNÓSTICO del segfault-al-1er-composite (MURO 2) ---
# Sin gdb en la imagen: subimos el logging categorizado de kwin (kwin_core/drm/scene/opengl) + mesa/egl
# para ver EN QUÉ FASE muere (bind EGL, alloc GBM, JIT llvmpipe, o el paint real). DIAG=0 para apagarlo.
if [ "${DIAG:-1}" = 1 ]; then
  export QT_LOGGING_RULES="kwin_*=true;qt.qpa.*=true"
  export EGL_LOG_LEVEL=debug
  export LIBGL_DEBUG=verbose
  export MESA_DEBUG=1
  export GALLIUM_HUD=          # no HUD, sólo para dejar constancia
  export KWIN_DRM_DEBUG=1
fi

mkdir -p "$XDG_RUNTIME_DIR" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" /run/dbus /root
chmod 700 "$XDG_RUNTIME_DIR"
[ -s /etc/machine-id ] || cat /proc/sys/kernel/random/uuid 2>/dev/null | tr -d - > /etc/machine-id

# kdeglobals: iconos + estilo Breeze (sin esto Qt cae a hicolor vacío = escritorio pelado)
cat > "$XDG_CONFIG_HOME/kdeglobals" <<EOF
[Icons]
Theme=breeze

[General]
ColorScheme=BreezeLight
widgetStyle=Breeze

[KDE]
widgetStyle=Breeze
EOF

# kcminputrc: el tema de cursor que kwin lee al arrancar (además del env XCURSOR_THEME)
cat > "$XDG_CONFIG_HOME/kcminputrc" <<EOF
[Mouse]
cursorTheme=breeze_cursors
cursorSize=24
EOF

say "GPU DRM: $(ls /dev/dri/ 2>/dev/null | tr '\n' ' ')"
[ -e /dev/dri/card0 ] || say "!! no hay /dev/dri/card0 — ¿virtio-gpu no cargó? (dmesg | grep -i drm)"

# D-Bus de SISTEMA: sin él, kf.solid/udisks2 spamea "Not connected to D-Bus server" y powerdevil + los
# applets de dispositivos/energía no inician. Escucha en el path estándar /run/dbus/system_bus_socket
# (los clientes lo hallan por DBUS_SYSTEM_BUS_ADDRESS por defecto ⇒ no exportamos nada). Corre como root
# y hace setuid a messagebus (uid 81, sembrado en el passwd de la imagen). No hay udisksd/upowerd reales
# en el rootfs ⇒ no habrá discos/batería, pero el bus existe y solid deja de errorear en cada poll.
mkdir -p /run/dbus /var/lib/dbus
cp -f /etc/machine-id /var/lib/dbus/machine-id 2>/dev/null || true
if [ ! -S /run/dbus/system_bus_socket ]; then
  if dbus-daemon --system --fork 2>/tmp/dbus-system.log; then
    say "system bus ON (/run/dbus/system_bus_socket)"
  else
    say "!! system bus NO arrancó:"; dump /tmp/dbus-system.log
  fi
fi

# D-Bus de sesión
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
  DBUS_SESSION_BUS_ADDRESS=$(dbus-daemon --session --fork --print-address 2>/dev/null)
  export DBUS_SESSION_BUS_ADDRESS
fi
say "dbus: ${DBUS_SESSION_BUS_ADDRESS:-NO ARRANCO}"

# --- CORE DUMP del segfault (MURO 2): sin gdb en la imagen, sacamos un core y lo leemos con el gdb del
# host apuntando al rootfs fundido. Localiza el frame culpable (libLLVM/llvmpipe vs kwin vs mesa). ---
if [ "${DIAG:-1}" = 1 ]; then
  ulimit -c unlimited 2>/dev/null || true
  # /core en la RAÍZ ext4 (NO /tmp: es tmpfs=RAM y se pierde al matar QEMU). Tras el crash hacemos sync
  # explícito ⇒ el core persiste en la imagen aunque después mate QEMU con SIGKILL.
  echo '/core.%e.%p' > /proc/sys/kernel/core_pattern 2>/dev/null || true
  say "core dumps ON (pattern=$(cat /proc/sys/kernel/core_pattern 2>/dev/null), ulimit -c=$(ulimit -c))"
fi

# SÓLO el card de virtio-gpu: el efifb/simpledrm sobrevive como 2º DRM ("Unknown-1", read-only) y kwin
# intenta manejar ambos outputs ⇒ conflicto de presentación ⇒ pinta y vuelve a NEGRO. Detectamos el card
# cuyo driver es virtio_gpu (robusto al numerado card0/card1 que cambia entre boots) y se lo pasamos a kwin.
VIRTIO_CARD=
for c in /dev/dri/card*; do
  n=$(basename "$c")
  drv=$(readlink -f "/sys/class/drm/$n/device/driver" 2>/dev/null)
  case "$drv" in *virtio*) VIRTIO_CARD="$c"; break;; esac
done
if [ -n "$VIRTIO_CARD" ]; then
  export KWIN_DRM_DEVICES="$VIRTIO_CARD"
  say "KWIN_DRM_DEVICES=$VIRTIO_CARD (ignorando efifb/simpledrm)"
else
  say "!! no hallé card virtio_gpu; kwin verá todos los DRM (posible doble output)"
fi

# Compositor sobre el DRM de virtio (software EGL/GBM vía kms_swrast). SIN --virtual: presenta al display.
say "lanzando kwin_wayland (DRM virtio, software GL)..."
/usr/bin/kwin_wayland --drm >/tmp/kwin.log 2>&1 &
KWIN=$!
# stream EN VIVO del kwin.log al serial (busybox sed NO soporta -u ⇒ nada de sed/pipes: tail -f directo).
( echo "==== kwin.log (stream vivo) ===="; tail -f /tmp/kwin.log ) > /dev/ttyS0 2>/dev/null &
TAILPID=$!
i=0
while [ $i -lt 60 ]; do
  [ -S "$XDG_RUNTIME_DIR/wayland-0" ] && break
  kill -0 $KWIN 2>/dev/null || { say "!! kwin murio:"; dump /tmp/kwin.log; exec /bin/sh; }
  i=$((i+1)); sleep 0.5
done
if [ -S "$XDG_RUNTIME_DIR/wayland-0" ]; then
  say "compositor OK (wayland-0)"
else
  say "!! kwin no expuso wayland-0:"; dump /tmp/kwin.log; exec /bin/sh
fi
export WAYLAND_DISPLAY=wayland-0

# kactivitymanagerd: plasmashell ABORTA sin el
KAMD=$(ls /usr/lib*/libexec/kactivitymanagerd 2>/dev/null | head -1)
[ -n "$KAMD" ] && { "$KAMD" >/tmp/kamd.log 2>&1 & sleep 2; say "kactivitymanagerd lanzado"; }

say "lanzando plasmashell..."
/usr/bin/plasmashell >/tmp/plasmashell.log 2>&1 &
sleep 8
say "procesos: $(ps axo comm 2>/dev/null | grep -E 'kwin|plasma' | sort -u | tr '\n' ' ')"
say "escritorio arriba (o mira /tmp/plasmashell.log). Ctrl-C mata la sesion."
dump /tmp/plasmashell.log
# monitor de estado: cada 15s dice si kwin/plasmashell siguen vivos ⇒ si vuelve a negro, sabremos si
# fue plasmashell que murió (containment) o kwin/output. (busybox pgrep -c cuenta procesos.)
( n=0; while sleep 15; do
    n=$((n+1))
    say "STATUS +$((n*15))s: kwin=$(pgrep kwin_wayland 2>/dev/null | wc -l) plasmashell=$(pgrep plasmashell 2>/dev/null | wc -l)"
  done ) &
wait $KWIN
RC=$?
# kwin salió ⇒ volcá su log para saber por qué (backend QPainter, DRM, modeset…)
say "kwin salió (código $RC) — volcando kwin.log:"
dump /tmp/kwin.log
# copiar los logs a la ext4 raíz y SYNC ⇒ core + logs persisten en la imagen para leer desde el host
# con debugfs/gdb (el SIGKILL de QEMU no sincroniza; sin este sync se pierde todo lo de esta sesión).
cp /tmp/kwin.log /kwin.log 2>/dev/null || true
cp /tmp/plasmashell.log /plasmashell.log 2>/dev/null || true
say "cores en /: $(ls -la /core.* 2>/dev/null | tr '\n' ' ' || echo NINGUNO)"
sync; sync
say "== disco SYNCED (core+logs en la raíz). serial CONGELADO. matá QEMU desde el host. =="
sleep 3600
