Bump to v3.1.2: tray icon re-stamps on state change, recording shows as a flag

- Tray icon now re-registers itself (hide+re-show) whenever the meaningful
  state changes — peer connect/drop, send/receive toggle, recording
  start/stop — so the name a screen reader announces stays in step with
  the live state. Fixes the persistent "no peers" then "1 peer" double
  announcement that the old "show window then minimise again" trick used
  to clear by hand. Same root cause as the v3.1.1 stuck-tooltip fix, just
  exposed when the state changes a moment after the icon appears.
- Recording shows as a plain "recording" flag in the tray rather than a
  live timer. A ticking timer would have either flickered the icon once a
  second or left a screen reader announcing a stale time next to the live
  one. Removed the now-dead FormatRecordingElapsed helper.
- Bundles the earlier Win7 updater fix (6cbde0d): a failed secure
  connection is no longer mislabelled as "you're up to date".
- About box, RELEASE_NOTES.md, readme.html and MANUAL.md updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-05-30 23:34:44 +01:00
co-authored by Claude Opus 4.8
parent 6cbde0da12
commit baf50baf73
7 changed files with 111 additions and 39 deletions
+38
View File
@@ -20,6 +20,44 @@ internal sealed class AboutDialog : Form
/// updates" path.</summary>
private const string ReleaseNotes =
"""
RemSound v3.1.2
A small accessibility fix for the system tray
icon, plus a clearer "check for updates" message
for Windows 7.
The tray icon's double announcement: if you use a
screen reader, the tray icon could read out two
states one after the other for example "no peers"
and then "1 peer" every time you landed on it. It
always happened in that same order. Windows stamps
a tray icon's name at the moment the icon appears,
which on a fresh start is a second before your other
machine has reconnected so the name was frozen as
"no peers" while only the hover text caught up to
"1 peer". RemSound now refreshes the icon itself
whenever something real changes a peer connecting
or dropping, sending or receiving switching on or
off, a recording starting or stopping so the
screen reader hears a single, current state.
Recording in the tray: while a recording is running
the tray now simply says "recording" rather than
counting the seconds. A live timer would have made
the icon flicker or read out a stale time; the exact
length is on the main window if you need it.
Windows 7 update message: on a Windows 7 machine
that can't make a secure connection to the update
server, "Check for updates" used to say "you're on
the latest version", which was misleading. It now
explains that the secure connection failed and points
to the Windows updates that fix it, with a manual
download link as a fallback. Windows 10 and 11 were
never affected.
Everything else from v3.1 is unchanged.
RemSound v3.1.1
Hot-fix for a small but annoying bug with the system
+9 -22
View File
@@ -5292,14 +5292,15 @@ public sealed class MainForm : Form
if (ph.State == PeerHealthState.Healthy) healthyPeers++;
}
}
// Recording timer — only included when a recording is actually running. Slots in
// right after the "RemSound" leader as Ed asked, so it reads as a status on the
// app itself rather than a property of the peer list.
string? recordingPart = null;
if (recordingController.IsRecording && recordingController.RecordingStartedUtc is { } startedUtc)
{
recordingPart = $"recording for {FormatRecordingElapsed(DateTime.UtcNow - startedUtc)}";
}
// Recording status — a plain "recording" flag while a capture is running, with no
// elapsed timer. A live timer would have to rewrite the tooltip every second, which
// fights the tray icon's re-stamp-on-change behaviour: it would either flicker the
// icon once a second or leave a screen reader announcing a stale time next to the live
// one. Recording starting and stopping are real state changes that re-stamp cleanly;
// the second-by-second count is intentionally left to the main window. Slots in right
// after the "RemSound" leader so it reads as a status on the app itself rather than a
// property of the peer list.
string? recordingPart = recordingController.IsRecording ? "recording" : null;
if (healthyPeers == 0 && !sendMyAudioCheckbox.Checked && !receiveAudioCheckbox.Checked)
{
return recordingPart is null
@@ -5346,20 +5347,6 @@ public sealed class MainForm : Form
return list.CheckedItems.Count > 0;
}
/// <summary>Compact duration formatter for the tray tooltip's "recording for X" segment.
/// Under an hour shows MM:SS; from an hour on it shows H:MM:SS — the same shape Windows
/// uses for media-player elapsed-time displays, so it reads naturally to people who
/// don't otherwise know it's a custom format.</summary>
private static string FormatRecordingElapsed(TimeSpan elapsed)
{
// Negative elapsed (clock skew across a sleep cycle) gets clamped to zero — better
// than displaying "-00:01" mid-tooltip.
if (elapsed < TimeSpan.Zero) elapsed = TimeSpan.Zero;
return elapsed.TotalHours >= 1
? $"{(int)elapsed.TotalHours}:{elapsed.Minutes:D2}:{elapsed.Seconds:D2}"
: $"{elapsed.Minutes:D2}:{elapsed.Seconds:D2}";
}
/// <summary>Pretty-print "WASAPI", "ASIO", "WASAPI + ASIO", or "no devices" depending
/// on which of the two flags are set. "no devices" covers the awkward case where the
/// user has the send/receive checkbox on but hasn't ticked anything for the engine to
@@ -40,6 +40,13 @@ internal sealed class MainFormTrayController : IDisposable
private readonly Func<string> buildTooltip;
private readonly Action exit;
/// <summary>The tooltip text the icon was last (re-)registered with. We compare against
/// this so we only re-register on a real state change — a peer connecting or dropping, a
/// lane switching, a recording starting or stopping. The tray tooltip has no per-second
/// element (recording shows a plain "recording" flag, not a ticking timer), so the text
/// only changes on those real events and the icon never flickers. Null until first shown.</summary>
private string? lastRegistrationText;
private readonly ToolStripMenuItem sendingItem;
private readonly ToolStripMenuItem receivingItem;
private readonly ToolStripMenuItem profilesItem;
@@ -152,6 +159,38 @@ internal sealed class MainFormTrayController : IDisposable
// NotifyIcon.Text throws on the same string-assigning path under some shell
// conditions (rare race during a session-end). Best-effort: swallow.
try { trayIcon.Text = text; } catch { /* harmless */ }
// Keep the icon's *registered* name — the text a screen reader announces — in step
// with the live state. Windows stamps a tray icon's accessible name at the moment the
// icon is added (NIM_ADD) and afterwards only refreshes the little hover bubble, not
// that stamped name. So if the icon appears at launch before the first peer has
// connected, the name is frozen as "no peers"; when the peer links up a second later
// the bubble updates to "1 peer" but the frozen name stays — and NVDA reads BOTH (the
// stale name, then the live bubble), which is the confusing double announcement.
//
// The cure is to re-add the icon (hide then re-show = NIM_DELETE + NIM_ADD) whenever
// the text changes, which re-stamps the name with the current text. This is exactly
// what the old "show the window then minimise again" workaround did by hand. The tray
// tooltip has no per-second element (recording shows a plain "recording" flag, not a
// ticking timer), so the text only changes on real events and this never flickers.
if (!trayIcon.Visible)
{
// The first call arrives from Minimize() a moment before the icon is shown — just
// record the baseline so the NIM_ADD that immediately follows is our reference.
lastRegistrationText = text;
return;
}
if (text == lastRegistrationText) return;
// Don't yank the icon out from under an open right-click menu (that would dismiss it
// mid-navigation). Leave the record stale so the next tick re-stamps once it closes.
if (trayIcon.ContextMenuStrip?.Visible == true) return;
lastRegistrationText = text;
try
{
trayIcon.Visible = false;
trayIcon.Visible = true;
}
catch { /* harmless — the 1 Hz snapshot tick will try again next second */ }
}
public void Toggle()
+1 -1
View File
@@ -14,7 +14,7 @@
tag_name on the latest GitHub release; bump it on every public release. The
AssemblyVersion / FileVersion default to this value, and Assembly.GetName().Version
is what the About dialog and the updater both read. -->
<Version>3.1.1</Version>
<Version>3.1.2</Version>
</PropertyGroup>
<ItemGroup>