atlas.monitors controls your monitors over DDC/CI — the channel the display already uses to talk to your computer. Brightness, contrast, input source, volume, power and any raw VCP feature, from a single command or an interactive TUI. Part of the Atlas Suite.
- 🎚️ Every VCP feature: Named shortcuts for the common ones, raw hex codes for the rest.
- ⌨️ Two modes: A full TUI when you run it bare, single commands when you don't.
- ➕ Relative and toggle:
brightness +10,input --toggle hdmi1,dp1— made for hotkeys. - 🛡️ Capability-aware: Values are checked against what the monitor actually declares, so an unsupported input fails with an explanation instead of being silently ignored.
- 💾 Profiles: Save a whole multi-monitor setup and switch back to it in one command.
- 🤖 Scriptable:
--jsonon every command, and an exit code that reflects partial failure. - 📦 Cross-platform: Windows and Apple Silicon macOS, with no cgo.
git clone https://github.com/fezcode/atlas.monitors
cd atlas.monitors
gobake buildBinaries land in build/. go build -o atlas.monitors . also works if you would rather
not use gobake.
Run it with no arguments for the interactive TUI:
atlas.monitorsOr give it a command and it does one thing and exits:
# What is connected?
atlas.monitors list
# Read
atlas.monitors brightness
atlas.monitors get contrast
atlas.monitors get 0x10
# Write
atlas.monitors brightness 40
atlas.monitors brightness +10 # every monitor, relative to its own value
atlas.monitors brightness 60% -m 2 # monitor 2 only
atlas.monitors set 0xE5 1 # any raw VCP code
# Inputs
atlas.monitors input
atlas.monitors input hdmi1
atlas.monitors input --toggle hdmi1,dp1Output is a plain aligned table, made to be piped:
$ atlas.monitors list
# MODEL DESCRIPTION DEVICE
1 GR93U Generic PnP Monitor \\.\DISPLAY1
2 OMEN by HP 27 Generic PnP Monitor \\.\DISPLAY2
$ atlas.monitors brightness -5
# MONITOR FEATURE VALUE
1 GR93U brightness 82 -> 77
2 OMEN by HP 27 brightness 50 -> 45The model name comes from the monitor's own capability string. Windows reports almost every display as "Generic PnP Monitor", which is no use for telling two apart.
Values are checked before anything is written, and the error says what the monitor will actually take:
$ atlas.monitors input composite1
# MONITOR FEATURE VALUE
1 GR93U input error: GR93U does not accept input = 5 (0x05); it reports
hdmi1 (0x11), hdmi2 (0x12), dp1 (0x0F), dp2 (0x10)-m / --monitor takes an index, part of a model name, or all. The default is all,
because dimming every screen at once is the usual intent.
atlas.monitors brightness 30 -m 2
atlas.monitors brightness 30 -m dell
atlas.monitors brightness 30 -m allatlas.monitors profile save night
atlas.monitors profile apply night
atlas.monitors profile list
atlas.monitors profile show night
atlas.monitors profile delete nightA profile captures brightness, contrast, volume, input and colour for every monitor it was saved from. Applying one matches monitors by identity, falling back to index, so it survives a reboot that reshuffled enumeration order.
atlas.monitors get brightness --json[
{
"monitor": { "index": 1, "model": "GR93U", "device": "\\\\.\\DISPLAY1" },
"feature": { "code": 16, "hex": "0x10", "name": "brightness", "kind": "continuous" },
"value": 80,
"max": 100,
"ok": true
}
]Exit codes: 0 success, 1 at least one monitor failed, 2 the command line was wrong.
Every targeted monitor produces a row whether it succeeded or not, so a partial failure is
visible rather than silent.
atlas.monitors caps # what this monitor declares
atlas.monitors codes # every feature this tool knows by namecaps is the authority. If a feature is not listed there, setting it will be refused
before anything is written.
Run with no arguments. Real output, two monitors:
ATLAS.MONITORS v0.1.0 2 monitor(s)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
① DISPLAYS
──────────────────────────────────────────────────────────────────────────────────────────
▸ 1 GR93U \\.\DISPLAY1
brightness ████████████████████░░░░ 82 / 100
contrast ████████████████████░░░░ 82 / 100
input dp1
2 OMEN by HP 27 \\.\DISPLAY2
brightness ████████████░░░░░░░░░░░░ 50 / 100
contrast ████████████████████░░░░ 100 / 120
──────────────────────────────────────────────────────────────────────────────────────────
↑↓ select · ←→ brightness · ↵ features · p profiles · r refresh · ? help · q quit
Press ↵ on a monitor for everything it supports:
② FEATURES
──────────────────────────────────────────────────────────────────────────────────────────
1 GR93U MCCS 2.1
▸ brightness ████████████████████░░░░ 82 / 100
contrast ████████████████████░░░░ 82 / 100
input dp1 → hdmi1 · [hdmi1] hdmi2 •dp1 dp2 ↵ apply
volume ████████████████████████ 100 / 100
power on
color-preset user1
red-gain ████████████░░░░░░░░░░░░ 50 / 100
──────────────────────────────────────────────────────────────────────────────────────────
↑↓ feature · ←→ adjust / choose · ↵ apply · ⇧←→ by 10 · tab next monitor · esc back
| Key | Action |
|---|---|
↑ ↓ / j k |
Select a monitor, or a feature in the detail view |
← → |
Adjust by 1 (brightness, contrast…) or choose a value (input, power…) |
Shift + ← → |
Adjust by 10 |
↵ |
Apply the chosen value |
↵ / l |
On the dashboard, open the monitor's features |
esc / h |
Cancel a choice, or go back |
tab |
Next monitor |
p |
Profiles — ↵ applies, s saves, d deletes |
r |
Re-read every monitor |
? |
Help |
q |
Quit |
On the dashboard, ← → adjusts brightness directly.
Continuous features (brightness, contrast, volume) apply as you press, so you see the panel
respond. Features with fixed values (input, power) work differently: ← → moves through
the values the monitor declares and shows the change it would make, and nothing is written
until ↵ confirms it. A stray keypress cannot switch away the screen you are looking at.
A DDC write takes around 60ms, far slower than key repeat, so writes are coalesced: one is in flight at a time and the newest value wins. Holding an arrow key tracks smoothly instead of queueing up seconds of writes.
| Form | Meaning |
|---|---|
40 |
Absolute |
0x11 |
Absolute, hex |
+10 / -10 |
Relative, clamped to the monitor's range |
60% |
Percentage of maximum (continuous features only) |
hdmi1 |
A named value — see atlas.monitors codes |
~/.atlas/monitors.json profiles
~/.atlas/monitors-caps.json capability-string cache
Reading a capability string costs up to two seconds per monitor, so it is cached. Pass
--refresh to re-read it.
- DDC/CI must be enabled in the monitor's own OSD menu. Many monitors ship with it off.
- Laptop built-in panels do not support DDC/CI. Only external displays do.
- Some KVMs, docks and adapters do not pass DDC/CI through.
- Switching input can black out your screen. If you switch a monitor to an input this computer is not on, switching back has to happen from the monitor's own buttons.
- DDC/CI is inherently flaky. Every read and write is retried before being reported failed.
| Platform | Status |
|---|---|
| Windows | Monitor Configuration API (dxva2.dll), no elevation needed |
| macOS (Apple Silicon) | IOKit IOAVService over raw I2C, reached via purego |
| macOS (Intel) | Not supported — needs the legacy IOFramebuffer path |
| Linux | Not supported — builds and reports so |
CGO is off on every target, so cross-compiled binaries are fully functional.
The macOS backend uses a private, undocumented Apple API — the same one m1ddc and
BetterDisplay rely on. It can break on a macOS update.
MIT
