DankMaterialShell icon indicating copy to clipboard operation
DankMaterialShell copied to clipboard

Privacy indicator can not detect screen-sharing and camera

Open YaQia opened this issue 1 month ago • 1 comments

Compositor

  • [x] niri
  • [ ] Hyprland
  • [ ] dwl (MangoWC)
  • [ ] sway
  • [ ] Other (specify)

Distribution

Arch

dms version

v0.6.2

Description

I saw the original commit of privacy indicator: d036684. It has a bash script for that. I fix the script to let it works properly on my laptop:

#!/usr/bin/bash
screencast_count=0
camera_count=0
microphone_count=0
if command -v lsof >/dev/null 2>&1; then
    video_device_users=$(lsof /dev/video* 2>/dev/null | wc -l | tr -d '[:space:]')
    if [ "$video_device_users" -gt 0 ]; then
        camera_count=1
    fi
    if command -v busctl >/dev/null 2>&1; then
        mic_access_count=$(busctl --user call org.freedesktop.portal.Desktop /org/freedesktop/portal/desktop org.freedesktop.DBus.Properties Get ss "org.freedesktop.portal.Inhibit" "Inhibited" 2>/dev/null | grep -c "microphone" | tr -d '[:space:]')
        if [ "$mic_access_count" -gt 0 ]; then
            microphone_count=1
        fi
    fi

    if [ "$microphone_count" -eq 0 ] && command -v pactl >/dev/null 2>&1; then
        total_outputs=$(pactl list short source-outputs | wc -l | tr -d '[:space:]')
        system_outputs=$(pactl list source-outputs | grep -c "media\\.name.*cava" | tr -d '[:space:]')
        user_outputs=$((total_outputs - system_outputs))
        if [ "$user_outputs" -gt 0 ]; then
            microphone_count=1
        fi
    fi
fi

if command -v busctl >/dev/null 2>&1; then
    screencast_sessions=$(busctl --user list | grep "org.freedesktop.portal.Session" | wc -l | tr -d '[:space:]')
    if [ "$screencast_sessions" -gt 0 ]; then
        screencast_count=1
    fi
fi

if command -v pw-dump >/dev/null 2>&1; then
    active_video_streams=$(
        pw-dump |
            jq '[ .[]
                  | select(
                        (.info.props."media.role"? | type == "string")
                        and
                        ((.info.props."media.role" | ascii_downcase) == "camera")
                    )
                  | select(.info.state == "running")
                ] | length'
    )
    if [ "$active_video_streams" -gt 0 ]; then
        camera_count=1
    fi

    active_screen_streams=$(
        pw-dump |
            jq '[ .[]
                  | select(
                        (.info.props."media.role"? | type == "string")
                        and
                        ((.info.props."media.role" | ascii_downcase) == "screen")
                    )
                  | select(.info.state == "running")
                ] | length'
    )
    if [ "$active_screen_streams" -gt 0 ]; then
        screencast_count=1
    fi
fi

echo "screencast:$screencast_count"
echo "camera:$camera_count"
echo "microphone:$microphone_count"

Expected Behavior

Steps to Reproduce

Error Messages/Logs

Screenshots/Recordings

https://github.com/user-attachments/assets/70010b8b-e746-4294-954f-58dc3e7909b4

YaQia avatar Nov 29 '25 05:11 YaQia

Using in Hyprland. Mic is not detected. From my understanding I think that's because you are checking cava usage (that I don't use). The current pipewire check detects it

For the camera it works well for me if using the camera app (not detected by the current version) but not if used for a team meeting in the PWA app - Brave browser (still trying to understand how to get the info)

Idem for the screensharing. It's not detected

pihomeserver avatar Nov 29 '25 09:11 pihomeserver

On Niri / Fedora here. The new mic logic works fine for me here, but I should note that the busctl command fails for me whether or not my mic is on:

$ busctl --user call org.freedesktop.portal.Desktop /org/freedesktop/portal/desktop org.freedesktop.DBus.Properties Get ss "org.freedesktop.portal.Inhibit" "Inhibited"
Call failed: No such property “Inhibited”

re: pihomeserver's comment

I think that's because you are checking cava usage (that I don't use). The current pipewire check detects it

That's line is actually excluding cava in this logic. Cava shows up as a device in the output of both pactl and pw-dump. That line counts the number of cava instances with grep -c, then subtracts it from the number of outputs in total.

That is confusing though... Maybe instead of doing this math we could just get the outputs in detail and then just count those that don't have a media.name prop like .*cava:

 pactl -f json list source-outputs |
      jq 'map(
              select(
                  (.properties."media.name" | test(".*cava") | not)
                  and
                  (.properties."application.name" != "cava")
              )
          ) | length'

In fact, after taking a closer look at all the parts here, you can do all this in one fell swoop using pw-dump and some jq functions:

pw-dump |
    jq -r 'def not_null: . != null;
           def is_cast:  ."media.class" | (not_null and test("Stream/Output/Video"));
           def is_audio: ."media.class" | (not_null and test("Stream/Input/Audio"));
           def is_video: ."media.class" | (not_null and test("Stream/Input/Video"));
           def not_cava: ."application.name" | (not_null and (test("cava") | not));
           def input_test(f): if map(.info.props | select (f)) | length > 0 then "1" else "0" end;

           . as $in | [
               "screencast:" + ($in | input_test(is_cast)),
               "camera:" + ($in | input_test(is_video)),
               "microphone:" + ($in | input_test(is_audio and not_cava))
           ] | join("\n")'

Output:

screencast:0
camera:1
microphone:1

The only caveat I'd mention here is that I couldn't find a way to have separate camera and screencast values. That is, if there is a screencast which is a "Stream/Output/Video", there will always be a matching "Stream/Input/Video" that appears in the list of cameras from is_video.

Since at the end of the day the counts are being evaluated as being in-use or not that's not a huge deal. But if you are screensharing and have your camera disabled this would still show that the camera is in-use.

Whether or not that's useful for anyone outside of myself I can't say. I tested it with my external webcam and Google Meet and it worked as expected.


Edit: 🤦🏻 Wow yea that referenced commit is so out of date. The current code does away with calling out to bash in the first place and uses native dbus calls instead. Those probably need to be refactored a little.

drfiresign avatar Dec 05 '25 00:12 drfiresign

Works great for all apps except for teams in Brave browser. I will check later if there is a way to detect the camera usage when used

pihomeserver avatar Dec 05 '25 07:12 pihomeserver