Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/device/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6178,6 +6178,17 @@ export function applyTeevolutionSensorMode(mode: NonNullable<MouseStatus["sensor
* Sensor sampling mode for drivers outside the Teevolution profile, where the
* mode is a plain device setting rather than one the polling rate can lock.
*/
/**
* The height a lift-off scale code stands for. A driver whose steps are uneven
* lists every code's height; otherwise the two ends are interpolated.
*/
export function liftOffScaleMillimetres(scale: NonNullable<MouseStatus["liftOffScale"]>, code: number): number {
const listed = scale.millimetresByCode?.[code - scale.min];
if (listed !== undefined) return listed;
return scale.minMillimetres
+ ((code - scale.min) * (scale.maxMillimetres - scale.minMillimetres)) / Math.max(1, scale.max - scale.min);
}

/**
* Lift-off for mice that tune it continuously rather than in three stops. The
* value is a raw device code; the driver owns what it means in millimetres.
Expand All @@ -6186,8 +6197,7 @@ export function applyLiftOffScale(code: number): void {
const scale = latestDeviceStatus?.liftOffScale;
if (!hasActiveClient() || !scale) return;
if (!Number.isInteger(code) || code < scale.min || code > scale.max) return;
const millimetres = scale.minMillimetres
+ ((code - scale.min) * (scale.maxMillimetres - scale.minMillimetres)) / Math.max(1, scale.max - scale.min);
const millimetres = liftOffScaleMillimetres(scale, code);
const label = `Lift-off ${millimetres.toFixed(1)} mm`;
stageChange({
key: "lift-off-scale",
Expand Down
5 changes: 5 additions & 0 deletions src/device/traits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ const BY_FAMILY: Readonly<Record<string, Partial<DriverTraits>>> = {
vgn: SHARED_ADVANCED,
wlmouse: DIRECT_MODE,
lamzu: DIRECT_MODE,
// The Atlantis client (Lamzu Atlantis, Scyrox V8S) stores debounce and sleep
// in flash and publishes its own debounce ceiling (15 ms), which only the
// direct-mode path reads. Without the entry the sleep card still appeared
// through its option list, but the debounce card never did.
"lamzu-atlantis": DIRECT_MODE,
lunafury: DIRECT_MODE,
"attack-shark": DIRECT_MODE,
bytech: DIRECT_MODE,
Expand Down
3 changes: 1 addition & 2 deletions src/features/devices/cards/PerformanceCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,7 @@ function LiftOffScale({ snapshot }: { snapshot: ControlSnapshot }): ReactNode {
if (!scale) return null;

const span = Math.max(1, scale.max - scale.min);
const millimetres = scale.minMillimetres
+ ((code - scale.min) * (scale.maxMillimetres - scale.minMillimetres)) / span;
const millimetres = control.liftOffScaleMillimetres(scale, code);

return (
<div id="lod-scale" className="lod-sliders">
Expand Down
8 changes: 8 additions & 0 deletions src/features/devices/cards/availability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,14 @@ test("debounce is offered only by the families that store it", () => {
})).debounce, false);
});

test("the Atlantis family (Lamzu Atlantis, Scyrox V8S) gets debounce and sleep", () => {
const status = { brand: "Scyrox", ui: { family: "lamzu-atlantis" }, debounceMs: 0, sleepTimeout: 60 } as const;
const has = cardAvailability(snapshot({ status }));
assert.equal(has.debounce, true);
assert.equal(has.sleep, true);
assert.equal(traitsFor({ ...STATUS, ...status }).directMode, true, "the debounce ceiling comes from the driver");
});

test("the Hitscan family alone gives the advanced section and debounce, not sleep or signal", () => {
// No ui flags beyond the family, so the result comes from the traits entry.
const status = { brand: "Hitscan", ui: { family: "hitscan" }, debounceMs: 4, sleepTimeout: 60, signalStrength: 80 } as const;
Expand Down
2 changes: 2 additions & 0 deletions src/features/hardware-test/supported-devices-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface SupportedDeviceEntry {
brand: string;
model: string;
status: SupportedDevicesStatus;
/** Known product ids from the page table (hex), when the driver pins them. */
pids?: readonly number[];
}

Expand Down Expand Up @@ -420,6 +421,7 @@ export const SUPPORTED_DEVICES: readonly SupportedDeviceEntry[] = [
{ brand: "Angry Miao", model: "Infinity .97", status: "unknown" },
{ brand: "Dark Project", model: "Novus SE", status: "unknown" },
{ brand: "Scyrox", model: "V8", status: "unknown" },
{ brand: "Scyrox", model: "V8S", status: "supported", pids: [0xf515, 0xf517] },
{ brand: "Mchose", model: "AX5", status: "unknown" },
{ brand: "AJAZZ", model: "AJ139 Pro", status: "unknown" },
{ brand: "AJAZZ", model: "AJ199", status: "unknown" },
Expand Down
Loading