feat: give the classroom a desktop instead of a shell

The whole appliance UI is now configuration for a desktop Debian already
ships: a Plasma 6.3 session with a colour scheme, a look and feel package
that builds the bar, window rules, and KDE's own Kiosk restrictions.

Two things were measured rather than assumed, and both changed a decision.

Plasma restarts a crashed panel three times a minute and then gives up,
which is the failure a classroom has already had. Four lines of systemd
drop-in make it unconditional; the panel then survived eight SIGKILLs a
second apart, back each time in 2.4s.

Plasma draws a minimised task and a background task identically, so the
five states the room needs cannot all be shown. Rather than write a task
manager, the classroom removes minimise: clicking a launcher always means
show me this.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
ekklesia
2026-09-10 08:35:12 -05:00
co-authored by Claude Opus 5
parent bfc7caf28f
commit 87b55761c6
51 changed files with 1352 additions and 36 deletions
+60
View File
@@ -0,0 +1,60 @@
# Ekklesia Classroom session policy.
#
# Installed to /etc/xdg/kdeglobals, where it is the bottom of Plasma's
# configuration cascade. Every group is marked [$i] — immutable — so a
# teacher, a stray click or a misbehaving application cannot override it
# from the user's own configuration. This is KDE's own Kiosk mechanism:
# nothing here is achieved by deleting a binary or hiding a menu entry.
#
# Lifting a restriction for maintenance is a one-line edit to this file
# followed by a session restart, which is what makes the appliance
# serviceable rather than merely locked.
[KDE Action Restrictions][$i]
# No way to reach a shell, a command line, or an arbitrary program.
action/kdesktop_rmb=false
action/kwin_rmb=false
action/openwith=false
action/switch_user=false
action/start_new_session=false
action/lock_screen=false
run_command=false
shell_access=false
logout=false
# The desktop and the panel are a fixed part of the appliance.
editable_desktop_icons=false
editable_bookmarks=false
movable_toolbars=false
plasma/plasmashell/unlockedDesktop=false
plasma-desktop/scripting_console=false
# Nothing in the classroom should offer to file a bug or open a manual.
action/help_report_bug=false
action/help_about_kde=false
action/help_contents=false
[General][$i]
ColorScheme=EkklesiaDark
AccentColor=79,179,168
# The teacher's own words are not decoration: keep text rendering plain
# and readable rather than styled.
XftAntialias=true
XftHintStyle=hintslight
XftSubPixel=rgb
[KDE][$i]
widgetStyle=Breeze
LookAndFeelPackage=org.ekklesia.classroom.desktop
SingleClick=false
AnimationDurationFactor=0.5
[Icons][$i]
Theme=breeze-dark
[KFileDialog Settings][$i]
Allow Expansion=false
Automatically select filename extension=true
Show Bookmarks=false
Show Full Path=false
Show Inline Previews=false
Show Speedbar=false
Sort directories first=true
+9
View File
@@ -0,0 +1,9 @@
# KRunner is a command line with a nicer face. The classroom has neither.
[General][$i]
Enabled=false
ActivateWhenTypingOnDesktop=false
[Plugins][$i]
shellEnabled=false
baloosearchEnabled=false
krunner_appstreamEnabled=false
krunner_shellEnabled=false
+9
View File
@@ -0,0 +1,9 @@
# A classroom appliance must never present a password prompt to a teacher.
# There is no lock screen, and nothing can bring one up: the action itself
# is disabled in kdeglobals as well, so a keyboard shortcut cannot reach it.
[Daemon][$i]
Autolock=false
LockOnResume=false
LockOnStart=false
LockGrace=0
Timeout=0
+8
View File
@@ -0,0 +1,8 @@
# The room is not somebody's account. Every session starts from the same
# empty desktop, so what the last teacher left open is never the first
# thing the next one sees.
[General][$i]
loginMode=emptySession
confirmLogout=false
offerShutdown=false
shutdownType=0
+102
View File
@@ -0,0 +1,102 @@
# The classroom's window manager.
#
# The shape of this file is the argument for the whole architecture: window
# stacking, minimising, dialog placement and full-screen media are settings
# here, not code somewhere in the classroom.
[Compositing][$i]
Enabled=true
# Let KWin choose between OpenGL and software rendering by itself. A
# classroom machine that loses acceleration after a kernel update should
# come up slower, not blank.
WindowsBlockCompositing=true
AllowTearing=false
LatencyPolicy=Medium
[Desktops][$i]
Number=1
Rows=1
# One desktop, so a teacher cannot land on an empty one and conclude the
# computer has lost their work.
[Windows][$i]
Placement=Centered
FocusPolicy=ClickToFocus
AutoRaise=false
DelayFocus=0
FocusStealingPreventionLevel=1
# A dialog belongs to the window that opened it and stays with it. This is
# what keeps a print dialog in front of the page it is printing without
# anything in the classroom arranging it.
SeparateScreenFocus=false
ActiveMouseScreen=false
TitlebarDoubleClickCommand=Maximize
MaximizeButtonLeftClickCommand=Maximize
MaximizeButtonMiddleClickCommand=Maximize
MaximizeButtonRightClickCommand=Maximize
[MouseBindings][$i]
CommandAllKey=Meta
CommandAll1=Move
CommandAll2=Nothing
CommandAll3=Nothing
CommandTitlebarWheel=Nothing
CommandAllWheel=Nothing
[TabBox][$i]
# No window switcher overlay. The bottom bar is the way between windows.
ShowTabBox=false
[Plugins][$i]
# Effects the classroom uses, and nothing else. Every line that is false is
# an effect that will not be loaded at all.
blurEnabled=true
contrastEnabled=true
kwin4_effect_fadeEnabled=true
kwin4_effect_translucencyEnabled=false
kwin4_effect_dialogparentEnabled=true
kwin4_effect_squashEnabled=true
kwin4_effect_maximizeEnabled=true
kwin4_effect_scaleEnabled=true
kwin4_effect_fullscreenEnabled=true
slidingpopupsEnabled=true
screenedgeEnabled=false
cubeEnabled=false
desktopgridEnabled=false
overviewEnabled=false
windowviewEnabled=false
wobblywindowsEnabled=false
magiclampEnabled=false
slideEnabled=false
zoomEnabled=false
mouseclickEnabled=false
showfpsEnabled=false
thumbnailasideEnabled=false
kwin4_effect_dimscreenEnabled=false
kwin4_effect_morphingpopupsEnabled=false
[Effect-blur][$i]
BlurStrength=8
NoiseStrength=2
[org.kde.kdecoration2][$i]
library=org.kde.breeze
theme=Breeze
# A pin and a close button, and nothing else.
#
# The pin is the Live View story: keeping a window above the others is a
# window-manager state, not a feature the classroom has to invent. There is
# deliberately no minimise button, because a minimised window is the one
# thing a teacher cannot see the state of — measured, not assumed. Double
# clicking the title bar still maximises, so nothing is unreachable.
ButtonsOnLeft=
ButtonsOnRight=FX
ShowToolTips=false
BorderSize=None
BorderSizeAuto=false
[Script-desktopchangeosd][$i]
Enabled=false
[Xwayland][$i]
Scale=1
+48
View File
@@ -0,0 +1,48 @@
# Window rules.
#
# There are three, they are all about size, and none of them is about
# stacking. Dialogs staying in front of the window that opened them,
# secondary windows floating over the main one instead of resizing it, and
# a print dialog being the window in front are all ordinary window-manager
# behaviour here, so there is nothing to write down.
#
# `types=1` is NET::Normal only. Without it a rule that maximises would
# also maximise the dialogs, which is exactly the behaviour the classroom
# is moving away from.
[General]
count=3
rules=classroom-browser,classroom-wonderink,classroom-video
[classroom-browser]
Description=The classroom browser fills the screen
wmclass=google-chrome
wmclassmatch=1
wmclasscomplete=false
types=1
maximizehoriz=true
maximizehorizrule=3
maximizevert=true
maximizevertrule=3
[classroom-wonderink]
Description=Wonder Ink fills the screen
# Chrome derives this from the --app URL. Measured on a running session
# rather than guessed; it is also what the launcher's StartupWMClass says,
# and the two have to agree or the panel shows two buttons for one app.
wmclass=chrome-app.wonderink.org__-Default
wmclassmatch=1
wmclasscomplete=false
types=1
maximizehoriz=true
maximizehorizrule=3
maximizevert=true
maximizevertrule=3
[classroom-video]
Description=Video is an appliance screen, not a window
wmclass=vlc
wmclassmatch=2
wmclasscomplete=false
noborder=true
noborderrule=2
+16
View File
@@ -0,0 +1,16 @@
# Small cards, top right, out of the way of the bottom bar and of anything
# a teacher is reading. Plasma's own notification service does this; the
# classroom does not run a notification daemon of its own.
[Notifications][$i]
PopupPosition=TopRight
PopupTimeout=6000
LowPriorityPopups=false
LowPriorityHistory=false
[Jobs][$i]
PermanentPopups=false
InNotifications=true
[DoNotDisturb][$i]
WhenFullscreen=true
WhenScreensMirrored=false