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]>
156 lines
4.0 KiB
Python
Executable File
156 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""Classroom DVD control.
|
|
|
|
Discs are never played automatically. Inserting a disc only produces a
|
|
notification and changes the dock icon; playback always starts from a
|
|
deliberate press of the DVD button.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
sys.path.insert(0, "/usr/local/lib/ekklesia-classroom")
|
|
sys.path.insert(
|
|
0,
|
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "lib"),
|
|
)
|
|
|
|
import classroom_optical as optical # noqa: E402
|
|
from classroom_vlc import PLAYER # noqa: E402
|
|
|
|
SESSION_TOOL = "/usr/local/bin/classroom-dvd-session"
|
|
|
|
|
|
def notify(title, message, urgency="normal"):
|
|
try:
|
|
subprocess.Popen(
|
|
[
|
|
"notify-send",
|
|
"--app-name=Ekklesia Classroom",
|
|
f"--urgency={urgency}",
|
|
"--",
|
|
title,
|
|
message,
|
|
],
|
|
start_new_session=True,
|
|
)
|
|
except OSError:
|
|
print(f"{title}: {message}", file=sys.stderr)
|
|
|
|
|
|
def play(device):
|
|
"""Hand the disc to DVD mode.
|
|
|
|
classroom-dvd-session owns the player from here: it puts a black
|
|
surface up immediately, starts VLC behind it, and provides the
|
|
classroom's own controls.
|
|
|
|
Started directly, with nothing arranging windows on the way. A second
|
|
session cannot happen — the session takes an exclusive lock on
|
|
dvd.lock and exits if it cannot get it — and bringing an existing one
|
|
back to the front is the panel's job, not this command's. That is
|
|
what replaced the window helper.
|
|
"""
|
|
if shutil.which(PLAYER) is None:
|
|
notify(
|
|
"DVD",
|
|
"The DVD player is not installed on this classroom computer.",
|
|
)
|
|
return 1
|
|
|
|
subprocess.Popen([SESSION_TOOL], start_new_session=True)
|
|
|
|
return 0
|
|
|
|
|
|
def action_toggle(force_play=False):
|
|
device = optical.default_device()
|
|
|
|
if device is None:
|
|
notify("DVD", "No DVD drive is connected to this classroom computer.")
|
|
return 1
|
|
|
|
status = optical.drive_status(device)
|
|
|
|
if status == optical.CDS_DISC_OK:
|
|
return play(device)
|
|
|
|
if status == optical.CDS_DRIVE_NOT_READY:
|
|
notify("DVD", "Reading the disc. Please try again in a moment.")
|
|
return 1
|
|
|
|
if status == optical.CDS_TRAY_OPEN:
|
|
notify("DVD", "The disc tray is open. Insert a disc to get started.")
|
|
return 1
|
|
|
|
if force_play:
|
|
return play(device)
|
|
|
|
notify("DVD", "No disc is in the drive. Insert a disc to get started.")
|
|
return 1
|
|
|
|
|
|
def action_status():
|
|
device = optical.default_device()
|
|
|
|
if device is None:
|
|
print("drive: none")
|
|
return 1
|
|
|
|
status = optical.drive_status(device)
|
|
|
|
print(f"drive: {device}")
|
|
print(f"status: {optical.describe(status)}")
|
|
print(f"player: {shutil.which(PLAYER) or 'not installed'}")
|
|
|
|
if status == optical.CDS_DISC_OK:
|
|
print(f"disc: {optical.disc_status(device)}")
|
|
return 0
|
|
|
|
return 1
|
|
|
|
|
|
def action_eject():
|
|
device = optical.default_device()
|
|
|
|
if device is None:
|
|
notify("DVD", "No DVD drive is connected to this classroom computer.")
|
|
return 1
|
|
|
|
if not optical.eject(device):
|
|
notify("DVD", "The disc could not be ejected. It may still be playing.")
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Classroom DVD control.")
|
|
subparsers = parser.add_subparsers(dest="action", required=True)
|
|
|
|
subparsers.add_parser("toggle", help="Dock button behaviour")
|
|
subparsers.add_parser("play", help="Start playback even if the drive is unsure")
|
|
subparsers.add_parser("status", help="Report drive and disc state")
|
|
subparsers.add_parser("eject", help="Open the disc tray")
|
|
|
|
arguments = parser.parse_args()
|
|
|
|
if arguments.action == "toggle":
|
|
return action_toggle()
|
|
|
|
if arguments.action == "play":
|
|
return action_toggle(force_play=True)
|
|
|
|
if arguments.action == "status":
|
|
return action_status()
|
|
|
|
return action_eject()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|