Add DS4 Color plugin (Hy4ri/ds4-color)#52
Merged
Conversation
Wraps the ps4-colors CLI (github.com/Hy4ri/ps4-colors) as a Noctalia plugin: bar button + color panel (native picker, hex field, presets) that sets the DualShock 4 lightbar via the ps4-colors binary. NOTE: thumbnail.webp still pending (generated in noctalia thumbnail generator) — CI will flag the missing asset until added.
960x540 WebP, deep crimson (#990000) to match the plugin accent.
- widget: left-click applies the saved color via service; right-click opens panel - service: add 'save' IPC (persist without touching controller) - panel: Save (persist) + Apply (set now) buttons - translations: add 'save' key
The plugin now writes the HID output report to /dev/hidrawN directly via noctalia.writeFile. USB report 0x05 (32B) and Bluetooth report 0x11 (78B) with CRC32 are built and the CRC verified against the kernel algorithm. Device detection scans /sys/class/hidraw uevent for Sony DS4 PIDs. No external binary or library required.
Move preset-click handler registration into onOpen (after presets are
loaded) instead of module top-level, where ipairs(nil) threw and aborted
the whole panel entry. Guard presets with 'or {}'. Replace unsupported
width='100%' string with a numeric width on the swatch.
Remove border='primary' (unknown value -> whole tree abort) and wrap=true. Swatch now uses border='outline' + borderWidth to show selection, mirroring oldirtty/color_picker. Aligns every ui.* node with a known-good panel.
- Resolve pluginDataDir lazily + nil-guard lastFile() so the service entry actually loads (top-level concat on nil threw -> no onIpc -> no apply -> no notification, which is why the color never changed). - setLightbar tries noctalia.writeFile, falls back to a python3 O_WRONLY binary write for the /dev/hidrawN char device (writeFile is file-oriented).
Noctalia's Lua is strict (PUC-Rio semantic), not Luau: '&', '|', '~', '>>' are syntax errors -> service failed to load at crc32_init -> no apply. Added band/rshift/bxor/bnot32 math helpers; CRC32 + BT report now use them and still produce the verified CRC 523bce6f.
Noctalia's _G is immutable at runtime, so assigning _G['onPreset'..i] in onOpen threw 'attempt to modify a readonly table' and aborted the panel. Presets are a fixed list, so define onPreset1..9 as plain module globals calling a shared applyPreset(i). Remove dead makePresetHandler.
'textAlign' is a label prop, not an input prop; Noctalia ignored it with a [WRN] ui-tree log line. Input defaults to left align anyway.
'controller' glyph was missing; swap to device-gamepad-2 for both the plugin catalog icon and the bar widget default glyph.
Rename plugin id, display name, bar glyph default, translation key (ps4_colors -> ds4_color) and all internal IPC references. Drop the stale ps4_colors_missing string (plugin has no external binary dependency). Keep accurate comments crediting the original ps4-colors C source.
…tch) Save/Apply buttons were already the last row (bottom of the column); this just compacts the overall panel height.
…[a-z0-9._-]*$) 'Hy4ri' -> 'hy4ri' in id, author, and all internal IPC references.
Contributor
The plugin declares no dependencies and documents that none are required, but its HID write fallback invokes python3. If the direct write fails, systems without Python cannot apply the color. Declare and document python3, or remove the external fallback.
The value loaded from last.json is not validated before entering shared state, then is inserted between single quotes in commands executed through /bin/sh -c. A crafted persisted value can escape the quotes and execute arbitrary shell commands when the widget or panel action is used. Validate loaded values as exactly six hexadecimal characters and avoid unsafe shell interpolation. |
ItsLemmy
marked this pull request as draft
July 18, 2026 23:39
Reviewer flagged: plugin declared dependencies=[] but the hidraw write fallback invokes python3, so systems without Python cannot apply the color if the direct writeFile fails. Declare 'python3' in dependencies, note it in the manifest description, and document the requirement in README Requirements.
Reviewer flagged: last.json value entered shared state unvalidated, then got
single-quote-interpolated into /bin/sh -c via runAsync -> shell injection.
- Add isHex6() (exactly ^[0-9a-fA-F]{6}$) and enforce it at: loadLast
(poisoned last.json), onSubmitHex, onApply/onSave (from state), onIpc
apply/save, and the apply()/save() service funcs.
- Since only 6 hex chars can reach runAsync, the single-quote interpolation
is provably safe (no quote-breaker / metachar can survive).
- Also fixes a latent bug: the old %x regex captured only 5 hex, rejecting
valid 6-char colors like 990000.
Hy4ri
marked this pull request as ready for review
July 19, 2026 02:05
Removed mention of 'hidraw write fallback' from the description.
Contributor
|
Thanks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Plugin
Hy4ri/ds4-colorplugin.toml)What it does
Sets the DualShock 4 lightbar color from a Noctalia bar button and a color panel. Left-click the bar button applies the last saved color directly; right-click opens a panel with a native color picker, a hex field, and 9 presets. Save persists the chosen color (so left-click reapplies it later) without touching the controller; Apply sets the lightbar immediately and also saves it.
The entire DualShock 4 HID output-report protocol is implemented in pure Lua — no external binary or library. The plugin detects controllers by scanning
/sys/class/hidraw/*/device/ueventfor Sony VID054c/ PIDs05c409cc0ba0, builds the USB report (0x05, 32 B) and Bluetooth report (0x11, 78 B with CRC32), and writes them to/dev/hidrawN.External dependencies
None.
dependencies = []inplugin.toml. The final byte write uses Noctalia'swriteFile, with apython3O_WRONLYfallback for the char device (onlypython3is used, no downloaded/remote code).Testing
Manually exercised: bar widget left-click applies saved color; right-click opens panel; preset click + Apply sets the lightbar on a connected DS4 (USB and Bluetooth); Save persists without writing; IPC
noctalia msg plugin Hy4ri/ds4-color:service all apply <hex>confirmed.Screenshots / Videos
thumbnail.webpis included (panel screenshot).Checklist
idafter the/inplugin.tomlexactly (ds4-color/).plugin.toml,README.md,thumbnail.webp, andtranslations/en.json.README.mdfollows the README template, documents every entry id and dependency, and includes exact panel IPC commands.thumbnail.webpwith the thumbnail generator (used a screenshot conversion instead — see note above).versionfollows semver (0.1.0) and is bumped in this PR;plugin_apiis the oldest API level this plugin requires (3).en.jsonshipped; no machine translations).catalog.toml; CI generates it.ds4-color/).Code review attestation
Plugins run as trusted, unsandboxed Luau in the user's session. Confirm:
python3spawned for the raw hidraw write; no network calls).licensedeclared inplugin.toml(MIT).