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
Showing only changes of commit 2365d303f3 - Show all commits
+58 -11
View File
@@ -1,11 +1,36 @@
import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import Clutter from 'gi://Clutter';
import St from 'gi://St';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import {SettingsKey} from './settingsKeys.js';
import {loadGstreamerModules, GSTREAMER_INSTALL_HINT} from './gstreamerAvailability.js';
/**
* Ways to get a decoded frame onto an actor, tried in order and "pinned"
* once one works. `new Clutter.Image()` failed with "is not a constructor"
* in real-world testing (Clutter.Image is apparently no longer directly
* constructible from GJS in current Mutter); St.ImageContent — the same
* mechanism gnome-shell's own code uses for uploading raw pixel buffers —
* is tried first, with the legacy Clutter.Image/set_data path kept only as
* a fallback for older shells.
*/
const FRAME_IMAGE_STRATEGIES = [
{
name: 'St.ImageContent',
create: (width, height) => St.ImageContent.new_with_preferred_size(width, height),
upload: (image, Cogl, data, width, height) =>
image.set_bytes(GLib.Bytes.new(data), Cogl.PixelFormat.RGBA_8888, width, height, width * 4),
},
{
name: 'Clutter.Image',
create: () => new Clutter.Image(),
upload: (image, Cogl, data, width, height) =>
image.set_data(data, Cogl.PixelFormat.RGBA_8888, width, height, width * 4),
},
];
/**
* Plays a video or animated GIF file as a looping animated background.
* `playbin` typefinds the source by content rather than extension, so GIFs
@@ -53,6 +78,8 @@ export class LiveWallpaperManager {
this._pollCount = 0;
this._pollTimeoutId = 0;
this._noFrameWatchdogId = 0;
this._imageStrategyIndex = 0;
this._frameErrorCount = 0;
}
get isAvailable() {
@@ -219,6 +246,8 @@ export class LiveWallpaperManager {
this._frameCount = 0;
this._pollCount = 0;
this._imageStrategyIndex = 0;
this._frameErrorCount = 0;
// ~30fps polling of the appsink from the main thread. See the
// comment on appsink's properties above for why this replaces
// a 'new-sample' signal handler.
@@ -282,7 +311,14 @@ export class LiveWallpaperManager {
buffer.unmap(mapInfo);
}
} catch (e) {
this._logger.error(e, 'Failed to poll live wallpaper frame');
// Rate-limited: this runs at ~30fps, so logging every failure
// would flood the journal (and this loop keeps retrying every
// frame, e.g. while cycling through FRAME_IMAGE_STRATEGIES).
this._frameErrorCount++;
if (this._frameErrorCount === 1 || this._frameErrorCount % 300 === 0) {
this._logger.error(
e, `Failed to poll live wallpaper frame (${this._frameErrorCount} failures so far)`);
}
}
return GLib.SOURCE_CONTINUE;
@@ -292,18 +328,29 @@ export class LiveWallpaperManager {
if (!this._actor)
return;
if (!this._image || this._videoWidth !== width || this._videoHeight !== height) {
this._image = new Clutter.Image();
this._videoWidth = width;
this._videoHeight = height;
const needsNewImage = !this._image || this._videoWidth !== width || this._videoHeight !== height;
while (this._imageStrategyIndex < FRAME_IMAGE_STRATEGIES.length) {
const strategy = FRAME_IMAGE_STRATEGIES[this._imageStrategyIndex];
try {
if (needsNewImage) {
this._image = strategy.create(width, height);
this._videoWidth = width;
this._videoHeight = height;
}
strategy.upload(this._image, this._Cogl, data, width, height);
this._actor.set_content(this._image);
return;
} catch (e) {
this._logger.warn(
`Live wallpaper: frame image strategy "${strategy.name}" failed ` +
`(${e.message ?? e}), trying the next one`);
this._imageStrategyIndex++;
this._image = null;
}
}
try {
this._image.set_data(data, this._Cogl.PixelFormat.RGBA_8888, width, height, width * 4);
this._actor.set_content(this._image);
} catch (e) {
this._logger.error(e, 'Failed to upload live wallpaper frame');
}
throw new Error('All live wallpaper frame image strategies failed');
}
_onBusMessage(message) {