Skip to content

Commit f4425ab

Browse files
authored
Merge pull request #929 from amnweb/fix/wallpaper-engine-resource-cleanup
fix(wallpaper_engine): resolve memory leaks and crashes on rapid wallpaper changes
2 parents efe8c8c + e8505dd commit f4425ab

1 file changed

Lines changed: 41 additions & 17 deletions

File tree

src/core/widgets/services/wallpapers/wallpaper_engine.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,17 @@ def __init__(self, image_path: str, animation: str = "circle") -> None:
260260
super().__init__()
261261
self._image_path = image_path
262262
self._animation = animation
263+
self._progress = 0.0
264+
self._committed = False
265+
self._areas: list[tuple[int, int, int, int, float]] = []
266+
self._per_screen_scaled_old: list[tuple[QPixmap, int, int]] = []
267+
self._per_screen_scaled_new: list[tuple[QPixmap, int, int]] = []
268+
self.fit_mode = _read_fit_mode()
269+
self._bg_color = _read_background_color()
270+
self._pixmap_new = QPixmap()
271+
self._pixmap_old = QPixmap()
272+
self._resources_freed = False
273+
263274
self.setGeometry(QApplication.primaryScreen().geometry())
264275
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True)
265276
self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating, True)
@@ -268,17 +279,6 @@ def __init__(self, image_path: str, animation: str = "circle") -> None:
268279
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
269280
self.winId()
270281

271-
self._pixmap_new = QPixmap()
272-
self._pixmap_old = QPixmap()
273-
274-
self.fit_mode = _read_fit_mode()
275-
self._bg_color = _read_background_color()
276-
self._areas: list[tuple[int, int, int, int, float]] = []
277-
self._per_screen_scaled_old: list[tuple[QPixmap, int, int]] = []
278-
self._per_screen_scaled_new: list[tuple[QPixmap, int, int]] = []
279-
self._progress = 0.0
280-
self._committed = False
281-
282282
self._timeline = QTimeLine(self._ANIMATION_MS, self)
283283
self._timeline.setUpdateInterval(self._FRAME_MS)
284284
self._timeline.setEasingCurve(QEasingCurve.Type.OutQuad)
@@ -287,9 +287,9 @@ def __init__(self, image_path: str, animation: str = "circle") -> None:
287287

288288
def start(self) -> None:
289289
"""Load images asynchronously, then attach to WorkerW and begin animation."""
290-
self._loader = _ImageLoader(self._image_path, _transcoded_wallpaper_path())
291-
self._loader.loaded.connect(self._on_images_loaded)
292-
self._loader.start()
290+
self._wallpaper_loader = _ImageLoader(self._image_path, _transcoded_wallpaper_path())
291+
self._wallpaper_loader.loaded.connect(self._on_images_loaded)
292+
self._wallpaper_loader.start()
293293

294294
def _on_images_loaded(self, new_img: QImage, old_img: QImage) -> None:
295295
self._pixmap_new = QPixmap.fromImage(new_img)
@@ -617,15 +617,39 @@ def paintEvent(self, _) -> None:
617617
p.drawPixmap(dx + ox_n, dy + oy_n, scaled_n)
618618
p.restore()
619619

620+
def _free_resources(self):
621+
if getattr(self, "_resources_freed", True):
622+
return
623+
self._resources_freed = True
624+
625+
for tl in (self._timeline, getattr(self, "_fade_timeline", None)):
626+
if tl and tl.state() != QTimeLine.State.NotRunning:
627+
tl.stop()
628+
629+
self._pixmap_new = QPixmap()
630+
self._pixmap_old = QPixmap()
631+
self._per_screen_scaled_new.clear()
632+
self._per_screen_scaled_old.clear()
633+
634+
wl = getattr(self, "_wallpaper_loader", None)
635+
if wl is not None:
636+
try:
637+
wl.loaded.disconnect()
638+
except TypeError, RuntimeError:
639+
pass
640+
if wl.isRunning():
641+
wl.quit()
642+
wl.wait(1000)
643+
self._wallpaper_loader = None
644+
620645
def nativeEvent(self, _, message):
621646
msg = ctypes.cast(int(message), ctypes.POINTER(wintypes.MSG)).contents
622647
if msg.message == WM_DESTROY:
648+
self._free_resources()
623649
self.deleteLater()
624650
return True, 0
625651
return False, 0
626652

627653
def closeEvent(self, event) -> None:
628-
for tl in (self._timeline, getattr(self, "_fade_timeline", None)):
629-
if tl and tl.state() != QTimeLine.State.NotRunning:
630-
tl.stop()
654+
self._free_resources()
631655
super().closeEvent(event)

0 commit comments

Comments
 (0)