fix: draw classroom icons at the display's real pixel density

Icons went soft under UI_SCALE while the clock text beside them stayed
sharp, which is visible on a 24" monitor even if it hides on a TV.

new_from_file_at_scale rasterises an SVG at its *logical* size. The
artwork is vector, but by the time sway scaled the session it was
already a fixed raster, and the compositor enlarged that. Text never had
the problem because GTK draws it at the real density.

GTK 3 has no fractional scaling, so sway advertises the ceiling integer
scale and scales the finished buffer down. Measured under sway: scale
factor 1 at output scale 1.0, and 2 at 1.25, 1.5 and 2.0. So each icon
is now rasterised at size x scale_factor physical pixels and handed to
GTK as a cairo surface carrying that device scale. The icon lands at the
real density; its logical size, and the whole dock layout, is unchanged.

Icons redraw on notify::scale-factor as well as at realize, since the
scale factor is not known until a widget is on a display and changes if
UI_SCALE is changed under a running session.

Browser, DVD, LIVE and all six DVD control icons go through it, in a
shared module so the dock and the DVD session cannot diverge. The
throbber is drawn rather than loaded and had the same flaw, so it draws
on a device-scaled surface too rather than being the one soft thing left
in a sharp dock.

The artwork is untouched. Verified by rendering the dock under sway at
1.0, 1.25, 1.5 and 2.0 against the previous build: apparent icon size is
identical at every scale (28, 35, 42, 56 px), 1.0 is unchanged
pixel-for-pixel, and peak edge contrast on the Chrome icon goes from 60
to 99 at 1.25, 49 to 83 at 1.5, and 37 to 82 at 2.0 out of 255. The DVD
control bar was checked the same way.

Also records that Wonder Ink session-only cookies are confirmed working
on the endpoint, which settles the question 0.1.34 shipped as unverified
and tells us the session is in cookies rather than localStorage.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
ekklesia
2026-09-06 14:19:38 -05:00
co-authored by Claude Opus 5
parent a0f2b74d05
commit a1a218b29f
7 changed files with 177 additions and 78 deletions
+19 -38
View File
@@ -57,50 +57,31 @@ is meant to avoid.
Worth a look at the endpoint: open that menu once and confirm it offers
nothing actionable.
## Wonder Ink between teachers — NOT YET VERIFIED
## Wonder Ink between teachers — CONFIRMED WORKING
The intent: teacher A signs in, uses the room, Chrome closes, teacher B
opens Chrome and Wonder Ink is signed out — while YouTube stays signed
into the church account.
Teacher A signs in, uses the room, Chrome closes, teacher B opens Chrome
and Wonder Ink is signed out, while YouTube stays signed into the church
account.
`CookiesSessionOnlyForUrls` with `[*.]wonderink.org` is the official
policy for that, and that pattern covers `app.wonderink.org` and every
other subdomain. **Whether it is sufficient has not been established.**
`CookiesSessionOnlyForUrls` with `[*.]wonderink.org` does it. That
pattern covers `app.wonderink.org` and every other subdomain.
It could not be tested here. Confirming it needs a real Wonder Ink
login, which this machine does not have, and three attempts to prove the
mechanism against a local test site were inconclusive — the positive
control never worked, so the negative results mean nothing. Rather than
report a guess as a result: unverified.
**Confirmed on the physical endpoint**, which is the only place it could
be: it needs a real Wonder Ink login. It could not be established here,
and three attempts to prove the mechanism against a local test site were
inconclusive because the positive control never worked — so this was
shipped as unverified and the endpoint settled it.
Two things must be checked at the endpoint before anyone relies on it.
Two consequences worth keeping in mind.
### 1. Is the session in cookies at all?
Wonder Ink's session is therefore **in cookies**, not in `localStorage`.
That matters because there is no per-site Chrome policy that clears
localStorage on exit: `ClearBrowsingDataOnExitList` and
`BrowsingDataLifetime` both exist in Chrome 152 but are global, and
blanket clearing would take YouTube with it. Had it gone the other way,
this would have needed a classroom-owned cleanup instead.
`CookiesSessionOnlyForUrls` clears **cookies**. A single-page app like
Wonder Ink may keep its token in `localStorage` instead, which that
policy does not promise to touch.
Sign in, then in DevTools (F12) → Application:
- **Cookies** under `https://app.wonderink.org` — is there a session or
auth cookie?
- **Local Storage** and **IndexedDB** for the same origin — is there a
token there?
Then the real test: close every Chrome window, confirm the process is
gone (`pgrep -x chrome` returns nothing), reopen, and see whether Wonder
Ink is still signed in.
If it is still signed in, the session is not in cookies, and **there is
no per-site Chrome policy that clears localStorage on exit**.
`ClearBrowsingDataOnExitList` and `BrowsingDataLifetime` both exist in
Chrome 152 but are global, not per-site — and blanket clearing is
exactly what must not happen here, because it would take YouTube with
it. That case needs a small classroom-owned cleanup instead, which has
deliberately not been built on speculation.
### 2. If Wonder Ink login is "Sign in with Google"
### If Wonder Ink login is "Sign in with Google"
`app.wonderink.org` loads Google Identity Services
(`accounts.google.com/gsi/client`), so Google sign-in is at least
+4
View File
@@ -32,6 +32,10 @@ Change it, then `sudo systemctl restart getty@tty1`.
- [ ] Chrome's buttons are still easy to hit with the mouse
- [ ] Dock, clock, notifications and the DVD bar grow by the same
amount — once, not twice
- [ ] Dock icons stay crisp at 1.25 and 1.5 on a normal-density monitor,
not just from across the room: compare an icon against the clock
text beside it
- [ ] DVD control bar icons are crisp at the same scales
- [ ] `UI_SCALE=0.8` shrinks everything together
- [ ] A nonsense value (`UI_SCALE=banana`) still gives a working shell
- [ ] An extreme value (`UI_SCALE=9`) is clamped, not obeyed
+10
View File
@@ -81,6 +81,16 @@ 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