Write the Driver
The driver is the class that implements the client contract described in Architecture, built on top of your codec from the previous step.
Location & shape
Put the driver under mouse-protocol/src/drivers/<vendor>/, typically as a
hid.ts (the SupportedClient implementation) alongside the codec module
(often at src/<vendor>/index.ts if it's shared/reusable beyond one
driver).
Minimum contract, called unconditionally by the app:
class MyVendorHidClient implements SupportedClient {
readonly device: HIDDevice;
async open(): Promise<void> { /* ... */ }
async close(): Promise<void> { /* ... */ }
async readStatus(): Promise<MouseStatus> { /* ... */ }
getDpiOptions(): DpiOption[] { return []; } // even if read-only
}
Read-only / non-mouse devices
If the device isn't a mouse, or you're shipping read-only support first,
return a MouseStatus with ui.settingsReady: false and no setters. The
app hides the settings grid for that flag rather than rendering broken
controls. getDpiOptions() still needs to return [] — it's called for
every connected client regardless of type.
Best-effort reads
Real devices are inconsistent about which reads succeed on the first try
(cold-boot state, firmware quirks). It's reasonable for readStatus() to
degrade gracefully — e.g. try a live device-config read, fall back to
identity-only fields if it fails — rather than throwing and breaking the
connect flow.
Testing
Write tests against your codec's encode/decode functions using the byte
fixtures from your captures, plus a test for the driver class itself
(mock HIDDevice, assert it calls sendReport/sendFeatureReport with the
expected bytes and parses a canned reply correctly).
Run the full suite before moving on — mouse-protocol has a repo-wide
npm run check that also includes a registry probe-matrix test (see
Register & Verify), so it's worth
running that at this stage too, even before you've added your registry
entries, to catch unrelated regressions early.