Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/mocha-wallpaper-assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Mocha wallpaper source configuration

The Noctalia template points its default and DP-1 wallpaper to the supplied
3840×2160 landscape PNG, and HDMI-A-1 to the separately composed 2160×3840
portrait PNG. Per-monitor browsing directories use the same pools. These are
source configuration and assets only; this change does not run `chezmoi apply`,
change a live session, or start/enable a timer.

The upstream template's `[wallpaper.automation] enabled = false` remains
unchanged. With automation off and no separately selected scheduler, the
wallpapers remain static; the supplied one-image-per-orientation pools do not
provide distinct candidates for rotation. Users who want Noctalia's native
automation can explicitly set `enabled = true` in their managed configuration.

Rotation alternatives are separate and opt-in. PR #92's
`ezwallpaper-rotation` integration uses the `ezwallpaper-mocha` pools. Local
rotation B (PR #95, documented in
[`ezwallpaper-rotation-b.md`](ezwallpaper-rotation-b.md)) has a path mismatch:
its profile template currently points to
`~/.local/share/wallpapers/{landscape,portrait}`, but its preflight guard is
hard-coded to require real directories at
`~/.local/share/wallpapers/ezwallpaper-mocha/{landscape,portrait}`. Changing
the profile alone does not make the guard support arbitrary pool paths; both
the profile's candidate paths and the guard's fixed paths must satisfy their
respective checks. Neither profile nor timer is added or enabled by this
asset/config change, and the two ezwallpaper schedulers must not run together.
B additionally requires a compatible `~/.local/bin/ezwallpaper` executable,
valid distinct candidates for rotation, and the session/config/unit safeguards;
neither that private/local binary nor additional runtime images are supplied
here. An operator selecting B must resolve the path contract, review scoped
`chezmoi diff`, establish B as the sole rotation owner, deploy its files
separately, verify the pools and session prerequisites, and ensure Noctalia's
managed automation and any GUI override are off before separately enabling B.
This change does not enable B or run its timer.

Noctalia's native automation is a distinct alternative: users may explicitly
set its managed `enabled` flag to true. That does not enable either ezwallpaper
integration, whose preflight requires native automation to be off. If no
rotation controller is deliberately deployed, this supplied one-image-per-
orientation configuration remains static; native Noctalia automation can
instead be opted into separately.

The B preflight is a point-in-time guard, not a lock against later config or
unit changes, and cannot prove the running Noctalia process reloaded settings
or that rendered pixels are correct. No live setter, service, timer, reload,
or apply was run while preparing this source change.
15 changes: 11 additions & 4 deletions dotfiles/dot_config/noctalia/desktop.toml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,28 @@ enabled = false

[wallpaper]
enabled = true
directory = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/omarchy"
directory = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/ezwallpaper-mocha/landscape"
fill_mode = "crop"
transition = ["fade"]
transition_duration = 900.0
transition_on_startup = true
fill_color = "surface"
per_monitor_directories = true

[wallpaper.default]
path = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/omarchy/sunset-lake-4k.webp"
path = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/ezwallpaper-mocha/landscape/mocha-flat-landscape.png"

[wallpaper.monitors.DP-1]
path = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/omarchy/sunset-lake-4k.webp"
path = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/ezwallpaper-mocha/landscape/mocha-flat-landscape.png"

[wallpaper.monitors.HDMI-A-1]
path = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/omarchy/sunset-lake-portrait.webp"
path = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/ezwallpaper-mocha/portrait/mocha-flat-portrait.png"

[wallpaper.monitor.DP-1]
directory = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/ezwallpaper-mocha/landscape"

[wallpaper.monitor.HDMI-A-1]
directory = "{{ .chezmoi.homeDir }}/.local/share/wallpapers/ezwallpaper-mocha/portrait"

[wallpaper.automation]
enabled = false
Expand Down
12 changes: 12 additions & 0 deletions dotfiles/dot_local/share/wallpapers/ezwallpaper-mocha/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Mocha flat wallpaper assets

This directory contains two project-authored, flat-vector wallpapers: a
3840×2160 landscape scene and a separately composed 2160×3840 portrait scene.
Their editable SVG sources are in `source/`; the PNGs in `landscape/` and
`portrait/` are the files referenced by the Noctalia desktop template. They
use no external images, fonts, or downloaded assets. The repository MIT
license applies; no third-party artwork is bundled.

Each runtime pool currently contains one image. This supplies stable desktop
wallpapers, not a meaningful rotation pool. Existing `landscape/`, `portrait/`,
and `omarchy/` source assets are not replaced or removed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
159 changes: 159 additions & 0 deletions tests/test_mocha_wallpaper_assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
"""Offline contract checks for the bundled Mocha wallpaper assets/config."""
from __future__ import annotations

import re
import struct
import tomllib
import unittest
import xml.etree.ElementTree as ET
import zlib
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
ASSETS = ROOT / "dotfiles/dot_local/share/wallpapers/ezwallpaper-mocha"
TEMPLATE = ROOT / "dotfiles/dot_config/noctalia/desktop.toml.tmpl"
DOC = ROOT / "docs/mocha-wallpaper-assets.md"
B_PROFILE = ROOT / "dotfiles/dot_config/ezwallpaper/rotation.json.tmpl"
B_PREFLIGHT = ROOT / "dotfiles/dot_local/bin/executable_ezwallpaper-rotation-preflight.py"
EXPECTED = {
"landscape/mocha-flat-landscape.png": (3840, 2160),
"portrait/mocha-flat-portrait.png": (2160, 3840),
}


def inspect_png(path: Path) -> tuple[int, int]:
"""Validate PNG chunks/CRCs and fully decompress non-interlaced RGB data."""
data = path.read_bytes()
if data[:8] != b"\x89PNG\r\n\x1a\n":
raise AssertionError(f"{path} is not a PNG")
pos = 8
width = height = bit_depth = color_type = interlace = None
compressed = bytearray()
saw_end = False
while pos < len(data):
if pos + 12 > len(data):
raise AssertionError(f"truncated PNG chunk in {path}")
length = struct.unpack_from(">I", data, pos)[0]
kind = data[pos + 4 : pos + 8]
end = pos + 8 + length
if end + 4 > len(data):
raise AssertionError(f"truncated PNG payload in {path}")
payload = data[pos + 8 : end]
expected_crc = struct.unpack_from(">I", data, end)[0]
actual_crc = zlib.crc32(kind + payload) & 0xFFFFFFFF
if actual_crc != expected_crc:
raise AssertionError(f"bad PNG CRC for {kind!r} in {path}")
if kind == b"IHDR":
if length != 13 or width is not None:
raise AssertionError("invalid or repeated PNG IHDR")
width, height, bit_depth, color_type, compression, filtering, interlace = struct.unpack(
">IIBBBBB", payload
)
if compression or filtering or interlace:
raise AssertionError("only non-interlaced standard PNGs are supported")
elif kind == b"IDAT":
compressed.extend(payload)
elif kind == b"IEND":
if length:
raise AssertionError("invalid PNG IEND")
saw_end = True
pos = end + 4
break
pos = end + 4
if not saw_end or pos != len(data) or width is None:
raise AssertionError(f"missing IEND/IHDR or trailing bytes in {path}")
if bit_depth != 8 or color_type != 2:
raise AssertionError(f"expected 8-bit RGB PNG: depth={bit_depth}, type={color_type}")
pixels = zlib.decompress(compressed)
if len(pixels) != height * (1 + width * 3):
raise AssertionError(f"unexpected decompressed scanline size in {path}")
return width, height


class MochaWallpaperContractTests(unittest.TestCase):
def test_assets_are_real_decodable_images_at_expected_dimensions(self) -> None:
for relative, dimensions in EXPECTED.items():
path = ASSETS / relative
self.assertTrue(path.is_file(), f"missing referenced asset: {relative}")
self.assertFalse(path.is_symlink(), f"asset must be a regular file: {relative}")
self.assertEqual(inspect_png(path), dimensions)

def test_svg_sources_are_self_contained_and_parse(self) -> None:
for relative in (
"source/mocha-flat-landscape.svg",
"source/mocha-flat-portrait.svg",
):
root = ET.parse(ASSETS / relative).getroot()
self.assertEqual(root.tag.rsplit("}", 1)[-1], "svg")
for element in root.iter():
self.assertNotIn(element.tag.rsplit("}", 1)[-1], {"script", "image"})
for key, value in element.attrib.items():
if key.rsplit("}", 1)[-1] in {"href", "src"}:
self.assertFalse(re.match(r"^[a-z]+:", value, re.I), value)

def test_rendered_wallpaper_config_references_only_supplied_mocha_paths(self) -> None:
template = TEMPLATE.read_text(encoding="utf-8")
block = template.split("[wallpaper]\n", 1)[1].split("\n[backdrop]\n", 1)[0]
paths = re.findall(r'path = "\{\{ \.chezmoi\.homeDir \}\}/\.local/share/wallpapers/([^\"]+)"', block)
self.assertEqual(paths.count("ezwallpaper-mocha/landscape/mocha-flat-landscape.png"), 2)
self.assertEqual(paths.count("ezwallpaper-mocha/portrait/mocha-flat-portrait.png"), 1)
directories = re.findall(r'directory = "\{\{ \.chezmoi\.homeDir \}\}/\.local/share/wallpapers/([^\"]+)"', block)
self.assertEqual(set(directories), {"ezwallpaper-mocha/landscape", "ezwallpaper-mocha/portrait"})
for relative in paths:
self.assertTrue((ROOT / "dotfiles/dot_local/share/wallpapers" / relative).is_file())
self.assertIn("per_monitor_directories = true", block)
self.assertIn("[wallpaper.monitor.DP-1]", block)
self.assertIn("[wallpaper.monitor.HDMI-A-1]", block)
self.assertIn("[wallpaper.automation]\nenabled = false", block)

def test_docs_disclose_static_default_and_b_prerequisites(self) -> None:
doc = DOC.read_text(encoding="utf-8")
profile = B_PROFILE.read_text(encoding="utf-8")
preflight = B_PREFLIGHT.read_text(encoding="utf-8")
for path in (
".local/share/wallpapers/landscape",
".local/share/wallpapers/portrait",
):
self.assertIn(path, profile)
for path in (
".local/share/wallpapers/ezwallpaper-mocha/landscape",
".local/share/wallpapers/ezwallpaper-mocha/portrait",
):
self.assertIn(path, preflight)
self.assertIn("~/.local/share/wallpapers/{landscape,portrait}", doc)
self.assertIn(
"~/.local/share/wallpapers/ezwallpaper-mocha/{landscape,portrait}",
doc,
)
self.assertIn("hard-coded", doc)
self.assertIn(
"does not make the guard support arbitrary pool paths",
doc,
)
for phrase in (
"remain static",
"native automation",
"ezwallpaper-rotation-b.md",
"private/local",
"ezwallpaper-rotation",
"wallpapers/{landscape,portrait}",
"native automation is a distinct alternative",
"cannot prove",
):
self.assertIn(phrase, doc)

def test_rendered_toml_wallpaper_tables_parse_and_defaults_stay_off(self) -> None:
template = TEMPLATE.read_text(encoding="utf-8")
rendered = template.replace("{{ .chezmoi.homeDir }}", "/home/test")
config = tomllib.loads(rendered)
wallpaper = config["wallpaper"]
self.assertEqual(wallpaper["directory"], "/home/test/.local/share/wallpapers/ezwallpaper-mocha/landscape")
self.assertTrue(wallpaper["per_monitor_directories"])
self.assertEqual(wallpaper["monitors"]["DP-1"]["path"], "/home/test/.local/share/wallpapers/ezwallpaper-mocha/landscape/mocha-flat-landscape.png")
self.assertEqual(wallpaper["monitors"]["HDMI-A-1"]["path"], "/home/test/.local/share/wallpapers/ezwallpaper-mocha/portrait/mocha-flat-portrait.png")
self.assertFalse(wallpaper["automation"]["enabled"])


if __name__ == "__main__":
unittest.main()
Loading