#!/usr/bin/env python3 """Read the classroom bar off the screen and say what order it is in. The launcher-order bug of 0.2.4 got past a full set of configuration tests, because the configuration was right and the applet did something else with it. So this asks the picture. It captures the panel strip as a PPM — no image library anywhere in this lab, and PPM is a length, three numbers and the bytes — finds the icons by looking for columns that are not the bar's own background, and names each one by its colour. Three icons, three colours that are nothing like each other: Wonder Ink orange and blue, and orange is what a column of it averages to Browser Chrome's wheel: red, green, yellow and blue, so its average is unusually grey for something this bright the player one flat purple disc Usage: panel-order [STRIP] default "0,990 1920x90" Prints one name per icon, left to right. """ import subprocess import sys CONTAINER = "classroom-v2-lab" STRIP = sys.argv[1] if len(sys.argv) > 1 else "0,990 1920x90" def capture(): socket = subprocess.run( ["docker", "exec", CONTAINER, "sh", "-c", "ls /run/user/1000/sway-ipc.* 2>/dev/null | head -1"], capture_output=True, text=True, check=True).stdout.strip() raw = subprocess.run( ["docker", "exec", "-u", "classroom", "-e", "XDG_RUNTIME_DIR=/run/user/1000", "-e", "WAYLAND_DISPLAY=wayland-1", "-e", f"SWAYSOCK={socket}", CONTAINER, "grim", "-t", "ppm", "-g", STRIP, "-"], capture_output=True, check=True).stdout return raw def pixels(raw): """(width, height, rows) from a binary PPM.""" fields = [] index = 0 while len(fields) < 4: while raw[index:index + 1].isspace(): index += 1 if raw[index:index + 1] == b"#": while raw[index:index + 1] not in (b"\n", b""): index += 1 continue start = index while not raw[index:index + 1].isspace(): index += 1 fields.append(raw[start:index]) index += 1 width, height = int(fields[1]), int(fields[2]) body = raw[index:] rows = [ body[y * width * 3:(y + 1) * width * 3] for y in range(height) ] return width, height, rows def columns(width, height, rows): """Average colour and brightness of each column.""" found = [] for x in range(width): r = g = b = 0 for row in rows: r += row[x * 3] g += row[x * 3 + 1] b += row[x * 3 + 2] found.append((r // height, g // height, b // height)) return found def name(colour): r, g, b = colour # The disc: blue and red well above green. if b > g + 25 and r > g + 15: return "player" # Wonder Ink: warm, red clearly ahead of blue. if r > b + 25: return "wonderink" return "browser" def main(): width, height, rows = pixels(capture()) found = columns(width, height, rows) # The bar is dark and flat; an icon is a run of brighter columns. floor = sorted(sum(c) for c in found)[len(found) // 2] + 60 runs = [] run = None for x, colour in enumerate(found): if sum(colour) > floor: if run is None: run = [x, x] else: run[1] = x elif run is not None: if run[1] - run[0] >= 12: runs.append(run) run = None if run is not None and run[1] - run[0] >= 12: runs.append(run) # The applications live in the middle third; the corners are Parent # Call on the left and the status icons on the right. middle = [ item for item in runs if width // 3 < (item[0] + item[1]) // 2 < (width * 2) // 3 ] for first, last in middle: centre = (first + last) // 2 window = found[max(first, centre - 8):min(last, centre + 8) + 1] r = sum(c[0] for c in window) // max(1, len(window)) g = sum(c[1] for c in window) // max(1, len(window)) b = sum(c[2] for c in window) // max(1, len(window)) print(name((r, g, b))) return 0 if __name__ == "__main__": sys.exit(main())