Skip to content

Commit e3a2afa

Browse files
committed
docs: Add AI Agents guidance
1 parent a16be48 commit e3a2afa

2 files changed

Lines changed: 244 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# AGENTS.md
2+
3+
This file provides guidance to AI coding assistants (Claude Code, Copilot, Cursor, Codex, etc.) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
HeadsetControl is a cross-platform C++20 application to control USB-connected headsets on Linux, macOS, and Windows. It enables controlling features like sidetone, battery status, LEDs, inactive time, equalizers, and more for various gaming headsets (Logitech, SteelSeries, Corsair, HyperX, Roccat, Audeze).
8+
9+
## Build System
10+
11+
The project uses **CMake** as its build system.
12+
13+
### Standard build commands:
14+
```bash
15+
# Initial setup
16+
mkdir build && cd build
17+
cmake ..
18+
make
19+
20+
# Install globally (includes udev rules on Linux)
21+
sudo make install
22+
23+
# On Linux after install, reload udev rules:
24+
sudo udevadm control --reload-rules && sudo udevadm trigger
25+
```
26+
27+
### Testing:
28+
```bash
29+
# Enable + build + run unit tests
30+
cmake -DBUILD_UNIT_TESTS=ON ..
31+
make check # Builds dependencies and runs ctest --output-on-failure
32+
# Or just:
33+
ctest
34+
```
35+
36+
### Code formatting:
37+
**IMPORTANT:** The CI uses clang-format version 18. To avoid formatting conflicts, install the matching version:
38+
```bash
39+
# On macOS:
40+
brew install llvm@18
41+
42+
# On Linux (Ubuntu/Debian):
43+
sudo apt-get install clang-format-18
44+
45+
# Format all code
46+
cmake -DENABLE_CLANG_FORMAT=ON ..
47+
make format
48+
```
49+
50+
### Code analysis:
51+
```bash
52+
cmake -DENABLE_CLANG_TIDY=ON ..
53+
make tidy
54+
```
55+
The `tidy` target prefers `clang-tidy-9` if installed, otherwise falls back to the system `clang-tidy` (typically the same version as your formatter).
56+
57+
## Architecture
58+
59+
### Project Structure
60+
61+
```
62+
HeadsetControl/
63+
├── lib/ # Core library (libheadsetcontrol)
64+
│ ├── devices/ # Device implementations (header-only)
65+
│ │ ├── hid_device.hpp # Base class for all devices
66+
│ │ ├── protocols/ # CRTP protocol templates
67+
│ │ │ ├── hidpp_protocol.hpp
68+
│ │ │ ├── steelseries_protocol.hpp
69+
│ │ │ ├── logitech_centurion_protocol.hpp
70+
│ │ │ └── logitech_calibrations.hpp
71+
│ │ └── *.hpp # Device-specific implementations
72+
│ ├── device.hpp # Capability enums and structs
73+
│ ├── device_registry.hpp # Device lookup singleton
74+
│ ├── result_types.hpp # Result<T> error handling
75+
│ ├── capability_descriptors.hpp # CAPABILITY_DESCRIPTORS metadata array
76+
│ ├── feature_handlers.hpp # FeatureHandlerRegistry dispatch table
77+
│ ├── headsetcontrol.hpp # Public C++ API
78+
│ ├── headsetcontrol_c.h # Public C API
79+
│ ├── dev.hpp / dev.cpp # Dev-mode HID exploration helpers
80+
│ └── utility.hpp # Helper functions
81+
├── cli/ # Command-line interface
82+
│ ├── main.cpp # Entry point
83+
│ ├── argument_parser.hpp # CLI argument parsing
84+
│ ├── dev.cpp # Developer/debug mode (--dev)
85+
│ └── output/ # Serialization (JSON, YAML, ENV)
86+
├── tests/ # Unit and integration tests
87+
├── docs/ # Documentation (ADDING_A_DEVICE, ADDING_A_CAPABILITY,
88+
│ # ADDING_A_CORSAIR_DEVICE, DEVELOPMENT, LIBRARY_USAGE)
89+
├── cmake_modules/ # Findhidapi.cmake and related
90+
├── assets/ # Icons and Windows resource file
91+
├── .github/ # CI workflows, issue and PR templates
92+
├── CMakePresets.json # CMake presets
93+
└── vcpkg.json # vcpkg manifest (Windows builds)
94+
```
95+
96+
### Device Registration System
97+
98+
The core architecture uses a **device registry pattern** with modern C++20:
99+
100+
1. **Device Registry** (`lib/device_registry.hpp`):
101+
- Singleton pattern for device management
102+
- `DeviceRegistry::instance().initialize()` registers all devices
103+
- `getDevice(vendor_id, product_id)` looks up devices by USB IDs
104+
- `getAllDevices()` returns all registered implementations
105+
106+
2. **HIDDevice Base Class** (`lib/devices/hid_device.hpp`):
107+
- Pure virtual interface for all headset devices
108+
- Virtual methods for each capability (e.g., `setSidetone()`, `getBattery()`)
109+
- Returns `Result<T>` types for proper error handling
110+
- Provides HID communication helpers (`writeHID()`, `readHIDTimeout()`)
111+
112+
3. **Device Implementations** (`lib/devices/*.hpp`):
113+
- Each headset is a class inheriting from `HIDDevice` (or a CRTP protocol template)
114+
- Header-only implementations
115+
- CRTP protocol templates under `lib/devices/protocols/` reduce boilerplate (HID++, SteelSeries, Logitech Centurion). Inherit via e.g. `protocols::HIDPPDevice<MyDevice>`.
116+
117+
### Result<T> Error Handling
118+
119+
All device methods return `Result<T>` (similar to `std::expected`):
120+
121+
```cpp
122+
Result<BatteryResult> getBattery(hid_device* handle) override {
123+
auto result = readHIDTimeout(handle, buffer, timeout);
124+
if (!result) {
125+
return result.error(); // Propagate error
126+
}
127+
return BatteryResult { .level_percent = 85, .status = BATTERY_AVAILABLE };
128+
}
129+
```
130+
131+
Error types: `DeviceError::timeout()`, `hidError()`, `protocolError()`, `notSupported()`
132+
133+
### Adding New Device Support
134+
135+
See `docs/ADDING_A_DEVICE.md` for detailed instructions. Quick overview:
136+
137+
1. Create `lib/devices/vendor_model.hpp`
138+
2. Inherit from `HIDDevice` or a protocol template
139+
3. Implement required virtual methods
140+
4. Register in `lib/device_registry.cpp`
141+
142+
### Using as a Library
143+
144+
See `docs/LIBRARY_USAGE.md` for integration guide. The library provides:
145+
146+
- `headsetcontrol_lib` static library target
147+
- `headsetcontrol_shared` shared library target (with `-DBUILD_SHARED_LIBRARY=ON`)
148+
- Public headers in `lib/` directory
149+
- C++ API (`headsetcontrol.hpp`) and C API (`headsetcontrol_c.h`)
150+
- Device discovery and control API
151+
152+
```bash
153+
# Build shared library for FFI (Python, Rust, etc.)
154+
cmake -DBUILD_SHARED_LIBRARY=ON ..
155+
make
156+
```
157+
158+
### Capability System
159+
160+
Capabilities are enumerated in `lib/device.hpp` via the `CAPABILITIES_XLIST` macro. The full set:
161+
162+
- `CAP_SIDETONE` — Microphone sidetone level
163+
- `CAP_BATTERY_STATUS` — Battery level and charging status
164+
- `CAP_NOTIFICATION_SOUND` — Play a notification tone
165+
- `CAP_LIGHTS` — LED on/off
166+
- `CAP_INACTIVE_TIME` — Auto-shutoff timer
167+
- `CAP_CHATMIX_STATUS` — Game/chat audio balance
168+
- `CAP_VOICE_PROMPTS` — Toggle spoken prompts
169+
- `CAP_ROTATE_TO_MUTE` — Rotate boom mic to mute
170+
- `CAP_EQUALIZER_PRESET` — Select preset EQ
171+
- `CAP_EQUALIZER` — Custom EQ bands
172+
- `CAP_PARAMETRIC_EQUALIZER` — Parametric EQ (gain, Q-factor, frequency)
173+
- `CAP_MICROPHONE_MUTE_LED_BRIGHTNESS`
174+
- `CAP_MICROPHONE_VOLUME`
175+
- `CAP_VOLUME_LIMITER`
176+
- `CAP_BT_WHEN_POWERED_ON` — Bluetooth-on-power-on behavior
177+
- `CAP_BT_CALL_VOLUME`
178+
- `CAP_NOISE_FILTER`
179+
180+
When adding a capability, update both `CAPABILITIES_XLIST` and the descriptor/handler tables (see Data-Driven Feature System below).
181+
182+
### Data-Driven Feature System
183+
184+
The codebase uses a data-driven approach for feature handling:
185+
186+
1. **Capability Descriptors** (`lib/capability_descriptors.hpp`):
187+
- Single source of truth for all capability metadata
188+
- CLI flag names, descriptions, value ranges
189+
- Used for validation and help text generation
190+
191+
2. **Feature Handler Registry** (`lib/feature_handlers.hpp`):
192+
- Dispatch table for feature execution
193+
- Eliminates giant switch statements
194+
- One handler per capability
195+
196+
```cpp
197+
// Adding a new capability only requires:
198+
// 1. Add descriptor to CAPABILITY_DESCRIPTORS array
199+
// 2. Register handler in FeatureHandlerRegistry::registerAllHandlers()
200+
```
201+
202+
See `docs/ADDING_A_CAPABILITY.md` for a step-by-step guide.
203+
204+
## Code Style
205+
206+
- **C++20 standard** with modern features
207+
- `.clang-format` uses WebKit base style
208+
- RAII for resource management
209+
- `Result<T>` is `[[nodiscard]]` at the class level — you do **not** need to mark virtual override methods returning `Result<T>` with `[[nodiscard]]`; the enforcement is on the type
210+
- `std::format` for string formatting
211+
- `std::optional`, `std::span`, `std::string_view`
212+
- Designated initializers for struct initialization
213+
- Device files are **header-only** (`.hpp`); the only `.cpp` in `lib/devices/` is `hid_device.cpp`
214+
- Naming: methods `camelCase`, member variables `snake_case_` (trailing underscore), constants `ALL_CAPS`
215+
- `using namespace std::string_view_literals;` at file scope is the convention in device headers
216+
- Use `[[maybe_unused]]` on unused parameters in virtual overrides
217+
218+
## Dependencies
219+
220+
- **HIDAPI** — USB HID communication library (required)
221+
- **CMake** — Build system (minimum 3.12)
222+
- **C++20 compiler** — GCC 13+, Clang 16+, Apple Clang 15+, MSVC 19.29+ (VS 2019 16.10+) as enforced in `CMakeLists.txt`
223+
224+
## Platform-Specific Notes
225+
226+
### Linux
227+
- Generates udev rules: `headsetcontrol -u > /etc/udev/rules.d/70-headset.rules`
228+
- Reload rules: `sudo udevadm control --reload-rules && sudo udevadm trigger`
229+
230+
### macOS
231+
- No special permissions needed
232+
- Homebrew: `brew install sapd/headsetcontrol/headsetcontrol --HEAD`
233+
234+
### Windows
235+
- Uses SetupAPI for HID access
236+
- Some devices require `usagepage`/`usageid` instead of `interface`
237+
238+
## Testing
239+
240+
- **Unit tests**: `tests/test_*.cpp` using a lightweight test framework
241+
- **Test device**: `HeadsetControlTest` (0xF00B:0xA00C) implements all capabilities
242+
- Run with: `./headsetcontrol --test-device -b`
243+
- **Dev mode**: `./headsetcontrol --dev -- --list` for HID exploration

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md

0 commit comments

Comments
 (0)