From bb68f91ea2d29392901013cb6caf87d226d46a12 Mon Sep 17 00:00:00 2001 From: Onyx Date: Mon, 19 Oct 2026 18:40:42 +0200 Subject: [PATCH] fix(logitech): do not reject lift-off on legacy scalar DPI formats Formats below 6 (for example the G305, format 3) store only a DPI value per slot and the writer never writes lift-off for them. The validator still required lod === 0, so enabling a new slot (which copies lift-off from the previous slot) failed the flash with "Slot 3 cannot store lift-off distance". The value is now ignored on these formats. --- src/drivers/logitech/onboard-profiles.test.ts | 6 ++++++ src/drivers/logitech/onboard-profiles.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/drivers/logitech/onboard-profiles.test.ts b/src/drivers/logitech/onboard-profiles.test.ts index 2c2f19d0..2eecf939 100644 --- a/src/drivers/logitech/onboard-profiles.test.ts +++ b/src/drivers/logitech/onboard-profiles.test.ts @@ -1025,6 +1025,12 @@ test("DPI slot limits are per format, not global", () => { assert.equal(clampDpi(Number.NaN, narrow), 200); }); +test("a legacy format accepts a slot whose lift-off was copied from another slot (G305 slot 3)", () => { + const limits = capabilitiesForFormat(3).dpiStages; + const stages = [800, 1200, 1600].map((dpi) => ({ x: dpi, y: dpi, lod: 2 })); + assert.equal(validateDpiStagePlan({ stages, defaultIndex: 0 }, limits, true), null); +}); + test("unwritable DPI stage plans are rejected before they reach flash", () => { const limits = capabilitiesForFormat(7).dpiStages; const stage = { x: 800, y: 800, lod: 1 }; diff --git a/src/drivers/logitech/onboard-profiles.ts b/src/drivers/logitech/onboard-profiles.ts index 295a239c..8cc952d6 100644 --- a/src/drivers/logitech/onboard-profiles.ts +++ b/src/drivers/logitech/onboard-profiles.ts @@ -1152,7 +1152,7 @@ export function validateDpiStagePlan( } if (legacyScalar) { if (stage.x !== stage.y) return `Slot ${index + 1} must use the same X and Y DPI.`; - if (stage.lod !== 0) return `Slot ${index + 1} cannot store lift-off distance.`; + // Lift-off is not stored here and the writer ignores it, so UI defaults must not fail the flash. } else if (!Number.isInteger(stage.lod) || stage.lod < 1 || stage.lod > 3) { // 0 is the "unused stage" marker on formats with per-stage lift-off. return `Slot ${index + 1} has an invalid lift-off level.`;