From 4179fcee83ce2b3fbc62b56ef7b6d503281d776e Mon Sep 17 00:00:00 2001 From: Thomas Kroll <99196436+tkroll-ionos@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:50:39 +0200 Subject: [PATCH] fix(privacy): detect screen casting on Niri via PipeWire (#2185) Screen sharing was not detected by PrivacyService on Niri because: 1. Niri creates the screencast as a Stream/Output/Video node, but screensharingActive only checked PwNodeType.VideoSource nodes. 2. looksLikeScreencast() only inspected application.name and node.name, missing Niri's node which has an empty application.name but identifies itself via media.name (niri-screen-cast-src). Add Stream/Output/Video to the checked media classes and include media.name in the screencast heuristic. Also add a forward-compatible check for NiriService.hasActiveCast for when Niri gains cast tracking in its IPC. Co-authored-by: Claude Opus 4.6 (1M context) --- quickshell/Services/PrivacyService.qml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/quickshell/Services/PrivacyService.qml b/quickshell/Services/PrivacyService.qml index 42364b0e..49088621 100644 --- a/quickshell/Services/PrivacyService.qml +++ b/quickshell/Services/PrivacyService.qml @@ -6,6 +6,7 @@ import QtQuick import Quickshell import Quickshell.Io import Quickshell.Services.Pipewire +import qs.Services Singleton { id: root @@ -58,6 +59,10 @@ Singleton { } readonly property bool screensharingActive: { + if (CompositorService.isNiri && NiriService.hasActiveCast) { + return true + } + if (!Pipewire.ready || !Pipewire.nodes?.values) { return false } @@ -74,6 +79,12 @@ Singleton { } } + 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() @@ -110,8 +121,9 @@ Singleton { } const appName = (node.properties && node.properties["application.name"] || "").toLowerCase() const nodeName = (node.name || "").toLowerCase() - const combined = appName + " " + nodeName - return /xdg-desktop-portal|xdpw|screencast|screen|gnome shell|kwin|obs/.test(combined) + 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() {