pipewire service, lock-aware applets, cpu overall chart, security comments

This commit is contained in:
Damocles 2026-04-27 18:36:07 +02:00
parent e7bf175169
commit 8628b4b27b
13 changed files with 183 additions and 208 deletions

View file

@ -1,6 +1,7 @@
import QtQuick
import "../services" as S
// NOT safe for lock screen - can toggle bluetooth power and connect/disconnect devices
Column {
id: root

View file

@ -11,7 +11,6 @@ Column {
required property color accentColor
property bool active: true
property bool locked: false
property bool _coreActive: false
onActiveChanged: {
@ -141,37 +140,26 @@ Column {
}
}
// Overall CPU utilization chart
Item {
width: root.width
height: 44
Separator {}
Text {
id: _totalLabel
anchors.left: parent.left
anchors.leftMargin: 12
anchors.verticalCenter: parent.verticalCenter
text: S.SystemStats.cpuUsage + "%"
color: S.Theme.loadColor(S.SystemStats.cpuUsage)
font.pixelSize: S.Theme.fontSize - 1
font.family: S.Theme.fontFamily
font.bold: true
width: 32
// Overall CPU utilization
InfoRow {
label: "Total"
value: S.SystemStats.cpuUsage + "% @ " + S.SystemStats.cpuFreqGhz.toFixed(2) + " GHz"
valueColor: S.Theme.loadColor(S.SystemStats.cpuUsage)
}
SparklineCanvas {
anchors.left: _totalLabel.right
anchors.leftMargin: 6
anchors.left: parent.left
anchors.leftMargin: 12
anchors.right: parent.right
anchors.rightMargin: 12
anchors.verticalCenter: parent.verticalCenter
height: 32
history: root._cpuHistory
strokeColor: root.accentColor
colorAt: v => S.Theme.loadColor(v)
active: root.active
}
}
property var _cpuHistory: []
Connections {
@ -184,10 +172,15 @@ Column {
}
}
// Process list - hidden on lock screen (exposes running process names)
Column {
visible: !S.LockService.locked
width: root.width
Separator {}
Item {
width: root.width
width: parent.width
height: 18
Text {
@ -213,7 +206,6 @@ Column {
}
}
// Top processes by CPU
Repeater {
model: root.processes
@ -253,3 +245,4 @@ Column {
height: 4
}
}
}

View file

@ -101,10 +101,15 @@ Column {
active: root.active
}
// Process list - hidden on lock screen (exposes running process names)
Column {
visible: !S.LockService.locked
width: root.width
Separator {}
Item {
width: root.width
width: parent.width
height: 18
Text {
@ -130,7 +135,6 @@ Column {
}
}
// Top processes by memory
Repeater {
model: root.processes
@ -170,3 +174,4 @@ Column {
height: 4
}
}
}

View file

@ -1,6 +1,7 @@
import QtQuick
import "../services" as S
// NOT safe for lock screen - can toggle wifi and connect/disconnect networks
Column {
id: root

View file

@ -3,6 +3,9 @@ import Quickshell
import "../services" as S
import "../modules" as M
// NOT safe for lock screen - notification contents may be sensitive,
// can dismiss/clear notifications and toggle DND
Column {
id: root

View file

@ -2,6 +2,7 @@ import QtQuick
import Quickshell
import "../services" as S
// NOT safe for lock screen - executes system commands (shutdown, reboot, logout, suspend)
Column {
id: root

View file

@ -5,11 +5,9 @@ import "../services" as S
Column {
id: root
required property var sink
required property var sinkList
required property var streamList
required property color accentColor
readonly property var sink: S.PipewireService.sink
property real volume: sink?.audio?.volume ?? 0
property bool muted: sink?.audio?.muted ?? false
readonly property string volumeIcon: muted ? "\uF026" : (volume > 0.5 ? "\uF028" : (volume > 0 ? "\uF027" : "\uF026"))
@ -95,14 +93,15 @@ Column {
}
}
// Device + stream list
// Device + stream list (hidden on lock screen)
Column {
id: deviceList
visible: !S.LockService.locked
width: root.width
// Output devices - only shown when more than one exists
Column {
visible: root.sinkList.length > 1
visible: S.PipewireService.sinks.length > 1
width: parent.width
Separator {}
@ -119,7 +118,7 @@ Column {
}
Repeater {
model: root.sinkList
model: S.PipewireService.sinks
delegate: HoverableListItem {
required property var modelData
@ -149,11 +148,11 @@ Column {
// Streams section
Separator {
visible: root.streamList.length > 0
visible: S.PipewireService.streams.length > 0
}
Text {
visible: root.streamList.length > 0
visible: S.PipewireService.streams.length > 0
width: parent.width
height: visible ? 24 : 0
verticalAlignment: Text.AlignVCenter
@ -165,7 +164,7 @@ Column {
}
Repeater {
model: root.streamList
model: S.PipewireService.streams
delegate: Item {
id: streamEntry

View file

@ -3,7 +3,6 @@ import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Services.Pipewire
import "." as D
import "../services" as S
import "../applets" as C
@ -325,9 +324,6 @@ PanelWindow {
C.VolumeApplet {
width: parent.width
sink: Pipewire.defaultAudioSink
sinkList: root._sinkList
streamList: root._streamList
accentColor: root._accent
}
}
@ -430,30 +426,6 @@ PanelWindow {
active: _memCard.expanded && S.DockState.open
}
PwObjectTracker {
objects: [Pipewire.defaultAudioSink, ...root._streamList]
}
readonly property var _sinkList: {
const sinks = [];
if (Pipewire.nodes) {
for (const node of Pipewire.nodes.values)
if (!node.isStream && node.isSink)
sinks.push(node);
}
return sinks;
}
readonly property var _streamList: {
const streams = [];
if (Pipewire.nodes) {
for (const node of Pipewire.nodes.values)
if (node.isStream && node.audio)
streams.push(node);
}
return streams;
}
Process {
id: _runner
}

View file

@ -1,5 +1,4 @@
import QtQuick
import Quickshell.Services.Pipewire
import "../services" as S
import "../applets" as C
@ -91,11 +90,7 @@ Item {
color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7)
border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3)
border.width: 1
visible: (S.Modules.lock.volume ?? true) && Pipewire.defaultAudioSink !== null
PwObjectTracker {
objects: [Pipewire.defaultAudioSink]
}
visible: (S.Modules.lock.volume ?? true) && S.PipewireService.sink !== null
C.VolumeApplet {
id: _volumeContent
@ -103,9 +98,6 @@ Item {
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 8
sink: Pipewire.defaultAudioSink
sinkList: []
streamList: []
accentColor: S.Theme.base0E
}
}

View file

@ -1,6 +1,5 @@
import QtQuick
import Quickshell
import Quickshell.Services.Pipewire
import "." as M
import "../services" as S
import "../applets" as C
@ -15,43 +14,15 @@ M.BarModule {
panelComponent: Component {
C.VolumeApplet {
width: parent.width
sink: root.sink
sinkList: root._sinkList
streamList: root._streamList
accentColor: root.accentColor
}
}
PwObjectTracker {
objects: [Pipewire.defaultAudioSink, ...root._streamList]
}
readonly property var sink: Pipewire.defaultAudioSink
readonly property real volume: sink?.audio?.volume ?? 0
readonly property bool muted: sink?.audio?.muted ?? false
readonly property real volume: S.PipewireService.volume
readonly property bool muted: S.PipewireService.muted
readonly property string _volumeIcon: muted ? "\uF026" : (volume > 0.5 ? "\uF028" : (volume > 0 ? "\uF027" : "\uF026"))
readonly property color _volumeColor: muted ? S.Theme.base04 : root.accentColor
readonly property var _sinkList: {
const sinks = [];
if (Pipewire.nodes) {
for (const node of Pipewire.nodes.values)
if (!node.isStream && node.isSink)
sinks.push(node);
}
return sinks;
}
readonly property var _streamList: {
const streams = [];
if (Pipewire.nodes) {
for (const node of Pipewire.nodes.values)
if (node.isStream && node.audio)
streams.push(node);
}
return streams;
}
property bool _volumeInit: false
property bool _mutedInit: false
@ -85,9 +56,9 @@ M.BarModule {
WheelHandler {
onWheel: event => {
if (!root.sink?.audio)
if (!S.PipewireService.sink?.audio)
return;
root.sink.audio.volume = Math.max(0, root.sink.audio.volume + (event.angleDelta.y > 0 ? 0.05 : -0.05));
S.PipewireService.sink.audio.volume = Math.max(0, S.PipewireService.sink.audio.volume + (event.angleDelta.y > 0 ? 0.05 : -0.05));
}
}
}

View file

@ -8,6 +8,7 @@ QtObject {
id: root
readonly property bool enabled: S.Modules.lock.enable
property bool locked: false
property string sessionPath: ""
// Lock/unlock requests from logind
@ -15,10 +16,10 @@ QtObject {
signal unlockRequested
// Set logind LockedHint
function setLockedHint(locked) {
function setLockedHint(isLocked) {
if (!sessionPath)
return;
_lockedHint._locked = locked;
root.locked = isLocked;
_lockedHint.running = true;
}
@ -82,7 +83,6 @@ QtObject {
// Set logind LockedHint
property Process _lockedHint: Process {
property bool _locked: false
command: ["busctl", "call", "--system", "org.freedesktop.login1", root.sessionPath || "/org/freedesktop/login1/session/auto", "org.freedesktop.login1.Session", "SetLockedHint", "b", _locked ? "true" : "false"]
command: ["busctl", "call", "--system", "org.freedesktop.login1", root.sessionPath || "/org/freedesktop/login1/session/auto", "org.freedesktop.login1.Session", "SetLockedHint", "b", root.locked ? "true" : "false"]
}
}

View file

@ -0,0 +1,36 @@
pragma Singleton
import QtQuick
import Quickshell.Services.Pipewire
QtObject {
id: root
readonly property var sink: Pipewire.defaultAudioSink
readonly property real volume: sink?.audio?.volume ?? 0
readonly property bool muted: sink?.audio?.muted ?? false
readonly property var sinks: {
const list = [];
if (Pipewire.nodes) {
for (const node of Pipewire.nodes.values)
if (!node.isStream && node.isSink)
list.push(node);
}
return list;
}
readonly property var streams: {
const list = [];
if (Pipewire.nodes) {
for (const node of Pipewire.nodes.values)
if (node.isStream && node.audio)
list.push(node);
}
return list;
}
property PwObjectTracker _tracker: PwObjectTracker {
objects: [Pipewire.defaultAudioSink, ...root.streams]
}
}

View file

@ -12,6 +12,7 @@ singleton MprisService 1.0 MprisService.qml
singleton NetworkService 1.0 NetworkService.qml
singleton NiriIpc 1.0 NiriIpc.qml
singleton NotifService 1.0 NotifService.qml
singleton PipewireService 1.0 PipewireService.qml
singleton PowerProfileService 1.0 PowerProfileService.qml
singleton ScreenshotService 1.0 ScreenshotService.qml
singleton SleepService 1.0 SleepService.qml