# winshift
[](https://crates.io/crates/winshift)
[](https://opensource.org/licenses/MIT)
A cross-platform library for monitoring window focus changes.
## Features
- Native window focus tracking on Linux via X11 (reference implementation)
- macOS support via Accessibility API (requires app tracking workaround)
- Event-driven callback system
- Minimal overhead window monitoring
- Optional embedded active window info in callbacks (macOS)
Note: The app tracking functionality on macOS exists solely to work around
platform limitations for reliable window focus detection.
## Supported Platforms
- **Linux**: Native window focus tracking via X11 (reference implementation)
- **macOS**: Window tracking via Accessibility API workaround (requires app tracking)
- **Windows**: Planned (not yet implemented)
## Quick Start
```sh
cargo add winshift
```
See the [Examples](#examples) section for usage patterns.
## Examples
### Illustrative Usage Pattern
This simplified example shows the basic structure. For complete, working examples see the [examples/](examples/) directory.
```rust,no_run
use winshift::{FocusChangeHandler, WindowFocusHook};
use std::sync::Arc;
struct MyHandler;
impl FocusChangeHandler for MyHandler {
fn on_app_change(&self, pid: i32, app_name: String) {
println!("App changed: {} (PID: {})", app_name, pid);
}
fn on_window_change(&self, window_title: String) {
println!("Window changed: {}", window_title);
}
}
fn main() -> Result<(), winshift::WinshiftError> {
let handler = Arc::new(MyHandler);
let hook = WindowFocusHook::new(handler);
// Start monitoring (runs in current thread)
hook.run()?;
// On macOS, you can stop with:
// winshift::stop_hook();
Ok(())
}
```
### macOS: Embedded ActiveWindowInfo (no extra queries)
When enabled, macOS event callbacks also include the full `ActiveWindowInfo` so apps don’t need to call `get_active_window_info()` after each event. This reduces overhead and simplifies consumers like chronicle.
How to enable:
```rust
use winshift::{FocusChangeHandler, WindowFocusHook, WindowHookConfig};
struct Handler;
impl FocusChangeHandler for Handler {
fn on_app_change(&self, pid: i32, app_name: String) {
println!("app: {app_name} ({pid})");
}
fn on_window_change(&self, title: String) {
println!("win: {title}");
}
// macOS-only rich payloads
#[cfg(target_os = "macos")]
fn on_app_change_info(&self, info: winshift::ActiveWindowInfo) {
println!("app+info: {} pid={} exe={} wid={} bounds=({},{} {}x{})",
info.app_name, info.process_id, info.proc_path, info.window_id,
info.bounds.x, info.bounds.y, info.bounds.width, info.bounds.height);
}
#[cfg(target_os = "macos")]
fn on_window_change_info(&self, info: winshift::ActiveWindowInfo) {
println!("win+info: '{}' exe={}", info.title, info.proc_path);
}
}
fn main() -> Result<(), winshift::WinshiftError> {
winshift::env_logger::init();
let mut cfg = WindowHookConfig::default();
cfg.embed_active_info = true; // opt-in to richer payloads
let hook = WindowFocusHook::with_config(Handler, cfg);
hook.run()
}
```
See `examples/embed_info_monitor.rs` for a complete example.
**Important Notes**:
1. Handler must be thread-safe (use Arc/RwLock if needed)
2. On macOS, call `stop_hook()` to clean up
3. See [examples/](examples/) for complete implementations
4. Linux uses same API but doesn't require combined tracking
5. Note: Implementation uses unsafe code and assumes single-threaded usage for now
6. Embedded info callbacks are currently macOS-only; other platforms still receive the base callbacks
## Platform Notes
### Linux (Reference Implementation)
- Pure window focus tracking via X11 protocol
- No app tracking required
- Tested with common desktop environments (GNOME, KDE, etc.)
### macOS Requirements
- Requires Accessibility permissions
- Enable in System Preferences > Security & Privacy > Privacy > Accessibility
### Windows
- Not yet implemented (planned for future release)
## API Documentation
Full API documentation is available via `cargo doc --open`.
## Contributing
Contributions are welcome! Please open issues or pull requests on GitHub.
## License
MIT - See [LICENSE](LICENSE) for details.