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:
@@ -170,6 +170,33 @@ public final class Icons {
|
||||
return themed("OUTPUT_MUTED", size, Icons::paintSpeakerMuted);
|
||||
}
|
||||
|
||||
/** The capture device is unavailable (another tab holds it), as opposed to muted. */
|
||||
public static ImageIcon micDisabled() {
|
||||
return micDisabled(SZ);
|
||||
}
|
||||
|
||||
public static ImageIcon micDisabled(int size) {
|
||||
return themed("HARDWARE_INPUT_MUTED", size, Icons::paintMicDisabled);
|
||||
}
|
||||
|
||||
/** The playback device is unavailable, as opposed to muted/deafened. */
|
||||
public static ImageIcon speakerDisabled() {
|
||||
return speakerDisabled(SZ);
|
||||
}
|
||||
|
||||
public static ImageIcon speakerDisabled(int size) {
|
||||
return themed("HARDWARE_OUTPUT_MUTED", size, Icons::paintSpeakerDisabled);
|
||||
}
|
||||
|
||||
/** TS3's "Local Mic Mute": silenced, but not reported as muted. */
|
||||
public static ImageIcon micLocalMuted() {
|
||||
return micLocalMuted(SZ);
|
||||
}
|
||||
|
||||
public static ImageIcon micLocalMuted(int size) {
|
||||
return themed("INPUT_MUTED_LOCAL", size, Icons::paintMicLocalMuted);
|
||||
}
|
||||
|
||||
/** A channel commander that is not talking. */
|
||||
public static ImageIcon clientCommander(int size) {
|
||||
return themed("PLAYER_COMMANDER_OFF", size, g -> paintPerson(g, Theme.IDLE_CLIENT));
|
||||
@@ -296,6 +323,35 @@ public final class Icons {
|
||||
g.drawLine(2, 2, 14, 14);
|
||||
}
|
||||
|
||||
/** Same shape as {@link #paintMicMuted}, but grey: unavailable, not muted by choice. */
|
||||
private static void paintMicDisabled(Graphics2D g) {
|
||||
g.setColor(Theme.IDLE_CLIENT);
|
||||
g.fillRoundRect(6, 2, 4, 7, 2, 2);
|
||||
g.setStroke(new BasicStroke(1.4f));
|
||||
g.drawArc(4, 6, 8, 6, 200, 140);
|
||||
g.drawLine(8, 12, 8, 14);
|
||||
g.setStroke(new BasicStroke(2f));
|
||||
g.drawLine(2, 2, 14, 14);
|
||||
}
|
||||
|
||||
/** Same shape as {@link #paintSpeakerMuted}, but grey: unavailable, not muted by choice. */
|
||||
private static void paintSpeakerDisabled(Graphics2D g) {
|
||||
g.setColor(Theme.IDLE_CLIENT);
|
||||
g.fillRect(2, 6, 3, 4);
|
||||
int[] xs = {5, 9, 9, 5};
|
||||
int[] ys = {6, 3, 13, 10};
|
||||
g.fillPolygon(xs, ys, 4);
|
||||
g.setStroke(new BasicStroke(2f));
|
||||
g.drawLine(2, 2, 14, 14);
|
||||
}
|
||||
|
||||
/** {@link #paintMicMuted}, marked with a small dot: silenced, but not reported as muted. */
|
||||
private static void paintMicLocalMuted(Graphics2D g) {
|
||||
paintMicMuted(g);
|
||||
g.setColor(Theme.ACCENT);
|
||||
g.fillOval(11, 10, 4, 4);
|
||||
}
|
||||
|
||||
private static void paintConnect(Graphics2D g) {
|
||||
g.setColor(new Color(0x2E8B57));
|
||||
g.setStroke(new BasicStroke(2f));
|
||||
|
||||
Reference in New Issue
Block a user