diff options
Diffstat (limited to 'raveos-hyprland-theme/theme-data/dms/Services/PrivacyService.qml')
| -rw-r--r-- | raveos-hyprland-theme/theme-data/dms/Services/PrivacyService.qml | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/dms/Services/PrivacyService.qml b/raveos-hyprland-theme/theme-data/dms/Services/PrivacyService.qml new file mode 100644 index 0000000..4908862 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/dms/Services/PrivacyService.qml @@ -0,0 +1,155 @@ +pragma Singleton + +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Services.Pipewire +import qs.Services + +Singleton { + id: root + + readonly property bool microphoneActive: { + if (!Pipewire.ready || !Pipewire.nodes?.values) { + return false + } + + for (let i = 0; i < Pipewire.nodes.values.length; i++) { + const node = Pipewire.nodes.values[i] + if (!node) { + continue + } + + if ((node.type & PwNodeType.AudioInStream) === PwNodeType.AudioInStream) { + if (!looksLikeSystemVirtualMic(node)) { + if (node.audio && node.audio.muted) { + return false + } + return true + } + } + } + return false + } + + PwObjectTracker { + objects: Pipewire.nodes.values.filter(node => !node.isStream) + } + + readonly property bool cameraActive: { + if (!Pipewire.ready || !Pipewire.nodes?.values) { + return false + } + + for (let i = 0; i < Pipewire.nodes.values.length; i++) { + const node = Pipewire.nodes.values[i] + if (!node || !node.ready) { + continue + } + + if (node.properties && node.properties["media.class"] === "Stream/Input/Video") { + if (node.properties["stream.is-live"] === "true") { + return true + } + } + } + return false + } + + readonly property bool screensharingActive: { + if (CompositorService.isNiri && NiriService.hasActiveCast) { + return true + } + + if (!Pipewire.ready || !Pipewire.nodes?.values) { + return false + } + + for (let i = 0; i < Pipewire.nodes.values.length; i++) { + const node = Pipewire.nodes.values[i] + if (!node || !node.ready) { + continue + } + + if ((node.type & PwNodeType.VideoSource) === PwNodeType.VideoSource) { + if (looksLikeScreencast(node)) { + return true + } + } + + if (node.properties && node.properties["media.class"] === "Stream/Output/Video") { + if (looksLikeScreencast(node)) { + return true + } + } + + if (node.properties && node.properties["media.class"] === "Stream/Input/Audio") { + const mediaName = (node.properties["media.name"] || "").toLowerCase() + const appName = (node.properties["application.name"] || "").toLowerCase() + + if (mediaName.includes("desktop") || appName.includes("screen") || appName === "obs") { + if (node.properties["stream.is-live"] === "true") { + if (node.audio && node.audio.muted) { + return false + } + return true + } + } + } + } + return false + } + + readonly property bool anyPrivacyActive: microphoneActive || cameraActive || screensharingActive + + function looksLikeSystemVirtualMic(node) { + if (!node) { + return false + } + const name = (node.name || "").toLowerCase() + const mediaName = (node.properties && node.properties["media.name"] || "").toLowerCase() + const appName = (node.properties && node.properties["application.name"] || "").toLowerCase() + const combined = name + " " + mediaName + " " + appName + return /cava|monitor|system/.test(combined) + } + + function looksLikeScreencast(node) { + if (!node) { + return false + } + const appName = (node.properties && node.properties["application.name"] || "").toLowerCase() + const nodeName = (node.name || "").toLowerCase() + const mediaName = (node.properties && node.properties["media.name"] || "").toLowerCase() + const combined = appName + " " + nodeName + " " + mediaName + return /xdg-desktop-portal|xdpw|screencast|screen-cast|screen|gnome shell|kwin|obs|niri/.test(combined) + } + + function getMicrophoneStatus() { + return microphoneActive ? "active" : "inactive" + } + + function getCameraStatus() { + return cameraActive ? "active" : "inactive" + } + + function getScreensharingStatus() { + return screensharingActive ? "active" : "inactive" + } + + function getPrivacySummary() { + const active = [] + if (microphoneActive) { + active.push("microphone") + } + if (cameraActive) { + active.push("camera") + } + if (screensharingActive) { + active.push("screensharing") + } + + return active.length > 0 ? `Privacy active: ${active.join(", ")}` : "No privacy concerns detected" + } +} |