A real room upgraded to 0.1.45 and came up as stock Debian Plasma with the old Waybar across the top, mako still catching notifications, and a lock screen on an account that has no password. The cause was one line, and it was in the lab: the harness handed the session under test XDG_CONFIG_DIRS by hand, so every result it had ever produced was a result about a machine somebody had already configured. On a machine SDDM starts the session through PAM, PAM does not read /etc/environment.d, and Debian's own plasma-workspace env.sh then set the variable from scratch — so the classroom's settings directory was not in the cascade at all. No layout, no lock policy, no Kiosk restrictions. The settings now arrive the way Plasma delivers them, through a file in /etc/xdg/plasma-workspace/env. Around that: the look and feel is immutable on its own key so a stale profile cannot answer for it; the palette is composed into the package, because naming a colour scheme does not produce one and the dark bar had been coming from a technician's hand; the desktop reset converges another desktop's appearance state and keeps the room's own; and classroom-migrate stops, deactivates and masks the old shell, including the D-Bus activation that brought mako back while the panel was down — found only by killing the panel on a migrated machine. device.conf loses COMPOSITOR and comes off the hyprland channel, which would otherwise have handed the room the experiment back. The lab stops flattering the package: it restarts the machine, lets SDDM actually autologin, and asks a fresh install and a machine upgraded from the Hyprland package the same sixty questions. Both answer identically, twice, and the panel still comes back from eight SIGKILLs. Co-Authored-By: Claude Opus 5 <[email protected]>
126 lines
3.8 KiB
Python
Executable File
126 lines
3.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Compose the classroom's kdeglobals, colours included.
|
|
|
|
The panel came up light on a machine that had never had a colour scheme
|
|
applied to it, and dark on one that had. That is the whole bug in one
|
|
sentence: `ColorScheme=EkklesiaDark` is a label. KConfig reads the actual
|
|
colours out of [Colors:*] groups in kdeglobals, and nothing was putting
|
|
them there except a technician running classroom-theme once, by hand, on
|
|
a machine that then looked right for reasons the package did not own.
|
|
|
|
So the package ships the expanded palette in its own kdeglobals, at the
|
|
bottom of the cascade, where a room with no user configuration at all
|
|
finds it. One source of truth stays EkklesiaDark.colors: this composes,
|
|
it does not duplicate.
|
|
|
|
classroom-theme still works. plasma-apply-colorscheme writes the chosen
|
|
palette into the user's own kdeglobals, which sits ahead of this file, and
|
|
classroom-desktop-reset keeps a user palette whose name is an Ekklesia one.
|
|
|
|
tools/build-kdeglobals > somewhere/kdeglobals
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
POLICY = ROOT / "config/plasma/xdg/kdeglobals"
|
|
SCHEME = ROOT / "config/plasma/colors/EkklesiaDark.colors"
|
|
|
|
# Name belongs to the scheme file, not to a session's configuration, and
|
|
# ColorScheme is already stated in the policy file above.
|
|
SKIP_IN_GENERAL = {"Name", "ColorScheme"}
|
|
|
|
|
|
def groups(text):
|
|
"""Yield (header, [lines]) for each group in a KConfig file."""
|
|
header = None
|
|
body = []
|
|
|
|
for line in text.splitlines():
|
|
if line.startswith("["):
|
|
if header is not None:
|
|
yield header, body
|
|
header, body = line, []
|
|
elif header is not None:
|
|
body.append(line)
|
|
|
|
if header is not None:
|
|
yield header, body
|
|
|
|
|
|
def policy_with(extra_general):
|
|
"""The policy file, with the scheme's stray [General] keys folded in.
|
|
|
|
Two [General] headers in one file is asking a parser a question it
|
|
should not be asked, so the keys go into the group that is already
|
|
there.
|
|
"""
|
|
lines = POLICY.read_text().rstrip("\n").splitlines()
|
|
out = []
|
|
inside = False
|
|
|
|
for line in lines:
|
|
if line.startswith("["):
|
|
if inside:
|
|
out.extend(extra_general)
|
|
inside = False
|
|
|
|
inside = line == "[General]"
|
|
|
|
out.append(line)
|
|
|
|
if inside:
|
|
out.extend(extra_general)
|
|
|
|
return "\n".join(out)
|
|
|
|
|
|
def main():
|
|
extra_general = []
|
|
|
|
for header, body in groups(SCHEME.read_text()):
|
|
if header != "[General]":
|
|
continue
|
|
|
|
extra_general = [
|
|
line
|
|
for line in body
|
|
if line.strip()
|
|
and not line.startswith("#")
|
|
and line.split("=", 1)[0].strip() not in SKIP_IN_GENERAL
|
|
]
|
|
|
|
out = [policy_with(extra_general), ""]
|
|
|
|
out.append(
|
|
"# ------------------------------------------------------------------\n"
|
|
"# Generated by tools/build-kdeglobals from EkklesiaDark.colors.\n"
|
|
"# Do not edit here: edit the scheme file and rebuild the package.\n"
|
|
"#\n"
|
|
"# These are the colours themselves. Without them a room with no user\n"
|
|
"# configuration gets KDE's built-in light palette however the scheme\n"
|
|
"# above is named.\n"
|
|
"# ------------------------------------------------------------------"
|
|
)
|
|
|
|
for header, body in groups(SCHEME.read_text()):
|
|
lines = [line for line in body if line.strip() and not line.startswith("#")]
|
|
|
|
if header == "[General]":
|
|
# Already folded into the policy file's own [General] above.
|
|
continue
|
|
|
|
if not lines:
|
|
continue
|
|
|
|
out.append("")
|
|
out.append(header)
|
|
out.extend(lines)
|
|
|
|
sys.stdout.write("\n".join(out) + "\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|