Dynamic Wayland display and wallpaper manager
oxyshift is Wayland display manager that automatically switches display profiles based on connected monitors and manages wallpaper state across sleep/wake cycles.
oxyshift has not been tested with a range of monitors or laptops so it might still be buggy on diffrent systems. I just developed it to help me solve an persistent wallpaper issue with swww.
Tested only in Arch and Hyprland.
- Automatic Display Configuration: Detects connected displays and applies matching profiles
- Unified Wallpaper Management: Integrated swww support with persistent wallpaper state
- TOML Configuration: Modern, readable configuration format
- Hot Reload: Automatically reloads configuration when changed
- Memory Safe: Written in Rust for reliability and performance
- Async Design: Non-blocking event handling with Tokio
- Quickshell Integration: Shares wallpaper state with quickshell settings
Unlike other display managers (kanshi, shikane, way-displays), oxyshift provides unified wallpaper + display management:
- Wallpaper changes through quickshell settings persist across sleep/wake cycles
- Shared state file (
~/.local/state/quickshell/user/current-wallpaper) synchronized between components - Profile wallpapers respect user selections instead of overriding them
- Automatic wallpaper restoration when displays reconnect
git clone https://github.com/yourusername/oxyshift.git
cd oxyshift
cargo build --release
sudo cp target/release/oxyshift /usr/local/bin/- A Wayland compositor with
wlr-output-management-unstable-v1support for Hyprland swwwfor wallpaper management (optional but recommended)
Create a configuration file at ~/.config/oxyshift/config.toml:
# Single monitor setup
[[profile]]
name = "laptop"
wallpaper = "~/Pictures/Wallpapers/laptop.jpg"
exec = ["hyprctl notify -1 2000 'rgb(00ff00)' 'Laptop mode activated'"]
[[profile.output]]
name = "eDP-1"
mode = "1920x1080@60"
scale = 1.5
position = [0, 0]
# External monitor setup
[[profile]]
name = "docked"
wallpaper = "~/Pictures/Wallpapers/desktop.jpg"
exec = ["hyprctl notify -1 2000 'rgb(0000ff)' 'Docked mode activated'"]
[[profile.output]]
name = "eDP-1"
enabled = false
[[profile.output]]
name = "HDMI-A-2"
mode = "2560x1440@144"
scale = 1.0
position = [0, 0]
adaptive_sync = true
# Dual monitor setup
[[profile]]
name = "dual-monitors"
wallpaper = "~/Pictures/Wallpapers/ultrawide.jpg"
[[profile.output]]
name = "eDP-1"
mode = "1920x1080@60"
scale = 1.0
position = [0, 0]
[[profile.output]]
name = "HDMI-A-2"
mode = "2560x1440@144"
scale = 1.0
position = [1920, 0]name: Unique identifier for the profilewallpaper: Optional wallpaper path (can use~for home directory)exec: Commands to execute when profile is activated
name: Output name (e.g., "eDP-1", "HDMI-A-2", or "*" for wildcard)enabled: Whether the output is enabled (default: true)mode: Display resolution and refresh rate (e.g., "1920x1080@60")scale: Display scaling factor (e.g., 1.5)position: Position in global coordinates[x, y]transform: Display rotation ("90", "180", "270", "flipped-90", etc.)adaptive_sync: Enable adaptive sync (FreeSync/G-Sync)
# Start the daemo
oxyshift
# With custom config file
oxyshift --config /path/to/config.toml
# Test config without applying
oxyshift --test-only
# Enable debug logging
oxyshift --debugCreate ~/.config/systemd/user/oxyshift.service:
[Unit]
Description=oxyshift - Dynamic Wayland Display and Wallpaper Manager
Documentation=https://github.com/yourusername/oxyshift
After=graphical-session.target
Wants=graphical-session.target
PartOf=graphical-session.target
[Service]
Type=exec
ExecStart=/usr/local/bin/oxyshift --config %h/.config/oxyshift/config.toml
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
# Environment for Wayland
Environment=XDG_RUNTIME_DIR=/run/user/1000
Environment=WAYLAND_DISPLAY=wayland-1
# Process management
KillMode=mixed
TimeoutStopSec=30
# Security settings
PrivateNetwork=false
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=%h/.config/oxyshift %h/.local/state
NoNewPrivileges=true
[Install]
WantedBy=graphical-session.targetThen enable and start:
systemctl --user enable --now oxyshift| Feature | kanshi | shikane | way-displays | oxyshift |
|---|---|---|---|---|
| Language | C | Rust | C | Rust |
| Configuration | Custom | TOML | YAML | TOML |
| Wallpaper Management | ❌ | ❌ | ❌ | ✅ |
| Wallpaper Persistence | ❌ | ❌ | ❌ | ✅ |
| Hot Reload | Manual | ✅ | ✅ | ✅ |
| Async Support | ❌ | ✅ | ✅ | ✅ |
| Quickshell Integration | ❌ | ❌ | ❌ | ✅ |
cargo buildcargo testRUST_LOG=debug cargo run -- --debugoxyshift consists of several key components:
- Wayland Client: Communicates with compositor via
wlr-output-managementprotocol - Profile Manager: Matches connected displays to configuration profiles
- Swww Manager: Handles wallpaper application and state persistence
- Config Watcher: Monitors config file for hot-reload
- Event Loop: Async event handling for display changes, signals, and config updates
-
Ensure
swwwis installed and accessible:which swww swww --version
-
Check shared wallpaper state file exists:
cat ~/.local/state/quickshell/user/current-wallpaper -
Check oxyshift logs:
journalctl --user -u oxyshift -f
Run with debug logging to see profile matching:
RUST_LOG=debug oxyshift --debugOr test config without applying:
oxyshift --test-onlyContributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see LICENSE file for details.
- Original kanshi for inspiration
- shikane for demonstrating Rust implementation
- swww for Wayland wallpaper management
- autorandr for the X11 predecessor
- The Rust Wayland ecosystem (wayland-rs, smithay)
- quickshell for desktop shell integration
oxyshift = "oxy" (oxygen/Rust) + "shift" (display profile and wallpaper transitions)