diff --git a/config/plasma/applications-local/org.kde.konsole.desktop b/config/plasma/applications-local/org.kde.konsole.desktop index 6b5ab7c..30c67ff 100644 --- a/config/plasma/applications-local/org.kde.konsole.desktop +++ b/config/plasma/applications-local/org.kde.konsole.desktop @@ -18,3 +18,12 @@ Terminal=false Categories=System;TerminalEmulator; NoDisplay=true StartupWMClass=org.kde.konsole +# Konsole's own entry declares X-KDE-Shortcuts=Ctrl+Alt+T. That is a +# chord a great many people know, and it is not one a classroom should +# answer. Blanked here. +X-KDE-Shortcuts= +# Konsole's own entry also carries this, and it is what makes the default +# chord inert even where the entry is still found: with the classroom's +# shell_access=false the launch is refused. Measured — invoking that +# shortcut over D-Bus opened nothing. Kept, so the two layers stay two. +X-KDE-AuthorizeAction=shell_access diff --git a/docs/classroom-v2-experiment.md b/docs/classroom-v2-experiment.md index 144e727..828da07 100644 --- a/docs/classroom-v2-experiment.md +++ b/docs/classroom-v2-experiment.md @@ -746,45 +746,6 @@ record failures rather than successes. machine whose network NetworkManager actually owns. -## Television control, and where CEC will fit - -The devices arrive today. **Nothing here guesses at their protocol** and -no CEC code has been written. - -What the architecture needs to leave room for: the volume control in the -bar today is the *computer's* volume, and once the television is under -CEC control that is the wrong thing for a teacher to be adjusting. The -teacher should be turning the television up, not the computer. - -The shape that keeps Plasma out of the hardware: - -``` -panel button / notification presentation only - | - v -ekklesia-classroom-tv.service a small supervised user service - | the same shape as the audio policy - +-- backend: CEC over the adapter - +-- interface: on, off, volume up, volume down, mute, input -``` - -Three properties to hold to: - -* **Plasma is presentation.** The panel may show state and send an - action; it must not contain the protocol. That is why the audio policy - is a service and not a plasmoid, and this should follow it. -* **The interface is verbs, not device commands.** "Volume up" is the - contract; whether that is a CEC user-control-pressed opcode or - something else is the backend's business. -* **Supervised and failure-tolerant.** A television that is off, an - adapter that is unplugged, or a set that ignores CEC must degrade to - nothing happening, never to a stuck classroom. - -When the hardware has been exercised, the computer volume applet comes -out of the bar and a television control goes in. Until then it stays, -because a room with no sound control at all is worse. - - ## Live View, and whether this desktop can carry it Not implemented. What the KDE architecture already provides for it: @@ -804,6 +765,178 @@ Not implemented. What the KDE architecture already provides for it: The likely media path (MediaMTX, WHEP) is a property of the player application and does not touch the desktop architecture. + +## The power button, and the way back in + +### A press does nothing + +Two layers, and both are needed. While a graphical session is running, +PowerDevil takes logind's `handle-power-key` inhibitor and acts on the +button itself, so a logind drop-in on its own would be overruled. + +| layer | file | what it says | +| --- | --- | --- | +| logind | `/etc/systemd/logind.conf.d/ekklesia-classroom.conf` | `HandlePowerKey`, `HandleSuspendKey`, `HandleHibernateKey`, `HandleRebootKey` and every lid switch set to `ignore`, long-press included | +| PowerDevil | `/etc/ekklesia-classroom/xdg/powermanagementprofilesrc` | `powerButtonAction=0` and `lidAction=0` in all three profiles, groups marked `[$i]` so the session cannot put an action back | + +logind covers the machine before the session starts, after it ends, and +if PowerDevil is not running at all. PowerDevil covers it while a teacher +is using it. + +Measured in the lab: with the drop-in installed and logind reloaded, +logind reports `ignore` for the power, suspend, hibernate and lid keys — +it reported `poweroff` and `suspend` before — and the running graphical +session was not disturbed by the reload. The `postinst` therefore +reloads logind, so the button stops working at install time rather than +at the next boot. + +**Holding the button still cuts the power.** That is firmware, below any +operating system, and it is left alone on purpose: it is the only +recovery a person standing at a wedged machine has. + +**Needs the hardware.** Nothing in a container has a power button. What +is proven is that both layers are configured, packaged, and read back +the way they should. Pressing the button is a bench test. + +### One chord opens a terminal + +`Meta+Ctrl+Alt+Shift+K`. + +Normal administration is still SSH. This is for the case where somebody +is standing at a machine and SSH is not available. + +The chord is declared in the package — `X-KDE-Shortcuts` in +`ekklesia-admin-terminal.desktop` — rather than seeded into a teacher's +configuration, where an update could not reach it. KGlobalAccel read the +entry and registered a component for it; invoking that component's +`_launch` over D-Bus opened a working shell, which is how the whole path +was verified without a keyboard. + +It needed a wrapper, for a reason worth recording. The classroom's Kiosk +policy sets `shell_access=false` and **Konsole honours it**: with the +classroom configuration in its cascade, Konsole starts and prints +*Program to run not set.* rather than opening a shell. That restriction +is worth keeping for everything else, so `classroom-admin-terminal` +lifts it for one process by leaving the classroom's configuration +directory out of that process's `XDG_CONFIG_DIRS`. Nothing is patched, +no binary is replaced, and the terminal therefore looks like stock KDE +rather than like the classroom — which is fair, because it is not part +of the classroom. + +**It grants no privilege.** The classroom user has no sudo rule, and +anything needing root still needs the administrator's own account and +password. `shell_access` was always a barrier in the interface, never a +privilege boundary. + +Konsole is a package dependency, so the route exists on every machine. +Its own entry is shadowed from `/usr/local/share/applications` with +`NoDisplay=true`, leaving Konsole's file exactly where its package put +it. + +One thing found while testing, and kept as a second layer rather than +removed: **Konsole ships `X-KDE-Shortcuts=Ctrl+Alt+T`**, a chord a great +many people know. The shadow entry blanks it, but the registration comes +from Konsole's own file and survives that — so it is still registered. +It is also inert: Konsole's entry carries +`X-KDE-AuthorizeAction=shell_access`, and with the classroom's +`shell_access=false` the launch is refused. Measured: invoking that +component over D-Bus opened nothing. + +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. + +**Needs the hardware.** The chord itself has never been pressed — key +injection cannot reach the nested session in this lab. What is verified +is that the shortcut is registered from the packaged entry and that its +action opens a shell. + + +## Long-Escape to turn the television off: it should change + +The gesture asked for was: hold Escape, silence for three seconds, then a +five-second visible countdown, cancel on release. Deliberate, hard to +trigger by accident, and a clear intent. + +### What was measured + +* **KGlobalAccel accepts a bare `Escape`.** A probe entry declaring + `X-KDE-Shortcuts=Escape` was registered and given its own component. So + the binding is possible. +* **Press *and release* are both observable.** The component's D-Bus + interface carries `globalShortcutPressed` **and** + `globalShortcutReleased`. Hold duration and cancel-on-release are + therefore expressible without any input daemon of our own. +* **KWin's scripting API has no key events at all** — `registerShortcut` + and screen edges, and nothing that reports a key going up. + +### What was not measured, and why it decides the answer + +Whether a registered global shortcut **takes the key away from +applications**. Key injection cannot reach the nested session in this +lab, so this could not be pressed. + +The mechanism says it does: on Wayland, KWin matches global shortcuts in +an input filter that runs before the event reaches the focused surface, +and a match stops propagation. That is how `Meta+Q` never arrives at an +application. A bare `Escape` bound globally should therefore stop +reaching anything — no leaving a fullscreen video, no dismissing a Chrome +dialog, no closing the Parent Call card. + +**This is inference, not a measurement, and it is a thirty-second bench +test**: bind the probe entry above, press Escape in a fullscreen video, +and see whether it leaves fullscreen. + +### The recommendation + +**Change the gesture.** Even if Escape were forwarded as well as +observed, the whole ceremony would rest on a global grab of the key an +appliance uses most, and a countdown driven from the process holding +that grab is an input daemon in all but name. That is the fragile thing +the classroom keeps removing. + +Two clean alternatives, both using mechanisms already proven here: + +1. **Hold-to-confirm on screen.** A television control where the volume + applet is now: press and hold it, a countdown runs, releasing + cancels. Press, hold and release inside an application's own surface + are ordinary Qt and need no global input at all. This keeps every + property the gesture was for — deliberate, visible countdown, + cancel-on-release — and costs nothing in reliability. +2. **A modifier chord**, the same mechanism as the maintenance terminal + and proven to work in this session. Instant rather than ceremonial, + but impossible to press by accident. Worth adding as a second route + for somebody who cannot find the mouse. + +If a keyboard hold is still wanted after the bench test, it should be on +a key the classroom does not otherwise use — never Escape. + +## The television control service + +Unchanged in shape from the previous checkpoint, and now with the +gesture question answered: + +``` +panel control / chord presentation only + | + v +ekklesia-classroom-tv.service small, supervised, ours + | + +-- backend: CEC over the adapter + +-- interface: on, off, volume up, volume down, mute, input +``` + +* The keyboard and the panel request **"TV off"**. They never speak CEC. +* The service decides how. CEC hardware can change without the desktop + noticing. +* The same service will own automatic TV-on at session start, volume, + mute and input, and it is why the computer's own volume applet is + expected to leave the bar. +* Supervised and self-recovering, the same way the audio policy is. + +**No CEC code has been written and no protocol has been guessed.** The +adapters arrive today; the commands get measured before anything is +built on them. + ## Unresolved risks In the order I would worry about them. @@ -848,6 +981,18 @@ In the order I would worry about them. against this palette, and is to be supplied separately. 10. **The disc launcher tracks the drive, not the disc.** A room with a drive shows the DVD button whether or not anything is in it. +11. **The power button has never been pressed.** Both layers are + configured, packaged, and read back correctly, but nothing in a + container has a power button. +12. **The maintenance chord has never been typed.** The shortcut is + registered from the packaged entry and its action opens a shell — + verified over D-Bus — but key injection cannot reach the nested + session, so `Meta+Ctrl+Alt+Shift+K` itself is unpressed. +13. **Ctrl+Alt+T is still registered.** Konsole's own entry declares it + and the registration survives being shadowed. It is inert, because + Konsole refuses to open a shell under the classroom's + `shell_access=false` — measured — but it rests on that one + restriction rather than on the chord not existing. ## Would I trust it on a classroom computer, unattended, for a Sunday? @@ -872,9 +1017,14 @@ Before any room: hardware*. 3. Press Parent Call and **type a name with a real keyboard**. Then do it with the coordinator's mode off, and with the broker stopped. -4. Print to Elmer. -5. Play a DVD, if that room uses them: cover, picture, controls, eject. -6. Leave it running from Friday to Sunday. +4. **Press the power button.** Nothing should happen. Then hold it, and + confirm the machine still dies — that is the recovery path and it + must stay. +5. **Type Meta+Ctrl+Alt+Shift+K.** A terminal should open. Then try + Ctrl+Alt+T and confirm nothing does. +6. Print to Elmer. +7. Play a DVD, if that room uses them: cover, picture, controls, eject. +8. Leave it running from Friday to Sunday. ## Alternate operating systems @@ -922,13 +1072,24 @@ The existing Debian classroom test VM should be kept. can reach a room that has already run. * **Recovery re-measured against the packaged build**, not a hand-edited session. +* **The power button disabled at both layers**, packaged, and read back + from logind after a reload that did not disturb the session. +* **A maintenance terminal behind one obscure chord**, declared in the + package, hidden from every application list, granting no privilege. +* **Long-Escape investigated and recommended against**, with the two + clean alternatives written down. **Next**, in order: +0. **The thirty-second Escape test**, before anything is built on the + gesture: bind a probe entry to `Escape`, press it in a fullscreen + video, and see whether the video leaves fullscreen. That settles the + one thing this pass could not measure. 1. **On the bench, tonight.** Type a name into Parent Call with a real keyboard. Switch a television on after the computer and listen. Play a disc. These three are the only things left that a container cannot - answer, and each of them is a Sunday-morning path. + answer, and each of them is a Sunday-morning path. Add the power + button and the maintenance chord to that list. 2. Publish `0.1.45~plasma1` to a channel of its own, the way the Hyprland milestone did, so a stable or canary room can never be offered it. 3. Bench-test SDDM: autologin, and that ending the session brings the diff --git a/docs/screenshots/README.md b/docs/screenshots/README.md index 2d9274d..97b8b31 100644 --- a/docs/screenshots/README.md +++ b/docs/screenshots/README.md @@ -59,6 +59,13 @@ container lab. See [../classroom-v2-experiment.md](../classroom-v2-experiment.md | `v2-layershell` | a gtk-layer-shell overlay under KWin — what the DVD loading cover is made of | | `v2-notification-crop` | a notification card, top right | +## Administration + +| | | +| --- | --- | +| `admin-terminal` | the maintenance terminal, opened by its chord's action | +| `admin-terminal-bar` | the bar with the terminal open, and after closing it: a task button while it runs, no launcher ever | + ## Reliability and lockdown | | | diff --git a/docs/screenshots/admin-terminal-bar.png b/docs/screenshots/admin-terminal-bar.png index 35cac74..af44e9d 100644 Binary files a/docs/screenshots/admin-terminal-bar.png and b/docs/screenshots/admin-terminal-bar.png differ diff --git a/docs/screenshots/admin-terminal.png b/docs/screenshots/admin-terminal.png index 3f58d3b..9b9608b 100644 Binary files a/docs/screenshots/admin-terminal.png and b/docs/screenshots/admin-terminal.png differ diff --git a/docs/screenshots/v2-idle-desktop.png b/docs/screenshots/v2-idle-desktop.png index 51ada94..92814a1 100644 Binary files a/docs/screenshots/v2-idle-desktop.png and b/docs/screenshots/v2-idle-desktop.png differ