Skip to content

feat(wallpaper): source selector, per-source grid scoping, and rotation controls - #25

Open
perlowja wants to merge 6 commits into
singularityos-lab:mainfrom
perlowja:wallpaper-source-selector-clean
Open

feat(wallpaper): source selector, per-source grid scoping, and rotation controls#25
perlowja wants to merge 6 commits into
singularityos-lab:mainfrom
perlowja:wallpaper-source-selector-clean

Conversation

@perlowja

@perlowja perlowja commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a Wallpaper Source selector to Desktop settings alongside the existing wallpaper grid, plus Rotate Wallpapers and Rotation Interval controls:

  • WallpaperCollections (new core class) parses the .collection KeyFile registry (Id/Name/Artist/Dir/Type) already used to describe installed wallpaper packs/providers, in "first root wins" priority order across search roots.
  • WallpaperRotationState (new core class) reads/writes the same plain-text state files (~/.config/ncz-wallpaper/{collection,rotate-enabled,rotate-interval}) the shipped rotation daemon already polls -- this is the UI's side of existing shared state, not a new mechanism or IPC.
  • WallpaperGallery (new core class, extracted from desktop_page.vala's inline scan) scopes the wallpaper grid to only the currently-selected source, replacing the previous behavior of showing every backgrounds path and every collection mixed together. It also fixes two boundary bugs found in review: a "recent" wallpaper from a different source no longer leaks into the current source's grid, and a nested, separately-registered pack is no longer recursively pulled into its parent's scan.
  • desktop_page.vala: wires the above into a SelectionRow (source), SwitchRow (rotate on/off), and SelectionRow (interval), and scopes populate_grid()'s background scan thread to the selected source's directory.

Validation evidence

Built via a real container build (build-singularity.sh, SINGULARITY_SOURCE_DIR override) on an aarch64 build host -- not just meson compile in isolation. Confirmed in the staged singularity-desktop binary via strings/grep, not inferred from a clean exit:

$ grep -l singularity_wallpaper_collections_parse opt/singularity/bin/singularity-desktop
opt/singularity/bin/singularity-desktop
$ grep -l 'singularity_wallpaper_rotation_state_get_selected_collection\|singularity_wallpaper_gallery_scan' opt/singularity/bin/singularity-desktop
opt/singularity/bin/singularity-desktop
$ strings opt/singularity/bin/singularity-desktop | grep -c 'Wallpaper Source\|Rotate Wallpapers\|Rotation Interval'
3

Manually verified end-to-end on real Sky1/labwc hardware: switching sources rescopes the grid, rotate toggle and interval persist and are honored by the existing rotation daemon.

tests/wallpaper_collections_test.vala, tests/wallpaper_rotation_state_test.vala, and tests/wallpaper_gallery_test.vala cover empty registries, malformed KeyFiles, missing Dir=, duplicate IDs across search roots, and the source-boundary-and-recents edge cases.

Compatibility

No change to the on-disk state file format or the rotation daemon -- purely additive on the UI side of an existing shared contract.

Rollback

Revert this branch's commits; no schema/state-file migration to undo.

…h the rotator daemon

(cherry picked from commit 50db4b3)
…rols

Implements Tasks 3-4 of docs/superpowers/plans/2026-09-04-wallpaper-source-selector-rotation.md
(Tasks 1-2 already landed as 36bcc12/50db4b3).

- Wire WallpaperCollections.parse() and WallpaperRotationState into
  desktop_page.vala: a Wallpaper Source selector row lists every installed
  .collection, and populate_grid() now scans only the selected collections
  directory instead of every backgrounds path plus every collection at once.
- Remove the now-superseded private collection_dirs() helper, fully
  replaced by WallpaperCollections.parse().
- Add Rotate Wallpapers (on/off) and Rotation Interval rows sharing the
  same ~/.config/ncz-wallpaper/{collection,rotate-enabled,rotate-interval}
  state files the shipped ncz-wallpaper-rotate/-daemon scripts already
  poll -- no daemon change, no new IPC.
- meson.build: register wallpaper_collections.vala and
  wallpaper_rotation_state.vala in the main app source list. Tasks 1-2
  only wired them into their own standalone test executables; desktop_page.vala
  could not resolve the types without this.

Verified via a real container build (SINGULARITY_SOURCE_DIR override,
build-singularity.sh): singularity-desktop links clean, and the shipped
binary carries both the new symbols (singularity_wallpaper_collections_parse,
singularity_wallpaper_rotation_state_get_selected_collection, etc.) and the
literal UI strings ("Wallpaper Source", "Rotate Wallpapers", "Rotation
Interval", "Every 10 minutes") -- checked directly in the binary, not
inferred from build success.

Not yet done: manual on-hardware verification of the actual UI (open
Desktop settings, switch sources, toggle rotation) -- this commit verifies
the code builds and ships, not that the UX behaves correctly end to end.

(cherry picked from commit 8bd1f5e)
Admit recent images only when the selected collection scan contains them. Exclude separately registered nested packs while retaining provider subdirectories. Exercise the production scanner with mixed history and a live-state probe.

(cherry picked from commit bd9dd7a)
Not something an upstream maintainer should receive as part of the
feature -- it was a local live-state debugging aid, not a test or a
build artifact anything else depends on.
Five real correctness issues found by an adversarial review of the
prior four commits before opening this as an upstream PR:

- WallpaperRotationState.write(): write-then-rename instead of a direct
  set_contents, so the rotator daemon (which polls these files on its
  own timer, independent of this UI) can never observe a partial write.
- get_rotate_enabled(): accept "false"/"off" as disabled, not only the
  literal "0" the UI itself writes -- a daemon written to a slightly
  different convention would otherwise read as always-on.
- WallpaperCollections.parse(): trim KeyFile string values (Dir/Id/Name/
  Artist/Type). GLib.KeyFile permits "Key = value" with surrounding
  whitespace; an untrimmed Dir would silently fail every path comparison
  downstream, and an untrimmed Id would defeat duplicate-id detection.
- WallpaperGallery.scan(): canonicalize paths (Posix.realpath) before
  comparing collection roots for the source-boundary exclusion, so a
  pack installed through a symlinked directory is still recognized as
  the same root instead of leaking into another source's scan.
- desktop_page.vala populate_grid(): when the persisted selection
  doesn't match any installed collection, persist the fallback (rather
  than re-deriving and re-logging the same mismatch on every refresh).

Verified via a real container build (SINGULARITY_SOURCE_DIR override,
build-singularity.sh) on ULTRA.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant