feat: give every room the broker, one scale, and its own panel back

Little Waders took the Plasma build and reported what a container
cannot. A disc player on a machine with no optical drive, a clock nobody
at the back of the room could read, a browser at half the size of the
panel around it, and Parent Call with no broker to call.

The broker is the same for every classroom, so it is a default now and
not something to fill in room by room. classroom-migrate brings an
existing room to the same place without touching its name, its username
or its password, and Parent Call distinguishes a broker it cannot reach
from one that was never set up: those send somebody to look at
completely different things.

The disc player's detection was right and ran too late — alongside the
session rather than before it, and the panel is built from the layout the
moment a session starts. It runs before the display manager now, and
again from udev when a drive is plugged in.

UI_SCALE had a ceiling of 1.5, which cannot express a 4K television; a
clock pinned at 15 points, which is the same size at every scale; and a
browser that was never told the number at all. All three are fixed and
the room doubles together at 200%.

This also brings 0.1.47's work into the repository, where it had only
ever existed in a build directory under /tmp: the hidden display dialog,
its authenticated writer, the polkit action, and the church's own
wallpaper. One piece of it is deliberately left out. The custom volume
display needed a shell package of our own, and with one selected
plasmashell builds no panel at all — measured twice, with that build's
own files, on a classroom that deletes its panel configuration at every
login by design.

The lab connected to the real church broker the moment the package
defaulted to it. Nothing was left behind, which was checked, but the
guard that should have stopped it had never worked: it used iptables,
which the lab image does not have, and probed with /dev/tcp under dash,
where that always fails. Both reported success.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
ekklesia
2026-09-12 19:05:09 -05:00
co-authored by Claude Opus 5
parent 926668dc9b
commit 34fd02e08f
59 changed files with 1335 additions and 198 deletions
+53 -90
View File
@@ -1,106 +1,69 @@
# UI scale
# Classroom display size (Plasma v2)
Every classroom gets the same design at a size that suits its TV. A 32"
panel a few feet from the children and a 65" set across a hall want
different sizes of the same room, not different rooms.
Each room keeps its own `UI_SCALE` in `/etc/ekklesia-classroom/device.conf`.
Package upgrades preserve the value. Plasma applies it at every classroom login.
Native display resolution is unchanged.
## Changing it
## Administrator setting
Edit `/etc/ekklesia-classroom/device.conf` on that endpoint:
On the classroom keyboard press **Meta+Ctrl+Alt+Shift+D** to open Display size.
It is hidden from application lists and the teacher's panel. Saving requires
administrator authentication through polkit; the keyboard shortcut grants no
privilege. Choose a percentage and press Save and apply. Canceling the password
prompt leaves the saved setting unchanged.
Start at **200% for a 4K TV**, which gives 1920×1080 logical space while retaining
3840×2160 output. The offered values are 100, 125, 150, 175, 200, 225, 250 and 300%.
The choice applies to all connected, enabled displays on that classroom computer.
For SSH administration, the fixed-purpose helper can also be run as root:
```
UI_SCALE=1.25
sudo /usr/local/bin/classroom-set-scale 2
```
Then restart the classroom shell:
It writes only UI_SCALE, preserving room configuration, broker credentials and
file ownership and permissions. The new size takes effect at the next classroom
login, or by running `classroom-scale` inside the classroom's graphical session.
The hidden window saves and applies in one operation.
```
sudo systemctl restart getty@tty1
```
## Implementation and validation limits
That ends the session and the autologin brings it straight back at the
new size. A reboot does the same. Nothing else needs touching, and no
CSS or source file is ever edited to change a room's size.
`classroom-scale` uses `kscreen-doctor`; the login autostart runs only in KDE and
the script applies only to the classroom account. Values read from older/manual
configurations remain clamped to 0.8–3.0; invalid/nonfinite values become 1.0.
The helper accepts only the window's predefined choices, takes no paths, and
writes the root-owned settings file atomically. Polkit requires auth_admin for
every save, with no retained authorization.
## What it scales
Hardware checks required: shortcut registration, administrator authentication,
200% at 4K, logout/reboot persistence, and Chrome/Parent Call/DVD sizing. XWayland
applications may be softer than native Wayland applications when scaled.
**Everything.** UI_SCALE is a property of the television, not of one
program, so it is applied where it reaches the whole session: sway's
output scale.
## Wallpaper in the v2 package
- Chrome's tabs, omnibox, toolbar icons and menus
- VLC, and any other application's own interface
- The dock, the clock, notifications and the DVD control bar
- Mouse hit targets, because they scale with the widgets
The approved background source is `config/wallpapers/classroom-calm.png`. The
package installs it at `/usr/share/backgrounds/ekklesia/classroom-calm.png`, the
path used by the packaged Plasma layout. The layout is rebuilt at login, so
upgraded classrooms receive the package wallpaper without per-room copying.
The display mode is **not** changed. The panel stays at its native
1920x1080. Only the logical coordinate space shrinks, which is what
makes everything drawn in it larger:
## What the one number reaches
| UI_SCALE | Logical space | Roughly |
| --- | --- | --- |
| 0.8 | 2400x1350 | Small or close-up panel |
| 1.0 | 1920x1080 | Default. 32–50" classroom TV |
| 1.25 | 1536x864 | Large room, or a set viewed from further back |
| 1.5 | 1280x720 | 65"+ |
`classroom-ui-scale` prints it, clamped, and everything that cannot read
the display scale for itself asks that command rather than the file:
Anything outside 0.8–1.5 is clamped, and anything that is not a number
falls back to 1.0, so a typo in device.conf cannot produce an unusable
shell.
* **Plasma** — the panel, the clock, notifications, the volume display and
every dialog, because `classroom-scale` sets it as the display's scale
through `kscreen-doctor` at every login.
* **Chrome and Wonder Ink** — `--force-device-scale-factor`, because
Chrome on Wayland was measured not to follow the compositor's output
scale on its own. It is the same number, not a second scaling: Chrome
replaces its detected scale with it.
* **The disc player's own surfaces** — they are measured in the logical
pixels the compositor is already scaling, so they follow without being
told.
## Measured
Against 1920x1080 under sway, with Chrome 152:
| | 1.0 | 1.25 | 1.5 |
| --- | --- | --- | --- |
| Chrome tab strip | 40px | 49px (x1.23) | 59px (x1.48) |
| sway title bar | 25px | 31px (x1.24) | 38px (x1.52) |
| Classroom dock | 74px | 92px (x1.24) | 110px (x1.49) |
Everything scales once, together.
## Why our own surfaces do not scale themselves
`classroom-scale` runs first in the sway config and sets the output
scale. From then on the dock, the notifications and the DVD control bar
are measured in the same logical pixels sway is already scaling, so they
must render **unscaled** — `SHELL_SCALE` in `classroom_config.py` is
`1.0`. Scaling them again would apply UI_SCALE twice and the dock would
come out half again too big.
The stylesheet templates still take a scale, and `SHELL_SCALE` is the
one place it comes from. If compositor scaling ever has to be abandoned,
setting it back to `ui_scale(config)` restores per-surface scaling
without touching a template.
## Tradeoffs
**GTK 3 has no fractional scaling.** At UI_SCALE=1.5 sway advertises an
integer scale of 2 to GTK, which renders at 2x, and sway scales the
result down to 1.5x. The dock is the right size but very slightly softer
than at 1.0. On a TV at classroom distance this is not visible.
Icons have to be drawn for that themselves. `new_from_file_at_scale`
rasterises an SVG at its *logical* size, and the compositor then
enlarges that finished raster — which made icons visibly soft on a 24"
monitor while the clock text beside them stayed sharp. `classroom_icons`
renders each SVG at `size x scale_factor` physical pixels and hands GTK
a cairo surface carrying that device scale, so the artwork lands at the
display's real density and the layout is unchanged. Measured on the
Chrome icon, peak edge contrast at UI_SCALE 1.5 went from 49 to 83 out
of 255, and at 2.0 from 37 to 82; at 1.0 it is identical to before.
**XWayland is softer at fractional scale.** X11 clients have no concept
of a fractional output scale, so sway renders them at the logical size
and scales the buffer. Measured on terminal text, scale 1.5 produced
about 50% more intermediate-tone pixels than scale 1.0 — real, modest
softening.
VLC is the only XWayland application in the classroom, and in DVD mode
none of its interface is on screen: only video, from a 720x480 disc
being upscaled regardless. Chrome runs on Wayland natively and is sharp
at every scale, which is what matters for reading.
At UI_SCALE=1.0 nothing is scaled at all and the session is identical to
one with no scaling.
The clock is the one thing that used to be left behind. It was pinned at
15 points, which is the same 15 points at every scale, and on a 4K
television it was a clock nobody at the back of the room could read. It
is sized by the bar now.