iToverDose/Software· 1 JUNI 2026 · 12:04

Lua-Erweiterungen selbst schreiben: So baute ich ein flexibles Plugin-System für mein Zeitmanagement-Tool

Ein Entwickler integriert Lua in sein C++-basiertes Aktivitäts-Tracking-Tool und ermöglicht so vollkommen flexible Erweiterungen. Wie das funktioniert und welche Herausforderungen dabei auftreten – eine detaillierte Analyse mit praktischen Beispielen.

DEV Community4 min0 Kommentare

Ein Entwickler hat eine innovative Lösung gefunden, um sein C++-basiertes Aktivitäts-Tracking-Tool HPR mit einem flexiblen Plugin-System auszustatten. Durch die Integration einer Lua-Erweiterungs-Engine können Nutzer nun eigene Skripte erstellen, die das Tool um zusätzliche Funktionen erweitern. Doch der Weg dorthin war alles andere als einfach – insbesondere die Vermeidung von Deadlocks stellte eine besondere Herausforderung dar.

Warum ein Plugin-System für ein Aktivitäts-Tracker sinnvoll ist

HPR ist ein leichtgewichtiger Aktivitäts-Tracker, der die aktive Anwendung eines Nutzers aufzeichnet und lokal speichert – ganz ohne Cloud oder Telemetrie. Ursprünglich beschränkte sich die Software darauf, Fensterwechsel zu protokollieren. Doch der Entwickler wollte mehr Flexibilität und startete ein ambitioniertes Projekt: die Integration eines Lua-Erweiterungssystems, das Nutzern erlaubt, das Tool nach ihren Bedürfnissen anzupassen.

Jede Erweiterung wird als .lua-Datei in einem speziellen Ordner abgelegt. HPR scannt diesen Ordner rekursiv, lädt jede Datei in einen eigenen sol::state und startet für jede Erweiterung einen dedizierten Thread. Diese Isolation stellt sicher, dass selbst fehlerhafte oder langsame Erweiterungen nicht die Hauptanwendung oder das Tracking beeinträchtigen können. Die Architektur folgt einem klaren Lifecycle:

  • init(): Gibt das Tick-Intervall in Millisekunden zurück, mit dem die onTick()-Funktion aufgerufen wird.
  • onTick(delta): Wird regelmäßig mit der verstrichenen Zeit seit dem letzten Aufruf ausgelöst.
  • onExit(): Wird beim Beenden der Anwendung aufgerufen und dient zur Bereinigung.

Welche Möglichkeiten die Lua-API bietet

Die integrierte API eröffnet Entwicklern ein breites Spektrum an Funktionen, die über das reine Fenster-Tracking hinausgehen. Mit den bereitgestellten Methoden können Erweiterungen:

  • Die aktive Anwendung abfragen (HPR.getCurrentWindow_E()).
  • Daten in der SQLite-Datenbank lesen und schreiben (HPR.dbQuery_E(), HPR.dbExecute_E()).
  • Ereignisse wie Fensterwechsel, Mitternachtswechsel oder Historie-Ladevorgänge abonnieren (WINDOW_CHANGED, MIDNIGHT_ROLLOVER, HISTORY_LOADED_SINGULAR).
  • Benutzerdefinierte Fenster-Erkennungslogik für Compositoren implementieren, die HPR nativ nicht unterstützt.
  • UI-Elemente dynamisch anpassen oder Callback-Funktionen registrieren (HPR.setUiProperty_E).
  • HTTP-Anfragen stellen oder einen eigenen Server hosten, JSON- und CSV-Daten verarbeiten.
  • Die Fenster-Tracking-Schleife komplett pausieren oder fortsetzen – eine Funktion, die beispielsweise für die AFK-Erkennung genutzt wird.

Ein besonders interessantes Beispiel ist die AFK-Erkennung: Wenn eine Erweiterung feststellt, dass der Nutzer inaktiv ist, kann sie die Tracking-Schleife unterbrechen und nach der Rückkehr des Nutzers wieder fortsetzen. Dies wird durch die Methoden HPR.stopTracking_E() und HPR.startTracking_E() ermöglicht.

Die größten Hürden: Thread-Sicherheit und Deadlocks

Obwohl die Implementierung der Lua-Erweiterungs-Engine auf den ersten Blick einfach erscheint, stieß der Entwickler auf erhebliche technische Herausforderungen. Vor allem die Thread-Sicherheit erwies sich als kritischer Faktor.

Die Lua-Umgebung ist nicht thread-sicher, die Slint-UI ebenfalls nicht und darf ausschließlich vom Event-Loop-Thread aus angesprochen werden. Gleichzeitig greifen mehrere Threads – darunter der Fenster-Poller, der Datenbank-Schreiber und die Lua-Erweiterungen – auf den gemeinsamen Applikationsstatus zu. Um Deadlocks zu vermeiden, setzte der Entwickler folgende Lösungen um:

  • Ein `recursive_mutex` pro Erweiterung, der alle Lua-Aufrufe umschließt.
  • Die Methode slint::invoke_from_event_loop, um UI-Aktualisierungen sicher vom Lua-Thread aus anzustoßen.
  • Mutex-basierte Synchronisation für Callback-Funktionen, die von Slint ausgelöst und an Lua weitergegeben werden.

Eine weitere knifflige Aufgabe war die zyklische Tabellen-Erkennung in Lua. Wenn eine Erweiterung eine sich selbst referenzierende Tabelle an die UI übergibt, würde dies zu einer Endlosschleife und einem Stack-Overflow führen. Der Entwickler implementierte einen Mechanismus, der Tabellen-Pointer während der Konvertierung verfolgt und bei Zyklen mit einer Fehlermeldung abbricht.

Praktisches Beispiel: Kompatibilität mit ActivityWatch herstellen

Ein konkretes Anwendungsbeispiel zeigt, wie mächtig das Lua-Erweiterungssystem sein kann. Der Entwickler schuf eine Erweiterung namens "AW Parasite", die einen HTTP-Server auf Port 5600 startet – genau der Standardport, den ActivityWatch nutzt. Dadurch können Tools wie aw-watcher-web oder aw-watcher-afk nahtlos mit HPR interagieren:

  • Sie senden Heartbeats, die die Erweiterung entgegennimmt.
  • Die Erweiterung sammelt die verbrachte Zeit auf Webseiten und den AFK-Status.
  • Alle Daten werden in die HPR-SQLite-Datenbank geschrieben.
  • Die UI wird über HPR.setUiProperty_E aktualisiert.

Besonders clever: Wenn der Nutzer den Rechner verlässt, ruft die Erweiterung HPR.stopTracking_E() auf, um die Fenster-Tracking-Schleife zu pausieren. Bei Rückkehr wird sie mit HPR.startTracking_E() wieder aktiviert. Die gesamte Logik ist in nur etwa 200 Zeilen Lua-Code umgesetzt und funktioniert sowohl unter Wayland als auch Windows, da sie rein auf HTTP basiert.

Hot-Reloading und Native Erweiterungen als weitere Features

HPR bietet zudem ein Hot-Reloading-System, mit dem Nutzer Erweiterungen zur Laufzeit laden, entladen oder neu laden können – ohne die Anwendung neu starten zu müssen. Ein weiterer Knopf ermöglicht das Nachladen neuer `.lua`-Dateien, die nach dem Start des Programms hinzugefügt wurden.

Beim Entladen einer Erweiterung wird deren Thread mit einem running = false-Flag beendet und erhält 450 Millisekunden Zeit, um sich ordnungsgemäß zu beenden. Gelingt dies nicht, wird der Thread abgehängt und eine Warnung protokolliert. Neben Lua-Erweiterungen unterstützt HPR auch native Erweiterungen (.dll oder .so-Dateien), die außerhalb der Sandbox laufen und dort eingesetzt werden, wo Lua an seine Grenzen stößt.

HPR steht als vollständig kostenloses Open-Source-Projekt zur Verfügung. Interessierte Entwickler können den Quellcode auf GitHub einsehen und das Projekt durch einen Stern unterstützen. Die Dokumentation und weitere Beispiele sollen in Zukunft ausgebaut werden, um die Möglichkeiten des Lua-Erweiterungssystems noch zugänglicher zu machen.

Der Aufbau eines solchen flexiblen Plugin-Systems unterstreicht, wie wichtig Modularität und Erweiterbarkeit in modernen Softwareprojekten sind – selbst wenn der Weg dorthin mit technischen Stolpersteinen gepflastert ist. Mit Lua als Brückentechnologie gelingt es, ein ansonsten statisches Tool in eine hochgradig anpassbare Plattform zu verwandeln.

KI-Zusammenfassung

C++23 ile geliştirilen açık kaynaklı HPR aktivite izleyicisine nasıl Lua tabanlı bir uzantı motoru eklendi? Ölümcül deadlocklardan kaçınma ve API kullanımı hakkında detaylar.

Kommentare

00
KOMMENTAR SCHREIBEN
ID #OR4GBV

0 / 1200 ZEICHEN

Menschen-Check

6 + 8 = ?

Erscheint nach redaktioneller Prüfung

Moderation · Spam-Schutz aktiv

Noch keine Kommentare. Sei der erste.