From 31f1360d1b8a0af53e79d3aed10ae1f9e1226c98 Mon Sep 17 00:00:00 2001 From: bbedward Date: Mon, 14 Jul 2025 14:36:08 -0400 Subject: [PATCH] ipc wallpaper handler, fix high CPU usage --- Common/Colors.qml | 32 ----------- Common/Prefs.qml | 9 ++++ Services/AudioService.qml | 51 +++++++++++++++--- Services/BluetoothService.qml | 14 +++-- Services/CalendarService.qml | 4 +- Services/ProcessMonitorService.qml | 30 ++++++++--- Services/SystemMonitorService.qml | 53 +++++++++++++------ Widgets/AppLauncher.qml | 4 +- .../CenterCommandCenter.qml | 2 +- .../CenterCommandCenter/MediaPlayerWidget.qml | 4 +- Widgets/ProcessListDropdown.qml | 6 +++ Widgets/SpotlightLauncher.qml | 3 +- Widgets/TopBar/ClockWidget.qml | 16 +++--- Widgets/TopBar/TopBar.qml | 2 +- {Scripts => scripts}/README-dynamic-theme.md | 0 {Scripts => scripts}/set-wallpaper.sh | 9 ++-- shell.qml | 37 +++++++++++-- 17 files changed, 186 insertions(+), 90 deletions(-) rename {Scripts => scripts}/README-dynamic-theme.md (100%) rename {Scripts => scripts}/set-wallpaper.sh (87%) diff --git a/Common/Colors.qml b/Common/Colors.qml index 8010deb6..6b55f31f 100644 --- a/Common/Colors.qml +++ b/Common/Colors.qml @@ -101,38 +101,6 @@ Singleton { /* ──────────────── wallpaper change monitor ──────────────── */ property string lastWallpaperTimestamp: "" - Timer { - id: wallpaperMonitorTimer - interval: 1000 // Check every second - repeat: true - - onTriggered: { - wallpaperNotifyMonitor.reload() - } - } - - FileView { - id: wallpaperNotifyMonitor - path: "file://" + notifyPath - - onLoaded: { - var timestamp = wallpaperNotifyMonitor.text() - if (timestamp && timestamp !== lastWallpaperTimestamp) { - console.log("Wallpaper change detected - updating dynamic theme") - lastWallpaperTimestamp = timestamp - - // Only update if we're currently using dynamic theme - if (typeof Theme !== "undefined" && Theme.isDynamicTheme) { - console.log("Triggering color extraction due to wallpaper change") - extractColors() - } - } - } - - onLoadFailed: { - // File doesn't exist yet, this is normal - } - } /* ──────────────── public helper ──────────────── */ function extractColors() { diff --git a/Common/Prefs.qml b/Common/Prefs.qml index 8589bb5d..26ad0b48 100644 --- a/Common/Prefs.qml +++ b/Common/Prefs.qml @@ -34,6 +34,15 @@ Singleton { Component.onCompleted: loadSettings() + // Monitor system resources preference changes to control service monitoring + onShowSystemResourcesChanged: { + console.log("Prefs: System resources monitoring", showSystemResources ? "enabled" : "disabled") + // Control SystemMonitorService based on whether system monitor widgets are visible + if (typeof SystemMonitorService !== 'undefined') { + SystemMonitorService.enableTopBarMonitoring(showSystemResources) + } + } + FileView { id: settingsFile path: StandardPaths.writableLocation(StandardPaths.ConfigLocation) + "/DankMaterialShell/settings.json" diff --git a/Services/AudioService.qml b/Services/AudioService.qml index eb72efb0..ddd48826 100644 --- a/Services/AudioService.qml +++ b/Services/AudioService.qml @@ -16,6 +16,10 @@ Singleton { property var audioSources: [] property string currentAudioSource: "" + // Device scanning control + property bool deviceScanningEnabled: false + property bool initialScanComplete: false + // Real Audio Control Process { id: volumeChecker @@ -51,7 +55,7 @@ Singleton { Process { id: audioSinkLister command: ["pactl", "list", "sinks"] - running: true + running: false stdout: StdioCollector { onStreamFinished: { @@ -136,7 +140,7 @@ Singleton { Process { id: audioSourceLister command: ["pactl", "list", "sources"] - running: true + running: false stdout: StdioCollector { onStreamFinished: { @@ -283,7 +287,9 @@ Singleton { console.log("Audio sink changed successfully") // Refresh current sink and list defaultSinkChecker.running = true - audioSinkLister.running = true + if (root.deviceScanningEnabled) { + audioSinkLister.running = true + } } else { console.error("Failed to change audio sink") } @@ -308,20 +314,51 @@ Singleton { console.log("Audio source changed successfully") // Refresh current source and list defaultSourceChecker.running = true - audioSourceLister.running = true + if (root.deviceScanningEnabled) { + audioSourceLister.running = true + } } else { console.error("Failed to change audio source") } } } - // Timer to refresh audio devices regularly (catches new Bluetooth devices) Timer { - interval: 4000 // 4s refresh to catch new BT devices - running: true; repeat: true + interval: 5000 + running: root.deviceScanningEnabled && root.initialScanComplete + repeat: true onTriggered: { + if (root.deviceScanningEnabled) { + audioSinkLister.running = true + audioSourceLister.running = true + } + } + } + + Component.onCompleted: { + console.log("AudioService: Starting initialization...") + // Do initial device scan + audioSinkLister.running = true + audioSourceLister.running = true + initialScanComplete = true + console.log("AudioService: Initialization complete") + } + + // Control functions for managing device scanning + function enableDeviceScanning(enabled) { + console.log("AudioService: Device scanning", enabled ? "enabled" : "disabled") + root.deviceScanningEnabled = enabled + if (enabled && root.initialScanComplete) { + // Immediately scan when enabled audioSinkLister.running = true audioSourceLister.running = true } } + + // Manual refresh function for when user opens audio settings + function refreshDevices() { + console.log("AudioService: Manual device refresh triggered") + audioSinkLister.running = true + audioSourceLister.running = true + } } \ No newline at end of file diff --git a/Services/BluetoothService.qml b/Services/BluetoothService.qml index bf0b0d3b..0bc94ee3 100644 --- a/Services/BluetoothService.qml +++ b/Services/BluetoothService.qml @@ -193,10 +193,10 @@ Singleton { ', root) } - // Timer to refresh adapter & device state Timer { - interval: 3000 // 3s refresh for more responsive updates - running: true; repeat: true + id: bluetoothMonitorTimer + interval: 5000 + running: false; repeat: true onTriggered: { bluetoothStatusChecker.running = true if (root.bluetoothEnabled) { @@ -208,6 +208,14 @@ Singleton { } } + function enableMonitoring(enabled) { + bluetoothMonitorTimer.running = enabled + if (enabled) { + // Immediately update when enabled + bluetoothStatusChecker.running = true + } + } + property var discoveredDevices: [] // Handle discovered devices diff --git a/Services/CalendarService.qml b/Services/CalendarService.qml index 781687ca..a09aaa16 100644 --- a/Services/CalendarService.qml +++ b/Services/CalendarService.qml @@ -12,10 +12,10 @@ Singleton { property bool isLoading: false property string lastError: "" - // Periodic refresh timer (5 minutes) + // Periodic refresh timer Timer { id: refreshTimer - interval: 300000 // 5 minutes + interval: 60000 running: root.khalAvailable repeat: true onTriggered: { diff --git a/Services/ProcessMonitorService.qml b/Services/ProcessMonitorService.qml index af52b8b2..260e41ad 100644 --- a/Services/ProcessMonitorService.qml +++ b/Services/ProcessMonitorService.qml @@ -10,7 +10,10 @@ Singleton { // Process list properties property var processes: [] property bool isUpdating: false - property int processUpdateInterval: 3000 + property int processUpdateInterval: 1500 + + // Performance control - only run when process monitor is actually visible + property bool monitoringEnabled: false // System information properties property int totalMemoryKB: 0 @@ -109,28 +112,41 @@ Singleton { } } - // System and process monitoring timer + // System and process monitoring timer - now conditional Timer { id: processTimer interval: root.processUpdateInterval - running: true + running: root.monitoringEnabled // Only run when monitoring is enabled repeat: true onTriggered: { - updateSystemInfo() - updateProcessList() + if (root.monitoringEnabled) { + updateSystemInfo() + updateProcessList() + } } } // Public functions function updateSystemInfo() { - if (!systemInfoProcess.running) { + if (!systemInfoProcess.running && root.monitoringEnabled) { systemInfoProcess.running = true } } + // Control functions for enabling/disabling monitoring + function enableMonitoring(enabled) { + console.log("ProcessMonitorService: Monitoring", enabled ? "enabled" : "disabled") + root.monitoringEnabled = enabled + if (enabled) { + // Immediately update when enabled + updateSystemInfo() + updateProcessList() + } + } + function updateProcessList() { - if (!root.isUpdating) { + if (!root.isUpdating && root.monitoringEnabled) { root.isUpdating = true // Update sort command based on current sort option diff --git a/Services/SystemMonitorService.qml b/Services/SystemMonitorService.qml index f5b2f4c0..fa9846ba 100644 --- a/Services/SystemMonitorService.qml +++ b/Services/SystemMonitorService.qml @@ -28,11 +28,14 @@ Singleton { // Temperature properties property real cpuTemperature: 0.0 - // Update intervals - property int cpuUpdateInterval: 1000 - property int memoryUpdateInterval: 2000 - property int temperatureUpdateInterval: 5000 + property int cpuUpdateInterval: 3000 + property int memoryUpdateInterval: 5000 + property int temperatureUpdateInterval: 10000 + // Performance control + property bool enabledForTopBar: true + property bool enabledForDetailedView: false + Component.onCompleted: { console.log("SystemMonitorService: Starting initialization...") getCpuInfo() @@ -176,12 +179,14 @@ Singleton { Timer { id: cpuTimer interval: root.cpuUpdateInterval - running: true + running: root.enabledForTopBar || root.enabledForDetailedView repeat: true onTriggered: { - cpuUsageProcess.running = true - cpuFrequencyProcess.running = true + if (root.enabledForTopBar || root.enabledForDetailedView) { + cpuUsageProcess.running = true + cpuFrequencyProcess.running = true + } } } @@ -189,11 +194,13 @@ Singleton { Timer { id: memoryTimer interval: root.memoryUpdateInterval - running: true + running: root.enabledForTopBar || root.enabledForDetailedView repeat: true onTriggered: { - memoryUsageProcess.running = true + if (root.enabledForTopBar || root.enabledForDetailedView) { + memoryUsageProcess.running = true + } } } @@ -201,11 +208,13 @@ Singleton { Timer { id: temperatureTimer interval: root.temperatureUpdateInterval - running: true + running: root.enabledForDetailedView repeat: true onTriggered: { - temperatureProcess.running = true + if (root.enabledForDetailedView) { + temperatureProcess.running = true + } } } @@ -215,10 +224,22 @@ Singleton { } function updateSystemStats() { - cpuUsageProcess.running = true - memoryUsageProcess.running = true - cpuFrequencyProcess.running = true - temperatureProcess.running = true + if (root.enabledForTopBar || root.enabledForDetailedView) { + cpuUsageProcess.running = true + memoryUsageProcess.running = true + cpuFrequencyProcess.running = true + if (root.enabledForDetailedView) { + temperatureProcess.running = true + } + } + } + + function enableTopBarMonitoring(enabled) { + root.enabledForTopBar = enabled + } + + function enableDetailedMonitoring(enabled) { + root.enabledForDetailedView = enabled } function getCpuUsageColor() { @@ -245,4 +266,4 @@ Singleton { if (cpuTemperature > 65) return "#f39c12" // Orange return "#27ae60" // Green } -} +} \ No newline at end of file diff --git a/Widgets/AppLauncher.qml b/Widgets/AppLauncher.qml index 61d2f5b2..30453b11 100644 --- a/Widgets/AppLauncher.qml +++ b/Widgets/AppLauncher.qml @@ -47,14 +47,12 @@ PanelWindow { onTriggered: updateFilteredModel() } - // Periodic rescan while open Timer { id: periodicRescanTimer - interval: 15000 // 15 seconds + interval: 60000 repeat: true running: launcher.isVisible onTriggered: { - console.log("AppLauncher: Periodic rescan triggered") if (DesktopEntries.rescan) { DesktopEntries.rescan() } diff --git a/Widgets/CenterCommandCenter/CenterCommandCenter.qml b/Widgets/CenterCommandCenter/CenterCommandCenter.qml index 0e3286bc..316d8056 100644 --- a/Widgets/CenterCommandCenter/CenterCommandCenter.qml +++ b/Widgets/CenterCommandCenter/CenterCommandCenter.qml @@ -107,7 +107,7 @@ PanelWindow { radius: parent.radius SequentialAnimation on opacity { - running: true + running: root.calendarVisible loops: Animation.Infinite NumberAnimation { to: 0.08 diff --git a/Widgets/CenterCommandCenter/MediaPlayerWidget.qml b/Widgets/CenterCommandCenter/MediaPlayerWidget.qml index b1ac2541..d1f53a35 100644 --- a/Widgets/CenterCommandCenter/MediaPlayerWidget.qml +++ b/Widgets/CenterCommandCenter/MediaPlayerWidget.qml @@ -36,10 +36,10 @@ Rectangle { return activePlayer && activePlayer.length > 0 ? currentPosition / activePlayer.length : 0 } - // Updates progress bar every second + // Updates progress bar every 2 seconds when playing Timer { id: positionTimer - interval: 1000 + interval: 2000 running: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && activePlayer.length > 0 && !progressMouseArea.isSeeking repeat: true onTriggered: { diff --git a/Widgets/ProcessListDropdown.qml b/Widgets/ProcessListDropdown.qml index c2fe6b40..d458724a 100644 --- a/Widgets/ProcessListDropdown.qml +++ b/Widgets/ProcessListDropdown.qml @@ -17,6 +17,12 @@ PanelWindow { visible: isVisible + // Monitor process dropdown visibility to enable/disable process monitoring + onIsVisibleChanged: { + console.log("Process dropdown", isVisible ? "opened" : "closed") + ProcessMonitorService.enableMonitoring(isVisible) + } + implicitWidth: 600 implicitHeight: 600 diff --git a/Widgets/SpotlightLauncher.qml b/Widgets/SpotlightLauncher.qml index 12c991b2..6af6f0bb 100644 --- a/Widgets/SpotlightLauncher.qml +++ b/Widgets/SpotlightLauncher.qml @@ -32,10 +32,9 @@ PanelWindow { onTriggered: updateFilteredApps() } - // Periodic rescan while open Timer { id: periodicRescanTimer - interval: 15000 // 15 seconds + interval: 60000 repeat: true running: spotlightOpen onTriggered: { diff --git a/Widgets/TopBar/ClockWidget.qml b/Widgets/TopBar/ClockWidget.qml index 66bf95cf..61d2a0e0 100644 --- a/Widgets/TopBar/ClockWidget.qml +++ b/Widgets/TopBar/ClockWidget.qml @@ -1,4 +1,5 @@ import QtQuick +import Quickshell import "../../Common" Rectangle { @@ -51,13 +52,14 @@ Rectangle { } } - Timer { - interval: 1000 - running: true - repeat: true - onTriggered: { - root.currentDate = new Date() - } + SystemClock { + id: systemClock + precision: SystemClock.Seconds + onDateChanged: root.currentDate = systemClock.date + } + + Component.onCompleted: { + root.currentDate = systemClock.date } MouseArea { diff --git a/Widgets/TopBar/TopBar.qml b/Widgets/TopBar/TopBar.qml index f2652906..b70b4652 100644 --- a/Widgets/TopBar/TopBar.qml +++ b/Widgets/TopBar/TopBar.qml @@ -121,7 +121,7 @@ PanelWindow { radius: parent.radius SequentialAnimation on opacity { - running: true + running: false loops: Animation.Infinite NumberAnimation { to: 0.08 diff --git a/Scripts/README-dynamic-theme.md b/scripts/README-dynamic-theme.md similarity index 100% rename from Scripts/README-dynamic-theme.md rename to scripts/README-dynamic-theme.md diff --git a/Scripts/set-wallpaper.sh b/scripts/set-wallpaper.sh similarity index 87% rename from Scripts/set-wallpaper.sh rename to scripts/set-wallpaper.sh index 585ac8da..3120cb92 100755 --- a/Scripts/set-wallpaper.sh +++ b/scripts/set-wallpaper.sh @@ -10,6 +10,9 @@ mkdir -p "$QS_DIR" LINK="$QS_DIR/current_wallpaper" ln -sf -- "$img" "$LINK" + +# Kill existing swaybg processes before starting new one +pkill -f "swaybg.*$LINK" 2>/dev/null || true swaybg -m fill -i "$LINK" & disown json="$(matugen image "$img" --json hex)" @@ -79,7 +82,5 @@ echo " (use in ghostty: theme = $QS_DIR/generated_ghostty_colors.conf )" niri msg action do-screen-transition --delay-ms 100 -# Notify running shell about wallpaper change (for dynamic theme updates) -NOTIFY_FILE="$QS_DIR/wallpaper_changed" -echo "$(date '+%s')" > "$NOTIFY_FILE" -echo "→ Shell notified: $NOTIFY_FILE" \ No newline at end of file +# Notify running shell about wallpaper change via IPC +qs -c "DankMaterialShell" ipc call wallpaper refresh 2>/dev/null && echo "→ Shell notified via IPC" || echo "→ Shell not running or IPC failed" \ No newline at end of file diff --git a/shell.qml b/shell.qml index 0ab3381c..08272de9 100644 --- a/shell.qml +++ b/shell.qml @@ -23,6 +23,11 @@ ShellRoot { Component.onCompleted: { // Make root accessible to Theme singleton for error handling Theme.rootObj = root + + // Initialize service monitoring states based on preferences + SystemMonitorService.enableTopBarMonitoring(Prefs.showSystemResources) + ProcessMonitorService.enableMonitoring(false) // Start disabled, enable when process dropdown is opened + AudioService.enableDeviceScanning(false) // Start disabled, enable when control center is opened } property bool calendarVisible: false @@ -40,6 +45,17 @@ ShellRoot { property MprisPlayer activePlayer: MprisController.activePlayer property bool hasActiveMedia: activePlayer && (activePlayer.trackTitle || activePlayer.trackArtist) property bool controlCenterVisible: false + + // Monitor control center visibility to enable/disable audio device scanning + onControlCenterVisibleChanged: { + console.log("Control center", controlCenterVisible ? "opened" : "closed") + AudioService.enableDeviceScanning(controlCenterVisible) + BluetoothService.enableMonitoring(controlCenterVisible) + if (controlCenterVisible) { + // Immediately refresh devices when opening control center + AudioService.refreshDevices() + } + } property bool batteryPopupVisible: false property bool powerMenuVisible: false property bool powerConfirmVisible: false @@ -165,14 +181,15 @@ ShellRoot { // Weather configuration - // WiFi Auto-refresh Timer Timer { id: wifiAutoRefreshTimer - interval: 10000 // 10 seconds + interval: 20000 running: root.wifiAutoRefreshEnabled && root.controlCenterVisible repeat: true onTriggered: { - WifiService.scanWifi() + if (root.wifiAutoRefreshEnabled && root.controlCenterVisible) { + WifiService.scanWifi() + } } } @@ -375,4 +392,18 @@ ShellRoot { ClipboardHistory { id: clipboardHistoryPopup } + + IpcHandler { + target: "wallpaper" + + function refresh() { + console.log("Wallpaper IPC: refresh() called") + // Trigger color extraction if using dynamic theme + if (typeof Theme !== "undefined" && Theme.isDynamicTheme) { + console.log("Triggering color extraction due to wallpaper IPC") + Colors.extractColors() + } + return "WALLPAPER_REFRESH_SUCCESS" + } + } } \ No newline at end of file