Logitech G Hub alternative for Linux — Rust-Port von Ghub4Linux
Find a file
Hermes 642f578286 feat(gui): GTK4/libadwaita-Fenster als optionales Feature
- Adw.ApplicationWindow mit Geraeteliste links, Panels rechts (Info, Batterie,
  DPI-Level, Lighting) wie gui/main_window.py
- gui ist ein Feature: CLI und Daemon bauen ohne GTK-Toolchain
- Fenster unter Xvfb verifiziert: 1000x700, Titel ghub4linux
- README nennt die noch fehlenden Editoren als offen, nicht als erledigt
2026-11-05 16:36:50 +01:00
data feat: Rust-Port von Ghub4Linux — Kern, Treiber und CLI 2026-11-05 16:33:37 +01:00
src feat(gui): GTK4/libadwaita-Fenster als optionales Feature 2026-11-05 16:36:50 +01:00
tests feat: Rust-Port von Ghub4Linux — Kern, Treiber und CLI 2026-11-05 16:33:37 +01:00
.gitignore feat: Rust-Port von Ghub4Linux — Kern, Treiber und CLI 2026-11-05 16:33:37 +01:00
Cargo.lock feat(gui): GTK4/libadwaita-Fenster als optionales Feature 2026-11-05 16:36:50 +01:00
Cargo.toml feat(gui): GTK4/libadwaita-Fenster als optionales Feature 2026-11-05 16:36:50 +01:00
README.md feat(gui): GTK4/libadwaita-Fenster als optionales Feature 2026-11-05 16:36:50 +01:00

ghub4linux-rs

Rust-Port von Ghub4Linux: Konfiguration, HID++-2.0-Kommunikation, Treiber für die unterstützten Logitech-Geräte, CLI und (im Aufbau) die GTK4-Oberfläche.

Bauen

cargo build --release                    # nur CLI + Daemon, kein GTK nötig
cargo build --release --features gui     # zusätzlich das Fenster (gtk4, libadwaita)
cargo test                               # 50 Tests, keine Hardware, kein X-Server

Das GUI ist ein Feature, weil der Daemon auf einem Server ohne GTK laufen soll — sonst zöge jeder CLI-Build eine komplette GTK-Toolchain mit.

CLI

Gleiche Kommandos, gleiche Optionen, gleiche Ausgabe wie ghub4linux-cli:

ghub4linux-cli list
ghub4linux-cli --json list
ghub4linux-cli info 046d:407f:mock123
ghub4linux-cli dpi 046d:407f:mock123 --level 0 --dpi 1600
ghub4linux-cli lighting 046d:407f:mock123 --on --effect breathing
ghub4linux-cli profile list 046d:407f:mock123
ghub4linux-cli profile copy-to-device src dst Default -n Laptop
ghub4linux-cli daemon --interval 60
ghub4linux-cli install-daemon
ghub4linux-cli monitor --interval 5

--json ist global und funktioniert auch hinter dem Subkommando. Der Exit-Code ist 1, wenn ein Gerät oder Profil nicht gefunden wird — dieselbe Konvention wie im Original.

Die Config liegt weiter unter $XDG_CONFIG_HOME/ghub4linux/config.json und ist dateikompatibel: Schlüsselnamen sind die Python-Dataclass-Felder, ein bestehendes File lädt unverändert. Eine kaputte Datei wird als config.json.corrupt beiseitegelegt, statt den Start zu verhindern; geschrieben wird über eine Temp-Datei + rename, damit ein voller Datenträger die alte Config nicht zerstört.

Was geprüft ist

cargo test — 50 Tests:

  • tests/hid_framing.rs (19) — Port von test_hid.py: exakte Bytes des 7-Byte-Kurz- und 20-Byte-Langframes, Umschalten bei vier Parametern, Function-Nibble (0x13 << 4 → 0x30), Device-Index, Truncation bei mehr als 16 Parametern, Read-Größe = Framelänge, leere Antwort → No response from device, Fehler-Wrapping für Write/Read.
  • tests/config_and_devices.rs (22) — DPI-Defaults (400/800/1600/3200/6400, aktiv Level 1), Hex-Ausgabe, Klemmen eines veralteten active_profile, Save/Load-Runde, .corrupt-Backup, JSON-Schlüsselnamen, Registry-Matching inklusive geteilter Lightspeed-Receiver-PIDs über den Produktstring, DPI-Validierung (0 und >16 Bit werden vor dem Senden abgelehnt), Effekt-Codes, Batterie-Lesepfad inklusive „kaputter Read ist None, nicht 0 %", Pro DEX 2 ohne RGB, Powerplay als Mauspad.
  • tests/cli.rs (9) — echte Binary-Aufrufe: No devices found., [] bei --json, Device not found: … mit Exit 1 in allen sechs Kommandos, Hilfe listet alle Kommandos, install-daemon schreibt eine Unit mit absolutem Pfad und räumt die alte Template-Unit weg, Platzhalter-Ersetzung.

Abweichungen zum Original

  • Ein Treiber statt Klassenhierarchie: G502 Hero/Lightspeed/X Plus und Pro DEX 2 teilen sich LogitechMouse mit einer MouseModel-Tabelle. Die Python-Subklassen unterschieden sich nur in Konstanten und has_rgb.
  • Doppeltes # korrigiert: dpi und lighting druckten ##ff0000, weil to_hex() schon ein # liefert und das Format noch eins davorsetzte. Sieht wie ein Tippfehler aus, war einer.
  • _get_firmware_version liefert immer Unknown: der Python-Code fragte IRoot-Funktion 5 und wertete zwei Bytes als Version — auf echter Hardware kam nie etwas anderes als Unknown heraus. Die Funktion ist nicht portiert, das Feld bleibt ehrlich leer.
  • Monitor/Daemon nutzen signal-hook statt signal.pause(); --interval 0 heißt hier „so schnell wie möglich", nicht „bis zum Signal blockieren".
  • GUI ist im Aufbau (siehe unten): Info-, Batterie-, DPI- und Lighting-Panel sind lesend da, die Editoren für DPI/Lighting/Profile/Makros fehlen noch.

Status der GUI

cargo run --features gui öffnet ein Adw.ApplicationWindow mit Geräteliste links und Panels rechts — dieselbe Struktur wie gui/main_window.py. Portiert sind Anzeige und Gerätewechsel; die schreibenden Dialoge (DPI-Level setzen, Effekt wählen, Profile anlegen/umbenennen/löschen, Makro-Editor) sind noch Python. Diese Lücke steht als Todo, nicht als erledigt.

Herkunft

Portiert am 2026-11-05 aus Ghub4Linux (src/ghub4linux, ~4.300 Zeilen inklusive Tests). Das Original bleibt unverändert; der Wechsel ist ein install des Binaries.