# syntax=docker/dockerfile:1.7

# Ubuntu 24.04 + NVIDIA GLX + XFCE + PipeWire + Firefox.
#
# Important: the Selkies GStreamer runtime, Python wheel, web client and
# joystick interposer all come from the SAME published Selkies release.
# Do not mix these assets with py-build:main or a frontend built from main.

ARG DISTRIB_RELEASE=24.04
ARG SELKIES_VERSION=1.6.2
ARG NVIDIA_VAAPI_DRIVER_VERSION=latest

# -----------------------------------------------------------------------------
# Build nvidia-vaapi-driver without retaining its build dependencies
# -----------------------------------------------------------------------------

FROM docker.io/library/ubuntu:${DISTRIB_RELEASE} AS nvidia-vaapi-builder

ARG DEBIAN_FRONTEND=noninteractive
ARG NVIDIA_VAAPI_DRIVER_VERSION

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

RUN apt-get update \
    && apt-get install --no-install-recommends -y \
        ca-certificates \
        curl \
        gcc \
        jq \
        meson \
        ninja-build \
        pkg-config \
        libdrm-dev \
        libegl-dev \
        libffmpeg-nvenc-dev \
        libgstreamer-plugins-bad1.0-dev \
        libva-dev \
    && rm -rf /var/lib/apt/lists/*

RUN set -eux; \
    version="${NVIDIA_VAAPI_DRIVER_VERSION}"; \
    if [[ "${version}" == "latest" ]]; then \
        version="$(curl -fsSL https://api.github.com/repos/elFarto/nvidia-vaapi-driver/releases/latest \
            | jq -r '.tag_name' \
            | sed 's/^v//')"; \
    fi; \
    curl -fsSL \
        "https://github.com/elFarto/nvidia-vaapi-driver/archive/refs/tags/v${version}.tar.gz" \
        -o /tmp/nvidia-vaapi-driver.tar.gz; \
    install -d -m 0755 /tmp/nvidia-vaapi-driver; \
    tar -xzf /tmp/nvidia-vaapi-driver.tar.gz \
        --strip-components=1 \
        -C /tmp/nvidia-vaapi-driver; \
    cd /tmp/nvidia-vaapi-driver; \
    meson setup build --prefix=/usr --buildtype=release; \
    meson compile -C build; \
    DESTDIR=/out meson install -C build

# -----------------------------------------------------------------------------
# Final image
# -----------------------------------------------------------------------------

FROM docker.io/library/ubuntu:${DISTRIB_RELEASE}

ARG DEBIAN_FRONTEND=noninteractive
ARG DISTRIB_RELEASE
ARG SELKIES_VERSION
ARG TZ=Europe/Paris
ARG USER_NAME=ubuntu
ARG USER_UID=1000
ARG USER_GID=1000

LABEL org.opencontainers.image.title="Selkies NVIDIA XFCE Desktop" \
      org.opencontainers.image.description="XFCE remote desktop using the stable Selkies GStreamer release stack" \
      org.opencontainers.image.source="https://github.com/selkies-project/docker-selkies-glx-desktop"

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

ENV TZ="${TZ}" \
    LANG="en_US.UTF-8" \
    LANGUAGE="en_US:en" \
    LC_ALL="en_US.UTF-8" \
    PASSWD="mypasswd" \
    DISPLAY=":20" \
    DISPLAY_SIZEW="1920" \
    DISPLAY_SIZEH="1080" \
    DISPLAY_REFRESH="60" \
    DISPLAY_DPI="96" \
    DISPLAY_CDEPTH="24" \
    VIDEO_PORT="DFP" \
    DESKTOP_SESSION="xfce" \
    XDG_SESSION_DESKTOP="xfce" \
    XDG_CURRENT_DESKTOP="XFCE" \
    XDG_SESSION_TYPE="x11" \
    NGINX_PORT="8080" \
    SELKIES_PORT="8081" \
    SELKIES_ENCODER="nvh264enc" \
    SELKIES_ENABLE_HTTPS="false" \
    SELKIES_ENABLE_RESIZE="false" \
    SELKIES_ENABLE_BASIC_AUTH="true" \
    NVIDIA_VISIBLE_DEVICES="all" \
    NVIDIA_DRIVER_CAPABILITIES="all" \
    __GL_SYNC_TO_VBLANK="0" \
    __GLX_VENDOR_LIBRARY_NAME="nvidia" \
    LIBVA_DRIVER_NAME="nvidia" \
    NVD_BACKEND="direct" \
    MOZ_DISABLE_RDD_SANDBOX="1" \
    MOZ_X11_EGL="1" \
    PIPEWIRE_LATENCY="128/48000" \
    XDG_RUNTIME_DIR="/tmp/runtime-ubuntu" \
    PIPEWIRE_RUNTIME_DIR="/tmp/runtime-ubuntu" \
    PULSE_RUNTIME_PATH="/tmp/runtime-ubuntu/pulse" \
    PULSE_SERVER="unix:/tmp/runtime-ubuntu/pulse/native" \
    DBUS_SYSTEM_BUS_ADDRESS="unix:path=/tmp/runtime-ubuntu/dbus-system-bus" \
    APPIMAGE_EXTRACT_AND_RUN="1" \
    SUDO_EDITOR="mousepad" \
    PIP_BREAK_SYSTEM_PACKAGES="1"

# Bootstrap APT and locales.
RUN apt-get update \
    && apt-get install --no-install-recommends -y \
        ca-certificates \
        curl \
        gnupg \
        locales \
        ssl-cert \
        tzdata \
    && locale-gen en_US.UTF-8 fr_FR.UTF-8 \
    && ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime \
    && echo "${TZ}" > /etc/timezone \
    && rm -rf /var/lib/apt/lists/*

# Firefox DEB and the same PipeWire/WirePlumber PPAs as the original image.
RUN install -d -m 0755 \
        /etc/apt/preferences.d \
        /etc/apt/sources.list.d \
        /etc/apt/trusted.gpg.d \
    && printf '%s\n' \
        'Package: firefox*' \
        'Pin: version 1:1snap*' \
        'Pin-Priority: -1' \
        > /etc/apt/preferences.d/firefox-nosnap \
    && curl -fsSL \
        'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x738BEB9321D1AAEC13EA9391AEBDF4819BE21867' \
        | gpg --dearmor \
        > /etc/apt/trusted.gpg.d/mozillateam-ubuntu-ppa.gpg \
    && echo \
        "deb https://ppa.launchpadcontent.net/mozillateam/ppa/ubuntu noble main" \
        > /etc/apt/sources.list.d/mozillateam-ubuntu-ppa.list \
    && curl -fsSL \
        'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xFC43B7352BCC0EC8AF2EEB8B25088A0359807596' \
        | gpg --dearmor \
        > /etc/apt/trusted.gpg.d/pipewire-debian-ubuntu.gpg \
    && echo \
        "deb https://ppa.launchpadcontent.net/pipewire-debian/pipewire-upstream/ubuntu noble main" \
        > /etc/apt/sources.list.d/pipewire-upstream.list \
    && echo \
        "deb https://ppa.launchpadcontent.net/pipewire-debian/wireplumber-upstream/ubuntu noble main" \
        > /etc/apt/sources.list.d/wireplumber-upstream.list

# Desktop, Xorg, NVIDIA interfaces, PipeWire and the dependencies used by the
# original Selkies GStreamer release. No KDE, Chrome, Wine, KasmVNC, RustDesk,
# i386 stack, Intel VA-API stack or AMD/Mesa Vulkan stack.
RUN apt-get update \
    && apt-get install --no-install-recommends -y \
        apache2-utils \
        apt-utils \
        alsa-utils \
        aom-tools \
        bash-completion \
        binutils \
        btop \
        bzip2 \
        clinfo \
        coturn \
        dbus-user-session \
        dbus-x11 \
        desktop-file-utils \
        dnsutils \
        file \
        firefox \
        fonts-dejavu \
        fonts-liberation \
        fonts-noto \
        fonts-noto-cjk \
        fonts-noto-color-emoji \
        fonts-noto-mono \
        fonts-ubuntu \
        fuse \
        git \
        glib-networking \
        gstreamer1.0-libcamera \
        gstreamer1.0-pipewire \
        gstreamer1.0-plugins-bad \
        gvfs \
        jackd2 \
        jq \
        kmod \
        less \
        libdrm2 \
        libegl1 \
        libelf-dev \
        libgcrypt20 \
        libgirepository-1.0-1 \
        libgl1 \
        libgles1 \
        libgles2 \
        libglib2.0-0 \
        libglvnd-dev \
        libglvnd0 \
        libglx0 \
        libgstreamer-plugins-bad1.0-0 \
        libgudev-1.0-0 \
        libjack-jackd2-0 \
        libopenh264-dev \
        libopengl0 \
        libopus0 \
        libpci3 \
        libpipewire-0.3-modules \
        libpipewire-module-x11-bell \
        libpulse0 \
        libsm6 \
        libspa-0.2-bluetooth \
        libspa-0.2-jack \
        libspa-0.2-modules \
        libva-drm2 \
        libva-x11-2 \
        libva2 \
        libvpx-dev \
        libvulkan1 \
        libwayland-dev \
        libwayland-egl1 \
        libx11-6 \
        libx11-xcb1 \
        libxau6 \
        libxcb-dri3-0 \
        libxcb1 \
        libxdamage1 \
        libxdmcp6 \
        libxext6 \
        libxfixes3 \
        libxkbcommon0 \
        libxtst6 \
        libxv1 \
        mousepad \
        nano \
        neofetch \
        net-tools \
        netcat-openbsd \
        nginx \
        ocl-icd-libopencl1 \
        pavucontrol \
        pciutils \
        pipewire \
        pipewire-alsa \
        pipewire-audio-client-libraries \
        pipewire-jack \
        pipewire-libcamera \
        pipewire-locales \
        pipewire-v4l2 \
        pipewire-vulkan \
        procps \
        psmisc \
        python3 \
        python3-dev \
        python3-gi \
        python3-pip \
        python3-setuptools \
        python3-venv \
        python3-wheel \
        ristretto \
        sudo \
        supervisor \
        svt-av1 \
        thunar \
        tumbler \
        udev \
        unzip \
        vainfo \
        vim \
        vulkan-tools \
        wayland-protocols \
        wget \
        wireplumber \
        wireplumber-locales \
        wmctrl \
        x11-apps \
        x11-utils \
        x11-xkb-utils \
        x11-xserver-utils \
        x264 \
        x265 \
        xauth \
        xbitmaps \
        xclip \
        xcvt \
        xdg-user-dirs \
        xdg-utils \
        xfce4 \
        xfce4-goodies \
        xfce4-notifyd \
        xfce4-pulseaudio-plugin \
        xfce4-terminal \
        xfonts-base \
        xfonts-scalable \
        xinit \
        xkb-data \
        xsel \
        xserver-xorg-core \
        xserver-xorg-input-libinput \
        xserver-xorg-legacy \
        xsettingsd \
        xterm \
        xdotool \
        xz-utils \
        zip \
        zstd \
        gir1.2-wp-0.5 \
    && sed -i \
        -e 's#/var/log/nginx/access\.log#/dev/stdout#g' \
        -e 's#/var/log/nginx/error\.log#/dev/stderr#g' \
        -e 's#/run/nginx\.pid#/tmp/nginx.pid#g' \
        /etc/nginx/nginx.conf \
    && printf '\nerror_log /dev/stderr;\n' >> /etc/nginx/nginx.conf \
    && apt-get clean \
    && rm -rf \
        /var/lib/apt/lists/* \
        /var/cache/apt/* \
        /var/cache/debconf/* \
        /var/log/* \
        /tmp/* \
        /var/tmp/*

# Keep only the compiled NVIDIA VA-API runtime.
COPY --from=nvidia-vaapi-builder /out/usr/ /usr/

# NVIDIA libraries are injected by the NVIDIA GPU Operator / Container Toolkit.
RUN printf '%s\n' \
        '/usr/local/nvidia/lib' \
        '/usr/local/nvidia/lib64' \
        > /etc/ld.so.conf.d/nvidia.conf \
    && install -d -m 0755 \
        /etc/OpenCL/vendors \
        /etc/vulkan/icd.d \
        /usr/share/glvnd/egl_vendor.d \
    && echo 'libnvidia-opencl.so.1' \
        > /etc/OpenCL/vendors/nvidia.icd \
    && cat > /etc/vulkan/icd.d/nvidia_icd.json <<'JSON'
{
  "file_format_version": "1.0.0",
  "ICD": {
    "library_path": "libGLX_nvidia.so.0",
    "api_version": "1.3.0"
  }
}
JSON

RUN cat > /usr/share/glvnd/egl_vendor.d/10_nvidia.json <<'JSON'
{
  "file_format_version": "1.0.0",
  "ICD": {
    "library_path": "libEGL_nvidia.so.0"
  }
}
JSON

ENV PATH="/usr/local/nvidia/bin:${PATH}" \
    LD_LIBRARY_PATH="/usr/local/nvidia/lib:/usr/local/nvidia/lib64"

# Install every Selkies component from the same stable v1.6.2 release.
# evdev is built from source on Python 3.12, so gcc is needed only for this step.
RUN set -eux; \
    apt-get update; \
    apt-get install --no-install-recommends -y gcc; \
    ubuntu_version="$(. /etc/os-release; printf '%s' "${VERSION_ID}")"; \
    arch="$(dpkg --print-architecture)"; \
    release_url="https://github.com/selkies-project/selkies/releases/download/v${SELKIES_VERSION}"; \
    curl -fsSL \
        "${release_url}/gstreamer-selkies_gpl_v${SELKIES_VERSION}_ubuntu${ubuntu_version}_${arch}.tar.gz" \
        | tar -xzf - -C /opt; \
    wheel_path="/tmp/selkies_gstreamer-${SELKIES_VERSION}-py3-none-any.whl"; \
    curl -fsSL \
        "${release_url}/selkies_gstreamer-${SELKIES_VERSION}-py3-none-any.whl" \
        -o "${wheel_path}"; \
    python3 -m pip install \
        --break-system-packages \
        --no-cache-dir \
        --force-reinstall \
        "${wheel_path}" \
        'websockets<14.0'; \
    curl -fsSL \
        "${release_url}/selkies-gstreamer-web_v${SELKIES_VERSION}.tar.gz" \
        | tar -xzf - -C /opt; \
    curl -fsSL \
        "${release_url}/selkies-js-interposer_v${SELKIES_VERSION}_ubuntu${ubuntu_version}_${arch}.deb" \
        -o /tmp/selkies-js-interposer.deb; \
    apt-get update; \
    apt-get install --no-install-recommends -y \
        /tmp/selkies-js-interposer.deb; \
    test -f /opt/gst-web/index.html; \
    test -f /opt/gstreamer/gst-env; \
    command -v selkies-gstreamer; \
    rm -f \
        "${wheel_path}" \
        /tmp/selkies-js-interposer.deb; \
    apt-get purge -y gcc; \
    apt-get autoremove -y; \
    apt-get clean; \
    rm -rf /var/lib/apt/lists/* /var/cache/apt/* /tmp/* /var/tmp/*

# Regular desktop user and the permissions required by the original entrypoints.
RUN set -eux; \
    if ! getent group "${USER_NAME}" >/dev/null; then \
        groupadd --gid "${USER_GID}" "${USER_NAME}"; \
    fi; \
    if ! id -u "${USER_NAME}" >/dev/null 2>&1; then \
        useradd \
            --uid "${USER_UID}" \
            --gid "${USER_NAME}" \
            --create-home \
            --shell /bin/bash \
            "${USER_NAME}"; \
    else \
        usermod --shell /bin/bash "${USER_NAME}"; \
    fi; \
    for group in \
        adm audio cdrom dialout dip fax floppy games input lp plugdev render \
        ssl-cert sudo tape tty video voice; \
    do \
        getent group "${group}" >/dev/null \
            && usermod -aG "${group}" "${USER_NAME}" \
            || true; \
    done; \
    echo "${USER_NAME} ALL=(ALL:ALL) NOPASSWD: ALL" \
        > "/etc/sudoers.d/${USER_NAME}"; \
    chmod 0440 "/etc/sudoers.d/${USER_NAME}"; \
    echo "${USER_NAME}:${PASSWD}" | chpasswd; \
    cp -a /usr/bin/sudo /usr/bin/sudo-root; \
    chown root:root /usr/bin/sudo-root; \
    chmod 4755 /usr/bin/sudo-root; \
    install -d -o "${USER_UID}" -g "${USER_GID}" -m 0700 \
        /tmp/runtime-ubuntu; \
    chown -R \
        "${USER_UID}:${USER_GID}" \
        "/home/${USER_NAME}" \
        /etc/X11 \
        /etc/nginx \
        /opt/gstreamer \
        /opt/gst-web \
        /var/lib/nginx

# XFCE defaults for a permanently streamed desktop.
RUN install -d -m 0755 \
        /etc/xdg/xfce4/xfconf/xfce-perchannel-xml \
        /etc/firefox/policies \
    && cat > /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml <<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<channel name="xfce4-power-manager" version="1.0">
  <property name="xfce4-power-manager" type="empty">
    <property name="blank-on-ac" type="int" value="0"/>
    <property name="dpms-enabled" type="bool" value="false"/>
    <property name="lock-screen-suspend-hibernate" type="bool" value="false"/>
  </property>
</channel>
XML

RUN cat > /etc/firefox/policies/policies.json <<'JSON'
{
  "policies": {
    "Preferences": {
      "gfx.x11-egl.force-enabled": {
        "Value": true,
        "Status": "default"
      },
      "media.ffmpeg.vaapi.enabled": {
        "Value": true,
        "Status": "default"
      },
      "media.hardware-video-decoding.force-enabled": {
        "Value": true,
        "Status": "default"
      },
      "media.rdd-ffmpeg.enabled": {
        "Value": true,
        "Status": "default"
      }
    }
  }
}
JSON

RUN update-alternatives --set x-www-browser /usr/bin/firefox || true

# Keep the ORIGINAL entrypoint and Selkies GStreamer entrypoint.
# Replace supervisord.conf with the cleaned version supplied with this file.
COPY --chown=${USER_UID}:${USER_GID} entrypoint.sh /etc/entrypoint.sh
COPY --chown=${USER_UID}:${USER_GID} selkies-gstreamer-entrypoint.sh /etc/selkies-gstreamer-entrypoint.sh
COPY --chown=${USER_UID}:${USER_GID} supervisord.conf /etc/supervisord.conf

RUN sed -i 's/\r$//' \
        /etc/entrypoint.sh \
        /etc/selkies-gstreamer-entrypoint.sh \
        /etc/supervisord.conf \
    && chmod 0755 \
        /etc/entrypoint.sh \
        /etc/selkies-gstreamer-entrypoint.sh \
        /etc/supervisord.conf

# Original coTURN helper used by selkies-gstreamer-entrypoint.sh.
RUN cat > /etc/start-turnserver.sh <<'EOF_TURN'
#!/bin/bash
set -e
exec turnserver \
    --verbose \
    --listening-ip="0.0.0.0" \
    --listening-ip="::" \
    --listening-port="${SELKIES_TURN_PORT:-3478}" \
    --realm="${TURN_REALM:-example.com}" \
    --external-ip="${TURN_EXTERNAL_IP:-127.0.0.1}" \
    --min-port="${TURN_MIN_PORT:-49152}" \
    --max-port="${TURN_MAX_PORT:-65535}" \
    --channel-lifetime="${TURN_CHANNEL_LIFETIME:--1}" \
    --lt-cred-mech \
    --user="selkies:${TURN_RANDOM_PASSWORD}" \
    --no-cli \
    --cli-password="${TURN_RANDOM_PASSWORD}" \
    --userdb="${XDG_RUNTIME_DIR:-/tmp}/turnserver-turndb" \
    --pidfile="${XDG_RUNTIME_DIR:-/tmp}/turnserver.pid" \
    --log-file="stdout" \
    --allow-loopback-peers \
    ${TURN_EXTRA_ARGS} "$@"
EOF_TURN

RUN chmod 0755 /etc/start-turnserver.sh

USER ${USER_UID}:${USER_GID}

ENV USER="${USER_NAME}" \
    HOME="/home/${USER_NAME}" \
    SHELL="/bin/bash"

WORKDIR /home/${USER_NAME}

EXPOSE 8080

ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
