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 5157f4a1c9 - Show all commits
+66 -6
View File
@@ -128,6 +128,61 @@ export class LiveWallpaperManager {
this._start(); this._start();
} }
/**
* Builds videoconvert ! videoscale ! capsfilter(RGBA) ! appsink using
* explicit element creation, linking, and a ghost pad, rather than
* Gst.parse_bin_from_description()'s gst-launch mini-language. Both
* are meant to be equivalent, but a user's testing showed the
* string-parsed version's appsink never fired 'new-sample' even once
* inside gnome-shell's process while an equivalent standalone
* gst-launch-1.0 pipeline worked fine — this removes the string
* parsing (and its automatic ghost-pad detection) as a variable, and
* surfaces link() failures explicitly instead of failing silently.
*/
_buildSinkBin() {
const Gst = this._Gst;
const videoconvert = Gst.ElementFactory.make('videoconvert', 'benthicbloom-convert');
const videoscale = Gst.ElementFactory.make('videoscale', 'benthicbloom-scale');
const capsfilter = Gst.ElementFactory.make('capsfilter', 'benthicbloom-capsfilter');
const appsink = Gst.ElementFactory.make('appsink', 'benthicbloom-appsink');
if (!videoconvert || !videoscale || !capsfilter || !appsink) {
throw new Error(
'Failed to create one or more GStreamer elements ' +
'(videoconvert/videoscale/capsfilter/appsink) — a required plugin is likely missing');
}
capsfilter.set_property('caps', Gst.Caps.from_string('video/x-raw,format=RGBA'));
appsink.set_property('emit-signals', true);
appsink.set_property('max-buffers', 2);
appsink.set_property('drop', true);
appsink.set_property('sync', true);
const sinkBin = new Gst.Bin({name: 'benthicbloom-sinkbin'});
sinkBin.add(videoconvert);
sinkBin.add(videoscale);
sinkBin.add(capsfilter);
sinkBin.add(appsink);
if (!videoconvert.link(videoscale))
throw new Error('Failed to link videoconvert -> videoscale');
if (!videoscale.link(capsfilter))
throw new Error('Failed to link videoscale -> capsfilter');
if (!capsfilter.link(appsink))
throw new Error('Failed to link capsfilter -> appsink');
const sinkPad = videoconvert.get_static_pad('sink');
const ghostPad = Gst.GhostPad.new('sink', sinkPad);
if (!ghostPad)
throw new Error('Failed to create ghost pad for live wallpaper sink bin');
ghostPad.set_active(true);
sinkBin.add_pad(ghostPad);
this._appsink = appsink;
return sinkBin;
}
_start() { _start() {
const path = this._settings.get_string(SettingsKey.LIVE_WALLPAPER_PATH); const path = this._settings.get_string(SettingsKey.LIVE_WALLPAPER_PATH);
if (!this._available || !path) if (!this._available || !path)
@@ -143,11 +198,7 @@ export class LiveWallpaperManager {
Main.layoutManager._backgroundGroup.add_child(this._actor); Main.layoutManager._backgroundGroup.add_child(this._actor);
this._layoutActor(); this._layoutActor();
const sinkBin = Gst.parse_bin_from_description( const sinkBin = this._buildSinkBin();
'videoconvert ! videoscale ! video/x-raw,format=RGBA ! ' +
'appsink name=benthicbloom_sink emit-signals=true max-buffers=2 drop=true sync=true',
true);
this._appsink = sinkBin.get_by_name('benthicbloom_sink');
this._appsink.connect('new-sample', sink => this._onNewSample(sink)); this._appsink.connect('new-sample', sink => this._onNewSample(sink));
this._playbin = Gst.ElementFactory.make('playbin', 'benthicbloom-live-wallpaper'); this._playbin = Gst.ElementFactory.make('playbin', 'benthicbloom-live-wallpaper');
@@ -381,13 +432,22 @@ export class LiveWallpaperManager {
} }
if (this._playbin) { if (this._playbin) {
const Gst = this._Gst;
const bus = this._playbin.get_bus(); const bus = this._playbin.get_bus();
if (this._busWatchId) { if (this._busWatchId) {
bus.disconnect(this._busWatchId); bus.disconnect(this._busWatchId);
this._busWatchId = 0; this._busWatchId = 0;
} }
bus.remove_signal_watch(); bus.remove_signal_watch();
this._playbin.set_state(this._Gst.State.NULL);
this._playbin.set_state(Gst.State.NULL);
// Block briefly for the (normally fast) transition to actually
// finish before dropping our reference — otherwise the element
// can get disposed mid-transition, which GStreamer logs as
// "Trying to dispose element ..., but it is in PLAYING instead
// of the NULL state".
this._playbin.get_state(200 * Gst.MSECOND);
this._playbin = null; this._playbin = null;
this._appsink = null; this._appsink = null;
} }