mirror of
https://oauth2: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@github.com/m1ngsama/deckless.git
synced 2026-05-10 19:11:12 +08:00
Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c98f72d8a1 | |||
| be056e0a9c |
11 changed files with 486 additions and 226 deletions
20
.SRCINFO
Normal file
20
.SRCINFO
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
pkgbase = deckless-git
|
||||
pkgdesc = Keep official Steam intact while fixing proxy split, Big Picture rendering, and i3 couch-mode handoff on Linux
|
||||
pkgver = 0.1.0.r0.g089aa68
|
||||
pkgrel = 1
|
||||
url = https://github.com/m1ngsama/deckless
|
||||
arch = any
|
||||
license = GPL-3.0-or-later
|
||||
makedepends = git
|
||||
depends = bash
|
||||
depends = jq
|
||||
depends = steam
|
||||
optdepends = gamescope: run Big Picture inside gamescope
|
||||
optdepends = gamemode: enable gamemode for Big Picture sessions
|
||||
optdepends = i3-wm: enable fullscreen handoff between Big Picture and launched games
|
||||
provides = deckless
|
||||
conflicts = deckless
|
||||
source = git+https://github.com/m1ngsama/deckless.git
|
||||
sha256sums = SKIP
|
||||
|
||||
pkgname = deckless-git
|
||||
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
The format is inspired by Keep a Changelog, and the project follows semantic versioning once tagged releases begin.
|
||||
|
||||
## [0.2.0] - 2026-03-28
|
||||
|
||||
### Added
|
||||
|
||||
- A `deckless-git` `PKGBUILD` and `.SRCINFO` entry point for Arch users who want to package Deckless from a local clone.
|
||||
- A real-machine Big Picture screenshot and validation notes in the README.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Wrapper timing around Steam's updater verification so Deckless no longer needs to present a modified wrapper during the verification phase to recover the proxied, GPU-enabled webhelper path.
|
||||
- Wrapper cleanup now survives Steam's shutdown path by detaching the cleanup helper from the launcher session before Steam exits.
|
||||
|
||||
## [0.1.0] - 2026-03-28
|
||||
|
||||
### Added
|
||||
|
|
@ -18,3 +30,5 @@ The format is inspired by Keep a Changelog, and the project follows semantic ver
|
|||
- GitHub Actions validation for shell syntax and ShellCheck.
|
||||
|
||||
[0.1.0]: https://github.com/m1ngsama/deckless/releases/tag/v0.1.0
|
||||
[0.2.0]: https://github.com/m1ngsama/deckless/compare/v0.1.0...v0.2.0
|
||||
[Unreleased]: https://github.com/m1ngsama/deckless/compare/v0.2.0...HEAD
|
||||
|
|
|
|||
56
PKGBUILD
Normal file
56
PKGBUILD
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Maintainer: m1ngsama <contact@m1ng.space>
|
||||
|
||||
pkgname=deckless-git
|
||||
pkgver=0.1.0.r0.g089aa68
|
||||
pkgrel=1
|
||||
pkgdesc='Keep official Steam intact while fixing proxy split, Big Picture rendering, and i3 couch-mode handoff on Linux'
|
||||
arch=('any')
|
||||
url='https://github.com/m1ngsama/deckless'
|
||||
license=('GPL-3.0-or-later')
|
||||
depends=('bash' 'jq' 'steam')
|
||||
makedepends=('git')
|
||||
optdepends=(
|
||||
'gamescope: run Big Picture inside gamescope'
|
||||
'gamemode: enable gamemode for Big Picture sessions'
|
||||
'i3-wm: enable fullscreen handoff between Big Picture and launched games'
|
||||
)
|
||||
provides=('deckless')
|
||||
conflicts=('deckless')
|
||||
source=('git+https://github.com/m1ngsama/deckless.git')
|
||||
sha256sums=('SKIP')
|
||||
|
||||
pkgver() {
|
||||
cd deckless
|
||||
git describe --long --tags --abbrev=7 | sed 's/^v//; s/-/.r/; s/-/./'
|
||||
}
|
||||
|
||||
package() {
|
||||
cd deckless
|
||||
|
||||
install -d "${pkgdir}/usr/share/deckless"
|
||||
cp -a \
|
||||
LICENSE \
|
||||
README.md \
|
||||
CHANGELOG.md \
|
||||
CONTRIBUTING.md \
|
||||
install.sh \
|
||||
uninstall.sh \
|
||||
bin \
|
||||
config \
|
||||
docs \
|
||||
"${pkgdir}/usr/share/deckless/"
|
||||
|
||||
install -Dm755 /dev/stdin "${pkgdir}/usr/bin/deckless-install" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
exec /usr/share/deckless/install.sh "$@"
|
||||
EOF
|
||||
|
||||
install -Dm755 /dev/stdin "${pkgdir}/usr/bin/deckless-uninstall" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
exec /usr/share/deckless/uninstall.sh "$@"
|
||||
EOF
|
||||
}
|
||||
38
README.md
38
README.md
|
|
@ -14,13 +14,26 @@ It started from a real Arch Linux desktop that needed direct game traffic, proxi
|
|||
|
||||
- Keeps `/usr/bin/steam` as the official upstream client.
|
||||
- Proxies only `steamwebhelper` traffic, so store, community pages, avatars, and embedded web content can use a proxy while games and downloads stay direct.
|
||||
- Restores the original `steamwebhelper_sniper_wrap.sh` when Steam exits.
|
||||
- Restores the original `steamwebhelper_sniper_wrap.sh` shortly after Steam exits through a detached cleanup helper.
|
||||
- Removes Steam's `--disable-gpu` and `--disable-gpu-compositing` flags for Big Picture webhelper sessions.
|
||||
- Exports sane `GBM`, `EGL`, and PulseAudio defaults to help Steam stay on hardware acceleration instead of dropping to software rendering.
|
||||
- Launches Big Picture through `gamescope` and `gamemode` when they are available.
|
||||
- Ships an optional i3 bridge that lets a newly launched game take fullscreen from Big Picture and hands fullscreen back when the game exits.
|
||||
- Installs cleanly into XDG paths and can be rolled back with `./uninstall.sh`.
|
||||
|
||||
## Real-machine verification
|
||||
|
||||
Validated on March 28, 2026 on an Arch Linux desktop with X11, i3, NVIDIA 595.58.3, and a 240 Hz monitor.
|
||||
|
||||

|
||||
|
||||
The live validation for this repository confirmed:
|
||||
|
||||
- Steam updater verification completed without a wrapper size mismatch loop.
|
||||
- The top-level `steamwebhelper` launched with `--proxy-server=...` and without Steam's forced `--disable-gpu*` flags.
|
||||
- `webhelper_gpu.txt` reported `gpu_compositing: enabled`, `opengl: enabled_on`, and `video_decode: enabled`.
|
||||
- Big Picture opened as a focused fullscreen `steamwebhelper` window under i3.
|
||||
|
||||
## Design goals
|
||||
|
||||
- Official Steam first, customization second.
|
||||
|
|
@ -66,11 +79,30 @@ For a controller-first session, launch:
|
|||
steam-bigpicture
|
||||
```
|
||||
|
||||
## Arch packaging
|
||||
|
||||
This repository also ships a `PKGBUILD` for a rolling `deckless-git` package.
|
||||
|
||||
From a local clone:
|
||||
|
||||
```bash
|
||||
makepkg -si
|
||||
deckless-install
|
||||
```
|
||||
|
||||
That installs Deckless under `/usr/share/deckless` and exposes:
|
||||
|
||||
- `deckless-install`
|
||||
- `deckless-uninstall`
|
||||
|
||||
## What gets installed
|
||||
|
||||
- `~/.local/share/deckless/bin/deckless-steam`
|
||||
- `~/.local/share/deckless/bin/deckless-bigpicture`
|
||||
- `~/.local/share/deckless/bin/deckless-i3-bigpicture-bridge`
|
||||
- `~/.local/share/deckless/bin/deckless-sync-webhelper-wrapper`
|
||||
- `~/.local/share/deckless/bin/deckless-webhelper-heal`
|
||||
- `~/.local/share/deckless/bin/deckless-webhelper-cleanup`
|
||||
- `~/.local/bin/steam`
|
||||
- `~/.local/bin/steam-bigpicture`
|
||||
- `~/.local/share/applications/steam.desktop`
|
||||
|
|
@ -119,6 +151,8 @@ See [docs/i3.md](docs/i3.md) for the handoff behavior.
|
|||
- official `steam` package
|
||||
- X11
|
||||
- i3
|
||||
- NVIDIA proprietary driver 595.58.3
|
||||
- 240 Hz display
|
||||
- `gamescope`
|
||||
- `gamemode`
|
||||
- `jq`
|
||||
|
|
@ -129,7 +163,7 @@ Other X11 desktop environments may still benefit from the proxy split and Big Pi
|
|||
|
||||
- Deckless never replaces `/usr/bin/steam`.
|
||||
- The Steam runtime wrapper is only rewritten while Steam is active.
|
||||
- The original wrapper is restored after Steam exits.
|
||||
- The original wrapper is restored shortly after Steam exits, once Steam activity is gone.
|
||||
- `./uninstall.sh` restores the local launchers and desktop entries that existed before the first install.
|
||||
|
||||
## Documentation
|
||||
|
|
|
|||
BIN
assets/screenshots/big-picture-arch-i3.png
Normal file
BIN
assets/screenshots/big-picture-arch-i3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 135 KiB |
|
|
@ -1,10 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
script_path="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/deckless"
|
||||
settings_env="${DECKLESS_SETTINGS_ENV:-${config_dir}/deckless.env}"
|
||||
legacy_proxy_env="${HOME}/.config/network/proxy-env.sh"
|
||||
proxy_env="${DECKLESS_PROXY_ENV:-${config_dir}/proxy-env.sh}"
|
||||
steam_bin="${DECKLESS_STEAM_BIN:-/usr/bin/steam}"
|
||||
|
||||
if [[ -r "$settings_env" ]]; then
|
||||
|
|
@ -22,7 +23,11 @@ target_dir="${target%/*}"
|
|||
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/deckless/steam"
|
||||
session_state="${state_dir}/session.env"
|
||||
original_wrapper="${state_dir}/steamwebhelper_sniper_wrap.sh.original"
|
||||
proxy_env="${DECKLESS_PROXY_ENV:-${config_dir}/proxy-env.sh}"
|
||||
|
||||
sync_helper="${script_dir}/deckless-sync-webhelper-wrapper"
|
||||
heal_helper="${script_dir}/deckless-webhelper-heal"
|
||||
cleanup_helper="${script_dir}/deckless-webhelper-cleanup"
|
||||
cleanup_done=0
|
||||
|
||||
if [[ ! -d "$target_dir" ]]; then
|
||||
printf 'Steam runtime directory not found: %s\n' "$target_dir" >&2
|
||||
|
|
@ -34,30 +39,83 @@ if [[ ! -x "$steam_bin" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
for helper in "$sync_helper" "$heal_helper" "$cleanup_helper"; do
|
||||
if [[ ! -x "$helper" ]]; then
|
||||
printf 'Deckless helper not found: %s\n' "$helper" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "$state_dir"
|
||||
|
||||
terminate_stale_monitors() {
|
||||
steam_activity_running() {
|
||||
local args
|
||||
|
||||
while IFS= read -r args; do
|
||||
if [[ "$args" == *"${steam_root}/ubuntu12_32/steam"* ]] || \
|
||||
[[ "$args" == *"${steam_root}/ubuntu12_64/steamwebhelper"* ]] || \
|
||||
[[ "$args" == *"${target}"* ]] || \
|
||||
[[ "$args" == *"${steam_bin}"* ]]; then
|
||||
return 0
|
||||
fi
|
||||
done < <(ps -ww -eo args=)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
terminate_stale_helpers() {
|
||||
local pid
|
||||
local args
|
||||
|
||||
while IFS= read -r line; do
|
||||
pid="${line%% *}"
|
||||
args="${line#* }"
|
||||
[[ "$pid" == "$$" ]] && continue
|
||||
|
||||
while read -r pid args; do
|
||||
case "$args" in
|
||||
"$script_path"|\
|
||||
"$script_path "*|\
|
||||
"bash $script_path"|\
|
||||
"bash $script_path "*|\
|
||||
"/usr/bin/bash $script_path"|\
|
||||
"/usr/bin/bash $script_path "*)
|
||||
"$sync_helper"|\
|
||||
"$sync_helper "*|\
|
||||
"$heal_helper"|\
|
||||
"$heal_helper "*|\
|
||||
"$cleanup_helper"|\
|
||||
"$cleanup_helper "*|\
|
||||
"bash $sync_helper"|\
|
||||
"bash $sync_helper "*|\
|
||||
"bash $heal_helper"|\
|
||||
"bash $heal_helper "*|\
|
||||
"bash $cleanup_helper"|\
|
||||
"bash $cleanup_helper "*|\
|
||||
"/usr/bin/bash $sync_helper"|\
|
||||
"/usr/bin/bash $sync_helper "*|\
|
||||
"/usr/bin/bash $heal_helper"|\
|
||||
"/usr/bin/bash $heal_helper "*|\
|
||||
"/usr/bin/bash $cleanup_helper"|\
|
||||
"/usr/bin/bash $cleanup_helper "*)
|
||||
kill -TERM "$pid" 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
done < <(ps -ww -eo pid=,args=)
|
||||
}
|
||||
|
||||
start_detached_helper() {
|
||||
local helper="$1"
|
||||
|
||||
if command -v setsid >/dev/null 2>&1; then
|
||||
setsid -f "$helper" >/dev/null 2>&1 </dev/null || true
|
||||
else
|
||||
nohup "$helper" >/dev/null 2>&1 </dev/null &
|
||||
fi
|
||||
}
|
||||
|
||||
restore_official_wrapper() {
|
||||
if (( cleanup_done == 1 )); then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if grep -Fq 'deckless-managed steam webhelper wrapper' "$target" 2>/dev/null && [[ -f "$original_wrapper" ]]; then
|
||||
cp -a "$original_wrapper" "$target"
|
||||
fi
|
||||
|
||||
rm -f "$session_state" "$original_wrapper"
|
||||
cleanup_done=1
|
||||
}
|
||||
|
||||
apply_optional_locale() {
|
||||
if [[ -n "${DECKLESS_LANG:-}" ]]; then
|
||||
while IFS='=' read -r name _; do
|
||||
|
|
@ -127,206 +185,11 @@ build_chromium_bypass_list() {
|
|||
printf '%s\n' "${entries[*]}"
|
||||
}
|
||||
|
||||
restore_tracked_wrapper() {
|
||||
if [[ -f "$original_wrapper" ]]; then
|
||||
cp -a "$original_wrapper" "$target"
|
||||
fi
|
||||
}
|
||||
|
||||
restore_official_wrapper() {
|
||||
restore_tracked_wrapper
|
||||
rm -f "$session_state" "$original_wrapper"
|
||||
}
|
||||
|
||||
write_managed_wrapper() {
|
||||
local tmp_wrapper
|
||||
local session_state_q
|
||||
|
||||
tmp_wrapper="$(mktemp "${target_dir}/.steamwebhelper_sniper_wrap.sh.XXXXXX")"
|
||||
printf -v session_state_q '%q' "$session_state"
|
||||
|
||||
cat >"$tmp_wrapper" <<EOF
|
||||
#!/bin/bash
|
||||
|
||||
# deckless-managed steam webhelper wrapper
|
||||
|
||||
session_state=${session_state_q}
|
||||
|
||||
export LD_LIBRARY_PATH=.\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}
|
||||
|
||||
if [[ -z "\${XDG_RUNTIME_DIR:-}" ]]; then
|
||||
export XDG_RUNTIME_DIR="/run/user/\$(id -u)"
|
||||
fi
|
||||
|
||||
if [[ -z "\${PULSE_SERVER:-}" && -S "\${XDG_RUNTIME_DIR}/pulse/native" ]]; then
|
||||
export PULSE_SERVER="unix:\${XDG_RUNTIME_DIR}/pulse/native"
|
||||
fi
|
||||
|
||||
if [[ -z "\${GBM_BACKENDS_PATH:-}" ]]; then
|
||||
if [[ -r /run/host/usr/lib/gbm/dri_gbm.so ]]; then
|
||||
export GBM_BACKENDS_PATH='/run/host/usr/lib/gbm'
|
||||
elif [[ -r /usr/lib/gbm/dri_gbm.so ]]; then
|
||||
export GBM_BACKENDS_PATH='/usr/lib/gbm'
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "\${__EGL_VENDOR_LIBRARY_DIRS:-}" ]]; then
|
||||
if [[ -d /run/host/usr/share/glvnd/egl_vendor.d ]]; then
|
||||
export __EGL_VENDOR_LIBRARY_DIRS='/run/host/usr/share/glvnd/egl_vendor.d'
|
||||
elif [[ -d /usr/share/glvnd/egl_vendor.d ]]; then
|
||||
export __EGL_VENDOR_LIBRARY_DIRS='/usr/share/glvnd/egl_vendor.d'
|
||||
fi
|
||||
fi
|
||||
|
||||
proxy_server="\${DECKLESS_WEBHELPER_PROXY_SERVER:-}"
|
||||
proxy_bypass="\${DECKLESS_WEBHELPER_PROXY_BYPASS_LIST:-}"
|
||||
if [[ -z "\$proxy_server" && -r "\$session_state" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "\$session_state"
|
||||
proxy_server="\${DECKLESS_WEBHELPER_PROXY_SERVER:-}"
|
||||
proxy_bypass="\${DECKLESS_WEBHELPER_PROXY_BYPASS_LIST:-}"
|
||||
fi
|
||||
|
||||
extra_args=()
|
||||
forwarded_args=()
|
||||
|
||||
for arg in "\$@"; do
|
||||
case "\$arg" in
|
||||
--disable-gpu|--disable-gpu-compositing)
|
||||
continue
|
||||
;;
|
||||
*)
|
||||
forwarded_args+=( "\$arg" )
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -n "\$proxy_server" ]]; then
|
||||
extra_args+=( "--proxy-server=\${proxy_server}" )
|
||||
fi
|
||||
|
||||
if [[ -n "\$proxy_bypass" ]]; then
|
||||
extra_args+=( "--proxy-bypass-list=\${proxy_bypass}" )
|
||||
fi
|
||||
|
||||
extra_args+=( "--ignore-gpu-blocklist" "--enable-gpu-rasterization" )
|
||||
|
||||
echo "<6>exec ./steamwebhelper \${extra_args[*]} \${forwarded_args[*]}"
|
||||
echo "<remaining-lines-assume-level=7>"
|
||||
exec ./steamwebhelper "\${extra_args[@]}" "\${forwarded_args[@]}"
|
||||
EOF
|
||||
|
||||
chmod 0755 "$tmp_wrapper"
|
||||
mv -f "$tmp_wrapper" "$target"
|
||||
}
|
||||
|
||||
ensure_managed_wrapper() {
|
||||
if grep -Fq 'deckless-managed steam webhelper wrapper' "$target" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
cp -a "$target" "$original_wrapper"
|
||||
write_managed_wrapper
|
||||
}
|
||||
|
||||
restart_unproxied_webhelper() {
|
||||
local pid
|
||||
local args
|
||||
local stale=0
|
||||
local -a top_level_pids=()
|
||||
|
||||
while IFS= read -r line; do
|
||||
pid="${line%% *}"
|
||||
args="${line#* }"
|
||||
|
||||
if [[ "$args" == *'steamwebhelper -nocrashdialog'* ]]; then
|
||||
stale=0
|
||||
if [[ -n "${proxy_server:-}" && "$args" != *'--proxy-server='* ]]; then
|
||||
stale=1
|
||||
fi
|
||||
if [[ "$args" == *'--disable-gpu '* || "$args" == *'--disable-gpu-compositing'* ]]; then
|
||||
stale=1
|
||||
fi
|
||||
(( stale == 1 )) && top_level_pids+=("$pid")
|
||||
elif [[ "$args" == *"${steam_root}/ubuntu12_64/steamwebhelper"* && "$args" != *'--type='* ]]; then
|
||||
stale=0
|
||||
if [[ -n "${proxy_server:-}" && "$args" != *'--proxy-server='* ]]; then
|
||||
stale=1
|
||||
fi
|
||||
if [[ "$args" == *'--disable-gpu '* || "$args" == *'--disable-gpu-compositing'* ]]; then
|
||||
stale=1
|
||||
fi
|
||||
(( stale == 1 )) && top_level_pids+=("$pid")
|
||||
fi
|
||||
done < <(ps -ww -eo pid=,args=)
|
||||
|
||||
if (( ${#top_level_pids[@]} > 0 )); then
|
||||
kill -TERM "${top_level_pids[@]}" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
steam_activity_running() {
|
||||
local args
|
||||
while IFS= read -r args; do
|
||||
if [[ "$args" == *"${steam_root}/ubuntu12_32/steam"* ]] || \
|
||||
[[ "$args" == *"${steam_root}/ubuntu12_64/steamwebhelper"* ]] || \
|
||||
[[ "$args" == *"${target}"* ]] || \
|
||||
[[ "$args" == *"${steam_bin}"* ]]; then
|
||||
return 0
|
||||
fi
|
||||
done < <(ps -ww -eo args=)
|
||||
return 1
|
||||
}
|
||||
|
||||
steam_updater_active() {
|
||||
local args
|
||||
while IFS= read -r args; do
|
||||
if [[ "$args" == *"${steam_root}/ubuntu12_32/steam"* && "$args" == *'-child-update-ui'* ]]; then
|
||||
return 0
|
||||
fi
|
||||
done < <(ps -ww -eo args=)
|
||||
return 1
|
||||
}
|
||||
|
||||
start_wrapper_monitor() {
|
||||
(
|
||||
local seen_steam=0
|
||||
local idle_checks=0
|
||||
local startup_checks=0
|
||||
|
||||
while (( idle_checks < 60 )); do
|
||||
if steam_activity_running; then
|
||||
seen_steam=1
|
||||
idle_checks=0
|
||||
startup_checks=0
|
||||
if steam_updater_active; then
|
||||
if grep -Fq 'deckless-managed steam webhelper wrapper' "$target" 2>/dev/null; then
|
||||
restore_tracked_wrapper
|
||||
elif [[ ! -f "$original_wrapper" ]]; then
|
||||
cp -a "$target" "$original_wrapper"
|
||||
fi
|
||||
else
|
||||
ensure_managed_wrapper
|
||||
restart_unproxied_webhelper
|
||||
fi
|
||||
else
|
||||
if (( seen_steam == 1 )); then
|
||||
(( idle_checks += 1 ))
|
||||
else
|
||||
(( startup_checks += 1 ))
|
||||
if (( startup_checks >= 60 )); then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if ! steam_activity_running; then
|
||||
terminate_stale_helpers
|
||||
restore_official_wrapper
|
||||
) >/dev/null 2>&1 &
|
||||
}
|
||||
fi
|
||||
|
||||
terminate_stale_monitors
|
||||
apply_optional_locale
|
||||
|
||||
if [[ -z "${SDL_VIDEODRIVER:-}" ]]; then
|
||||
|
|
@ -364,12 +227,6 @@ if [[ -n "$bypass_candidate" ]]; then
|
|||
proxy_bypass="$(build_chromium_bypass_list "$bypass_candidate")"
|
||||
fi
|
||||
|
||||
if grep -Fq 'deckless-managed steam webhelper wrapper' "$target" 2>/dev/null && [[ -f "$original_wrapper" ]]; then
|
||||
cp -a "$original_wrapper" "$target"
|
||||
fi
|
||||
|
||||
cp -a "$target" "$original_wrapper"
|
||||
|
||||
tmp_state="$(mktemp "${state_dir}/.session.env.XXXXXX")"
|
||||
trap 'rm -f "${tmp_state:-}"' EXIT
|
||||
{
|
||||
|
|
@ -381,11 +238,22 @@ chmod 0600 "$tmp_state"
|
|||
mv -f "$tmp_state" "$session_state"
|
||||
trap - EXIT
|
||||
|
||||
start_wrapper_monitor
|
||||
|
||||
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
|
||||
unset ALL_PROXY all_proxy
|
||||
unset no_proxy NO_PROXY
|
||||
unset DECKLESS_WEBHELPER_PROXY_SERVER DECKLESS_WEBHELPER_PROXY_BYPASS_LIST
|
||||
|
||||
exec "$steam_bin" "$@"
|
||||
trap 'restore_official_wrapper' EXIT HUP INT TERM
|
||||
|
||||
start_detached_helper "$heal_helper"
|
||||
start_detached_helper "$cleanup_helper"
|
||||
|
||||
set +e
|
||||
"$steam_bin" "$@"
|
||||
steam_status=$?
|
||||
set -e
|
||||
|
||||
restore_official_wrapper
|
||||
trap - EXIT HUP INT TERM
|
||||
|
||||
exit "$steam_status"
|
||||
|
|
|
|||
104
bin/deckless-sync-webhelper-wrapper
Executable file
104
bin/deckless-sync-webhelper-wrapper
Executable file
|
|
@ -0,0 +1,104 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
steam_root="$(readlink -e "$HOME/.steam/steam" || true)"
|
||||
if [[ -z "$steam_root" ]]; then
|
||||
steam_root="${XDG_DATA_HOME:-$HOME/.local/share}/Steam"
|
||||
fi
|
||||
|
||||
target="${steam_root}/ubuntu12_64/steamwebhelper_sniper_wrap.sh"
|
||||
target_dir="${target%/*}"
|
||||
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/deckless/steam"
|
||||
session_state="${state_dir}/session.env"
|
||||
original_wrapper="${state_dir}/steamwebhelper_sniper_wrap.sh.original"
|
||||
|
||||
[[ -d "$target_dir" ]] || exit 0
|
||||
[[ -f "$target" ]] || exit 0
|
||||
[[ -f "$session_state" ]] || exit 0
|
||||
|
||||
mkdir -p "$state_dir"
|
||||
|
||||
if ! grep -Fq 'deckless-managed steam webhelper wrapper' "$target" 2>/dev/null && [[ ! -f "$original_wrapper" ]]; then
|
||||
cp -a "$target" "$original_wrapper"
|
||||
fi
|
||||
|
||||
tmp_wrapper="$(mktemp "${target_dir}/.steamwebhelper_sniper_wrap.sh.XXXXXX")"
|
||||
trap 'rm -f "$tmp_wrapper"' EXIT
|
||||
|
||||
printf -v session_state_q '%q' "$session_state"
|
||||
|
||||
cat >"$tmp_wrapper" <<EOF
|
||||
#!/bin/bash
|
||||
|
||||
# deckless-managed steam webhelper wrapper
|
||||
|
||||
session_state=${session_state_q}
|
||||
|
||||
export LD_LIBRARY_PATH=.\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}
|
||||
|
||||
if [[ -z "\${XDG_RUNTIME_DIR:-}" ]]; then
|
||||
export XDG_RUNTIME_DIR="/run/user/\$(id -u)"
|
||||
fi
|
||||
|
||||
if [[ -z "\${PULSE_SERVER:-}" && -S "\${XDG_RUNTIME_DIR}/pulse/native" ]]; then
|
||||
export PULSE_SERVER="unix:\${XDG_RUNTIME_DIR}/pulse/native"
|
||||
fi
|
||||
|
||||
if [[ -z "\${GBM_BACKENDS_PATH:-}" ]]; then
|
||||
if [[ -r /run/host/usr/lib/gbm/dri_gbm.so ]]; then
|
||||
export GBM_BACKENDS_PATH='/run/host/usr/lib/gbm'
|
||||
elif [[ -r /usr/lib/gbm/dri_gbm.so ]]; then
|
||||
export GBM_BACKENDS_PATH='/usr/lib/gbm'
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "\${__EGL_VENDOR_LIBRARY_DIRS:-}" ]]; then
|
||||
if [[ -d /run/host/usr/share/glvnd/egl_vendor.d ]]; then
|
||||
export __EGL_VENDOR_LIBRARY_DIRS='/run/host/usr/share/glvnd/egl_vendor.d'
|
||||
elif [[ -d /usr/share/glvnd/egl_vendor.d ]]; then
|
||||
export __EGL_VENDOR_LIBRARY_DIRS='/usr/share/glvnd/egl_vendor.d'
|
||||
fi
|
||||
fi
|
||||
|
||||
proxy_server="\${DECKLESS_WEBHELPER_PROXY_SERVER:-}"
|
||||
proxy_bypass="\${DECKLESS_WEBHELPER_PROXY_BYPASS_LIST:-}"
|
||||
if [[ -z "\$proxy_server" && -r "\$session_state" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "\$session_state"
|
||||
proxy_server="\${DECKLESS_WEBHELPER_PROXY_SERVER:-}"
|
||||
proxy_bypass="\${DECKLESS_WEBHELPER_PROXY_BYPASS_LIST:-}"
|
||||
fi
|
||||
|
||||
extra_args=()
|
||||
forwarded_args=()
|
||||
|
||||
for arg in "\$@"; do
|
||||
case "\$arg" in
|
||||
--disable-gpu|--disable-gpu-compositing)
|
||||
continue
|
||||
;;
|
||||
*)
|
||||
forwarded_args+=( "\$arg" )
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -n "\$proxy_server" ]]; then
|
||||
extra_args+=( "--proxy-server=\${proxy_server}" )
|
||||
fi
|
||||
|
||||
if [[ -n "\$proxy_bypass" ]]; then
|
||||
extra_args+=( "--proxy-bypass-list=\${proxy_bypass}" )
|
||||
fi
|
||||
|
||||
extra_args+=( "--ignore-gpu-blocklist" "--enable-gpu-rasterization" )
|
||||
|
||||
echo "<6>exec ./steamwebhelper \${extra_args[*]} \${forwarded_args[*]}"
|
||||
echo "<remaining-lines-assume-level=7>"
|
||||
exec ./steamwebhelper "\${extra_args[@]}" "\${forwarded_args[@]}"
|
||||
EOF
|
||||
|
||||
if [[ ! -f "$target" ]] || ! cmp -s "$tmp_wrapper" "$target"; then
|
||||
chmod 0755 "$tmp_wrapper"
|
||||
mv -f "$tmp_wrapper" "$target"
|
||||
fi
|
||||
63
bin/deckless-webhelper-cleanup
Executable file
63
bin/deckless-webhelper-cleanup
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
steam_root="$(readlink -e "$HOME/.steam/steam" || true)"
|
||||
if [[ -z "$steam_root" ]]; then
|
||||
steam_root="${XDG_DATA_HOME:-$HOME/.local/share}/Steam"
|
||||
fi
|
||||
|
||||
wrapper_target="${steam_root}/ubuntu12_64/steamwebhelper_sniper_wrap.sh"
|
||||
webhelper_binary="${steam_root}/ubuntu12_64/steamwebhelper"
|
||||
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/deckless/steam"
|
||||
session_state="${state_dir}/session.env"
|
||||
original_wrapper="${state_dir}/steamwebhelper_sniper_wrap.sh.original"
|
||||
|
||||
[[ -f "$session_state" ]] || exit 0
|
||||
|
||||
steam_activity_running() {
|
||||
local args
|
||||
|
||||
while IFS= read -r args; do
|
||||
if [[ "$args" == *"${steam_root}/ubuntu12_32/steam"* ]] || \
|
||||
[[ "$args" == *"${webhelper_binary}"* ]] || \
|
||||
[[ "$args" == *"${wrapper_target}"* ]] || \
|
||||
[[ "$args" == *'/usr/bin/steam'* ]]; then
|
||||
return 0
|
||||
fi
|
||||
done < <(ps -ww -eo args=)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
restore_wrapper() {
|
||||
if grep -Fq 'deckless-managed steam webhelper wrapper' "$wrapper_target" 2>/dev/null && [[ -f "$original_wrapper" ]]; then
|
||||
cp -a "$original_wrapper" "$wrapper_target"
|
||||
fi
|
||||
|
||||
rm -f "$session_state" "$original_wrapper"
|
||||
}
|
||||
|
||||
seen_steam=0
|
||||
idle_checks=0
|
||||
startup_checks=0
|
||||
|
||||
while (( idle_checks < 30 )); do
|
||||
if steam_activity_running; then
|
||||
seen_steam=1
|
||||
idle_checks=0
|
||||
startup_checks=0
|
||||
else
|
||||
if (( seen_steam == 1 )); then
|
||||
(( idle_checks += 1 ))
|
||||
else
|
||||
(( startup_checks += 1 ))
|
||||
if (( startup_checks >= 30 )); then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
done
|
||||
|
||||
restore_wrapper
|
||||
85
bin/deckless-webhelper-heal
Executable file
85
bin/deckless-webhelper-heal
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
steam_root="$(readlink -e "$HOME/.steam/steam" || true)"
|
||||
if [[ -z "$steam_root" ]]; then
|
||||
steam_root="${XDG_DATA_HOME:-$HOME/.local/share}/Steam"
|
||||
fi
|
||||
|
||||
wrapper_target="${steam_root}/ubuntu12_64/steamwebhelper_sniper_wrap.sh"
|
||||
webhelper_binary="${steam_root}/ubuntu12_64/steamwebhelper"
|
||||
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/deckless/steam"
|
||||
session_state="${state_dir}/session.env"
|
||||
sync_helper="${script_dir}/deckless-sync-webhelper-wrapper"
|
||||
|
||||
[[ -f "$session_state" ]] || exit 0
|
||||
[[ -x "$sync_helper" ]] || exit 0
|
||||
|
||||
proxy_server=''
|
||||
if [[ -r "$session_state" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "$session_state"
|
||||
proxy_server="${DECKLESS_WEBHELPER_PROXY_SERVER:-}"
|
||||
fi
|
||||
|
||||
webhelper_args_healthy() {
|
||||
local args="$1"
|
||||
|
||||
if [[ -n "$proxy_server" && "$args" != *'--proxy-server='* ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$args" == *'--disable-gpu '* || "$args" == *'--disable-gpu-compositing'* ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
attempt=0
|
||||
while (( attempt < 180 )); do
|
||||
top_level_pids=()
|
||||
wrap_pids=()
|
||||
healthy=0
|
||||
|
||||
while read -r pid args; do
|
||||
if [[ "$args" == *"${wrapper_target}"* ]]; then
|
||||
wrap_pids+=("$pid")
|
||||
fi
|
||||
|
||||
if [[ "$args" == *'steamwebhelper -nocrashdialog'* ]]; then
|
||||
if webhelper_args_healthy "$args"; then
|
||||
healthy=1
|
||||
break
|
||||
fi
|
||||
top_level_pids+=("$pid")
|
||||
elif [[ "$args" == *"${webhelper_binary}"* && "$args" != *'--type='* ]]; then
|
||||
if webhelper_args_healthy "$args"; then
|
||||
healthy=1
|
||||
break
|
||||
fi
|
||||
top_level_pids+=("$pid")
|
||||
fi
|
||||
done < <(ps -ww -eo pid=,args=)
|
||||
|
||||
(( healthy == 1 )) && exit 0
|
||||
|
||||
if (( ${#wrap_pids[@]} > 0 || ${#top_level_pids[@]} > 0 )); then
|
||||
"$sync_helper" || exit 0
|
||||
|
||||
if (( ${#wrap_pids[@]} > 0 )); then
|
||||
kill -TERM "${wrap_pids[@]}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if (( ${#top_level_pids[@]} > 0 )); then
|
||||
kill -TERM "${top_level_pids[@]}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
(( attempt += 1 ))
|
||||
done
|
||||
|
|
@ -15,9 +15,22 @@ This is the main launcher.
|
|||
- resolves the active Steam runtime path
|
||||
- sources optional Deckless config
|
||||
- loads proxy settings
|
||||
- writes a session-only replacement for `steamwebhelper_sniper_wrap.sh`
|
||||
- writes session state for helper processes
|
||||
- starts a healer that waits for the first Steam webhelper hop and then patches `steamwebhelper_sniper_wrap.sh`
|
||||
- clears proxy environment variables before starting the official Steam client
|
||||
- restores the original wrapper after Steam exits
|
||||
- starts a detached cleanup worker that restores the original wrapper after Steam exits
|
||||
|
||||
### `deckless-sync-webhelper-wrapper`
|
||||
|
||||
This helper writes the managed `steamwebhelper_sniper_wrap.sh` replacement for the current session and captures the official upstream wrapper before replacing it.
|
||||
|
||||
### `deckless-webhelper-heal`
|
||||
|
||||
This helper waits for the first top-level `steamwebhelper` launch, checks whether proxy and GPU policy landed, and re-launches it once through the managed wrapper when needed.
|
||||
|
||||
### `deckless-webhelper-cleanup`
|
||||
|
||||
This helper runs detached from the launcher session, waits until Steam activity is gone, restores the official wrapper, and removes the session backup.
|
||||
|
||||
### `deckless-bigpicture`
|
||||
|
||||
|
|
@ -29,7 +42,7 @@ This bridge listens to i3 window events and hands fullscreen from Big Picture to
|
|||
|
||||
## Why patch the Steam webhelper wrapper at runtime
|
||||
|
||||
Steam itself already uses `steamwebhelper_sniper_wrap.sh` as the last hop before `steamwebhelper`. Replacing that wrapper only while Steam is running gives Deckless a narrow control point for:
|
||||
Steam itself already uses `steamwebhelper_sniper_wrap.sh` as the last hop before `steamwebhelper`. Replacing that wrapper only after Steam reaches the webhelper launch phase gives Deckless a narrow control point for:
|
||||
|
||||
- webhelper-only proxy flags
|
||||
- removing forced `--disable-gpu` arguments
|
||||
|
|
|
|||
|
|
@ -124,6 +124,9 @@ backup_once "${autostart_dir}/deckless-i3-bigpicture-bridge.desktop" "autostart/
|
|||
install -Dm755 "${repo_root}/bin/deckless-steam" "${install_root}/bin/deckless-steam"
|
||||
install -Dm755 "${repo_root}/bin/deckless-bigpicture" "${install_root}/bin/deckless-bigpicture"
|
||||
install -Dm755 "${repo_root}/bin/deckless-i3-bigpicture-bridge" "${install_root}/bin/deckless-i3-bigpicture-bridge"
|
||||
install -Dm755 "${repo_root}/bin/deckless-sync-webhelper-wrapper" "${install_root}/bin/deckless-sync-webhelper-wrapper"
|
||||
install -Dm755 "${repo_root}/bin/deckless-webhelper-heal" "${install_root}/bin/deckless-webhelper-heal"
|
||||
install -Dm755 "${repo_root}/bin/deckless-webhelper-cleanup" "${install_root}/bin/deckless-webhelper-cleanup"
|
||||
install -Dm644 "${repo_root}/config/proxy-env.example.sh" "${config_dir}/proxy-env.example.sh"
|
||||
install -Dm644 "${repo_root}/config/deckless.env.example" "${config_dir}/deckless.env.example"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue