initial commit

This commit is contained in:
ray
2026-07-12 10:17:17 -04:00
commit dab5a4ebc6
1424 changed files with 330463 additions and 0 deletions
@@ -0,0 +1,59 @@
#!/bin/bash
# Quick diagnostic: is uinput functional on this kernel?
# Sunshine uses libevdev/uinput for keyboard/mouse injection, NOT XTEST.
# If this script fails, all input will be silently broken regardless of
# Moonlight settings, admin rights, or WM choice.
set -e
echo "=== uinput diagnostic for Sunshine game streaming ==="
echo "Kernel: $(uname -r)"
echo ""
# Check 1: basic device node
if [ -e /dev/uinput ]; then
echo "[PASS] /dev/uinput exists"
else
echo "[FAIL] /dev/uinput does not exist"
exit 1
fi
# Check 2: writable
if [ -w /dev/uinput ]; then
echo "[PASS] /dev/uinput is writable"
else
echo "[FAIL] /dev/uinput is NOT writable"
echo " Fix: sudo usermod -a -G input $USER && newgrp input"
exit 1
fi
# Check 3: libevdev uinput create (the exact API Sunshine uses)
python3 -c '
import ctypes, sys
libevdev = ctypes.CDLL("libevdev.so.2")
dev = libevdev.libevdev_new()
if not dev:
print("[FAIL] libevdev_new() returned NULL")
sys.exit(1)
libevdev.libevdev_set_name(dev, b"diagnose-uinput")
libevdev.libevdev_enable_event_type(dev, 1) # EV_KEY
uinput_fd = ctypes.c_int(-1)
ret = libevdev.libevdev_uinput_create_from_device(dev, 3, ctypes.byref(uinput_fd))
libevdev.libevdev_free(dev)
if ret == 0 and uinput_fd.value >= 0:
print(f"[PASS] uinput device created (fd={uinput_fd.value})")
import os; os.close(uinput_fd.value)
elif ret < 0:
import errno
errname = errno.errorcode.get(-ret, f"unknown({ret})")
print(f"[FAIL] uinput create returned {ret} (errno: {errname})")
if ret == -25:
print(" ENOTTY: uinput interface broken on this kernel (known on 7.0.0-22)")
print(" Workaround: Steam Remote Play or boot older kernel")
sys.exit(1)
else:
print(f"[FAIL] uinput create returned {ret}")
sys.exit(1)
'
echo ""
echo "=== Diagnostic complete ==="
@@ -0,0 +1,34 @@
#!/bin/bash
# Bootstraps Steam on a headless server without requiring X11 interaction.
# Bypasses the zenity license dialog that normally hangs headless installations.
set -e
# Get the current version expected by the Debian package
if [ -f "/usr/games/steam" ]; then
VERSION=$(grep -oP 'version="\K[^"]+' /usr/games/steam || echo "1.0.0.85")
else
VERSION="1.0.0.85"
fi
echo "Bootstrapping Steam headless installation (Version $VERSION)..."
STEAMDIR="$HOME/.steam/debian-installation"
mkdir -p "$STEAMDIR"
echo "Downloading Steam bootstrap tarball..."
curl -fsSL "https://repo.steampowered.com/steam/archive/beta/steam_${VERSION}.tar.gz" | tar -xz -C "$STEAMDIR"
echo "Extracting embedded Steam runtime..."
tar -xJf "$STEAMDIR/steam-launcher/bootstraplinux_ubuntu12_32.tar.xz" -C "$STEAMDIR"
echo "Marking installation version..."
mkdir -p "$STEAMDIR/deb-installer"
echo "$VERSION" > "$STEAMDIR/deb-installer/version"
echo "Creating required API symlinks..."
ln -fns "$STEAMDIR" "$HOME/.steam/steam"
ln -fns "$STEAMDIR" "$HOME/.steam/root"
echo "Steam is now bootstrapped."
echo "Run 'DISPLAY=:0 steam' to trigger the background update (takes ~3 minutes)."
@@ -0,0 +1,49 @@
#!/bin/bash
# Launch Steam Big Picture and force it fullscreen on headless X11
#
# On headless servers with an HDMI dummy plug, Steam Big Picture frequently
# renders as a 1280x800 window instead of going fullscreen. This means
# Sunshine captures the entire root window (3840x2160) and Moonlight shows
# mostly desktop background — user sees a "black screen."
#
# Additionally, the dummy plug enters DPMS Off after ~15 minutes of
# inactivity, causing X11 capture to return black frames. This script
# forces the display on before launching Steam.
#
# Deploy to ~/.config/sunshine/scripts/steam-bigpicture.sh
# Reference in apps.json:
# "detached": ["/home/ray/.config/sunshine/scripts/steam-bigpicture.sh"]
# Also add DPMS fix to prep-cmd (Sunshine runs commands directly, use sh -c):
# "do": "sh -c 'DISPLAY=:0 xset -dpms s off; DISPLAY=:0 xset dpms force on'"
export DISPLAY=:0
export STEAM_BIGPICTURE=1
# Force the display ON and disable DPMS/screensaver
xset -dpms s off 2>/dev/null
xset dpms force on 2>/dev/null
# Launch Steam into Big Picture mode
steam -fulldesktopres -bigpicture &
# Wait up to 30 seconds for the Big Picture window to appear
for i in $(seq 1 30); do
sleep 1
WIN_ID=$(xdotool search --name "Steam Big Picture Mode" 2>/dev/null | head -1)
if [ -n "$WIN_ID" ]; then
sleep 3
# Fullscreen via wmctrl (EWMH protocol — most reliable on XFWM4)
wmctrl -ir "$WIN_ID" -b add,fullscreen 2>/dev/null
sleep 1
# Fallback: xdotool resize + move + activate
xdotool windowsize "$WIN_ID" 3840 2160 2>/dev/null
xdotool windowmove "$WIN_ID" 0 0 2>/dev/null
xdotool windowactivate "$WIN_ID" 2>/dev/null
exit 0
fi
done
exit 1
@@ -0,0 +1,62 @@
/*
* LD_PRELOAD shim for Sunshine on kernel 7.0+.
*
* PROBLEM: Kernel 7.0 (confirmed: 7.0.0-22-generic) requires UI_DEV_SETUP ioctl
* BEFORE UI_DEV_CREATE on /dev/uinput. The old write()+UI_DEV_CREATE sequence
* and bare UI_DEV_CREATE both fail with EINVAL. Sunshine links against
* libevdev which uses the old method. Result: keyboard and mouse input silently
* fail (Sunshine receives Moonlight packets but can't inject into X11).
*
* FIX: This shim overrides libevdev_uinput_create_from_device() to use the
* correct UI_DEV_SETUP + UI_DEV_CREATE sequence.
*
* COMPILE: gcc -shared -fPIC -o uinput_fix.so uinput-shim.c -ldl
*
* DEPLOY:
* 1. cp uinput_fix.so ~/.config/sunshine/uinput_fix.so
* 2. Add to Sunshine systemd override:
* [Service]
* Environment=LD_PRELOAD=/home/ray/.config/sunshine/uinput_fix.so
* 3. systemctl --user daemon-reload
* 4. systemctl --user restart sunshine
*
* VERIFY: After connecting Moonlight, check that input events are created:
* grep "keyboard packet" ~/.config/sunshine/sunshine.log | tail -5
* # Should see packets arriving, AND keyboard/mouse now works on client.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/uinput.h>
int libevdev_uinput_create_from_device(void *dev, int flags, int *uifd) {
int fd = open("/dev/uinput", O_WRONLY);
if (fd < 0) { return -errno; }
struct uinput_setup usetup;
memset(&usetup, 0, sizeof(usetup));
usetup.id.bustype = BUS_USB;
usetup.id.vendor = 0x1234;
usetup.id.product = 0x5678;
strncpy(usetup.name, "Sunshine Keyboard", UINPUT_MAX_NAME_SIZE-1);
if (ioctl(fd, UI_DEV_SETUP, &usetup) < 0) {
close(fd);
return -errno;
}
if (ioctl(fd, UI_DEV_CREATE) < 0) {
close(fd);
return -errno;
}
*uifd = fd;
return 0;
}