Skip to content

Hold-modifier escalation, BlueZ input-plugin install step, About version#24

Merged
LordVicky merged 24 commits into
mainfrom
dev
Jul 15, 2026
Merged

Hold-modifier escalation, BlueZ input-plugin install step, About version#24
LordVicky merged 24 commits into
mainfrom
dev

Conversation

@LordVicky

@LordVicky LordVicky commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Trigger modifiers: hold escalation (hardware-verified)

Multiple trigger-held-over modifiers on one trigger now escalate — e.g. one feel past 200ms, another past 300ms. Previously the first matching modifier shadowed everything below it, and all hold modifiers shared one timer that could be reset by a sibling's failing threshold check. Every modifier now runs its own timer and the longest satisfied hold wins; non-hold conditions keep first-match priority, so existing profiles behave identically. Verified on hardware.

Installer: BlueZ input plugin

vds needs raw ownership of the controller's Bluetooth HID channels; with BlueZ's input plugin active the system claims the DualSense first and the app never sees it. The installer now bundles vds's override-bluetoothd.sh and applies disable-input as an install step (skipped when already in effect). The NixOS flake gets the same via services.opends5.disableBluetoothInputPlugin (default on). Trade-off documented in README/INSTALLER.md: other BT input devices don't work while active.

About: app version

Settings → About now shows the running app version (new app:version IPC).

Game profiles: Game Path editor

Game profile detail view gains a Game Path button (below Delete) that opens an overlay for manually setting the profile's process-name match. It reuses the trigger-profile Process Names system — comma-separated input plus the "Detect running game" candidate popover — and saves through saveTriggerProfile, preserving the profile's meta, effects and settings. Useful when auto-detection misses the game's executable or it changes between updates.

Installer: kernel-variant headers for gaming/performance kernels

The header-package resolver previously collapsed every CachyOS kernel to linux-cachyos-headers, hardcoded kernel-default-devel on openSUSE, and passed a dead LLVM=1 arg that dkms silently ignores. It now keeps the full kernel-variant suffix and resolves the matching package per distro:

  • Arch family: all CachyOS variants (linux-cachyos-bore-headers, -hardened, …), plus new XanMod (linux-xanmod-headers) and Liquorix (linux-lqx-headers) support alongside zen/hardened/rt/lts.
  • Fedora family: CachyOS COPR variants (kernel-cachyos-lts-devel, -lto, -rt — hyphenated even where the uname runs them together) and Nobara's kernel-fsync-devel.
  • openSUSE: devel package follows the uname flavor (kernel-rt-devel, kernel-longterm-devel).
  • Debian family: unchanged — exact-uname headers already cover XanMod/Liquorix/HWE.

The dead LLVM=1 path and detect_clang_lto are removed; clang/LTO kernels are handled by the module Makefile's own compiler auto-detection from the headers' compile.h. Wiki gains a Prerequisites page documenting supported distros/kernels and requirements.

Docs

README header now links the Discord server.

541 companion tests + installer suite (123 asserts, 12 files) green.

LordVicky and others added 24 commits July 14, 2026 18:23
The settings menu's About row now reads "OpenDS5 <version>" via a new
app:version IPC channel, so users can finally tell which release they
are on without checking the AppImage filename.
…roller

vds needs raw ownership of the DualSense's Bluetooth HID L2CAP channels;
with BlueZ's input plugin active the system claims the controller as a
plain gamepad and the app never reads it. This worked on the dev machine
only because the override had been applied by hand during vds bring-up.

- installer: bundle vds's override-bluetoothd.sh and run disable-input
  --restart as a userspace step (skipped when the override is already in
  effect, so re-runs stay idempotent)
- flake: hardware.bluetooth enabled by default and a new
  services.opends5.disableBluetoothInputPlugin option (default true)
- docs: trade-off documented in README and INSTALLER.md — other Bluetooth
  input devices stop working while the override is active (upstream vds
  limitation), with the enable-input revert command
…s and settings profiles

The new default landing page is an Nvidia-App-style cover grid where every
tile is an existing trigger profile. Opening a game lands on its hub with a
Game Settings / Global Settings scope toggle: scope is switched by selecting
game-owned controller/button-remap profiles (id game:<trigger-profile-id>),
so every existing settings tab edits the game's snapshot through the normal
profile live-sync — no per-page forks, and the trigger profiles system keeps
working standalone.

- GameSettingsCoordinator (main): follows the trigger engine's detection to
  select a game's profile pair while it runs and restore the user's global
  selections after, with a persisted restore point for crash recovery and a
  guard so manual mid-game profile picks win over the restore.
- SettingsStore: ensure/remove helpers for caller-keyed controller and
  button-remap profiles that never disturb the selection.
- GameArtworkStore (main): SteamGridDB cover search/fetch with the user's own
  API key (new steamGridDbApiKey setting), disk-cached per profile with an
  index, served to the renderer as data URLs; tiles fall back to a generated
  gradient monogram.
- Renderer: Game Profile tab (grid + Now Playing hero + per-game hub), scope
  banner across all tabs while a game is open, Add Game dialog with running-
  process detection, cover art dialog, delete flow that cleans up the game's
  settings pair and artwork; game-owned profiles hidden from the generic
  profile dropdowns.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYWhFB6zQEduyqf96DM5vY
…alidator

The validator-sync CI gate pins protocol.ts byte-for-byte to the copy vendored
in OpenDS5-Profiles. The game settings id helpers are app plumbing, not part of
the validation contract, so they move to their own shared/game-settings.ts and
protocol.ts returns to the published version.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYWhFB6zQEduyqf96DM5vY
…ng the rest

Two defects kept a second trigger-held-over modifier from ever firing:
resolveSlot returned the first match, so once a 200ms hold matched a
300ms one below it was unreachable; and all hold modifiers shared one
heldSinceMs timer, letting one modifier's failing threshold check reset
another's progress.

Every modifier is now evaluated on every update with its own hold timer,
and the most specific match wins: longest satisfied hold first, list
order breaking ties — so profiles that relied on first-match priority
between non-hold conditions behave exactly as before.
…cse39

Game Profile tab: per-game hub over trigger profiles and settings profiles
…s, keyless covers

Adding a game now consults the OpenDS5-Profiles catalog instead of always
minting a blank trigger profile: a published profile installs as-is
(verified preferred, typed process names merged in), a native game keeps
the default trigger feel with custom trigger settings disabled (NATIVE
badge and chip on the tile, explanatory card in place of the editor),
and anything else starts with no custom triggers.

Deleting a game's trigger profile from the Trigger Profiles tab now
strips the effects and library linkage but keeps the Game Profile —
settings, artwork and detection ride on the same entry and survive.
Full deletion stays on the Game Profile page.

Cover art drops the SteamGridDB API-key requirement: creation auto-
fetches through the Bottles steamgrid proxy the way Heroic Games
Launcher does (one request, no account), with the key-based API kept
as a fallback and for manual browsing.
native.json entries now carry features.triggers/haptics/lightbar (from
PCGamingWiki, generated in OpenDS5-Profiles); the app reads them and
flags exactly what a game drives itself: the Native chip moves from a
blanket mark to per-card — Adaptive Triggers stays disabled only when
triggers are native, Haptics and Lighting cards gain their own Native
chips and copy, and tiles show Native triggers / Haptics / LED chips.
Legacy feature-less entries read as triggers-only, so the app behaves
identically until the annotated list ships.
The Game/Global settings toggle awaited the full profile-selection
round-trip to the daemon before the UI updated, so it read as dead for
seconds. The coordinator now flips the scope in the returned status
immediately and settles selections on its queue behind a follow-up
status event; applyTarget reads editingProfileId at run time, so rapid
toggles converge on the last state.

The detail-page poster now navigates back to All Games (cover changes
stay on the Cover Art button).
The banner on other tabs read as an overlay: borderless back text on
the left while the detail page puts a bordered back button on the
right. It now mirrors the detail heading — game title and saving copy
stacked left, scope toggle and a bordered 'Game Profile' back action
right, flat divider instead of the accent strip.
The scope pill derives from gameSettingsStatus, which only updated
after the enterGameSettingsScope round-trip — so opening a game card
showed Global Settings active and visibly jumped to Game Settings.
Flip the status optimistically on open and on toggle; the IPC result
and pushed status events confirm it.
Both back actions (All Games on the detail page, Game Profile in the
scope banner) drop the boxed heading-action look for a circled accent
arrow with a bare uppercase label, so back navigation reads as a step
back rather than one more action button. The disc fills and nudges
left on hover.
Back actions become a chevron with a stacked 'Back to / destination'
label inside one softly bordered box (design E from the prototype
round). Mouse thumb buttons now walk a small in-app history over
(control tab, open game hub): back re-opens where you were, forward
returns — recorded on tab switches and game hub open/close, capped at
50 entries.
Read the just-published release back and hand its title/body/url to
github-releases-to-discord via its manual inputs, since GITHUB_TOKEN-created
releases don't fire the release:published event a standalone notifier needs.
Webhook lives in the DISCORD_WEBHOOK repo secret.
The Add Game dialog gains a 'From your libraries' picker fed by a new
on-disk scanner (no store APIs, no logins): Steam via libraryfolders.vdf
and appmanifest ACFs across all library folders and flatpak roots, with
tooling (Proton, runtimes, redistributables) filtered and covers pulled
from all three librarycache layouts; Heroic via its legendary/GOG/
sideload caches, with launcher-declared executables taken as ground
truth. Executable candidates are ranked (name similarity, size, junk
penalties, Unreal-depth scan) and shown as pre-ticked checkboxes;
creation feeds the existing pipeline so OpenDS5-Profiles matching and
native flagging still apply, and launcher artwork wins over the keyless
proxy. Scan is cached per session and games with existing profiles are
filtered main-side.

Verified against the real machine: 11 games, zero errors, covers on
every Steam title. Implements docs/superpowers/specs/2026-07-15-
installed-games-import-design.md (depth cap revised to 4).
'Ghost of Tsushima DIRECTORS CUT' (a Heroic sideload name) failed to
match the catalog's "Ghost of Tsushima Director's Cut" over one
apostrophe, so the game lost its native flags. Normalization now strips
punctuation on both sides; the installed-games filter in main mirrors
it so scanned entries hide behind existing profiles the same way.
Adding a game (native or otherwise) creates its Game Profile record on
the trigger-profile store, but with no effects it rides the Default
feel — listing it in the Trigger Profiles strip read as 'adding a game
created a custom trigger profile'. The strip now hides game-owned
profiles without effects, except while one is selected for editing,
which is how a non-native game gets its first effects.
The tile of the game currently detected gets an accent border with a
soft breathing glow (static under prefers-reduced-motion). Deleting a
game whose profile carries custom trigger effects now warns and offers
a choice: Delete Everything, or Keep Trigger Profile — which strips the
game identity (settings, artwork, tile) but leaves the effects as an
ordinary trigger profile that still applies when the game runs.
Add a 'Game Path' button below Delete in the game profile detail view that
opens an overlay for manually setting a game's process-name match, reusing the
same comma-separated input and 'Detect running game' flow as trigger profiles.
Saves through saveTriggerProfile, preserving the profile's meta and effects.
CachyOS variant kernels (linux-cachyos-bore, -lts, -hardened…) previously all
collapsed to flavor 'cachyos' and got linux-cachyos-headers, the wrong package
for anything but the default kernel. Keep the full variant suffix instead and
derive the package from it: linux-<variant>-headers on Arch, hyphenated
kernel-cachyos-*-devel for the Fedora COPR unames that run the variant
together (cachyoslts1.fc44). SUSE now follows the uname flavor suffix
(kernel-<flavor>-devel) instead of hardcoding kernel-default-devel; Debian
already used the exact uname and needs nothing.

Also drop the LLVM=1 arg from the dkms install step and the now-unused
detect_clang_lto: dkms ignores trailing VAR=1 args ('I do not know how to
handle LLVM=1'), and the module Makefile already auto-detects clang kernels
from the headers' compile.h.
Add flavor detection for the remaining popular gaming/performance kernels:
xanmod and lqx on Arch (linux-xanmod-headers, linux-lqx-headers) and fsync on
Fedora/Nobara (kernel-fsync-devel from the sentry COPR). Debian-family kernels
including XanMod and Liquorix already resolve via the exact uname headers
package.
@LordVicky
LordVicky merged commit 438e602 into main Jul 15, 2026
2 checks passed
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