- Rust 97.7%
- Nix 2.3%
| assets | feat: new icon | |
| src | refactor: tray icon | |
| .editorconfig | chore: editor config | |
| .envrc | refactor: various | |
| .gitattributes | refactor: various | |
| .gitignore | refactor: various | |
| build.rs | refactor: various | |
| Cargo.lock | refactor: tray icon | |
| Cargo.toml | chore: add license | |
| flake.lock | refactor: various | |
| flake.nix | refactor: various | |
| LICENSE-APACHE | chore: add license | |
| LICENSE-MIT | chore: add license | |
| README.md | docs: readme | |
| rustfmt.toml | refactor: various | |
ASUS Pixel Cleaner
A better automatic OLED "Pixel Cleaning" for the ASUS ROG Swift PG49WCD.
OLED panels run a maintenance routine (ASUS calls it "Pixel Cleaning") that compensates for uneven pixel wear. The
PG49WCD is supposed to run it automatically when the display is inactive, but I've found it extremely inconsistent, if
working at all. I got annoyed by the constant pop-up reminders to run it manually. The DisplayWidgetCenter app only
allows to run it manually too. I wanted something that would actually automatically do it when the computer is inactive.
I you've felt the same way, this tool is for you: it sits in the system tray and fires the cleaning automatically once the machine has been idle for a while.
How it works
The behaviour was reverse-engineered from ASUS's DisplayWidgetCenter (a .NET app). That app drives the monitor over
DDC/CI, the low-speed I2C channel that rides on the DisplayPort/HDMI cable. "Pixel Cleaning" turns out to be a
read-modify-write of the vendor VCP code 0xFD (ToggleSettings2): read the current value and write it back with bit
0x10 set.
This tool reproduces that write and wraps it in the housekeeping needed to make it reliable when you're away from the machine:
- A background watcher polls the system idle time and triggers cleaning once you've been idle past the configured threshold.
- If the display has gone to sleep, it forces a wake (the monitor needs a live video signal to accept the command), waits for the link to settle, and retries.
- It holds the video signal alive for the full cleaning cycle (~6 minutes on the PG49WCD) so the display-off timer can't cut the signal mid-routine, which the monitor treats as an abort.
- If it had to wake the display, it puts it back to sleep afterwards, unless you came back to the machine in the meantime.
- Cleaning is skipped while a fullscreen game, video, or presentation is active, and never runs more often than a configurable minimum interval (persisted across restarts).
Installation & usage
The app lives entirely in the system tray. Launch it and right-click the tray icon for the menu:
- Clean now runs the routine immediately, ignoring the minimum-interval guard.
- Pause / Resume auto-clean toggles the automatic trigger.
- Idle delay picks how long the machine must be idle before cleaning fires (2 to 60 minutes).
- Start on login registers the app to launch automatically.
- Open config folder opens the directory holding the config file and logs.
Note that this software has only been tested with the aforementioned model. It might work for other models, if you try it let me know!
Configuration
Settings are stored as JSON in the platform config directory (open it from the tray menu). Most options are reachable from the menu, but the file exposes a few extra ones:
| Key | Default | Meaning |
|---|---|---|
idle_minutes |
20 |
Trigger cleaning after the machine is idle this many minutes. |
min_interval_hours |
24 |
Never clean more often than this many hours apart. |
model_match |
"PG49WCD" |
Substring matched against the monitor's DDC/CI capabilities string. |
enabled |
true |
Master on/off for the automatic trigger. |
cleaning_hold_minutes |
7 |
How long to hold the video signal alive while the routine runs. |
Building from source
The project is a standard Cargo project:
cargo build --release
A Nix flake provides a dev shell with the pinned toolchain (nix develop).
Platform support
Note
Only Windows is supported at the moment. macOS and Linux support is planned for the future.
All platform-dependent code (DDC/CI access, idle detection, display power control, autostart, single-instance locking)
sits behind a set of traits, with a cfg-selected implementation chosen at compile time. Adding a new OS means
implementing those traits for it, and nothing in the core logic is Windows-specific.