Release v4.4: keyboard shortcuts are now machine-wide, not per-profile

Fixes issue #14: shortcuts were stored on each Profile, so one set on profile A
didn't apply on profile B and seemed to vanish on switch. They now live in
AppConfig (one set shared by every profile). RemSoundSettingsStore's Load*/Save*
hotkey methods re-point to AppConfig (callers unchanged); the per-profile cache
fields, profile load/save plumbing, and the HotkeySetting helper class are removed.
Profile's HotkeyRecord fields stay only for back-compat deserialization.

On upgrade, shortcuts reset to defaults (there's no single correct set to carry over
since profiles could hold different/partial sets). A one-time startup notice tells
upgraders to re-set them; fresh installs are silently marked done (nothing to reset).

Manual, About changelog and RELEASE_NOTES updated; csproj <Version> 4.4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-06-19 23:11:53 +01:00
co-authored by Claude Opus 4.8
parent 32be3426e7
commit 8a1bc0c813
8 changed files with 153 additions and 199 deletions
+2
View File
@@ -768,6 +768,8 @@ Alt+F4| Close (the standard Windows shortcut)
## 15. Global hotkeys (work even when minimised)
Your keyboard shortcuts are **shared across all your profiles** — set one once and it works on every profile, and stays put when you switch between them. (Before version 4.4 they were saved separately inside each profile, so a shortcut set on one profile wouldn't work on another. If you're upgrading, your shortcuts will have gone back to their defaults the first time you run the new version — just set the ones you use again, once.)
You set these up in the Keyboard shortcuts dialog (Ctrl+K, or Options → Keyboard shortcuts). The dialog is a single list of every hotkey you can set: **Enter** sets the highlighted row, **Del** clears it (back to _not set_), and **Escape** or the Close button closes the dialog. The defaults:
Hotkey| Action| Default
+14 -6
View File
@@ -1,18 +1,26 @@
# RemSound v4.3.1
# RemSound v4.4
A small update: a gentler default update sound.
Keyboard shortcuts are now shared across all your profiles.
The cue that plays when an update is on its way is now quieter by default — enough to let you know it's coming, without breaking your concentration while you're working. As before, you can choose your own update sound, or switch it off, on the Audio cues tab in Preferences.
Until now, shortcuts were saved separately inside each profile — so a shortcut you set on one profile wouldn't work on another, and switching profiles could make a shortcut seem to disappear. From this version they're one set shared by the whole app: set a shortcut once and it works on every profile, and stays put when you switch. This is what people asked for.
Everything else is unchanged from v4.3.
**Because of this change, shortcuts have gone back to their defaults.** If you'd set up any of your own, please set them again in Options → Keyboard shortcuts (Ctrl+K) — you only need to do it once, and a reminder shows the first time you run this version. (Brand-new installs aren't affected.)
## Compatibility
**v4.4 talks to v3.3 through v4.3 with no trouble** — the over-the-network format is unchanged, so you don't have to update both ends at once. (Everyone still needs **v3.3 or newer**, where end-to-end encryption came in.)
## Install
1. Download `RemSound-v4.3.1.zip` from this release.
1. Download `RemSound-v4.4.zip` from this release.
2. Close RemSound.
3. Extract the zip **over your existing RemSound folder**, overwriting program files when prompted. The zip is program files only — it won't touch your settings, profiles, logs or recordings.
4. Run `RemSound.exe`.
## Upgrading
Help → Check for updates installs v4.3.1 with the in-app updater (from v3.6 or newer). On older versions, install by hand using the steps above.
**From v3.6 or newer:** Help → Check for updates installs v4.4 with the in-app updater — and if it can't finish, it puts your old version back exactly as it was.
**From v1.9v3.5:** Check for updates works, but uses your current version's older updater for this one hop. If auto-update has been failing on your machine, install by hand using the steps above.
**v1.8 and earlier:** the auto-updater in those versions can't install updates — install by hand using the steps above.
+2
View File
@@ -810,6 +810,8 @@ Audient USB Audio ASIO Driver &mdash; Pair 3 (channels 5/6): Loop-back 1 (L) / L
<h2 id="hotkeys">15. Global hotkeys (work even when minimised)</h2>
<p>Your keyboard shortcuts are <strong>shared across all your profiles</strong> — set one once and it works on every profile, and stays put when you switch between them. (Before version 4.4 they were saved separately inside each profile, so a shortcut set on one profile wouldn't work on another. If you're upgrading, your shortcuts will have gone back to their defaults the first time you run the new version — just set the ones you use again, once.)</p>
<p>You set these up in the Keyboard shortcuts dialog (Ctrl+K, or Options &rarr; Keyboard shortcuts). The dialog is a single list of every hotkey you can set: <strong>Enter</strong> sets the highlighted row, <strong>Del</strong> clears it (back to <em>not set</em>), and <strong>Escape</strong> or the Close button closes the dialog. The defaults:</p>
<table>
+14
View File
@@ -20,6 +20,20 @@ internal sealed class AboutDialog : Form
/// updates" path.</summary>
private const string ReleaseNotes =
"""
RemSound v4.4
Keyboard shortcuts are now shared across all
your profiles, instead of being saved separately
in each one. Set a shortcut once and it works on
every profile, and stays put when you switch
which is what people asked for.
Because of this change, your shortcuts have gone
back to their defaults. If you'd set up any of
your own, please set them again in Options
Keyboard shortcuts (Ctrl+K). You only need to do
this once.
RemSound v4.3.1
A gentler update sound. The cue that plays when
+51
View File
@@ -1380,6 +1380,8 @@ public sealed class MainForm : Form
/// </summary>
private void RunStartupNotices()
{
if (IsDisposed) return;
MaybeShowKeyboardShortcutsGlobalNotice();
if (IsDisposed) return;
MaybeShowWhatsNewAfterUpdate();
if (IsDisposed) return;
@@ -1431,6 +1433,55 @@ public sealed class MainForm : Form
}
}
/// <summary>One-time notice (v4.4) telling upgraders that keyboard shortcuts have moved from
/// per-profile to machine-wide storage (issue #14), so their shortcuts have reset to defaults and
/// need re-setting. Shown once to anyone who ran an earlier version; on a brand-new install (which
/// has nothing to reset) it's silently marked done and never shown. Must run BEFORE
/// <see cref="MaybeShowWhatsNewAfterUpdate"/>, which overwrites the LastWhatsNewVersion we read to
/// tell upgraders apart from fresh installs.</summary>
private void MaybeShowKeyboardShortcutsGlobalNotice()
{
if (IsDisposed) return;
AppConfig cfg;
try { cfg = AppConfig.Load(); }
catch { return; }
if (cfg.KeyboardShortcutsGlobalNoticeShown) return;
// A non-empty LastWhatsNewVersion means a previous version has run on this machine — i.e. this
// is an upgrade, so there were per-profile shortcuts that have now reset. A fresh install has it
// empty (it's set later, by MaybeShowWhatsNewAfterUpdate) and has nothing to reset.
var isUpgrade = !string.IsNullOrEmpty(cfg.LastWhatsNewVersion);
if (isUpgrade)
{
logFile.Event("keyboard shortcuts: showing one-time 'now shared across profiles' notice");
var page = new TaskDialogPage
{
Caption = "RemSound",
Heading = "Your keyboard shortcuts are now shared across profiles",
Text = "Keyboard shortcuts used to be saved separately for each profile, so a shortcut you set on one "
+ "profile wouldn't work on another. From this version they're shared across all your profiles "
+ "instead — one set for the whole app, which our users have requested.\n\n"
+ "Because of this change, your shortcuts have started fresh at their defaults. If you'd set up any "
+ "shortcuts of your own, please set them again in Options → Keyboard shortcuts (Ctrl+K). You only "
+ "need to do this once — from now on they'll stay put whatever profile you're on.",
Icon = TaskDialogIcon.Information,
Buttons = { TaskDialogButton.OK },
AllowCancel = true,
};
try { ForegroundDialog.Show(owner => TaskDialog.ShowDialog(owner, page)); }
catch (Exception ex) { logFile.Event($"keyboard shortcuts notice failed: {ex.GetType().Name}: {ex.Message}"); }
}
// Mark done either way (shown to upgraders, silently to fresh installs) so it's strictly one-time.
try
{
var fresh = AppConfig.Load();
fresh.KeyboardShortcutsGlobalNoticeShown = true;
fresh.Save();
}
catch { /* harmless — at worst the notice shows again next launch */ }
}
/// <summary>If the user opted in (<see cref="AppConfig.ShowWhatsNewAfterUpdate"/>) and the
/// running version changed since the last launch we recorded, open the About box once so
/// they see what changed in the update just installed. Always records the current version
+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>4.3.1</Version>
<Version>4.4</Version>
</PropertyGroup>
<ItemGroup>
+26
View File
@@ -147,6 +147,32 @@ public sealed class AppConfig
/// days ago are deleted at startup. Range 130, default 14. Only consulted when that flag is on.</summary>
public int PruneOldLogsDays { get; set; } = 14;
/// <summary>The keyboard shortcuts, machine-wide as of v4.4. Before v4.4 these lived on each
/// <see cref="Profile"/>, so a shortcut set on one profile didn't apply on another (issue #14).
/// They now live here — one set shared by every profile, loaded/saved via
/// <see cref="RemSoundSettingsStore"/>'s Load*/Save* hotkey methods. Null = use the built-in default
/// for that action. The old per-profile <see cref="HotkeyRecord"/> fields on <see cref="Profile"/>
/// are kept only so old profile JSONs still deserialise; they are no longer read or written.</summary>
public HotkeyRecord? ReceiveMuteHotkey { get; set; }
public HotkeyRecord? SendMuteHotkey { get; set; }
public HotkeyRecord? TrayHotkey { get; set; }
public HotkeyRecord? VolumeUpHotkey { get; set; }
public HotkeyRecord? VolumeDownHotkey { get; set; }
public HotkeyRecord? ToggleRecordingHotkey { get; set; }
public HotkeyRecord? RemoteVolumeUpHotkey { get; set; }
public HotkeyRecord? RemoteVolumeDownHotkey { get; set; }
public HotkeyRecord? RemoteMuteToggleHotkey { get; set; }
public HotkeyRecord? SystemVolumeUpHotkey { get; set; }
public HotkeyRecord? SystemVolumeDownHotkey { get; set; }
public HotkeyRecord? SystemMuteToggleHotkey { get; set; }
public HotkeyRecord? QuickProfileSwitchHotkey { get; set; }
public HotkeyRecord? SpeakStatusLineHotkey { get; set; }
/// <summary>True once the one-time "your keyboard shortcuts are now shared across profiles" notice
/// has been shown (to an upgrader), or silently marked done on a fresh install that had nothing to
/// reset. Stops the notice re-appearing. v4.4.</summary>
public bool KeyboardShortcutsGlobalNoticeShown { get; set; }
/// <summary>If non-null and a profile with this title exists, RemSound skips the
/// startup profile picker and loads this profile directly. Combine with
/// <see cref="StartMinimised"/> + the Windows auto-start registry entry
+43 -192
View File
@@ -14,145 +14,73 @@ public sealed class RemSoundSettingsStore
{
public RemSoundSettingsStore(string appName) { }
public HotkeyInfo LoadReceiveMuteHotkey() =>
Try(() => Load()?.ReceiveMuteHotkey?.ToHotkeyInfo()) ?? new HotkeyInfo(Keys.R, true, true, true);
public void SaveReceiveMuteHotkey(HotkeyInfo hotkey)
// Keyboard shortcuts are MACHINE-WIDE as of v4.4 — one set shared by every profile, stored in
// AppConfig (issue #14: before v4.4 they lived on each Profile, so a shortcut set on one profile
// didn't apply on another). The Load*/Save* method names are unchanged, so the hotkey controller
// didn't need touching; only the backing store moved from the per-profile cache to AppConfig. The
// built-in defaults below are unchanged.
private static void SaveGlobalHotkey(Action<AppConfig> set)
{
var s = Load() ?? new Settings();
s.ReceiveMuteHotkey = HotkeySetting.From(hotkey);
Save(s);
var c = AppConfig.Load();
set(c);
try { c.Save(); } catch { /* best-effort, like the app's other AppConfig writes */ }
}
public HotkeyInfo LoadReceiveMuteHotkey() =>
AppConfig.Load().ReceiveMuteHotkey?.ToHotkeyInfo() ?? new HotkeyInfo(Keys.R, true, true, true);
public void SaveReceiveMuteHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.ReceiveMuteHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadSendMuteHotkey() =>
Try(() => Load()?.SendMuteHotkey?.ToHotkeyInfo()) ?? new HotkeyInfo(Keys.S, true, true, true);
public void SaveSendMuteHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.SendMuteHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().SendMuteHotkey?.ToHotkeyInfo() ?? new HotkeyInfo(Keys.S, true, true, true);
public void SaveSendMuteHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.SendMuteHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadTrayHotkey() =>
Try(() => Load()?.TrayHotkey?.ToHotkeyInfo()) ?? new HotkeyInfo(Keys.F10, true, true, false);
public void SaveTrayHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.TrayHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().TrayHotkey?.ToHotkeyInfo() ?? new HotkeyInfo(Keys.F10, true, true, false);
public void SaveTrayHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.TrayHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadVolumeUpHotkey() =>
Try(() => Load()?.VolumeUpHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveVolumeUpHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.VolumeUpHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().VolumeUpHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveVolumeUpHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.VolumeUpHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadVolumeDownHotkey() =>
Try(() => Load()?.VolumeDownHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveVolumeDownHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.VolumeDownHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().VolumeDownHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveVolumeDownHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.VolumeDownHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadToggleRecordingHotkey() =>
Try(() => Load()?.ToggleRecordingHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveToggleRecordingHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.ToggleRecordingHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().ToggleRecordingHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveToggleRecordingHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.ToggleRecordingHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadRemoteVolumeUpHotkey() =>
Try(() => Load()?.RemoteVolumeUpHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveRemoteVolumeUpHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.RemoteVolumeUpHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().RemoteVolumeUpHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveRemoteVolumeUpHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.RemoteVolumeUpHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadRemoteVolumeDownHotkey() =>
Try(() => Load()?.RemoteVolumeDownHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveRemoteVolumeDownHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.RemoteVolumeDownHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().RemoteVolumeDownHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveRemoteVolumeDownHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.RemoteVolumeDownHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadRemoteMuteToggleHotkey() =>
Try(() => Load()?.RemoteMuteToggleHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveRemoteMuteToggleHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.RemoteMuteToggleHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().RemoteMuteToggleHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveRemoteMuteToggleHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.RemoteMuteToggleHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadSystemVolumeUpHotkey() =>
Try(() => Load()?.SystemVolumeUpHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveSystemVolumeUpHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.SystemVolumeUpHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().SystemVolumeUpHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveSystemVolumeUpHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.SystemVolumeUpHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadSystemVolumeDownHotkey() =>
Try(() => Load()?.SystemVolumeDownHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveSystemVolumeDownHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.SystemVolumeDownHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().SystemVolumeDownHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveSystemVolumeDownHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.SystemVolumeDownHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadSystemMuteToggleHotkey() =>
Try(() => Load()?.SystemMuteToggleHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveSystemMuteToggleHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.SystemMuteToggleHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().SystemMuteToggleHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveSystemMuteToggleHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.SystemMuteToggleHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadQuickProfileSwitchHotkey() =>
Try(() => Load()?.QuickProfileSwitchHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveQuickProfileSwitchHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.QuickProfileSwitchHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().QuickProfileSwitchHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveQuickProfileSwitchHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.QuickProfileSwitchHotkey = HotkeyRecord.From(hotkey));
public HotkeyInfo LoadSpeakStatusLineHotkey() =>
Try(() => Load()?.SpeakStatusLineHotkey?.ToHotkeyInfo()) ?? HotkeyInfo.Unset;
public void SaveSpeakStatusLineHotkey(HotkeyInfo hotkey)
{
var s = Load() ?? new Settings();
s.SpeakStatusLineHotkey = HotkeySetting.From(hotkey);
Save(s);
}
AppConfig.Load().SpeakStatusLineHotkey?.ToHotkeyInfo() ?? HotkeyInfo.Unset;
public void SaveSpeakStatusLineHotkey(HotkeyInfo hotkey) => SaveGlobalHotkey(c => c.SpeakStatusLineHotkey = HotkeyRecord.From(hotkey));
public bool LoadAcceptRemoteVolumeCommands(bool defaultValue = false) =>
Try(() => Load()?.AcceptRemoteVolumeCommands) ?? defaultValue;
@@ -590,20 +518,9 @@ public sealed class RemSoundSettingsStore
if (profile is null) throw new ArgumentNullException(nameof(profile));
cache = new Settings
{
ReceiveMuteHotkey = profile.ReceiveMuteHotkey is null ? null : HotkeySettingFromRecord(profile.ReceiveMuteHotkey),
SendMuteHotkey = profile.SendMuteHotkey is null ? null : HotkeySettingFromRecord(profile.SendMuteHotkey),
TrayHotkey = profile.TrayHotkey is null ? null : HotkeySettingFromRecord(profile.TrayHotkey),
VolumeUpHotkey = profile.VolumeUpHotkey is null ? null : HotkeySettingFromRecord(profile.VolumeUpHotkey),
VolumeDownHotkey = profile.VolumeDownHotkey is null ? null : HotkeySettingFromRecord(profile.VolumeDownHotkey),
ToggleRecordingHotkey = profile.ToggleRecordingHotkey is null ? null : HotkeySettingFromRecord(profile.ToggleRecordingHotkey),
RemoteVolumeUpHotkey = profile.RemoteVolumeUpHotkey is null ? null : HotkeySettingFromRecord(profile.RemoteVolumeUpHotkey),
RemoteVolumeDownHotkey = profile.RemoteVolumeDownHotkey is null ? null : HotkeySettingFromRecord(profile.RemoteVolumeDownHotkey),
RemoteMuteToggleHotkey = profile.RemoteMuteToggleHotkey is null ? null : HotkeySettingFromRecord(profile.RemoteMuteToggleHotkey),
SystemVolumeUpHotkey = profile.SystemVolumeUpHotkey is null ? null : HotkeySettingFromRecord(profile.SystemVolumeUpHotkey),
SystemVolumeDownHotkey = profile.SystemVolumeDownHotkey is null ? null : HotkeySettingFromRecord(profile.SystemVolumeDownHotkey),
SystemMuteToggleHotkey = profile.SystemMuteToggleHotkey is null ? null : HotkeySettingFromRecord(profile.SystemMuteToggleHotkey),
QuickProfileSwitchHotkey = profile.QuickProfileSwitchHotkey is null ? null : HotkeySettingFromRecord(profile.QuickProfileSwitchHotkey),
SpeakStatusLineHotkey = profile.SpeakStatusLineHotkey is null ? null : HotkeySettingFromRecord(profile.SpeakStatusLineHotkey),
// Keyboard shortcuts are no longer per-profile (v4.4) — they live in AppConfig now and are
// not carried through the profile cache. Profile's HotkeyRecord fields remain only so old
// profile JSONs still deserialise; they're ignored.
AcceptRemoteVolumeCommands = profile.AcceptRemoteVolumeCommands,
MaxLatencyMs = profile.MaxLatencyMs,
Codec = profile.Codec,
@@ -647,20 +564,7 @@ public sealed class RemSoundSettingsStore
{
if (profile is null) throw new ArgumentNullException(nameof(profile));
var s = cache;
profile.ReceiveMuteHotkey = s.ReceiveMuteHotkey is null ? null : HotkeyRecordFromSetting(s.ReceiveMuteHotkey);
profile.SendMuteHotkey = s.SendMuteHotkey is null ? null : HotkeyRecordFromSetting(s.SendMuteHotkey);
profile.TrayHotkey = s.TrayHotkey is null ? null : HotkeyRecordFromSetting(s.TrayHotkey);
profile.VolumeUpHotkey = s.VolumeUpHotkey is null ? null : HotkeyRecordFromSetting(s.VolumeUpHotkey);
profile.VolumeDownHotkey = s.VolumeDownHotkey is null ? null : HotkeyRecordFromSetting(s.VolumeDownHotkey);
profile.ToggleRecordingHotkey = s.ToggleRecordingHotkey is null ? null : HotkeyRecordFromSetting(s.ToggleRecordingHotkey);
profile.RemoteVolumeUpHotkey = s.RemoteVolumeUpHotkey is null ? null : HotkeyRecordFromSetting(s.RemoteVolumeUpHotkey);
profile.RemoteVolumeDownHotkey = s.RemoteVolumeDownHotkey is null ? null : HotkeyRecordFromSetting(s.RemoteVolumeDownHotkey);
profile.RemoteMuteToggleHotkey = s.RemoteMuteToggleHotkey is null ? null : HotkeyRecordFromSetting(s.RemoteMuteToggleHotkey);
profile.SystemVolumeUpHotkey = s.SystemVolumeUpHotkey is null ? null : HotkeyRecordFromSetting(s.SystemVolumeUpHotkey);
profile.SystemVolumeDownHotkey = s.SystemVolumeDownHotkey is null ? null : HotkeyRecordFromSetting(s.SystemVolumeDownHotkey);
profile.SystemMuteToggleHotkey = s.SystemMuteToggleHotkey is null ? null : HotkeyRecordFromSetting(s.SystemMuteToggleHotkey);
profile.QuickProfileSwitchHotkey = s.QuickProfileSwitchHotkey is null ? null : HotkeyRecordFromSetting(s.QuickProfileSwitchHotkey);
profile.SpeakStatusLineHotkey = s.SpeakStatusLineHotkey is null ? null : HotkeyRecordFromSetting(s.SpeakStatusLineHotkey);
// Keyboard shortcuts moved to AppConfig (machine-wide) in v4.4 — no longer written to profiles.
if (s.AcceptRemoteVolumeCommands is bool arvc) profile.AcceptRemoteVolumeCommands = arvc;
if (s.MaxLatencyMs is int ml) profile.MaxLatencyMs = ml;
if (s.Codec is AudioTransportCodec c) profile.Codec = c;
@@ -697,38 +601,8 @@ public sealed class RemSoundSettingsStore
if (s.RecordingSettings is RecordingSettings rs) profile.RecordingSettings = rs.Clone();
}
private static HotkeySetting HotkeySettingFromRecord(HotkeyRecord r) => new()
{
Key = r.Key,
Control = r.Control,
Shift = r.Shift,
Alt = r.Alt,
};
private static HotkeyRecord HotkeyRecordFromSetting(HotkeySetting s) => new()
{
Key = s.Key,
Control = s.Control,
Shift = s.Shift,
Alt = s.Alt,
};
private sealed class Settings
{
public HotkeySetting? ReceiveMuteHotkey { get; set; }
public HotkeySetting? SendMuteHotkey { get; set; }
public HotkeySetting? TrayHotkey { get; set; }
public HotkeySetting? VolumeUpHotkey { get; set; }
public HotkeySetting? VolumeDownHotkey { get; set; }
public HotkeySetting? ToggleRecordingHotkey { get; set; }
public HotkeySetting? RemoteVolumeUpHotkey { get; set; }
public HotkeySetting? RemoteVolumeDownHotkey { get; set; }
public HotkeySetting? RemoteMuteToggleHotkey { get; set; }
public HotkeySetting? SystemVolumeUpHotkey { get; set; }
public HotkeySetting? SystemVolumeDownHotkey { get; set; }
public HotkeySetting? SystemMuteToggleHotkey { get; set; }
public HotkeySetting? QuickProfileSwitchHotkey { get; set; }
public HotkeySetting? SpeakStatusLineHotkey { get; set; }
public bool? AcceptRemoteVolumeCommands { get; set; }
public int? MaxLatencyMs { get; set; }
public AudioTransportCodec? Codec { get; set; }
@@ -769,27 +643,4 @@ public sealed class RemSoundSettingsStore
public RecordingSettings? RecordingSettings { get; set; }
}
private sealed class HotkeySetting
{
public string Key { get; set; } = "M";
public bool Control { get; set; }
public bool Shift { get; set; }
public bool Alt { get; set; }
public static HotkeySetting From(HotkeyInfo hotkey) => new()
{
Key = hotkey.Key.ToString(),
Control = hotkey.Control,
Shift = hotkey.Shift,
Alt = hotkey.Alt,
};
public HotkeyInfo ToHotkeyInfo()
{
if (!Enum.TryParse<Keys>(Key, out var parsedKey)) return HotkeyInfo.Default;
var hotkey = new HotkeyInfo(parsedKey, Control, Shift, Alt);
if (hotkey.IsUnset) return HotkeyInfo.Unset;
return hotkey.IsValid ? hotkey : HotkeyInfo.Default;
}
}
}