191 lines
4.8 KiB
Bash
191 lines
4.8 KiB
Bash
#!/usr/bin/env bash
|
|
# dependencies: pipewire (pw-dump), v4l2loopback-dkms, jq, dbus-send (dbus)
|
|
set -euo pipefail
|
|
|
|
JQ_BIN="${JQ:-jq}"
|
|
PW_DUMP_CMD="${PW_DUMP:-pw-dump}"
|
|
|
|
mic=0
|
|
cam=0
|
|
loc=0
|
|
scr=0
|
|
|
|
mic_app=""
|
|
cam_app=""
|
|
loc_app=""
|
|
scr_app=""
|
|
|
|
# mic & camera
|
|
if command -v "$PW_DUMP_CMD" >/dev/null 2>&1 && command -v "$JQ_BIN" >/dev/null 2>&1; then
|
|
dump="$($PW_DUMP_CMD 2>/dev/null || true)"
|
|
|
|
mic="$(
|
|
printf '%s' "$dump" \
|
|
| $JQ_BIN -r '
|
|
[ .[]
|
|
| select(.type=="PipeWire:Interface:Node")
|
|
| select((.info.props."media.class"=="Audio/Source" or .info.props."media.class"=="Audio/Source/Virtual"))
|
|
| select((.info.state=="running") or (.state=="running"))
|
|
] | (if length>0 then 1 else 0 end)
|
|
' 2>/dev/null || echo 0
|
|
)"
|
|
|
|
if [[ "$mic" -eq 1 ]]; then
|
|
mic_app="$(
|
|
printf '%s' "$dump" \
|
|
| $JQ_BIN -r '
|
|
[ .[]
|
|
| select(.type=="PipeWire:Interface:Node")
|
|
| select((.info.props."media.class"=="Stream/Input/Audio"))
|
|
| select((.info.state=="running") or (.state=="running"))
|
|
| .info.props["node.name"]
|
|
] | unique | join(", ")
|
|
' 2>/dev/null || echo ""
|
|
)"
|
|
fi
|
|
|
|
if command -v fuser >/dev/null 2>&1; then
|
|
cam=0
|
|
for dev in /dev/video*; do
|
|
if [ -e "$dev" ] && fuser "$dev" >/dev/null 2>&1; then
|
|
cam=1
|
|
break
|
|
fi
|
|
done
|
|
else
|
|
cam=0
|
|
fi
|
|
|
|
if command -v fuser >/dev/null 2>&1; then
|
|
for dev in /dev/video*; do
|
|
if [ -e "$dev" ] && fuser "$dev" >/dev/null 2>&1; then
|
|
pids=$(fuser "$dev" 2>/dev/null)
|
|
for pid in $pids; do
|
|
pname=$(ps -p "$pid" -o comm=)
|
|
if [[ -n "$pname" ]]; then
|
|
cam_app+="$pname, "
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
cam_app="${cam_app%, }"
|
|
fi
|
|
|
|
fi
|
|
|
|
# location
|
|
if command -v gdbus >/dev/null 2>&1; then
|
|
loc="$(
|
|
if ps aux | grep [g]eoclue >/dev/null 2>&1; then
|
|
echo 1
|
|
else
|
|
echo 0
|
|
fi
|
|
)"
|
|
fi
|
|
|
|
if command -v gdbus >/dev/null 2>&1; then
|
|
if pids=$(pgrep -x geoclue); then
|
|
loc=1
|
|
for pid in $pids; do
|
|
pname=$(ps -p "$pid" -o comm=)
|
|
[[ -n "$pname" ]] && loc_app+="$pname, "
|
|
done
|
|
loc_app="${loc_app%, }"
|
|
else
|
|
loc=0
|
|
fi
|
|
fi
|
|
|
|
# screen sharing
|
|
if command -v "$PW_DUMP_CMD" >/dev/null 2>&1 && command -v "$JQ_BIN" >/dev/null 2>&1; then
|
|
if [[ -z "${dump:-}" ]]; then
|
|
dump="$($PW_DUMP_CMD 2>/dev/null || true)"
|
|
fi
|
|
|
|
scr="$(
|
|
printf '%s' "$dump" \
|
|
| $JQ_BIN -e '
|
|
[ .[]
|
|
| select(.info?.props?)
|
|
| select(
|
|
(.info.props["media.name"]? // "")
|
|
| test("^(xdph-streaming|gsr-default|game capture)")
|
|
)
|
|
]
|
|
| (if length > 0 then true else false end)
|
|
' >/dev/null && echo 1 || echo 0
|
|
)"
|
|
fi
|
|
|
|
if [[ "$scr" -eq 1 ]]; then
|
|
scr_app="$(
|
|
printf '%s' "$dump" \
|
|
| $JQ_BIN -r '
|
|
[ .[]
|
|
| select(.type=="PipeWire:Interface:Node")
|
|
| select((.info.props."media.class"=="Stream/Input/Video") or (.info.props."media.name"=="gsr-default_output") or (.info.props."media.name"=="game capture"))
|
|
| select((.info.state=="running") or (.state=="running"))
|
|
| .info.props["media.name"]
|
|
] | unique | join(", ")
|
|
' 2>/dev/null || echo ""
|
|
)"
|
|
fi
|
|
|
|
# output
|
|
green="#30D158"
|
|
orange="#FF9F0A"
|
|
blue="#0A84FF"
|
|
purple="#9B32FA"
|
|
|
|
dot() {
|
|
local on="$1" color="$2" icon="$3"
|
|
if [[ "$on" -eq 1 ]]; then
|
|
printf '<span foreground="%s">'$icon'</span>' "$color"
|
|
else
|
|
printf ''
|
|
fi
|
|
}
|
|
|
|
dots=()
|
|
mic_dot="$(dot "$mic" "$green" "")"; [[ -n "$mic_dot" ]] && dots+=("$mic_dot")
|
|
cam_dot="$(dot "$cam" "$orange" "")"; [[ -n "$cam_dot" ]] && dots+=("$cam_dot")
|
|
loc_dot="$(dot "$loc" "$blue" "")"; [[ -n "$loc_dot" ]] && dots+=("$loc_dot")
|
|
scr_dot="$(dot "$scr" "$purple" "")"; [[ -n "$scr_dot" ]] && dots+=("$scr_dot")
|
|
|
|
text="${dots[*]}"
|
|
|
|
if [[ -n "$mic_app" ]]; then
|
|
mic_status="Mic: $mic_app"
|
|
else
|
|
mic_status="Mic: off"
|
|
fi
|
|
|
|
if [[ -n "$cam_app" ]]; then
|
|
cam_status="Cam: $cam_app"
|
|
else
|
|
cam_status="Cam: off"
|
|
fi
|
|
|
|
if [[ -n "$loc_app" ]]; then
|
|
loc_status="Location: $loc_app"
|
|
else
|
|
loc_status="Location: off"
|
|
fi
|
|
|
|
if [[ -n "$scr_app" ]]; then
|
|
scr_status="Screen sharing: $scr_app"
|
|
else
|
|
scr_status="Screen sharing: off"
|
|
fi
|
|
|
|
tooltip="$mic_status | $cam_status | $loc_status | $scr_status"
|
|
|
|
classes="privacydot"
|
|
[[ $mic -eq 1 ]] && classes="$classes mic-on" || classes="$classes mic-off"
|
|
[[ $cam -eq 1 ]] && classes="$classes cam-on" || classes="$classes cam-off"
|
|
[[ $loc -eq 1 ]] && classes="$classes loc-on" || classes="$classes loc-off"
|
|
[[ $scr -eq 1 ]] && classes="$classes scr-on" || classes="$classes scr-off"
|
|
|
|
jq -c -n --arg text "$text" --arg tooltip "$tooltip" --arg class "$classes" \
|
|
'{text:$text, tooltip:$tooltip, class:$class}'
|