Suspend rotation while a live wallpaper is on screen

The extension's own log showed rotation kept firing ("Wallpaper
changed to ...") after live wallpaper playback had already started.
Each rotation change writes org.gnome.desktop.background's
picture-uri, which makes GNOME Shell repaint its own background actor
into the same _backgroundGroup our live wallpaper actor lives in,
landing on top of it and hiding the video/GIF entirely.

LiveWallpaperManager now takes an onActiveChanged callback, invoked
only on actual start/stop transitions, which extension.js uses to
suspend/unsuspend RotationManager. This is tracked separately from
user-initiated pause()/resume() so turning live wallpaper off restores
whatever rotation state the user actually had, and next()/previous()
now no-op while suspended so manual/forced-OLED wallpaper changes
can't sneak one in either. The indicator's "Next Wallpaper" item is
greyed out to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
This commit is contained in:
Claude
2026-08-04 00:33:01 +00:00
parent 4a5ef4fe86
commit d180c90434
5 changed files with 58 additions and 8 deletions
+4 -1
View File
@@ -28,6 +28,7 @@ class BenthicBloomIndicator extends PanelMenu.Button {
nextItem.connect('activate', () => {
rotationManager.next().catch(e => console.error(`[BenthicBloom] ${e.message ?? e}`));
});
nextItem.setSensitive(!settings.get_boolean(SettingsKey.LIVE_WALLPAPER_ENABLED));
this.menu.addMenuItem(nextItem);
const liveToggle = new PopupMenu.PopupSwitchMenuItem(
@@ -58,7 +59,9 @@ class BenthicBloomIndicator extends PanelMenu.Button {
rotationToggle.setToggleState(settings.get_boolean(SettingsKey.ROTATION_ENABLED));
}),
settings.connect(`changed::${SettingsKey.LIVE_WALLPAPER_ENABLED}`, () => {
liveToggle.setToggleState(settings.get_boolean(SettingsKey.LIVE_WALLPAPER_ENABLED));
const enabled = settings.get_boolean(SettingsKey.LIVE_WALLPAPER_ENABLED);
liveToggle.setToggleState(enabled);
nextItem.setSensitive(!enabled);
}),
settings.connect(`changed::${SettingsKey.OLED_PROTECTION_ENABLED}`, () => {
oledToggle.setToggleState(settings.get_boolean(SettingsKey.OLED_PROTECTION_ENABLED));
+11 -3
View File
@@ -21,9 +21,10 @@ import {loadGstreamerModules, GSTREAMER_INSTALL_HINT} from './gstreamerAvailabil
* that belongs to gnome-shell itself.
*/
export class LiveWallpaperManager {
constructor(settings, logger) {
constructor(settings, logger, {onActiveChanged} = {}) {
this._settings = settings;
this._logger = logger;
this._onActiveChanged = onActiveChanged ?? (() => {});
this._available = false;
this._active = false;
@@ -57,6 +58,13 @@ export class LiveWallpaperManager {
return this._active;
}
_setActive(active) {
if (this._active === active)
return;
this._active = active;
this._onActiveChanged(active);
}
async enable() {
await this._loadGstreamer();
@@ -149,7 +157,7 @@ export class LiveWallpaperManager {
this._applyMute();
this._playbin.set_state(Gst.State.PLAYING);
this._active = true;
this._setActive(true);
this._paused = false;
this._connectPowerWatches();
this._logger.debug(`Live wallpaper started: ${path}`);
@@ -336,7 +344,7 @@ export class LiveWallpaperManager {
this._videoWidth = 0;
this._videoHeight = 0;
this._active = false;
this._setActive(false);
this._paused = false;
}
}
+26 -2
View File
@@ -31,6 +31,7 @@ export class RotationManager {
this._timeoutId = 0;
this._settingsSignals = [];
this._paused = false;
this._suspended = false;
this._lastChangeTime = GLib.get_monotonic_time();
this._currentPath = null;
this._transitionOverlays = [];
@@ -89,7 +90,7 @@ export class RotationManager {
_restartTimer() {
this._clearTimer();
if (this._paused || !this._settings.get_boolean(SettingsKey.ROTATION_ENABLED))
if (this._paused || this._suspended || !this._settings.get_boolean(SettingsKey.ROTATION_ENABLED))
return;
const interval = Math.max(
@@ -118,7 +119,30 @@ export class RotationManager {
this._restartTimer();
}
/**
* Distinct from user-initiated pause(): called while a live wallpaper
* is actually covering the desktop, so rotation doesn't keep changing
* a static image nobody can see (which also churns the shell's own
* background actor on top of the live wallpaper's). Resuming restores
* whatever the user's own pause() state was, rather than forcing
* rotation back on.
*/
suspend() {
this._suspended = true;
this._clearTimer();
}
unsuspend() {
this._suspended = false;
this._restartTimer();
}
async next({forced = false} = {}) {
if (this._suspended) {
this._logger.debug('Rotation suspended while live wallpaper is active');
return;
}
if (this._images.length === 0)
await this._reloadImages();
@@ -144,7 +168,7 @@ export class RotationManager {
}
async previous() {
if (this._images.length === 0)
if (this._suspended || this._images.length === 0)
return;
const mode = this._settings.get_string(SettingsKey.ROTATION_MODE);