This commit is contained in:
@@ -1,225 +1,90 @@
|
||||
# Korin Timer (Linux)
|
||||
# Korin Timer
|
||||
|
||||
Eigenständige Neu-Implementierung eines 2-Step-Title-Switching-Timers als
|
||||
Overlay (kein Reverse Engineering, keine Codeübernahme aus proprietärer
|
||||
Software - nur nach Funktionsbeschreibung neu gebaut).
|
||||
Korin Timer ist ein transparentes Linux-Overlay für Elsword. Es zeigt die
|
||||
Cooldowns deiner Titel an und lässt sich komplett über globale Hotkeys
|
||||
bedienen.
|
||||
|
||||
## Funktionsprinzip
|
||||
## Schnellstart
|
||||
|
||||
- **Ohne Vor-Aktivierung** (Normalfall): Hotkey drücken startet den
|
||||
Cooldown-Timer direkt, ein einziger Druck reicht.
|
||||
- **Mit Vor-Aktivierung** (optional, pro Titel im Settings-Fenster
|
||||
einstellbar): eine zusätzliche Taste/Kombination - z.B. eine Taste, die
|
||||
im Spiel ohnehin gedrückt wird - die den Titel in Standby versetzt
|
||||
(gepulster Rahmen zeigt das an). Die Haupttaste startet dann, *ein
|
||||
einziges Mal gedrückt*, den Cooldown; sie allein (ohne vorherige
|
||||
Vor-Aktivierung) macht in diesem Modus nichts.
|
||||
- **Standby-Timeout** (optional, pro Titel, 0 = aus, wirkt nur im
|
||||
Vor-Aktivierungs-Modus): springt automatisch zurück auf Idle, wenn die
|
||||
Haupttaste nicht innerhalb der eingestellten Sekundenzahl nach der
|
||||
Vor-Aktivierung gedrückt wird - inklusive Countdown-Anzeige im Overlay,
|
||||
solange er läuft.
|
||||
- **Neu triggerbar** (Checkbox pro Titel, aus per Default): normalerweise
|
||||
wird die Haupttaste während laufendem Cooldown ignoriert. Mit dieser
|
||||
Option gedrückt startet sie den Cooldown stattdessen neu (volle Dauer ab
|
||||
dem Zeitpunkt des Drucks) - für Buffs, die man aktiv auffrischen will.
|
||||
- **Dogma-Modus** (`dogma_mode: true` in der Config, Schalter im
|
||||
Settings-Fenster): solange irgendein Cooldown läuft, werden Hotkeys für
|
||||
*andere* Titel ignoriert. Aus, wenn alle Titel unabhängig voneinander
|
||||
gleichzeitig laufen sollen.
|
||||
- **Verschieben**: `Strg+Alt` gedrückt halten + mit der linken Maustaste aufs
|
||||
Overlay klicken und ziehen. Die neue Position wird beim Loslassen in der
|
||||
Config-Datei des aktiven Charakter-Profils (`overlay.x`/`overlay.y`,
|
||||
siehe unten) gespeichert und bleibt über Neustarts erhalten. Funktioniert
|
||||
wie die Hotkeys über
|
||||
rohes `evdev` (siehe unten) - das Overlay ist click-through, bekommt also
|
||||
nie normale Mausereignisse vom Fenstersystem. (Nicht nur `Alt`, weil das
|
||||
mit GNOMEs eigenem "Alt+Drag verschiebt jedes Fenster"-Standardverhalten
|
||||
kollidiert.)
|
||||
|
||||
## Warum XWayland/EWMH statt Layer-Shell
|
||||
|
||||
Erster Ansatz war `gtk4-layer-shell` (`wlr-layer-shell`-Protokoll) - das
|
||||
funktioniert aber nur auf wlroots-Compositors (Sway, Hyprland). **GNOME/Mutter
|
||||
implementiert dieses Protokoll nicht.** Getestet gegen eine echte GNOME-
|
||||
Wayland-Session: die Layer-Shell-Initialisierung schlug fehl
|
||||
(`compositor does not support the Layer Shell protocol`).
|
||||
|
||||
Stattdessen läuft das Overlay über XWayland als normales X11-Fenster mit
|
||||
EWMH-Hints (`_NET_WM_STATE_ABOVE`, `_NET_WM_STATE_SKIP_TASKBAR`, Klick-durch
|
||||
via XShape-Extension). Das funktioniert auf GNOME, KDE *und* wlroots-
|
||||
Compositors, weil XWayland praktisch überall mitläuft. Einzige Falle dabei:
|
||||
Mutter respektiert `_NET_WM_STATE_ABOVE` nur, wenn es per ClientMessage
|
||||
**nach** dem Mapping des Fensters gesendet wird (nicht als Property davor,
|
||||
im Gegensatz zu SKIP_TASKBAR/SKIP_PAGER) - siehe `x11_overlay.raise_above()`.
|
||||
|
||||
Das umgeht auch das ursprüngliche Wine-Problem: statt eines transparenten
|
||||
DirectX-Fensters unter Wine (das dort schwarz statt durchsichtig gerendert
|
||||
wurde) ist das hier ein natives GTK4-Fenster mit echter RGBA-Transparenz.
|
||||
|
||||
## Charakter-Profile
|
||||
|
||||
Jeder Charakter hat eine eigene, vollständige Config unter
|
||||
`~/.config/titletimer/profiles/<Name>.json` (Titel, Hotkeys, Cooldowns,
|
||||
Dogma-Modus, Overlay-Position - alles). Im Einstellungsfenster oben eine
|
||||
Zeile "Charakter:" mit Dropdown + `Neu`/`Duplizieren`/`Umbenennen`/`Löschen`:
|
||||
|
||||
- **Neu**: legt ein Profil mit den Beispiel-Titeln aus `config.example.json`
|
||||
an, das direkt umbenannt/umgebaut werden kann.
|
||||
- **Duplizieren**: kopiert das aktuell aktive Profil unter neuem Namen -
|
||||
praktisch für einen zweiten Charakter mit ähnlichem Titel-Setup.
|
||||
- **Umbenennen** / **Löschen**: benennt die Profil-Datei um bzw. entfernt
|
||||
sie (der letzte verbleibende Charakter lässt sich nicht löschen).
|
||||
|
||||
Profil-Wechsel im Dropdown lädt sofort das gewählte Profil - laufendes
|
||||
Overlay übernimmt es ohne Neustart, wie beim normalen "Speichern". Nicht
|
||||
gespeicherte Änderungen am vorherigen Profil gehen beim Wechsel verloren
|
||||
(genau wie beim Schließen des Fensters ohne zu speichern). Das zuletzt
|
||||
aktive Profil wird gemerkt (`~/.config/titletimer/last_profile.txt`) und
|
||||
beim nächsten Start automatisch wieder geladen.
|
||||
|
||||
Bereits vorhandene Configs aus einer Version vor diesem Feature
|
||||
(`~/.config/titletimer/config.json`) werden beim ersten Start automatisch
|
||||
zu einem Profil namens "Default" migriert (per Umbenennen in
|
||||
`profiles/Default.json`) - kein manueller Schritt nötig.
|
||||
|
||||
`--config <pfad>` umgeht das Profil-System komplett und lädt direkt eine
|
||||
beliebige Datei (z.B. zum Testen) - dann ist die Profil-Zeile im
|
||||
Einstellungsfenster ausgeblendet, da es kein Profil-Konzept dafür gibt.
|
||||
|
||||
## Setup (aus dem Quellcode, ohne AppImage)
|
||||
|
||||
Falls du nicht das AppImage nutzt, sondern direkt aus `src/` starten willst,
|
||||
brauchst du GTK4/PyGObject/evdev/xlib systemweit oder per `nix-shell`
|
||||
(siehe `shell.nix` im Repo - relevant auf NixOS, wo es kein systemweites
|
||||
`python3`/GTK4 auf dem `PATH` gibt):
|
||||
1. Öffne im Repository den Bereich **Releases** und lade
|
||||
`Korin-Timer.AppImage` herunter.
|
||||
2. Mache die Datei ausführbar und starte sie:
|
||||
|
||||
```bash
|
||||
# Arch/CachyOS
|
||||
sudo pacman -S python-gobject gtk4 gtk4-layer-shell python-evdev python-xlib
|
||||
# NixOS
|
||||
nix-shell # nutzt die shell.nix im Repo-Root
|
||||
chmod +x Korin-Timer.AppImage
|
||||
./Korin-Timer.AppImage
|
||||
```
|
||||
|
||||
# Nutzer muss in der input-Gruppe sein, damit evdev /dev/input lesen darf
|
||||
3. Beim ersten Start öffnen sich Overlay und Einstellungsfenster. Lege dort
|
||||
deine Titel, Hotkeys und Cooldowns an und klicke auf **Speichern**.
|
||||
|
||||
Die App benötigt Leserechte auf die Eingabegeräte, damit Hotkeys außerhalb
|
||||
des Fensters funktionieren. Führe einmal aus und melde dich anschließend
|
||||
vollständig ab und wieder an:
|
||||
|
||||
```bash
|
||||
sudo usermod -aG input "$USER"
|
||||
# danach ab- und wieder anmelden (Gruppenmitgliedschaft wird erst beim
|
||||
# nächsten Login aktiv)
|
||||
```
|
||||
|
||||
Config: Falls beim ersten Start noch kein Charakter-Profil existiert
|
||||
(`~/.config/titletimer/profiles/`, siehe oben), wird automatisch eines
|
||||
namens "Default" aus der eingebauten `config.example.json` angelegt (auch
|
||||
im AppImage - keine separate Datei nötig). Danach anpassen - entweder
|
||||
per Hand:
|
||||
## Bedienung
|
||||
|
||||
```bash
|
||||
$EDITOR ~/.config/titletimer/profiles/Default.json
|
||||
# hotkey ist entweder ein einzelner evdev-Keycode-String ("KEY_F1") oder
|
||||
# eine Liste für eine Tastenkombination (["KEY_LEFTCTRL", "KEY_F1"]) - alle
|
||||
# gelisteten Tasten muessen gleichzeitig gehalten werden. Keycode-Namen:
|
||||
# python3 -c "from evdev import ecodes; print([k for k in dir(ecodes) if k.startswith('KEY_')])"
|
||||
```
|
||||
- Drücke den Hotkey eines Titels, um seinen Cooldown zu starten.
|
||||
- Optional kann ein Titel erst per Vor-Aktivierungs-Hotkey in den Standby
|
||||
wechseln. Erst der normale Hotkey startet dann den Cooldown.
|
||||
- Mit **Neu triggerbar** startet derselbe Hotkey einen bereits laufenden
|
||||
Cooldown erneut.
|
||||
- Im Dogma-Modus ist immer nur ein Titel gleichzeitig aktiv.
|
||||
- Verschiebe das Overlay mit **Strg + Alt + linker Maustaste ziehen**.
|
||||
Die Position wird im aktiven Profil gespeichert.
|
||||
|
||||
oder über das eingebaute Einstellungsfenster (Titel hinzufügen/entfernen,
|
||||
Hotkeys per Klick-und-Tastendruck erfassen - auch Mehrfachkombinationen
|
||||
wie Strg+F1 -, Cooldowns, Farben, optionales Icon pro Titel, Dogma-Modus,
|
||||
Anker/Rand/Skalierung, sowie die per Alt+Drag gesetzte Position
|
||||
zurücksetzen) - das öffnet sich automatisch mit dem Overlay zusammen.
|
||||
"Speichern" wirkt sofort auf das laufende Overlay, kein Neustart nötig
|
||||
(ein aktuell laufender Cooldown wird dabei zurückgesetzt).
|
||||
Das Einstellungsfenster muss geöffnet bleiben: Sein Schließen beendet auch
|
||||
das rahmenlose, klickdurchlässige Overlay.
|
||||
|
||||
Starten (aus dem Projektverzeichnis):
|
||||
## Profile
|
||||
|
||||
Jeder Charakter erhält ein eigenes Profil unter
|
||||
`~/.config/titletimer/profiles/`. Über das Dropdown im Einstellungsfenster
|
||||
kannst du Profile anlegen, duplizieren, umbenennen, wechseln und löschen.
|
||||
Ein Wechsel übernimmt die neue Konfiguration sofort; nicht gespeicherte
|
||||
Änderungen des vorherigen Profils gehen dabei verloren.
|
||||
|
||||
## Neues Release erstellen
|
||||
|
||||
Jeder Push auf `main` startet automatisch einen Gitea-Actions-Build. Nach
|
||||
einem erfolgreichen Lauf:
|
||||
|
||||
1. Öffne **Actions** und den aktuellen erfolgreichen Build.
|
||||
2. Lade das Artefakt `Korin-Timer.AppImage` herunter.
|
||||
3. Öffne **Releases** → **New Release**.
|
||||
4. Erstelle einen Tag, etwa `v0.1.0`, und hänge die AppImage-Datei an.
|
||||
5. Veröffentliche das Release.
|
||||
|
||||
Das Actions-Artefakt wird nach 30 Tagen gelöscht; der Release-Anhang bleibt
|
||||
verfügbar.
|
||||
|
||||
## Aus dem Quellcode starten
|
||||
|
||||
Für Entwicklung brauchst du GTK4, PyGObject, `evdev` und `python-xlib`.
|
||||
Danach startest du die Anwendung aus dem Projektverzeichnis mit:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=src python3 -m titletimer
|
||||
```
|
||||
|
||||
Startet Overlay **und** Einstellungsfenster zusammen. Das Einstellungs-
|
||||
fenster zu schließen beendet die ganze App (auch das Overlay) - das ist
|
||||
aktuell der einzige Weg, das rahmenlose, click-through Overlay wieder zu
|
||||
stoppen. Nur das Einstellungsfenster ohne Overlay:
|
||||
Nur das Einstellungsfenster öffnest du mit:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=src python3 -m titletimer --settings
|
||||
# oder: ./Korin-Timer.AppImage --settings
|
||||
```
|
||||
|
||||
## AppImage bauen (voll gebündelt, keine Systemabhängigkeiten)
|
||||
## AppImage lokal bauen
|
||||
|
||||
Der CI-Workflow ist der empfohlene Buildweg. In einer Debian-artigen
|
||||
Umgebung kann das AppImage auch lokal gebaut werden:
|
||||
|
||||
```bash
|
||||
./packaging/build-appimage.sh
|
||||
```
|
||||
|
||||
Das AppImage ist **vollständig in sich geschlossen**: eigener
|
||||
Python-Interpreter + Standardbibliothek, GTK4, `gtk4-layer-shell`,
|
||||
PyGObject, pycairo, `evdev`, `python-xlib` - und deren *gesamter*
|
||||
transitiver Shared-Library-Baum (~78 `.so`-Dateien), inklusive `ld.so` und
|
||||
`libc` selbst. Nichts davon kommt vom Zielsystem.
|
||||
|
||||
Warum so radikal gebündelt: Auf NixOS gibt es kein FHS-`/usr/bin/python3`
|
||||
und kein System-GTK4 auf dem `PATH` - der ursprüngliche Ansatz ("GTK4 ist
|
||||
auf so gut wie jedem Desktop schon da") war für NixOS schlicht falsch.
|
||||
`packaging/bundle-runtime.sh` sammelt den kompletten Abhängigkeitsbaum per
|
||||
rekursivem `ldd` über Python3-Binary, PyGObject-Extensions
|
||||
(`_gi`, `_gi_cairo`), pycairo, `libgtk-4.so.1` und
|
||||
`libgtk4-layer-shell.so.0` ein. `AppRun` ruft danach nicht mehr `python3`
|
||||
über `PATH` auf, sondern das gebündelte `ld-linux-x86-64.so.2` direkt mit
|
||||
`--library-path` auf das gebündelte `lib/`-Verzeichnis, plus
|
||||
`PYTHONHOME`/`PYTHONPATH`/`GI_TYPELIB_PATH` auf die gebündelten Pfade.
|
||||
Damit entfällt auch die vorherige Sorge um `evdev`s Python-ABI-Tag
|
||||
(CPython-Minor-Version-Mismatch) komplett - es ist ja unser eigener
|
||||
Interpreter, keiner vom Zielsystem.
|
||||
|
||||
Größe: ca. 28 MB (statt vorher 1,4 MB) - der Preis für echte
|
||||
Systemunabhängigkeit.
|
||||
|
||||
Der Build erzeugt `build/Korin-Timer.AppImage`. Das Artefakt gehört nicht
|
||||
zum Repository und wird nicht eingecheckt.
|
||||
|
||||
```bash
|
||||
chmod +x build/Korin-Timer.AppImage # nur falls Ausführbarkeit fehlt
|
||||
./build/Korin-Timer.AppImage
|
||||
```
|
||||
|
||||
Einzige verbleibende Voraussetzung auf dem Zielsystem: die `input`-Gruppen-
|
||||
Mitgliedschaft für `evdev` (s.o.) - das kann eine AppImage-Datei nicht
|
||||
selbst mitbringen, das ist eine Kernel-Berechtigung.
|
||||
|
||||
**Wie das `.AppImage` selbst gebaut wurde**: `appimagetool`s eigene
|
||||
AppImage-Datei ist selbst eine statisch-PIE-gelinkte ELF-Datei ohne
|
||||
`PT_INTERP` - in meiner Sandbox (ein eingeschränkter Podman-Container)
|
||||
ließ sich dieses Binärformat gar nicht ausführen (`execve` schlug mit
|
||||
ENOENT fehl, unabhängig von FUSE). Deshalb baut das Skript jetzt
|
||||
automatisch alternativ von Hand: die separate `runtime-x86_64`-Datei (der
|
||||
reine ELF-Loader-Stub) direkt mit `mksquashfs AppDir` zusammen-`cat`en -
|
||||
exakt das, was `appimagetool` intern auch tut. Auf deinem echten System
|
||||
sollte `appimagetool` normal durchlaufen; falls nicht, greift derselbe
|
||||
Fallback automatisch.
|
||||
|
||||
## Was tatsächlich getestet wurde
|
||||
|
||||
- **Config-Loader + State-Machine** (2-Step-Umschaltung, Dogma-Mutex,
|
||||
Cooldown-Expiry): per Skript ausgeführt und alle Assertions verifiziert.
|
||||
- **GTK4/GdkX11/evdev-Imports**: installiert und erfolgreich importiert.
|
||||
- **Overlay live gegen deine echte laufende GNOME-Wayland-Session getestet**
|
||||
(nicht nur eine isolierte Testumgebung): Fenster wird erzeugt, `evdev`
|
||||
findet deine echten Tastatur-Devices, und per `xprop` verifiziert:
|
||||
`_NET_WM_STATE_ABOVE`, `_NET_WM_STATE_SKIP_TASKBAR`,
|
||||
`_NET_WM_STATE_SKIP_PAGER` und `_NET_WM_WINDOW_TYPE_UTILITY` sind
|
||||
tatsächlich gesetzt, Fenster läuft ohne Exceptions.
|
||||
- **AppImage-Datei selbst**: ELF-Header und eingebettetes SquashFS per
|
||||
`unsquashfs` an der korrekten Byte-Offset-Grenze extrahiert und geprüft
|
||||
(`AppRun`, `.desktop`-Datei lesbar) - der Aufbau der Datei ist also
|
||||
korrekt. **Nicht** verifiziert: das tatsächliche Doppelklick-Starten der
|
||||
`.AppImage`, weil meine Sandbox dieses Binärformat generell nicht
|
||||
ausführen kann (s.o.) - das musst du einmal bei dir bestätigen.
|
||||
- **Nicht** verifiziert: wie es *visuell* aussieht (Puls-Rahmen-Optik,
|
||||
Transparenz-Feinschliff) und ob es zuverlässig über einem Vollbild-Spiel
|
||||
liegt - das kannst nur du im echten Gameplay beurteilen.
|
||||
|
||||
## Aktueller Funktionsumfang
|
||||
|
||||
Nur der 2-Step Title-Switching-Timer + Dogma-Mode ist implementiert (siehe
|
||||
Absprache). Flow-System (Pet-Tracking), Horary Loop Timer, Custom/Foto-Timer
|
||||
und Post-it-Labels aus dem Original sind bewusst noch nicht gebaut - können
|
||||
bei Bedarf nachgezogen werden.
|
||||
Das Ergebnis liegt anschließend unter `build/Korin-Timer.AppImage` und wird
|
||||
nicht ins Repository eingecheckt.
|
||||
|
||||
Reference in New Issue
Block a user