Native on every major desktop.
Wayland-first. Each platform has a dedicated backend with its own window query mechanism. No X11 fallback hacks.
Platform matrix.
HPR reads $XDG_CURRENT_DESKTOP at startup and dispatches to the correct backend automatically.
| Platform | Backend | Display Server | Extra Setup |
|---|---|---|---|
| Hyprland | hyprctl IPC |
Wayland | None |
| GNOME | Custom GNOME Shell Extension | Wayland | Extension |
| KDE Plasma 6+ | KWin scripting | Wayland X11 | None |
| Cinnamon | org.Cinnamon.Eval D-Bus |
X11 Wayland | None |
| niri | niri msg --json focused-window IPC |
Wayland | None |
| Windows 10/11 | Win32 API | — | None |
Hyprland
The cleanest backend. Hyprland exposes a Unix socket IPC interface that returns the active window directly. HPR queries via hyprctl and parses the JSON output natively in C++ — no jq required.
How it works
HPR calls hyprctl activewindow -j every 50ms and parses the JSON response natively in C++ to extract the window class and title. Zero overhead, zero hacks, zero extra dependencies.
// Platform: Hyprland (Wayland) // Mechanism: Unix socket IPC // Parsing: native C++ std::string (no jq) hyprctl activewindow -j // extracts "class" and "title" keys // via std::string::find() — no shell pipe // Direct, fast, no hacks needed. // This is what good Wayland // integration looks like.
GNOME (Wayland)
GNOME on Wayland does not expose the active window to external processes. HPR solves this with a custom GNOME Shell extension — lol-another-window-extension — built specifically for HPR.
Run the bundled installWindowCallsExtension.sh, which clones the extension and enables it. Then log out and back in once — GNOME on Wayland cannot hot-reload shell extensions.
Auto-Detection
On first launch, HPR checks whether its GNOME extension is active. If it isn't, HPR sets its internal platform to GNOME_NO_EXTENSION and tells you directly rather than silently returning garbage. It will not attempt to install the extension autonomously.
# One-time extension install chmod +x installWindowCallsExtension.sh ./installWindowCallsExtension.sh # Log out and back in (required) # GNOME Wayland can't hot-reload # shell extensions # Every launch after that is automatic
KDE Plasma 6+ (Wayland / X11)
The KDE backend uses Kwin scripting (Just like kdotool without relying on kdotool itself).
HPR uses the same underlying KWin scripting approach as kdotool — so it's accurate to say that wherever kdotool works, HPR also works.
// Platform: KDE Plasma // Works on both Wayland and X11 // Every 50ms: 1. Get window data via Kwin script 2. Parse output into required format 3. Parse window class + title 4. Filter out js:: noise // Validated on multiple KDE configs
Cinnamon (X11 + Wayland)
Cinnamon runs on top of Muffin (a fork of GNOME's Mutter). Unlike other Linux backends, Cinnamon exposes org.Cinnamon.Eval — a D-Bus method that evaluates JavaScript directly inside the live Cinnamon process.
D-Bus Eval
HPR queries the internal global.display.focus_window object for the window class via get_wm_class() and the title via get_meta_window().get_title().
Cross-Session
Because this goes through Cinnamon's own compositor internals rather than X11 display properties, it works identically on both the X11 session (default on Linux Mint) and the experimental Wayland session without any code branching.
// Window class (application name) global.display.focus_window .get_wm_class() // Window title (active tab/document) global.get_window_actors() .filter(a => a.meta_window.has_focus() )[0] .get_meta_window() .get_title() // Both go through Muffin's compositor // Works on X11 and Wayland identically
niri (Wayland)
The scrollable-tiling Wayland compositor niri is natively supported via its direct command-line IPC interface.
Niri Msg IPC
HPR runs niri msg --json focused-window every 50ms and parses the JSON response natively in C++ to query the active application's app_id and window title.
// Platform: niri // Works natively on Wayland // Runs every 50ms: niri msg --json focused-window // parses "app_id", "title" and "pid"
Windows 10 / 11
The simplest backend. Win32 API provides direct access to the foreground window. No injection, no scraping, no extensions.
Tray Mode
Built without a console window. HPR sits in your system tray and stays out of your way. No terminal flash on launch.
Performance
Around 13 MB RSS in real use. That's HPR's own code plus the Windows runtime. The lightest footprint of any platform.
// Platform: Windows 10/11 // Mechanism: Win32 API GetForegroundWindow() GetWindowText(hwnd, buffer, size) // Direct API. No tricks needed. // ~13 MB RSS in real use. // No console window — tray only.
Platform detection & architecture.
All platform window detection is contained inside CurrentWindowManager. The constructor reads $XDG_CURRENT_DESKTOP and dispatches to the right backend.
getCurrentWindow() { if (platform.contains("Hyprland")) return getCurrentWindow_Hyprland(); else if (platform.contains("GNOME")) return getCurrentWindow_GNOME(); else if (platform.contains("KDE")) return getCurrentWindow_KDE(); else if (platform.contains("Cinnamon")) return getCurrentWindow_Cinnamon(); else if (platform.contains("niri")) return getCurrentWindow_niri(); // Windows handled at compile time }
HPR reads $XDG_CURRENT_DESKTOP and matches substrings via std::string::contains. Non-standard desktop session variables or nested compositor configurations may not resolve correctly.
validateAndUpdateWindow_Cross runs on every return from every platform getter automatically. It filters known system noise — including plasmashell, searchhost, and KWin JS runtime artifacts (js:: prefix outputs) — so they never appear in your tracking data. New backends inherit all of this for free.
Every HPR feature — the Lua extension engine, per-app limits and goals, multi-day historical data queries, interpreted UI mode, and the alias system — works identically on Hyprland, GNOME, KDE, Cinnamon, niri, and Windows. There is no platform-specific feature subset.
Your platform is supported.
HPR runs natively on Hyprland, GNOME, KDE Plasma, Cinnamon, niri, and Windows. No compatibility layers.