mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-26 21:17:47 -04:00
* feat(freebsd): add comprehensive native FreeBSD support * Update BatteryService.qml * Add battery probe completion and fallback updates * Add isVideoSource function and update PwObjectTracker filter * Add isPipewireVideoSource function to AudioService Added a function to check if a Pipewire node is a video source. * Refactor video source check to use AudioService * Remove unnecessary call in BatteryService.qml * Add call to updateFreeBsdBatteryFallback on completion * Simplify AudioService by eliminating redundant bindings Removed unnecessary bindings in the AudioService loader to streamline the loading process. * Improve error handling in CheckUpdates function Handle specific exit error for package upgrade check. * Enhance FreeBSD network detection logic * Implement FreeBSD Wi-Fi hardware discovery in Initialize Added FreeBSD Wi-Fi hardware discovery and updated initialization logic to handle cases where no wpa_supplicant control socket is found. * Implement Ethernet state monitoring in StartMonitoring Added monitoring for Ethernet state updates in StartMonitoring function. * Add Ethernet management functions to backend * Remove unimplemented Ethernet control methods Removed unimplemented Ethernet control methods from WpaSupplicantBackend. * Implement SetWiFiEnabled method for WiFi control Add error handling and state management for WiFi control * Update core/internal/server/network/backend_wpa_ethernet.go Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> * Implement Ethernet monitoring in WpaSupplicantBackend * Refactor startEthernetMonitor and add debounce logic Refactor the startEthernetMonitor function to improve readability and maintainability. Introduce a debounce mechanism for processing relevant FreeBSD route events. * Reorder import statements in backend_pkg.go * Remove unnecessary blank line in backend_wpa_ethernet.go * Implement FreeBSD audio polling based on UI visibility Introduce a polling mechanism for FreeBSD audio device updates based on visibility of UI elements. * Add readonly freeBsdPollingRequested property and optimize polling Introduce a readonly property to determine if FreeBSD polling is requested based on various UI conditions. Update command execution to prioritize polling efficiency. * Simplify freeBsdPollingRequested logic Removed unnecessary conditions from freeBsdPollingRequested property. * Implement reference counting for battery consumers Added reference counting to manage battery information consumers and updated related functions for better battery state handling. --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
183 lines
5.6 KiB
QML
183 lines
5.6 KiB
QML
pragma Singleton
|
|
|
|
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.Services
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
readonly property bool microphoneActive: {
|
|
if (!AudioService.pipewireReady) {
|
|
return false
|
|
}
|
|
|
|
for (let i = 0; i < AudioService.pipewireNodes.length; i++) {
|
|
const node = AudioService.pipewireNodes[i]
|
|
if (!node) {
|
|
continue
|
|
}
|
|
|
|
if (node.properties?.["media.class"] === "Stream/Input/Audio") {
|
|
if (!looksLikeSystemVirtualMic(node)) {
|
|
if (node.audio && node.audio.muted) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
|
|
readonly property bool cameraActive: {
|
|
if (!AudioService.pipewireReady) {
|
|
return false
|
|
}
|
|
|
|
for (let i = 0; i < AudioService.pipewireNodes.length; i++) {
|
|
const node = AudioService.pipewireNodes[i]
|
|
if (!node || !node.ready || node.properties?.["media.role"] === "Screen") {
|
|
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 (!AudioService.pipewireReady) {
|
|
return false
|
|
}
|
|
|
|
for (let i = 0; i < AudioService.pipewireNodes.length; i++) {
|
|
const node = AudioService.pipewireNodes[i]
|
|
if (!node || !node.ready) {
|
|
continue
|
|
}
|
|
|
|
if (AudioService.isPipewireVideoSource(node)) {
|
|
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 screencastSourceIds() {
|
|
const ids = []
|
|
|
|
if (CompositorService.isNiri) {
|
|
for (const cast of NiriService.casts) {
|
|
if (cast && cast.is_active) {
|
|
ids.push("niri:" + cast.stream_id)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!AudioService.pipewireReady) {
|
|
return ids
|
|
}
|
|
|
|
for (let i = 0; i < AudioService.pipewireNodes.length; i++) {
|
|
const node = AudioService.pipewireNodes[i]
|
|
if (!node || !node.ready) {
|
|
continue
|
|
}
|
|
|
|
const isVideoSource = node.properties?.["media.class"] === "Video/Source"
|
|
const isVideoStream = node.properties && node.properties["media.class"] === "Stream/Output/Video"
|
|
if ((isVideoSource || isVideoStream) && looksLikeScreencast(node)) {
|
|
ids.push("pw:" + node.id)
|
|
}
|
|
}
|
|
|
|
return ids.sort()
|
|
}
|
|
|
|
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"
|
|
}
|
|
}
|