Commit Graph
22 Commits
Author SHA1 Message Date
Quinta e39d8973d5 custom playback speed fixed 2026-08-11 17:41:35 +02:00
Quinta 9876f98317 Add CodeQL analysis workflow configuration 2026-08-08 17:30:08 +02:00
Quinta 3795e9316c Update README with user attachments link
Added a link to user attachments in the README.
2026-08-07 15:00:31 +02:00
Quinta f8adc2594f edited the readme 2026-08-07 13:52:54 +02:00
Quinta faa3a7257f more oled features and system freeze fixes 2026-08-06 19:58:50 +02:00
QuintaandClaude Sonnet 5 40af1dad26 Fix potential login freeze: skip blocking GStreamer registry rescan
Gst.init() has no async variant. If the plugin registry cache is stale
(e.g. right after a system update), it synchronously rescans every
plugin, spawning gst-plugin-scanner per plugin and probing hardware
for codecs. Since gnome-shell is single-threaded and is the
compositor, that can freeze the whole screen for several seconds,
same as the blocking UPower proxy lookup fixed earlier.

Set GST_REGISTRY_UPDATE=no when a cache already exists so Gst.init()
trusts it and returns immediately, then refresh the cache in a
throwaway subprocess afterward so it doesn't go stale forever.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 12:11:11 +02:00
Quinta d7278ee56d Merge pull request #2 from Quinta0/claude/gnome-wallpaper-auto-rotation-r1ailq
Fix login freeze: make the UPower proxy lookup async, not blocking
2026-08-06 11:21:08 +02:00
Claude 0d7b4187da Fix login freeze: make the UPower proxy lookup async, not blocking
_connectPowerWatches() used Gio.DBusProxy.new_for_bus_sync(), which
blocks gnome-shell's single main thread until the system bus replies.
Since gnome-shell is the Wayland compositor, that stalls the entire
screen for however long it takes — and this runs every time live
wallpaper starts, including automatically at login whenever it was
left enabled. Right after login, with upowerd/D-Bus still warming up
alongside everything else, that block can run for many seconds up to
the ~25s GDBus timeout, which matches reports of the whole system
freezing right after the greeter/login. Switched to the async
new_for_bus(), guarded by a generation counter so a stale in-flight
lookup can't clobber state from a later _stop()/_connectPowerWatches().
2026-08-06 09:17:26 +00:00
Quinta 5761d37145 Merge pull request #1 from Quinta0/claude/gnome-wallpaper-auto-rotation-r1ailq
Claude/gnome wallpaper auto rotation r1ailq
2026-08-04 20:53:28 +02:00
Claude f246d6a4fe Add live wallpaper source folders and bundle preferences page icons
Live Wallpaper page gains a "Wallpaper Folders" list (mirroring the
static rotation one) plus an "Available Media" list of the videos/GIFs
found inside them to pick from, backed by a new live-wallpaper-folders
GSettings key and a generalized wallpaperSource.js extension scan.

The preferences page-switcher icons (General, Rotation, Live Wallpaper,
OLED Protection, About) were showing as broken-image placeholders since
they depended on exact symbolic icon names being present in the user's
system icon theme. Bundle a small set of custom SVG icons under icons/
and register them via Gtk.IconTheme.add_search_path so they always
resolve regardless of the system theme.
2026-08-04 02:08:14 +00:00
Claude bbd21c9e5a Fix St.ImageContent.set_bytes missing Cogl.Context argument
set_bytes() takes a Cogl.Context as its first parameter (added upstream
in GNOME Shell 48, see js/ui/screenshot.js); omitting it produced
"At least 6 arguments required, but only 5 passed" for every frame,
which fell through to the equally non-functional Clutter.Image fallback
and left the live wallpaper blank.
2026-08-04 01:34:30 +00:00
Claude 2365d303f3 Fix frame upload: Clutter.Image isn't constructible from GJS anymore
The polling fix worked and pinpointed the real, final bug: appsink was
delivering frames correctly (confirmed by the polling infrastructure
itself running fine), but every single upload attempt threw
"(intermediate value).Image is not a constructor" from `new
Clutter.Image()`. In current Mutter, Clutter.Image apparently isn't
directly constructible via `new` from GJS anymore, and the resulting
exception was also flooding the log at ~30/sec with no rate limiting.

Adds St.ImageContent.new_with_preferred_size() + set_bytes(GLib.Bytes)
as the primary path — the same mechanism gnome-shell's own code uses
for uploading raw pixel buffers onto actors — with the old
Clutter.Image/set_data() path kept only as a fallback for older
shells. The two are tried in order per frame size change and "pinned"
once one works, avoiding a live per-frame strategy search. Also
rate-limits the poll error log to the 1st and every 300th failure
instead of every single one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
2026-08-04 01:26:38 +00:00
Claude d5f6d3ecad Poll appsink instead of relying on the new-sample signal
The last round of testing ruled out the pipeline construction itself:
even with the sink bin rebuilt from explicit, individually-checked
link() calls (no thrown errors, playbin reaches PLAYING), the
'new-sample' signal still never fired inside gnome-shell — while an
equivalent pipeline worked fine standalone via gst-launch-1.0.

The remaining, gnome-shell-specific difference: appsink emits
'new-sample' from GStreamer's own streaming thread, not the main
thread. GJS's JS engine isn't safe to call into from an arbitrary
background thread, and a cross-thread signal emission can be silently
dropped rather than invoked or crashed on — which looks exactly like
"the signal never fires" from here, even though the pipeline is
actually running.

Replaces the signal entirely with polling: a ~30fps GLib.timeout_add()
on the main thread calls appsink.try_pull_sample(0), which is
explicitly documented as safe to call from any thread since we are now
the ones calling into GStreamer rather than the reverse. Also updates
the no-frames watchdog to report the poll count instead of a signal
fire count.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
2026-08-04 01:17:07 +00:00
Claude 5157f4a1c9 Build the live wallpaper sink bin explicitly instead of via string parsing
User testing pinned the bug precisely: our try/catch is airtight now,
and the diagnostics showed "new-sample signal fired 0 times" — the
appsink genuinely never receives a single sample inside gnome-shell's
process, while an equivalent pipeline (videoconvert ! videoscale !
RGBA caps ! appsink) played correctly standalone via gst-launch-1.0.

Replaces Gst.parse_bin_from_description()'s gst-launch mini-language
parsing (with its automatic unlinked-pad ghosting) with explicit
ElementFactory.make() + Bin.add() + element.link() + a manually
created GhostPad. This removes the string-parsing path as a variable
entirely and, importantly, surfaces any link() failure as a thrown
error instead of failing silently, which the old code path had no way
to report even if that patch were the whole cachet of the problem.

Also fixes a related warning ("Trying to dispose element ..., but it
is in PLAYING instead of the NULL state") from _stop() dropping the
playbin reference immediately after requesting the NULL state, before
the (async) transition actually completed — now waits briefly via
get_state() for it to settle first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
2026-08-04 01:11:29 +00:00
Claude 14078f0466 Close try/catch gap around appsink pull_sample()
Standalone gst-launch testing confirmed appsink-as-playbin-video-sink
works fine outside the extension (reaches PLAYING, negotiates caps,
runs to completion), narrowing the "no frames received" symptom down
to something inside our own new-sample handling in the shell process.

_onNewSample()'s try/catch didn't cover the sink.pull_sample() call
itself — if that specific call throws when invoked as a GJS signal
callback, the exception bypassed our logging entirely, falling back
to GJS's generic uncaught-exception path (which, like the
openPreferences case, may not mention "benthicbloom" and gets missed
by a filtered grep). Now the whole handler is covered, logs when the
signal fires for the first time, logs a null pull_sample() or failed
buffer.map() explicitly, and the watchdog reports the new-sample fire
count so we can tell "signal never fired" from "signal fired but
processing failed" on the next test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
2026-08-04 01:04:07 +00:00
Claude 2688568fac Add live wallpaper pipeline diagnostics
A user's live wallpaper reached "started" with zero errors (Cogl and
GStreamer both loading fine, actor added to _backgroundGroup) but
never actually appeared on screen, and no code path here could tell
us why: the bus handler only looked at EOS/ERROR, silently discarding
WARNING and STATE_CHANGED messages where a stalled negotiation or
preroll would actually show up.

Adds: WARNING message logging, PLAYING/PAUSED/etc. state-changed
logging for the playbin itself, a frame counter with first-frame and
periodic debug logs, and a one-shot watchdog that logs an explicit
warning if zero frames arrive within 4s of set_state(PLAYING). Also
switched appsink's pull-sample from an emitted action signal to the
plain pull_sample() method, which is the more directly supported
GstApp.AppSink API and one less variable while debugging.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
2026-08-04 00:50:24 +00:00
Claude f7c05c2c76 Fix false-positive "GStreamer Not Found" warning in Preferences
The Preferences window's availability probe (added to surface missing
GStreamer deps in-UI) reused loadGstreamerModules(), which also checks
for Cogl. Cogl is Mutter's private library — its typelib is only
reachable from inside the actual gnome-shell process (which gets a
private GI search path), never from the separate, plain-GTK4
Preferences process. So the check always failed there with "Requiring
Cogl ... not found" even when live wallpapers were working correctly
in the real shell process, as confirmed by a user report where the
extension's own log showed live wallpaper playback starting cleanly
while Preferences simultaneously claimed GStreamer was missing.

Split into loadGstreamerModules() (Gst+GstApp+Cogl, used only by
liveWallpaper.js in the shell process) and a new lighter
checkGstreamerBaseAvailable() (Gst+GstApp only) for prefs.js, with a
note in the UI that a clean check there isn't a full guarantee since
the private half can't be verified from that process.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
2026-08-04 00:36:48 +00:00
Claude d180c90434 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
2026-08-04 00:33:01 +00:00
Claude 4a5ef4fe86 Surface missing GStreamer deps in Preferences instead of only logging
Previously, if the Gst/GstApp/Cogl GI typelibs couldn't be loaded (e.g.
gst-plugins-base/good not installed, only the bare gstreamer package),
the only sign was a warning buried in the shell's log — the live
wallpaper page looked fully functional and silently did nothing.

Preferences now probes for the same bindings the running extension
needs and shows a warning row with the exact import error plus
per-distro install commands, and greys out the controls that only
matter once GStreamer is actually available. Extracted the shared
probing logic into lib/gstreamerAvailability.js so extension.js and
prefs.js (separate processes) don't duplicate it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
2026-08-04 00:26:53 +00:00
Claude 5e8884d01e Allow selecting animated GIFs as live wallpapers
The playback pipeline already typefinds by content and decodes GIFs
via GStreamer's decodebin like any other video, but the preferences
file picker filtered to video/* MIME types only, hiding .gif files.
Widen the filter to include image/gif and *.gif, add an "All files"
fallback, and update the related copy/docs to mention GIF support.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
2026-08-04 00:18:43 +00:00
Claude c0ec4a59d5 Implement BenthicBloom GNOME 50 wallpaper extension
Adds a complete GNOME Shell 50 extension with folder-based wallpaper
auto-rotation (shuffle/sequential, crossfade transitions, lock screen
sync), GStreamer-backed live/video wallpapers rendered onto the shell's
own Clutter stage, and OLED burn-in protection (pixel shifting, idle
dimming, forced periodic rotation). Includes a top-bar quick-access
indicator, a libadwaita preferences window, GSettings schema, and a
Makefile for install/packaging.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RDqbdjsiisSU7CbQke4g8
2026-08-04 00:05:58 +00:00
Quinta 068c00c0ac Initial commit 2026-08-04 01:48:27 +02:00