Skip to content

docs: update README for Linux support#182

Open
cserby wants to merge 35 commits into
AprilNEA:masterfrom
cserby:docs/update-linux-support
Open

docs: update README for Linux support#182
cserby wants to merge 35 commits into
AprilNEA:masterfrom
cserby:docs/update-linux-support

Conversation

@cserby

@cserby cserby commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Dependencies

This PR must merge after the following PRs land on master, in order:

  1. PR feat(linux): launch_at_login + input device access permission check #172 — Linux launch_at_login + permission check (adds the systemd user unit and the Settings permissions row)
  2. PR feat(linux): install artifacts and docs #173 — Linux install scripts + docs (adds docs/INSTALL-linux.md and packaging/linux/)
  3. PR feat(linux): nfpm .deb/.rpm packaging and CI release job #179 — Native .deb/.rpm packaging (adds the package releases referenced in the Linux install instructions)
  4. PR feat(hid): Unifying receiver support #181 — Unifying receiver support (adds the Unifying receivers row to the roadmap)

The branch is rebased on top of PR #173 so docs/INSTALL-linux.md resolves in the tree.

Summary

  • Removes "Linux and Windows are coming soon" — Linux is now fully supported
  • Updates roadmap: Linux hook, Unifying receivers, button remapping, action catalog, DPI/SmartShift, launch-at-login all promoted to ✅; per-app profiles correctly marked 🟡 (X11 only); Windows hook left as ❌
  • Install section split into macOS and Linux subsections with .deb/.rpm download instructions and systemctl --user enable step
  • Footnote updated: media key actions use D-Bus MPRIS on Linux
  • Fixes "Bolt successor" label → "older protocol, replaced by Bolt" (Unifying predates Bolt)

Test plan

  • Verify all four dependency PRs are merged before merging this one
  • Read through roadmap entries and confirm they match the merged code
  • Verify Linux install steps work against a released .deb/.rpm
  • Confirm docs/INSTALL-linux.md link resolves after PR feat(linux): install artifacts and docs #173 merges

🤖 Generated with Claude Code

@greptile-apps

greptile-apps Bot commented Jun 8, 2026

Copy link
Copy Markdown

Greptile Summary

This PR delivers full Linux support: Unifying receiver enumeration, a systemd user-unit launch_at_login backend, Linux-aware permissions UI, .deb/.rpm packaging via nfpm, install/uninstall scripts, and updated README and install guide.

  • Unifying receiver: probe_unifying_receiver / probe_unifying_slot added alongside the Bolt path; DeviceRoute::Unifying variant added with a device_route_for factory that routes by PID list; hid-logitech-dj child nodes filtered on Linux to prevent timeout noise.
  • Packaging & CI: nfpm descriptor, xtask package-linux command, and a new linux-packages GitHub Actions job that builds .deb/.rpm and uploads them to the release; the nfpm SHA-256 hash stored in the workflow is 62 hex chars instead of the required 64, so sha256sum -c will reject it and block every build.
  • Docs: docs/INSTALL-linux.md is added but still contains a warning and a Known Limitations entry saying Unifying is unsupported and no pre-built packages exist, directly contradicting the code and README changes in this same PR.

Confidence Score: 4/5

Safe to review further, but the linux-packages CI job will fail on every run until the nfpm hash is corrected.

The nfpm SHA-256 hash in the workflow is truncated to 62 hex characters; sha256sum -c will refuse to parse it and the CI step will exit non-zero on every release trigger, preventing .deb/.rpm artifacts from being built or uploaded.

.github/workflows/release.yml (truncated SHA-256) and docs/INSTALL-linux.md (stale warning and limitations table).

Important Files Changed

Filename Overview
.github/workflows/release.yml Adds linux-packages CI job; the nfpm SHA-256 hash is truncated to 62 chars (needs 64), which will cause sha256sum -c to fail and block every release build.
docs/INSTALL-linux.md New Linux install guide; warning block and Known Limitations entry still say Unifying is unsupported and no packages exist, contradicting the code and README in the same PR.
crates/openlogi-hid/src/inventory.rs Adds probe_unifying_receiver / probe_unifying_slot alongside the existing Bolt path; uses UNIFYING_SLOT_PROBE per-slot timeout and product-ID fallback when receiver UID is unavailable.
crates/openlogi-hid/src/route.rs Adds DeviceRoute::Unifying variant and consolidates device_route_for factory using canonical BOLT_PIDS/UNIFYING_PIDS lists; defaults unknown PIDs to Bolt to avoid silent write drops.
crates/openlogi-hidpp/src/receiver/unifying.rs Adds event emitter, count_pairings, trigger_device_arrival, get_device_pairing_information, and get_unique_id to the Unifying receiver; introduces DeviceConnection / Event types.
crates/openlogi-agent/src/launch_agent.rs Adds Linux launch_at_login backend via a generated systemd user unit; escape_systemd_exec handles %/$/spaces; failures are logged but not propagated.
crates/openlogi-gui/src/platform/permissions.rs Adds Linux input_device_access() probe via /dev/uinput and /sys/class/hidraw/*/device/uevent; pure classify() function is separately tested without filesystem access.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[enumerate_hidpp_devices\nfilters child nodes on Linux] --> B[probe_one per device]
    B --> C{receiver::detect}
    C -->|Receiver::Bolt| D[probe_bolt_receiver\nget_unique_id\ncount_pairings\ndrain_device_arrival]
    C -->|Receiver::Unifying| E[probe_unifying_receiver\nget_unique_id\ncount_pairings\ndrain_device_arrival_unifying]
    C -->|None / other| F[probe_direct\nHID++ 0xff self-index]
    D --> G[probe_bolt_slot xN\nCacheKey::Bolt]
    E --> H[probe_unifying_slot xN\nCacheKey::UnifyingSlot\nper-slot UNIFYING_SLOT_PROBE timeout]
    G --> I[DeviceInventory]
    H --> I
    F --> I
    I --> J[device_route_for\npick Bolt / Unifying / Direct\nby UNIFYING_PIDS list]
    J --> K[DeviceRoute::Bolt\nDeviceRoute::Unifying\nDeviceRoute::Direct]
Loading

Fix All in Codex Fix All in Claude Code

Reviews (8): Last reviewed commit: "fix(docs): correct Unifying label; remov..." | Re-trigger Greptile

Comment thread README.md
Comment thread README.md Outdated
@cserby cserby force-pushed the docs/update-linux-support branch 3 times, most recently from c649d2f to 01271a4 Compare June 9, 2026 07:54
cserby and others added 6 commits June 10, 2026 08:42
Reconcile the agent's autostart state on Linux by writing/removing a
systemd user unit at $XDG_CONFIG_HOME/systemd/user/openlogi-agent.service.
Mirrors the macOS LaunchAgent semantics exactly:

- Restart=on-failure (crash respawns; clean exit(0) stays stopped)
- WantedBy=graphical-session.target (takes effect at next login)
- ExecStart escaped for systemd (% doubled, spaces quoted)
- Idempotent write/remove — only touches disk when content changes
- systemctl --user daemon-reload + enable/disable best-effort

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a Linux-specific permission probe and settings page row:

- probe_uinput(): checks write access to /dev/uinput
- probe_logitech_hidraw(): iterates /dev/hidraw*, confirms Logitech
  vendor (HID_ID sysfs field parsed numerically — 0000046D matches 046d)
- classify(uinput_ok, hidraw_ok): pure function → Granted/Denied/Unknown
- Settings → Permissions shows one "Input device access" row on Linux
  with description only when access is not yet granted (no noise when
  everything works)
- macOS permission rows and helpers gated #[cfg(target_os = "macos")]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Accessibility footer in the main window hidden on Linux
- "Open" button in permission rows gated to macOS only
- Launch-at-login description no longer says "log in to macOS"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove misplaced sysfs comment above the open() call in
  probe_logitech_hidraw (the sysfs check is in is_logitech_hidraw)
- Remove dead #[cfg(not(target_os = "macos"))] suppressor inside the
  already-macOS-gated permission_field function
- Split Denied/Unknown description text: Unknown means uinput is
  accessible but no Logitech device is connected, so point the user
  at the device rather than the udev install guide

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- escape_systemd_exec: double $ → $$ to prevent systemd variable
  substitution in ExecStart paths containing a literal dollar sign
- paths: add pub xdg_config_home() that returns the raw XDG config base
  without the openlogi sub-directory; refactor config_dir() to call it
- unit_path: use xdg_config_home() directly instead of relying on the
  fragile .parent() traversal from config_dir()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Added missing translations for the Linux permission row label introduced
in this PR. Follows the same pattern as 'Input Monitoring'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cserby cserby force-pushed the docs/update-linux-support branch 2 times, most recently from df0799c to c31703c Compare June 10, 2026 07:10
cserby and others added 8 commits June 10, 2026 09:14
… scope

- Add 'Unifying receiver' and 'Input device access' keys to en.yml
  (all locale files must match en.yml for the i18n test)
- Add 'Ricevitore Unifying' to it.yml for key parity
- Move InteractiveElement import outside #[cfg(target_os = "macos")]
  so on_action() calls for CloseWindow/Minimize/Zoom work on Linux
- Fix rustfmt line-wrap in launch_agent::unit_path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- udev/70-openlogi.rules: TAG+="uaccess" for hidraw (Logitech VID 046d)
  and uinput; includes non-systemd GROUP="input" fallback instructions
- systemd/openlogi-agent.service: packaged user-unit template for
  /usr/lib/systemd/user/ (complements the per-user unit written by
  launch_at_login)
- desktop/openlogi.desktop: XDG application launcher
- install.sh / uninstall.sh: POSIX sh, set -eu; copies binaries + all
  artifacts, reloads udevadm, best-effort systemd and icon cache updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
docs/INSTALL-linux.md covers: prerequisites, build from source, udev
rules (uaccess + non-systemd fallback), install.sh usage, autostart via
systemctl, verification steps, and known limitations table.

README.md gets a Linux subsection under ## Install with the minimal
quick-start (build + udev) and a link to the full guide.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: rewrite ExecStart in the systemd unit via sed so the
  installed service always points to $BINDIR/openlogi-agent, not the
  hardcoded /usr/bin path (which mismatches the default /usr/local prefix)
- uninstall.sh: add udevadm trigger after reload so hidraw and uinput
  nodes lose the uaccess tag immediately, not only after re-plug/reboot

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: escape sed replacement string for BINDIR metacharacters
  (& \ |) so paths like /opt/my&pkg don't corrupt the unit file
- install.sh: best-effort daemon-reload via sudo -u $SUDO_USER after
  writing the unit so reinstalls pick up the new ExecStart immediately
- uninstall.sh: use SUDO_USER to target the real user's systemd session
  when the script is run under sudo, preventing the disable from silently
  targeting root's session while the user's agent keeps running

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bluetooth HID devices go through the uhid virtual bus, which has no
idVendor sysfs attribute — ATTRS{idVendor}=="046d" doesn't match them.
Add a second rule matching on the HID kernel name format
"bus:VID:PID.iface", whose VID field (046D) covers both BT and USB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sudo -u strips the environment, so systemctl --user cannot locate the
user's D-Bus socket without XDG_RUNTIME_DIR. Without it, disable --now
in uninstall.sh silently fails (exit code 1, swallowed by || true),
leaving the agent enabled even after the binary is removed.
daemon-reload in install.sh has the same problem — it would silently
skip the reload, requiring a manual reload before the updated unit takes
effect.

Fix both by computing REAL_UID / INSTALL_USER and passing
XDG_RUNTIME_DIR=/run/user/<uid> explicitly to the sudo -u invocation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both install.sh (PREFIX/bin) and nfpm postinstall (/usr/bin) now
expand the placeholder explicitly, removing the implicit assumption
that the template's hardcoded path matches any particular installer.
Comment thread crates/openlogi-hid/src/inventory.rs Outdated
cserby and others added 8 commits June 10, 2026 09:16
nfpm.yaml maps the three binaries + udev rules + systemd user unit +
desktop entry + icon into .deb/.rpm. VERSION env var is templated in
at build time by the xtask.

nfpm-scripts/postinstall.sh: reload udevadm, refresh icon and desktop
caches, print enable-agent instructions.
nfpm-scripts/preremove.sh: reload udevadm to revoke uaccess tags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
xtask/src/linux.rs: PackageLinux command builds release binaries (unless
--no-build), then invokes nfpm for both deb and rpm. Reads the workspace
version from Cargo.toml and passes it as VERSION to nfpm.

.github/workflows/release.yml: new linux-packages job (ubuntu-latest)
installs nfpm via the goreleaser apt repo, runs `cargo run -p xtask --
package-linux`, and uploads .deb/.rpm as an artifact. The publish job now
depends on both dmg and linux-packages, downloads both artifact sets, and
attaches .deb/.rpm to the GitHub Release alongside the DMGs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace workspace_version() file-I/O + fragile TOML line-scanner with
  env!("CARGO_PKG_VERSION") — Cargo bakes the workspace version in at
  compile time, zero I/O and always correct
- Merge two separate apt-get update + install steps in the linux-packages
  CI job into one (add goreleaser repo first, then single update + install)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- nfpm.yaml: drop v prefix from version field — dpkg/rpm expect bare
  semver (0.6.0 not v0.6.0); v prefix can cause upgrade ordering issues
- nfpm.yaml: fix maintainer to Name <email> format required by Debian
- release.yml: replace trusted=yes with a proper GPG key pin for the
  goreleaser apt repo to prevent supply-chain substitution attacks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the goreleaser apt repo (which has no GPG key and requires
trusted=yes) with a direct download of a pinned nfpm release from
GitHub. The SHA256 is checked before install, preventing a compromised
CDN or MITM from substituting a malicious nfpm binary in the release
pipeline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
postinstall.sh / preremove.sh: udevadm trigger is asynchronous — it
queues uevent changes but returns before udev finishes processing them.
Without udevadm settle, a user who immediately runs
`systemctl --user enable --now openlogi-agent` after package install
may find /dev/hidraw* and /dev/uinput still denying access because
the uaccess tags haven't propagated yet. Add `udevadm settle || true`
after the trigger calls in both scripts.

release.yml: extend the minisign signing loop from dist/*.dmg to also
cover dist/*.deb and dist/*.rpm, so all release artifacts have a .minisig
sidecar and SHA256SUMS is consistent across all platforms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cserby and others added 9 commits June 10, 2026 09:17
xtask/src/linux.rs: PackageLinux command builds release binaries (unless
--no-build), then invokes nfpm for both deb and rpm. Reads the workspace
version from Cargo.toml and passes it as VERSION to nfpm.

.github/workflows/release.yml: new linux-packages job (ubuntu-latest)
installs nfpm via the goreleaser apt repo, runs `cargo run -p xtask --
package-linux`, and uploads .deb/.rpm as an artifact. The publish job now
depends on both dmg and linux-packages, downloads both artifact sets, and
attaches .deb/.rpm to the GitHub Release alongside the DMGs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OpenLogi previously only enumerated Logi Bolt receivers (PID 0xC548).
Users with a Unifying receiver (PID 0xC52B / 0xC532) saw no devices.

openlogi-hidpp/receiver/unifying.rs
  Add the missing HID++ 1.0 methods needed for device enumeration:
  count_pairings(), trigger_device_arrival(), get_device_pairing_information(),
  get_unique_id(). Add EventEmitter<Event> + message listener for 0x41
  device-connection notifications (same pattern as BoltReceiver). Add
  DeviceKind enum (shifted vs Bolt at values 5+: Remote=5, Trackball=6,
  Touchpad=7). Update receiver/mod.rs to call get_unique_id() directly.

openlogi-hid/transport.rs
  Add is_receiver_child_node() (Linux): the hid-logitech-dj kernel driver
  creates a virtual hidraw node per paired Unifying device; these matched
  our HID++ filter and caused 5-second probe timeouts every tick. Detect
  them by checking whether any known receiver PID appears as a *parent*
  directory component in the device's sysfs path, and filter them out
  before probing. Extract the path-matching logic to is_receiver_child_sysfs_path()
  for testability without filesystem access.
  Add BOLT_PIDS and UNIFYING_PIDS as the canonical PID lists; pairing.rs
  now derives from them so a new receiver PID needs editing in one place.

openlogi-hid/inventory.rs
  probe_one: replace Bolt-only match with explicit Bolt / Unifying / direct
  arms. Add probe_unifying_receiver (uses device-arrival events to surface
  online paired devices) and probe_unifying_slot (builds PairedDevice from
  the event; falls back gracefully on timeout). Add UNIFYING_SLOT_PROBE
  (3.5 s) per-slot budget so a slow wireless HID++ 2.0 round-trip on one
  device cannot consume the shared PROBE_BUDGET on behalf of others. Add
  drain_device_arrival_unifying and map_unifying_kind.

openlogi-hid/route.rs
  Add DeviceRoute::Unifying { receiver_uid, slot } — same structure as Bolt
  but distinct so the label and write path are correct. Add device_route_for()
  constructor that picks Unifying/Bolt/Direct from the receiver's product ID
  using the canonical PID lists. Update open_route_channel to handle the
  Unifying arm. Add BOLT_PIDS and UNIFYING_PIDS constants.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Consumers of the HID layer all constructed DeviceRoute::Bolt for any
receiver-backed device. This caused three problems for Unifying devices:
1. open_route_channel only tried Receiver::Bolt — writes silently failed.
2. The Device tab showed "Bolt receiver" for Unifying devices.
3. The Buttons tab showed the generic mouse hotspot layout for keyboards
   (K540 has ReprogControls so capabilities.buttons=true, but no asset).
4. Paired devices without HID++ 2.0 model info were skipped entirely by
   build_device_list, so the K540 never appeared in the carousel.

openlogi-agent-core
  device_order.rs: DeviceStableId::from_parts folds Unifying into the Bolt
  variant (same slot-based sort key regardless of receiver family, so the
  GUI carousel and agent agree on "first device").
  orchestrator.rs: call DeviceRoute::device_route_for() directly.

openlogi-cli/diag
  Use DeviceRoute::device_route_for() at both diag call sites.

openlogi-gui/app.rs
  route_label: add "Unifying receiver" arm for DeviceRoute::Unifying.
  tabs_for: gate the Buttons tab on `can_show_mouse_model` — a pointer-type
  device (Mouse/Trackball) or one with a resolved asset. A keyboard that
  exposes ReprogControls but has no asset would otherwise show the generic
  mouse hotspot layout (Middle Click, DPI Toggle, …) which is wrong.
  Update tabs_follow_capabilities_not_kind test to use kind=Mouse (the
  0x0005 kind-correction fix from AprilNEA#127 corrects mislabeled mice at probe
  time; the test scenario of kind=Keyboard + mouse-caps no longer arises).
  Add keyboard_without_asset_hides_buttons_tab test.

openlogi-gui/state/devices.rs
  build_device_list: remove the hard model_info guard. Devices without
  HID++ 2.0 model info (HID++ 1.0 devices, or probes that timed out)
  are now surfaced with a wpid-based config_key ("wpid{:04x}") so their
  settings persist across sessions; slot is the last-resort fallback.
  Call DeviceRoute::device_route_for() directly (one-liner wrapper removed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
inventory.rs — probe_unifying_slot now receives the receiver's unique_id
and incorporates its first 3 ASCII bytes into the CacheKey so two
Unifying receivers with a device on the same slot number use distinct
cache entries. The previous [0,0,0,slot] key was receiver-agnostic and
would hand receiver B's slot-1 query the cached ProbedFeatures built for
receiver A's slot-1 device (different model, different capabilities).

route.rs — device_route_for: make the Bolt-default design explicit in
the doc comment and add a tracing::debug for receivers whose PID is in
neither BOLT_PIDS nor UNIFYING_PIDS. Behaviour is unchanged — returning
None for unknown PIDs would silently drop writes for future Bolt variants
with new PIDs, which is worse than routing them as Bolt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
unifying.rs — #[derive(Clone)] copied msg_listener_hdl (u32) verbatim,
so dropping any clone called remove_msg_listener with the shared handle
and silently deregistered the listener for all surviving clones. The
next drain_device_arrival_unifying would return empty, making Unifying
devices vanish from inventory.

Replace the bare u32 with Arc<ListenerDropGuard>: every Receiver clone
shares the Arc; remove_msg_listener is called exactly once, when the
Arc's refcount hits zero (last clone dropped). The manual Drop impl is
removed — ListenerDropGuard::drop owns the cleanup.

inventory.rs — CacheKey::Bolt { unit_id: [uid[0..3], slot] } used only
3 ASCII bytes of the receiver serial as a prefix, so two receivers whose
serials share the same first three characters (common in Logitech batches,
e.g. "DA2699E1" and "DA2604F2") still collided on the same slot.

Add CacheKey::UnifyingSlot { receiver_uid: String, slot: u8 } keyed on
the full serial string + slot. Two Unifying receivers with a device on
the same slot number now have provably distinct cache entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Added missing translations for the route label introduced in this PR.
Placed alongside 'Bolt receiver' following the same pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BoltReceiver had the same Clone+Drop bug fixed for UnifyingReceiver in
the previous commit: #[derive(Clone)] copied msg_listener_hdl (u32)
verbatim, so the first drop of any clone deregistered the HID++ listener
for all surviving copies — subsequent drain_device_arrival calls would
return empty and Bolt devices would vanish from inventory.

Move ListenerDropGuard to receiver/mod.rs (pub(super)) so it is shared
by both bolt.rs and unifying.rs without duplication. Apply the Arc-wrap
fix to BoltReceiver identically: store Arc<ListenerDropGuard> instead of
a bare u32, remove the manual impl Drop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On timeout the fallback returned ProbedFeatures::default() even when a
prior successful probe was in the cache, causing battery, model name,
and capability flags to disappear from the carousel for that device.
Return the cached probe when available and emit Seen so the entry is not
counted toward eviction.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cserby cserby force-pushed the docs/update-linux-support branch from c31703c to b0c9bd5 Compare June 10, 2026 07:20
The function is only called from a #[cfg(target_os = "linux")] context,
making it dead code on macOS/Windows. Gate it with
#[cfg(any(target_os = "linux", test))] so the tests remain cross-platform
while silencing the dead_code lint in production builds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cserby cserby force-pushed the docs/update-linux-support branch from b0c9bd5 to 2a7cbfe Compare June 10, 2026 07:31
cserby and others added 3 commits June 10, 2026 09:51
Empty string fallback causes CacheKey collisions when two receivers
share the same slot and both fail get_unique_id(). Product ID is a
weaker but non-empty discriminant; a warning is logged so the
degraded isolation is visible.
- Remove "Linux... coming soon" — Linux is now fully supported
- Roadmap: mark Linux hook, Unifying receivers, button remapping,
  action catalog, launch-at-login, and localization as ✅ macOS + Linux
- DPI and SmartShift are platform-independent writes — drop the macOS qualifier
- Add Linux packaging row (udev, systemd, .deb/.rpm)
- Per-app profiles: ✅ macOS, 🟡 Linux X11 only (Wayland pending)
- Install section: add Linux install steps (.deb/.rpm + systemd enable)
  and link to docs/INSTALL-linux.md
- Update footnote: media actions use D-Bus MPRIS on Linux

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 'Bolt successor' → 'older protocol, replaced by Bolt' (Bolt is the
  successor to Unifying, not the other way around)
- Remove the stale build-from-source Linux section introduced by the
  install-scripts PR; the new .deb/.rpm section supersedes it
- Rebased onto install-script-and-docs so docs/INSTALL-linux.md exists
  in this branch's tree and the link resolves

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cserby cserby force-pushed the docs/update-linux-support branch from 2a7cbfe to 848f351 Compare June 10, 2026 07:51
- name: Install system dependencies and nfpm
env:
NFPM_VERSION: "2.46.3"
NFPM_SHA256: "d6417f99d5fa32bba7a4e007084615d3897651498c2e443118c26b9ec3b698a8"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Truncated SHA-256 hash breaks nfpm integrity check

The NFPM_SHA256 value is only 62 hex characters — SHA-256 requires exactly 64. sha256sum -c will reject the input entirely (no properly formatted SHA256 checksum lines found), causing the "Install system dependencies and nfpm" step to fail on every run and blocking the entire linux-packages job. Regenerate the hash with sha256sum nfpm_2.46.3_amd64.deb after downloading the file.

Fix in Codex Fix in Claude Code

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.

1 participant