#!/bin/bash
# Set up a local (no-root) Xvfb under /tmp/xvfb-install on Debian 12.
# Only needed when the host has no X server / no Xvfb package installed.
# After running this once, /tmp/xvfb-install/start-xvfb.sh launches Xvfb.
set -eu
ROOT=/tmp/xvfb-install
mkdir -p "$ROOT" && cd "$ROOT"

curl -sLO http://deb.debian.org/debian/pool/main/x/xorg-server/xvfb_21.1.7-3+deb12u12_amd64.deb
curl -sLO http://deb.debian.org/debian/pool/main/x/xorg-server/xserver-common_21.1.7-3+deb12u12_all.deb
curl -sLO http://deb.debian.org/debian/pool/main/x/x11-xserver-utils/x11-xserver-utils_7.7+9+b1_amd64.deb
curl -sLO http://deb.debian.org/debian/pool/main/libu/libunwind/libunwind8_1.6.2-3_amd64.deb
curl -sLO http://deb.debian.org/debian/pool/main/libx/libxfont/libxfont2_2.0.4-1_amd64.deb
curl -sLO http://deb.debian.org/debian/pool/main/libf/libfontenc/libfontenc1_1.1.4-1_amd64.deb
curl -sLO http://deb.debian.org/debian/pool/main/x/x11-xkb-utils/x11-xkb-utils_7.7+7_amd64.deb
curl -sLO http://deb.debian.org/debian/pool/main/libx/libxkbfile/libxkbfile1_1.1.0-1_amd64.deb
for f in *.deb; do dpkg-deb -x "$f" extracted/; done

# LD_PRELOAD shim: rewrite the hard-coded "/usr/bin/xkbcomp" path that the
# X server passes to /bin/sh -c.
cat > xkbshim.c << 'EOF'
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int execl(const char* path, const char* arg0, ...) {
  va_list ap; const char* args[64]; int n = 0;
  args[n++] = arg0;
  va_start(ap, arg0);
  const char* a;
  while ((a = va_arg(ap, const char*)) != NULL && n < 63) args[n++] = a;
  args[n] = NULL; va_end(ap);
  if (n >= 3 && args[1] && strcmp(args[1], "-c") == 0 && args[2] &&
      strstr(args[2], "xkbcomp")) {
    const char* to = getenv("XKBCOMP_PATH");
    const char* from = "\"/usr/bin/xkbcomp\"";
    if (to) {
      const char* src = args[2];
      char* p = strstr(src, from);
      if (p) {
        char* dst = malloc(strlen(src) + strlen(to) + 16);
        size_t off = p - src;
        memcpy(dst, src, off);
        sprintf(dst + off, "\"%s\"%s", to, p + strlen(from));
        args[2] = dst;
      }
    }
  }
  return execv(path, (char* const*)args);
}
EOF
gcc -shared -fPIC -o xkbshim.so xkbshim.c -ldl

cat > xkbcomp-wrapper << 'EOF'
#!/bin/sh
export LD_LIBRARY_PATH=/tmp/xvfb-install/extracted/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
exec /tmp/xvfb-install/extracted/usr/bin/xkbcomp "$@"
EOF
chmod +x xkbcomp-wrapper

cat > start-xvfb.sh << 'EOF'
#!/bin/bash
ROOT=/tmp/xvfb-install/extracted
export LD_LIBRARY_PATH=$ROOT/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
export LD_PRELOAD=/tmp/xvfb-install/xkbshim.so
export XKBCOMP_PATH=/tmp/xvfb-install/xkbcomp-wrapper
mkdir -p /tmp/.X11-unix 2>/dev/null
exec $ROOT/usr/bin/Xvfb "$@"
EOF
chmod +x start-xvfb.sh

echo "[setup-xvfb] done; launch with: $ROOT/start-xvfb.sh :99 -screen 0 1280x800x24 -ac +extension RANDR"
