Bump to v3.1.1: tray tooltip 'starting up' could get stuck after first launch
Bug: if v3.1 installed via auto-update on a profile with StartMinimised on, the tray icon's hover tooltip got stuck at the initial "RemSound - starting up" string. The snapshot tick was running and SetTooltip was being called every second with the live state, but Windows shell kept showing the original cached text on hover. The fix-by-workaround was a hide-then-re-show cycle which forced the shell to rebuild the icon registration with the latest NotifyIcon.Text. Root cause: NotifyIcon.Text values set BEFORE the icon's first NIM_ADD (i.e. while Visible=false) become the shell's "initial" tooltip when the icon eventually appears. Subsequent NIM_MODIFY calls from text changes DO propagate, but the shell tends to keep showing the original text on hover - presumably a tooltip-cache eviction quirk. In the resume-after-update-with-StartMinimised flow, the window briefly shows then BeginInvokes a Minimize that flips Visible=true before the snapshot timer has had a chance to fire, so the shell registers with the stale "starting up" string. Fix: drop the hard-coded "starting up" initial text from the controller ctor entirely. The controller now takes a Func<string> buildTooltip callback from MainForm and calls it in Minimize() right before flipping Visible=true, so the shell's NIM_ADD sees current live state instead of a stale string. The 1 Hz snapshot tick keeps working for ongoing live updates while the icon is visible. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
752c12b579
commit
aa099eb555
+14
-53
@@ -1,71 +1,32 @@
|
|||||||
# RemSound v3.1
|
# RemSound v3.1.1
|
||||||
|
|
||||||
A meaningful round of work on **audio cue sounds** and the **system tray**. Two new cues, a way to swap in your own WAV files per profile, and a rebuilt tray menu with a live status tooltip. Plus a couple of smaller bug fixes. No wire format change — v3.1 talks to v3.0.x machines exactly as before.
|
Hot-fix for one small but annoying bug introduced in v3.1.
|
||||||
|
|
||||||
## New audio cue sounds
|
## What was happening
|
||||||
|
|
||||||
Two new sounds join the existing connect / disconnect / record-start / record-stop cues:
|
If the v3.1 auto-update fired while RemSound was set to start minimised to the tray, the tray icon's hover tooltip could get stuck saying **"RemSound — starting up"** indefinitely, instead of switching to the live "X peers, sending, receiving" summary after a second or so. The workaround was to show the main window and then minimise it again — that cleared the stuck text.
|
||||||
|
|
||||||
* **Profile saved.** Plays a short cue whenever a profile is saved — either via File → Save (Ctrl+S) or Save as. An audible "yes, that took" so you don't have to look at the screen.
|
## Why it happened
|
||||||
* **Profile switched.** Plays a short cue whenever a profile finishes loading. Fires at startup if you started with a profile, and after every mid-session profile switch. **It plays the new profile's cue, not the old one's** — so if you give each profile a different switched-to sound, you can hear which profile you're now on without checking the title bar.
|
|
||||||
|
|
||||||
## Custom cue sounds, per profile
|
Windows registers a tray icon's tooltip text with the shell at the exact moment the icon becomes visible. RemSound was setting an initial "starting up" string at construction time, and the shell tended to keep showing that text on hover even after the live state had been computed and pushed through. The hide-then-re-show workaround forced the shell to rebuild its registration of the icon, which picked up whatever the current live text was.
|
||||||
|
|
||||||
The **Audio cue sounds** list in Preferences now has six entries (Connect, Disconnect, Recording start, Recording stop, Profile saved, Profile switched), each with a tickbox to enable or silence it.
|
## The fix
|
||||||
|
|
||||||
Two new buttons sit just below the list, both acting on whichever cue is currently highlighted:
|
The right tooltip text is now computed once, just before the icon becomes visible for the first time, so Windows registers the icon with the correct live summary from the start. From there, the same 1-second refresh keeps it current — same as before.
|
||||||
|
|
||||||
* **Play [cue name]** (Alt+P) previews the currently-configured sound through your default Windows output. Works regardless of whether the cue is ticked, so you can listen before deciding to enable.
|
## Nothing else has changed
|
||||||
* **Browse for [cue name]…** (Alt+B) opens a Windows file picker so you can choose your own WAV file to replace the default sound for that cue. Right-clicking the Browse button offers a **Use default sound** item to undo the swap.
|
|
||||||
|
|
||||||
Both the tick states AND the custom sound choices are **saved with the active profile**. A "quiet listening" profile can have all cues off; a "studio" profile can use a distinct set of sounds. When you switch profiles, the cues switch too.
|
Same wire format, same codec list, same audio cues, same everything else from v3.1. If you didn't see the stuck-tooltip issue, this update is small and silent — but worth taking because the underlying cause was a real Windows shell behaviour and the fix is solid.
|
||||||
|
|
||||||
The default WAV files have moved out of the install folder root into a new `sounds\` subfolder, keeping the install layout tidier.
|
|
||||||
|
|
||||||
## System tray icon redesigned
|
|
||||||
|
|
||||||
**Hover summary.** The tray icon's tooltip now shows a live summary that refreshes every second:
|
|
||||||
|
|
||||||
* `RemSound — not connected`
|
|
||||||
* `RemSound — 2 peers, sending (WASAPI), receiving (WASAPI)`
|
|
||||||
* `RemSound — recording for 5:23, 1 peer, sending (WASAPI + ASIO), receiving (WASAPI + ASIO)`
|
|
||||||
|
|
||||||
The recording timer only shows when you're actually recording. The "(WASAPI)", "(ASIO)", or "(WASAPI + ASIO)" tag reflects which lanes the corresponding direction is actually using.
|
|
||||||
|
|
||||||
**Right-click menu rebuilt.** Five items, each with a single-letter shortcut:
|
|
||||||
|
|
||||||
| Item | Key |
|
|
||||||
|---|---|
|
|
||||||
| Show RemSound | W |
|
|
||||||
| Enable sending (tickable, shows current state) | S |
|
|
||||||
| Enable receiving (tickable, shows current state) | R |
|
|
||||||
| Profiles (submenu of recent profiles) | P |
|
|
||||||
| Exit | X |
|
|
||||||
|
|
||||||
* **Show RemSound** now reliably brings the window to the front AND gives it focus, fixing a case where screen-reader users had to Alt+Tab to actually reach the restored window.
|
|
||||||
* **Enable sending / Enable receiving** now **toggle** the state instead of always switching it on — so you can use them to turn off as well.
|
|
||||||
* The **Profiles submenu** shows your five most recent profiles (same list the File menu uses) with number-key shortcuts. While the submenu is open, press 1 for the most recent, 2 for the next, and so on. Switching from the tray works the same way as from the File menu — RemSound reloads under the new profile, your devices and peers come back as the new profile has them.
|
|
||||||
|
|
||||||
## Smaller bug fixes
|
|
||||||
|
|
||||||
* **Tray icon's first-launch tooltip read as "RemSound RemSound"** for some screen-reader users because the tooltip text matched the process name. Initial tooltip is now "RemSound — starting up", which avoids the duplicate read. Within a second the live state takes over and reads cleanly from then on.
|
|
||||||
* **Recent profiles in both the File menu and the new tray menu** no longer announce a "Recent profile N:" prefix on each item — they just read the profile name. The 1..5 number-key shortcuts still work either way.
|
|
||||||
|
|
||||||
## Compatibility
|
|
||||||
|
|
||||||
No wire format change. v3.1 talks to other v3.0.x machines (v3.0, v3.0.1, v3.0.2) exactly the same as before. Profiles created or saved on v3.1 carry the new per-cue settings and a v3.0.x build loading one will silently ignore the new fields (so the profile still works, just without the new cue-customisation state).
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
1. Download `RemSound-v3.1.zip` from this release.
|
1. Download `RemSound-v3.1.1.zip` from this release.
|
||||||
2. Close RemSound.
|
2. Close RemSound.
|
||||||
3. Extract the zip **over your existing RemSound folder**, overwriting program files when prompted. The zip is program files only — it will not touch your profiles, settings or recordings.
|
3. Extract the zip **over your existing RemSound folder**, overwriting program files when prompted. The zip is program files only — it will not touch your profiles, settings or recordings.
|
||||||
4. Run `RemSound.exe`. Press F1 for the user manual.
|
4. Run `RemSound.exe`.
|
||||||
|
|
||||||
Requires the .NET 10 Desktop Runtime. If it's missing, Windows offers to fetch it on first launch.
|
|
||||||
|
|
||||||
## Upgrading
|
## Upgrading
|
||||||
|
|
||||||
**v1.9 through v3.0.2:** Help → Check for updates works — it will fetch and install v3.1 automatically. If you've ticked "Check for updates on startup" and "Silently install updates", v3.1 installs itself shortly after launch and RemSound reopens on whichever profile you were running.
|
**v1.9 through v3.1:** Help → Check for updates works — it will fetch and install v3.1.1 automatically. If you've ticked "Check for updates on startup" and "Silently install updates", v3.1.1 installs itself shortly after launch.
|
||||||
|
|
||||||
**v1.8 and earlier:** the auto-updater in those versions has a fault that prevents it from installing updates, so Check for updates will download v3.1 but not apply it. Install v3.1 by hand using the steps above — just this once. From the build you install onward, updates are automatic.
|
**v1.8 and earlier:** the auto-updater in those versions has a fault that prevents it from installing updates, so Check for updates will download v3.1.1 but not apply it. Install v3.1.1 by hand using the steps above — just this once. From the build you install onward, updates are automatic.
|
||||||
|
|||||||
@@ -20,6 +20,33 @@ internal sealed class AboutDialog : Form
|
|||||||
/// updates" path.</summary>
|
/// updates" path.</summary>
|
||||||
private const string ReleaseNotes =
|
private const string ReleaseNotes =
|
||||||
"""
|
"""
|
||||||
|
RemSound v3.1.1
|
||||||
|
|
||||||
|
Hot-fix for a small but annoying bug with the system
|
||||||
|
tray tooltip after v3.1 first installed itself.
|
||||||
|
|
||||||
|
What was happening: if the v3.1 auto-update fired
|
||||||
|
while RemSound was set to start minimised to the tray,
|
||||||
|
the tray icon's hover text could get stuck saying
|
||||||
|
"RemSound — starting up" indefinitely, instead of
|
||||||
|
switching to the live "X peers, sending, receiving"
|
||||||
|
summary after a second or so. Showing the main window
|
||||||
|
and then minimising it again was the workaround.
|
||||||
|
|
||||||
|
What was actually going wrong: Windows registers a
|
||||||
|
tray icon's tooltip with the shell at the moment the
|
||||||
|
icon becomes visible. RemSound was setting an initial
|
||||||
|
"starting up" string at construction time, and the
|
||||||
|
shell tended to keep showing that on hover even after
|
||||||
|
the live state had been computed and pushed through.
|
||||||
|
The workaround (hide then re-show the icon) cleared
|
||||||
|
the shell's cache. The fix is to compute the right
|
||||||
|
live tooltip text once, just before the icon becomes
|
||||||
|
visible for the first time, so the shell registers
|
||||||
|
the icon with correct text from the start.
|
||||||
|
|
||||||
|
Everything else in v3.1 is unchanged.
|
||||||
|
|
||||||
RemSound v3.1
|
RemSound v3.1
|
||||||
|
|
||||||
Two big rounds of work on audio cues and the system
|
Two big rounds of work on audio cues and the system
|
||||||
|
|||||||
@@ -622,6 +622,10 @@ public sealed class MainForm : Form
|
|||||||
// every time the submenu opens, so newly-loaded profiles appear immediately.
|
// every time the submenu opens, so newly-loaded profiles appear immediately.
|
||||||
getRecentProfilePaths: () => AppConfig.Load().RecentProfiles,
|
getRecentProfilePaths: () => AppConfig.Load().RecentProfiles,
|
||||||
switchToProfile: path => SwitchToRecentProfile(path),
|
switchToProfile: path => SwitchToRecentProfile(path),
|
||||||
|
// Tooltip builder — called once just before the tray icon first becomes visible
|
||||||
|
// (in Minimize) so the shell's NIM_ADD sees the current live state instead of a
|
||||||
|
// stale "starting up" string. Subsequent updates ride on the 1 Hz snapshot tick.
|
||||||
|
buildTooltip: BuildTrayTooltip,
|
||||||
exit: Close);
|
exit: Close);
|
||||||
|
|
||||||
recordingController = new RecordingController(
|
recordingController = new RecordingController(
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ internal sealed class MainFormTrayController : IDisposable
|
|||||||
private readonly Action toggleReceiving;
|
private readonly Action toggleReceiving;
|
||||||
private readonly Func<IReadOnlyList<string>> getRecentProfilePaths;
|
private readonly Func<IReadOnlyList<string>> getRecentProfilePaths;
|
||||||
private readonly Action<string> switchToProfile;
|
private readonly Action<string> switchToProfile;
|
||||||
|
private readonly Func<string> buildTooltip;
|
||||||
private readonly Action exit;
|
private readonly Action exit;
|
||||||
|
|
||||||
private readonly ToolStripMenuItem sendingItem;
|
private readonly ToolStripMenuItem sendingItem;
|
||||||
@@ -51,6 +52,7 @@ internal sealed class MainFormTrayController : IDisposable
|
|||||||
Action toggleReceiving,
|
Action toggleReceiving,
|
||||||
Func<IReadOnlyList<string>> getRecentProfilePaths,
|
Func<IReadOnlyList<string>> getRecentProfilePaths,
|
||||||
Action<string> switchToProfile,
|
Action<string> switchToProfile,
|
||||||
|
Func<string> buildTooltip,
|
||||||
Action exit)
|
Action exit)
|
||||||
{
|
{
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
@@ -60,16 +62,20 @@ internal sealed class MainFormTrayController : IDisposable
|
|||||||
this.toggleReceiving = toggleReceiving;
|
this.toggleReceiving = toggleReceiving;
|
||||||
this.getRecentProfilePaths = getRecentProfilePaths;
|
this.getRecentProfilePaths = getRecentProfilePaths;
|
||||||
this.switchToProfile = switchToProfile;
|
this.switchToProfile = switchToProfile;
|
||||||
|
this.buildTooltip = buildTooltip;
|
||||||
this.exit = exit;
|
this.exit = exit;
|
||||||
|
|
||||||
// Initial tooltip text — deliberately NOT just "RemSound" because some screen
|
// We deliberately don't bake any "starting up" / "running" string into the icon
|
||||||
// readers (NVDA in particular) read tray icons as "<process name>, <tooltip>",
|
// here. The tooltip is computed fresh from buildTooltip() at the moment the icon
|
||||||
// which with a single-word "RemSound" tooltip on a "RemSound" process renders as
|
// first becomes visible (in Minimize) and refreshed every second from MainForm's
|
||||||
// "RemSound RemSound" until the first snapshot tick (~1s after launch) overwrites
|
// snapshot tick after that. The reason: Windows' shell caches the tooltip text
|
||||||
// it. Picking a sensible startup-state string avoids the duplicate read entirely;
|
// it sees at NIM_ADD time and is reluctant to refresh hover text for the same
|
||||||
// the snapshot tick refreshes this with live peer / send / receive info from then
|
// icon ID. Setting an "initial" string here meant that on a slow / minimised-at-
|
||||||
// on.
|
// launch flow (e.g. the resume-after-update path with StartMinimised on), the
|
||||||
trayIcon.Text = "RemSound — starting up";
|
// shell registered the icon with the stale string and kept showing it until the
|
||||||
|
// user hid + re-showed the icon. By computing the right text once, just before
|
||||||
|
// we set Visible = true for the first time, the shell sees the live state from
|
||||||
|
// NIM_ADD onward.
|
||||||
trayIcon.Icon = SystemIcons.Application;
|
trayIcon.Icon = SystemIcons.Application;
|
||||||
trayIcon.Visible = false;
|
trayIcon.Visible = false;
|
||||||
trayIcon.DoubleClick += (_, _) => Restore();
|
trayIcon.DoubleClick += (_, _) => Restore();
|
||||||
@@ -182,6 +188,13 @@ internal sealed class MainFormTrayController : IDisposable
|
|||||||
public void Minimize()
|
public void Minimize()
|
||||||
{
|
{
|
||||||
owner.Hide();
|
owner.Hide();
|
||||||
|
// Refresh the tooltip BEFORE showing the icon so the shell's NIM_ADD call carries
|
||||||
|
// the current live state (peer count, send / receive routing, recording timer),
|
||||||
|
// not a stale "starting up" string set earlier. The shell tends to cache hover
|
||||||
|
// text from NIM_ADD time and is slow to update on subsequent NIM_MODIFY calls —
|
||||||
|
// computing the right text now means the first hover already reads correctly.
|
||||||
|
try { SetTooltip(buildTooltip()); }
|
||||||
|
catch { /* harmless — fall through to the snapshot-tick refresh */ }
|
||||||
trayIcon.Visible = true;
|
trayIcon.Visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
tag_name on the latest GitHub release; bump it on every public release. The
|
tag_name on the latest GitHub release; bump it on every public release. The
|
||||||
AssemblyVersion / FileVersion default to this value, and Assembly.GetName().Version
|
AssemblyVersion / FileVersion default to this value, and Assembly.GetName().Version
|
||||||
is what the About dialog and the updater both read. -->
|
is what the About dialog and the updater both read. -->
|
||||||
<Version>3.1.0</Version>
|
<Version>3.1.1</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user