@@ -2,10 +2,7 @@ import QtQuick
22import QtQuick.Controls
33import QtQuick.Layouts
44import Quickshell
5- import Quickshell.Io
6- import Quickshell.Services.Pipewire
75import qs.Commons
8- import qs.Modules.Bar.Extras
96import qs.Services.UI
107import qs.Widgets
118
@@ -26,22 +23,25 @@ Item {
2623 readonly property real capsuleHeight: Style .getCapsuleHeightForScreen (screenName)
2724 readonly property real barFontSize: Style .getBarFontSizeForScreen (screenName)
2825
29- property bool micActive: false
30- property bool camActive: false
31- property bool scrActive: false
32- property var micApps: []
33- property var camApps: []
34- property var scrApps: []
26+ // Access main instance for state
27+ readonly property var mainInstance: pluginApi? .mainInstance
28+
29+ property bool micActive: mainInstance ? mainInstance .micActive : false
30+ property bool camActive: mainInstance ? mainInstance .camActive : false
31+ property bool scrActive: mainInstance ? mainInstance .scrActive : false
32+ property var micApps: mainInstance ? mainInstance .micApps : []
33+ property var camApps: mainInstance ? mainInstance .camApps : []
34+ property var scrApps: mainInstance ? mainInstance .scrApps : []
3535
3636 property var cfg: pluginApi? .pluginSettings || ({})
3737 property var defaults: pluginApi? .manifest ? .metadata ? .defaultSettings || ({})
3838
39- property bool hideInactive: cfg .hideInactive ?? defaults .hideInactive
40- property bool removeMargins : cfg .removeMargins ?? defaults .removeMargins
41- property int iconSpacing : cfg .iconSpacing || Style . marginXS
42-
43- property string activeColorKey: cfg .activeColor ?? defaults .activeColor
44- property string inactiveColorKey: cfg .inactiveColor ?? defaults .inactiveColor
39+ property bool hideInactive: cfg .hideInactive ?? defaults .hideInactive ?? false
40+ property bool enableToast : cfg .enableToast ?? defaults .enableToast ?? true
41+ property bool removeMargins : cfg .removeMargins ?? defaults . removeMargins ?? false
42+ property int iconSpacing : cfg . iconSpacing ?? defaults . iconSpacing ?? 4
43+ property string activeColorKey: cfg .activeColor ?? defaults .activeColor ?? " primary "
44+ property string inactiveColorKey: cfg .inactiveColor ?? defaults .inactiveColor ?? " none "
4545
4646 readonly property color activeColor: Color .resolveColorKey (activeColorKey)
4747 readonly property color inactiveColor: inactiveColorKey === " none" ? Qt .alpha (Color .mOnSurfaceVariant , 0.3 ) : Color .resolveColorKey (inactiveColorKey)
@@ -63,156 +63,6 @@ Item {
6363 visible: root .isVisible
6464 opacity: root .isVisible ? 1.0 : 0.0
6565
66- PwObjectTracker {
67- objects: Pipewire .ready ? Pipewire .nodes .values : []
68- }
69-
70- Process {
71- id: cameraCheckProcess
72- running: false
73-
74- command: [" sh" , " -c" , " for dev in /dev/video*; do [ -e \" $dev\" ] && [ -n \" $(find /proc/[0-9]*/fd/ -lname \" $dev\" 2>/dev/null | head -n1)\" ] && echo \" active\" && exit 0; done; exit 1" ]
75-
76- onExited : (code , status ) => {
77- var isActive = code === 0 ;
78- root .camActive = isActive;
79-
80- if (isActive) {
81- cameraAppsProcess .running = true ;
82- } else {
83- root .camApps = [];
84- }
85- }
86- }
87-
88- Process {
89- id: cameraAppsProcess
90- running: false
91-
92- command: [" sh" , " -c" , " for dev in /dev/video*; do [ -e \" $dev\" ] && for fd in /proc/[0-9]*/fd/*; do [ -L \" $fd\" ] && [ \" $(readlink \" $fd\" 2>/dev/null)\" = \" $dev\" ] && ps -p \" $(echo \" $fd\" | cut -d/ -f3)\" -o comm= 2>/dev/null; done; done | sort -u | tr '\\ n' ',' | sed 's/,$//'" ]
93-
94- onExited : (code , status ) => {
95- if (stdout) {
96- var appsString = stdout .trim ();
97- var apps = appsString .length > 0 ? appsString .split (' ,' ) : [];
98- root .camApps = apps;
99- } else {
100- root .camApps = [];
101- }
102- }
103- }
104-
105- Timer {
106- interval: 1000
107- repeat: true
108- running: true
109- triggeredOnStart: true
110- onTriggered: updatePrivacyState ()
111- }
112-
113- function hasNodeLinks (node , links ) {
114- for (var i = 0 ; i < links .length ; i++ ) {
115- var link = links[i];
116- if (link && (link .source === node || link .target === node)) {
117- return true ;
118- }
119- }
120- return false ;
121- }
122-
123- function getAppName (node ) {
124- return node .properties [" application.name" ] || node .nickname || node .name || " " ;
125- }
126-
127- function updateMicrophoneState (nodes , links ) {
128- var appNames = [];
129- var isActive = false ;
130-
131- for (var i = 0 ; i < nodes .length ; i++ ) {
132- var node = nodes[i];
133- if (! node || ! node .isStream || ! node .audio || node .isSink ) continue ;
134- if (! hasNodeLinks (node, links) || ! node .properties ) continue ;
135-
136- var mediaClass = node .properties [" media.class" ] || " " ;
137- if (mediaClass === " Stream/Input/Audio" ) {
138- if (node .properties [" stream.capture.sink" ] === " true" ) {
139- continue ;
140- }
141-
142- isActive = true ;
143- var appName = getAppName (node);
144- if (appName && appNames .indexOf (appName) === - 1 ) {
145- appNames .push (appName);
146- }
147- }
148- }
149-
150- root .micActive = isActive;
151- root .micApps = appNames;
152- }
153-
154- function updateCameraState () {
155- cameraCheckProcess .running = true ;
156- }
157-
158- function isScreenShareNode (node ) {
159- if (! node .properties ) {
160- return false ;
161- }
162-
163- var mediaClass = node .properties [" media.class" ] || " " ;
164-
165- if (mediaClass .indexOf (" Audio" ) >= 0 ) {
166- return false ;
167- }
168-
169- if (mediaClass .indexOf (" Video" ) === - 1 ) {
170- return false ;
171- }
172-
173- var mediaName = (node .properties [" media.name" ] || " " ).toLowerCase ();
174-
175- if (mediaName .match (/ ^ (xdph-streaming| gsr-default| game capture| screen| desktop| display| cast| webrtc| v4l2)/ ) ||
176- mediaName === " gsr-default_output" ||
177- mediaName .match (/ screen-cast| screen-capture| desktop-capture| monitor-capture| window-capture| game-capture/ i )) {
178- return true ;
179- }
180-
181- return false ;
182- }
183-
184- function updateScreenShareState (nodes , links ) {
185- var appNames = [];
186- var isActive = false ;
187-
188- for (var i = 0 ; i < nodes .length ; i++ ) {
189- var node = nodes[i];
190- if (! node || ! hasNodeLinks (node, links) || ! node .properties ) continue ;
191-
192- if (isScreenShareNode (node)) {
193- isActive = true ;
194- var appName = getAppName (node);
195- if (appName && appNames .indexOf (appName) === - 1 ) {
196- appNames .push (appName);
197- }
198- }
199- }
200-
201- root .scrActive = isActive;
202- root .scrApps = appNames;
203- }
204-
205- function updatePrivacyState () {
206- if (! Pipewire .ready ) return ;
207-
208- var nodes = Pipewire .nodes .values || [];
209- var links = Pipewire .links .values || [];
210-
211- updateMicrophoneState (nodes, links);
212- updateCameraState ();
213- updateScreenShareState (nodes, links);
214- }
215-
21666 function buildTooltip () {
21767 var parts = [];
21868
@@ -301,12 +151,14 @@ Item {
301151
302152 MouseArea {
303153 anchors .fill : parent
304- acceptedButtons: Qt .RightButton
154+ acceptedButtons: Qt .RightButton | Qt . LeftButton
305155 hoverEnabled: true
306156
307157 onClicked : function (mouse ) {
308158 if (mouse .button === Qt .RightButton ) {
309159 PanelService .showContextMenu (contextMenu, root, screen );
160+ } else if (mouse .button === Qt .LeftButton ) {
161+ if (pluginApi) pluginApi .openPanel (root .screen , root);
310162 }
311163 }
312164
0 commit comments