2026-08-03 20:27:06 +02:00
2026-08-03 20:27:06 +02:00
2026-08-03 20:27:06 +02:00
2026-08-03 20:27:06 +02:00
2026-08-03 20:27:06 +02:00
2026-08-03 20:27:06 +02:00

Korin Timer (Linux)

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).

Funktionsprinzip

  • 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):

# 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

# Nutzer muss in der input-Gruppe sein, damit evdev /dev/input lesen darf
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:

$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_')])"

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).

Starten (aus dem Projektverzeichnis):

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:

PYTHONPATH=src python3 -m titletimer --settings
# oder: ./Korin-Timer.AppImage --settings

AppImage bauen (voll gebündelt, keine Systemabhängigkeiten)

./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 evdevs 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.

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: appimagetools 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-caten - 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.

S
Description
No description provided
Readme
2.2 MiB
2026-08-03 20:46:40 +02:00
Languages
Python 93.7%
Shell 6.3%