Claude/gnome wallpaper auto rotation r1ailq #1

Merged
Quinta0 merged 12 commits from claude/gnome-wallpaper-auto-rotation-r1ailq into main 2026-08-04 20:53:28 +02:00
5 changed files with 58 additions and 8 deletions
Showing only changes of commit d180c90434 - Show all commits
+4 -1
View File
@@ -14,7 +14,10 @@ protection against OLED burn-in.
desktop background. Frames are decoded with GStreamer and rendered desktop background. Frames are decoded with GStreamer and rendered
directly by GNOME Shell's own Clutter stage (no external, incompatible directly by GNOME Shell's own Clutter stage (no external, incompatible
Clutter build involved). Playback can automatically pause on battery Clutter build involved). Playback can automatically pause on battery
power or while a window is fullscreen. power or while a window is fullscreen. Automatic rotation is
automatically suspended while a live wallpaper is actually on screen
(and resumes afterward), since changing the static wallpaper would
otherwise repaint over the video.
- **OLED burn-in protection** — three independent, individually toggled - **OLED burn-in protection** — three independent, individually toggled
techniques: techniques:
- *Pixel shifting*: nudges the background a few pixels on a slow drift - *Pixel shifting*: nudges the background a few pixels on a slow drift
+13 -1
View File
@@ -14,7 +14,19 @@ export default class BenthicBloomExtension extends Extension {
this._logger = new Logger(this._settings, this.metadata.name); this._logger = new Logger(this._settings, this.metadata.name);
this._rotationManager = new RotationManager(this._settings, this._logger); this._rotationManager = new RotationManager(this._settings, this._logger);
this._liveWallpaperManager = new LiveWallpaperManager(this._settings, this._logger); this._liveWallpaperManager = new LiveWallpaperManager(this._settings, this._logger, {
// Rotation changes the static picture-uri GSettings key, which
// makes the shell repaint its own background actor on top of
// the live wallpaper's. Suspend rotation while a live
// wallpaper is actually on screen, independent of any
// user-initiated pause.
onActiveChanged: active => {
if (active)
this._rotationManager.suspend();
else
this._rotationManager.unsuspend();
},
});
this._oledProtectionManager = new OledProtectionManager(this._settings, this._logger, { this._oledProtectionManager = new OledProtectionManager(this._settings, this._logger, {
forceNextWallpaper: maxSeconds => { forceNextWallpaper: maxSeconds => {
if (this._rotationManager.secondsSinceLastChange >= maxSeconds) { if (this._rotationManager.secondsSinceLastChange >= maxSeconds) {
+4 -1
View File
@@ -28,6 +28,7 @@ class BenthicBloomIndicator extends PanelMenu.Button {
nextItem.connect('activate', () => { nextItem.connect('activate', () => {
rotationManager.next().catch(e => console.error(`[BenthicBloom] ${e.message ?? e}`)); rotationManager.next().catch(e => console.error(`[BenthicBloom] ${e.message ?? e}`));
}); });
nextItem.setSensitive(!settings.get_boolean(SettingsKey.LIVE_WALLPAPER_ENABLED));
this.menu.addMenuItem(nextItem); this.menu.addMenuItem(nextItem);
const liveToggle = new PopupMenu.PopupSwitchMenuItem( const liveToggle = new PopupMenu.PopupSwitchMenuItem(
@@ -58,7 +59,9 @@ class BenthicBloomIndicator extends PanelMenu.Button {
rotationToggle.setToggleState(settings.get_boolean(SettingsKey.ROTATION_ENABLED)); rotationToggle.setToggleState(settings.get_boolean(SettingsKey.ROTATION_ENABLED));
}), }),
settings.connect(`changed::${SettingsKey.LIVE_WALLPAPER_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}`, () => { settings.connect(`changed::${SettingsKey.OLED_PROTECTION_ENABLED}`, () => {
oledToggle.setToggleState(settings.get_boolean(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. * that belongs to gnome-shell itself.
*/ */
export class LiveWallpaperManager { export class LiveWallpaperManager {
constructor(settings, logger) { constructor(settings, logger, {onActiveChanged} = {}) {
this._settings = settings; this._settings = settings;
this._logger = logger; this._logger = logger;
this._onActiveChanged = onActiveChanged ?? (() => {});
this._available = false; this._available = false;
this._active = false; this._active = false;
@@ -57,6 +58,13 @@ export class LiveWallpaperManager {
return this._active; return this._active;
} }
_setActive(active) {
if (this._active === active)
return;
this._active = active;
this._onActiveChanged(active);
}
async enable() { async enable() {
await this._loadGstreamer(); await this._loadGstreamer();
@@ -149,7 +157,7 @@ export class LiveWallpaperManager {
this._applyMute(); this._applyMute();
this._playbin.set_state(Gst.State.PLAYING); this._playbin.set_state(Gst.State.PLAYING);
this._active = true; this._setActive(true);
this._paused = false; this._paused = false;
this._connectPowerWatches(); this._connectPowerWatches();
this._logger.debug(`Live wallpaper started: ${path}`); this._logger.debug(`Live wallpaper started: ${path}`);
@@ -336,7 +344,7 @@ export class LiveWallpaperManager {
this._videoWidth = 0; this._videoWidth = 0;
this._videoHeight = 0; this._videoHeight = 0;
this._active = false; this._setActive(false);
this._paused = false; this._paused = false;
} }
} }
+26 -2
View File
@@ -31,6 +31,7 @@ export class RotationManager {
this._timeoutId = 0; this._timeoutId = 0;
this._settingsSignals = []; this._settingsSignals = [];
this._paused = false; this._paused = false;
this._suspended = false;
this._lastChangeTime = GLib.get_monotonic_time(); this._lastChangeTime = GLib.get_monotonic_time();
this._currentPath = null; this._currentPath = null;
this._transitionOverlays = []; this._transitionOverlays = [];
@@ -89,7 +90,7 @@ export class RotationManager {
_restartTimer() { _restartTimer() {
this._clearTimer(); 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; return;
const interval = Math.max( const interval = Math.max(
@@ -118,7 +119,30 @@ export class RotationManager {
this._restartTimer(); 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} = {}) { async next({forced = false} = {}) {
if (this._suspended) {
this._logger.debug('Rotation suspended while live wallpaper is active');
return;
}
if (this._images.length === 0) if (this._images.length === 0)
await this._reloadImages(); await this._reloadImages();
@@ -144,7 +168,7 @@ export class RotationManager {
} }
async previous() { async previous() {
if (this._images.length === 0) if (this._suspended || this._images.length === 0)
return; return;
const mode = this._settings.get_string(SettingsKey.ROTATION_MODE); const mode = this._settings.get_string(SettingsKey.ROTATION_MODE);