Report mute/hardware status correctly, add Local Mic Mute, fix stale talking indicator

- Mute/deafen status now goes out through clientupdate instead of
  clientedit, which silently rejected client_input_muted/output_muted
  since they are runtime status, not editable client properties.
- Publish client_input_hardware so other clients see "Microphone
  Disabled" instead of silence while another tab holds the capture
  device; track input/output hardware flags for other clients too,
  and show a distinct grey "disabled" icon instead of reusing the red
  "muted" one for both the tree and the info panel.
- Implement TS3's Enable/Disable/Toggle Local Mic Mute hotkeys: they
  silence capture like a real mute, but never touch the published
  mute status or play a sound.
- A speaker's "talking" indicator only ever cleared when its
  zero-length end-of-burst voice packet arrived; if that one UDP
  packet was lost, the indicator stuck until their next burst. Add a
  watchdog that clears it after 200ms of silence from that speaker
  regardless.
This commit is contained in:
2026-08-17 08:43:22 +00:00
parent e228dd4ad0
commit 75ae6b89b2
12 changed files with 198 additions and 15 deletions

View File

@@ -49,6 +49,10 @@ final class HotkeyActions implements HotkeyEngine.Handler {
case MIC_UNMUTE -> setMicMuted(targets, false);
case MIC_TOGGLE -> setMicMuted(targets, !anyMicMuted(targets));
case MIC_LOCAL_MUTE -> setMicLocalMuted(targets, true);
case MIC_LOCAL_UNMUTE -> setMicLocalMuted(targets, false);
case MIC_LOCAL_TOGGLE -> setMicLocalMuted(targets, !anyMicLocalMuted(targets));
case SPEAKER_MUTE -> setDeafened(targets, true);
case SPEAKER_UNMUTE -> setDeafened(targets, false);
case SPEAKER_TOGGLE -> setDeafened(targets, !anyDeafened(targets));
@@ -105,6 +109,11 @@ final class HotkeyActions implements HotkeyEngine.Handler {
frame.refreshAfterHotkey();
}
private void setMicLocalMuted(List<ServerTab> targets, boolean muted) {
for (ServerTab tab : targets) tab.setMicLocalMuted(muted);
frame.refreshAfterHotkey();
}
private void setDeafened(List<ServerTab> targets, boolean deaf) {
for (ServerTab tab : targets) tab.setDeafened(deaf);
frame.refreshAfterHotkey();
@@ -124,6 +133,10 @@ final class HotkeyActions implements HotkeyEngine.Handler {
return tabs.stream().anyMatch(ServerTab::isMicMuted);
}
private static boolean anyMicLocalMuted(List<ServerTab> tabs) {
return tabs.stream().anyMatch(ServerTab::isMicLocalMuted);
}
private static boolean anyDeafened(List<ServerTab> tabs) {
return tabs.stream().anyMatch(ServerTab::isDeafened);
}