Skip to content

Apply theme accent to OpenRGB devices on theme switch - #12199

Open
ekollof wants to merge 9 commits into
omacom:quattrofrom
ekollof:feature/openrgb-theme
Open

ekollof wants to merge 9 commits into
omacom:quattrofrom
ekollof:feature/openrgb-theme

Conversation

@ekollof

@ekollof ekollof commented Sep 16, 2026

Copy link
Copy Markdown

Theme switching retints ASUS ROG (asusctl) and Framework 16 (qmk_hid) keyboards, but devices managed by OpenRGB were left untouched. This adds OpenRGB to the theme engine.

  • New hidden command bin/omarchy-theme-set-openrgb: reads the theme accent from keyboard.rgb, probes openrgb --list-devices once, and applies a device-native gradient mode where one exists, falling back to static accent everywhere else (single broadcast when detection fails).
  • Wired into bin/omarchy-theme-set-keyboard, so it runs in the existing parallel post-theme batch with no template changes.
  • Silent no-op when openrgb or keyboard.rgb is missing (or the color is invalid); every OpenRGB call is timeout-bounded so a dead server never stalls the switch.

Verified live against a Logitech G512 + Razer Basilisk V3 (both flip to static accent, exit 0), plus new test/shell.d/openrgb-theme-test.sh (7 assertions, mocked openrgb). Full ./test/cli passes; ./test/shell shows only pre-existing environmental failures.

@ekollof
ekollof marked this pull request as ready for review September 16, 2026 22:55
@ekollof

ekollof commented Sep 16, 2026

Copy link
Copy Markdown
Author

Note that this only does keyboard and mice for now. My little laptop does not have RGB fans, but they could definitely be supported in the future with OpenRGB.

@shelbybark

Copy link
Copy Markdown

Tested on a Logitech G213 (046d:c336, OpenRGB 1.0rc3-3 from extra, Omarchy 4.0.4).

The G213 exposes Modes: [Direct] Off Cycle Wave Breathing — no Static — so the per-device fallback at L88 hits:

$ openrgb -d 0 -m static -c 6b8cae -b 100
Error: Mode 'static' not available for device 'Logitech G213'
$ echo $?
0

openrgb prints the error but still exits 0, so the || true never engages and the theme switch silently applies nothing to the keyboard.

openrgb -d 0 -m direct -c 6b8cae -b 100 works and applies correctly on the same device.

Suggestion: the Modes: line is already being parsed for gradient detection — checking it for Direct and preferring that over static when present would cover Direct-only devices. #7977 ran into the same thing on ENE DRAM / ASUS Aura ("Direct mode is load-bearing"), so it looks like a general pattern rather than a G213 quirk.

Happy to re-test a revision against the G213.

@ekollof
ekollof force-pushed the feature/openrgb-theme branch from cec9256 to d469461 Compare September 19, 2026 16:42
@ekollof

ekollof commented Sep 19, 2026

Copy link
Copy Markdown
Author

Thanks for the detailed report — the exit 0 on the rejected mode is a nasty detail, and you're right that the fix belongs inside the modes parse rather than exit-code handling (which can never fire here).

Pushed a revision on that basis (b5b50218): the modes list is now triaged per device — a documented Direct mode is preferred over static when the device has no Static mode, so direct-only hardware like the G213 gets -m direct, and devices exposing both (G512/Basilisk norm) stay on static exactly as before. Also covered by new tests on the exact Modes: [Direct] Off Cycle Wave Breathing shape you quoted.

Happy for you to re-test against the branch — and appreciated the pointer to #7977, which confirms Direct is generally the load-bearing mode and not a G213 quirk.

@shelbybark

Copy link
Copy Markdown

Re-tested against the G213 at d469461.

Single-device (G213 only): works. The script issues openrgb -d 0 -m direct -c a6bace -b 100 and the keyboard takes the colour. Thanks for the quick turnaround.

Two issues in the parse, though — one is a real regression on multi-device systems:

1. has_static leaks across devices. It's never reset per device in the awk, so once any Static-capable device is parsed, every later Direct-only device is also treated as having Static. Ran the script as-shipped with a stubbed --list-devices of two keyboards:

  • G512 listed first, G213 second → -d 1 -m static for the G213 (the original silent no-op)
  • G213 first, G512 second → -d 0 -m direct ✅

So a G512 + G213 rig gets the bug back depending on enumeration order. Fix: has_static = 0 at the top of the /^ Modes: / block.

2. The direct marker lands in mode, not is_direct. The awk emits id\t\tdirect, but IFS=$'\t' read treats consecutive tabs as a single delimiter (tab is IFS whitespace), so mode="direct" and is_direct="". The tag ends up in GRADIENT_MODE_FOR_DEVICE; the DIRECT_DEVICE branch is never taken. It works by accident because the gradient branch also passes the mode name through — the mocked tests can't distinguish the two. Emitting a placeholder for the empty field (e.g. id\t-\tdirect, with - treated as no-gradient) would make it land where intended.

Reproduction for #1, standalone:

$ printf '%s\n' "0: A" "  Modes: Static [Direct]" "1: B" "  Modes: [Direct] Off" | awk '<the Modes-parsing block>'
0
1          # ← no "1<TAB><TAB>direct" line for device 1

Happy to re-test again.

@ekollof

ekollof commented Sep 20, 2026

Copy link
Copy Markdown
Author

Both confirmed and fixed in 9615c141 (with fb5df529 tests) — thanks for the precise report, including the standalone repro.

  1. Leak fixed: the Static/Direct scan now resets per device, and the awk emits exactly one classification line per device (gradient, direct-only marker, or nothing for static — the latter was already covered by the bare id line). Your G512-first fixture now yields -d 1 -m direct for the G213, both orders covered in the tests.
  2. Marker fixed: the direct tag is emitted as id\t-\tdirect, so bash's IFS-whitespace splitting can no longer collapse the empty mode field and misfile direct as a gradient mode. Devices with both a gradient and Direct keep their single gradient apply (verified by a new assertion, since the old shape masked exactly this).

Also fixed while in here: my first revision of the != "-" read-loop guard dropped the empty-mode check and classified every static device as gradient — caught by the existing static test before pushing, and the guard is now -n plus not--.

Happy for a second re-test whenever convenient.

@ekollof
ekollof force-pushed the feature/openrgb-theme branch from fb5df52 to e124d37 Compare September 20, 2026 19:07
@shelbybark

Copy link
Copy Markdown

Re-tested at e124d37 — both fixes confirmed.

  • G512-first and G213-first fixtures both route the G213 to -m direct; the G512 stays on static in both orders.
  • Gradient + Direct device gets exactly one apply, in the gradient mode.
  • Real G213: -d 0 -m direct -c 9bb99f -b 100, colour applied, exit 0.

One small leftover: Direct-only devices are applied twice. The bare-id line pushes the device into DEVICE_IDS, and then the id\t-\tdirect marker line hits the else branch and pushes it again, so the loop issues -m direct for the same device back-to-back:

openrgb -d 0 -m direct -c 9bb99f -b 100
openrgb -d 0 -m direct -c 9bb99f -b 100

Harmless (idempotent) but it doubles the per-device time on the switch, and a "single apply" assertion for Direct-only devices would currently fail. Splitting the read-loop into three arms fixes it:

if [[ -n $mode && $mode != "-" ]]; then
  GRADIENT_MODE_FOR_DEVICE[$id]="$mode"
elif [[ $mode == "-" ]]; then
  DIRECT_DEVICE[$id]=1        # id already in DEVICE_IDS from the bare line
else
  DEVICE_IDS+=("$id")
fi

Static-only and gradient devices are unaffected (verified: one apply each).

Otherwise this looks ready from the G213 side — thanks for the fast iterations.

@ekollof

ekollof commented Sep 22, 2026

Copy link
Copy Markdown
Author

Good catch — fixed exactly as you suggested. The id\t-\tdirect marker line no longer pushes a second DEVICE_IDS entry; it only records DIRECT_DEVICE[$id]=1 since the bare id line already queued it.

Pushed in 27a3d80 with single-apply assertions added to openrgb-theme-test.sh (both orders). Suite is green here:

ok - Direct-only devices apply through direct mode
ok - Static detection does not leak across devices

Thanks for the fast re-tests on the G213 — let me know if the double-apply is gone on your end.

@shelbybark

Copy link
Copy Markdown

Confirmed fixed on my end — single apply for the G213 in every fixture I ran (both enumeration orders, gradient+Direct, direct-only, static-only), and the real hardware takes the colour on one -m direct call. All good from here.

Thanks for the quick turnarounds on all three rounds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants