diff --git a/DECISIONS.md b/DECISIONS.md new file mode 100644 index 0000000..24de09e --- /dev/null +++ b/DECISIONS.md @@ -0,0 +1,19 @@ +# Decisions + +## 2026-04-09 – License: GPLv3 instead of MIT +- **Who**: Dom, F.R.I.D.A.Y. +- **Why**: Plan to support multiple headset protocols using HeadsetControl (GPLv3) as reference for protocol implementations. HeadsetControl's code is community-tested — reimplementing blind without hardware to test against is impractical. GPL copyleft is acceptable since the tool is open source on GitHub anyway. +- **Tradeoffs**: MIT would allow proprietary use, but we gain access to tested protocol implementations. GPL has no practical downside for a CLI tool distributed as source. +- **How**: GPLv3-or-later in Cargo.toml and LICENSE file. + +## 2026-04-09 – Tool name: must avoid "Corsair" trademark +- **Who**: Dom, F.R.I.D.A.Y. +- **Why**: "Corsair" is a registered trademark. Publishing under `corsairctl` risks trademark issues. "Bragi" is also risky (Bragi GmbH, Danish audio company). +- **Tradeoffs**: `headsetctl` is descriptive and follows Linux naming conventions (`bluetoothctl`, `brightnessctl`). Less specific but safer. +- **How**: Rename TBD — to be decided as part of the publish plan. + +## 2026-04-09 – Multi-protocol architecture using HeadsetControl as reference +- **Who**: Dom, F.R.I.D.A.Y. +- **Why**: Each headset vendor uses a proprietary HID protocol. No common standard exists. HeadsetControl already has tested implementations for SteelSeries, Logitech, HyperX, Corsair, etc. +- **Tradeoffs**: Porting C code to Rust takes effort, but the protocol logic is the hard part (byte sequences, timing, quirks) — and that's already documented in HeadsetControl's source. +- **How**: Modular backend architecture — each protocol as a separate module behind a common trait. Bragi is the first backend; others follow. diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..d0f33e6 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,152 @@ +# Plan: Publish as Multi-Protocol Headset Tool + +## Status: DRAFT — awaiting review + +## Goal + +Publish `corsairctl` as an open-source, multi-headset CLI tool under GPLv3. +Rename away from "Corsair" trademark. Support additional headset protocols +using HeadsetControl (C, GPLv3) as reference. + +--- + +## Phase 1: Rename & License (no functional changes) + +1. **Pick a new name** — candidates: + - `headsetctl` (clear, follows Linux conventions: `brightnessctl`, `bluetoothctl`) + - `hsetctl` (shorter, less likely to collide) + - `openheadset` (explicit open-source angle) + - Decision needed before anything else — name affects crate name, binary, repo, docs + +2. **License file** — ✅ Done (GPLv3 LICENSE + Cargo.toml updated) + +3. **Rename crate & binary** — update `Cargo.toml` name, all references in code, + CLAUDE.md, README, udev rules, Waybar wrapper, PKGBUILD + +4. **Add SPDX headers** — GPLv3 requires license notice in each source file + +5. **Clean up repo** — there's a nested `src/corsairctl/` directory that looks like + an old copy of the entire repo (has its own `.git/`, `Cargo.toml`, `CLAUDE.md`). + Investigate and remove if it's dead weight. + +--- + +## Phase 2: Abstract device layer (architecture) + +Current state: `BragiDevice` directly uses `hidapi` and the Bragi protocol. +Everything is Corsair-specific (VID, PIDs, handshake). + +Target state: a trait-based backend system so multiple protocols can coexist. + +``` +src/ +├── backend/ +│ ├── mod.rs // Headset trait + DeviceInfo struct +│ ├── bragi.rs // Corsair Bragi (current code, extracted) +│ ├── steelseries.rs // future +│ └── logitech.rs // future +├── hid.rs // generic HID helpers (send/recv/flush) +├── cli.rs +├── output.rs +├── error.rs +└── main.rs +``` + +### The `Headset` trait + +```rust +pub trait Headset { + fn name(&self) -> &str; + fn battery_level(&self) -> Result; + fn battery_status(&self) -> Result; + fn brightness(&self) -> Option>; // not all headsets have LEDs + fn set_brightness(&self, value: u16) -> Option>; + fn sidetone(&self) -> Option>; // not all have sidetone + fn set_sidetone(&self, value: u8) -> Option>; + fn info(&self) -> Result; +} +``` + +### Auto-detection + +Scan HID devices, try each backend's `probe()` function: + +```rust +pub fn detect() -> Result> { + // Try Bragi first (checks Corsair VID + known PIDs) + // Then SteelSeries, Logitech, etc. +} +``` + +--- + +## Phase 3: Port first additional protocol + +Pick the easiest one from HeadsetControl as proof of concept. +Good candidates (simple protocol, popular hardware): + +- **SteelSeries Arctis Nova 7** — straightforward HID, well-documented in HeadsetControl +- **Logitech G PRO X** — also relatively simple + +Steps per protocol: +1. Study HeadsetControl's C implementation +2. Implement as a Rust module behind the `Headset` trait +3. Add VID/PID to auto-detection +4. Update udev rules generator +5. Test (needs hardware or community testers) + +--- + +## Phase 4: Publish + +1. **GitHub repo** — rename or create new repo +2. **README** — features, supported devices, install instructions +3. **crates.io** — publish crate +4. **AUR package** — update PKGBUILD +5. **HeadsetControl community** — announce, invite testers for untested protocols + +--- + +## Phase 5: Upstream contribution to HeadsetControl + +Contribute Bragi protocol support back to [Sapd/HeadsetControl](https://github.com/Sapd/HeadsetControl). + +### Why this is feasible + +HeadsetControl already has `corsair_void_v2w.hpp` which uses a near-identical +protocol pattern to Bragi: multi-step handshake (firmware query → software mode +→ heartbeat), receiver + headset endpoints, HID buffer flushing with 5ms timeout, +sidetone 0-1000 range. The `CorsairDevice` base class provides shared helpers +for sidetone mapping, battery parsing, and LED control. + +### What a `corsair_bragi.hpp` would contain + +- New class inheriting from `CorsairDevice` +- Bragi-specific PIDs: `0x0A6B` (HS80), potentially HS65, Virtuoso SE +- Our Bragi property ID set (from `properties.rs`) +- LED brightness support (0-1000, not in existing Corsair implementations) +- Init handshake adapted to Bragi's two-phase sequence (receiver → headset) + +### Steps + +1. Study `corsair_device.hpp` and `corsair_void_v2w.hpp` in detail +2. Write `corsair_bragi.hpp` in C++20, following their patterns +3. Test with our HS80 hardware +4. Open PR with protocol documentation (`docs/bragi-protocol.md`) +5. List all known Bragi PIDs we can find (community input helps here) + +### Alternative: documentation-only contribution + +If writing C++ is too much effort, contribute just the protocol documentation +as a PR. HeadsetControl's community can implement it from there. We already +have `docs/bragi-protocol.md` which covers the full protocol. + +--- + +## Open Questions + +- [ ] Final name? +- [ ] Should sidetone stay ALSA-based or also go through HID where supported? +- [ ] Minimum supported Rust version (MSRV)? +- [ ] Do we want a config file for per-device settings? +- [ ] The nested `src/corsairctl/` directory — is this an old copy or something active?