initial commit
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user