Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rxtt

X11 activity tracker. Records focused window intervals to SQLite. Generates usage reports and workflow timelines.

Platform

  • Linux with X11 (Wayland not supported)
  • Tested: Debian 13 + XFCE, single monitor
  • Requires: X11 server, EWMH _NET_ACTIVE_WINDOW, XScreenSaver extension (for idle detection)

Example

# rxtt report --no-tree --no-ansi
Usage report for 2026-07-17 (3h 53m 04s/12h 39m 26s)
Alacritty  1h 30m 52s
Chromium  47m 11s
firefox  39m 40s
Spotify  26m 41s
TelegramDesktop  20m 26s
Free Download Manager  6m 37s
Xfdesktop  26s
Throne  20s
Xfce4-clipman-history  11s

# rxtt report --no-ansi
Usage report for 2026-07-17 (3h 53m 04s/12h 39m 26s)
Alacritty  1h 32m 45s
  ├─ "rxtt"  37m 36s
  ├─ "ilya@ilya:~/projects/rust/rxtt"  19m 06s
  ├─ "π - rxtt"  9m 12s
  ├─ "nvim README.md"  3m 43s
  ├─ "nvim ~/.config/rxtt/config.toml"  3m 09s
# and so on...

Prebuilt binaries

Grab the pre-compiled binaries from the latest release page.

Build

Prerequisites:

  • Rust toolchain (edition 2024)
  • X11 development headers (libx11-dev on Debian/Ubuntu, libX11-devel on Fedora)
git clone <repo> && cd rxtt
cargo build --release
# or, build statically linked binaries using glibc or musl (make required)
make build-static-glibc
make build-static-musl # require musl-tools

Binary lands at target/release/rxtt. Install globally:

cargo install --path .

Dependencies:

Crate Purpose
clap (derive) CLI parsing
x11rb (+screensaver) X11 connection, EWMH atoms, idle queries
rusqlite (bundled) SQLite storage (sqlite3 bundled, no system dep)
chrono Date/time handling
regex Title grouping patterns
toml + serde Config file parsing
unicode-normalization Title normalization (NFKC, format-mark removal)
signal-hook SIGINT/SIGTERM handling
anstyle ANSI terminal styling
nix (poll) poll() for X11 event loop

Commands

Command Description
rxtt daemon Foreground tracker. Watches X11 focus/idle, writes intervals to SQLite
rxtt report Usage totals grouped by application and title for a date range
rxtt workflow Chronological sequence of active window spans
rxtt title-test Trace how a raw title normalizes through grouping rules
rxtt probe Diagnostic: print X11/EWMH/XScreenSaver support and current active window

Global flags

Flag Description
--config PATH TOML config path (default: $XDG_CONFIG_HOME/rxtt/config.toml or ~/.config/rxtt/config.toml). Auto-creates file with default rules on first run

rxtt daemon

Run foreground. Watches _NET_ACTIVE_WINDOW property changes and XScreenSaver idle state. Writes completed intervals to SQLite. Streams status events to stdout. Stops on Ctrl+C (SIGINT) or SIGTERM.

Only completed intervals persist. If the process dies mid-interval, in-memory data for that interval is lost.

Arguments

Flag Default Description
--database PATH $XDG_STATE_HOME/rxtt/activity.sqlite3 (or ~/.local/state/rxtt/activity.sqlite3) SQLite database path
--idle-threshold SECONDS 300 (5 min) Seconds without input before entering idle state. Must be finite, >= 0
--sample-interval SECONDS 0.25 Seconds between safety focus/idle polls. Must be finite, > 0
--title-interval SECONDS 5 Minimum seconds between emitted title-change events for one active window (throttle)
--no-idle off Disable idle detection. Track focused windows only (no idle intervals)

Usage

# Default: 5 min idle threshold, 5 s title throttle
rxtt daemon

# Custom database, no idle tracking
rxtt daemon --database /tmp/activity.sqlite3 --no-idle

# Faster polling, longer idle
rxtt daemon --idle-threshold 600 --sample-interval 0.5

Tracker loop

  1. Subscribe to _NET_ACTIVE_WINDOW property changes
  2. Subscribe to current window's title change events
  3. Reconcile current state (active/idle) → start interval if needed
  4. Loop: drain X11 events → safety poll → flush pending title update → reconcile
  5. On shutdown: finish current interval → dispatch completes → exit 0

rxtt report

Print usage totals grouped by application. Shows title tree (collapsed equivalent titles) beneath each app.

Date range defaults to today. Heading shows (active usage / elapsed selected range). When range includes today, elapsed ends at current time.

Arguments

Flag Default Description
--database PATH same as daemon SQLite database path
--since YYYY-MM-DD today First included calendar date (inclusive)
--until YYYY-MM-DD today Last included calendar date (inclusive)
--no-tree off Hide title branches below each application
--no-group-titles off Show exact stored titles instead of grouping equivalents
--no-ansi off Disable ANSI styling (use when redirecting to file)

Title grouping

Grouping merges titles that differ only in formatting or volatile UI state:

  • Universal normalization: NFKC unicode, lowercase, remove format marks, normalize curly quotes, collapse whitespace
  • App-specific regex rules: Strip browser tab badges ((5) Inboxinbox), Telegram unread counters, terminal spinner prefixes, etc.

Rules run in file order. Every replacement in a rule runs in order. See Config for details.

Usage

# Today
rxtt report

# Two-week range with title tree
rxtt report --since 2026-07-01 --until 2026-07-16

# Flat output, exact titles, no ANSI (for piping)
rxtt report --since 2026-07-01 --until 2026-07-16 --no-tree --no-group-titles --no-ansi

rxtt workflow

Print chronological active window spans. Each span shows: local start/end time, application, exact stored title, duration.

Adjacent spans with identical app + raw title merge. Idle time omitted. Title changes shown at daemon's --title-interval resolution.

Arguments

Flag Default Description
--database PATH same as daemon SQLite database path
--since YYYY-MM-DD today First included calendar date (inclusive)
--until YYYY-MM-DD today Last included calendar date (inclusive)
--no-ansi off Disable ANSI styling
--json off Export as JSON array (suppresses heading/ANSI)

JSON output

Each entry: { "app_id", "title" (or null), "started_at" (unix), "ended_at" (unix), "duration_seconds" }

Usage

# Chronological view for two weeks
rxtt workflow --since 2026-07-01 --until 2026-07-16

# JSON export
rxtt workflow --since 2026-07-01 --json > workflow.json

rxtt title-test

Trace title normalization pipeline. Shows universal normalization step, then every app rule (matched/skipped) with per-replacement input/output.

No database needed. Uses same config and pipeline as report.

Arguments

Positional Description
APP_ID Application identifier (matched against rule match regex)
TITLE Raw window title to normalize
Flag Description
--expect TITLE Expected final title. Prints PASS/FAIL and exits 0/1. Useful for shell regression

Usage

# Trace normalization
rxtt title-test Firefox '(33) Inbox'

# Regression check (exits nonzero on mismatch)
rxtt title-test Firefox '(33) Inbox' --expect inbox

rxtt probe

One-shot diagnostic. Connects to X11, checks EWMH _NET_ACTIVE_WINDOW and XScreenSaver support, prints active window info and any warnings.

No arguments beyond --config.

Usage

rxtt probe

Config

First rxtt run creates $XDG_CONFIG_HOME/rxtt/config.toml (or ~/.config/rxtt/config.toml). Use --config PATH with any command to select alternate file.

Format: TOML. Invalid TOML, wrong version, or bad regex → warning + built-in defaults for that run.

Structure

version = 1

[[title_grouping.apps]]
match = "(?i)my-app"
replacements = [
  { pattern = '^\[\d+%\]\s*', replacement = "" },
]
Field Type Description
version integer Optional. Default - latest config version, now '1'
title_grouping.apps array Ordered list of app rules
apps[].match regex Rust regex pattern matched against application ID
apps[].replacements array Ordered list of { pattern, replacement }
replacements[].pattern regex Pattern to match (Rust regex syntax)
replacements[].replacement string Replacement string. Supports $1, ${name} capture refs

Built-in rules

App match What it strips
Firefox, Chromium, Chrome, Brave Tab count *, (N), N · , Telegram-style message counters
Telegram Unread counters (N), inline - (N)
Terminals (Alacritty, Kitty, WezTerm, GNOME Terminal, Konsole, Terminator, Tilix, Foot, Xterm) Block-element spinner chars, [!] / [·] status prefixes

Rules run in file order. Multiple rules can match same app (all replacements chain).

Database

SQLite file at ~/.local/state/rxtt/activity.sqlite3 (or --database path).

Settings: WAL mode, foreign keys ON, 5s busy timeout, versioned migrations (PRAGMA user_version = 1).

Schema (v1)

activity_interval — completed intervals

Column Type Notes
id INTEGER PK Auto-increment
tracker_interval_id INTEGER Unique per daemon session
state TEXT 'active' or 'idle'
started_at INTEGER Unix epoch seconds
ended_at INTEGER Unix epoch seconds
start_reason TEXT e.g. "startup", "_NET_ACTIVE_WINDOW changed"
end_reason TEXT e.g. "shutdown", "periodic sample; idle=305.2s"
initial_window_id INTEGER X11 window ID
initial_title TEXT Window title at interval start
initial_wm_instance TEXT WM_INSTANCE_NAME
initial_wm_class TEXT WM_CLASS class
initial_pid INTEGER Process ID
initial_executable TEXT Executable path
app_id TEXT Derived application identifier

window_metadata_change — title/metadata updates during interval

Column Type Notes
id INTEGER PK Auto-increment
interval_id INTEGER FK activity_interval.id (CASCADE delete)
observed_at INTEGER Unix epoch seconds
window_id INTEGER X11 window ID
title TEXT Updated title
wm_instance TEXT WM_INSTANCE_NAME
wm_class TEXT WM_CLASS class
pid INTEGER Process ID
executable TEXT Executable path
app_id TEXT Derived application identifier

activity_title_segment — view for title-level reporting

Splits each active interval into contiguous title segments. Starts from initial title, ends each segment at next metadata change or interval end. Exposes duration_seconds per segment.

Exit codes

Code Meaning
0 Success
1 Runtime error (storage fail, dispatcher crash)
2 Configuration error (invalid path, bad dates, X11 init fail)

Notes

  • WAL mode: Database readable while daemon runs (concurrent reads safe)
  • No recovery: Unfinished intervals on crash are not recovered
  • Title throttle: --title-interval controls resolution. Shorter = more granular title tracking, more DB rows

📜 License

Project is licensed under the BSD-3-Clause license.

About

Rust X11 time tracker - CLI that gathers your focused window time and intervals

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages