focus-tracker
A cross-platform focus tracker for Linux (X11), macOS, and Windows that monitors window focus changes and provides detailed information about the currently focused window.
Features
- Cross-platform support (Linux X11, macOS, Windows)
- Real-time focus tracking with automatic deduplication
- Window information (title, process name, PID)
- Icon extraction with configurable sizes and bounded cache
- Async API with tokio
- Configurable polling intervals
- Graceful shutdown with stop signals
Installation
Add to your Cargo.toml:
[]
= "1.1.0"
= { = "1", = ["full"] }
Quick Start
Track focus changes using the async API with tokio:
use FocusTracker;
use Arc;
use ;
async
Configuration
Customize behavior with FocusTrackerConfig:
use ;
let config = builder
.poll_interval.unwrap // Faster polling (default: 100ms)
.icon // Custom icon size (default: 128)
.icon_cache_capacity.unwrap // Bounded icon cache (default: 64)
.build;
let tracker = builder.config.build;
Ignoring focus events
Each platform exposes its own ignore-rule set. When a rule matches a focused
window, the tracker suppresses the event entirely: no on_focus callback,
no dedup-state update, and no icon work. Rule sets for the other platforms
are accepted by the builder (so cross-platform consumers don't need cfg!
scaffolding) and silently unused.
An IgnoreRule combines a process-name predicate and a window-title
predicate. A rule matches when both predicates match; the set matches
when any rule does.
use ;
let config = builder
.linux_ignore_rules
.macos_ignore_rules
.windows_ignore_rules
.build;
Builder setters
IgnoreRule::builder() exposes two setters, both optional:
| Setter | Accepts | Default | Effect |
|---|---|---|---|
.process_name(s) |
any Into<String> |
ProcessNameMatch::Any |
matches the named process byte-exactly |
.window_title(m) |
a WindowTitleMatch |
WindowTitleMatch::Any |
restricts to titles matching m |
Pass WindowTitleMatch::Missing to match titleless windows (None or
Some("")), Present for any non-empty title, or Exact(t) for a
specific non-empty title. Omitting .process_name(...) matches every
process — useful for title-only rules like "ignore every titleless focus
event regardless of process":
use ;
let rule = builder
.window_title
.build;
Matching is strict
Names and titles are matched byte-exactly against the values emitted by
the platform — no case folding, no .exe stripping, no basename
normalization. Provide every spelling you want to suppress.
| Platform | Source of process_name |
Typical value |
|---|---|---|
| Linux | /proc/$pid/comm, falling back to resolved /proc/$pid/exe |
firefox (or a full path on fallback) |
| macOS | NSRunningApplication.localizedName() |
Firefox (localized, can differ by locale) |
| Windows | GetModuleBaseNameW of the process's main module |
firefox.exe |
A consequence: on Linux the comm name is capped at 15 bytes by the kernel, and the exe-fallback path includes the full filesystem path. If you need to ignore a process by both spellings, add a rule for each.
with_title_missing deliberately collapses None and Some("") into the
same "no title" category, because platforms disagree about which they emit
for a titleless window.
Examples
Run the included examples:
# Basic focus tracking
# Advanced example with icon saving and statistics
Platform Support
| Platform | Window System | Status |
|---|---|---|
| Linux | X11 | ✅ Full support |
| Linux | Wayland | ❌ Not supported |
| macOS | Cocoa | ✅ Full support |
| Windows | Win32 API | ✅ Full support |
Platform Notes
- Linux X11: Full support
- Linux Wayland: Not supported (technical limitations)
- macOS: Requires accessibility permissions
- Windows: Full support on Windows 7+
System Requirements
macOS
Accessibility permissions required. Grant in: System Preferences > Security & Privacy > Accessibility
Linux
X11 development libraries required (pre-installed on most distributions)
Windows
No additional requirements
API Documentation
For detailed API documentation, visit docs.rs/focus-tracker.
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.