Route audio to HDMI automatically when an external display is connected
Previously nothing switched PipeWire's default sink, so audio always stayed on the laptop/built-in speakers regardless of HDMI mirroring. Adds /usr/local/bin/kiosk-audio-route.sh, which finds a sink whose name contains 'hdmi' and makes it the default (moving any already-playing streams onto it) whenever an external display is connected, falling back to the non-HDMI sink when it isn't. Called from autostart once PipeWire is confirmed ready, and from kiosk-hotplug.sh alongside the existing display mirroring on every plug/unplug event. Documents the behavior and manual fallback in the README.
This commit is contained in:
@@ -407,6 +407,28 @@ If a forced CVT mode is rejected by the display (blank screen only after mirrori
|
||||
sudo -u kiosk DISPLAY=:0 XAUTHORITY=/home/kiosk/.Xauthority xrandr --output HDMI2 --mode 1360x768 --same-as eDP1
|
||||
```
|
||||
|
||||
**No sound over HDMI (audio only from laptop/built-in speakers):**
|
||||
|
||||
Whenever an external display is connected/mirrored, `kiosk-audio-route.sh` switches PipeWire's default sink to whichever sink's name contains `hdmi`, and moves any already-playing audio stream onto it. It's called at kiosk login (after PipeWire is confirmed ready) and by `kiosk-hotplug.service` on every plug/unplug — see `/usr/local/bin/kiosk-mirror-display.sh` above for the display side of the same hotplug event.
|
||||
|
||||
```bash
|
||||
# List sinks and confirm an HDMI one exists (name will contain "hdmi")
|
||||
sudo -u kiosk pactl list sinks short
|
||||
|
||||
# Check what the routing logic actually did
|
||||
journalctl | grep "KIOSK: audio routed\|KIOSK: failed to route" | tail -10
|
||||
|
||||
# Check current default sink
|
||||
sudo -u kiosk pactl get-default-sink
|
||||
|
||||
# Manually re-trigger routing
|
||||
sudo systemctl start kiosk-hotplug.service
|
||||
|
||||
# Force it by hand if needed (replace with your sink name from the list above)
|
||||
sudo -u kiosk pactl set-default-sink alsa_output.pci-0000_00_1f.3.hdmi-stereo
|
||||
```
|
||||
If no sink name contains `hdmi`, the audio codec on that HDMI port either isn't exposed by ALSA/PipeWire on this hardware, or the monitor/TV doesn't report HDMI audio support in its EDID (common on monitors that only do video) — in that case there's no PipeWire-side fix, audio has to come from the laptop speakers or a separate cable.
|
||||
|
||||
**Audio not working:**
|
||||
```bash
|
||||
# Check PipeWire (use menu: Advanced → Audio Diagnostics)
|
||||
@@ -1152,6 +1174,7 @@ See the LICENSE file in the repository for full terms.
|
||||
|
||||
**Recent Updates (v1.0.3):**
|
||||
- **HDMI/external display mirroring:** any connected display beyond the primary (e.g. HDMI-out to a monitor/TV) is now mirrored automatically at the primary's exact resolution — generating a custom `cvt` mode if the external display doesn't natively list it — both at kiosk login/boot and live on plug/unplug via a new udev-triggered `kiosk-hotplug.service`. Previously the external output was left inactive even when detected by X, and would otherwise mirror at its own native resolution instead of matching the kiosk panel
|
||||
- **HDMI audio routing:** audio now follows the same hotplug event — the default PipeWire sink automatically switches to the HDMI audio output when an external display is connected/mirrored, and back to the built-in sink when it's disconnected (`kiosk-audio-route.sh`)
|
||||
- **Package install:** installer now also installs `net-tools` and `ncdu` (alongside the already-installed `curl` and `git`)
|
||||
- **Touch input fix (keyring):** added `--password-store=basic` to the Electron launch. Under LightDM autologin the GNOME keyring stays locked; when Chromium accessed it, the keyring unlock dialog grabbed all keyboard/touch input — the kiosk rendered fine but ignored every tap and keypress. This flag stops Electron from using the keyring, so the dialog never appears.
|
||||
- **Touch gesture fix (libinput):** any touch screen is now forced to the `libinput` driver via `/etc/X11/xorg.conf.d/99-finger-libinput.conf` (matched by hardware capability, so it works on any brand and never affects keyboards, mice, or the pen/stylus). Some drivers — notably `wacom` — only do single-touch pointer emulation and never pass real multitouch to Chromium, so 1-finger and 2-finger swipe gestures could not fire. libinput delivers proper multitouch.
|
||||
|
||||
@@ -7449,6 +7449,53 @@ done
|
||||
MIRRORSCRIPT
|
||||
sudo chmod +x /usr/local/bin/kiosk-mirror-display.sh
|
||||
|
||||
# Shared logic for routing audio to an HDMI audio sink whenever an
|
||||
# external (non-primary) display is connected/mirrored, and back to the
|
||||
# built-in sink when it isn't. Requires PipeWire/pipewire-pulse to
|
||||
# already be running (pactl needs a live socket), so unlike
|
||||
# kiosk-mirror-display.sh this is called later in autostart, after the
|
||||
# "wait for PipeWire/ALSA" steps below — and from kiosk-hotplug.sh, where
|
||||
# the system is already fully booted by the time it fires.
|
||||
sudo tee /usr/local/bin/kiosk-audio-route.sh > /dev/null <<'AUDIOSCRIPT'
|
||||
#!/bin/bash
|
||||
# Assumes DISPLAY/XAUTHORITY are set (for xrandr) and pactl already has a
|
||||
# working PipeWire/pulse socket for the invoking context.
|
||||
|
||||
QUERY=$(xrandr --query 2>/dev/null)
|
||||
[ -z "$QUERY" ] && exit 0
|
||||
|
||||
PRIMARY_OUTPUT=$(echo "$QUERY" | awk '/ primary/{print $1; exit}')
|
||||
[ -z "$PRIMARY_OUTPUT" ] && exit 0
|
||||
|
||||
EXTERNAL_CONNECTED=$(echo "$QUERY" | awk -v p="$PRIMARY_OUTPUT" '/ connected/ && $1!=p{f=1} END{print (f==1)?"yes":"no"}')
|
||||
|
||||
HDMI_SINK=$(pactl list sinks short 2>/dev/null | awk 'tolower($2) ~ /hdmi/{print $2; exit}')
|
||||
NON_HDMI_SINK=$(pactl list sinks short 2>/dev/null | awk 'tolower($2) !~ /hdmi/{print $2; exit}')
|
||||
|
||||
route_to() {
|
||||
local sink="$1" label="$2"
|
||||
if [ -z "$sink" ]; then
|
||||
logger "KIOSK: no $label audio sink found, leaving routing unchanged"
|
||||
return
|
||||
fi
|
||||
pactl set-default-sink "$sink" 2>/dev/null \
|
||||
&& logger "KIOSK: audio routed to $label sink ($sink)" \
|
||||
|| logger "KIOSK: failed to route audio to $label sink ($sink)"
|
||||
pactl list sink-inputs short 2>/dev/null | awk '{print $1}' | while read -r sid; do
|
||||
pactl move-sink-input "$sid" "$sink" 2>/dev/null
|
||||
done
|
||||
pactl set-sink-volume "$sink" 100% 2>/dev/null
|
||||
pactl set-sink-mute "$sink" 0 2>/dev/null
|
||||
}
|
||||
|
||||
if [ "$EXTERNAL_CONNECTED" = "yes" ]; then
|
||||
route_to "$HDMI_SINK" "HDMI"
|
||||
else
|
||||
route_to "$NON_HDMI_SINK" "built-in"
|
||||
fi
|
||||
AUDIOSCRIPT
|
||||
sudo chmod +x /usr/local/bin/kiosk-audio-route.sh
|
||||
|
||||
sudo -u "$KIOSK_USER" tee "$KIOSK_HOME/.config/openbox/autostart" > /dev/null <<'AUTOSTART'
|
||||
#!/bin/bash
|
||||
|
||||
@@ -7525,6 +7572,9 @@ for i in {1..10}; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Route audio to HDMI if an external display is connected/mirrored, else built-in
|
||||
/usr/local/bin/kiosk-audio-route.sh
|
||||
|
||||
# Set audio levels (speakers 100%, mic 100%, mic unmuted)
|
||||
pactl set-sink-volume @DEFAULT_SINK@ 100%
|
||||
pactl set-source-volume @DEFAULT_SOURCE@ 100%
|
||||
@@ -7603,6 +7653,9 @@ AUTOSTART
|
||||
# Give X a moment to finish enumerating the output after the hotplug event
|
||||
sleep 2
|
||||
sudo -u kiosk DISPLAY=:0 XAUTHORITY=/home/kiosk/.Xauthority /usr/local/bin/kiosk-mirror-display.sh
|
||||
|
||||
kiosk_uid=$(id -u kiosk)
|
||||
sudo -u kiosk DISPLAY=:0 XAUTHORITY=/home/kiosk/.Xauthority XDG_RUNTIME_DIR="/run/user/${kiosk_uid}" /usr/local/bin/kiosk-audio-route.sh
|
||||
EOF
|
||||
sudo chmod +x /usr/local/bin/kiosk-hotplug.sh
|
||||
|
||||
|
||||
Reference in New Issue
Block a user