feat: initial commit for gamesir-keepalive
This commit is contained in:
commit
b4776f5bdd
7 changed files with 231 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.DS_Store
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal 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
77
README.md
Normal 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
51
gamesir-keepalive.py
Normal 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
12
gamesir-keepalive.service
Normal 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
46
install.sh
Executable 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
20
uninstall.sh
Executable 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}"
|
||||
Loading…
Reference in a new issue