Skip to content

Commit 6b5571c

Browse files
committed
Initial commit
0 parents  commit 6b5571c

8 files changed

Lines changed: 695 additions & 0 deletions

File tree

.gitattributes

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Set the default behavior, in case people don't have core.autocrlf set.
2+
* text=auto
3+
4+
# Explicitly declare text files you want to always be normalized and converted
5+
# to native line endings on checkout.
6+
*.rs text eol=lf
7+
*.sh text eol=lf
8+
*.toml text eol=lf
9+
*.md text eol=lf
10+
.gitignore text eol=lf
11+
.gitattributes text eol=lf

README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Legion Go S - UI Reset Service
2+
3+
A lightweight, passive system service designed for Legion Go S running SteamOS/Linux.
4+
It allows you to force-restart the graphical interface (Gamescope/SDDM) when it freezes by holding both volume buttons.
5+
6+
> [!CAUTION]
7+
> ## Disclaimer
8+
> This software is provided "as is", without warranty of any kind. Use at your own risk. This tool interacts with low-level input devices and system services.
9+
10+
## 📂 Project Structure
11+
12+
This repository contains two implementations of the service:
13+
14+
| Version | Path | Status | Memory Usage | Description |
15+
| :--- | :--- | :--- | :--- | :--- |
16+
| **Rust** | `/rust_version` | **Recommended** | **~140 KB** | Ultra-lightweight, zero CPU usage, hardened systemd service. |
17+
| **Python** | `/python_version` | Legacy | ~20 MB | Original prototype using Python `evdev`. |
18+
19+
---
20+
21+
## 🚀 Rust Version (Recommended)
22+
23+
### Features
24+
* **Zero-Overhead:** Consumes less than 1MB of RAM and 0% CPU when idle.
25+
* **Safe Passive Mode:** Does not block other applications from using volume buttons.
26+
* **Systemd Hardening:** Runs in a restricted sandbox for security.
27+
* **Smart Management:** Single script for installation, updates, and uninstallation.
28+
29+
### Installation
30+
31+
## ⚡ Quick Install (One-Liner)
32+
33+
You don't need to clone the repository manually. Just open the terminal (Konsole) and paste this single command:
34+
35+
```bash
36+
mkdir -p /tmp/legion_install && cd /tmp/legion_install && wget -O legion_go_reset https://github.com/Veizeczek/LegionGoS-UI-Reset/releases/download/v1.0/legion_go_reset && wget -O install.sh https://raw.githubusercontent.com/Veizeczek/LegionGoS-UI-Reset/main/rust_version/install.sh && chmod +x install.sh && sudo ./install.sh && cd ~ && rm -rf /tmp/legion_install
37+
```
38+
(This command downloads the binary release and the installer script to a temporary folder, installs the service, and cleans up afterwards.)
39+
40+
## Uninstall
41+
To completely remove the service and configuration:
42+
```bash
43+
curl -sL https://raw.githubusercontent.com/Veizeczek/LegionGoS-UI-Reset/main/rust_version/install.sh | sudo bash -s uninstall
44+
```
45+
46+
## 🛠️ Manual
47+
48+
**Prerequisites:** You need `cargo` (Rust compiler) installed to build the binary.
49+
50+
1. **Clone the repository:**
51+
```bash
52+
git clone [https://github.com/Veizeczek/LegionGoS-UI-Reset.git](https://github.com/Veizeczek/LegionGoS-UI-Reset.git)
53+
cd LegionGoS-UI-Reset/rust_version
54+
```
55+
56+
2. **Build the binary:**
57+
```bash
58+
cargo build --release --target x86_64-unknown-linux-musl
59+
```
60+
*(Note: If building directly on SteamOS, standard `cargo build --release` is usually sufficient).*
61+
62+
3. **Install the service:**
63+
```bash
64+
chmod +x install.sh
65+
sudo ./install.sh
66+
```
67+
68+
### Uninstallation
69+
To remove the service and clean up all files:
70+
```bash
71+
cd rust_version
72+
sudo ./install.sh uninstall
73+
```
74+
## Usage
75+
76+
1. Press and hold **Volume Up (+)**.
77+
2. While holding Vol+, press and hold **Volume Down (-)**.
78+
3. Keep **BOTH** buttons held together for **2 seconds**.
79+
4. The screen will go black momentarily as the UI session (SDDM) restarts.
80+
81+
*Note: The order of pressing buttons does not matter, as long as both are held down simultaneously for the required duration.*
82+
83+
---
84+
85+
## Python Version (Legacy)
86+
87+
If you prefer the Python implementation or cannot compile Rust, navigate to the `/python_version` directory.
88+
89+
```bash
90+
cd python_version
91+
# Ensure you have 'evdev' installed via pip or pacman
92+
sudo python3 steam-vol-reset.py
93+
```
94+
(Note: The Python version is kept for archival purposes and is no longer actively developed.)

python_version/install.sh

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/bin/bash
2+
3+
# --- LEGION GO S UI RESET ---
4+
SCRIPT_PATH="/usr/local/bin/steam-vol-reset.py"
5+
SERVICE_NAME="steam-vol-reset.service"
6+
# Specific device name for Legion Go S
7+
TARGET_DEVICE="AT Translated Set 2 keyboard"
8+
9+
# Root check
10+
if [ "$EUID" -ne 0 ]; then
11+
echo "ERROR: Please run with sudo!"
12+
exit 1
13+
fi
14+
15+
echo ">>> [1/5] Installing dependencies..."
16+
# Unlocking filesystem specifically for installation
17+
steamos-readonly disable 2>/dev/null
18+
19+
pacman -S --needed --noconfirm python-evdev
20+
21+
echo ">>> [2/5] Generating Python script..."
22+
cat << EOF > $SCRIPT_PATH
23+
#!/usr/bin/env python3
24+
import evdev
25+
from evdev import InputDevice, ecodes, list_devices
26+
import time
27+
import subprocess
28+
import sys
29+
import os
30+
31+
# Configuration
32+
TARGET = "$TARGET_DEVICE"
33+
HOLD_TIME = 2.0 # Seconds to hold
34+
35+
def find_device():
36+
try:
37+
devices = [InputDevice(path) for path in list_devices()]
38+
for dev in devices:
39+
if TARGET in dev.name:
40+
return dev
41+
except Exception:
42+
pass
43+
return None
44+
45+
def restart_ui():
46+
print("!!! RESTARTING UI !!!")
47+
# SECURITY FIX: Avoid shell=True. Use list format.
48+
try:
49+
subprocess.run(["systemctl", "restart", "sddm"], check=False)
50+
except Exception as e:
51+
print(f"Error executing restart: {e}")
52+
53+
# Cooldown to prevent loop restarts
54+
time.sleep(10)
55+
56+
def monitor_loop():
57+
dev = find_device()
58+
if not dev:
59+
print(f"Waiting for device: {TARGET}...")
60+
return
61+
62+
print(f"Connected to: {dev.name}")
63+
print(f"Ready. Hold Vol+ and Vol- for {HOLD_TIME}s to restart UI.")
64+
65+
# 115 = Vol Up, 114 = Vol Down
66+
vol_up = False
67+
vol_down = False
68+
combo_start_time = 0
69+
70+
try:
71+
# Passive, blocking read_loop (Efficient C implementation)
72+
for event in dev.read_loop():
73+
if event.type == ecodes.EV_KEY:
74+
# Update key states (1=Down, 2=Hold, 0=Up)
75+
if event.code == ecodes.KEY_VOLUMEUP:
76+
vol_up = (event.value > 0)
77+
elif event.code == ecodes.KEY_VOLUMEDOWN:
78+
vol_down = (event.value > 0)
79+
80+
# Logic Check
81+
if vol_up and vol_down:
82+
if combo_start_time == 0:
83+
combo_start_time = time.time()
84+
print("-> Combo detected... holding...")
85+
else:
86+
# Check duration
87+
if (time.time() - combo_start_time) >= HOLD_TIME:
88+
restart_ui()
89+
# Reset states after restart
90+
combo_start_time = 0
91+
vol_up = False
92+
vol_down = False
93+
else:
94+
# Reset timer if any button is released
95+
combo_start_time = 0
96+
97+
except Exception as e:
98+
print(f"Device connection lost or error: {e}")
99+
100+
if __name__ == "__main__":
101+
# Main Keep-Alive Loop
102+
while True:
103+
try:
104+
monitor_loop()
105+
except KeyboardInterrupt:
106+
sys.exit(0)
107+
except Exception as e:
108+
print(f"Critical Loop Error: {e}")
109+
110+
# Wait before trying to reconnect device
111+
time.sleep(3)
112+
EOF
113+
114+
echo ">>> [3/5] Setting permissions..."
115+
chmod +x $SCRIPT_PATH
116+
117+
echo ">>> [4/5] Configuring systemd service..."
118+
cat <<EOF > /etc/systemd/system/$SERVICE_NAME
119+
[Unit]
120+
Description=Legion Go UI Reset Service
121+
# Wait for graphics to be ready, as this service manages UI
122+
After=graphical.target
123+
124+
[Service]
125+
Type=simple
126+
ExecStart=$SCRIPT_PATH
127+
Restart=always
128+
RestartSec=5
129+
User=root
130+
131+
# --- SECURITY HARDENING ---
132+
# Service cannot acquire new privileges
133+
NoNewPrivileges=true
134+
# Protect system directories
135+
ProtectSystem=full
136+
# Private /tmp directory
137+
PrivateTmp=true
138+
139+
[Install]
140+
# Start with graphical interface
141+
WantedBy=graphical.target
142+
EOF
143+
144+
echo ">>> [5/5] Starting service..."
145+
systemctl daemon-reload
146+
systemctl enable $SERVICE_NAME
147+
systemctl restart $SERVICE_NAME
148+
149+
steamos-readonly enable
150+
151+
echo ">>> DONE. Service running for: $TARGET_DEVICE"

python_version/uninstall.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/bash
2+
3+
SERVICE_NAME="steam-vol-reset.service"
4+
SCRIPT_PATH="/usr/local/bin/steam-vol-reset.py"
5+
6+
if [ "$EUID" -ne 0 ]; then
7+
echo "Please run with sudo (root privileges)."
8+
exit 1
9+
fi
10+
11+
echo ">>> Stopping service..."
12+
systemctl stop $SERVICE_NAME
13+
systemctl disable $SERVICE_NAME 2>/dev/null
14+
15+
echo ">>> Removing system files..."
16+
rm -f /etc/systemd/system/$SERVICE_NAME
17+
rm -f $SCRIPT_PATH
18+
19+
echo ">>> Reloading systemd configuration..."
20+
systemctl daemon-reload
21+
22+
echo ">>> Removing installation folder..."
23+
rm -rf "$PWD"
24+
25+
echo ">>> DONE. Service has been completely uninstalled."

0 commit comments

Comments
 (0)