Claude/gnome wallpaper auto rotation r1ailq #1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user