From 58cdb43ac73c3ad6cde015ea03a6ce8d8a114d5e Mon Sep 17 00:00:00 2001 From: chexit Date: Fri, 13 Nov 2026 12:06:09 +0300 Subject: [PATCH] Reduce Mini 2 volume tap latency --- station/panel.go | 37 ++++++++++++++++++++++++++----------- station/panel_test.go | 22 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/station/panel.go b/station/panel.go index 9b2e4b9..29768ac 100644 --- a/station/panel.go +++ b/station/panel.go @@ -17,14 +17,15 @@ const ( panelPlus = 115 panelCenter = 582 - panelMinTap = 18 * time.Millisecond - panelGap = 205 * time.Millisecond - panelMaxSwipe = 520 * time.Millisecond - panelHold = 500 * time.Millisecond - panelRepeat = 250 * time.Millisecond - panelWakeHold = 3 * time.Second - panelCooldown = 170 * time.Millisecond - panelMaxHold = 6 * time.Second + panelMinTap = 18 * time.Millisecond + panelVolumeGap = 115 * time.Millisecond + panelGap = 205 * time.Millisecond + panelMaxSwipe = 520 * time.Millisecond + panelHold = 500 * time.Millisecond + panelRepeat = 250 * time.Millisecond + panelWakeHold = 3 * time.Second + panelCooldown = 170 * time.Millisecond + panelMaxHold = 6 * time.Second ) type panelAction string @@ -46,6 +47,8 @@ type panelGesture struct { lastRelease time.Time nextHold time.Time cooldown time.Time + volumeTail time.Time + volumeZone uint16 held bool invalid bool ignoreToSync bool @@ -85,10 +88,16 @@ func (g *panelGesture) finish(now time.Time) panelAction { } } cooldown := g.cooldown + var volumeTail time.Time + var volumeZone uint16 if action == panelNext || action == panelPrevious { cooldown = now.Add(panelCooldown) } - *g = panelGesture{cooldown: cooldown} + if action == panelQuieter || action == panelLouder { + volumeTail = g.lastRelease.Add(panelGap) + volumeZone = g.path[0] + } + *g = panelGesture{cooldown: cooldown, volumeTail: volumeTail, volumeZone: volumeZone} return action } @@ -96,7 +105,11 @@ func (g *panelGesture) tick(now time.Time) panelAction { if g.count == 0 { return "" } - if g.down == 0 && !now.Before(g.lastRelease.Add(panelGap)) { + gap := panelGap + if g.count == 1 && !g.invalid && !g.held && g.path[0] != panelCenter { + gap = panelVolumeGap + } + if g.down == 0 && !now.Before(g.lastRelease.Add(gap)) { return g.finish(now) } if g.down == 0 || g.invalid || g.count != 1 || now.Before(g.nextHold) { @@ -142,7 +155,9 @@ func (g *panelGesture) key(now time.Time, code uint16, value uint32) { if code == panelCenter { g.nextHold = now.Add(panelWakeHold) } - g.invalid = now.Before(g.cooldown) + // A late second zone belongs to the same ambiguous motion. Another + // touch on the original volume zone can still be an independent tap. + g.invalid = now.Before(g.cooldown) || (now.Before(g.volumeTail) && code != g.volumeZone) } // A moving finger can leave and re-enter the center zone while crossing it. if g.count == 2 && code == panelCenter && g.path[1] == panelCenter { diff --git a/station/panel_test.go b/station/panel_test.go index 1f87845..a15f6d5 100644 --- a/station/panel_test.go +++ b/station/panel_test.go @@ -96,3 +96,25 @@ func TestPanelHoldRepeatsWithoutReleaseTap(t *testing.T) { t.Fatalf("center hold emitted %q", got) } } + +func TestPanelVolumeRespondsBeforeSwipeWindowEnds(t *testing.T) { + start := time.Unix(100, 0) + release := start.Add(75 * time.Millisecond) + var g panelGesture + g.key(start, panelPlus, 1) + g.key(release, panelPlus, 0) + if got := g.tick(release.Add(panelVolumeGap - time.Millisecond)); got != "" { + t.Fatalf("emitted before adjacent-zone window: %q", got) + } + if got := g.tick(release.Add(panelVolumeGap)); got != panelLouder { + t.Fatalf("volume did not respond after the shorter gap: %q", got) + } + center := release.Add(160 * time.Millisecond) + g.key(center, panelCenter, 1) + g.key(center.Add(30*time.Millisecond), panelCenter, 0) + g.key(center.Add(55*time.Millisecond), panelMinus, 1) + g.key(center.Add(80*time.Millisecond), panelMinus, 0) + if got := g.tick(center.Add(time.Second)); got != "" { + t.Fatalf("late swipe continuation emitted a second action: %q", got) + } +}