Skip to content

Commit

Permalink
Improve output in privacy block
Browse files Browse the repository at this point in the history
  • Loading branch information
bim9262 committed Feb 13, 2024
1 parent ea473c3 commit dcc010e
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 72 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ icu_datetime = { version = "1.3.0", optional = true }
icu_calendar = { version = "1.3.0", optional = true }
icu_locid = { version = "1.3.0", optional = true }
inotify = "0.10"
itertools = "0.12"
libc = "0.2"
libpulse-binding = { version = "2.0", default-features = false, optional = true }
log = "0.4"
Expand Down
1 change: 1 addition & 0 deletions cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ words:
- inotify
- iowait
- ipapi
- itertools
- iwctl
- kbdd
- kbddbus
Expand Down
65 changes: 54 additions & 11 deletions src/blocks/privacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@
//! - `webcam`
//! - `unknown`

use std::collections::HashSet;

use futures::future::{select_all, try_join_all};

use super::prelude::*;
Expand Down Expand Up @@ -114,8 +112,53 @@ enum Type {
Unknown,
}

// {type: {name: {reader}}
type PrivacyInfo = HashMap<Type, HashMap<String, HashSet<String>>>;
// {type: {source: {destination: count}}
type PrivacyInfo = HashMap<Type, PrivacyInfoInner>;

type PrivacyInfoInnerType = HashMap<String, HashMap<String, usize>>;
#[derive(Default, Debug)]
struct PrivacyInfoInner(PrivacyInfoInnerType);

impl std::ops::Deref for PrivacyInfoInner {
type Target = PrivacyInfoInnerType;
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl std::ops::DerefMut for PrivacyInfoInner {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl std::fmt::Display for PrivacyInfoInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{{ {} }}",
itertools::join(
self.iter().map(|(source, destinations)| {
format!(
"{} => [ {} ]",
source,
itertools::join(
destinations
.iter()
.map(|(destination, count)| if count == &1 {
destination.into()
} else {
format!("{} (x{})", destination, count)
}),
", "
)
)
}),
", ",
)
)
}
}

#[async_trait]
trait PrivacyMonitor {
Expand Down Expand Up @@ -149,12 +192,12 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
loop {
let mut widget = Widget::new().with_format(format.clone());

let mut info = PrivacyInfo::new();
let mut info = PrivacyInfo::default();
//Merge driver info
for driver_info in try_join_all(drivers.iter_mut().map(|driver| driver.get_info())).await? {
for (type_, mapping) in driver_info {
let existing_mapping = info.entry(type_).or_default();
for (source, dest) in mapping {
for (source, dest) in mapping.0 {
existing_mapping.entry(source).or_default().extend(dest);
}
}
Expand All @@ -168,31 +211,31 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
if let Some(info_by_type) = info.get(&Type::Audio) {
values.extend(map! {
"icon_audio" => Value::icon("microphone"),
"info_audio" => Value::text(format!("{:?}", info_by_type))
"info_audio" => Value::text(format!("{}", info_by_type))
});
}
if let Some(info_by_type) = info.get(&Type::AudioSink) {
values.extend(map! {
"icon_audio_sink" => Value::icon("volume"),
"info_audio_sink" => Value::text(format!("{:?}", info_by_type))
"info_audio_sink" => Value::text(format!("{}", info_by_type))
});
}
if let Some(info_by_type) = info.get(&Type::Video) {
values.extend(map! {
"icon_video" => Value::icon("xrandr"),
"info_video" => Value::text(format!("{:?}", info_by_type))
"info_video" => Value::text(format!("{}", info_by_type))
});
}
if let Some(info_by_type) = info.get(&Type::Webcam) {
values.extend(map! {
"icon_webcam" => Value::icon("webcam"),
"info_webcam" => Value::text(format!("{:?}", info_by_type))
"info_webcam" => Value::text(format!("{}", info_by_type))
});
}
if let Some(info_by_type) = info.get(&Type::Unknown) {
values.extend(map! {
"icon_unknown" => Value::icon("unknown"),
"info_unknown" => Value::text(format!("{:?}", info_by_type))
"info_unknown" => Value::text(format!("{}", info_by_type))
});
}

Expand Down
101 changes: 42 additions & 59 deletions src/blocks/privacy/pipewire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use ::pipewire::{
context::Context, core::PW_ID_CORE, keys, main_loop::MainLoop, properties::properties,
spa::utils::dict::DictRef, types::ObjectType,
};
use itertools::Itertools;
use tokio::sync::mpsc::{self, Receiver, Sender};

use std::{collections::HashMap, sync::Mutex, thread};
Expand Down Expand Up @@ -35,7 +36,7 @@ impl Node {
}
}

#[derive(Debug)]
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord)]
struct Link {
link_output_node: u32,
link_input_node: u32,
Expand Down Expand Up @@ -185,6 +186,16 @@ enum NodeDisplay {
Nickname,
}

impl NodeDisplay {
fn map_node(&self, node: &Node) -> String {
match self {
NodeDisplay::Name => node.name.clone(),
NodeDisplay::Description => node.description.clone().unwrap_or(node.name.clone()),
NodeDisplay::Nickname => node.nick.clone().unwrap_or(node.name.clone()),
}
}
}

pub(super) struct Monitor<'a> {
config: &'a Config,
updates: Receiver<()>,
Expand Down Expand Up @@ -215,73 +226,45 @@ impl<'a> PrivacyMonitor for Monitor<'a> {
debug! {"{:?}", node};
}

// The links must be sorted and then dedup'ed since you can multiple links between any given pair of nodes
for Link {
link_output_node,
link_input_node,
..
} in data.links.values()
} in data.links.values().sorted().dedup()
{
if let (Some(output_node), Some(input_node)) = (
let (Some(output_node), Some(input_node)) = (
data.nodes.get(link_output_node),
data.nodes.get(link_input_node),
) {
if input_node.media_class != Some("Audio/Sink".into())
&& !self.config.exclude_output.contains(&output_node.name)
&& !self.config.exclude_input.contains(&input_node.name)
{
let type_ = if input_node.media_class == Some("Stream/Input/Video".into()) {
if output_node.media_role == Some("Camera".into()) {
Type::Webcam
} else {
Type::Video
}
} else if input_node.media_class == Some("Stream/Input/Audio".into()) {
if output_node.media_class == Some("Audio/Sink".into()) {
Type::AudioSink
} else {
Type::Audio
}
) else {
continue;
};
if input_node.media_class != Some("Audio/Sink".into())
&& !self.config.exclude_output.contains(&output_node.name)
&& !self.config.exclude_input.contains(&input_node.name)
{
let type_ = if input_node.media_class == Some("Stream/Input/Video".into()) {
if output_node.media_role == Some("Camera".into()) {
Type::Webcam
} else {
Type::Unknown
};
use NodeDisplay::*;
match self.config.display {
Name => {
mapping
.entry(type_)
.or_default()
.entry(output_node.name.clone())
.or_default()
.insert(input_node.name.clone());
}
Description => {
mapping
.entry(type_)
.or_default()
.entry(
output_node
.description
.clone()
.unwrap_or(output_node.name.clone()),
)
.or_default()
.insert(
input_node
.description
.clone()
.unwrap_or(input_node.name.clone()),
);
}
Nickname => {
mapping
.entry(type_)
.or_default()
.entry(output_node.nick.clone().unwrap_or(output_node.name.clone()))
.or_default()
.insert(input_node.nick.clone().unwrap_or(input_node.name.clone()));
}
Type::Video
}
}
} else if input_node.media_class == Some("Stream/Input/Audio".into()) {
if output_node.media_class == Some("Audio/Sink".into()) {
Type::AudioSink
} else {
Type::Audio
}
} else {
Type::Unknown
};
*mapping
.entry(type_)
.or_default()
.entry(self.config.display.map_node(output_node))
.or_default()
.entry(self.config.display.map_node(input_node))
.or_default() += 1;
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/blocks/privacy/v4l.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,13 @@ impl<'a> PrivacyMonitor for Monitor<'a> {
continue;
}
debug!("{} {:?}", reader, link_path);
mapping
*mapping
.entry(Type::Webcam)
.or_default()
.entry(link_path.to_string_lossy().to_string())
.or_default()
.insert(reader);
.entry(reader)
.or_default() += 1;
debug!("{:?}", mapping);
}
}
Expand Down

0 comments on commit dcc010e

Please sign in to comment.