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 via qdbus6 |
Wayland X11 | None |
| Cinnamon | org.Cinnamon.Eval D-Bus |
X11 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 injects a JavaScript payload into KWin via qdbus6 (or qdbus-qt6 on Fedora — auto-detected at startup) on every 50ms tick and scrapes the system journal for the output.
That means shell forks and disk reads at 20 Hz. Somehow this lands at around 1% CPU. Every other approach tested did not work. This one does and has been validated across multiple KDE configurations. It is a hack. It is a working hack.
JS Noise Filter
During the JavaScript injection, KWin's own JS runtime briefly appears as the active window. HPR's validateAndUpdateWindow_Cross function contains a specific js:: filter to suppress strings like js::kwin_tmp_1234 from polluting your time log.
// Platform: KDE Plasma // Works on both Wayland and X11 // Auto-detects qdbus6 vs qdbus-qt6 // Every 50ms: 1. Inject JS payload via qdbus6 2. Scrape journalctl for output 3. Parse window class + title 4. Filter out js:: noise // ~1% CPU despite 20 Hz forks // 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
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 8 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. // ~8 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(); // 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. New backends inherit all normalization and noise filtering for free.
Your platform is supported.
HPR runs natively on Hyprland, GNOME, KDE Plasma, Cinnamon, and Windows. No compatibility layers.