Add ddcutil fallback for brightness on desktops without backlight device#5282
Add ddcutil fallback for brightness on desktops without backlight device#5282felipe3dfx wants to merge 1 commit intobasecamp:devfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a DDC/CI (ddcutil) fallback to omarchy-brightness-display so brightness keybindings work on systems without a /sys/class/backlight device (e.g., desktops/external monitors), and wires up install + migration hooks to grant i2c device access.
Changes:
- Extend
bin/omarchy-brightness-displaywith a DDC/CI fallback, including caching + debouncedsetvcp. - Add
ddcutilto base packages and introduce an install hook to create thei2cgroup + install a udev rule. - Add a migration to apply the same udev/group setup for existing installs.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
bin/omarchy-brightness-display |
Adds DDC/CI fallback path with caching, locking, unified OSD reporting, and debounced writes. |
default/udev/ddcutil-i2c.rules |
New udev rule to grant group access to /dev/i2c-* devices. |
install/omarchy-base.packages |
Installs ddcutil by default. |
install/config/ddcutil-i2c.sh |
Fresh-install hook to create i2c group, add user, and install udev rule. |
install/config/all.sh |
Runs the new ddcutil-i2c.sh hook during install. |
migrations/1776070000.sh |
Upgrade migration to install ddcutil and apply udev/group access for existing users. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
f73213b to
ab1183c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ab1183c to
220874f
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
XF86MonBrightnessDown/Upsilently fails on systems without/sys/class/backlight— desktops, NUCs, mini-PCs, and laptops where the driver doesn't register a backlight device (common on NVIDIA and some AMD hybrid setups — see #5067). This PR extendsomarchy-brightness-displaywith a DDC/CI fallback viaddcutil, so the same keybindings that work on laptops also work on external monitors, out of the box.Zero user setup: the Arch
ddcutilpackage already shipsmodules-load.d/ddcutil.conf(auto-loadsi2c-devat boot) and a60-ddcutil-i2c.rulesudev rule that grantsuaccessto display-class i2c buses. This PR leans on that — the migration just installs the package and kicks the module + udev for users whose current session was already running.What's in the PR
Script (
bin/omarchy-brightness-display)ddcutil-detected i2c buses, read/write VCP0x10, step all monitors together, report a unified percent toswayosd.$XDG_RUNTIME_DIR(300s TTL) so repeats skip the ~1 sddcutil detectand the per-monitorgetvcpround-trip.flock(not-n) serializes key repeats through the cache read-modify-write.-nsilently drops taps during the cold path and users feel it as "one of my three presses didn't register". The debounce worker does not inherit the lock (9>&-).setvcp— rapid taps coalesce into a singlesetvcpper gesture via a timestamp + atomic target-file + background worker (250 ms window). This is the same pattern KDE PowerDevil uses (D8626), for the same reasons:ddcutildocs) are minimized.--sleep-multiplier 0.04 --noverifyonsetvcpfor responsive interactive use.EPOCHSECONDS/EPOCHREALTIMEbuiltins — nodateforks on the hot path.Package + migration
ddcutiladded toinstall/omarchy-base.packages(alphabetical).migrations/1776070000.sh— upgrade path for existing users:omarchy-pkg-add ddcutilif missing.modprobe i2c-devso the current session can use DDC without a reboot.udevadm control --reload-rules && udevadm trigger --subsystem-match=i2c-devso the upstream package's uaccess rule applies to already-enumerated i2c buses in the active seat.set -eso a failingomarchy-pkg-addormodprobedoesn't get silently marked as applied byomarchy-migrate.No fresh-install hook is needed: pacman running during install drops
modules-load.d/ddcutil.conf, whichsystemd-modules-load.servicepicks up on the first boot into the installed system, and the bundled udev rule processes all i2c buses on that same boot — so first login comes up with/dev/i2c-*already ACL-tagged for the active seat user.Measurements
BenQ EX2780Q + Dell U2415, NVIDIA proprietary on Hyprland:
ddcutil detect)getvcpphysical floor, BenQ, multiplier 0.04setvcpphysical floor, BenQ, multiplier 0.04The 300–500 ms per VCP op is physical DDC/CI wire time — that is why the cache + debounce pattern matters: it keeps that time off the hot path.
Tested
swayosd: verified live.setvcpper gesture, monitor slews to the final value once: verified live.rm /run/user/$UID/omarchy-ddc-*): re-detects buses, re-reads state, one slow tap then warm path: verified live.Review iteration
An earlier revision of this PR shipped its own
default/udev/ddcutil-i2c.rules+ ani2cgroup +install/config/ddcutil-i2c.shthat copied the rule and added the user to the group (requiring a logout). The Copilot review oninstall/config/ddcutil-i2c.shandmigrations/1776070000.shpushed on thei2c-devmodule loading question, and digging into that surfaced that the Archddcutilpackage already ships both amodules-load.dentry and its own60-ddcutil-i2c.ruleswithTAG+="uaccess"— which is a strictly better approach than the group-based one (no logout, handled by systemd-logind, filtered to display-class buses viaATTRS{class}=="0x03*"). This revision leans on that instead. Net result: 3 files + 140 lines instead of 6 files + 169 lines, and no more logout notice.Related