diff --git a/modules/Bar.qml b/modules/Bar.qml index 701c70b..25d2570 100644 --- a/modules/Bar.qml +++ b/modules/Bar.qml @@ -57,6 +57,7 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter spacing: M.Theme.barSpacing + M.Privacy {} M.BarGroup { borderColor: M.Theme.base0D M.Clock { diff --git a/modules/Bluetooth.qml b/modules/Bluetooth.qml index ee55f22..9b7a888 100644 --- a/modules/Bluetooth.qml +++ b/modules/Bluetooth.qml @@ -41,8 +41,23 @@ M.BarSection { onStreamFinished: root._parse(text) } } + // Event-driven: watch BlueZ DBus property changes + Process { + id: btMonitor + running: true + command: ["sh", "-c", "dbus-monitor --system \"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path_namespace='/org/bluez'\" 2>/dev/null"] + stdout: SplitParser { + splitMarker: "\n" + onRead: _debounce.restart() + } + } Timer { - interval: M.Modules.bluetooth.interval || 5000 + id: _debounce + interval: 500 + onTriggered: proc.running = true + } + Timer { + interval: 60000 running: true repeat: true onTriggered: proc.running = true diff --git a/modules/Network.qml b/modules/Network.qml index 47c9efc..71b4b69 100644 --- a/modules/Network.qml +++ b/modules/Network.qml @@ -68,8 +68,24 @@ M.BarSection { } } } + // Event-driven: re-poll on any network change + Process { + id: monitor + running: true + command: ["nmcli", "monitor"] + stdout: SplitParser { + splitMarker: "\n" + onRead: _debounce.restart() + } + } Timer { - interval: M.Modules.network.interval || 5000 + id: _debounce + interval: 300 + onTriggered: proc.running = true + } + // Fallback poll + Timer { + interval: 60000 running: true repeat: true onTriggered: proc.running = true diff --git a/modules/PowerProfile.qml b/modules/PowerProfile.qml index c3a2426..483ca0e 100644 --- a/modules/PowerProfile.qml +++ b/modules/PowerProfile.qml @@ -28,8 +28,23 @@ M.BarIcon { onStreamFinished: root.profile = text.trim() } } + // Event-driven: watch power-profiles-daemon DBus changes + Process { + id: ppMonitor + running: true + command: ["sh", "-c", "dbus-monitor --system \"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/net/hadess/PowerProfiles'\" 2>/dev/null"] + stdout: SplitParser { + splitMarker: "\n" + onRead: _debounce.restart() + } + } Timer { - interval: M.Modules.powerProfile.interval || 5000 + id: _debounce + interval: 300 + onTriggered: proc.running = true + } + Timer { + interval: 60000 running: true repeat: true onTriggered: proc.running = true diff --git a/modules/Privacy.qml b/modules/Privacy.qml new file mode 100644 index 0000000..549743b --- /dev/null +++ b/modules/Privacy.qml @@ -0,0 +1,83 @@ +import QtQuick +import QtQuick.Effects +import Quickshell.Services.Pipewire +import "." as M + +Row { + id: root + spacing: M.Theme.moduleSpacing + + readonly property bool _screenShare: { + if (!Pipewire.nodes) return false; + for (const node of Pipewire.nodes.values) { + if (!node.isStream) continue; + const mc = node.properties?.["media.class"] ?? ""; + if (mc.includes("Video")) return true; + } + return false; + } + + readonly property bool _audioIn: { + if (!Pipewire.nodes) return false; + for (const node of Pipewire.nodes.values) { + if (!node.isStream) continue; + const mc = node.properties?.["media.class"] ?? ""; + if (mc === "Stream/Input/Audio") return true; + } + return false; + } + + visible: root._screenShare || root._audioIn + + // Screenshare indicator + Text { + visible: root._screenShare + text: "\uF03D" + color: M.Theme.base08 + font.pixelSize: M.Theme.fontSize + 2 + font.family: M.Theme.iconFontFamily + anchors.verticalCenter: parent.verticalCenter + + layer.enabled: true + layer.effect: MultiEffect { + shadowEnabled: true + shadowColor: M.Theme.base08 + shadowBlur: 0.8 + shadowVerticalOffset: 0 + shadowHorizontalOffset: 0 + } + + SequentialAnimation on opacity { + running: root._screenShare + loops: Animation.Infinite + NumberAnimation { to: 0.4; duration: 600; easing.type: Easing.InOutQuad } + NumberAnimation { to: 1; duration: 600; easing.type: Easing.InOutQuad } + } + } + + // Microphone indicator + Text { + visible: root._audioIn + text: "\uF130" + color: M.Theme.base0B + font.pixelSize: M.Theme.fontSize + 2 + font.family: M.Theme.iconFontFamily + anchors.verticalCenter: parent.verticalCenter + + layer.enabled: true + layer.effect: MultiEffect { + shadowEnabled: true + shadowColor: M.Theme.base0B + shadowBlur: 0.8 + shadowVerticalOffset: 0 + shadowHorizontalOffset: 0 + } + + SequentialAnimation on opacity { + running: root._audioIn + loops: Animation.Infinite + NumberAnimation { to: 0.4; duration: 600; easing.type: Easing.InOutQuad } + NumberAnimation { to: 1; duration: 600; easing.type: Easing.InOutQuad } + } + } +} diff --git a/modules/qmldir b/modules/qmldir index e936add..42004e8 100644 --- a/modules/qmldir +++ b/modules/qmldir @@ -34,3 +34,4 @@ PowerProfile 1.0 PowerProfile.qml IdleInhibitor 1.0 IdleInhibitor.qml Notifications 1.0 Notifications.qml Power 1.0 Power.qml +Privacy 1.0 Privacy.qml