Files
Classroom_Desktop/scripts/classroom-printer-setup
T
ekklesiaandClaude Opus 5 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]>
2026-09-06 20:24:51 -05:00

197 lines
6.5 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# Configure the one explicitly managed church printer. The Ricoh Aficio MP
# C3502 supports PCL 6 as standard; Debian Trixie's compressed Foomatic
# database supplies this generic color PCL 6/PCL XL path without vendor code.
QUEUE="Elmer"
LEGACY_QUEUE="Classroom_Color"
DEFAULT_URI="socket://10.10.0.155:9100"
DRIVER="foomatic-db-compressed-ppds:0/ppd/foomatic-ppd/Generic-PCL_6_PCL_XL_Printer-pxlcolor.ppd"
DESCRIPTION="Elmer"
# Blank on purpose. CUPS shows Location under the queue name in every
# print dialog, and the teacher-facing identity of this printer is
# simply "Elmer".
LOCATION=""
MAKE_DEFAULT=true
COMMAND_TIMEOUT=10
usage() {
cat <<'USAGE'
Usage: classroom-printer-setup [--no-default] [PRINTER-IP-OR-URI]
Examples:
sudo classroom-printer-setup
sudo classroom-printer-setup 10.10.0.155
sudo classroom-printer-setup socket://printer.example.org:9100
With no address, configures the church Ricoh as Elmer at
socket://10.10.0.155:9100.
An IP address or hostname uses JetDirect/AppSocket on port 9100. Explicit
socket://, ipp://, ipps:// and lpd:// URIs are also accepted.
USAGE
}
log() {
echo "[classroom-printer-setup] $1"
}
fail() {
log "ERROR: $1" >&2
exit 1
}
if [[ "${CLASSROOM_PRINTER_SETUP_TESTING:-false}" != "true" && $EUID -ne 0 ]]; then
fail "run this command as root: sudo classroom-printer-setup"
fi
if [[ "${1:-}" == "--no-default" ]]; then
MAKE_DEFAULT=false
shift
fi
if [[ $# -gt 1 || "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
[[ $# -le 1 ]] && exit 0
exit 2
fi
address="${1:-$DEFAULT_URI}"
case "$address" in
socket://*|ipp://*|ipps://*|lpd://*)
uri="$address"
;;
*://*)
fail "unsupported printer URI scheme: $address"
;;
*)
if [[ ! "$address" =~ ^[A-Za-z0-9._:-]+$ ]]; then
fail "printer address contains unsupported characters: $address"
fi
uri="socket://${address}:9100"
;;
esac
for command in timeout lpadmin lpinfo lpoptions lpstat cupsenable cupsaccept; do
command -v "$command" >/dev/null 2>&1 || \
fail "$command is missing; reinstall the ekklesia-classroom package"
done
run_bounded() {
timeout \
--signal=TERM \
--kill-after=2s \
"${COMMAND_TIMEOUT}s" \
"$@"
}
disable_discovery() {
command -v systemctl >/dev/null 2>&1 || return 0
# cups-browsed creates local queues for advertised printers. CUPS can also
# enumerate DNS-SD destinations through Avahi even without cups-browsed,
# so disable both Avahi activation paths on this single-purpose appliance.
run_bounded systemctl disable --now cups-browsed.service \
>/dev/null 2>&1 || true
run_bounded systemctl disable --now \
avahi-daemon.socket avahi-daemon.service >/dev/null 2>&1 || true
run_bounded systemctl mask \
avahi-daemon.socket avahi-daemon.service >/dev/null 2>&1 || true
}
disable_discovery
if ! run_bounded lpstat -r >/dev/null 2>&1; then
command -v systemctl >/dev/null 2>&1 && \
run_bounded systemctl start cups.service >/dev/null 2>&1 || true
# systemctl can return as the service begins starting. Give the local
# scheduler a short, bounded chance to create its socket.
for _ in {1..20}; do
run_bounded lpstat -r >/dev/null 2>&1 && break
sleep 0.25
done
fi
run_bounded lpstat -r >/dev/null 2>&1 || \
fail "CUPS is not running; check: systemctl status cups"
if run_bounded lpstat -p "$LEGACY_QUEUE" >/dev/null 2>&1; then
log "removing legacy ${LEGACY_QUEUE} queue"
run_bounded lpadmin -x "$LEGACY_QUEUE" || \
fail "CUPS could not remove legacy queue ${LEGACY_QUEUE}"
fi
device="$(run_bounded lpstat -v "$QUEUE" 2>/dev/null || true)"
status="$(run_bounded lpstat -p "$QUEUE" 2>/dev/null || true)"
accepting="$(run_bounded lpstat -a "$QUEUE" 2>/dev/null || true)"
default="$(run_bounded lpstat -d 2>/dev/null || true)"
details="$(run_bounded lpstat -l -p "$QUEUE" 2>/dev/null || true)"
options="$(run_bounded lpoptions -p "$QUEUE" -l 2>/dev/null || true)"
if grep -Fq ": ${uri}" <<< "$device" \
&& grep -Fq "printer ${QUEUE}" <<< "$status" \
&& grep -Fq " enabled " <<< " ${status} " \
&& grep -Fq "${QUEUE} accepting requests" <<< "$accepting" \
&& grep -Fq "system default destination: ${QUEUE}" <<< "$default" \
&& grep -Fq "Description: ${DESCRIPTION}" <<< "$details" \
&& grep -Eq '^[[:space:]]*Location:[[:space:]]*$' <<< "$details" \
&& grep -Eq '^PrintoutMode/.*:.*\*Normal([[:space:]]|$)' <<< "$options" \
&& grep -Eq '^ColorModel/.*:.*\*Color([[:space:]]|$)' <<< "$options"; then
log "${QUEUE} is already correctly configured."
exit 0
fi
models="$(run_bounded lpinfo -m)" || \
fail "CUPS could not list installed printer drivers"
if ! grep -q "^${DRIVER} " <<< "$models"; then
fail "the Debian generic color PCL 6 driver is not installed"
fi
log "configuring ${QUEUE} at ${uri}"
# Repeating this command updates the same named queue; it never creates a
# second destination. None of these operations connects to the printer.
run_bounded lpadmin \
-p "$QUEUE" \
-v "$uri" \
-m "$DRIVER" \
-D "$DESCRIPTION" \
-L "$LOCATION" || fail "CUPS could not configure ${QUEUE}"
# CUPS on Trixie does not reliably retain PPD choices supplied in the same
# request that selects the driver. Apply the physically verified defaults in a
# second request after the PPD exists.
run_bounded lpadmin \
-p "$QUEUE" \
-o PrintoutMode=Normal \
-o ColorModel=Color || fail "CUPS could not set the color defaults for ${QUEUE}"
options="$(run_bounded lpoptions -p "$QUEUE" -l 2>/dev/null || true)"
grep -Eq '^PrintoutMode/.*:.*\*Normal([[:space:]]|$)' <<< "$options" \
|| fail "CUPS did not retain PrintoutMode=Normal for ${QUEUE}"
grep -Eq '^ColorModel/.*:.*\*Color([[:space:]]|$)' <<< "$options" \
|| fail "CUPS did not retain ColorModel=Color for ${QUEUE}"
run_bounded cupsenable "$QUEUE" || fail "CUPS could not enable ${QUEUE}"
run_bounded cupsaccept "$QUEUE" || fail "CUPS could not accept jobs for ${QUEUE}"
if [[ "$MAKE_DEFAULT" == true ]]; then
run_bounded lpadmin -d "$QUEUE" || \
fail "CUPS could not make ${QUEUE} the default"
fi
log "${QUEUE} is configured, enabled and accepting jobs."
if [[ "$MAKE_DEFAULT" == true ]]; then
log "${QUEUE} is the system default printer."
fi
log "Print defaults: PrintoutMode=Normal, ColorModel=Color."
log "The printer may remain offline until it is reachable; this does not affect boot."