feat: Init

This commit is contained in:
Wanjohi
2026-04-03 00:33:36 +03:00
parent b743dab332
commit 13db20bad9
499 changed files with 2311 additions and 80039 deletions

484
build/Dockerfile Normal file
View File

@@ -0,0 +1,484 @@
# syntax=docker/dockerfile:1
# =============================================================================
# Nestri Unified Multi-stage Build
# =============================================================================
#
# Build graph (BuildKit runs independent branches in parallel):
#
# initial ─► builder ──┬─► libkrunfw-build ─► libkrun-build ──┐
# ├─► virgl-build ───────────────────────┤
# ├─► mesa-build ─► lib32-mesa-build │
# │ │
# └─► rust-builder ─┬─► gst-wayland-build
# ├─► wl-proxy-build
# ├─► nestrisink-build
# └─► muvm-build ◄─────┘
#
# initial ─► runner ◄── all build stages
#
# Build:
# docker buildx build --target runner -t nestri:latest .
#
# Recommended .dockerignore:
# target/
# .git/
# *.md
#
# =============================================================================
#******************************************************************************
# initial
#******************************************************************************
# Minimal updated Arch Linux base. Every other stage inherits from here.
FROM docker.io/archlinux/archlinux:latest AS initial
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -Syu --noconfirm
#******************************************************************************
# builder
#******************************************************************************
# builder has C/C++ tooling, makepkg, and a non-root build user.
FROM initial AS builder
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm \
base-devel \
clang \
cmake \
git \
meson \
ninja \
python \
pkg-config
WORKDIR /scratch
ENV ARTIFACTS=/artifacts
RUN mkdir -p "$ARTIFACTS"
# makepkg refuses to run as root
RUN useradd -m builder \
&& echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
# Maximise build parallelism
RUN sed -i "s/#MAKEFLAGS=.*/MAKEFLAGS=\"-j$(nproc)\"/" /etc/makepkg.conf
# Let the builder user write to /scratch and /artifacts
RUN chgrp builder /scratch "$ARTIFACTS" \
&& chmod g+ws /scratch "$ARTIFACTS"
#******************************************************************************
# rust-builder
#******************************************************************************
# rust-builder adds the Rust toolchain and common crate build deps.
FROM builder AS rust-builder
ENV CARGO_HOME=/usr/local/cargo
ENV PATH="${CARGO_HOME}/bin:${PATH}"
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm \
rust \
cargo \
gstreamer \
gst-plugins-base \
gst-plugins-good \
gst-plugins-bad \
libdrm \
libxkbcommon \
wayland \
wayland-protocols \
libinput \
libseccomp \
libcap
# cargo-c is needed by gst-wayland-display (cargo cinstall)
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
cargo install cargo-c
#******************************************************************************
# libkrunfw-build
#******************************************************************************
# Build libkrunfw package with 32-bit support.
FROM builder AS libkrunfw-build
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
bc \
python-pyelftools \
cpio \
curl \
wget
COPY build/packages/libkrunfw/ /scratch/libkrunfw/
WORKDIR /scratch/libkrunfw
RUN chown -R builder:builder .
USER builder
RUN makepkg --syncdeps --noconfirm --skippgpcheck \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# libkrun-build
#******************************************************************************
# Build libkrun package (depends on libkrunfw).
FROM builder AS libkrun-build
# Install previously-built libkrunfw
WORKDIR /scratch
COPY --from=libkrunfw-build /artifacts/*.zst pkgs/
RUN pacman -U --noconfirm pkgs/*.zst && rm -rf pkgs
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
rust \
cargo \
patchelf \
clang \
clang18 \
pipewire \
virglrenderer
COPY build/packages/libkrun/ /scratch/libkrun/
WORKDIR /scratch/libkrun
RUN chown -R builder:builder .
USER builder
# NOTE: clang18 required newer versions fail to build libkrun
RUN LIBCLANG_PATH=/usr/lib/llvm18/lib \
makepkg --syncdeps --noconfirm --skippgpcheck \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# virgl-build
#******************************************************************************
# Build virglrenderer package.
FROM builder AS virgl-build
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
python-yaml \
libepoxy \
mesa \
libva \
libdrm \
vulkan-headers \
vulkan-icd-loader \
libx11 \
glu
COPY build/virglrenderer/ /scratch/virglrenderer/
WORKDIR /scratch/virglrenderer
RUN chown -R builder:builder .
USER builder
RUN makepkg --syncdeps --noconfirm --skippgpcheck --skipinteg \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# mesa-build
#******************************************************************************
# Build Mesa packages (64-bit).
FROM builder AS mesa-build
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
git \
python-mako \
python-packaging \
python-yaml \
llvm \
rust \
rust-bindgen \
cbindgen \
clang \
glslang \
wayland-protocols \
libvdpau \
libva \
libxrandr
COPY build/mesa/ /scratch/mesa/
WORKDIR /scratch/mesa
RUN chown -R builder:builder .
USER builder
RUN makepkg --syncdeps --noconfirm --skippgpcheck --skipinteg \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# lib32-mesa-build
#******************************************************************************
# Build 32-bit Mesa packages (depends on 64-bit mesa).
FROM builder AS lib32-mesa-build
# Enable multilib repository for 32-bit dependencies
RUN echo -e "\n[multilib]\nInclude = /etc/pacman.d/mirrorlist" \
>> /etc/pacman.conf
# Install 64-bit mesa packages first
WORKDIR /scratch
COPY --from=mesa-build /artifacts/*.zst pkgs/
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -Syu --noconfirm \
&& pacman -U --noconfirm pkgs/*.zst \
&& rm -rf pkgs
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
git \
python-mako \
python-packaging \
python-yaml \
llvm \
rust \
rust-bindgen \
cbindgen \
clang \
glslang \
wayland-protocols \
libvdpau \
libva \
libxrandr \
lib32-gcc-libs \
lib32-libx11 \
lib32-libdrm \
lib32-llvm \
lib32-expat \
lib32-libelf \
lib32-zstd \
lib32-wayland
COPY build/packages/lib32-mesa/ /scratch/lib32-mesa/
WORKDIR /scratch/lib32-mesa
RUN chown -R builder:builder .
USER builder
RUN makepkg --syncdeps --noconfirm --skippgpcheck --skipinteg \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# gst-wayland-build
#******************************************************************************
# Build gst-wayland-display plugin.
FROM rust-builder AS gst-wayland-build
WORKDIR /scratch/gst-wayland-display
RUN git clone https://github.com/games-on-whales/gst-wayland-display.git . \
&& git checkout 67b1183997fd7aaf57398e4b01bd64c4d2433c45
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
--mount=type=cache,target=/scratch/gst-wayland-display/target \
cargo cinstall --prefix="${ARTIFACTS}/usr" --release --features cuda
#******************************************************************************
# wl-proxy-build
#******************************************************************************
# Build wl-cross-domain-proxy.
FROM rust-builder AS wl-proxy-build
WORKDIR /scratch/wl-cross-domain-proxy
RUN git clone https://codeberg.org/drakulix/wl-cross-domain-proxy.git . \
&& git checkout c6ce1ca89fb4d6f4f18d3aaf88324d40d4589177
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
--mount=type=cache,target=/scratch/wl-cross-domain-proxy/target \
cargo build --release \
&& install -Dm755 target/release/wl-cross-domain-proxy \
"${ARTIFACTS}/usr/bin/wl-cross-domain-proxy"
#******************************************************************************
# nestrisink-build
#******************************************************************************
# Build nestri GStreamer sink plugin.
FROM rust-builder AS nestrisink-build
WORKDIR /scratch/nestrisink
COPY Cargo.toml Cargo.lock ./
COPY crates/ ./crates/
COPY apps/ ./apps/
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
--mount=type=cache,target=/scratch/nestrisink/target \
cargo build -p nestri-gst --release \
&& mkdir -p "${ARTIFACTS}/usr/lib/gstreamer-1.0" \
&& cp target/release/libgstnestri.so \
"${ARTIFACTS}/usr/lib/gstreamer-1.0/"
#******************************************************************************
# muvm-build
#******************************************************************************
# Build muvm binary (depends on libkrun + virgl packages).
FROM rust-builder AS muvm-build
# Install custom-built packages that muvm links against
WORKDIR /scratch
COPY --from=libkrunfw-build /artifacts/*.zst pkgs/
COPY --from=libkrun-build /artifacts/*.zst pkgs/
COPY --from=virgl-build /artifacts/*.zst pkgs/
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed pipewire \
&& pacman -U --noconfirm pkgs/*.zst \
&& rm -rf pkgs
WORKDIR /scratch/muvm
COPY Cargo.toml Cargo.lock ./
COPY crates/ ./crates/
COPY apps/ ./apps/
COPY share/ ./share/
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
--mount=type=cache,target=/scratch/muvm/target \
cargo build -p muvm --release \
&& install -Dm755 target/release/muvm "${ARTIFACTS}/usr/bin/muvm" \
&& install -Dm755 target/release/muvm-guest "${ARTIFACTS}/usr/bin/muvm-guest"
#******************************************************************************
# runner
#******************************************************************************
# The final runtime image kept as simple as possible.
FROM initial AS runner
# ---- Enable multilib for 32-bit packages ----
RUN echo -e "\n[multilib]\nInclude = /etc/pacman.d/mirrorlist" >> /etc/pacman.conf
# ---- Collect and install custom-built .zst packages ----
COPY --from=mesa-build /artifacts/*.zst /tmp/pkgs/
COPY --from=lib32-mesa-build /artifacts/*.zst /tmp/pkgs/
COPY --from=libkrunfw-build /artifacts/*.zst /tmp/pkgs/
COPY --from=libkrun-build /artifacts/*.zst /tmp/pkgs/
COPY --from=virgl-build /artifacts/*.zst /tmp/pkgs/
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -Syu --noconfirm \
&& pacman -U --noconfirm /tmp/pkgs/*.zst \
&& rm -rf /tmp/pkgs
# ---- System runtime packages (single transaction for consistency) ----
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm \
# Core
base systemd sudo bash \
# Steam & Gaming
steam gamescope mangohud seatd \
# Vulkan / Mesa / VA-API
vulkan-icd-loader vulkan-tools lib32-vulkan-icd-loader \
libva-mesa-driver libva-utils \
# Wayland / X
wayland libdrm libxkbcommon \
xorg-xwayland xwayland-satellite \
libinput \
# Audio
pipewire pipewire-pulse pipewire-alsa wireplumber \
lib32-libpulse \
# GStreamer runtime plugins
gstreamer gst-plugins-base \
gst-plugins-good gst-plugins-bad \
gst-plugin-isobmff gst-plugin-va \
gst-plugin-pipewire gst-plugin-qsv \
# Video acceleration
vpl-gpu-rt \
# GUI / Fonts
gtk3 lib32-gtk3 \
ttf-liberation noto-fonts-cjk \
# Networking & System
dbus curl passt openssh hwdata \
jq pacman-contrib lib32-libvdpau
# ---- Copy artifacts from Rust build stages ----
COPY --from=wl-proxy-build /artifacts/usr/ /usr/
COPY --from=gst-wayland-build /artifacts/usr/ /usr/
COPY --from=nestrisink-build /artifacts/usr/ /usr/
COPY --from=muvm-build /artifacts/usr/ /usr/
# ---- muvm-guest multi-call symlinks ----
RUN mkdir -p /opt/bin \
&& ln -sf /usr/bin/muvm-guest /opt/bin/muvm-remote \
&& ln -sf /usr/bin/muvm-guest /opt/bin/muvm-configure-network \
&& ln -sf /usr/bin/muvm-guest /opt/bin/muvm-pwbridge
# ---- User setup ----
ARG NESTRI_USER_PWD=""
ENV NESTRI_USER="nestri" \
NESTRI_UID=1000 \
NESTRI_GID=1000 \
NESTRI_LANG=en_US.UTF-8 \
NESTRI_XDG_RUNTIME_DIR=/run/user/1000 \
NESTRI_HOME=/home/nestri \
NVIDIA_DRIVER_CAPABILITIES=all
RUN mkdir -p "/home/${NESTRI_USER}" \
&& groupadd -g "${NESTRI_GID}" "${NESTRI_USER}" \
&& useradd -d "/home/${NESTRI_USER}" \
-u "${NESTRI_UID}" -g "${NESTRI_GID}" \
-s /bin/bash "${NESTRI_USER}" \
&& echo "${NESTRI_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers \
&& NESTRI_USER_PWD="${NESTRI_USER_PWD:-$(openssl rand -base64 12)}" \
&& echo "Setting password for ${NESTRI_USER}" \
&& echo "${NESTRI_USER}:${NESTRI_USER_PWD}" | chpasswd \
&& mkdir -p "${NESTRI_XDG_RUNTIME_DIR}" \
&& chown "${NESTRI_USER}:${NESTRI_USER}" "${NESTRI_XDG_RUNTIME_DIR}" \
&& usermod -aG input,video,render,seat,wheel,systemd-journal "${NESTRI_USER}"
# ---- Wireplumber suspend disable ----
RUN mkdir -p /run/dbus \
&& sed -i -z \
-e 's/{[[:space:]]*name = node\/suspend-node\.lua,[[:space:]]*type = script\/lua[[:space:]]*provides = hooks\.node\.suspend[[:space:]]*}[[:space:]]*//g' \
-e '/wants = \[/{s/hooks\.node\.suspend\s*//; s/,\s*\]/]/}' \
/usr/share/wireplumber/wireplumber.conf
# ---- Audio & config directories ----
COPY etc/ /etc/
# ---- Steam config ----
RUN mkdir -p "${NESTRI_HOME}/.local/share/Steam/config"
COPY build/steam/config.vdf "${NESTRI_HOME}/.local/share/Steam/config/"
# ---- Mask noisy/broken services ----
RUN systemctl mask \
dm-event.socket \
systemd-firstboot.service \
systemd-homed.service \
systemd-hwdb-update.service \
systemd-network-generator.service \
systemd-networkd.service \
systemd-networkd-wait-online.service \
systemd-remount-fs.service \
systemd-resolved.service \
systemd-timesyncd.service \
systemd-userdbd.service \
getty@tty1.service \
serial-getty@.service
# ---- Systemd units ----
RUN systemctl enable dbus.service \
&& systemctl set-default microvm.target
# ---- Scripts (copied last they change the most) ----
COPY build/usr/bin/ /usr/bin/
# ---- Smoke test ----
RUN command -v steam muvm muvm-guest wl-cross-domain-proxy \
nestri-entry nestri-init
# ---- Final cleanup ----
RUN rm -rf /tmp/*

View File

@@ -0,0 +1,48 @@
legacy_layout=false
# common
horizontal
horizontal_stretch
hud_no_margin
no_small_font
background_alpha=0.66
round_corners=0
background_color=000000
font_size=24
position=top-left
engine_short_names
# colors
text_color=DFDFDF
gpu_color=FF4E00
cpu_color=00AA00
engine_color=00AA00
vram_color=00AA00
ram_color=00AA00
frametime_color=FF4E00
# load colors
cpu_load_color=DFDFDF,DF964D,DF3D3D
gpu_load_color=DFDFDF,DF964D,DF3D3D
# GPU and VRAM
gpu_text=NESTRI
gpu_stats
gpu_load_change
gpu_load_value=70,90
vram
# CPU and RAM
cpu_text=CPU
cpu_stats
cpu_load_change
cpu_load_value=70,90
ram
# FPS and timing
fps
fps_metrics=0.01
frame_timing

View File

@@ -0,0 +1,26 @@
context.modules = [
{
name = libpipewire-module-loopback
args = {
node.description = "Nestri Audio"
capture.props = {
node.name = "nestri-sink"
media.class = "Audio/Sink"
media.type = "Audio"
audio.position = [FL FR]
node.always-process = true
session.suspend-timeout-seconds = 0
priority.session = 10000
priority.driver = 10000
}
playback.props = {
node.name = "nestri-source"
media.class = "Audio/Source"
media.type = "Audio"
audio.position = [FL FR]
node.always-process = true
session.suspend-timeout-seconds = 0
}
}
}
]

View File

@@ -0,0 +1,7 @@
context.properties = {
default.clock.rate = 48000
default.clock.allowed-rates = [48000]
default.clock.min-quantum = 128
default.clock.max-quantum = 1024
default.clock.quantum = 512
}

View File

@@ -0,0 +1,4 @@
[Unit]
Description=Nestri microVM system
AllowIsolate=yes
Wants=systemd-journald.socket systemd-udevd.service dbus.socket

View File

@@ -0,0 +1 @@
../nestri-network.service

View File

@@ -0,0 +1 @@
../nestri-pipewire-pulse.socket

View File

@@ -0,0 +1 @@
../nestri-pwbridge.socket

View File

@@ -0,0 +1 @@
../nestri-remote.service

View File

@@ -0,0 +1 @@
../nestri-session-bus.socket

View File

@@ -0,0 +1 @@
../nestri-user-session.service

View File

@@ -0,0 +1 @@
../nestri-wayland-proxy.socket

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Configure VM network via muvm
DefaultDependencies=no
Before=network.target
After=systemd-udevd.service
Wants=microvm.target
[Service]
Type=oneshot
ExecStart=/opt/bin/muvm-configure-network
RemainAfterExit=yes
[Install]
WantedBy=microvm.target

View File

@@ -0,0 +1,15 @@
[Unit]
Description=PipeWire PulseAudio compatibility daemon
Requires=nestri-pipewire-pulse.socket
After=nestri-pwbridge.service
[Service]
Type=exec
ExecStartPre=/bin/mkdir -p /run/vm-user/pulse
ExecStart=/usr/bin/pipewire-pulse
Environment=XDG_RUNTIME_DIR=/run/vm-user
Environment=PIPEWIRE_RUNTIME_DIR=/run/vm-user
User=nestri
Group=nestri
Restart=on-failure
RestartSec=1

View File

@@ -0,0 +1,13 @@
[Unit]
Description=PipeWire PulseAudio compatibility socket
PartOf=nestri-pipewire-pulse.service
After=nestri-pwbridge.socket nestri-user-session.service
Requires=nestri-pwbridge.socket nestri-user-session.service
[Socket]
ListenStream=/run/vm-user/pulse/native
SocketUser=nestri
SocketGroup=nestri
[Install]
WantedBy=microvm.target

View File

@@ -0,0 +1,12 @@
[Unit]
Description=Make /proc/sys writable for muvm
DefaultDependencies=no
Before=systemd-sysctl.service systemd-tmpfiles-setup.service
After=local-fs-pre.target
[Service]
Type=oneshot
RemainAfterExit=yes
# umount the ro bind mount; exit 0 so service succeeds
# even if there's nothing to unmount (defensive)
ExecStart=/bin/sh -c 'umount /proc/sys 2>/dev/null || true'

View File

@@ -0,0 +1,7 @@
[Unit]
Description=PipeWire cross-domain proxy
Requires=nestri-pwbridge.socket
[Service]
Type=exec
ExecStart=/opt/bin/muvm-pwbridge

View File

@@ -0,0 +1,13 @@
[Unit]
Description=PipeWire cross-domain proxy socket
PartOf=nestri-pwbridge.service
After=nestri-user-session.service
Requires=nestri-user-session.service
[Socket]
ListenStream=/run/vm-user/pipewire-0
SocketUser=nestri
SocketGroup=nestri
[Install]
WantedBy=microvm.target

View File

@@ -0,0 +1,33 @@
[Unit]
Description=microVM application runner
OnFailure=exit.target
OnSuccess=exit.target
Wants=sockets.target
After=sockets.target
[Service]
Type=exec
PassEnvironment=MUVM_REMOTE_CONFIG MESA_LOADER_DRIVER_OVERRIDE TERM VK_DRIVER_FILES
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/vm-user/dbus.sock
Environment=XDG_RUNTIME_DIR=/run/vm-user
Environment=WAYLAND_DISPLAY=wayland-1
Environment=XDG_SESSION_TYPE=wayland
Environment=PULSE_SERVER=unix:/run/vm-user/pulse/native
Environment=SDL_AUDIO_DRIVER=pipewire
Environment=MESA_LOADER_DRIVER_OVERRIDE=zink
Environment=GALLIUM_DRIVER=zink
Environment=XAUTHORITY=/run/vm-user/xauth
Environment=MANGOHUD_CONFIGFILE=/etc/nestri/configs/MangoHud/MangoHud.conf
#Environment=SDL_VIDEODRIVER=wayland
Environment=PATH=/usr/bin:/usr/sbin:/bin:/sbin
User=root
Group=root
ExecStartPre=+/bin/chown nestri:nestri /run/vm-user
ExecStart=/opt/bin/muvm-remote
StandardOutput=tty
StandardInput=tty
StandardError=tty
TTYPath=/dev/hvc0
[Install]
WantedBy=microvm.target

View File

@@ -0,0 +1,9 @@
[Unit]
Description=D-Bus session bus
Requires=nestri-session-bus.socket
[Service]
Type=exec
ExecStart=/usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --syslog-only
User=nestri
Group=nestri

View File

@@ -0,0 +1,13 @@
[Unit]
Description=D-Bus session bus socket
PartOf=nestri-session-bus.service
After=nestri-user-session.service
Requires=nestri-user-session.service
[Socket]
ListenStream=/run/vm-user/dbus.sock
SocketUser=nestri
SocketGroup=nestri
[Install]
WantedBy=microvm.target

View File

@@ -0,0 +1,13 @@
[Unit]
Description=Prepare VM user runtime directory
DefaultDependencies=no
Before=nestri-wayland-proxy.socket nestri-pwbridge.socket nestri-session-bus.socket
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'mkdir -p /run/vm-user && chown 1000:1000 /run/vm-user && chmod 700 /run/vm-user'
RemainAfterExit=yes
[Install]
WantedBy=microvm.target

View File

@@ -0,0 +1,12 @@
[Unit]
Description=Wayland cross-domain proxy
Requires=nestri-wayland-proxy.socket
[Service]
Type=exec
ExecStartPre=+/bin/chmod 0666 /dev/dri/card0 /dev/dri/renderD128
ExecStart=/usr/bin/wl-cross-domain-proxy --listen-fd
User=nestri
Group=nestri
StandardOutput=file:/tmp/wayland-proxy.log
StandardError=file:/tmp/wayland-proxy-err.log

View File

@@ -0,0 +1,14 @@
[Unit]
Description=Wayland cross-domain proxy socket
PartOf=nestri-wayland-proxy.service
After=nestri-user-session.service
Requires=nestri-user-session.service
[Socket]
ListenStream=/run/vm-user/wayland-1
SocketUser=nestri
SocketGroup=nestri
FileDescriptorName=wayland
[Install]
WantedBy=microvm.target

View File

@@ -0,0 +1 @@
../nestri-procsys-rw.service

View File

@@ -0,0 +1,30 @@
{
"wireplumber.rules": [
{
"description": "Global audio format and rate for audio nodes",
"matches": [
{ "media.class": "Audio/Sink" },
{ "media.class": "Audio/Source" }
],
"apply_properties": {
"audio.format": "F32P",
"audio.rate": 48000,
"audio.channels": 2,
"session.suspend-timeout-seconds": 0
}
},
{
"description": "PulseAudio bridge specific tweaks",
"matches": [
{ "node.name": "pulse_sink" },
{ "node.name": "pulse_source" }
],
"apply_properties": {
"pulse.min.req": 128,
"pulse.max.req": 1024,
"pulse.idle.timeout": 0
}
}
]
}

View File

@@ -0,0 +1,412 @@
pkgbase=lib32-mesa
pkgname=(
lib32-mesa
lib32-vulkan-intel
lib32-vulkan-nouveau
lib32-vulkan-radeon
lib32-vulkan-virtio
lib32-vulkan-mesa-layers
lib32-vulkan-mesa-implicit-layers
)
pkgver=26.1.0
_pkgver=${pkgver/[a-z]/-&}
pkgrel=1
epoch=1
pkgdesc="Open-source OpenGL drivers - 32-bit"
url="https://gitlab.freedesktop.org/mesa/mesa"
arch=(x86_64)
license=("MIT AND BSD-3-Clause AND SGI-B-2.0")
makedepends=(
lib32-clang
lib32-directx-headers
lib32-expat
lib32-gcc-libs
lib32-glibc
lib32-libdisplay-info
lib32-libdrm
lib32-libelf
lib32-libglvnd
lib32-libpng
lib32-libva
lib32-libx11
lib32-libxcb
lib32-libxext
lib32-libxml2
lib32-libxrandr
lib32-libxshmfence
lib32-libxxf86vm
lib32-llvm
lib32-llvm-libs
lib32-lm_sensors
lib32-rust-libs
lib32-spirv-llvm-translator
lib32-spirv-tools
lib32-systemd
lib32-vulkan-icd-loader
lib32-wayland
lib32-xcb-util-keysyms
lib32-zlib
lib32-zstd
# shared between mesa and lib32-mesa
cbindgen
clang
cmake
elfutils
glslang
libclc
meson
python-mako
python-packaging
python-ply
python-pycparser
python-yaml
rust-bindgen
wayland-protocols
xorgproto
)
options=(
# GCC 14 LTO causes segfault in LLVM under si_llvm_optimize_module
# https://gitlab.freedesktop.org/mesa/mesa/-/issues/11140
#
# In general, upstream considers LTO to be broken until explicit notice.
!lto
)
source=("${pkgname}::git+${url}.git#branch=main")
# Rust crates for NVK, used as Meson subprojects
declare -A _crates=(
bitflags 2.9.1
cfg-if 1.0.0
equivalent 1.0.1
errno 0.3.12
hashbrown 0.14.1
indexmap 2.2.6
libc 0.2.171
log 0.4.27
once_cell 1.8.0
paste 1.0.14
pest 2.8.0
pest_derive 2.8.0
pest_generator 2.8.0
pest_meta 2.8.0
proc-macro2 1.0.86
quote 1.0.35
remain 0.2.12
roxmltree 0.20.0
rustc-hash 2.1.1
rustix 1.1.2
syn 2.0.87
thiserror 2.0.11
thiserror-impl 2.0.11
ucd-trie 0.1.6
unicode-ident 1.0.12
windows-link 0.2.0
windows-sys 0.61.1
zerocopy 0.8.13
zerocopy-derive 0.8.13
)
# Used to generate the above table
_gencrates() {
grep '^source_url' subprojects/*-rs.wrap | \
sed -r 's|.*crates/([^/]+)/([0-9.]+)/download|\1 \2|' | \
column -t -S 2 | sed 's/^/ /'
}
for _crate in "${!_crates[@]}"; do
_ver="${_crates[$_crate]}"
source+=(
"$_crate-$_ver.tar.gz::https://crates.io/api/v1/crates/$_crate/$_ver/download"
)
done
# https://docs.mesa3d.org/relnotes.html
sha256sums=('SKIP')
prepare() {
cd lib32-mesa
local src
for src in "${source[@]}"; do
src="${src%%::*}"
src="${src##*/}"
src="${src%.zst}"
[[ $src = *.patch ]] || continue
echo "Applying patch $src..."
patch -Np1 < "../$src"
done
}
build() {
local meson_options=(
--cross-file lib32
-D amdgpu-virtio=true
-D intel-virtio-experimental=true
-D android-libbacktrace=disabled
-D b_ndebug=true
-D gallium-drivers=all
-D gallium-extra-hud=true
-D gallium-mediafoundation=disabled
-D gallium-rusticl-enable-drivers=asahi,freedreno,radeonsi
-D gallium-rusticl=true
-D gles1=disabled
-D html-docs=disabled
-D intel-rt=disabled
-D libunwind=disabled
-D microsoft-clc=disabled
-D sysprof=false
-D valgrind=disabled
-D video-codecs=all
-D vulkan-drivers=all
-D vulkan-layers=device-select,intel-nullhw,overlay,screenshot,anti-lag,vram-report-limit
-D vulkan-manifest-per-architecture=false
)
# Build only minimal debug info to reduce size
CFLAGS+=" -g1"
CXXFLAGS+=" -g1"
# Inject subproject packages
export MESON_PACKAGE_CACHE_DIR="$srcdir"
arch-meson lib32-mesa build "${meson_options[@]}"
meson compile -C build
}
_pick() {
local p="$1" f d; shift
for f; do
d="$srcdir/$p/${f#$pkgdir/}"
mkdir -p "$(dirname "$d")"
mv -v "$f" "$d"
rmdir -p --ignore-fail-on-non-empty "$(dirname "$f")"
done
}
package_lib32-mesa() {
depends=(
lib32-expat
lib32-gcc-libs
lib32-glibc
lib32-libdrm
lib32-libelf
lib32-libglvnd
lib32-libx11
lib32-libxcb
lib32-libxext
lib32-libxshmfence
lib32-libxxf86vm
lib32-llvm-libs
lib32-lm_sensors
lib32-spirv-tools
lib32-wayland
lib32-zlib
lib32-zstd
mesa
)
optdepends=("opengl-man-pages: for the OpenGL API man pages")
provides=(
"lib32-libva-mesa-driver=$epoch:$pkgver-$pkgrel"
"lib32-mesa-libgl=$epoch:$pkgver-$pkgrel"
lib32-libva-driver
lib32-opengl-driver
)
conflicts=(
'lib32-libva-mesa-driver<1:24.2.7-1'
'lib32-mesa-libgl<17.0.1-2'
)
replaces=(
'lib32-libva-mesa-driver<1:24.2.7-1'
'lib32-mesa-libgl<17.0.1-2'
)
meson install -C build --destdir "$pkgdir" --no-rebuild
(
local libdir=usr/lib32
cd "$pkgdir"
_pick vkintel $libdir/libvulkan_intel{,_hasvk}.so
_pick vknvidia $libdir/libvulkan_nouveau.so
_pick vkradeon $libdir/libvulkan_radeon.so
_pick vkvirtio $libdir/libvulkan_virtio.so
_pick vkdevice $libdir/libVkLayer_MESA_anti_lag.so
_pick vkdevice $libdir/libVkLayer_MESA_device_select.so
_pick vklayer $libdir/libVkLayer_*.so
rm -rv etc usr/{bin,include,share}
# indirect rendering
ln -sr $libdir/libGLX_{mesa,indirect}.so.0
)
install -Dm644 lib32-mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_lib32-vulkan-intel() {
pkgdesc="Open-source Vulkan driver for Intel GPUs - 32-bit"
depends=(
lib32-expat
lib32-gcc-libs
lib32-glibc
lib32-libdisplay-info
lib32-libdrm
lib32-libx11
lib32-libxcb
lib32-libxshmfence
lib32-spirv-tools
lib32-systemd
lib32-vulkan-icd-loader
lib32-vulkan-mesa-implicit-layers
lib32-wayland
lib32-xcb-util-keysyms
lib32-zlib
lib32-zstd
vulkan-intel
)
optdepends=("lib32-vulkan-mesa-layers: additional vulkan layers")
provides=(lib32-vulkan-driver)
mv vkintel/* "$pkgdir"
install -Dm644 lib32-mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_lib32-vulkan-nouveau() {
pkgdesc="Open-source Vulkan driver for Nvidia GPUs - 32-bit"
depends=(
lib32-expat
lib32-gcc-libs
lib32-glibc
lib32-libdisplay-info
lib32-libdrm
lib32-libx11
lib32-libxcb
lib32-libxshmfence
lib32-spirv-tools
lib32-systemd
lib32-vulkan-icd-loader
lib32-vulkan-mesa-implicit-layers
lib32-wayland
lib32-xcb-util-keysyms
lib32-zlib
lib32-zstd
vulkan-nouveau
)
optdepends=("lib32-vulkan-mesa-layers: additional vulkan layers")
provides=(lib32-vulkan-driver)
mv vknvidia/* "$pkgdir"
install -Dm644 lib32-mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_lib32-vulkan-radeon() {
pkgdesc="Open-source Vulkan driver for AMD GPUs - 32-bit"
depends=(
lib32-expat
lib32-gcc-libs
lib32-glibc
lib32-libdisplay-info
lib32-libdrm
lib32-libelf
lib32-libx11
lib32-libxcb
lib32-libxshmfence
lib32-llvm-libs
lib32-spirv-tools
lib32-systemd
lib32-vulkan-icd-loader
lib32-vulkan-mesa-implicit-layers
lib32-wayland
lib32-xcb-util-keysyms
lib32-zlib
lib32-zstd
vulkan-radeon
)
optdepends=("lib32-vulkan-mesa-layers: additional vulkan layers")
provides=(lib32-vulkan-driver)
replaces=('lib32-amdvlk<=2025.Q2.1-1')
mv vkradeon/* "$pkgdir"
install -Dm644 lib32-mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_lib32-vulkan-virtio() {
pkgdesc="Open-source Vulkan driver for Virtio-GPU (Venus) - 32-bit"
depends=(
lib32-expat
lib32-gcc-libs
lib32-glibc
lib32-libdisplay-info
lib32-libdrm
lib32-libx11
lib32-libxcb
lib32-libxshmfence
lib32-systemd
lib32-vulkan-icd-loader
lib32-vulkan-mesa-implicit-layers
lib32-wayland
lib32-xcb-util-keysyms
lib32-zlib
lib32-zstd
vulkan-virtio
)
optdepends=("lib32-vulkan-mesa-layers: additional vulkan layers")
provides=(lib32-vulkan-driver)
mv vkvirtio/* "$pkgdir"
install -Dm644 lib32-mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_lib32-vulkan-mesa-layers() {
pkgdesc="Mesa's explicit Vulkan layers - 32-bit"
depends=(
lib32-gcc-libs
lib32-glibc
lib32-libpng
vulkan-mesa-layers
)
conflicts=(lib32-vulkan-mesa-layer)
replaces=(lib32-vulkan-mesa-layer)
mv vklayer/* "$pkgdir"
install -Dm644 lib32-mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_lib32-vulkan-mesa-implicit-layers() {
pkgdesc="Mesa's implicit Vulkan layers - 32-bit"
depends=(
lib32-gcc-libs
lib32-glibc
lib32-libdrm
lib32-libxcb
lib32-wayland
vulkan-mesa-implicit-layers
)
conflicts=(lib32-vulkan-mesa-device-select)
replaces=(lib32-vulkan-mesa-device-select)
mv vkdevice/* "$pkgdir"
install -Dm644 lib32-mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
# vim:set sw=2 sts=-1 et:

View File

@@ -0,0 +1,39 @@
pkgname=libkrun
pkgver=1.17.3.r0.gvalpackett
pkgrel=1
pkgdesc="A dynamic library providing Virtualization-based process isolation capabilities (val/vluqsysxtqrk branch)"
url='https://github.com/valpackett/libkrun'
arch=('x86_64')
license=('Apache-2.0')
makedepends=('cargo' 'patchelf' 'clang' 'git')
depends=('glibc' 'gcc-libs' 'libkrunfw' 'pipewire' 'virglrenderer')
# Format: 'folder_name::git+url#branch=branch_name'
source=("${pkgname}::git+${url}.git#branch=val/vluqsysxtqrk")
sha256sums=('SKIP')
pkgver() {
cd "$pkgname"
# Generates a version string based on the latest tag and commit count
git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//'
}
prepare() {
cd "$pkgname"
cargo fetch --locked --target "$(rustc --print host-tuple)"
}
build() {
cd "$pkgname"
export ZSTD_SYS_USE_PKG_CONFIG=1
make BLK=1 NET=1 EFI=0 GPU=1 SND=1 INPUT=1 VERBOSE=1
}
package() {
cd "$pkgname"
make DESTDIR="$pkgdir" PREFIX=/usr LIBDIR_Linux=lib install
install -Dm644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE
}

View File

@@ -0,0 +1,51 @@
pkgname=libkrunfw
_kver=6.12.68
pkgver=5.2.1
pkgrel=1
pkgdesc="A dynamic library bundling the guest payload consumed by libkrun (with 32-bit support)"
url="https://github.com/containers/libkrunfw"
arch=('x86_64')
license=('GPL-2.0-only' 'LGPL-2.1-only')
depends=('glibc')
makedepends=('bc' 'python-pyelftools' 'cpio')
source=("https://github.com/containers/libkrunfw/archive/refs/tags/v$pkgver/$pkgname-$pkgver.tar.gz"
"https://cdn.kernel.org/pub/linux/kernel/v${_kver%%.*}.x/linux-${_kver}.tar.xz")
noextract=("linux-${_kver}.tar.xz")
options=(!debug)
sha256sums=('c7eebd7149b15e4d74d2dff9acd3137dc13eedf604adf1df2efa52d9f36fe0bb'
'd367c7504bd4da520dd01eb08125d2d0ac088bc8af4cd56d236f2074dd4225b7')
build() {
cd "$pkgname-$pkgver"
mkdir -p tarballs
cp -s "$srcdir/linux-${_kver}.tar.xz" tarballs/
_config="config-libkrunfw_x86_64"
# Enable IA32 (32-bit) emulation support
sed -i 's/# CONFIG_IA32_EMULATION is not set/CONFIG_IA32_EMULATION=y/' "$_config"
# Enable modify_ldt syscall (needed by many 32-bit apps including Steam)
sed -i 's/# CONFIG_MODIFY_LDT_SYSCALL is not set/CONFIG_MODIFY_LDT_SYSCALL=y/' "$_config"
# Enable 32-bit time compatibility
sed -i 's/# CONFIG_COMPAT_32BIT_TIME is not set/CONFIG_COMPAT_32BIT_TIME=y/' "$_config"
# Enable x32 ABI
sed -i 's/# CONFIG_X86_X32_ABI is not set/CONFIG_X86_X32_ABI=y/' "$_config"
# Increase max CPU count (default 16 is low for gaming)
sed -i 's/CONFIG_NR_CPUS=16/CONFIG_NR_CPUS=128/' "$_config"
make
}
package() {
cd "$pkgname-$pkgver"
make DESTDIR="$pkgdir" PREFIX=/usr LIBDIR_Linux=lib install
install -Dm644 LICENSE-GPL-2.0-only "$pkgdir"/usr/share/licenses/$pkgname/LICENSE-GPL-2.0-only
install -Dm644 LICENSE-LGPL-2.1-only "$pkgdir"/usr/share/licenses/$pkgname/LICENSE-LGPL-2.1-only
}

View File

@@ -0,0 +1,418 @@
pkgbase=mesa
pkgname=(
mesa
vulkan-intel
vulkan-nouveau
vulkan-radeon
vulkan-virtio
vulkan-mesa-layers
vulkan-mesa-implicit-layers
)
pkgver=26.1.0
_pkgver=${pkgver/[a-z]/-&}
pkgrel=1
epoch=1
pkgdesc="Open-source OpenGL drivers"
url="https://gitlab.freedesktop.org/mesa/mesa"
arch=(x86_64)
license=("MIT AND BSD-3-Clause AND SGI-B-2.0")
makedepends=(
clang
directx-headers
expat
glibc
libdisplay-info
libdrm
libelf
libgcc
libglvnd
libpng
libstdc++
libva
libx11
libxcb
libxext
libxml2
libxrandr
libxshmfence
libxxf86vm
llvm
llvm-libs
lm_sensors
rust
spirv-llvm-translator
spirv-tools
systemd-libs
vulkan-icd-loader
wayland
xcb-util-keysyms
zlib
zstd
# shared between mesa and lib32-mesa
cbindgen
clang
cmake
elfutils
glslang
libclc
meson
python-mako
python-packaging
python-ply
python-pycparser
python-yaml
rust-bindgen
wayland-protocols
xorgproto
# mesa-only deps
libsysprof-capture
valgrind
# html-docs
python-sphinx
python-sphinx-hawkmoth
)
options=(
# GCC 14 LTO causes segfault in LLVM under si_llvm_optimize_module
# https://gitlab.freedesktop.org/mesa/mesa/-/issues/11140
#
# In general, upstream considers LTO to be broken until explicit notice.
!lto
)
source=("${pkgname}::git+${url}.git#branch=main")
# Rust crates for NVK, used as Meson subprojects
declare -A _crates=(
bitflags 2.9.1
cfg-if 1.0.0
equivalent 1.0.1
errno 0.3.12
hashbrown 0.14.1
indexmap 2.2.6
libc 0.2.171
log 0.4.27
once_cell 1.8.0
paste 1.0.14
pest 2.8.0
pest_derive 2.8.0
pest_generator 2.8.0
pest_meta 2.8.0
proc-macro2 1.0.86
quote 1.0.35
remain 0.2.12
roxmltree 0.20.0
rustc-hash 2.1.1
rustix 1.1.2
syn 2.0.87
thiserror 2.0.11
thiserror-impl 2.0.11
ucd-trie 0.1.6
unicode-ident 1.0.12
windows-link 0.2.0
windows-sys 0.61.1
zerocopy 0.8.13
zerocopy-derive 0.8.13
)
# Used to generate the above table
_gencrates() {
grep '^source_url' subprojects/*-rs.wrap | \
sed -r 's|.*crates/([^/]+)/([0-9.]+)/download|\1 \2|' | \
column -t -S 2 | sed 's/^/ /'
}
for _crate in "${!_crates[@]}"; do
_ver="${_crates[$_crate]}"
source+=(
"$_crate-$_ver.tar.gz::https://crates.io/api/v1/crates/$_crate/$_ver/download"
)
done
# https://docs.mesa3d.org/relnotes.html
sha256sums=('SKIP')
prepare() {
cd mesa
local src
for src in "${source[@]}"; do
src="${src%%::*}"
src="${src##*/}"
src="${src%.zst}"
[[ $src = *.patch ]] || continue
echo "Applying patch $src..."
patch -Np1 < "../$src"
done
}
build() {
local meson_options=(
-D amdgpu-virtio=true
-D intel-virtio-experimental=true
-D android-libbacktrace=disabled
-D b_ndebug=true
-D gallium-drivers=all
-D gallium-extra-hud=true
-D gallium-mediafoundation=disabled
-D gallium-rusticl-enable-drivers=asahi,freedreno,radeonsi
-D gallium-rusticl=true
-D gles1=disabled
-D html-docs=enabled
-D intel-rt=enabled
-D libunwind=disabled
-D microsoft-clc=disabled
-D sysprof=true
-D valgrind=enabled
-D video-codecs=all
-D vulkan-drivers=all
-D vulkan-layers=device-select,intel-nullhw,overlay,screenshot,anti-lag,vram-report-limit
-D vulkan-manifest-per-architecture=false
)
# Build only minimal debug info to reduce size
CFLAGS+=" -g1"
CXXFLAGS+=" -g1"
# Inject subproject packages
export MESON_PACKAGE_CACHE_DIR="$srcdir"
arch-meson mesa build "${meson_options[@]}"
meson compile -C build
}
_pick() {
local p="$1" f d; shift
for f; do
d="$srcdir/$p/${f#$pkgdir/}"
mkdir -p "$(dirname "$d")"
mv -v "$f" "$d"
rmdir -p --ignore-fail-on-non-empty "$(dirname "$f")"
done
}
package_mesa() {
depends=(
expat
glibc
libdrm
libelf
libgcc
libglvnd
libstdc++
libx11
libxcb
libxext
libxshmfence
libxxf86vm
llvm-libs
lm_sensors
spirv-tools
wayland
zlib
zstd
)
optdepends=("opengl-man-pages: for the OpenGL API man pages")
provides=(
"libva-mesa-driver=$epoch:$pkgver-$pkgrel"
"mesa-libgl=$epoch:$pkgver-$pkgrel"
libva-driver
opengl-driver
)
conflicts=(
'libva-mesa-driver<1:24.2.7-1'
'mesa-libgl<17.0.1-2'
)
replaces=(
'libva-mesa-driver<1:24.2.7-1'
'mesa-libgl<17.0.1-2'
)
meson install -C build --destdir "$pkgdir" --no-rebuild
(
local libdir=usr/lib
cd "$pkgdir"
_pick vkintel $libdir/libvulkan_intel{,_hasvk}.so
_pick vkintel usr/share/vulkan/icd.d/intel{,_hasvk}_icd.json
_pick vknvidia $libdir/libvulkan_nouveau.so
_pick vknvidia usr/share/vulkan/icd.d/nouveau_icd.json
_pick vkradeon $libdir/libvulkan_radeon.so
_pick vkradeon usr/share/drirc.d/00-radv-defaults.conf
_pick vkradeon usr/share/vulkan/icd.d/radeon_icd.json
_pick vkvirtio $libdir/libvulkan_virtio.so
_pick vkvirtio usr/share/vulkan/icd.d/virtio_icd.json
_pick vkdevice $libdir/libVkLayer_MESA_anti_lag.so
_pick vkdevice $libdir/libVkLayer_MESA_device_select.so
_pick vkdevice usr/share/vulkan/implicit_layer.d
_pick vklayer $libdir/libVkLayer_*.so
_pick vklayer usr/bin/mesa-*-control.py
_pick vklayer usr/share/vulkan/explicit_layer.d
# indirect rendering
ln -sr $libdir/libGLX_{mesa,indirect}.so.0
)
install -Dm644 mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_vulkan-intel() {
pkgdesc="Open-source Vulkan driver for Intel GPUs"
depends=(
expat
glibc
libdisplay-info
libdrm
libgcc
libstdc++
libx11
libxcb
libxshmfence
spirv-tools
systemd-libs
vulkan-icd-loader
vulkan-mesa-implicit-layers
wayland
xcb-util-keysyms
zlib
zstd
)
optdepends=("vulkan-mesa-layers: additional vulkan layers")
provides=(vulkan-driver)
mv vkintel/* "$pkgdir"
install -Dm644 mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_vulkan-nouveau() {
pkgdesc="Open-source Vulkan driver for Nvidia GPUs"
depends=(
expat
glibc
libdisplay-info
libdrm
libgcc
libstdc++
libx11
libxcb
libxshmfence
spirv-tools
systemd-libs
vulkan-icd-loader
vulkan-mesa-implicit-layers
wayland
xcb-util-keysyms
zlib
zstd
)
optdepends=("vulkan-mesa-layers: additional vulkan layers")
provides=(vulkan-driver)
mv vknvidia/* "$pkgdir"
install -Dm644 mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_vulkan-radeon() {
pkgdesc="Open-source Vulkan driver for AMD GPUs"
depends=(
expat
glibc
libdisplay-info
libdrm
libelf
libgcc
libstdc++
libx11
libxcb
libxshmfence
llvm-libs
spirv-tools
systemd-libs
vulkan-icd-loader
vulkan-mesa-implicit-layers
wayland
xcb-util-keysyms
zlib
zstd
)
optdepends=("vulkan-mesa-layers: additional vulkan layers")
provides=(vulkan-driver)
replaces=('amdvlk<=2025.Q2.1-1')
mv vkradeon/* "$pkgdir"
install -Dm644 mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_vulkan-virtio() {
pkgdesc="Open-source Vulkan driver for Virtio-GPU (Venus)"
depends=(
expat
glibc
libdisplay-info
libdrm
libgcc
libx11
libxcb
libxshmfence
systemd-libs
vulkan-icd-loader
vulkan-mesa-implicit-layers
wayland
xcb-util-keysyms
zlib
zstd
)
optdepends=("vulkan-mesa-layers: additional vulkan layers")
provides=(vulkan-driver)
mv vkvirtio/* "$pkgdir"
install -Dm644 mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_vulkan-mesa-layers() {
pkgdesc="Mesa's explicit Vulkan layers"
depends=(
glibc
libgcc
libpng
libstdc++
python
)
conflicts=(vulkan-mesa-layer)
replaces=(vulkan-mesa-layer)
mv vklayer/* "$pkgdir"
install -Dm644 mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
package_vulkan-mesa-implicit-layers() {
pkgdesc="Mesa's implicit Vulkan layers"
depends=(
glibc
libdrm
libxcb
wayland
)
conflicts=(vulkan-mesa-device-select)
replaces=(vulkan-mesa-device-select)
mv vkdevice/* "$pkgdir"
install -Dm644 mesa/docs/license.rst -t "$pkgdir/usr/share/licenses/$pkgname"
}
# vim:set sw=2 sts=-1 et:

View File

@@ -0,0 +1,28 @@
pkgname=virglrenderer
pkgver=1.3.0
pkgrel=1
pkgdesc='A virtual 3D GPU library, that allows the guest operating system to use the host GPU to accelerate 3D rendering'
arch=(x86_64)
url='https://virgil3d.github.io/'
license=(MIT)
depends=(libepoxy mesa libva vulkan-icd-loader)
makedepends=(python meson ninja vulkan-headers python-yaml)
checkdepends=(check)
_tag=virglrenderer-$pkgver
source=(virglrenderer-$pkgver.tar.bz2::https://gitlab.freedesktop.org/virgl/virglrenderer/-/archive/$_tag/virglrenderer-$_tag.tar.bz2)
sha256sums=('47a64189492a754685a430c713ac6700f4b1c3e7b871e87889ddb96e4d65e8ab')
build() {
cd virglrenderer-$_tag
meson --prefix=/usr --libexecdir=lib/$pkgname build \
-Dvideo=true \
-Dvenus=true \
-Ddrm-renderers=amdgpu-experimental,i915-experimental
ninja -C build
}
package() {
cd virglrenderer-$_tag
DESTDIR="$pkgdir" ninja -C build install
install -D -m644 COPYING "$pkgdir/usr/share/licenses/$pkgname/COPYING"
}

21
build/steam/config.vdf Normal file
View File

@@ -0,0 +1,21 @@
"InstallConfigStore"
{
"Software"
{
"Valve"
{
"Steam"
{
"CompatToolMapping"
{
"0"
{
"name" "proton_experimental"
"config" ""
"priority" "75"
}
}
}
}
}
}

576
build/usr/bin/nestri-entry Normal file
View File

@@ -0,0 +1,576 @@
#!/bin/bash
set -e
# ============================================================
# nestri-entry
#
# Entry point that runs inside the bwrap sandbox.
# Launched by nestri-runner.sh as the top-level process.
#
# Responsibilities:
# 1. Start PipeWire (sandbox-local audio server)
# 2. Start GStreamer pipeline (Wayland compositor + encoding + streaming)
# 3. Launch muvm (microVM that runs the actual games)
#
# The GStreamer pipeline IS the Wayland compositor:
# - waylanddisplaysrc creates $XDG_RUNTIME_DIR/wayland-1
# - VM guest connects via wl-cross-domain-proxy (virtio-gpu)
# - Video frames captured as DMA-BUF, encoded, streamed via QUIC
# - Audio captured from PipeWire, encoded as Opus, muxed with video
#
# Environment variables (set by nestri-runner.sh):
# NESTRI_WIDTH Stream width (default: 1920)
# NESTRI_HEIGHT Stream height (default: 1080)
# NESTRI_FPS Stream framerate (default: 60)
# NESTRI_BITRATE Encoder bitrate in kbps (default: 8000)
# NESTRI_RENDER_NODE GPU render node (default: /dev/dri/renderD128)
# NESTRI_BROADCAST Stream broadcast name (default: live)
# NESTRI_GPU GPU type: amd, intel, nvidia (default: amd)
# NESTRI_CODEC Video codec: h264, h265, av1 (default: h264)
# MICROVM_UID User ID inside sandbox/VM
# MICROVM_GID Group ID inside sandbox/VM
#
# muvm-guest binary symlinks (in /opt/bin/):
# muvm-remote — session runner (runs user command)
# muvm-configure-network — network setup for VM
# muvm-pwbridge — PipeWire bridge (host ↔ guest audio)
# ============================================================
# ============================================================
# Configuration (from environment or defaults)
# ============================================================
NESTRI_WIDTH="${NESTRI_WIDTH:-1920}"
NESTRI_HEIGHT="${NESTRI_HEIGHT:-1080}"
NESTRI_FPS="${NESTRI_FPS:-60}"
NESTRI_BITRATE="${NESTRI_BITRATE:-8000}"
NESTRI_RENDER_NODE="${NESTRI_RENDER_NODE:-/dev/dri/renderD128}"
NESTRI_BROADCAST="${NESTRI_BROADCAST:-live}"
MICROVM_UID="${MICROVM_UID:-1000}"
# GPU and codec selection
# GPU options: amd, intel, nvidia
# Codec options: h264, h265, av1
NESTRI_GPU="${NESTRI_GPU:-amd}"
NESTRI_CODEC="${NESTRI_CODEC:-h264}"
# Setup XDG_RUNTIME_DIR
mkdir -p "$XDG_RUNTIME_DIR"
# Track background PIDs for cleanup
PIDS=()
cleanup() {
echo "nestri-entry: shutting down..." >&2
# Kill all tracked background processes
for pid in "${PIDS[@]}"; do
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
kill -TERM "$pid" 2>/dev/null
fi
done
# Wait briefly for graceful shutdown
sleep 0.5
# Force kill anything still alive
for pid in "${PIDS[@]}"; do
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
kill -KILL "$pid" 2>/dev/null
fi
done
# dbus-launch sets this separately
if [ -n "${DBUS_SESSION_BUS_PID:-}" ]; then
kill -TERM "$DBUS_SESSION_BUS_PID" 2>/dev/null
fi
exit
}
trap cleanup EXIT INT TERM
# ============================================================
# 1. D-Bus Session Bus + PipeWire (sandbox-local audio)
#
# WirePlumber requires a D-Bus session bus — it's a hard
# dependency. PipeWire itself can start without D-Bus, but
# WirePlumber (the session manager that handles routing,
# device enumeration, policy) will abort without one.
#
# Inside the VM, systemd provides the session bus via
# nestri-session-bus.service. Here in bwrap, we have no
# systemd, so we launch dbus-daemon manually.
#
# Audio flow:
# Game (in VM) → PipeWire (guest) → muvm-pwbridge → PipeWire (here)
# PipeWire (here) → pipewiresrc → GStreamer → Opus encode → QUIC
# ============================================================
# --- D-Bus session bus ---
echo "nestri-entry: starting dbus session bus..." >&2
# Create dbus directory
mkdir -p "$XDG_RUNTIME_DIR/dbus"
# Launch a session bus and capture its address
eval "$(dbus-launch --sh-syntax)"
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
echo "nestri-entry: WARNING — dbus-launch failed, trying manual start..." >&2
DBUS_SOCKET="$XDG_RUNTIME_DIR/dbus/session-bus"
dbus-daemon \
--session \
--address="unix:path=$DBUS_SOCKET" \
--nofork \
--print-address \
&>/dev/null &
DBUS_PID=$!
PIDS+=($DBUS_PID)
# Wait for socket
for i in $(seq 1 30); do
[ -S "$DBUS_SOCKET" ] && break
sleep 0.1
done
if [ -S "$DBUS_SOCKET" ]; then
export DBUS_SESSION_BUS_ADDRESS="unix:path=$DBUS_SOCKET"
echo "nestri-entry: dbus ready at $DBUS_SOCKET" >&2
else
echo "nestri-entry: WARNING — dbus-daemon failed to start" >&2
echo "nestri-entry: wireplumber will likely fail" >&2
fi
else
# dbus-launch succeeded, track its PID for cleanup
PIDS+=("${DBUS_SESSION_BUS_PID:-}")
echo "nestri-entry: dbus ready (via dbus-launch) PID=${DBUS_SESSION_BUS_PID:-}" >&2
fi
export DBUS_SESSION_BUS_ADDRESS
# --- PipeWire ---
echo "nestri-entry: starting pipewire..." >&2
pipewire &
PIDS+=($!)
# Wait for PipeWire socket
for i in $(seq 1 50); do
[ -S "$XDG_RUNTIME_DIR/pipewire-0" ] && break
sleep 0.1
done
if [ -S "$XDG_RUNTIME_DIR/pipewire-0" ]; then
echo "nestri-entry: pipewire ready" >&2
# WirePlumber needs both PipeWire AND D-Bus
if [ -n "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
wireplumber &
PIDS+=($!)
sleep 0.5
echo "nestri-entry: wireplumber ready" >&2
else
echo "nestri-entry: WARNING — skipping wireplumber (no dbus)" >&2
echo "nestri-entry: audio routing may not work correctly" >&2
fi
else
echo "nestri-entry: WARNING — pipewire not ready, continuing without audio" >&2
fi
# Wait for WirePlumber to register the loopback node
for i in $(seq 1 50); do
if pw-cli ls Node 2>/dev/null | grep -q "nestri-source"; then
break
fi
sleep 0.1
done
if pw-cli ls Node 2>/dev/null | grep -q "nestri-source"; then
echo "nestri-entry: wireplumber ready, loopback node available" >&2
else
echo "nestri-entry: WARNING — loopback node not found after 5s" >&2
fi
# ============================================================
# 2. GStreamer Pipeline — Encoder Detection & Configuration
#
# VAAPI encoders come in two variants:
# - vaXenc — standard encoder
# - vaXlpenc — low-power variant (sometimes the only option)
#
# We probe for available encoders and use what's present.
# ============================================================
# Check if a GStreamer element exists
gst_element_exists() {
gst-inspect-1.0 "$1" &>/dev/null
}
# Find the best available VAAPI encoder for a codec
find_vaapi_encoder() {
local codec="$1"
local standard_enc="va${codec}enc"
local lowpower_enc="va${codec}lpenc"
# Prefer standard encoder, fall back to low-power
if gst_element_exists "$standard_enc"; then
echo "$standard_enc"
elif gst_element_exists "$lowpower_enc"; then
echo "$lowpower_enc"
else
echo ""
fi
}
# ============================================================
# 2. GStreamer Pipeline — Encoding Configuration
#
# GPU-specific encoding paths:
#
# NVIDIA (NVENC):
# Memory: video/x-raw(memory:CUDAMemory)
# H.264: nvh264enc (p1 preset, ultra-low-latency tune)
# H.265: nvh265enc (p1 preset, ultra-low-latency tune)
# AV1: nvav1enc (p1 preset, ultra-low-latency tune)
#
# Modern NVENC presets (p1-p7):
# p1 = fastest (lowest quality)
# p7 = slowest (highest quality)
# Tunes: ultra-low-latency, low-latency, high-quality
#
#
# Intel (QSV):
# Memory: video/x-raw(memory:DMABuf) → vapostproc → video/x-raw(memory:VAMemory)
# H.264: qsvh264enc
# H.265: qsvh265enc
# AV1: qsvav1enc
#
# target-usage is 7 for all (speed) with low-latency set to true
#
#
# AMD/Intel/.. (VAAPI):
# Memory: video/x-raw(memory:DMABuf) → vapostproc → video/x-raw(memory:VAMemory)
# H.264: vah264enc or vah264lpenc (low-power variant)
# H.265: vah265enc or vah265lpenc (low-power variant)
# AV1: vaav1enc or vaav1lpenc (low-power variant)
#
# target-usage: 1 (quality) to 7 (speed)
# For streaming, we use 7 (maximum speed/minimum latency)
#
# The vapostproc element converts DMA-BUF to VA-API memory and
# transforms to NV12 format required by the VA-API encoders.
# ============================================================
# Calculate GOP size (keyframe interval)
# For low latency, we use 1-2 seconds worth of frames
GOP_SIZE=$((NESTRI_FPS * 2))
setup_vaapi_encoder() {
# AMD/Intel VAAPI path — uses DMA-BUF → VA-API memory
# vapostproc handles colorspace conversion to NV12
GST_MEM_CAPS="video/x-raw(memory:DMABuf),width=${NESTRI_WIDTH},height=${NESTRI_HEIGHT},framerate=${NESTRI_FPS}/1 ! vapostproc ! video/x-raw(memory:VAMemory),format=NV12"
case "$NESTRI_CODEC" in
av1)
VAAPI_ENC=$(find_vaapi_encoder "av1")
if [ -z "$VAAPI_ENC" ]; then
echo "nestri-entry: FATAL — no VAAPI AV1 encoder found" >&2
echo "nestri-entry: tried: vaav1enc, vaav1penc" >&2
exit 1
fi
echo "nestri-entry: using VAAPI encoder: $VAAPI_ENC" >&2
GST_ENC="$VAAPI_ENC"
GST_ENC="$GST_ENC bitrate=${NESTRI_BITRATE}"
GST_ENC="$GST_ENC key-int-max=${GOP_SIZE}"
GST_ENC="$GST_ENC target-usage=7"
;;
h265)
VAAPI_ENC=$(find_vaapi_encoder "h265")
if [ -z "$VAAPI_ENC" ]; then
echo "nestri-entry: FATAL — no VAAPI H.265 encoder found" >&2
echo "nestri-entry: tried: vah265enc, vah265lpenc" >&2
exit 1
fi
echo "nestri-entry: using VAAPI encoder: $VAAPI_ENC" >&2
GST_ENC="$VAAPI_ENC"
GST_ENC="$GST_ENC bitrate=${NESTRI_BITRATE}"
GST_ENC="$GST_ENC key-int-max=${GOP_SIZE}"
GST_ENC="$GST_ENC target-usage=7"
;;
h264|*)
VAAPI_ENC=$(find_vaapi_encoder "h264")
if [ -z "$VAAPI_ENC" ]; then
echo "nestri-entry: FATAL — no VAAPI H.264 encoder found" >&2
echo "nestri-entry: tried: vah264enc, vah264lpenc" >&2
exit 1
fi
echo "nestri-entry: using VAAPI encoder: $VAAPI_ENC" >&2
GST_ENC="$VAAPI_ENC"
GST_ENC="$GST_ENC bitrate=${NESTRI_BITRATE}"
GST_ENC="$GST_ENC key-int-max=${GOP_SIZE}"
GST_ENC="$GST_ENC target-usage=7"
;;
esac
}
# Select memory caps and encoder based on GPU type
case "$NESTRI_GPU" in
nvidia)
# NVIDIA NVENC path — uses CUDA memory
# Modern presets: p1 (fastest) to p7 (highest quality)
# Tunes: ultra-low-latency, low-latency, high-quality
GST_MEM_CAPS="video/x-raw(memory:CUDAMemory),width=${NESTRI_WIDTH},height=${NESTRI_HEIGHT},framerate=${NESTRI_FPS}/1"
case "$NESTRI_CODEC" in
av1)
GST_ENC="nvav1enc"
GST_ENC="$GST_ENC bitrate=${NESTRI_BITRATE}"
GST_ENC="$GST_ENC gop-size=${GOP_SIZE}"
GST_ENC="$GST_ENC preset=p1"
GST_ENC="$GST_ENC tune=ultra-low-latency"
GST_ENC="$GST_ENC multi-pass=disabled"
GST_ENC="$GST_ENC zerolatency=true"
;;
h265)
GST_ENC="nvh265enc"
GST_ENC="$GST_ENC bitrate=${NESTRI_BITRATE}"
GST_ENC="$GST_ENC gop-size=${GOP_SIZE}"
GST_ENC="$GST_ENC preset=p1"
GST_ENC="$GST_ENC tune=ultra-low-latency"
GST_ENC="$GST_ENC multi-pass=disabled"
GST_ENC="$GST_ENC zerolatency=true"
;;
h264|*)
GST_ENC="nvh264enc"
GST_ENC="$GST_ENC bitrate=${NESTRI_BITRATE}"
GST_ENC="$GST_ENC gop-size=${GOP_SIZE}"
GST_ENC="$GST_ENC preset=p1"
GST_ENC="$GST_ENC tune=ultra-low-latency"
GST_ENC="$GST_ENC multi-pass=disabled"
GST_ENC="$GST_ENC zerolatency=true"
;;
esac
;;
intel)
# Check for QSV element plugin
if gst_element_exists "qsv"; then
# Intel QSV path — uses DMA-BUF → VA-API memory
# vapostproc handles colorspace conversion to NV12
GST_MEM_CAPS="video/x-raw(memory:DMABuf),width=${NESTRI_WIDTH},height=${NESTRI_HEIGHT},framerate=${NESTRI_FPS}/1 ! vapostproc ! video/x-raw(memory:VAMemory),format=NV12"
case "$NESTRI_CODEC" in
av1)
GST_ENC="qsvav1enc"
GST_ENC="$GST_ENC bitrate=${NESTRI_BITRATE}"
GST_ENC="$GST_ENC gop-size=${GOP_SIZE}"
GST_ENC="$GST_ENC target-usage=7"
GST_ENC="$GST_ENC low-latency=true"
;;
h265)
GST_ENC="qsvh265enc"
GST_ENC="$GST_ENC bitrate=${NESTRI_BITRATE}"
GST_ENC="$GST_ENC gop-size=${GOP_SIZE}"
GST_ENC="$GST_ENC target-usage=7"
GST_ENC="$GST_ENC low-latency=true"
;;
h264|*)
GST_ENC="qsvh264enc"
GST_ENC="$GST_ENC bitrate=${NESTRI_BITRATE}"
GST_ENC="$GST_ENC gop-size=${GOP_SIZE}"
GST_ENC="$GST_ENC target-usage=7"
GST_ENC="$GST_ENC low-latency=true"
;;
esac
else
# Fallback to VAAPI if no QSV available
setup_vaapi_encoder
fi
;;
amd|*)
setup_vaapi_encoder
;;
esac
# ============================================================
# 2. GStreamer Pipeline — Launch
#
# Pipeline structure:
#
# Video path:
# waylanddisplaysrc (compositor + capture)
# → queue (buffer management)
# → [GPU-specific caps + encoder]
# → [codec parser]
#
# Audio path:
# pipewiresrc (capture from PipeWire)
# → audioconvert/audiorate/audioresample
# → opusenc (Opus encoding @ 128kbps)
#
# Output:
# nestrisink → QUIC/iroh-moq streaming
#
# waylanddisplaysrc IS the Wayland compositor:
# - Creates socket at $XDG_RUNTIME_DIR/wayland-1
# - Captures client buffers as DMA-BUF (zero-copy)
# - Input events arrive via nestrisink (bidirectional)
# ============================================================
echo "nestri-entry: starting gstreamer pipeline..." >&2
echo "nestri-entry: resolution: ${NESTRI_WIDTH}x${NESTRI_HEIGHT}@${NESTRI_FPS}fps" >&2
echo "nestri-entry: bitrate: ${NESTRI_BITRATE}kbps" >&2
echo "nestri-entry: gop-size: ${GOP_SIZE} frames" >&2
echo "nestri-entry: render: ${NESTRI_RENDER_NODE}" >&2
echo "nestri-entry: gpu: ${NESTRI_GPU}" >&2
echo "nestri-entry: codec: ${NESTRI_CODEC}" >&2
echo "nestri-entry: encoder: ${GST_ENC%% *}" >&2
echo "nestri-entry: broadcast: ${NESTRI_BROADCAST}" >&2
# Note: GST_MEM_CAPS and GST_ENC are intentionally unquoted
# to allow bash word splitting for GStreamer's '!' element separators.
gst-launch-1.0 --gst-debug-no-color -e \
nestrisink name=sink broadcast="$NESTRI_BROADCAST" \
waylanddisplaysrc render-node="$NESTRI_RENDER_NODE" ! \
queue max-size-buffers=2 max-size-time=0 max-size-bytes=0 ! \
$GST_MEM_CAPS ! \
$GST_ENC ! \
sink. \
pipewiresrc target-object="nestri-source" use-bufferpool=false do-timestamp=true ! \
queue max-size-buffers=2 max-size-time=0 max-size-bytes=0 ! \
"audio/x-raw,format=S16LE,channels=2,rate=48000" ! \
audioconvert ! \
audiorate ! \
audioresample ! \
opusenc bitrate=128000 frame-size=10 ! \
sink. \
>> /tmp/gstreamer.log 2>&1 &
GST_PID=$!
PIDS+=($GST_PID)
# Wait for compositor socket
for i in $(seq 1 100); do
[ -S "$XDG_RUNTIME_DIR/wayland-1" ] && break
sleep 0.1
done
if [ ! -S "$XDG_RUNTIME_DIR/wayland-1" ]; then
echo "nestri-entry: FATAL — compositor socket not created" >&2
echo "nestri-entry: check waylanddisplaysrc plugin and GPU access" >&2
exit 1
fi
echo "nestri-entry: wayland compositor ready at $XDG_RUNTIME_DIR/wayland-1" >&2
# X_DISPLAY_NUM="12"
# X_SOCKET="/tmp/.X11-unix/X$X_DISPLAY_NUM"
# # Launch the XWayland satellite process to handle X11 clients in the VM.
# xwayland-satellite ":$X_DISPLAY_NUM" >> /tmp/x11.log 2>&1 &
# PIDS+=($!)
# # Wait for X11 socket
# for i in $(seq 1 100); do
# [ -S "$X_SOCKET" ] && break
# sleep 0.1
# done
# if [ ! -S "$X_SOCKET" ]; then
# echo "nestri-entry: FATAL — X11 socket $X_SOCKET not created" >&2
# echo "nestri-entry: check xwayland-satellite logs for startup errors" >&2
# exit 1
# fi
# echo "nestri-entry: X11 server ready at $X_SOCKET" >&2
# export DISPLAY=":$X_DISPLAY_NUM"
# ============================================================
# DNS Setup
#
# passt (muvm's network backend) reads /etc/resolv.conf from
# the bwrap namespace to get DNS servers for the VM's DHCP.
# The rootfs may have a stale or symlinked resolv.conf.
# We overwrite it here with the real DNS from the host.
#
# nestri-init also tries to copy /run/nestri/resolv.conf into
# the VM's /etc/resolv.conf, but that often fails because the
# VM filesystem may be read-only. Fixing it here in the bwrap
# layer ensures passt gets the right servers.
# ============================================================
if [ -f /run/nestri/resolv.conf ]; then
# Remove symlink if present, then write directly
rm -f /etc/resolv.conf 2>/dev/null || true
cp /run/nestri/resolv.conf /etc/resolv.conf 2>/dev/null || {
# If /etc is somehow read-only, try writing in-place
cat /run/nestri/resolv.conf > /etc/resolv.conf 2>/dev/null || true
}
echo "nestri-entry: DNS configured:" >&2
cat /etc/resolv.conf >&2
fi
# ============================================================
# 3. muvm — MicroVM Launch
#
# The VM boots from this same filesystem via virtiofs.
# nestri-init wraps systemd, setting up machine-id, dns, etc.
#
# Guest systemd services (started automatically):
# nestri-network.service — muvm-configure-network (networking)
# nestri-wayland-proxy.* — wl-cross-domain-proxy (socket-activated)
# nestri-pwbridge.* — muvm-pwbridge (socket-activated audio)
# nestri-session-bus.* — dbus-daemon (socket-activated)
# nestri-remote.service — muvm-remote (runs user command)
#
# Cross-domain Wayland proxy:
# The VM guest runs wl-cross-domain-proxy which connects to
# the host's wayland-1 socket via virtio-gpu cross-domain context.
# This allows the guest gamescope to render to the host compositor.
#
# Environment passed to guest:
# WAYLAND_DISPLAY=wayland-1 — compositor socket name
# XDG_RUNTIME_DIR=/run/vm-user — guest runtime directory
# XDG_SESSION_TYPE=wayland — session type hint
# SDL_VIDEODRIVER=wayland — force SDL wayland backend
# SDL_AUDIO_DRIVER=pipewire — force SDL pipewire audio
# NESTRI_* — stream configuration
# ============================================================
echo "nestri-entry: starting muvm..." >&2
# -e "SDL_VIDEODRIVER=wayland" \
# -e "ELECTRON_OZONE_PLATFORM_HINT=wayland" \
# -e "_JAVA_AWT_WM_NONREPARENTING=1" \
# -e "QT_QPA_PLATFORM=wayland" \
muvm \
--custom-init-cmdline "nestri-init /sbin/init --echo-target=console" \
-e "container=muvm" \
-e "XDG_SESSION_TYPE=wayland" \
-e "XDG_RUNTIME_DIR=/run/vm-user" \
-e "SDL_AUDIO_DRIVER=pipewire" \
-e "WAYLAND_DISPLAY=wayland-1" \
-e "VK_DRIVER_FILES=$VK_DRIVER_FILES" \
-e "NESTRI_WIDTH=$NESTRI_WIDTH" \
-e "NESTRI_HEIGHT=$NESTRI_HEIGHT" \
-e "NESTRI_FPS=$NESTRI_FPS" \
-e "MICROVM_UID=$MICROVM_UID" \
-e "MICROVM_GID=${MICROVM_GID:-$MICROVM_UID}" \
-e "BOOT_TIME_OFFSET=${BOOT_TIME_OFFSET:-}" \
-i -t \
"$@"
MUVM_EXIT=$?
echo "nestri-entry: muvm exited with code $MUVM_EXIT" >&2
exit $MUVM_EXIT
# /usr/bin/steam -gamepadui -cef-force-gpu &
# STEAM_PID=$!
# PIDS+=($STEAM_PID)
# wait $STEAM_PID

93
build/usr/bin/nestri-init Normal file
View File

@@ -0,0 +1,93 @@
#!/bin/bash
set -e
# ============================================================
# 1. Save files from old /run
# ============================================================
_resolv=""
_localtime=""
[ -f /run/nestri/resolv.conf ] && _resolv=$(cat /run/nestri/resolv.conf)
[ -f /run/nestri/localtime ] && _localtime=$(base64 /run/nestri/localtime 2>/dev/null) || true
# ============================================================
# 2. Fresh tmpfs on /run
# ============================================================
mount -t tmpfs tmpfs /run -o nosuid,nodev,strictatime
# ============================================================
# 3. Restore saved files
# ============================================================
if [ -n "$_resolv" ]; then
echo "$_resolv" > /run/resolv.conf
echo "$_resolv" > /etc/resolv.conf 2>/dev/null || true
fi
if [ -n "$_localtime" ]; then
echo "$_localtime" | base64 -d > /run/localtime
echo "$_localtime" | base64 -d > /etc/localtime 2>/dev/null || true
fi
# ============================================================
# 4. Machine ID
# ============================================================
# FIXME(wanjohiryan): Use the same machine-id as the host? For Steam, i believe it does check machine id and stuff like that for SteamGuard
if [ ! -s /etc/machine-id ]; then
head -c 16 /dev/urandom | od -A n -t x1 | tr -d ' \n' > /etc/machine-id
echo "" >> /etc/machine-id
fi
# ============================================================
# 5. Journald
# ============================================================
mkdir -p /run/systemd/journal /run/log/journal
mkdir -p /etc/systemd/system/systemd-journald.service.d
cat > /etc/systemd/system/systemd-journald.service.d/override.conf << 'EOF'
[Service]
RuntimeDirectory=
LogsDirectory=
StateDirectory=
ExecStartPre=/bin/mkdir -p /run/systemd/journal /run/log/journal
EOF
# ============================================================
# 6. Hide container FILE markers (ro rootfs)
# But DO NOT strip the `container` env var!
# systemd needs it for exit.target (clean shutdown).
# ============================================================
for marker in /.dockerenv /run/.containerenv; do
if [ -e "$marker" ]; then
mount --bind /dev/null "$marker" 2>/dev/null || true
fi
done
# ============================================================
# 8. Isolate X11 socket directory
# ============================================================
if [ -d /tmp/.X11-unix ]; then
mount -t tmpfs tmpfs /tmp/.X11-unix -o noexec,nosuid,relatime
fi
# ============================================================
# 9. Remount /dev/shm as virtiofs with DAX
# (required for cross-domain shared memory, DRI3 fences)
# ============================================================
umount /dev/shm
mount -t virtiofs devshm /dev/shm -o noexec,nosuid,dax
# ============================================================
# 10. Boot time offset
# ============================================================
if [ -n "$BOOT_TIME_OFFSET" ]; then
if unshare --time true 2>/dev/null; then
exec unshare --time -- bash -c '
echo "monotonic '"$BOOT_TIME_OFFSET"'" > /proc/self/timens_offsets 2>/dev/null
echo "boottime '"$BOOT_TIME_OFFSET"'" >> /proc/self/timens_offsets 2>/dev/null
exec "$@"
' -- "$@"
fi
fi
# ============================================================
# 11. Hand off to systemd
# ============================================================
exec "$@"

BIN
build/usr/bin/steam-token.exe Executable file

Binary file not shown.