feat: initial commit for gamesir-keepalive

This commit is contained in:
Murilo Scolari 2026-10-10 18:47:24 -03:00
commit b4776f5bdd
7 changed files with 231 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
__pycache__/
*.py[cod]
*$py.class
.DS_Store

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Murilo Scolari
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

77
README.md Normal file
View file

@ -0,0 +1,77 @@
# gamesir-keepalive
Fix para o problema de desconexão/reconexão contínua em loop de controles **GameSir** (como *Tegenaria Lite*, *Nova Lite* e modelos similares com ID USB `3537:1093`) no Linux.
---
## 📌 O Problema
Em algumas distribuições Linux, controles GameSir entram em um ciclo de desconexão e reconexão USB a cada ~3 segundos:
```text
usb 1-2: USB disconnect, device number X
usb 1-2: new full-speed USB device number Y
```
Isso ocorre porque o firmware do controle espera que algum processo do sistema mantenha o arquivo de eventos (`/dev/input/js*` ou `/dev/input/by-id/*joystick`) aberto para leitura contínua. Caso contrário, ele sofre um timeout interno e reinicia o barramento USB.
Muitos usuários notam que abrir a **Steam** "resolve" temporariamente o problema — isso acontece porque o subsistema *Steam Input* abre o joystick em segundo plano para escutar comandos.
---
## 🚀 Como este projeto resolve
Este projeto fornece um serviço ultraleve para o `systemd` que:
* Detecta automaticamente a conexão de controles GameSir via `/dev/input/by-id/`.
* Abre o descritor de leitura de eventos em modo bloqueante (consumindo **0% de CPU** e menos de **5MB de RAM**).
* Mantém o sinal ativo (heartbeat), impedindo a desconexão sem precisar manter a Steam aberta.
* Reconecta silenciosamente caso o controle seja desconectado e conectado fisicamente.
* Não interfere em jogos nem no Steam Input (o Linux permite múltiplos leitores simultâneos do dispositivo de entrada).
---
## 📥 Instalação
Clone o repositório e execute o script instalador:
```bash
git clone https://github.com/muriloscolari/gamesir-keepalive.git
cd gamesir-keepalive
./install.sh
```
O `install.sh` faz tudo automaticamente:
1. Copia o script para `~/.local/bin/gamesir-keepalive.py`.
2. Instala e ativa a unidade de usuário do systemd em `~/.config/systemd/user/gamesir-keepalive.service`.
3. Inicia o serviço imediatamente e o configura para inicializar com o seu login.
---
## 🔍 Verificar status
Para checar se o serviço está rodando:
```bash
systemctl --user status gamesir-keepalive.service
```
Para ver os logs do serviço:
```bash
journalctl --user -u gamesir-keepalive.service -f
```
---
## 🗑️ Desinstalação
Se desejar remover:
```bash
cd gamesir-keepalive
./uninstall.sh
```
---
## 📄 Licença
MIT License.

51
gamesir-keepalive.py Normal file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""
GameSir Controller Keepalive Service
Mantém o dispositivo de entrada do GameSir aberto para leitura contínua,
evitando que o firmware do controle entre no ciclo de timeout e reinicie a porta USB.
"""
import glob
import os
import sys
import time
def find_gamesir_device():
# Procura por dispositivos joystick da GameSir em /dev/input/by-id/
patterns = [
"/dev/input/by-id/*GameSir*joystick*",
"/dev/input/by-id/*3537*joystick*",
]
for pattern in patterns:
matches = glob.glob(pattern)
if matches:
return matches[0]
return None
def main():
print("Iniciando GameSir Keepalive Service...", flush=True)
while True:
device_path = find_gamesir_device()
if not device_path or not os.path.exists(device_path):
time.sleep(1)
continue
try:
print(f"Dispositivo GameSir encontrado em {device_path}. Abrindo descritor...", flush=True)
with open(device_path, "rb") as dev:
print("Conexão mantida ativa com sucesso.", flush=True)
while True:
# Leitura bloqueante de eventos; consome 0% de CPU
data = dev.read(64)
if not data:
break
except (OSError, IOError) as e:
# Ocorre quando o controle é desconectado fisicamente
print(f"Dispositivo desconectado ou indisponível: {e}. Aguardando reconexão...", flush=True)
time.sleep(1)
except Exception as e:
print(f"Erro inesperado: {e}", file=sys.stderr, flush=True)
time.sleep(1)
if __name__ == "__main__":
main()

12
gamesir-keepalive.service Normal file
View file

@ -0,0 +1,12 @@
[Unit]
Description=GameSir Controller Keepalive Service
After=default.target
[Service]
Type=simple
ExecStart=%h/.local/bin/gamesir-keepalive.py
Restart=always
RestartSec=2
[Install]
WantedBy=default.target

46
install.sh Executable file
View file

@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -e
# Cores para output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # Sem cor
echo -e "${BLUE}==========================================${NC}"
echo -e "${BLUE} GameSir Controller Keepalive Installer ${NC}"
echo -e "${BLUE}==========================================${NC}"
# Verificar se python3 está disponível
if ! command -v python3 &> /dev/null; then
echo -e "${RED}[ERRO] Python 3 não foi encontrado. Por favor, instale o python3 primeiro.${NC}"
exit 1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BIN_DIR="$HOME/.local/bin"
SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
echo -e "${YELLOW}[*] Criando diretórios necessários...${NC}"
mkdir -p "$BIN_DIR"
mkdir -p "$SYSTEMD_USER_DIR"
echo -e "${YELLOW}[*] Instalando script executável em $BIN_DIR...${NC}"
cp "$SCRIPT_DIR/gamesir-keepalive.py" "$BIN_DIR/gamesir-keepalive.py"
chmod +x "$BIN_DIR/gamesir-keepalive.py"
echo -e "${YELLOW}[*] Instalando arquivo de serviço do systemd em $SYSTEMD_USER_DIR...${NC}"
cp "$SCRIPT_DIR/gamesir-keepalive.service" "$SYSTEMD_USER_DIR/gamesir-keepalive.service"
echo -e "${YELLOW}[*] Recarregando daemon do systemd de usuário...${NC}"
systemctl --user daemon-reload
echo -e "${YELLOW}[*] Ativando e iniciando o serviço...${NC}"
systemctl --user enable --now gamesir-keepalive.service
echo -e "${GREEN}==========================================${NC}"
echo -e "${GREEN} Instalação concluída com sucesso! ${NC}"
echo -e "${GREEN}==========================================${NC}"
echo ""
systemctl --user status gamesir-keepalive.service --no-pager || true

20
uninstall.sh Executable file
View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -e
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
echo -e "${YELLOW}[*] Parando e desabilitando gamesir-keepalive.service...${NC}"
systemctl --user stop gamesir-keepalive.service 2>/dev/null || true
systemctl --user disable gamesir-keepalive.service 2>/dev/null || true
echo -e "${YELLOW}[*] Removendo arquivos instalados...${NC}"
rm -f "$HOME/.local/bin/gamesir-keepalive.py"
rm -f "$HOME/.config/systemd/user/gamesir-keepalive.service"
echo -e "${YELLOW}[*] Recarregando daemon do systemd...${NC}"
systemctl --user daemon-reload
echo -e "${GREEN}[✓] Desinstalação concluída com sucesso.${NC}"