related to memory leak

This commit is contained in:
sankalp committed 2026-04-23 16:09:52 +05:45
1 parent 7eb21daf2f
commit 6577c19dcb
25 files changed
+610 -68

No files matched your search

+1 -5
View File
@@ -510,11 +510,7 @@ class BluetoothConnections(Box):
return False
# Skip if update in progress or client not available/enabled
if (
self._update_in_progress
or not self.client
or not self.client.enabled
):
if self._update_in_progress or not self.client or not self.client.enabled:
return True # Continue monitoring
try:
+1 -5
View File
@@ -510,11 +510,7 @@ class BluetoothConnections(Box):
return False
# Skip if update in progress or client not available/enabled
if (
self._update_in_progress
or not self.client
or not self.client.enabled
):
if self._update_in_progress or not self.client or not self.client.enabled:
return True # Continue monitoring
try:
+41 -2
View File
@@ -184,6 +184,20 @@ class EmbeddedExpandedPlayer(Box):
# This will automatically update as MPRIS players change
pass
def suspend(self):
"""Suspend updates when control center is hidden"""
if hasattr(self, "player_stack") and self.player_stack:
for child in self.player_stack.get_children():
if hasattr(child, "suspend"):
child.suspend()
def resume(self):
"""Resume updates when control center is shown"""
if hasattr(self, "player_stack") and self.player_stack:
for child in self.player_stack.get_children():
if hasattr(child, "resume"):
child.resume()
def destroy(self):
"""Clean up resources and prevent memory leaks"""
logger.debug("🗑️ EmbeddedExpandedPlayer cleanup starting")
@@ -787,7 +801,9 @@ class PlayerBox(Box):
"label",
GObject.BindingFlags.DEFAULT,
lambda _, x: (
re.sub(r"\r?\n", " ", x) if x != "" and x is not None else "No Title"
re.sub(r"\r?\n", " ", x)
if x != "" and x is not None
else "No Title"
), # type: ignore
)
)
@@ -811,7 +827,9 @@ class PlayerBox(Box):
"label",
GObject.BindingFlags.DEFAULT,
lambda _, x: (
re.sub(r"\r?\n", " ", x) if x != "" and x is not None else "No Album"
re.sub(r"\r?\n", " ", x)
if x != "" and x is not None
else "No Album"
), # type: ignore
)
)
@@ -1097,6 +1115,27 @@ class PlayerBox(Box):
# Start new timer and store its ID
self._seekbar_timer_id = invoke_repeater(1000, self._move_seekbar)
def suspend(self):
"""Stop timer and other active updates when hidden"""
if self._seekbar_timer_id:
try:
GLib.source_remove(self._seekbar_timer_id)
except Exception:
pass
self._seekbar_timer_id = None
logger.debug(
f"[PlayerBox] Suspended timer for {self.player.player_name if self.player else 'unknown'}"
)
def resume(self):
"""Restart timer and updates when shown"""
if self.exit or self.player is None:
return
if not self._seekbar_timer_id:
self._seekbar_timer_id = invoke_repeater(1000, self._move_seekbar)
logger.debug(f"[PlayerBox] Resumed timer for {self.player.player_name}")
def _cleanup_temp_files(self):
"""Clean up temporary artwork files."""
for temp_file in self.temp_artwork_files:
+70 -11
View File
@@ -8,6 +8,10 @@ from fabric.widgets.label import Label
from fabric.widgets.scale import Scale
from fabric.widgets.wayland import WaylandWindow as Window
from services.brightness import Brightness
from services.network import NetworkClient
from utils.roam import audio_service, modus_service
from utils.utils import svg_file
from window.controlcenter.bluetooth import (
BluetoothConnections,
set_bluetooth_enabled_with_fallback,
@@ -17,10 +21,6 @@ from window.controlcenter.nightlight import create_night_light_widget
from window.controlcenter.per_app_volume import PerAppVolumeControl
from window.controlcenter.player import PlayerBoxStack, get_shared_mpris_manager
from window.controlcenter.wifi import WifiConnections
from services.brightness import Brightness
from services.network import NetworkClient
from utils.roam import audio_service, modus_service
from utils.utils import svg_file
brightness_service = Brightness.get_initial()
@@ -487,11 +487,27 @@ class ModusControlCenter(Window):
def _on_visibility_changed(self, widget, param):
"""Handle visibility changes for resource management"""
if not self.get_visible():
# Suspend player updates
if hasattr(self, "music_widget") and hasattr(self.music_widget, "suspend"):
self.music_widget.suspend()
if hasattr(self, "_expanded_player_widget") and hasattr(
self._expanded_player_widget, "suspend"
):
self._expanded_player_widget.suspend()
# Just disconnect signals and reset state flags - don't destroy widgets
self._disconnect_signals_when_hidden()
else:
self._initialize_resources()
# Resume player updates
if hasattr(self, "music_widget") and hasattr(self.music_widget, "resume"):
self.music_widget.resume()
if hasattr(self, "_expanded_player_widget") and hasattr(
self._expanded_player_widget, "resume"
):
self._expanded_player_widget.resume()
def _initialize_resources(self):
"""Initialize resources and connect signals when the control center becomes visible."""
if self._resources_initialized:
@@ -512,11 +528,28 @@ class ModusControlCenter(Window):
# Store signal connections as (obj, handler_id) tuples for proper cleanup
self._signal_connections.extend(
[
(audio_service, audio_service.connect("changed", self.audio_changed)),
(audio_service, audio_service.connect("changed", self.volume_changed)),
(modus_service, modus_service.connect("wlan-changed", self.wlan_changed)),
(modus_service, modus_service.connect("bluetooth-changed", self.bluetooth_changed)),
(modus_service, modus_service.connect("dont-disturb-changed", self.dnd_changed)),
(
audio_service,
audio_service.connect("changed", self.audio_changed),
),
(
audio_service,
audio_service.connect("changed", self.volume_changed),
),
(
modus_service,
modus_service.connect("wlan-changed", self.wlan_changed),
),
(
modus_service,
modus_service.connect(
"bluetooth-changed", self.bluetooth_changed
),
),
(
modus_service,
modus_service.connect("dont-disturb-changed", self.dnd_changed),
),
]
)
@@ -525,7 +558,10 @@ class ModusControlCenter(Window):
self.brightness_scale.connect("change-value", self.set_brightness)
self.brightness_scale.connect("scroll-event", self.on_brightness_scroll)
self._signal_connections.append(
(brightness_service, brightness_service.connect("screen", self.brightness_changed))
(
brightness_service,
brightness_service.connect("screen", self.brightness_changed),
)
)
# Connect volume scale signals
@@ -547,7 +583,28 @@ class ModusControlCenter(Window):
def _disconnect_signals_when_hidden(self):
"""Disconnect signals when hidden to reduce resource usage, but keep widgets intact"""
try:
# Just disconnect signals and reset flags - don't destroy widgets
# Actually disconnect the signals we tracked
for obj, handler_id in self._signal_connections:
try:
obj.disconnect(handler_id)
except Exception as e:
logger.warning(f"Failed to disconnect signal: {e}")
self._signal_connections.clear()
# Disconnect direct scale signals (connected in _initialize_resources)
if hasattr(self, "volume_scale"):
try:
self.volume_scale.disconnect_by_func(self.set_volume)
self.volume_scale.disconnect_by_func(self.on_volume_scroll)
except Exception:
pass
if hasattr(self, "brightness_scale"):
try:
self.brightness_scale.disconnect_by_func(self.set_brightness)
self.brightness_scale.disconnect_by_func(self.on_brightness_scroll)
except Exception:
pass
self._signals_connected = False
self._resources_initialized = False
@@ -560,6 +617,8 @@ class ModusControlCenter(Window):
# Reset current view
self.current_view = "main"
logger.debug("Control center signals disconnected while hidden")
except Exception as e:
logger.warning(f"Control center signal disconnection failed: {e}")
+6 -1
View File
@@ -170,7 +170,12 @@ class PerAppVolumeControl(Box):
def _populate_apps(self):
"""Populate the widget with current audio applications"""
# Clear existing widgets
# Clear and DESTROY existing widgets to prevent memory leaks
for child in list(self.apps_container.get_children()):
try:
child.destroy()
except Exception:
pass
self.apps_container.children = []
self._app_widgets.clear()
+48 -1
View File
@@ -259,6 +259,20 @@ class PlayerBoxStack(Box):
super().destroy()
def suspend(self):
"""Suspend all child players when hidden"""
for child in self.player_stack.get_children():
if hasattr(child, "suspend"):
child.suspend()
logger.debug("[PlayerBoxStack] Suspended child players")
def resume(self):
"""Resume all child players when shown"""
for child in self.player_stack.get_children():
if hasattr(child, "resume"):
child.resume()
logger.debug("[PlayerBoxStack] Resumed child players")
def _periodic_cleanup(self):
"""Enhanced cleanup for reuse - clean internal state and free memory"""
try:
@@ -776,7 +790,9 @@ class PlayerBox(Box):
"label",
GObject.BindingFlags.DEFAULT,
lambda _, x: (
re.sub(r"\r?\n", " ", x) if x != "" and x is not None else "No Title"
re.sub(r"\r?\n", " ", x)
if x != "" and x is not None
else "No Title"
), # type: ignore
)
)
@@ -904,6 +920,37 @@ class PlayerBox(Box):
for handler_id in connections:
self._signal_connections.append((self.player, handler_id))
def suspend(self):
"""Disconnect signals when hidden"""
self.exit = True
for obj, handler_id in self._signal_connections:
try:
obj.disconnect(handler_id)
except Exception:
pass
self._signal_connections.clear()
logger.debug(
f"[PlayerBox] Suspended signals for {self.player.player_name if self.player else 'unknown'}"
)
def resume(self):
"""Reconnect signals when shown"""
if self.player is None:
return
self.exit = False
if not self._signal_connections:
connections = bulk_connect(
self.player,
{
"closed": self._on_player_exit,
"notify::playback-status": self._on_playback_change,
"notify::metadata": self._on_metadata,
},
)
for handler_id in connections:
self._signal_connections.append((self.player, handler_id))
logger.debug(f"[PlayerBox] Resumed signals for {self.player.player_name}")
def destroy(self):
"""Clean up all resources when the widget is destroyed."""
# Set exit flag first — stops all callbacks from doing work
+24 -7
View File
@@ -266,7 +266,12 @@ class WifiConnections(Box):
# Wait for network service to be ready — track signal ID
self._signal_ids.append(
(self.network_service, self.network_service.connect("wifi-device-added", self.on_network_ready))
(
self.network_service,
self.network_service.connect(
"wifi-device-added", self.on_network_ready
),
)
)
# Create pull-to-refresh indicator
@@ -392,18 +397,30 @@ class WifiConnections(Box):
# Connect to WiFi service signals — track IDs for cleanup
self._signal_ids.append(
(self.wifi_service, self.wifi_service.connect(
"notify::wireless-enabled", self.on_wifi_enabled_changed
))
(
self.wifi_service,
self.wifi_service.connect(
"notify::wireless-enabled", self.on_wifi_enabled_changed
),
)
)
self._signal_ids.append(
(self.wifi_service, self.wifi_service.connect("changed", self.update_networks))
(
self.wifi_service,
self.wifi_service.connect("changed", self.update_networks),
)
)
self._signal_ids.append(
(self.wifi_service, self.wifi_service.connect("ap-added", self.update_networks))
(
self.wifi_service,
self.wifi_service.connect("ap-added", self.update_networks),
)
)
self._signal_ids.append(
(self.wifi_service, self.wifi_service.connect("ap-removed", self.update_networks))
(
self.wifi_service,
self.wifi_service.connect("ap-removed", self.update_networks),
)
)
# Initial network update