2325a5993bca6b4d35c26c165ba6babb19aefdd9
19
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
55f4c183c7 |
feat: Presentations, for a teacher who downloaded tonight's session
PRESENTATIONS_ENABLED, off by default, independent of everything else. A teacher downloads a session in ordinary Chrome and it is on the Presentations screen a few seconds later, ready for the television. No file manager, no import button, no Impress. WHY NOTHING LAUNCHES A DOCUMENT APPLICATION. The brief was to use existing presentation software, so the existing software was tried first, on Debian 13, before any of this was designed. `soffice --show` does start a real fullscreen slideshow — and needs XDG_SESSION_TYPE plus a VCL plugin or it dies with "Failed to open display", shows a splash, opened a "Tip of the Day: 1/225" dialog over the slides, and leaves the Impress editor when the show ends. `okular --presentation` opens THREE windows: the presentation, the ordinary document window behind it, and an information dialog. zathura is not a presenter. pdfpc is the closest and is another dependency with a presenter-console model. Every one of them leaves an application on screen that can open any file on the machine, and none of them knows there is a library to go back to. So LibreOffice converts once, headless, at import, with no window on any screen; poppler renders the pages; the classroom shows the pictures. Measured: 0.34s for a ten-slide PPTX, 0.03s for its ten thumbnails, 0.10s for one slide at 1920px. That is not a new renderer — it is the same engine Impress would have used live — and it means Escape is ours, presenting is instant, and a PDF exported from Google Slides skips the conversion entirely and keeps the appearance Google gave it. THE THREE THINGS KEPT APART. The library thumbnail, the starting slide and the retention clock are independent, because the first slide of a Bible study session is a generic opening shared by every session and the one that names it is usually the second. Quick Look chooses the picture; it does not change where the slideshow starts and it does not count as having presented anything. mark_presented() is called when a slide is actually drawn, not when Present is pressed: an intention is not a presentation, and a viewer that failed to start must not buy another thirty days. IMPORTING. The teacher's download is copied, never moved, renamed or deleted. A file with a .crdownload beside it is still arriving. Wonder Ink's own folder is not looked at. Two downloads of the same name are two presentations, the second titled (2) — found in the lab, where a PDF and a PPTX of one session both came out with the same name because the import being named was excluded from the taken-titles set BY TITLE, and a set holds one copy of a repeated string. Metadata is written saying "importing" before any work, so a reboot mid-conversion leaves a directory the next startup sweeps and imports again. A file that cannot be converted gets a card saying so, offering only Remove, with the teacher's download untouched. Retention: 180 days never presented, 30 days from the last presentation, every presentation starting the thirty over. The cleanup never touches a presentation being shown, never leaves the library directory, and never looks at Downloads. The dock order is unchanged: Presentations is appended after the disc player, so Wonder Ink, the browser and the player keep the places teachers have been reaching for. There is a test for exactly that. classroom-wonderink now asks feature() rather than carrying its own copy of the default, so the panel and the command cannot disagree. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
34fd02e08f |
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]> |
||
|
|
926668dc9b |
fix: make an existing classroom converge into the Plasma appliance
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]> |
||
|
|
30a924d945 |
feat: the power button does nothing, and one chord opens a terminal
Two layers for the button, because one is not enough: while a session is running PowerDevil holds logind's handle-power-key inhibitor and acts on the button itself, so a logind drop-in alone would be overruled. logind covers the machine before the session starts, after it ends, and if PowerDevil is not running. Verified: after the drop-in is installed and logind reloaded, it reports ignore for the power, suspend, hibernate and lid keys, and the running session was not disturbed by the reload. Holding the button still cuts the power. That is firmware, below any operating system, and it is deliberately left alone as the only recovery a person standing at a wedged machine has. The maintenance terminal is Meta+Ctrl+Alt+Shift+K, declared in the package rather than seeded into a teacher's configuration, where an update could not reach it. KGlobalAccel read it and registered the component; invoking it opened a working shell. It needed a wrapper. The classroom's Kiosk policy sets shell_access=false and Konsole honours it — measured: with the classroom configuration in its cascade Konsole starts and says "Program to run not set." The restriction is worth keeping everywhere else, so it is lifted for this one process by leaving the classroom's configuration directory out of its cascade. It grants no privilege: the classroom user has no sudo rule, and that restriction was always a barrier in the interface rather than a privilege boundary. Konsole's own entry is shadowed and hidden from /usr/local/share, so nothing is patched and Konsole's package stays maintainable. Nothing is pinned to the panel; a task button appears only while a terminal is open, and closing it returns the bar to three launchers. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
d2d30d6b0c |
feat: take the last of sway out of the classroom
The DVD player was the only thing still speaking a compositor the room no longer runs. It asked sway's tree whether VLC's window had really gone fullscreen, because VLC reports playing while its window is still the black startup one the loading cover exists to hide. VLC's own status was measured as a replacement and is not one: it reports fullscreen true in the same 250ms poll as playing, because the flag is the option it was started with and not a report about its window. So the cover is held for a floor and capped by the deadline that was already there, and the compositor is not asked anything. Focus-or-launch went the same way. The session already takes an exclusive lock, so a second one cannot start, and bringing an existing one to the front is the panel's job. classroom-window, classroom_sway and classroom_chrome are gone with it — the last of them was reachable from nothing at all. DVD also moves out of the status row, where it sat beside the clock as though a disc were a machine reading. It is an application: it is in the row with Wonder Ink and the browser, and classroom-provision removes it from the layout on a machine with no optical drive, which retires DVD_ENABLED. A computer that moves between rooms takes its drive with it; nobody remembers to edit a file when it does. The panel is rebuilt from the package at every login now. Plasma keeps whatever panel it first built, so an update could change the bar in the package and never change it in a room — measured, with an icon that outlived the file it came from. Editing the live panel configuration was tried instead and destroyed the panel, because the shell owns that file. Removing it before the shell starts is safe and makes the desktop stateless. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
f7d5aecd6c |
build: make the classroom desktop something a .deb installs
Building the package found two things reading the tree did not. dpkg refused it outright: plasma-workspace already owns /etc/xdg/plasmanotifyrc and friends, and two packages cannot own one path. The room's settings move to a directory of their own with /etc/environment.d putting it in front of /etc/xdg, which is KConfig's own cascade rather than a file fight. The postinst then failed on a chmod for a command that no longer exists. It now walks what the package installed instead of naming files, removes the login shell that used to start sway on tty1, and enables SDDM and the audio service. Verified by installing the built package on a fresh machine and starting the session from it: restrictions in force, palette applied, audio service enabled and running, panel supervision unconditional. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
f65c814eff |
fix: put the popup close button in the dock, and clear the printer location
Three things, none of them redesigns. The close affordance for a secondary Chrome window was a separate layer-shell surface anchored to that window's corner, repositioned by a five-per-second poll. Any position change tore the surface down and built another, so on real hardware it chased the window and flickered. Removed entirely: the GTK button process, the geometry maths that placed it, the --close-button mode that spawned it, and the poll loop that drove it. The window helper no longer imports GTK at all. The behaviour underneath it was right and is kept. The first Chrome top-level is the classroom browser, tiled and borderless; later ones float centred at about three quarters of the output. Creation order is the only thing that can tell them apart — confirmed on a real session, where every Chrome top-level shares app_id, shell, PID and a null transient_for, and sway exposes no window properties for Wayland toplevels at all. Titles are still not used. It now follows sway's own window events instead of polling, so dragging a popup costs nothing. Close moved into the dock, which was already on screen and does not move, so it cannot chase anything. It appears only while a secondary window is focused and runs kill on that one container. The helper publishes which window that is; the dock watches the file rather than polling. A stock sway title bar has no close button, so restoring the border would not have solved this either. Two bugs found by looking rather than by testing state. Chrome renegotiates the size of a popup opened with explicit dimensions after the resize returns, which left windows centred for a width they no longer had; placement now settles and repeats once, at creation only, so dragging is still free. And the dock's Close button came up empty because show_all does not recurse into a no-show-all widget. The printer's Location is now blank. CUPS prints it under the queue name in every dialog, so "Ekklesia Classroom" was a second line the teacher had to read past; the identity is one word. The already-configured check includes Location, so a machine set up before this is corrected rather than left as close enough. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
1e9ca24d57 | Release 0.1.37 deployment prep | ||
|
|
7f66228b9e | Release 0.1.37 deployment prep | ||
|
|
dfd42f6fdf |
feat: give the rooms a line to the church broker
The first piece of turning these machines into connected endpoints. classroom-mqtt is one small service per room that subscribes, validates what arrives, and hands the work to commands that already exist. Nothing in the room depends on it. A broker that is down, a network that is missing, a machine with no broker configured at all — each is a service sitting quietly, not a classroom that fails. Nothing waits for it at boot and the shipped default is off, so every existing classroom upgrades into this release with messaging disabled until a broker exists. The room is the hostname, which is already how these machines are named. Topics carry no product name because the broker is the church's and these are just rooms: classroom/notify/all, classroom/notify/<room>, and a retained classroom/status/<room> so anyone asking later can be told which rooms are up without waiting for them to say so again. Notifications are events and are never retained; status is state and is. Stale messages cannot come back, which is the property that matters most and has two halves. The session is clean, so a broker never hands over a backlog from while the room was off. And retained notifications are refused on arrival, because otherwise one would replay into every room at every reboot forever. The honest limit is written down: MQTT only flags a retained message when it is replayed to a new subscription, so a retained publish delivered live is indistinguishable from an ordinary one and is shown once — which is right, since it is live at that moment, and the replay is the actual hazard. Delivery reuses the session-aware sender rather than a second copy of it. classroom-notify-test grew into the real thing, so it is now classroom-notify with the sample sender a wrapper over it, and its 36 tests came along. The broker password lives in device.conf, so that file stops being world readable: 0640 root:classroom, which everything that reads it already satisfies. Transport logic is kept free of any broker library so it can be tested without one — 40 checks on room identity, the topic contract and which payloads become notifications. Separately verified against a real mosquitto: connect, subscribe, retained status, per-room and all-room delivery, urgency, duplicate and expired and malformed messages ignored, the broker killed with the daemon surviving, reconnect, and a stale retained notification left on the broker not reaching the screen when a room boots. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
255072ac94 |
feat: settle Chrome's window, crash bubble and password prompts
Three things a volunteer should never have to see. Sway was drawing its own title bar above Chrome's tab strip, which already carries the window controls. Verified on a real session: Chrome is an xdg_shell client with app_id "google-chrome" and sway was giving it border "normal". A scoped rule removes the compositor decoration only — Chrome keeps its tabs and address bar, stays a normal window, and still maximises and minimises. A class rule covers an XWayland fallback. Nothing else changes: the only other window in this classroom is VLC, which is already borderless, so no broader rule is warranted. "Restore pages? Chrome didn't shut down correctly" is suppressed with --hide-crash-restore-bubble. Note the name. The switch this was going to use, --disable-session-crashed-bubble, is not present in Chrome 152 at all — checked against the strings of the installed binary — so it would have silently done nothing. That hides the prompt; the profile was still being marked as crashed, which is the untidy half. Chrome's browser process shuts down cleanly on SIGTERM, so a shutdown unit now gives it one and waits up to eight seconds before the session is torn out from under it. Best effort by construction: a shutdown that will not finish is far worse than a crash flag. Password prompts go off with the PasswordManagerEnabled policy, on the path the classroom already manages. Existing profile and session data are untouched, and website logins are unaffected: Wonder Ink and YouTube stay signed in. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
8bed3d186a |
feat: scale the whole session, not just the classroom's own windows
UI_SCALE only ever reached the surfaces we draw ourselves. Chrome stayed tiny — its tabs, omnibox and menus are Chrome's own pixels, and Ctrl+ only scales page content, which is no use to a volunteer reading a toolbar from across the room. UI_SCALE is a property of the television, not of one program, so it now sets sway's output scale, which every client is measured against. classroom-scale runs first in the session and applies it. The display mode is untouched: the panel stays at 1920x1080 and only the logical coordinate space shrinks, which is what makes everything in it larger. Our own surfaces stop scaling themselves. They are measured in the same logical pixels sway is already scaling, so applying UI_SCALE again would double it. SHELL_SCALE is that decision in one named place, and setting it back to ui_scale(config) would restore per-surface scaling without touching a template. Measured at 1920x1080 with Chrome 152. Chrome's tab strip 40 -> 49 -> 59px against 1.0, 1.25, 1.5; sway's title bar 25 -> 31 -> 38px; the classroom dock 74 -> 92 -> 110px. Everything scales once, together, and 1.0 is identical to no scaling at all. Two tradeoffs, both measured rather than assumed. GTK 3 has no fractional scaling, so at 1.5 it renders at 2x and sway scales down — very slightly softer, invisible at classroom distance. XWayland is softer too: terminal text at 1.5 showed about 50% more intermediate-tone pixels than at 1.0. VLC is the only XWayland application here, and in DVD mode none of its interface is on screen, only video from a 720x480 disc that is being upscaled regardless. Chrome is a native Wayland client and stays sharp, which is what matters for reading. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
f8ddfbe345 |
feat: make DVD playback a classroom surface, not a media player
Pressing DVD now enters DVD mode rather than opening VLC. VLC is still the backend, because its dvdnav module is what decodes disc menus and nothing else on Debian does that reliably, but nothing a person can see or press belongs to it any more. classroom-dvd-session owns the whole thing. It puts a full-screen black layer-shell surface up the moment the button is pressed, starts VLC behind it, and drops it once playback is actually on screen — so the harsh cut from wallpaper to VLC's black window is gone, and no partial VLC window or reserved dock strip is ever visible. A deadline drops the surface regardless, because DVD mode must never leave a black screen with nothing behind it. The controls are a classroom bar on the overlay layer, the one layer sway draws above a fullscreen window. It slides up when the pointer reaches the bottom of the screen and hides again shortly after it leaves. It is drawn from the dock's own stylesheet at the room's UI scale, so it is the same surface, the same radius and the same white icons. While hidden it only accepts the pointer in a thin strip along the bottom edge, so a click on a disc menu goes to the disc. The bar reaches VLC through an HTTP interface on loopback behind a password generated per session, sending hotkey *actions* by name. That is what makes the next part possible. All 108 hotkeys VLC 3.0.23 defines are unset, then Space, Esc and the arrow keys plus Enter are bound. Crop, aspect ratio, subtitles, audio tracks and deinterlacing are no longer reachable by pressing a key at random, and the arrow keys no longer change VLC's volume — the television owns volume, and CEC will own it properly later. Esc quits instead of leaving fullscreen. There is nothing to leave, and leaving it was what stranded people on a black VLC window. Close, Eject and Esc all ask VLC to quit and escalate to SIGTERM then SIGKILL if it does not; when VLC exits by any route, including a crash, the session ends and the classroom desktop comes back. Verified against VLC 3.0.23 on Trixie: every argument accepted, every control action accepted over the control interface, a wrong password refused, and quit exiting cleanly. The surfaces were rendered under a headless sway and screenshotted to check the bar against the dock. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
a3dba6e8fb |
feat: provision Chrome and DVD codecs without a manual step
A fresh classroom needed two things installed by hand on every box: Google Chrome, which is not in Debian, and the DVD codecs, which Debian cannot ship. Both now happen on their own. ekklesia-classroom-provision.service runs after every boot and does the work that needs the network and the apt lock, neither of which a package postinst can have. Each step returns immediately if it is already done, so a provisioned machine costs nothing and a machine that was offline the first time gets another go at the next boot. Nothing there is allowed to fail the boot: a classroom with no DVD codecs is still a working classroom, and it says so in the log rather than silently. The postinst enables the service but deliberately does not start it. Every OTA update reboots the moment the install finishes, and an apt run killed by that reboot would leave dpkg half-configured. Chrome comes from Google's own repository rather than a one-off .deb, so it keeps getting security updates. Google's package migrates whatever we write into a deb822 file of its own, so the repository is looked for across every source file instead of only the one we would write; otherwise a machine that had Chrome removed would end up with two copies of the same repository. The DVD codec helper previously assumed deb822 sources and did nothing on a machine using a classic /etc/apt/sources.list, which is what the hardware actually runs. It now handles both formats, and only ever adds a missing component to an existing Debian entry — so it cannot produce a duplicate repository or a duplicate component however many times it runs, and third-party repositories like Chrome's are never touched. It also answers libdvd-pkg's debconf questions, so nothing blocks on a prompt in a service with no terminal. Tested against fixture APT trees for classic sources.list, deb822 .sources, contrib already enabled, repeated runs, third-party repositories and the awkward real lines: bracketed options, indentation, trailing comments and flat repositories. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
428eb27a74 |
feat: scale the shell to the room's television
A 32" panel a few feet from the children and a 65" set across a hall want the same design at different sizes. UI_SCALE in device.conf now sets that, and nobody has to edit CSS or source to tune a room. The stylesheet and the notification config become templates carrying measurement tokens. classroom-dock resolves the stylesheet in memory at startup; classroom-notifications resolves the notification config into the session's runtime directory and execs mako on it, so the process is still just mako and everything that looks for it by name still finds it. Neither template is rewritten on disk, so an update cannot lose a room's setting and a room cannot drift from the packaged design. Dock height and the reserved strip, corner radii, padding, button size, spacing, every icon, the clock and its inset, and the notification geometry all follow the scale. Colours, translucency and the hairline borders do not: those are the design, not the size. At UI_SCALE=1.0 the rendered stylesheet is byte-identical to the one the shell used before any of this existed, which is the property worth keeping. Values outside 0.8 to 1.5 are clamped and anything that is not a number falls back to 1.0, so a typo in device.conf cannot produce an unusable classroom. Device configuration moves into a shared module, since the dock and the notification daemon now have to agree about what a setting means. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
0c1acfe5f5 |
feat: detect and play DVDs where a drive is fitted
DVD support stays optional per room: DVD_ENABLED decides whether the button is built at all, and rooms without it poll nothing. Upgrades append a commented DVD_ENABLED=false so the option is discoverable without changing what those rooms already do. The dock polls the drive with CDROM_DRIVE_STATUS, which is a status query rather than a read, so it does not spin the disc up. A disc that becomes ready announces itself once and brings the DVD logo up to full strength; an empty or unplugged drive holds it back, the same gray versus colour language the LIVE icon already uses. Nothing autoplays — playback always needs a deliberate press. Playback is VLC rather than mpv because mpv dropped DVD menu support, and menus are the part that matters for a commercial disc. It goes through classroom-window like everything else, so the player minimizes and restores from the dock exactly as the browser does. Pressing the button with no drive, no disc, or a disc still spinning up gives a plain-language notification instead of failing silently. Reading the drive needs group cdrom, which the postinst grants and the OTA reboot activates. Commercial CSS discs additionally need libdvdcss, which Debian cannot ship; classroom-dvd-css-setup bootstraps it from contrib. That is deliberately not run from the postinst, which executes while dpkg already holds the apt lock. The optical helpers live in a shared module both the dock and classroom-dvd import, and the dock imports it defensively: if it ever goes missing the shell loses disc detection rather than failing to start. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
6b133bdc3e |
feat: settle Chrome and notifications into the classroom look
Chrome stays a full browser. The managed policy only removes the things a volunteer should never be asked about — the first-run tour, the default-browser prompt, sync and sign-in promos, the profile picker — and pins the Home button to Wonder Ink so there is always one obvious way back. Startup URL is a recommended policy rather than a managed one, so a teacher can still change it. Nothing restricts where they browse, and the profile is untouched, so YouTube and other logins survive reboots and updates. Notifications were still stock mako, which looked nothing like the rest of the shell. Give them the dock's palette: the same dark slate, the same hairline white border, rounded, white text, top right. Kept a little more opaque than the dock so text stays readable over video, and on the overlay layer so a parent-call alert can still reach someone watching a fullscreen DVD. mako was only ever reaching the session through D-Bus activation. Start it from the sway config so it is running before anything needs it. classroom-notify-test sends a sample so the styling can be checked without waiting for a real event. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
af79aba21b |
feat: minimize and restore windows the way a desktop does
The scratchpad technically minimized Chrome, but sway hands scratchpad windows back floating, so restoring gave a draggable window instead of the maximized one the user put away. Park windows on a hidden workspace as ordinary tiled containers instead, and move them back to the visible workspace on restore. classroom-window carries the whole state machine, so the dock buttons and Super+M share one implementation: not running -> launch unfocused -> focus focused -> minimize minimized -> restore, tiled and maximized Fullscreen is dropped on the way out, since a container parked while still fullscreen keeps drawing over everything else. floating disable on the way back also rescues windows left floating by the old scratchpad path. Super+Shift+M restores the newest minimized window, so nothing can get stranded on the hidden workspace. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
5d7781c352 | chore: checkpoint classroom 0.1.5 stable |