#!/bin/bash # Which displays a machine drives, and what a closed lid does. # # The laptop going in for the women's Bible study sits on AC with its # battery out, its lid shut and one television on HDMI. Two separate # things have to be true for that: its internal panel is off, and closing # the lid does nothing at all. set -uo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PYTHONPATH="$ROOT/lib" python3 - "$ROOT" <<'PY' import sys from pathlib import Path root = Path(sys.argv[1]) sys.path.insert(0, str(root / "lib")) from classroom_display import ( ALL, EXTERNAL_ONLY, TYPE_PANEL, changes, is_internal, plan, ) from classroom_config import display_mode passed = failed = 0 def check(label, condition, detail=""): global passed, failed if condition: print(f" ok {label}") passed += 1 else: print(f" FAIL {label}") if detail: print(f" {detail}") failed += 1 def out(name, kind, enabled=True, connected=True): return {"name": name, "type": kind, "enabled": enabled, "connected": connected} HDMI, DISPLAYPORT, UNKNOWN = 6, 14, 0 # The Bible study laptop: its own panel, and the television. LAPTOP = [out("eDP-1", TYPE_PANEL), out("HDMI-A-1", HDMI)] # A classroom tower with one television. TOWER = [out("HDMI-A-1", HDMI)] print("1. the built-in panel is found by what it IS, not what it is called") check("kscreen's Panel type is the built-in screen", is_internal(out("whatever-3", TYPE_PANEL))) check("an HDMI output is not", not is_internal(out("HDMI-A-1", HDMI))) check("nor DisplayPort", not is_internal(out("DP-2", DISPLAYPORT))) # Some drivers report no type at all, so the names are a fallback and # only a fallback. check("a driver that reports no type falls back to the name", is_internal(out("eDP-1", UNKNOWN)) and is_internal(out("LVDS-1", UNKNOWN))) check("and an unknown-type television is still not internal", not is_internal(out("HDMI-A-1", UNKNOWN))) print("2. every existing classroom is untouched") check("a tower on 'all' drives its television", plan(TOWER, ALL) == (["HDMI-A-1"], [])) check("a device that has never heard of DISPLAY_MODE gets 'all'", display_mode({}) == ALL) check("and an unreadable value is 'all' rather than a dark room", display_mode({"DISPLAY_MODE": "nonsense"}) == ALL) print("3. the Bible study laptop") enable, disable = plan(LAPTOP, EXTERNAL_ONLY) check("the television is driven", enable == ["HDMI-A-1"], str(enable)) check("and the laptop's own panel is switched off", disable == ["eDP-1"], str(disable)) print("4. the same laptop is not forced into it") # external-only must be a setting, never a property of having a lid. check("a laptop on 'all' keeps both screens", plan(LAPTOP, ALL) == (["eDP-1", "HDMI-A-1"], [])) print("5. never leave the machine with no screen") # A television that is off at boot is the ordinary case, and a laptop # that answers it by blinding itself is one nobody can fix standing in # front of it. lid_only = [out("eDP-1", TYPE_PANEL)] check("external-only with no external display keeps the panel on", plan(lid_only, EXTERNAL_ONLY) == (["eDP-1"], []), str(plan(lid_only, EXTERNAL_ONLY))) check("and nothing at all connected decides nothing", plan([], EXTERNAL_ONLY) == ([], [])) check("a disconnected output is not counted as somewhere to go", plan([out("eDP-1", TYPE_PANEL), out("HDMI-A-1", HDMI, connected=False)], EXTERNAL_ONLY) == (["eDP-1"], [])) print("6. only what is not already true is applied") # This is what keeps a television power cycle from becoming a stream of # mode sets, and what keeps the journal quiet when there is nothing to do. settled = [out("eDP-1", TYPE_PANEL, enabled=False), out("HDMI-A-1", HDMI)] check("a laptop already in external-only needs no change", changes(settled, EXTERNAL_ONLY) == ([], []), str(changes(settled, EXTERNAL_ONLY))) check("a television just switched on is enabled, panel switched off", changes([out("eDP-1", TYPE_PANEL), out("HDMI-A-1", HDMI, enabled=False)], EXTERNAL_ONLY) == (["HDMI-A-1"], ["eDP-1"])) check("a settled tower needs no change", changes(TOWER, ALL) == ([], [])) print("7. a closed lid does nothing, at every layer that could act on it") logind = (root / "config/systemd/logind-ekklesia-classroom.conf").read_text() powerdevil = (root / "config/plasma/xdg/powermanagementprofilesrc").read_text() sleep_rules = (root / "config/polkit/org.ekklesia.classroom.sleep.rules").read_text() locker = (root / "config/plasma/xdg/kscreenlockerrc").read_text() for key in ("HandleLidSwitch=ignore", "HandleLidSwitchExternalPower=ignore", "HandleLidSwitchDocked=ignore"): check(f"logind: {key}", key in logind) # PowerDevil takes logind's inhibitor while a session is running, so # logind alone would be overruled. Both halves, in every profile. check("PowerDevil does nothing on a lid event, on AC and on battery", powerdevil.count("lidAction=0") >= 3, powerdevil.count("lidAction=0")) check("and does not act just because a monitor is attached", powerdevil.count("triggerLidActionWhenExternalMonitorPresent=false") >= 3) check("every power profile is immutable, so a stray file cannot add one", powerdevil.count("[$i]") >= 6) check("the classroom account cannot suspend by any other route", "login1.suspend" in sleep_rules and "login1.hibernate" in sleep_rules) check("and the session never locks", "Autolock=false" in locker or "LockOnResume=false" in locker, locker[:200]) print() print(f"{passed} passed, {failed} failed") raise SystemExit(1 if failed else 0) PY